Merge mozilla-central to inbound. a=merge CLOSED TREE
authorMargareta Eliza Balazs <ebalazs@mozilla.com>
Wed, 04 Apr 2018 12:51:48 +0300
changeset 411647 dd31fb345c11732432fa61a7d8a1b727d41b4a1c
parent 411613 7c2423c6e9d0d22b1db406db1f81fbab2a0b02ef (current diff)
parent 411646 ff0efa4132f0efd78af0910762aec7dcc1a8de66 (diff)
child 411648 372811caffc6bdcb9ea6e0094766191f5fb5ae52
push id101712
push userebalazs@mozilla.com
push dateWed, 04 Apr 2018 09:52:15 +0000
treeherdermozilla-inbound@dd31fb345c11 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone61.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge mozilla-central to inbound. a=merge CLOSED TREE
layout/style/nsCSSFontFaceRule.cpp
layout/style/nsCSSFontFaceRule.h
modules/libpref/init/all.js
third_party/rust/itoa/performance.png
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -356,28 +356,29 @@ name = "crossbeam-utils"
 version = "0.2.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "cssparser"
-version = "0.23.2"
+version = "0.23.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "cssparser-macros 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "dtoa-short 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "itoa 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "itoa 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "matches 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "phf 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)",
+ "proc-macro2 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "procedural-masquerade 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)",
+ "quote 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "smallvec 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)",
+ "syn 0.12.12 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "cssparser-macros"
 version = "0.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "phf_codegen 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -693,17 +694,17 @@ dependencies = [
  "zip 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "geckoservo"
 version = "0.0.1"
 dependencies = [
  "atomic_refcell 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "cssparser 0.23.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cssparser 0.23.4 (registry+https://github.com/rust-lang/crates.io-index)",
  "cstr 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "env_logger 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "malloc_size_of 0.0.1",
  "nsstring 0.1.0",
  "parking_lot 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)",
  "selectors 0.19.0",
@@ -852,16 +853,21 @@ dependencies = [
 ]
 
 [[package]]
 name = "itoa"
 version = "0.3.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
+name = "itoa"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
 name = "js"
 version = "0.1.4"
 dependencies = [
  "bindgen 0.33.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "cmake 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)",
  "env_logger 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
  "lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -958,17 +964,17 @@ dependencies = [
  "cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "malloc_size_of"
 version = "0.0.1"
 dependencies = [
  "app_units 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "cssparser 0.23.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cssparser 0.23.4 (registry+https://github.com/rust-lang/crates.io-index)",
  "euclid 0.17.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "hashglobe 0.1.0",
  "selectors 0.19.0",
  "servo_arc 0.1.1",
  "smallbitvec 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "smallvec 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
@@ -1537,17 +1543,17 @@ name = "scopeguard"
 version = "0.3.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
 name = "selectors"
 version = "0.19.0"
 dependencies = [
  "bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "cssparser 0.23.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cssparser 0.23.4 (registry+https://github.com/rust-lang/crates.io-index)",
  "fnv 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "matches 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "phf 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)",
  "phf_codegen 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)",
  "precomputed-hash 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "servo_arc 0.1.1",
  "smallvec 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1647,17 +1653,17 @@ version = "0.0.1"
 dependencies = [
  "app_units 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "arrayvec 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "atomic_refcell 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "bindgen 0.33.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
- "cssparser 0.23.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cssparser 0.23.4 (registry+https://github.com/rust-lang/crates.io-index)",
  "debug_unreachable 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "euclid 0.17.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "fallible 0.0.1",
  "fnv 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "hashglobe 0.1.0",
  "itertools 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "itoa 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1702,30 +1708,30 @@ dependencies = [
 ]
 
 [[package]]
 name = "style_traits"
 version = "0.0.1"
 dependencies = [
  "app_units 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "cssparser 0.23.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cssparser 0.23.4 (registry+https://github.com/rust-lang/crates.io-index)",
  "euclid 0.17.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "malloc_size_of 0.0.1",
  "malloc_size_of_derive 0.0.1",
  "selectors 0.19.0",
  "servo_arc 0.1.1",
 ]
 
 [[package]]
 name = "stylo_tests"
 version = "0.0.1"
 dependencies = [
  "atomic_refcell 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "cssparser 0.23.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cssparser 0.23.4 (registry+https://github.com/rust-lang/crates.io-index)",
  "cstr 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "env_logger 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "euclid 0.17.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "geckoservo 0.0.1",
  "libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "malloc_size_of 0.0.1",
  "regex 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -2249,17 +2255,17 @@ dependencies = [
 "checksum core-graphics 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fb0ed45fdc32f9ab426238fba9407dfead7bacd7900c9b4dd3f396f46eafdae3"
 "checksum core-text 9.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2bd581c37283d0c23311d179aefbb891f2324ee0405da58a26e8594ab76e5748"
 "checksum cose 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "72fa26cb151d3ae4b70f63d67d0fed57ce04220feafafbae7f503bef7aae590d"
 "checksum cose-c 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "49726015ab0ca765144fcca61e4a7a543a16b795a777fa53f554da2fffff9a94"
 "checksum crc 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bd5d02c0aac6bd68393ed69e00bbc2457f3e89075c6349db7189618dc4ddc1d7"
 "checksum crossbeam-deque 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f739f8c5363aca78cfb059edf753d8f0d36908c348f3d8d1503f03d8b75d9cf3"
 "checksum crossbeam-epoch 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "927121f5407de9956180ff5e936fe3cf4324279280001cd56b669d28ee7e9150"
 "checksum crossbeam-utils 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2760899e32a1d58d5abb31129f8fae5de75220bc2176e77ff7c627ae45c918d9"
-"checksum cssparser 0.23.2 (registry+https://github.com/rust-lang/crates.io-index)" = "8a807ac3ab7a217829c2a3b65732b926b2befe6a35f33b4bf8b503692430f223"
+"checksum cssparser 0.23.4 (registry+https://github.com/rust-lang/crates.io-index)" = "b906ac3f6108d8d0bfd4158469abe5909df1497116c8400346b5e08944579edd"
 "checksum cssparser-macros 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "079adec4af52bb5275eadd004292028c79eb3c5f5b4ee8086a36d4197032f6df"
 "checksum cstr 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "b6557bdb1dc9647eae1cf7f5601b14cd45fc3c7ccf2df618387416fe542da6ea"
 "checksum cstr-macros 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f9f316203d1ea36f4f18316822806f6999aa3dc5ed1adf51e35b77e3b3933d78"
 "checksum cubeb 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7923bed2d5a1a64ba0c3e8b6badc360508ba488d1f2f59f16a688802e755bb85"
 "checksum cubeb-backend 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fdcac95519416d9ec814db2dc40e6293e7da25b906023d93f48b87f0587ab138"
 "checksum cubeb-core 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "169d9a36f5daa60f9c4597905132aef056cf62693addd4a3421492853ccad565"
 "checksum cubeb-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e405ad4dff2c1a7cbfa6998e5925e8ceafe900feeacfcad35a3e3790bea0f2aa"
 "checksum darling 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d3effd06d4057f275cb7858889f4952920bab78dd8ff0f6e7dfe0c8d2e67ed89"
@@ -2294,16 +2300,17 @@ dependencies = [
 "checksum httparse 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "af2f2dd97457e8fb1ae7c5a420db346af389926e36f43768b96f101546b04a07"
 "checksum humantime 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0484fda3e7007f2a4a0d9c3a703ca38c71c54c55602ce4660c419fd32e188c9e"
 "checksum hyper 0.10.13 (registry+https://github.com/rust-lang/crates.io-index)" = "368cb56b2740ebf4230520e2b90ebb0461e69034d85d1945febd9b3971426db2"
 "checksum ident_case 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3c9826188e666f2ed92071d2dadef6edc430b11b158b5b2b3f4babbcc891eaaa"
 "checksum idna 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "014b298351066f1512874135335d62a789ffe78a9974f94b43ed5621951eaf7d"
 "checksum iovec 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "29d062ee61fccdf25be172e70f34c9f6efc597e1fb8f6526e8437b2046ab26be"
 "checksum itertools 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)" = "b07332223953b5051bceb67e8c4700aa65291535568e1f12408c43c4a42c0394"
 "checksum itoa 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "eb2f404fbc66fd9aac13e998248505e7ecb2ad8e44ab6388684c5fb11c6c251c"
+"checksum itoa 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c069bbec61e1ca5a596166e55dfe4773ff745c3d16b700013bcaff9a6df2c682"
 "checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
 "checksum khronos_api 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d867c645cfeb8a7fec503731679eac03ac11b7105aa5a71cb8f8ee5271636add"
 "checksum language-tags 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a91d884b6667cd606bb5a69aa0c99ba811a115fc68915e7056ec08a46e93199a"
 "checksum lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c8f31047daa365f19be14b47c29df4f7c3b581832407daabe6ae77397619237d"
 "checksum lazycell 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ce12306c4739d86ee97c23139f3a34ddf0387bbf181bc7929d287025a8c3ef6b"
 "checksum libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)" = "f54263ad99207254cf58b5f701ecb432c717445ea2ee8af387334bdd1a03fdff"
 "checksum libloading 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9c3ad660d7cb8c5822cd83d10897b0f1f1526792737a179e73896152f85b88c2"
 "checksum libudev 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ea626d3bdf40a1c5aee3bcd4f40826970cae8d80a8fec934c82a63840094dcfe"
--- a/browser/components/enterprisepolicies/Policies.jsm
+++ b/browser/components/enterprisepolicies/Policies.jsm
@@ -425,17 +425,22 @@ var Policies = {
           }
         });
       }
       if ("Uninstall" in param) {
         runOncePerModification("extensionsUninstall", JSON.stringify(param.Uninstall), () => {
           AddonManager.getAddonsByIDs(param.Uninstall, (addons) => {
             for (let addon of addons) {
               if (addon) {
-                addon.uninstall();
+                try {
+                  addon.uninstall();
+                } catch (e) {
+                  // This can fail for add-ons that can't be uninstalled.
+                  // Just ignore.
+                }
               }
             }
           });
         });
       }
       if ("Locked" in param) {
         for (let ID of param.Locked) {
           manager.disallowFeature(`modify-extension:${ID}`);
--- a/browser/modules/ZoomUI.jsm
+++ b/browser/modules/ZoomUI.jsm
@@ -61,17 +61,17 @@ function onZoomChange(event) {
  * Updates zoom controls.
  *
  * @param {object} aBrowser The browser that the zoomed content resides in.
  * @param {boolean} aAnimate Should be True for all cases unless the zoom
  *   change is related to tab switching. Optional
  */
 function updateZoomUI(aBrowser, aAnimate = false) {
   let win = aBrowser.ownerGlobal;
-  if (aBrowser != win.gBrowser.selectedBrowser) {
+  if (!win.gBrowser || win.gBrowser.selectedBrowser != aBrowser) {
     return;
   }
 
   let appMenuZoomReset = win.document.getElementById("appMenu-zoomReset-button");
   let customizableZoomControls = win.document.getElementById("zoom-controls");
   let customizableZoomReset = win.document.getElementById("zoom-reset-button");
   let urlbarZoomButton = win.document.getElementById("urlbar-zoom-button");
   let zoomFactor = Math.round(win.ZoomManager.zoom * 100);
--- a/devtools/client/inspector/fonts/test/browser_fontinspector_expand-css-code.js
+++ b/devtools/client/inspector/fonts/test/browser_fontinspector_expand-css-code.js
@@ -15,37 +15,37 @@ add_task(async function() {
   info("Checking that the css font-face rule is collapsed by default");
   let fontEl = getUsedFontsEls(viewDoc)[0];
   let codeEl = fontEl.querySelector(".font-css-code");
   is(codeEl.textContent, "@font-face {}", "The font-face rule is collapsed");
 
   info("Expanding the rule by clicking on the expander icon");
   let onExpanded = BrowserTestUtils.waitForCondition(() => {
     return codeEl.textContent === `@font-face {
-  font-family: "bar";
+  font-family: bar;
   src: url("bad/font/name.ttf"), url("ostrich-regular.ttf") format("truetype");
 }`;
-  }, "Waiting for the font-face rule");
+  }, "Waiting for the font-face rule 1");
 
   let expander = fontEl.querySelector(".font-css-code .theme-twisty");
   expander.click();
   await onExpanded;
 
   ok(true, "Font-face rule is now expanded");
 
   info("Expanding another rule by clicking on the [...] icon instead");
   fontEl = getUsedFontsEls(viewDoc)[1];
   codeEl = fontEl.querySelector(".font-css-code");
 
   onExpanded = BrowserTestUtils.waitForCondition(() => {
     return codeEl.textContent === `@font-face {
-  font-family: "bar";
-  font-weight: bold;
+  font-family: bar;
   src: url("ostrich-black.ttf");
+  font-weight: bold;
 }`;
-  }, "Waiting for the font-face rule");
+  }, "Waiting for the font-face rule 2");
 
   expander = fontEl.querySelector(".font-css-code .font-css-code-expander");
   expander.click();
   await onExpanded;
 
   ok(true, "Font-face rule is now expanded too");
 });
--- a/dom/bindings/Bindings.conf
+++ b/dom/bindings/Bindings.conf
@@ -182,18 +182,18 @@ DOMInterfaces = {
 },
 
 'CSSCounterStyleRule': {
     'nativeType': 'nsCSSCounterStyleRule',
     'headerFile': 'nsCSSCounterStyleRule.h',
 },
 
 'CSSFontFaceRule': {
-    'nativeType': 'nsCSSFontFaceRule',
-    'headerFile': 'nsCSSFontFaceRule.h',
+    'nativeType': 'mozilla::ServoFontFaceRule',
+    'headerFile': 'mozilla/ServoFontFaceRule.h',
 },
 
 'CSSGroupingRule': {
     'concrete': False,
     'nativeType': 'mozilla::css::GroupRule',
 },
 
 'CSSLexer': {
--- a/dom/media/tests/mochitest/templates.js
+++ b/dom/media/tests/mochitest/templates.js
@@ -478,17 +478,17 @@ function PC_LOCAL_REMOVE_RTCPMUX_FROM_OF
 
 function PC_LOCAL_REMOVE_SSRC_FROM_OFFER(test) {
   test.originalOffer.sdp = sdputils.removeSSRCs(test.originalOffer.sdp);
   info("Updated no SSRCs offer: " + JSON.stringify(test.originalOffer));
 };
 
 function PC_REMOTE_REMOVE_SSRC_FROM_ANSWER(test) {
   test.originalAnswer.sdp = sdputils.removeSSRCs(test.originalAnswer.sdp);
-  info("Updated no SSRCs answer: " + JSON.stringify(test.originalAnswerr));
+  info("Updated no SSRCs answer: " + JSON.stringify(test.originalAnswer));
 };
 
 var addRenegotiation = (chain, commands, checks) => {
   chain.append(commands);
   chain.append(commandsPeerConnectionOfferAnswer);
   if (checks) {
     chain.append(checks);
   }
--- a/dom/plugins/ipc/PluginProcessChild.cpp
+++ b/dom/plugins/ipc/PluginProcessChild.cpp
@@ -121,17 +121,17 @@ PluginProcessChild::Init(int aArgc, char
 
     pluginFilename = WideToUTF8(values[0]);
 
     // We don't initialize XPCOM but we need the thread manager and the
     // logging framework for the FunctionBroker.
     NS_SetMainThread();
     mozilla::TimeStamp::Startup();
     NS_LogInit();
-    mozilla::LogModule::Init();
+    mozilla::LogModule::Init(aArgc, aArgv);
     nsThreadManager::get().Init();
 
 #if defined(MOZ_SANDBOX)
     // This is probably the earliest we would want to start the sandbox.
     // As we attempt to tighten the sandbox, we may need to consider moving this
     // to later in the plugin initialization.
     mozilla::SandboxTarget::Instance()->StartSandbox();
 #endif
--- a/gfx/layers/wr/WebRenderCommandBuilder.cpp
+++ b/gfx/layers/wr/WebRenderCommandBuilder.cpp
@@ -530,17 +530,20 @@ struct DIGroup
         InvalidateRect(data->mRect);
         iter.Remove();
         delete data;
       } else {
         data->mUsed = false;
       }
     }
 
+    // Round the bounds in a way that matches the existing fallback code
     LayoutDeviceRect bounds = LayoutDeviceRect::FromAppUnits(mGroupBounds, aGrouper->mAppUnitsPerDevPixel);
+    bounds = LayoutDeviceRect(RoundedToInt(bounds));
+
     IntSize size = mGroupBounds.Size().ScaleToNearestPixels(mScale.width, mScale.height, aGrouper->mAppUnitsPerDevPixel);
 
     if (mInvalidRect.IsEmpty()) {
       GP("Not repainting group because it's empty\n");
       GP("End EndGroup\n");
       if (mKey) {
         PushImage(aBuilder, bounds);
       }
@@ -576,22 +579,23 @@ struct DIGroup
         aWrManager->AddImageKeyForDiscard(mKey.value());
         mKey = Nothing();
       }
       return;
     }
 
     PaintItemRange(aGrouper, aStartItem, aEndItem, context, recorder);
 
-    if (!mKey) {
-#if 0
+    if (aStartItem->Frame()->PresContext()->GetPaintFlashing()) {
       context->SetMatrix(Matrix());
-      dt->FillRect(gfx::Rect(0, 0, size.width, size.height), gfx::ColorPattern(gfx::Color(0., 1., 0., 0.5)));
+      float r = float(rand()) / RAND_MAX;
+      float g = float(rand()) / RAND_MAX;
+      float b = float(rand()) / RAND_MAX;
+      dt->FillRect(gfx::Rect(0, 0, size.width, size.height), gfx::ColorPattern(gfx::Color(r, g, b, 0.5)));
       dt->FlushItem(IntRect(IntPoint(0, 0), size));
-#endif
     }
 
     // XXX: set this correctly perhaps using aItem->GetOpaqueRegion(aDisplayListBuilder, &snapped).Contains(paintBounds);?
     bool isOpaque = false;
 
     bool hasItems = recorder->Finish();
     GP("%d Finish\n", hasItems);
     Range<uint8_t> bytes((uint8_t*)recorder->mOutputStream.mData, recorder->mOutputStream.mLength);
--- a/js/xpconnect/src/XPCShellImpl.cpp
+++ b/js/xpconnect/src/XPCShellImpl.cpp
@@ -1066,17 +1066,17 @@ XRE_XPCShellMain(int argc, char** argv, 
     nsresult rv;
 
     gErrFile = stderr;
     gOutFile = stdout;
     gInFile = stdin;
 
     NS_LogInit();
 
-    mozilla::LogModule::Init();
+    mozilla::LogModule::Init(argc, argv);
 
 #ifdef MOZ_GECKO_PROFILER
     char aLocal;
     profiler_init(&aLocal);
 #endif
 
     if (PR_GetEnv("MOZ_CHAOSMODE")) {
         ChaosFeature feature = ChaosFeature::Any;
new file mode 100644
--- /dev/null
+++ b/layout/base/crashtests/1443027-1.html
@@ -0,0 +1,20 @@
+<!DOCTYPE HTML>
+<html class="reftest-wait">
+<body>
+  <div id="first" style="position:absolute; width: 200px; height: 200px; background-color:blue; z-index: 1"></div>
+  <div style="position:absolute; left: 400px; width: 200px; height: 200px; background-color:green; z-index: 2"></div>
+  <div id="overlay" style="position:absolute; top: 100px; width: 600px; height: 200px; background-color:orange; z-index: 10"></div>
+</body>
+<script>
+  function doTest2() {
+    document.getElementById("overlay").style.zIndex= 11;
+    document.documentElement.removeAttribute('class');
+  }
+  function doTest() {
+    document.getElementById("first").style.zIndex = 3;
+    setTimeout(doTest2, 1000);
+  }
+
+  window.addEventListener("MozReftestInvalidate", doTest);
+</script>
+</html>
--- a/layout/base/crashtests/crashtests.list
+++ b/layout/base/crashtests/crashtests.list
@@ -522,9 +522,10 @@ load 1425893.html
 load 1428353.html
 pref(dom.webcomponents.shadowdom.enabled,true) load 1429088.html
 load 1429961.html
 load 1435015.html
 load 1429962.html
 pref(dom.webcomponents.shadowdom.enabled,true) load 1439016.html
 load 1442506.html
 load 1437155.html
+load 1443027-1.html
 load 1448841-1.html
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -3103,22 +3103,21 @@ nsIFrame::BuildDisplayListForStackingCon
     // repeating display list building if it changed.
 
     // If we changed whether we're going to build a blend mode item,
     // then we need to make sure we're marked as invalid and we've built
     // the full display list.
     if (aBuilder->ContainsBlendMode() != BuiltBlendContainer() &&
         aBuilder->IsRetainingDisplayList()) {
       SetBuiltBlendContainer(aBuilder->ContainsBlendMode());
-      aBuilder->MarkCurrentFrameModifiedDuringBuilding();
 
       // If we did a partial build then delete all the items we just built
       // and repeat building with the full area.
       if (!aBuilder->GetDirtyRect().Contains(aBuilder->GetVisibleRect())) {
-        aBuilder->SetDirtyRect(aBuilder->GetVisibleRect());
+        aBuilder->MarkCurrentFrameModifiedDuringBuilding();
         set.DeleteAll(aBuilder);
 
         if (eventRegions) {
           eventRegions->Destroy(aBuilder);
           eventRegions = MakeDisplayItem<nsDisplayLayerEventRegions>(aBuilder, this);
           eventRegions->AddFrame(aBuilder, this);
           aBuilder->SetLayerEventRegions(eventRegions);
         }
--- a/layout/inspector/InspectorFontFace.cpp
+++ b/layout/inspector/InspectorFontFace.cpp
@@ -8,16 +8,17 @@
 
 #include "gfxTextRun.h"
 #include "gfxUserFontSet.h"
 #include "nsFontFaceLoader.h"
 #include "mozilla/gfx/2D.h"
 #include "brotli/decode.h"
 #include "zlib.h"
 #include "mozilla/dom/FontFaceSet.h"
+#include "mozilla/ServoFontFaceRule.h"
 #include "mozilla/Unused.h"
 
 namespace mozilla {
 namespace dom {
 
 bool
 InspectorFontFace::FromFontGroup()
 {
@@ -48,33 +49,45 @@ InspectorFontFace::GetName(nsAString& aN
 }
 
 void
 InspectorFontFace::GetCSSFamilyName(nsAString& aCSSFamilyName)
 {
   aCSSFamilyName = mFontEntry->FamilyName();
 }
 
-nsCSSFontFaceRule*
+ServoFontFaceRule*
 InspectorFontFace::GetRule()
 {
-  // check whether this font entry is associated with an @font-face rule
-  // in the relevant font group's user font set
-  nsCSSFontFaceRule* rule = nullptr;
-  if (mFontEntry->IsUserFont()) {
-    FontFaceSet::UserFontSet* fontSet =
-      static_cast<FontFaceSet::UserFontSet*>(mFontGroup->GetUserFontSet());
-    if (fontSet) {
-      FontFaceSet* fontFaceSet = fontSet->GetFontFaceSet();
-      if (fontFaceSet) {
-        rule = fontFaceSet->FindRuleForEntry(mFontEntry);
+  if (!mRule) {
+    // check whether this font entry is associated with an @font-face rule
+    // in the relevant font group's user font set
+    RawServoFontFaceRule* rule = nullptr;
+    if (mFontEntry->IsUserFont()) {
+      FontFaceSet::UserFontSet* fontSet =
+        static_cast<FontFaceSet::UserFontSet*>(mFontGroup->GetUserFontSet());
+      if (fontSet) {
+        FontFaceSet* fontFaceSet = fontSet->GetFontFaceSet();
+        if (fontFaceSet) {
+          rule = fontFaceSet->FindRuleForEntry(mFontEntry);
+        }
       }
     }
+    if (rule) {
+      // XXX It would be better if we can share this with CSSOM tree,
+      // but that may require us to create another map, which is not
+      // great either. As far as they would use the same backend, and
+      // we don't really support mutating @font-face rule via CSSOM,
+      // it's probably fine for now.
+      uint32_t line, column;
+      Servo_FontFaceRule_GetSourceLocation(rule, &line, &column);
+      mRule = new ServoFontFaceRule(do_AddRef(rule), line, column);
+    }
   }
-  return rule;
+  return mRule;
 }
 
 int32_t
 InspectorFontFace::SrcIndex()
 {
   if (mFontEntry->IsUserFont()) {
     NS_ASSERTION(mFontEntry->mUserFontData, "missing userFontData");
     return mFontEntry->mUserFontData->mSrcIndex;
--- a/layout/inspector/InspectorFontFace.h
+++ b/layout/inspector/InspectorFontFace.h
@@ -2,23 +2,23 @@
 /* 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/. */
 
 #ifndef mozilla_InspectorFontFace_h
 #define mozilla_InspectorFontFace_h
 
+#include "mozilla/ServoFontFaceRule.h"
 #include "mozilla/dom/InspectorUtilsBinding.h"
 #include "mozilla/dom/NonRefcountedDOMObject.h"
 #include "nsRange.h"
 
 class gfxFontEntry;
 class gfxFontGroup;
-class nsCSSFontFaceRule;
 
 namespace mozilla {
 namespace dom {
 
 /**
  * Information on font face usage by a given DOM Range, as returned by
  * InspectorUtils.getUsedFontFaces.
  */
@@ -49,17 +49,17 @@ public:
   }
 
   // Web IDL
   bool FromFontGroup();
   bool FromLanguagePrefs();
   bool FromSystemFallback();
   void GetName(nsAString& aName);
   void GetCSSFamilyName(nsAString& aCSSFamilyName);
-  nsCSSFontFaceRule* GetRule();
+  ServoFontFaceRule* GetRule();
   int32_t SrcIndex();
   void GetURI(nsAString& aURI);
   void GetLocalName(nsAString& aLocalName);
   void GetFormat(nsAString& aFormat);
   void GetMetadata(nsAString& aMetadata);
 
   void GetVariationAxes(nsTArray<InspectorVariationAxis>& aResult,
                         ErrorResult& aRV);
@@ -75,16 +75,17 @@ public:
                   JS::MutableHandle<JSObject*> aReflector)
   {
     return InspectorFontFaceBinding::Wrap(aCx, this, aGivenProto, aReflector);
   }
 
 protected:
   RefPtr<gfxFontEntry> mFontEntry;
   RefPtr<gfxFontGroup> mFontGroup;
+  RefPtr<ServoFontFaceRule> mRule;
   uint8_t mMatchType;
 
   nsTArray<RefPtr<nsRange>> mRanges;
 };
 
 } // namespace dom
 } // namespace mozilla
 
--- a/layout/painting/RetainedDisplayListBuilder.cpp
+++ b/layout/painting/RetainedDisplayListBuilder.cpp
@@ -104,193 +104,85 @@ SelectAGRForFrame(nsIFrame* aFrame, Anim
 
 // Removes any display items that belonged to a frame that was deleted,
 // and mark frames that belong to a different AGR so that get their
 // items built again.
 // TODO: We currently descend into all children even if we don't have an AGR
 // to mark, as child stacking contexts might. It would be nice if we could
 // jump into those immediately rather than walking the entire thing.
 bool
-RetainedDisplayListBuilder::PreProcessDisplayList(nsDisplayList* aList,
+RetainedDisplayListBuilder::PreProcessDisplayList(RetainedDisplayList* aList,
                                                   AnimatedGeometryRoot* aAGR)
 {
-  bool modified = false;
+  // The DAG merging algorithm does not have strong mechanisms in place to keep the
+  // complexity of the resulting DAG under control. In some cases we can build up
+  // edges very quickly. Detect those cases and force a full display list build if
+  // we hit them.
+  static const uint32_t kMaxEdgeRatio = 5;
+  bool initializeDAG = !aList->mDAG.Length();
+  if (!initializeDAG &&
+      aList->mDAG.mDirectPredecessorList.Length() >
+      (aList->mDAG.mNodesInfo.Length() * kMaxEdgeRatio)) {
+    return false;
+
+  }
+
   nsDisplayList saved;
-  while (nsDisplayItem* i = aList->RemoveBottom()) {
-    if (i->HasDeletedFrame() || !i->CanBeReused()) {
-      i->Destroy(&mBuilder);
-      modified = true;
+  aList->mOldItems.SetCapacity(aList->Count());
+  size_t i = 0;
+  while (nsDisplayItem* item = aList->RemoveBottom()) {
+    if (item->HasDeletedFrame() || !item->CanBeReused()) {
+      // If we haven't yet initialized the DAG, then we can
+      // just drop this item. Otherwise we need to keep it
+      // around to preserve indices, and merging will
+      // get rid of it.
+      if (initializeDAG) {
+        item->Destroy(&mBuilder);
+      } else {
+        aList->mOldItems.AppendElement(OldItemInfo(item));
+      }
       continue;
     }
 
-    nsIFrame* f = i->Frame();
+    aList->mOldItems.AppendElement(OldItemInfo(item));
+    if (initializeDAG) {
+      if (i == 0) {
+        aList->mDAG.AddNode(Span<const MergedListIndex>());
+      } else {
+        MergedListIndex previous(i - 1);
+        aList->mDAG.AddNode(Span<const MergedListIndex>(&previous, 1));
+      }
 
-    if (i->GetChildren()) {
-      if (PreProcessDisplayList(i->GetChildren(), SelectAGRForFrame(f, aAGR))) {
-        modified = true;
+      aList->mKeyLookup.Put({ item->Frame(), item->GetPerFrameKey() }, i);
+      i++;
+    }
+
+    nsIFrame* f = item->Frame();
+
+    if (item->GetChildren()) {
+      if (!PreProcessDisplayList(item->GetChildren(), SelectAGRForFrame(f, aAGR))) {
+        mBuilder.MarkFrameForDisplayIfVisible(f, mBuilder.RootReferenceFrame());
+        mBuilder.MarkFrameModifiedDuringBuilding(f);
       }
     }
 
     // TODO: We should be able to check the clipped bounds relative
     // to the common AGR (of both the existing item and the invalidated
     // frame) and determine if they can ever intersect.
-    if (aAGR && i->GetAnimatedGeometryRoot()->GetAsyncAGR() != aAGR) {
+    if (aAGR && item->GetAnimatedGeometryRoot()->GetAsyncAGR() != aAGR) {
       mBuilder.MarkFrameForDisplayIfVisible(f, mBuilder.RootReferenceFrame());
-      modified = true;
     }
 
     // TODO: This is here because we sometimes reuse the previous display list
     // completely. For optimization, we could only restore the state for reused
     // display items.
-    i->RestoreState();
-
-    saved.AppendToTop(i);
-  }
-  aList->AppendToTop(&saved);
-  aList->RestoreState();
-  return modified;
-}
-
-struct DisplayItemKey
-{
-  bool operator ==(const DisplayItemKey& aOther) const {
-    return mFrame == aOther.mFrame &&
-           mPerFrameKey == aOther.mPerFrameKey;
-  }
-
-  nsIFrame* mFrame;
-  uint32_t mPerFrameKey;
-};
-
-class DisplayItemHashEntry : public PLDHashEntryHdr
-{
-public:
-  typedef DisplayItemKey KeyType;
-  typedef const DisplayItemKey* KeyTypePointer;
-
-  explicit DisplayItemHashEntry(KeyTypePointer aKey)
-    : mKey(*aKey) {}
-  explicit DisplayItemHashEntry(const DisplayItemHashEntry& aCopy)=default;
-
-  ~DisplayItemHashEntry() = default;
-
-  KeyType GetKey() const { return mKey; }
-  bool KeyEquals(KeyTypePointer aKey) const
-  {
-    return mKey == *aKey;
-  }
-
-  static KeyTypePointer KeyToPointer(KeyType& aKey) { return &aKey; }
-  static PLDHashNumber HashKey(KeyTypePointer aKey)
-  {
-    if (!aKey)
-      return 0;
-
-    return mozilla::HashGeneric(aKey->mFrame, aKey->mPerFrameKey);
-  }
-  enum { ALLOW_MEMMOVE = true };
-
-  DisplayItemKey mKey;
-};
-
-template<typename T>
-void SwapAndRemove(nsTArray<T>& aArray, uint32_t aIndex)
-{
-  if (aIndex != (aArray.Length() - 1)) {
-    T last = aArray.LastElement();
-    aArray.LastElement() = aArray[aIndex];
-    aArray[aIndex] = last;
+    item->RestoreState();
   }
-
-  aArray.RemoveLastElement();
-}
-
-static bool
-MergeFrameRects(nsDisplayLayerEventRegions* aOldItem,
-                nsDisplayLayerEventRegions* aNewItem,
-                nsDisplayLayerEventRegions::FrameRects nsDisplayLayerEventRegions::*aRectList,
-                nsTArray<nsIFrame*>& aAddedFrames)
-{
-  bool modified = false;
-  // Go through the old item's rect list and remove any rectangles
-  // belonging to invalidated frames (deleted frames should
-  // already be gone at this point)
-  nsDisplayLayerEventRegions::FrameRects& oldRects = aOldItem->*aRectList;
-  uint32_t i = 0;
-  while (i < oldRects.mFrames.Length()) {
-    // TODO: As mentioned in nsDisplayLayerEventRegions, this
-    // operation might perform really poorly on a vector.
-    nsIFrame* f = oldRects.mFrames[i];
-    if (IsAnyAncestorModified(f)) {
-      MOZ_ASSERT(f != aOldItem->Frame());
-      f->RemoveDisplayItem(aOldItem);
-      SwapAndRemove(oldRects.mFrames, i);
-      SwapAndRemove(oldRects.mBoxes, i);
-      modified = true;
-    } else {
-      i++;
-    }
-  }
-  if (!aNewItem) {
-    return modified;
-  }
-
-  // Copy items from the source list to the dest list, but
-  // only if the dest doesn't already include them.
-  nsDisplayItem* destItem = aOldItem;
-  nsDisplayLayerEventRegions::FrameRects* destRects = &(aOldItem->*aRectList);
-  nsDisplayLayerEventRegions::FrameRects* srcRects = &(aNewItem->*aRectList);
-
-  for (uint32_t i = 0; i < srcRects->mFrames.Length(); i++) {
-    nsIFrame* f = srcRects->mFrames[i];
-    if (!f->HasDisplayItem(destItem)) {
-      // If this frame isn't already in the destination item,
-      // then add it!
-      destRects->Add(f, srcRects->mBoxes[i]);
-
-      // We also need to update RealDisplayItemData for 'f',
-      // but that'll mess up this check for the following
-      // FrameRects lists, so defer that until the end.
-      aAddedFrames.AppendElement(f);
-      MOZ_ASSERT(f != aOldItem->Frame());
-
-      modified = true;
-    }
-
-  }
-  return modified;
-}
-
-bool MergeLayerEventRegions(nsDisplayItem* aOldItem,
-                            nsDisplayItem* aNewItem)
-{
-  nsDisplayLayerEventRegions* oldItem =
-    static_cast<nsDisplayLayerEventRegions*>(aOldItem);
-  nsDisplayLayerEventRegions* newItem =
-    static_cast<nsDisplayLayerEventRegions*>(aNewItem);
-
-  nsTArray<nsIFrame*> addedFrames;
-
-  bool modified = false;
-  modified |= MergeFrameRects(oldItem, newItem, &nsDisplayLayerEventRegions::mHitRegion, addedFrames);
-  modified |= MergeFrameRects(oldItem, newItem, &nsDisplayLayerEventRegions::mMaybeHitRegion, addedFrames);
-  modified |= MergeFrameRects(oldItem, newItem, &nsDisplayLayerEventRegions::mDispatchToContentHitRegion, addedFrames);
-  modified |= MergeFrameRects(oldItem, newItem, &nsDisplayLayerEventRegions::mNoActionRegion, addedFrames);
-  modified |= MergeFrameRects(oldItem, newItem, &nsDisplayLayerEventRegions::mHorizontalPanRegion, addedFrames);
-  modified |= MergeFrameRects(oldItem, newItem, &nsDisplayLayerEventRegions::mVerticalPanRegion, addedFrames);
-
-  // MergeFrameRects deferred updating the display item data list during
-  // processing so that earlier calls didn't change the result of later
-  // ones. Fix that up now.
-  for (nsIFrame* f : addedFrames) {
-    if (!f->HasDisplayItem(aOldItem)) {
-      f->AddDisplayItem(aOldItem);
-    }
-  }
-  return modified;
+  aList->RestoreState();
+  return true;
 }
 
 void
 RetainedDisplayListBuilder::IncrementSubDocPresShellPaintCount(nsDisplayItem* aItem)
 {
   MOZ_ASSERT(aItem->GetType() == DisplayItemType::TYPE_SUBDOCUMENT);
 
   nsSubDocumentFrame* subDocFrame =
@@ -317,276 +209,268 @@ UpdateASR(nsDisplayItem* aItem,
     return;
   }
 
   wrapList->SetActiveScrolledRoot(
     ActiveScrolledRoot::PickAncestor(wrapList->GetFrameActiveScrolledRoot(),
                                      aContainerASR.value()));
 }
 
+void
+OldItemInfo::AddedMatchToMergedList(RetainedDisplayListBuilder* aBuilder,
+                                    MergedListIndex aIndex)
+{
+  mItem->Destroy(aBuilder->Builder());
+  AddedToMergedList(aIndex);
+}
+
+void
+OldItemInfo::Discard(RetainedDisplayListBuilder* aBuilder,
+                     nsTArray<MergedListIndex>&& aDirectPredecessors)
+{
+  MOZ_ASSERT(!IsUsed());
+  mUsed = mDiscarded = true;
+  mDirectPredecessors = Move(aDirectPredecessors);
+  mItem->Destroy(aBuilder->Builder());
+  mItem = nullptr;
+}
+
 /**
- * Takes two display lists and merges them into an output list.
- *
- * The basic algorithm is:
- *
- * For-each item i in the new list:
- *     If the item has a matching item in the old list:
- *         Remove items from the start of the old list up until we reach an item that also exists in the new list (leaving the matched item in place):
- *             Add valid items to the merged list, destroy invalid items.
- *     Add i into the merged list.
- *     If the start of the old list matches i, remove and destroy it, otherwise mark the old version of i as used.
- * Add all remaining valid items from the old list into the merged list, skipping over (and destroying) any that are marked as used.
- *
- * If any item has a child display list, then we recurse into the merge
- * algorithm once we match up the new/old versions (if present).
- *
- * Example 1:
- *
- * Old List: A,B,C,D
- * Modified List: A,D
- * Invalidations: C,D
- *
- * We first match the A items, and add the new one to the merged list.
- * We then match the D items, copy B into the merged list, but not C
- * (since it's invalid). We then add the new D to the list and we're
- * finished.
- *
- * Merged List: A,B,D
- *
- * Example 2 (layout/reftests/retained-dl-zindex-1.html):
- *
- * Old List: A, B
- * Modified List: B, A
- * Invalidations: A
- *
- * In this example A has been explicitly moved to the back.
- *
- * We match the B items, but don't copy A since it's invalid, and then add the
- * new B into the merged list. We then add A, and we're done.
- *
- * Merged List: B, A
- *
- * Example 3:
+ * A C++ implementation of Markus Stange's merge-dags algorthim.
+ * https://github.com/mstange/merge-dags
  *
- * Old List: A, B
- * Modified List: B, A
- * Invalidations: -
- *
- * This can happen because a prior merge might have changed the ordering
- * for non-intersecting items.
- *
- * We match the B items, but don't copy A since it's also present in the new list
- * and then add the new B into the merged list. We then add A, and we're done.
- *
- * Merged List: B, A
- *
- * Example 4 (layout/reftests/retained-dl-zindex-2.html):
- *
- * Element A has two elements covering it (B and C), that don't intersect each
- * other. We then move C to the back.
- *
- * The correct initial ordering has B and C after A, in any order.
- *
- * Old List: A, B, C
- * Modified List: C, A
- * Invalidations: C
- *
- * We match the C items, but don't add anything from the old list because A is present
- * in both lists. We add C to the merged list, and mark the old version of C as reused.
- *
- * We then match A, add the new version the merged list and delete the old version.
- *
- * We then process the remainder of the old list, B is added (since it is valid,
- * and hasn't been mark as reused), C is destroyed since it's marked as reused and
- * is already present in the merged list.
- *
- * Merged List: C, A, B
+ * MergeState handles combining a new list of display items into an existing
+ * DAG and computes the new DAG in a single pass.
+ * Each time we add a new item, we resolve all dependencies for it, so that the resulting
+ * list and DAG are built in topological ordering.
  */
-bool
-RetainedDisplayListBuilder::MergeDisplayLists(nsDisplayList* aNewList,
-                                              nsDisplayList* aOldList,
-                                              nsDisplayList* aOutList,
-                                              Maybe<const ActiveScrolledRoot*>& aOutContainerASR)
-{
-  bool modified = false;
+class MergeState {
+public:
+  MergeState(RetainedDisplayListBuilder* aBuilder, RetainedDisplayList&& aOldList)
+    : mBuilder(aBuilder)
+    , mOldItems(Move(aOldList.mOldItems))
+    , mOldDAG(Move(*reinterpret_cast<DirectedAcyclicGraph<OldListUnits>*>(&aOldList.mDAG)))
+    , mResultIsModified(false)
+  {
+    mOldKeyLookup.SwapElements(aOldList.mKeyLookup);
+    mMergedDAG.EnsureCapacityFor(mOldDAG);
+  }
+
+  MergedListIndex ProcessItemFromNewList(nsDisplayItem* aNewItem, const Maybe<MergedListIndex>& aPreviousItem) {
+    OldListIndex oldIndex;
+    if (mOldKeyLookup.Get({ aNewItem->Frame(), aNewItem->GetPerFrameKey() }, &oldIndex.val)) {
+      if (!IsChanged(aNewItem)) {
+        MOZ_ASSERT(!mOldItems[oldIndex.val].IsUsed());
+        if (aNewItem->GetChildren()) {
+          Maybe<const ActiveScrolledRoot*> containerASRForChildren;
+          if (mBuilder->MergeDisplayLists(aNewItem->GetChildren(),
+                                          mOldItems[oldIndex.val].mItem->GetChildren(),
+                                          aNewItem->GetChildren(),
+                                          containerASRForChildren)) {
+            mResultIsModified = true;
+
+          }
+          UpdateASR(aNewItem, containerASRForChildren);
+          aNewItem->UpdateBounds(mBuilder->Builder());
+        }
 
-  nsDisplayList merged;
-  const auto UseItem = [&](nsDisplayItem* aItem) {
+        AutoTArray<MergedListIndex, 2> directPredecessors = ProcessPredecessorsOfOldNode(oldIndex);
+        MergedListIndex newIndex = AddNewNode(aNewItem, Some(oldIndex), directPredecessors, aPreviousItem);
+        mOldItems[oldIndex.val].AddedMatchToMergedList(mBuilder, newIndex);
+        return newIndex;
+      }
+    }
+    mResultIsModified = true;
+    return AddNewNode(aNewItem, Nothing(), Span<MergedListIndex>(), aPreviousItem);
+  }
+
+  RetainedDisplayList Finalize() {
+    for (size_t i = 0; i < mOldDAG.Length(); i++) {
+      if (mOldItems[i].IsUsed()) {
+        continue;
+      }
+
+      AutoTArray<MergedListIndex, 2> directPredecessors =
+        ResolveNodeIndexesOldToMerged(mOldDAG.GetDirectPredecessors(OldListIndex(i)));
+      ProcessOldNode(OldListIndex(i), Move(directPredecessors));
+    }
+
+    RetainedDisplayList result;
+    result.AppendToTop(&mMergedItems);
+    result.mDAG = Move(mMergedDAG);
+    result.mKeyLookup.SwapElements(mMergedKeyLookup);
+    return result;
+  }
+
+  bool IsChanged(nsDisplayItem* aItem) {
+    return aItem->HasDeletedFrame() || !aItem->CanBeReused() ||
+           IsAnyAncestorModified(aItem->FrameForInvalidation());
+  }
+
+  void UpdateContainerASR(nsDisplayItem* aItem)
+  {
     const ActiveScrolledRoot* itemClipASR =
       aItem->GetClipChain() ? aItem->GetClipChain()->mASR : nullptr;
 
     const ActiveScrolledRoot* finiteBoundsASR = ActiveScrolledRoot::PickDescendant(
       itemClipASR, aItem->GetActiveScrolledRoot());
-    if (!aOutContainerASR) {
-      aOutContainerASR = Some(finiteBoundsASR);
+    if (!mContainerASR) {
+      mContainerASR = Some(finiteBoundsASR);
     } else {
-      aOutContainerASR =
-        Some(ActiveScrolledRoot::PickAncestor(aOutContainerASR.value(), finiteBoundsASR));
-    }
-
-    merged.AppendToTop(aItem);
-  };
-
-  const auto ReuseItem = [&](nsDisplayItem* aItem) {
-    UseItem(aItem);
-    aItem->SetReused(true);
-
-    if (aItem->GetType() == DisplayItemType::TYPE_SUBDOCUMENT) {
-      IncrementSubDocPresShellPaintCount(aItem);
-    }
-  };
-
-  const bool newListIsEmpty = aNewList->IsEmpty();
-  if (!newListIsEmpty) {
-    // Build a hashtable of items in the old list so we can look for them quickly.
-    // We have similar data in the nsIFrame DisplayItems() property, but it doesn't
-    // know which display list items are in, and we only want to match items in
-    // this list.
-    nsDataHashtable<DisplayItemHashEntry, nsDisplayItem*> oldListLookup(aOldList->Count());
-
-    for (nsDisplayItem* i = aOldList->GetBottom(); i != nullptr; i = i->GetAbove()) {
-      i->SetReused(false);
-      oldListLookup.Put({ i->Frame(), i->GetPerFrameKey() }, i);
-    }
-
-    nsDataHashtable<DisplayItemHashEntry, nsDisplayItem*> newListLookup(aNewList->Count());
-    for (nsDisplayItem* i = aNewList->GetBottom(); i != nullptr; i = i->GetAbove()) {
-#ifdef DEBUG
-      if (newListLookup.Get({ i->Frame(), i->GetPerFrameKey() }, nullptr)) {
-        MOZ_CRASH_UNSAFE_PRINTF("Duplicate display items detected!: %s(0x%p) type=%d key=%d",
-                                  i->Name(), i->Frame(),
-                                  static_cast<int>(i->GetType()), i->GetPerFrameKey());
-      }
-#endif
-      newListLookup.Put({ i->Frame(), i->GetPerFrameKey() }, i);
+      mContainerASR = Some(ActiveScrolledRoot::PickAncestor(mContainerASR.value(), finiteBoundsASR));
     }
 
-    while (nsDisplayItem* newItem = aNewList->RemoveBottom()) {
-      if (nsDisplayItem* oldItem = oldListLookup.Get({ newItem->Frame(), newItem->GetPerFrameKey() })) {
-        // The new item has a matching counterpart in the old list that we haven't yet reached,
-        // so copy all valid items from the old list into the merged list until we get to the
-        // matched item.
-        nsDisplayItem* old = nullptr;
-        while ((old = aOldList->GetBottom()) && old != oldItem) {
-          if (IsAnyAncestorModified(old->FrameForInvalidation())) {
-            // The old item is invalid, discard it.
-            oldListLookup.Remove({ old->Frame(), old->GetPerFrameKey() });
-            aOldList->RemoveBottom();
-            old->Destroy(&mBuilder);
-            modified = true;
-          } else if (newListLookup.Get({ old->Frame(), old->GetPerFrameKey() })) {
-            // This old item is also in the new list, but we haven't got to it yet.
-            // Stop now, and we'll deal with it when we get to the new entry.
-            modified = true;
-            break;
-          } else {
-            // Recurse into the child list (without a matching new list) to
-            // ensure that we find and remove any invalidated items.
-            if (old->GetChildren()) {
-              nsDisplayList empty;
-              Maybe<const ActiveScrolledRoot*> containerASRForChildren;
-              if (MergeDisplayLists(&empty, old->GetChildren(),
-                                    old->GetChildren(), containerASRForChildren)) {
-                modified = true;
-              }
-              UpdateASR(old, containerASRForChildren);
-              old->UpdateBounds(&mBuilder);
-            }
-            aOldList->RemoveBottom();
-            ReuseItem(old);
-          }
+  }
+
+  MergedListIndex AddNewNode(nsDisplayItem* aItem,
+                             const Maybe<OldListIndex>& aOldIndex,
+                             Span<const MergedListIndex> aDirectPredecessors,
+                             const Maybe<MergedListIndex>& aExtraDirectPredecessor) {
+    UpdateContainerASR(aItem);
+    mMergedItems.AppendToTop(aItem);
+    MergedListIndex newIndex = mMergedDAG.AddNode(aDirectPredecessors, aExtraDirectPredecessor);
+    mMergedKeyLookup.Put({ aItem->Frame(), aItem->GetPerFrameKey() }, newIndex.val);
+    return newIndex;
+  }
+
+  void ProcessOldNode(OldListIndex aNode, nsTArray<MergedListIndex>&& aDirectPredecessors) {
+    nsDisplayItem* item = mOldItems[aNode.val].mItem;
+    if (IsChanged(item)) {
+      mOldItems[aNode.val].Discard(mBuilder, Move(aDirectPredecessors));
+      mResultIsModified = true;
+    } else {
+      if (item->GetChildren()) {
+        Maybe<const ActiveScrolledRoot*> containerASRForChildren;
+        nsDisplayList empty;
+        if (mBuilder->MergeDisplayLists(&empty, item->GetChildren(), item->GetChildren(),
+                                        containerASRForChildren)) {
+          mResultIsModified = true;
         }
-        bool destroy = false;
-        if (old == oldItem) {
-          // If we advanced the old list until the matching item then we can pop
-          // the matching item off the old list and make sure we clean it up.
-          aOldList->RemoveBottom();
-          destroy = true;
-        } else {
-          // If we didn't get to the matching item, then mark the old item
-          // as being reused (since we're adding the new version to the new
-          // list now) so that we don't add it twice at the end.
-          oldItem->SetReused(true);
-        }
+        UpdateASR(item, containerASRForChildren);
+        item->UpdateBounds(mBuilder->Builder());
+      }
+      if (item->GetType() == DisplayItemType::TYPE_SUBDOCUMENT) {
+        mBuilder->IncrementSubDocPresShellPaintCount(item);
+      }
+      item->SetReused(true);
+      mOldItems[aNode.val].AddedToMergedList(
+        AddNewNode(item, Some(aNode), aDirectPredecessors, Nothing()));
+    }
+  }
+
+  struct PredecessorStackItem {
+    PredecessorStackItem(OldListIndex aNode, Span<OldListIndex> aPredecessors)
+     : mNode(aNode)
+     , mDirectPredecessors(aPredecessors)
+     , mCurrentPredecessorIndex(0)
+    {}
+
+    bool IsFinished() {
+      return mCurrentPredecessorIndex == mDirectPredecessors.Length();
+    }
 
-        // Recursively merge any child lists, destroy the old item and add
-        // the new one to the list.
-        if (destroy &&
-            oldItem->GetType() == DisplayItemType::TYPE_LAYER_EVENT_REGIONS &&
-            !IsAnyAncestorModified(oldItem->FrameForInvalidation())) {
-          // Event regions items don't have anything interesting other than
-          // the lists of regions and frames, so we have no need to use the
-          // newer item. Always use the old item instead since we assume it's
-          // likely to have the bigger lists and merging will be quicker.
-          if (MergeLayerEventRegions(oldItem, newItem)) {
-            modified = true;
-          }
-          ReuseItem(oldItem);
-          newItem->Destroy(&mBuilder);
+    OldListIndex GetAndIncrementCurrentPredecessor() { return mDirectPredecessors[mCurrentPredecessorIndex++]; }
+
+    OldListIndex mNode;
+    Span<OldListIndex> mDirectPredecessors;
+    size_t mCurrentPredecessorIndex;
+  };
+
+  AutoTArray<MergedListIndex, 2> ProcessPredecessorsOfOldNode(OldListIndex aNode) {
+    AutoTArray<PredecessorStackItem,256> mStack;
+    mStack.AppendElement(PredecessorStackItem(aNode, mOldDAG.GetDirectPredecessors(aNode)));
+
+    while (true) {
+      if (mStack.LastElement().IsFinished()) {
+        // If we've finished processing all the entries in the current set, then pop
+        // it off the processing stack and process it.
+        PredecessorStackItem item = mStack.PopLastElement();
+        AutoTArray<MergedListIndex,2> result =
+          ResolveNodeIndexesOldToMerged(item.mDirectPredecessors);
+        if (mStack.IsEmpty()) {
+          return result;
         } else {
-          if (IsAnyAncestorModified(oldItem->FrameForInvalidation())) {
-            modified = true;
-          } else if (oldItem->GetChildren()) {
-            MOZ_ASSERT(newItem->GetChildren());
-            Maybe<const ActiveScrolledRoot*> containerASRForChildren;
-            if (MergeDisplayLists(newItem->GetChildren(), oldItem->GetChildren(),
-                                  newItem->GetChildren(), containerASRForChildren)) {
-              modified = true;
-            }
-            UpdateASR(newItem, containerASRForChildren);
-            newItem->UpdateBounds(&mBuilder);
-          }
-
-          if (destroy) {
-            oldItem->Destroy(&mBuilder);
-          }
-          UseItem(newItem);
+          ProcessOldNode(item.mNode, Move(result));
         }
       } else {
-        // If there was no matching item in the old list, then we only need to
-        // add the new item to the merged list.
-        modified = true;
-        UseItem(newItem);
+        // Grab the current predecessor, push predecessors of that onto the processing
+        // stack (if it hasn't already been processed), and then advance to the next entry.
+        OldListIndex currentIndex = mStack.LastElement().GetAndIncrementCurrentPredecessor();
+        if (!mOldItems[currentIndex.val].IsUsed()) {
+          mStack.AppendElement(
+            PredecessorStackItem(currentIndex, mOldDAG.GetDirectPredecessors(currentIndex)));
+        }
       }
     }
   }
 
-  // Reuse the remaining valid items from the old display list.
-  while (nsDisplayItem* old = aOldList->RemoveBottom()) {
-    if (!IsAnyAncestorModified(old->FrameForInvalidation()) &&
-        (!old->IsReused() || newListIsEmpty)) {
-      if (old->GetChildren()) {
-        // We are calling MergeDisplayLists() to ensure that the display items
-        // with modified or deleted children will be correctly handled.
-        // Passing an empty new display list as an argument skips the merging
-        // loop above and jumps back here.
-        nsDisplayList empty;
-        Maybe<const ActiveScrolledRoot*> containerASRForChildren;
-
-        if (MergeDisplayLists(&empty, old->GetChildren(),
-                              old->GetChildren(), containerASRForChildren)) {
-          modified = true;
+  AutoTArray<MergedListIndex, 2> ResolveNodeIndexesOldToMerged(Span<OldListIndex> aDirectPredecessors) {
+    AutoTArray<MergedListIndex, 2> result;
+    result.SetCapacity(aDirectPredecessors.Length());
+    for (OldListIndex index : aDirectPredecessors) {
+      OldItemInfo& oldItem = mOldItems[index.val];
+      if (oldItem.IsDiscarded()) {
+        for (MergedListIndex inner : oldItem.mDirectPredecessors) {
+          if (!result.Contains(inner)) {
+            result.AppendElement(inner);
+          }
         }
-        UpdateASR(old, containerASRForChildren);
-        old->UpdateBounds(&mBuilder);
+      } else {
+        result.AppendElement(oldItem.mIndex);
       }
-      if (old->GetType() == DisplayItemType::TYPE_LAYER_EVENT_REGIONS) {
-        if (MergeLayerEventRegions(old, nullptr)) {
-          modified = true;
-        }
-      }
-      ReuseItem(old);
-    } else {
-      old->Destroy(&mBuilder);
-      modified = true;
     }
+    return result;
   }
 
-  aOutList->AppendToTop(&merged);
-  return modified;
+  RetainedDisplayListBuilder* mBuilder;
+  Maybe<const ActiveScrolledRoot*> mContainerASR;
+  nsTArray<OldItemInfo> mOldItems;
+  DirectedAcyclicGraph<OldListUnits> mOldDAG;
+  // Unfortunately we can't use strong typing for the hashtables
+  // since they internally encode the type with the mOps pointer,
+  // and assert when we try swap the contents
+  nsDataHashtable<DisplayItemHashEntry, size_t> mOldKeyLookup;
+  nsDisplayList mMergedItems;
+  DirectedAcyclicGraph<MergedListUnits> mMergedDAG;
+  nsDataHashtable<DisplayItemHashEntry, size_t> mMergedKeyLookup;
+  bool mResultIsModified;
+};
+
+void RetainedDisplayList::ClearDAG()
+{
+  mDAG.Clear();
+  mKeyLookup.Clear();
+}
+
+/**
+ * Takes two display lists and merges them into an output list.
+ *
+ * Display lists wthout an explicit DAG are interpreted as linear DAGs (with a maximum
+ * of one direct predecessor and one direct successor per node). We add the two DAGs
+ * together, and then output the topological sorted ordering as the final display list.
+ *
+ * Once we've merged a list, we then retain the DAG (as part of the RetainedDisplayList
+ * object) to use for future merges.
+ */
+bool
+RetainedDisplayListBuilder::MergeDisplayLists(nsDisplayList* aNewList,
+                                              RetainedDisplayList* aOldList,
+                                              RetainedDisplayList* aOutList,
+                                              mozilla::Maybe<const mozilla::ActiveScrolledRoot*>& aOutContainerASR)
+{
+  MergeState merge(this, Move(*aOldList));
+
+  Maybe<MergedListIndex> previousItemIndex;
+  while (nsDisplayItem* item = aNewList->RemoveBottom()) {
+    previousItemIndex = Some(merge.ProcessItemFromNewList(item, previousItemIndex));
+  }
+
+  *aOutList = Move(merge.Finalize());
+  aOutContainerASR = merge.mContainerASR;
+  return merge.mResultIsModified;
 }
 
 static void
 TakeAndAddModifiedAndFramesWithPropsFromRootFrame(
   nsTArray<nsIFrame*>* aModifiedFrames,
   nsTArray<nsIFrame*>* aFramesWithProps,
   nsIFrame* aRootFrame)
 {
@@ -1104,17 +988,25 @@ RetainedDisplayListBuilder::AttemptParti
   // nsLayoutUtils::InvalidateForDisplayPortChange. The display port change also
   // marks the frame modified, so those regions are cleared here as well.
   AutoClearFramePropsArray modifiedFrames;
   AutoClearFramePropsArray framesWithProps;
   GetModifiedAndFramesWithProps(&mBuilder, &modifiedFrames.Frames(), &framesWithProps.Frames());
 
   // Do not allow partial builds if the retained display list is empty, or if
   // ShouldBuildPartial heuristic fails.
-  const bool shouldBuildPartial = !mList.IsEmpty() && ShouldBuildPartial(modifiedFrames.Frames());
+  bool shouldBuildPartial = !mList.IsEmpty() && ShouldBuildPartial(modifiedFrames.Frames());
+
+  // We don't support retaining with overlay scrollbars, since they require
+  // us to look at the display list and pick the highest z-index, which
+  // we can't do during partial building.
+  if (mBuilder.BuiltOverlayScrollbars()) {
+    shouldBuildPartial = false;
+    mBuilder.SetBuiltOverlayScrollbars(false);
+  }
 
   if (mPreviousCaret != mBuilder.GetCaretFrame()) {
     if (mPreviousCaret) {
       if (mBuilder.MarkFrameModifiedDuringBuilding(mPreviousCaret)) {
         modifiedFrames.Frames().AppendElement(mPreviousCaret);
       }
     }
 
@@ -1126,26 +1018,27 @@ RetainedDisplayListBuilder::AttemptParti
 
     mPreviousCaret = mBuilder.GetCaretFrame();
   }
 
   nsRect modifiedDirty;
   AnimatedGeometryRoot* modifiedAGR = nullptr;
   if (!shouldBuildPartial ||
       !ComputeRebuildRegion(modifiedFrames.Frames(), &modifiedDirty,
-                           &modifiedAGR, framesWithProps.Frames())) {
-    mBuilder.LeavePresShell(mBuilder.RootReferenceFrame(), &mList);
+                           &modifiedAGR, framesWithProps.Frames()) ||
+      !PreProcessDisplayList(&mList, modifiedAGR)) {
+    mBuilder.LeavePresShell(mBuilder.RootReferenceFrame(), List());
+    mList.ClearDAG();
     return PartialUpdateResult::Failed;
   }
 
   modifiedDirty.IntersectRect(modifiedDirty, mBuilder.RootReferenceFrame()->GetVisualOverflowRectRelativeToSelf());
 
   PartialUpdateResult result = PartialUpdateResult::NoChange;
-  if (PreProcessDisplayList(&mList, modifiedAGR) ||
-      !modifiedDirty.IsEmpty() ||
+  if (!modifiedDirty.IsEmpty() ||
       !framesWithProps.IsEmpty()) {
     result = PartialUpdateResult::Updated;
   }
 
   mBuilder.SetDirtyRect(modifiedDirty);
   mBuilder.SetPartialUpdate(true);
 
   nsDisplayList modifiedDL;
@@ -1178,11 +1071,11 @@ RetainedDisplayListBuilder::AttemptParti
   Maybe<const ActiveScrolledRoot*> dummy;
   if (MergeDisplayLists(&modifiedDL, &mList, &mList, dummy)) {
     result = PartialUpdateResult::Updated;
   }
 
   //printf_stderr("Painting --- Merged list:\n");
   //nsFrame::PrintDisplayList(&mBuilder, mList);
 
-  mBuilder.LeavePresShell(mBuilder.RootReferenceFrame(), &mList);
+  mBuilder.LeavePresShell(mBuilder.RootReferenceFrame(), List());
   return result;
 }
--- a/layout/painting/RetainedDisplayListBuilder.h
+++ b/layout/painting/RetainedDisplayListBuilder.h
@@ -44,29 +44,30 @@ struct RetainedDisplayListBuilder {
    * Also clears the frame properties set by RetainedDisplayListBuilder for all
    * the frames in the modified frame lists.
    */
   void ClearFramesWithProps();
 
   NS_DECLARE_FRAME_PROPERTY_DELETABLE(Cached, RetainedDisplayListBuilder)
 
 private:
-  bool PreProcessDisplayList(nsDisplayList* aList, AnimatedGeometryRoot* aAGR);
-
+  bool PreProcessDisplayList(RetainedDisplayList* aList, AnimatedGeometryRoot* aAGR);
   bool MergeDisplayLists(nsDisplayList* aNewList,
-                         nsDisplayList* aOldList,
-                         nsDisplayList* aOutList,
+                         RetainedDisplayList* aOldList,
+                         RetainedDisplayList* aOutList,
                          mozilla::Maybe<const mozilla::ActiveScrolledRoot*>& aOutContainerASR);
 
   bool ComputeRebuildRegion(nsTArray<nsIFrame*>& aModifiedFrames,
                             nsRect* aOutDirty,
                             AnimatedGeometryRoot** aOutModifiedAGR,
                             nsTArray<nsIFrame*>& aOutFramesWithProps);
 
   void IncrementSubDocPresShellPaintCount(nsDisplayItem* aItem);
 
+  friend class MergeState;
+
   nsDisplayListBuilder mBuilder;
-  nsDisplayList mList;
+  RetainedDisplayList mList;
   WeakFrame mPreviousCaret;
 
 };
 
 #endif // RETAINEDDISPLAYLISTBUILDER_H_
new file mode 100644
--- /dev/null
+++ b/layout/painting/RetainedDisplayListHelpers.h
@@ -0,0 +1,199 @@
+/* -*- 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/. */
+
+#ifndef RETAINEDDISPLAYLISTHELPERS_H_
+#define RETAINEDDISPLAYLISTHELPERS_H_
+
+#include "PLDHashTable.h"
+
+struct DisplayItemKey
+{
+  bool operator ==(const DisplayItemKey& aOther) const {
+    return mFrame == aOther.mFrame &&
+           mPerFrameKey == aOther.mPerFrameKey;
+  }
+
+  nsIFrame* mFrame;
+  uint32_t mPerFrameKey;
+};
+
+class DisplayItemHashEntry : public PLDHashEntryHdr
+{
+public:
+  typedef DisplayItemKey KeyType;
+  typedef const DisplayItemKey* KeyTypePointer;
+
+  explicit DisplayItemHashEntry(KeyTypePointer aKey)
+    : mKey(*aKey) {}
+  explicit DisplayItemHashEntry(const DisplayItemHashEntry& aCopy)=default;
+
+  ~DisplayItemHashEntry() = default;
+
+  KeyType GetKey() const { return mKey; }
+  bool KeyEquals(KeyTypePointer aKey) const
+  {
+    return mKey == *aKey;
+  }
+
+  static KeyTypePointer KeyToPointer(KeyType& aKey) { return &aKey; }
+  static PLDHashNumber HashKey(KeyTypePointer aKey)
+  {
+    if (!aKey)
+      return 0;
+
+    return mozilla::HashGeneric(aKey->mFrame, aKey->mPerFrameKey);
+  }
+  enum { ALLOW_MEMMOVE = true };
+
+  DisplayItemKey mKey;
+};
+
+template <typename T>
+bool SpanContains(mozilla::Span<const T>& aSpan, T aItem)
+{
+  for (const T& i : aSpan) {
+    if (i == aItem) {
+      return true;
+    }
+  }
+  return false;
+}
+
+class OldListUnits {};
+class MergedListUnits {};
+
+template <typename Units>
+struct Index {
+  Index()
+    : val(0)
+  {}
+  explicit Index(size_t aVal)
+    : val(aVal)
+  {}
+
+  bool operator==(const Index<Units>& aOther) const
+  {
+    return val == aOther.val;
+  }
+
+  size_t val;
+};
+typedef Index<OldListUnits> OldListIndex;
+typedef Index<MergedListUnits> MergedListIndex;
+
+
+template <typename T>
+class DirectedAcyclicGraph {
+public:
+  DirectedAcyclicGraph() {}
+  DirectedAcyclicGraph(DirectedAcyclicGraph&& aOther)
+    : mNodesInfo(mozilla::Move(aOther.mNodesInfo))
+    , mDirectPredecessorList(mozilla::Move(aOther.mDirectPredecessorList))
+  {}
+
+  DirectedAcyclicGraph& operator=(DirectedAcyclicGraph&& aOther)
+  {
+    mNodesInfo = mozilla::Move(aOther.mNodesInfo);
+    mDirectPredecessorList = mozilla::Move(aOther.mDirectPredecessorList);
+    return *this;
+  }
+
+  Index<T> AddNode(mozilla::Span<const Index<T>> aDirectPredecessors,
+                   const mozilla::Maybe<Index<T>>& aExtraPredecessor = mozilla::Nothing())
+  {
+    size_t index = mNodesInfo.Length();
+    mNodesInfo.AppendElement(NodeInfo(mDirectPredecessorList.Length(), aDirectPredecessors.Length()));
+    if (aExtraPredecessor && !SpanContains(aDirectPredecessors, aExtraPredecessor.value())) {
+      mNodesInfo.LastElement().mDirectPredecessorCount++;
+      mDirectPredecessorList.SetCapacity(mDirectPredecessorList.Length() + aDirectPredecessors.Length() + 1);
+      mDirectPredecessorList.AppendElements(aDirectPredecessors);
+      mDirectPredecessorList.AppendElement(aExtraPredecessor.value());
+    } else {
+      mDirectPredecessorList.AppendElements(aDirectPredecessors);
+    }
+    return Index<T>(index);
+  }
+
+  size_t Length()
+  {
+    return mNodesInfo.Length();
+  }
+
+  mozilla::Span<Index<T>> GetDirectPredecessors(Index<T> aNodeIndex)
+  {
+    NodeInfo& node = mNodesInfo[aNodeIndex.val];
+    return mozilla::MakeSpan(mDirectPredecessorList).Subspan(node.mIndexInDirectPredecessorList,
+                                                             node.mDirectPredecessorCount);
+  }
+
+  template<typename OtherUnits>
+  void EnsureCapacityFor(const DirectedAcyclicGraph<OtherUnits>& aOther)
+  {
+    mNodesInfo.SetCapacity(aOther.mNodesInfo.Length());
+    mDirectPredecessorList.SetCapacity(aOther.mDirectPredecessorList.Length());
+  }
+
+  void Clear()
+  {
+    mNodesInfo.Clear();
+    mDirectPredecessorList.Clear();
+  }
+
+  struct NodeInfo {
+    NodeInfo(size_t aIndexInDirectPredecessorList,
+             size_t aDirectPredecessorCount)
+      : mIndexInDirectPredecessorList(aIndexInDirectPredecessorList)
+      , mDirectPredecessorCount(aDirectPredecessorCount)
+    {}
+    size_t mIndexInDirectPredecessorList;
+    size_t mDirectPredecessorCount;
+  };
+
+  nsTArray<NodeInfo> mNodesInfo;
+  nsTArray<Index<T>> mDirectPredecessorList;
+};
+
+struct RetainedDisplayListBuilder;
+class nsDisplayItem;
+
+struct OldItemInfo {
+  explicit OldItemInfo(nsDisplayItem* aItem)
+    : mItem(aItem)
+    , mUsed(false)
+    , mDiscarded(false)
+  {}
+
+  void AddedToMergedList(MergedListIndex aIndex)
+  {
+    MOZ_ASSERT(!IsUsed());
+    mUsed = true;
+    mIndex = aIndex;
+    mItem = nullptr;
+  }
+
+  void AddedMatchToMergedList(RetainedDisplayListBuilder* aBuilder,
+                              MergedListIndex aIndex);
+  void Discard(RetainedDisplayListBuilder* aBuilder,
+               nsTArray<MergedListIndex>&& aDirectPredecessors);
+  bool IsUsed()
+  {
+    return mUsed;
+  }
+
+  bool IsDiscarded()
+  {
+    MOZ_ASSERT(IsUsed());
+    return mDiscarded;
+  }
+
+  nsDisplayItem* mItem;
+  bool mUsed;
+  bool mDiscarded;
+  MergedListIndex mIndex;
+  nsTArray<MergedListIndex> mDirectPredecessors;
+};
+
+#endif // RETAINEDDISPLAYLISTHELPERS_H_
--- a/layout/painting/moz.build
+++ b/layout/painting/moz.build
@@ -17,16 +17,17 @@ EXPORTS += [
     'nsCSSRenderingBorders.h',
     'nsCSSRenderingGradients.h',
     'nsDisplayItemTypes.h',
     'nsDisplayItemTypesList.h',
     'nsDisplayList.h',
     'nsDisplayListInvalidation.h',
     'nsImageRenderer.h',
     'RetainedDisplayListBuilder.h',
+    'RetainedDisplayListHelpers.h',
 ]
 
 EXPORTS.mozilla += [
     'PaintTracker.h',
 ]
 
 UNIFIED_SOURCES += [
     'ActiveLayerTracker.cpp',
--- a/layout/painting/nsDisplayList.h
+++ b/layout/painting/nsDisplayList.h
@@ -39,16 +39,17 @@
 #include "mozilla/UniquePtr.h"
 #include "mozilla/TimeStamp.h"
 #include "mozilla/gfx/UserData.h"
 #include "mozilla/layers/LayerAttributes.h"
 #include "nsCSSRenderingBorders.h"
 #include "nsPresArena.h"
 #include "nsAutoLayoutPhase.h"
 #include "nsDisplayItemTypes.h"
+#include "RetainedDisplayListHelpers.h"
 
 #include <stdint.h>
 #include "nsTHashtable.h"
 
 #include <stdlib.h>
 #include <algorithm>
 #include <unordered_set>
 
@@ -1091,21 +1092,21 @@ public:
       if (aBuilder->mCurrentFrame == aForChild->GetParent()) {
         if (mCurrentAGRState == AGR_YES) {
           aBuilder->mCurrentAGR = aBuilder->WrapAGRForFrame(aForChild, isAsync, aBuilder->mCurrentAGR);
         }
       } else if (aForChild != aBuilder->mCurrentFrame) {
         aBuilder->mCurrentAGR = aBuilder->FindAnimatedGeometryRootFor(aForChild);
       }
       MOZ_ASSERT(nsLayoutUtils::IsAncestorFrameCrossDoc(aBuilder->RootReferenceFrame(), *aBuilder->mCurrentAGR));
+      aBuilder->mInInvalidSubtree = aBuilder->mInInvalidSubtree || aForChild->IsFrameModified();
       aBuilder->mCurrentFrame = aForChild;
       aBuilder->mVisibleRect = aVisibleRect;
-      aBuilder->mDirtyRect = aDirtyRect;
+      aBuilder->mDirtyRect = aBuilder->mInInvalidSubtree ? aVisibleRect : aDirtyRect;
       aBuilder->mIsAtRootOfPseudoStackingContext = aIsRoot;
-      aBuilder->mInInvalidSubtree = aBuilder->mInInvalidSubtree || aForChild->IsFrameModified();
     }
     void SetReferenceFrameAndCurrentOffset(const nsIFrame* aFrame, const nsPoint& aOffset) {
       mBuilder->mCurrentReferenceFrame = aFrame;
       mBuilder->mCurrentOffsetToReferenceFrame = aOffset;
     }
     bool IsAnimatedGeometryRoot() const {
       return mCurrentAGRState == AGR_YES;
     }
@@ -1735,16 +1736,17 @@ public:
     }
     return false;
   }
 
   bool MarkCurrentFrameModifiedDuringBuilding()
   {
     if (MarkFrameModifiedDuringBuilding(const_cast<nsIFrame*>(mCurrentFrame))) {
       mInInvalidSubtree = true;
+      mDirtyRect = mVisibleRect;
       return true;
     }
     return false;
   }
 
   /**
    * This is a convenience function to ease the transition until AGRs and ASRs
    * are unified.
@@ -2011,16 +2013,17 @@ private:
   bool                           mInInvalidSubtree;
   bool                           mBuildCompositorHitTestInfo;
   bool                           mLessEventRegionItems;
   bool                           mBuiltOverlayScrollbars;
 };
 
 class nsDisplayItem;
 class nsDisplayList;
+class RetainedDisplayList;
 /**
  * nsDisplayItems are put in singly-linked lists rooted in an nsDisplayList.
  * nsDisplayItemLink holds the link. The lists are linked from lowest to
  * highest in z-order.
  */
 class nsDisplayItemLink {
   // This is never instantiated directly, so no need to count constructors and
   // destructors.
@@ -2650,17 +2653,17 @@ public:
    * the same 3d rendering context.
    */
   virtual void DoUpdateBoundsPreserves3D(nsDisplayListBuilder* aBuilder) {}
 
   /**
    * If this has a child list, return it, even if the children are in
    * a different coordinate system to this item.
    */
-  virtual nsDisplayList* GetChildren() const { return nullptr; }
+  virtual RetainedDisplayList* GetChildren() const { return nullptr; }
 
   /**
    * Returns the visible rect.
    */
   const nsRect& GetVisibleRect() const { return mVisibleRect; }
 
   void SetVisibleRect(const nsRect& aVisibleRect, bool aStore)
   {
@@ -3321,16 +3324,69 @@ struct nsDisplayListCollection : public 
 private:
   // This class is only used on stack, so we don't have to worry about leaking
   // it.  Don't let us be heap-allocated!
   void* operator new(size_t sz) CPP_THROW_NEW;
 
   nsDisplayList mLists[6];
 };
 
+/**
+ * A display list that also retains the partial build
+ * information (in the form of a DAG) used to create it.
+ *
+ * Display lists built from a partial list aren't necessarily
+ * in the same order as a full build, and the DAG retains
+ * the information needing to interpret the current
+ * order correctly.
+ */
+class RetainedDisplayList : public nsDisplayList {
+public:
+  RetainedDisplayList() {}
+  RetainedDisplayList(RetainedDisplayList&& aOther)
+  {
+    AppendToTop(&aOther);
+    mDAG = mozilla::Move(aOther.mDAG);
+    mKeyLookup.SwapElements(aOther.mKeyLookup);
+  }
+  ~RetainedDisplayList()
+  {
+    MOZ_ASSERT(mOldItems.IsEmpty(), "Must empty list before destroying");
+  }
+
+  RetainedDisplayList& operator=(RetainedDisplayList&& aOther)
+  {
+    MOZ_ASSERT(!Count(), "Can only move into an empty list!");
+    MOZ_ASSERT(mOldItems.IsEmpty(), "Can only move into an empty list!");
+    AppendToTop(&aOther);
+    mDAG = mozilla::Move(aOther.mDAG);
+    mKeyLookup.SwapElements(aOther.mKeyLookup);
+    return *this;
+  }
+
+  void DeleteAll(nsDisplayListBuilder* aBuilder)
+  {
+    for (OldItemInfo& i : mOldItems) {
+      if (i.mItem) {
+        i.mItem->Destroy(aBuilder);
+      }
+    }
+    mOldItems.Clear();
+    nsDisplayList::DeleteAll(aBuilder);
+  }
+
+  void ClearDAG();
+
+  DirectedAcyclicGraph<MergedListUnits> mDAG;
+  nsDataHashtable<DisplayItemHashEntry, size_t> mKeyLookup;
+
+  // Temporary state initialized during the preprocess pass
+  // of RetainedDisplayListBuilder and then used during merging.
+  nsTArray<OldItemInfo> mOldItems;
+};
 
 class nsDisplayImageContainer : public nsDisplayItem {
 public:
   typedef mozilla::LayerIntPoint LayerIntPoint;
   typedef mozilla::LayoutDeviceRect LayoutDeviceRect;
   typedef mozilla::layers::ImageContainer ImageContainer;
   typedef mozilla::layers::ImageLayer ImageLayer;
 
@@ -5000,17 +5056,17 @@ public:
   {
     NS_ASSERTION(mListPtr->IsEmpty() || !ReferenceFrame() ||
                  !mListPtr->GetBottom()->ReferenceFrame() ||
                  mListPtr->GetBottom()->ReferenceFrame() == ReferenceFrame(),
                  "Children must have same reference frame");
     return mListPtr;
   }
 
-  virtual nsDisplayList* GetChildren() const override { return mListPtr; }
+  virtual RetainedDisplayList* GetChildren() const override { return mListPtr; }
 
   virtual int32_t ZIndex() const override
   {
     return (mHasZIndexOverride) ? mOverrideZIndex : nsDisplayItem::ZIndex();
   }
 
   void SetOverrideZIndex(int32_t aZIndex)
   {
@@ -5046,18 +5102,18 @@ protected:
   void MergeFromTrackingMergedFrames(const nsDisplayWrapList* aOther)
   {
     mBounds.UnionRect(mBounds, aOther->mBounds);
     mVisibleRect.UnionRect(mVisibleRect, aOther->mVisibleRect);
     mMergedFrames.AppendElement(aOther->mFrame);
     mMergedFrames.AppendElements(aOther->mMergedFrames);
   }
 
-  nsDisplayList mList;
-  nsDisplayList* mListPtr;
+  RetainedDisplayList mList;
+  RetainedDisplayList* mListPtr;
   // The active scrolled root for the frame that created this
   // wrap list.
   RefPtr<const ActiveScrolledRoot> mFrameActiveScrolledRoot;
   // The frames from items that have been merged into this item, excluding
   // this item's own frame.
   nsTArray<nsIFrame*> mMergedFrames;
   nsRect mBounds;
   // Visible rect contributed by this display item itself.
@@ -6181,17 +6237,17 @@ public:
   virtual nsRect GetComponentAlphaBounds(nsDisplayListBuilder* aBuilder) const override
   {
     if (mStoredList.GetComponentAlphaBounds(aBuilder).IsEmpty())
       return nsRect();
     bool snap;
     return GetBounds(aBuilder, &snap);
   }
 
-  virtual nsDisplayList* GetChildren() const override
+  virtual RetainedDisplayList* GetChildren() const override
   {
     return mStoredList.GetChildren();
   }
 
   virtual void SetActiveScrolledRoot(const ActiveScrolledRoot* aActiveScrolledRoot) override
   {
     nsDisplayItem::SetActiveScrolledRoot(aActiveScrolledRoot);
     mStoredList.SetActiveScrolledRoot(aActiveScrolledRoot);
@@ -6581,17 +6637,17 @@ public:
     return true;
   }
 
   virtual nsDisplayList* GetSameCoordinateSystemChildren() const override
   {
     return mList.GetChildren();
   }
 
-  virtual nsDisplayList* GetChildren() const override
+  virtual RetainedDisplayList* GetChildren() const override
   {
     return mList.GetChildren();
   }
 
   virtual void SetActiveScrolledRoot(const ActiveScrolledRoot* aActiveScrolledRoot) override
   {
     nsDisplayItem::SetActiveScrolledRoot(aActiveScrolledRoot);
     mList.SetActiveScrolledRoot(aActiveScrolledRoot);
new file mode 100644
--- /dev/null
+++ b/layout/reftests/display-list/1443027-1.html
@@ -0,0 +1,93 @@
+<!DOCTYPE html>
+<html lang="en" class="reftest-wait"><head>
+<meta http-equiv="content-type" content="text/html; charset=UTF-8"><meta charset="utf-8">
+<title>Bug 1443027 - Test merging across multiple paints</title>
+
+<style>
+
+body { opacity: 0.9; }
+
+div {
+  position: absolute;
+}
+
+#A {
+  left: 250px;
+  top: 50px;
+  width: 100px;
+  height: 100px;
+  background-color: red;
+}
+
+#B {
+  left: 200px;
+  top: 0px;
+  width: 100px;
+  height: 100px;
+  background-color: yellow;
+}
+
+#C {
+  left: 0px;
+  top: 0px;
+  width: 100px;
+  height: 100px;
+  background-color: green;
+}
+
+#D {
+  left: 80px;
+  top: 20px;
+  width: 140px;
+  height: 100px;
+  background-color: blue;
+}
+
+</style>
+</head>
+<body>
+<div id="A"></div>
+<div id="B"></div>
+<div id="C"></div>
+<div id="D"></div>
+
+<script>
+
+var A = document.getElementById("A");
+var B = document.getElementById("B");
+var C = document.getElementById("C");
+var D = document.getElementById("D");
+
+A.style.visibility = "hidden";
+B.style.visibility = "hidden";
+C.style.visibility = "hidden";
+D.style.visibility = "hidden";
+
+window.addEventListener("MozReftestInvalidate", step1);
+
+function step1() {
+  C.style.visibility = "visible";
+  C.style.transform = "translatez(1px)";
+  D.style.visibility = "visible";
+  D.style.transform = "translatez(1px)";
+
+  window.requestAnimationFrame(function() { window.requestAnimationFrame(step2); });
+}
+
+function step2() {
+  A.style.visibility = "visible";
+  A.style.transform = "translatez(1px)";
+  B.style.visibility = "visible";
+  B.style.transform = "translatez(1px)";
+
+  window.requestAnimationFrame(step3);
+}
+
+function step3() {
+  D.style.visibility = "hidden";
+  D.style.transform = "";
+  document.documentElement.removeAttribute('class');
+}
+
+</script>
+</body></html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/display-list/1443027-2.html
@@ -0,0 +1,93 @@
+<!DOCTYPE html>
+<html lang="en" class="reftest-wait"><head>
+<meta http-equiv="content-type" content="text/html; charset=UTF-8"><meta charset="utf-8">
+<title>Bug 1443027 - Test merging across multiple paints</title>
+
+<style>
+
+body { opacity: 0.9; }
+
+div {
+  position: absolute;
+}
+
+#A {
+  left: 250px;
+  top: 50px;
+  width: 100px;
+  height: 100px;
+  background-color: red;
+}
+
+#B {
+  left: 200px;
+  top: 0px;
+  width: 100px;
+  height: 100px;
+  background-color: yellow;
+}
+
+#C {
+  left: 0px;
+  top: 0px;
+  width: 100px;
+  height: 100px;
+  background-color: green;
+}
+
+#D {
+  left: 80px;
+  top: 20px;
+  width: 140px;
+  height: 100px;
+  background-color: blue;
+}
+
+</style>
+</head>
+<body>
+<div id="A"></div>
+<div id="B"></div>
+<div id="C"></div>
+<div id="D"></div>
+
+<script>
+
+var A = document.getElementById("A");
+var B = document.getElementById("B");
+var C = document.getElementById("C");
+var D = document.getElementById("D");
+
+A.style.visibility = "hidden";
+B.style.visibility = "hidden";
+C.style.visibility = "hidden";
+D.style.visibility = "hidden";
+
+window.addEventListener("MozReftestInvalidate", step1);
+
+function step1() {
+  A.style.visibility = "visible";
+  A.style.transform = "translatez(1px)";
+  B.style.visibility = "visible";
+  B.style.transform = "translatez(1px)";
+  D.style.visibility = "visible";
+  D.style.transform = "translatez(1px)";
+
+  window.requestAnimationFrame(function() { window.requestAnimationFrame(step2); });
+}
+
+function step2() {
+  C.style.visibility = "visible";
+  C.style.transform = "translatez(1px)";
+
+  window.requestAnimationFrame(step3);
+}
+
+function step3() {
+  D.style.visibility = "hidden";
+  D.style.transform = "";
+  document.documentElement.removeAttribute('class');
+}
+
+</script>
+</body></html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/display-list/1443027-3-ref.html
@@ -0,0 +1,54 @@
+<!DOCTYPE html>
+<html lang="en"><head>
+<meta http-equiv="content-type" content="text/html; charset=UTF-8"><meta charset="utf-8">
+<title>Bug 1443027 - Test merging across multiple paints</title>
+
+<style>
+
+body { opacity: 0.9; }
+
+div {
+  position: absolute;
+  transform: translatez(1px);
+}
+
+#A {
+  left: 250px;
+  top: 50px;
+  width: 100px;
+  height: 100px;
+  background-color: red;
+}
+
+#B {
+  left: 200px;
+  top: 0px;
+  width: 100px;
+  height: 100px;
+  background-color: yellow;
+}
+
+#C {
+  left: 0px;
+  top: 0px;
+  width: 100px;
+  height: 100px;
+  background-color: green;
+}
+
+#D {
+  left: 80px;
+  top: 20px;
+  width: 140px;
+  height: 100px;
+  background-color: blue;
+}
+
+</style>
+</head>
+<body>
+<div id="A"></div>
+<div id="B"></div>
+<div id="D"></div>
+<div id="C"></div>
+</body></html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/display-list/1443027-3.html
@@ -0,0 +1,91 @@
+<!DOCTYPE html>
+<html lang="en" class="reftest-wait"><head>
+<meta http-equiv="content-type" content="text/html; charset=UTF-8"><meta charset="utf-8">
+<title>Bug 1443027 - Test merging across multiple paints</title>
+
+<style>
+
+body { opacity: 0.9; }
+
+div {
+  position: absolute;
+}
+
+#A {
+  left: 250px;
+  top: 50px;
+  width: 100px;
+  height: 100px;
+  background-color: red;
+}
+
+#B {
+  left: 200px;
+  top: 0px;
+  width: 100px;
+  height: 100px;
+  background-color: yellow;
+}
+
+#C {
+  left: 0px;
+  top: 0px;
+  width: 100px;
+  height: 100px;
+  background-color: green;
+}
+
+#D {
+  left: 80px;
+  top: 20px;
+  width: 140px;
+  height: 100px;
+  background-color: blue;
+}
+
+</style>
+</head>
+<body>
+<div id="A"></div>
+<div id="B"></div>
+<div id="D"></div>
+<div id="C"></div>
+
+<script>
+
+var A = document.getElementById("A");
+var B = document.getElementById("B");
+var C = document.getElementById("C");
+var D = document.getElementById("D");
+
+A.style.visibility = "hidden";
+B.style.visibility = "hidden";
+C.style.visibility = "hidden";
+D.style.visibility = "hidden";
+
+window.addEventListener("MozReftestInvalidate", step1);
+
+function step1() {
+  A.style.visibility = "visible";
+  A.style.transform = "translatez(1px)";
+
+  window.requestAnimationFrame(function() { window.requestAnimationFrame(step2); });
+}
+
+function step2() {
+  C.style.visibility = "visible";
+  C.style.transform = "translatez(1px)";
+  D.style.visibility = "visible";
+  D.style.transform = "translatez(1px)";
+
+  window.requestAnimationFrame(step3);
+}
+
+function step3() {
+  B.style.visibility = "visible";
+  B.style.transform = "translatez(1px)";
+  document.documentElement.removeAttribute('class');
+}
+
+</script>
+</body></html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/display-list/1443027-ref.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<html lang="en"><head>
+<meta http-equiv="content-type" content="text/html; charset=UTF-8"><meta charset="utf-8">
+<title>Bug 1443027 - Test merging across multiple paints</title>
+
+<style>
+
+body { opacity: 0.9; }
+
+div {
+  position: absolute;
+  transform: translatez(1px);
+}
+
+#A {
+  left: 250px;
+  top: 50px;
+  width: 100px;
+  height: 100px;
+  background-color: red;
+}
+
+#B {
+  left: 200px;
+  top: 0px;
+  width: 100px;
+  height: 100px;
+  background-color: yellow;
+}
+
+#C {
+  left: 0px;
+  top: 0px;
+  width: 100px;
+  height: 100px;
+  background-color: green;
+}
+
+</style>
+</head>
+<body>
+<div id="A"></div>
+<div id="B"></div>
+<div id="C"></div>
+</body></html>
--- a/layout/reftests/display-list/reftest.list
+++ b/layout/reftests/display-list/reftest.list
@@ -18,9 +18,11 @@ skip-if(Android) == 1428993-1.html 14289
 == 1420480-1.html 1420480-1-ref.html
 == 1428993-2.html 1428993-2-ref.html
 needs-focus == 1429027-1.html 1429027-1-ref.html
 == 1432553-1.html 1432553-1-ref.html
 == 1432553-2.html 1432553-2-ref.html
 == 1436189-1.html 1436189-1-ref.html
 skip-if(!asyncPan) == 1437374-1.html 1437374-1-ref.html
 == 1439809-1.html 1439809-1-ref.html
-
+== 1443027-1.html 1443027-ref.html
+== 1443027-2.html 1443027-ref.html
+== 1443027-3.html 1443027-3-ref.html
--- a/layout/reftests/position-dynamic-changes/relative/reftest.list
+++ b/layout/reftests/position-dynamic-changes/relative/reftest.list
@@ -1,5 +1,5 @@
-fuzzy-if(cocoaWidget,1,2) fuzzy-if(d2d,47,26) fuzzy-if(asyncPan&&!layersGPUAccelerated,149,716) == move-right-bottom.html move-right-bottom-ref.html
-fuzzy-if(cocoaWidget,1,2) fuzzy-if(asyncPan&&!layersGPUAccelerated,149,716) == move-top-left.html move-top-left-ref.html # Bug 688545
+fuzzy-if(cocoaWidget,1,2) fuzzy-if(d2d,47,26) fuzzy-if(asyncPan&&!layersGPUAccelerated,169,970) == move-right-bottom.html move-right-bottom-ref.html
+fuzzy-if(cocoaWidget,1,2) fuzzy-if(asyncPan&&!layersGPUAccelerated,169,970) == move-top-left.html move-top-left-ref.html # Bug 688545
 fuzzy-if(cocoaWidget,1,3) fuzzy-if(asyncPan&&!layersGPUAccelerated,144,580) == move-right-bottom-table.html move-right-bottom-table-ref.html
 fuzzy-if(cocoaWidget,1,3) fuzzy-if(asyncPan&&!layersGPUAccelerated,144,580) == move-top-left-table.html move-top-left-table-ref.html # Bug 688545
 == percent.html percent-ref.html
new file mode 100644
--- /dev/null
+++ b/layout/reftests/w3c-css/submitted/flexbox/flexbox-abspos-child-002-ref.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>CSS Reftest Reference</title>
+  <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com">
+  <style>
+    .tealBlock {
+      background: teal;
+      width: 10px;
+      height: 10px;
+      margin-bottom: 5px;
+    }
+    .purpleBlock {
+      background: purple;
+      width: 10px;
+      height: 10px;
+      margin-bottom: 5px;
+    }
+  </style>
+</head>
+<body>
+
+  <div class="purpleBlock"></div>
+  <div class="purpleBlock"></div>
+  <div class="purpleBlock"></div>
+  <div class="purpleBlock"></div>
+
+  <div class="tealBlock"></div>
+  <div class="tealBlock"></div>
+  <div class="tealBlock"></div>
+  <div class="tealBlock"></div>
+
+  <div class="tealBlock"></div>
+  <div class="tealBlock"></div>
+  <div class="tealBlock"></div>
+  <div class="tealBlock"></div>
+
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/w3c-css/submitted/flexbox/flexbox-abspos-child-002.html
@@ -0,0 +1,62 @@
+<!DOCTYPE html>
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>
+    CSS Test: Test that "flex-basis" doesn't affect layout of abspos flex child
+  </title>
+  <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com">
+  <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#abspos-items">
+  <link rel="match" href="flexbox-abspos-child-001-ref.html">
+  <style>
+    .flex {
+      display: flex;
+      height: 10px;
+      width: 10px;
+      background: purple;
+      margin-bottom: 5px;
+      position: relative;
+    }
+    .flex > * {
+      position: absolute;
+      background: teal;
+      height: 10px;
+    }
+    .sized {
+      width: 10px;
+    }
+    .implied {
+      left: 0;
+      right: 0;
+    }
+  </style>
+</head>
+<body>
+  <!-- In all cases below, flex-basis should have no effect on layout (because
+       it's on an element that is abspos and hence not a flex item). -->
+
+  <!-- Abspos child has auto width (which should end up 0): -->
+  <div class="flex"><div style="flex-basis: 2px"></div></div>
+  <div class="flex"><div style="flex-basis: 100px"></div></div>
+  <div class="flex"><div style="flex-basis: 80%"></div></div>
+  <div class="flex"><div style="flex-basis: content"></div></div>
+
+  <!-- Abspos child has explicit 10px width: -->
+  <div class="flex"><div class="sized" style="flex-basis: 2px"></div></div>
+  <div class="flex"><div class="sized" style="flex-basis: 100px"></div></div>
+  <div class="flex"><div class="sized" style="flex-basis: 80%"></div></div>
+  <div class="flex"><div class="sized" style="flex-basis: content"></div></div>
+
+  <!-- Abspos child has implicit 10px width (implied by auto width and
+       constrained left/right properties): -->
+  <div class="flex"><div class="implied" style="flex-basis: 2px"></div></div>
+  <div class="flex"><div class="implied" style="flex-basis: 100px"></div></div>
+  <div class="flex"><div class="implied" style="flex-basis: 80%"></div></div>
+  <div class="flex"><div class="implied" style="flex-basis: content"></div></div>
+
+</body>
+</html>
--- a/layout/reftests/w3c-css/submitted/flexbox/reftest.list
+++ b/layout/reftests/w3c-css/submitted/flexbox/reftest.list
@@ -1,11 +1,12 @@
 # Tests for absolutely-positioned children of a flex container
 == flexbox-abspos-child-001a.html flexbox-abspos-child-001-ref.html
 == flexbox-abspos-child-001b.html flexbox-abspos-child-001-ref.html
+== flexbox-abspos-child-002.html flexbox-abspos-child-002-ref.html
 
 # Tests for handling anonymous flex items
 == flexbox-anonymous-items-001.html flexbox-anonymous-items-001-ref.html
 
 # Tests for alignment of flex lines (align-content property)
 == flexbox-align-content-horiz-001a.xhtml flexbox-align-content-horiz-001-ref.xhtml
 == flexbox-align-content-horiz-001b.xhtml flexbox-align-content-horiz-001-ref.xhtml
 == flexbox-align-content-vert-001a.xhtml  flexbox-align-content-vert-001-ref.xhtml
--- a/layout/reftests/w3c-css/submitted/ruby/reftest.list
+++ b/layout/reftests/w3c-css/submitted/ruby/reftest.list
@@ -1,11 +1,11 @@
 # Tests for inlinizing block-level boxes
 == ruby-inlinize-blocks-001.html ruby-inlinize-blocks-001-ref.html
-== ruby-inlinize-blocks-002.html ruby-inlinize-blocks-002-ref.html
+fuzzy-if(winWidget,144,1) == ruby-inlinize-blocks-002.html ruby-inlinize-blocks-002-ref.html
 == ruby-inlinize-blocks-003.html ruby-inlinize-blocks-003-ref.html
 == ruby-inlinize-blocks-004.html ruby-inlinize-blocks-004-ref.html
 == ruby-inlinize-blocks-005.html ruby-inlinize-blocks-005-ref.html
 
 # Tests for autohiding base-identical annotations
 == ruby-autohide-001.html ruby-autohide-001-ref.html
 == ruby-autohide-002.html ruby-autohide-002-ref.html
 == ruby-autohide-003.html ruby-autohide-003-ref.html
--- a/layout/style/FontFace.cpp
+++ b/layout/style/FontFace.cpp
@@ -8,21 +8,22 @@
 
 #include <algorithm>
 #include "mozilla/dom/FontFaceBinding.h"
 #include "mozilla/dom/FontFaceSet.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/TypedArray.h"
 #include "mozilla/dom/UnionTypes.h"
 #include "mozilla/CycleCollectedJSContext.h"
+#include "mozilla/ServoBindings.h"
 #include "mozilla/ServoCSSParser.h"
+#include "mozilla/ServoFontFaceRule.h"
 #include "mozilla/ServoStyleSet.h"
 #include "mozilla/ServoUtils.h"
 #include "mozilla/StaticPrefs.h"
-#include "nsCSSFontFaceRule.h"
 #include "nsCSSParser.h"
 #include "nsIDocument.h"
 #include "nsStyleUtil.h"
 
 namespace mozilla {
 namespace dom {
 
 // -- FontFaceBufferSource ---------------------------------------------------
@@ -72,25 +73,23 @@ GetDataFrom(const T& aObject, uint8_t*& 
 
 // -- FontFace ---------------------------------------------------------------
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(FontFace)
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(FontFace)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParent)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLoaded)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRule)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFontFaceSet)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOtherFontFaceSets)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(FontFace)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mParent)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mLoaded)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mRule)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mFontFaceSet)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mOtherFontFaceSets)
   NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(FontFace)
   NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
 NS_IMPL_CYCLE_COLLECTION_TRACE_END
@@ -151,17 +150,17 @@ LoadStateToStatus(gfxUserFontEntry::User
   }
   NS_NOTREACHED("invalid aLoadState value");
   return FontFaceLoadStatus::Error;
 }
 
 already_AddRefed<FontFace>
 FontFace::CreateForRule(nsISupports* aGlobal,
                         FontFaceSet* aFontFaceSet,
-                        nsCSSFontFaceRule* aRule)
+                        RawServoFontFaceRule* aRule)
 {
   RefPtr<FontFace> obj = new FontFace(aGlobal, aFontFaceSet);
   obj->mRule = aRule;
   obj->mSourceType = eSourceType_FontFaceRule;
   obj->mInFontFaceSet = true;
   return obj.forget();
 }
 
@@ -188,19 +187,18 @@ FontFace::Constructor(const GlobalObject
   obj->InitializeSource(aSource);
   return obj.forget();
 }
 
 void
 FontFace::InitializeSource(const StringOrArrayBufferOrArrayBufferView& aSource)
 {
   if (aSource.IsString()) {
-    if (!ParseDescriptor(eCSSFontDesc_Src,
-                         aSource.GetAsString(),
-                         mDescriptors->mSrc)) {
+    IgnoredErrorResult rv;
+    if (!SetDescriptor(eCSSFontDesc_Src, aSource.GetAsString(), rv)) {
       Reject(NS_ERROR_DOM_SYNTAX_ERR);
 
       SetStatus(FontFaceLoadStatus::Error);
       return;
     }
 
     mSourceType = eSourceType_URLs;
     return;
@@ -250,45 +248,45 @@ FontFace::SetFamily(const nsAString& aVa
   mFontFaceSet->FlushUserFontSet();
   SetDescriptor(eCSSFontDesc_Family, aValue, aRv);
 }
 
 void
 FontFace::GetStyle(nsString& aResult)
 {
   mFontFaceSet->FlushUserFontSet();
-  GetDesc(eCSSFontDesc_Style, eCSSProperty_font_style, aResult);
+  GetDesc(eCSSFontDesc_Style, aResult);
 }
 
 void
 FontFace::SetStyle(const nsAString& aValue, ErrorResult& aRv)
 {
   mFontFaceSet->FlushUserFontSet();
   SetDescriptor(eCSSFontDesc_Style, aValue, aRv);
 }
 
 void
 FontFace::GetWeight(nsString& aResult)
 {
   mFontFaceSet->FlushUserFontSet();
-  GetDesc(eCSSFontDesc_Weight, eCSSProperty_font_weight, aResult);
+  GetDesc(eCSSFontDesc_Weight, aResult);
 }
 
 void
 FontFace::SetWeight(const nsAString& aValue, ErrorResult& aRv)
 {
   mFontFaceSet->FlushUserFontSet();
   SetDescriptor(eCSSFontDesc_Weight, aValue, aRv);
 }
 
 void
 FontFace::GetStretch(nsString& aResult)
 {
   mFontFaceSet->FlushUserFontSet();
-  GetDesc(eCSSFontDesc_Stretch, eCSSProperty_font_stretch, aResult);
+  GetDesc(eCSSFontDesc_Stretch, aResult);
 }
 
 void
 FontFace::SetStretch(const nsAString& aValue, ErrorResult& aRv)
 {
   mFontFaceSet->FlushUserFontSet();
   SetDescriptor(eCSSFontDesc_Stretch, aValue, aRv);
 }
@@ -296,17 +294,17 @@ FontFace::SetStretch(const nsAString& aV
 void
 FontFace::GetUnicodeRange(nsString& aResult)
 {
   mFontFaceSet->FlushUserFontSet();
 
   // There is no eCSSProperty_unicode_range for us to pass in to GetDesc
   // to get a serialized (possibly defaulted) value, but that function
   // doesn't use the property ID for this descriptor anyway.
-  GetDesc(eCSSFontDesc_UnicodeRange, eCSSProperty_UNKNOWN, aResult);
+  GetDesc(eCSSFontDesc_UnicodeRange, aResult);
 }
 
 void
 FontFace::SetUnicodeRange(const nsAString& aValue, ErrorResult& aRv)
 {
   mFontFaceSet->FlushUserFontSet();
   SetDescriptor(eCSSFontDesc_UnicodeRange, aValue, aRv);
 }
@@ -329,47 +327,45 @@ FontFace::SetVariant(const nsAString& aV
   // XXX Ignore assignments to variant until we support font-variant
   // descriptors (bug 1055385).
 }
 
 void
 FontFace::GetFeatureSettings(nsString& aResult)
 {
   mFontFaceSet->FlushUserFontSet();
-  GetDesc(eCSSFontDesc_FontFeatureSettings, eCSSProperty_font_feature_settings,
-          aResult);
+  GetDesc(eCSSFontDesc_FontFeatureSettings, aResult);
 }
 
 void
 FontFace::SetFeatureSettings(const nsAString& aValue, ErrorResult& aRv)
 {
   mFontFaceSet->FlushUserFontSet();
   SetDescriptor(eCSSFontDesc_FontFeatureSettings, aValue, aRv);
 }
 
 void
 FontFace::GetVariationSettings(nsString& aResult)
 {
   mFontFaceSet->FlushUserFontSet();
-  GetDesc(eCSSFontDesc_FontVariationSettings, eCSSProperty_font_variation_settings,
-          aResult);
+  GetDesc(eCSSFontDesc_FontVariationSettings, aResult);
 }
 
 void
 FontFace::SetVariationSettings(const nsAString& aValue, ErrorResult& aRv)
 {
   mFontFaceSet->FlushUserFontSet();
   SetDescriptor(eCSSFontDesc_FontVariationSettings, aValue, aRv);
 }
 
 void
 FontFace::GetDisplay(nsString& aResult)
 {
   mFontFaceSet->FlushUserFontSet();
-  GetDesc(eCSSFontDesc_Display, eCSSProperty_UNKNOWN, aResult);
+  GetDesc(eCSSFontDesc_Display, aResult);
 }
 
 void
 FontFace::SetDisplay(const nsAString& aValue, ErrorResult& aRv)
 {
   mFontFaceSet->FlushUserFontSet();
   SetDescriptor(eCSSFontDesc_Display, aValue, aRv);
 }
@@ -520,161 +516,121 @@ FontFace::DoReject(nsresult aResult)
     // See comments in Gecko_GetFontMetrics.
     ss->AppendTask(PostTraversalTask::RejectFontFaceLoadedPromise(this, aResult));
     return;
   }
 
   mLoaded->MaybeReject(aResult);
 }
 
-bool
-FontFace::ParseDescriptor(nsCSSFontDesc aDescID,
-                          const nsAString& aString,
-                          nsCSSValue& aResult)
+already_AddRefed<URLExtraData>
+FontFace::GetURLExtraData() const
 {
   nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(mParent);
   nsCOMPtr<nsIPrincipal> principal = global->PrincipalOrNull();
 
   nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(mParent);
   nsCOMPtr<nsIURI> docURI = window->GetDocumentURI();
   nsCOMPtr<nsIURI> base = window->GetDocBaseURI();
 
   RefPtr<URLExtraData> url = new URLExtraData(base, docURI, principal);
-  return ServoCSSParser::ParseFontDescriptor(aDescID, aString, url, aResult);
+  return url.forget();
 }
 
-void
+bool
 FontFace::SetDescriptor(nsCSSFontDesc aFontDesc,
                         const nsAString& aValue,
                         ErrorResult& aRv)
 {
+  // FIXME We probably don't need to distinguish between this anymore
+  // since we have common backend now.
   NS_ASSERTION(!HasRule(),
                "we don't handle rule backed FontFace objects yet");
   if (HasRule()) {
-    return;
+    return false;
   }
 
-  nsCSSValue parsedValue;
-  if (!ParseDescriptor(aFontDesc, aValue, parsedValue)) {
+  NS_ConvertUTF16toUTF8 value(aValue);
+  RefPtr<URLExtraData> url = GetURLExtraData();
+  if (!Servo_FontFaceRule_SetDescriptor(GetData(), aFontDesc, &value, url)) {
     aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
-    return;
+    return false;
   }
 
-  mDescriptors->Get(aFontDesc) = parsedValue;
-
   if (aFontDesc == eCSSFontDesc_UnicodeRange) {
     mUnicodeRangeDirty = true;
   }
 
   // XXX Setting descriptors doesn't actually have any effect on FontFace
   // objects that have started loading or have already been loaded.
+  return true;
 }
 
 bool
 FontFace::SetDescriptors(const nsAString& aFamily,
                          const FontFaceDescriptors& aDescriptors)
 {
   MOZ_ASSERT(!HasRule());
   MOZ_ASSERT(!mDescriptors);
 
-  mDescriptors = new CSSFontFaceDescriptors;
+  IgnoredErrorResult rv;
+  mDescriptors = Servo_FontFaceRule_CreateEmpty().Consume();
 
   // Parse all of the mDescriptors in aInitializer, which are the values
   // we got from the JS constructor.
-  if (!ParseDescriptor(eCSSFontDesc_Family,
-                       aFamily,
-                       mDescriptors->mFamily) ||
-      *mDescriptors->mFamily.GetStringBufferValue() == 0 ||
-      !ParseDescriptor(eCSSFontDesc_Style,
-                       aDescriptors.mStyle,
-                       mDescriptors->mStyle) ||
-      !ParseDescriptor(eCSSFontDesc_Weight,
-                       aDescriptors.mWeight,
-                       mDescriptors->mWeight) ||
-      !ParseDescriptor(eCSSFontDesc_Stretch,
-                       aDescriptors.mStretch,
-                       mDescriptors->mStretch) ||
-      !ParseDescriptor(eCSSFontDesc_UnicodeRange,
-                       aDescriptors.mUnicodeRange,
-                       mDescriptors->mUnicodeRange) ||
-      !ParseDescriptor(eCSSFontDesc_FontFeatureSettings,
-                       aDescriptors.mFeatureSettings,
-                       mDescriptors->mFontFeatureSettings) ||
+  if (!SetDescriptor(eCSSFontDesc_Family, aFamily, rv) ||
+      !SetDescriptor(eCSSFontDesc_Style, aDescriptors.mStyle, rv) ||
+      !SetDescriptor(eCSSFontDesc_Weight, aDescriptors.mWeight, rv) ||
+      !SetDescriptor(eCSSFontDesc_Stretch, aDescriptors.mStretch, rv) ||
+      !SetDescriptor(eCSSFontDesc_UnicodeRange,
+                     aDescriptors.mUnicodeRange, rv) ||
+      !SetDescriptor(eCSSFontDesc_FontFeatureSettings,
+                     aDescriptors.mFeatureSettings, rv) ||
       (StaticPrefs::layout_css_font_variations_enabled() &&
-       !ParseDescriptor(eCSSFontDesc_FontVariationSettings,
-                        aDescriptors.mVariationSettings,
-                        mDescriptors->mFontVariationSettings)) ||
-      !ParseDescriptor(eCSSFontDesc_Display,
-                       aDescriptors.mDisplay,
-                       mDescriptors->mDisplay)) {
+       !SetDescriptor(eCSSFontDesc_FontVariationSettings,
+                      aDescriptors.mVariationSettings, rv)) ||
+      !SetDescriptor(eCSSFontDesc_Display, aDescriptors.mDisplay, rv)) {
     // XXX Handle font-variant once we support it (bug 1055385).
 
     // If any of the descriptors failed to parse, none of them should be set
     // on the FontFace.
-    mDescriptors = new CSSFontFaceDescriptors;
+    mDescriptors = Servo_FontFaceRule_CreateEmpty().Consume();
 
     Reject(NS_ERROR_DOM_SYNTAX_ERR);
 
     SetStatus(FontFaceLoadStatus::Error);
     return false;
   }
 
   return true;
 }
 
 void
 FontFace::GetDesc(nsCSSFontDesc aDescID, nsCSSValue& aResult) const
 {
-  if (HasRule()) {
-    MOZ_ASSERT(mRule);
-    MOZ_ASSERT(!mDescriptors);
-    mRule->GetDesc(aDescID, aResult);
-  } else {
-    aResult = mDescriptors->Get(aDescID);
-  }
+  aResult.Reset();
+  Servo_FontFaceRule_GetDescriptor(GetData(), aDescID, &aResult);
 }
 
 void
-FontFace::GetDesc(nsCSSFontDesc aDescID,
-                  nsCSSPropertyID aPropID,
-                  nsString& aResult) const
+FontFace::GetDesc(nsCSSFontDesc aDescID, nsString& aResult) const
 {
-  MOZ_ASSERT(aDescID == eCSSFontDesc_UnicodeRange ||
-             aDescID == eCSSFontDesc_Display ||
-             aPropID != eCSSProperty_UNKNOWN,
-             "only pass eCSSProperty_UNKNOWN for eCSSFontDesc_UnicodeRange");
-
-  nsCSSValue value;
-  GetDesc(aDescID, value);
-
   aResult.Truncate();
+  Servo_FontFaceRule_GetDescriptorCssText(GetData(), aDescID, &aResult);
 
   // Fill in a default value for missing descriptors.
-  if (value.GetUnit() == eCSSUnit_Null) {
+  if (aResult.IsEmpty()) {
     if (aDescID == eCSSFontDesc_UnicodeRange) {
       aResult.AssignLiteral("U+0-10FFFF");
     } else if (aDescID == eCSSFontDesc_Display) {
       aResult.AssignLiteral("auto");
     } else if (aDescID != eCSSFontDesc_Family &&
                aDescID != eCSSFontDesc_Src) {
       aResult.AssignLiteral("normal");
     }
-    return;
-  }
-
-  if (aDescID == eCSSFontDesc_UnicodeRange) {
-    // Since there's no unicode-range property, we can't use
-    // nsCSSValue::AppendToString to serialize this descriptor.
-    nsStyleUtil::AppendUnicodeRange(value, aResult);
-  } else if (aDescID == eCSSFontDesc_Display) {
-    AppendASCIItoUTF16(nsCSSProps::ValueToKeyword(value.GetIntValue(),
-                                                  nsCSSProps::kFontDisplayKTable),
-                       aResult);
-  } else {
-    value.AppendToString(aPropID, aResult);
   }
 }
 
 void
 FontFace::SetUserFontEntry(gfxUserFontEntry* aEntry)
 {
   if (mUserFontEntry) {
     mUserFontEntry->mFontFaces.RemoveElement(this);
@@ -723,18 +679,17 @@ FontFace::GetFamilyName(nsString& aResul
 }
 
 void
 FontFace::DisconnectFromRule()
 {
   MOZ_ASSERT(HasRule());
 
   // Make a copy of the descriptors.
-  mDescriptors = new CSSFontFaceDescriptors;
-  mRule->GetDescriptors(*mDescriptors);
+  mDescriptors = Servo_FontFaceRule_Clone(mRule).Consume();
   mRule = nullptr;
   mInFontFaceSet = false;
 }
 
 bool
 FontFace::HasFontData() const
 {
   return mSourceType == eSourceType_Buffer && mSourceBuffer;
--- a/layout/style/FontFace.h
+++ b/layout/style/FontFace.h
@@ -10,21 +10,22 @@
 #include "mozilla/dom/FontFaceBinding.h"
 #include "gfxUserFontSet.h"
 #include "nsAutoPtr.h"
 #include "nsCSSPropertyID.h"
 #include "nsCSSValue.h"
 #include "nsWrapperCache.h"
 
 class gfxFontFaceBufferSource;
-class nsCSSFontFaceRule;
+struct RawServoFontFaceRule;
 
 namespace mozilla {
 struct CSSFontFaceDescriptors;
 class PostTraversalTask;
+class ServoFontFaceRule;
 namespace dom {
 class FontFaceBufferSource;
 struct FontFaceDescriptors;
 class FontFaceSet;
 class Promise;
 class StringOrArrayBufferOrArrayBufferView;
 } // namespace dom
 } // namespace mozilla
@@ -74,19 +75,19 @@ public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(FontFace)
 
   nsISupports* GetParentObject() const { return mParent; }
   virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
   static already_AddRefed<FontFace>
   CreateForRule(nsISupports* aGlobal, FontFaceSet* aFontFaceSet,
-                nsCSSFontFaceRule* aRule);
+                RawServoFontFaceRule* aRule);
 
-  nsCSSFontFaceRule* GetRule() { return mRule; }
+  RawServoFontFaceRule* GetRule() { return mRule; }
 
   void GetDesc(nsCSSFontDesc aDescID, nsCSSValue& aResult) const;
 
   gfxUserFontEntry* CreateUserFontEntry();
   gfxUserFontEntry* GetUserFontEntry() const { return mUserFontEntry; }
   void SetUserFontEntry(gfxUserFontEntry* aEntry);
 
   /**
@@ -175,43 +176,40 @@ private:
   FontFace(nsISupports* aParent, FontFaceSet* aFontFaceSet);
   ~FontFace();
 
   void InitializeSource(const StringOrArrayBufferOrArrayBufferView& aSource);
 
   // Helper function for Load.
   void DoLoad();
 
-  /**
-   * Parses a @font-face descriptor value, storing the result in aResult.
-   * Returns whether the parsing was successful.
-   */
-  bool ParseDescriptor(nsCSSFontDesc aDescID, const nsAString& aString,
-                       nsCSSValue& aResult);
-
   // Helper function for the descriptor setter methods.
-  void SetDescriptor(nsCSSFontDesc aFontDesc,
+  // Returns whether it successfully sets the descriptor.
+  bool SetDescriptor(nsCSSFontDesc aFontDesc,
                      const nsAString& aValue,
                      mozilla::ErrorResult& aRv);
 
   /**
    * Sets all of the descriptor values in mDescriptors using values passed
    * to the JS constructor.
    */
   bool SetDescriptors(const nsAString& aFamily,
                       const FontFaceDescriptors& aDescriptors);
 
   /**
    * Sets the current loading status.
    */
   void SetStatus(mozilla::dom::FontFaceLoadStatus aStatus);
 
-  void GetDesc(nsCSSFontDesc aDescID,
-               nsCSSPropertyID aPropID,
-               nsString& aResult) const;
+  void GetDesc(nsCSSFontDesc aDescID, nsString& aResult) const;
+
+  already_AddRefed<URLExtraData> GetURLExtraData() const;
+
+  RawServoFontFaceRule* GetData() const
+    { return HasRule() ? mRule : mDescriptors; }
 
   /**
    * Returns and takes ownership of the buffer storing the font data.
    */
   void TakeBuffer(uint8_t*& aBuffer, uint32_t& aLength);
 
   // Acts like mLoaded->MaybeReject(aResult), except it doesn't create mLoaded
   // if it doesn't already exist.
@@ -231,17 +229,17 @@ private:
   // when JS asks for it.
   RefPtr<mozilla::dom::Promise> mLoaded;
 
   // Saves the rejection code for mLoaded if mLoaded hasn't been created yet.
   nsresult mLoadedRejection;
 
   // The @font-face rule this FontFace object is reflecting, if it is a
   // rule backed FontFace.
-  RefPtr<nsCSSFontFaceRule> mRule;
+  RefPtr<RawServoFontFaceRule> mRule;
 
   // The FontFace object's user font entry.  This is initially null, but is set
   // during FontFaceSet::UpdateRules and when a FontFace is explicitly loaded.
   RefPtr<Entry> mUserFontEntry;
 
   // The current load status of the font represented by this FontFace.
   // Note that we can't just reflect the value of the gfxUserFontEntry's
   // status, since the spec sometimes requires us to go through the event
@@ -261,17 +259,20 @@ private:
   // If the FontFace was constructed with an ArrayBuffer(View), this is a
   // copy of the data from it.
   uint8_t* mSourceBuffer;
   uint32_t mSourceBufferLength;
 
   // The values corresponding to the font face descriptors, if we are not
   // a rule backed FontFace object.  For rule backed objects, we use
   // the descriptors stored in mRule.
-  nsAutoPtr<mozilla::CSSFontFaceDescriptors> mDescriptors;
+  // FIXME This should hold a unique ptr to just the descriptors inside,
+  // so that we don't need to create a rule for it and don't need to
+  // assign a fake line number and column number. See bug 1450904.
+  RefPtr<RawServoFontFaceRule> mDescriptors;
 
   // The value of the unicode-range descriptor as a gfxCharacterMap.  Valid
   // only when mUnicodeRangeDirty is false.
   RefPtr<gfxCharacterMap> mUnicodeRange;
 
   // The primary FontFaceSet this FontFace is associated with,
   // regardless of whether it is currently "in" the set.
   RefPtr<FontFaceSet> mFontFaceSet;
--- a/layout/style/FontFaceSet.cpp
+++ b/layout/style/FontFaceSet.cpp
@@ -15,16 +15,17 @@
 #include "mozilla/dom/FontFaceSetLoadEvent.h"
 #include "mozilla/dom/FontFaceSetLoadEventBinding.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/net/ReferrerPolicy.h"
 #include "mozilla/AsyncEventDispatcher.h"
 #include "mozilla/Logging.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/ServoCSSParser.h"
+#include "mozilla/ServoFontFaceRule.h"
 #include "mozilla/ServoStyleSet.h"
 #include "mozilla/ServoUtils.h"
 #include "mozilla/Sprintf.h"
 #include "mozilla/StaticPrefs.h"
 #include "mozilla/Telemetry.h"
 #include "mozilla/LoadInfo.h"
 #include "nsAutoPtr.h"
 #include "nsContentPolicyUtils.h"
@@ -676,17 +677,17 @@ FontFaceSet::UpdateRules(const nsTArray<
   MOZ_ASSERT(mUserFontSet);
 
   // If there was a change to the mNonRuleFaces array, then there could
   // have been a modification to the user font set.
   bool modified = mNonRuleFacesDirty;
   mNonRuleFacesDirty = false;
 
   // reuse existing FontFace objects mapped to rules already
-  nsDataHashtable<nsPtrHashKey<nsCSSFontFaceRule>, FontFace*> ruleFaceMap;
+  nsDataHashtable<nsPtrHashKey<RawServoFontFaceRule>, FontFace*> ruleFaceMap;
   for (size_t i = 0, i_end = mRuleFaces.Length(); i < i_end; ++i) {
     FontFace* f = mRuleFaces[i].mFontFace;
     if (!f) {
       continue;
     }
     ruleFaceMap.Put(f->GetRule(), f);
   }
 
@@ -707,24 +708,24 @@ FontFaceSet::UpdateRules(const nsTArray<
   for (auto it = mUserFontSet->mFontFamilies.Iter(); !it.Done(); it.Next()) {
     it.Data()->DetachFontEntries();
   }
 
   // Sometimes aRules has duplicate @font-face rules in it; we should make
   // that not happen, but in the meantime, don't try to insert the same
   // FontFace object more than once into mRuleFaces.  We track which
   // ones we've handled in this table.
-  nsTHashtable<nsPtrHashKey<nsCSSFontFaceRule>> handledRules;
+  nsTHashtable<nsPtrHashKey<RawServoFontFaceRule>> handledRules;
 
   for (size_t i = 0, i_end = aRules.Length(); i < i_end; ++i) {
     // Insert each FontFace objects for each rule into our list, migrating old
     // font entries if possible rather than creating new ones; set  modified  to
     // true if we detect that rule ordering has changed, or if a new entry is
     // created.
-    nsCSSFontFaceRule* rule = aRules[i].mRule;
+    RawServoFontFaceRule* rule = aRules[i].mRule;
     if (!handledRules.EnsureInserted(rule)) {
       // rule was already present in the hashtable
       continue;
     }
     RefPtr<FontFace> f = ruleFaceMap.Get(rule);
     if (!f.get()) {
       f = FontFace::CreateForRule(GetParentObject(), this, rule);
     }
@@ -1079,17 +1080,19 @@ FontFaceSet::FindOrCreateUserFontEntryFr
 
     face->mSourceType = gfxFontFaceSrc::eSourceType_Buffer;
     face->mBuffer = aFontFace->CreateBufferSource();
     face->mReferrerPolicy = mozilla::net::RP_Unset;
   } else {
     aFontFace->GetDesc(eCSSFontDesc_Src, val);
     unit = val.GetUnit();
     if (unit == eCSSUnit_Array) {
-      nsCSSValue::Array* srcArr = val.GetArrayValue();
+      // Hold a strong reference because content of val is going away
+      // in the loop below.
+      RefPtr<nsCSSValue::Array> srcArr = val.GetArrayValue();
       size_t numSrc = srcArr->Count();
 
       for (size_t i = 0; i < numSrc; i++) {
         val = srcArr->Item(i);
         unit = val.GetUnit();
         gfxFontFaceSrc* face = srcArray.AppendElements(1);
         if (!face)
           return nullptr;
@@ -1193,31 +1196,31 @@ FontFaceSet::FindOrCreateUserFontEntryFr
                                                  stretch, italicStyle,
                                                  featureSettings,
                                                  variationSettings,
                                                  languageOverride,
                                                  unicodeRanges, fontDisplay);
   return entry.forget();
 }
 
-nsCSSFontFaceRule*
+RawServoFontFaceRule*
 FontFaceSet::FindRuleForEntry(gfxFontEntry* aFontEntry)
 {
   NS_ASSERTION(!aFontEntry->mIsUserFontContainer, "only platform font entries");
   for (uint32_t i = 0; i < mRuleFaces.Length(); ++i) {
     FontFace* f = mRuleFaces[i].mFontFace;
     gfxUserFontEntry* entry = f->GetUserFontEntry();
     if (entry && entry->GetPlatformFontEntry() == aFontEntry) {
       return f->GetRule();
     }
   }
   return nullptr;
 }
 
-nsCSSFontFaceRule*
+RawServoFontFaceRule*
 FontFaceSet::FindRuleForUserFontEntry(gfxUserFontEntry* aUserFontEntry)
 {
   for (uint32_t i = 0; i < mRuleFaces.Length(); ++i) {
     FontFace* f = mRuleFaces[i].mFontFace;
     if (f->GetUserFontEntry() == aUserFontEntry) {
       return f->GetRule();
     }
   }
@@ -1281,34 +1284,38 @@ FontFaceSet::LogMessage(gfxUserFontEntry
   message.AppendLiteral(" source: ");
   message.Append(fontURI);
 
   if (LOG_ENABLED()) {
     LOG(("userfonts (%p) %s", mUserFontSet.get(), message.get()));
   }
 
   // try to give the user an indication of where the rule came from
-  nsCSSFontFaceRule* rule = FindRuleForUserFontEntry(aUserFontEntry);
+  RawServoFontFaceRule* rule = FindRuleForUserFontEntry(aUserFontEntry);
   nsString href;
   nsString text;
   uint32_t line = 0;
   uint32_t column = 0;
   if (rule) {
-    rule->GetCssText(text);
+    Servo_FontFaceRule_GetCssText(rule, &text);
+    Servo_FontFaceRule_GetSourceLocation(rule, &line, &column);
+    // FIXME We need to figure out an approach to get the style sheet
+    // of this raw rule. See bug 1450903.
+#if 0
     StyleSheet* sheet = rule->GetStyleSheet();
     // if the style sheet is removed while the font is loading can be null
     if (sheet) {
       nsCString spec = sheet->GetSheetURI()->GetSpecOrDefault();
       CopyUTF8toUTF16(spec, href);
     } else {
       NS_WARNING("null parent stylesheet for @font-face rule");
       href.AssignLiteral("unknown");
     }
-    line = rule->GetLineNumber();
-    column = rule->GetColumnNumber();
+#endif
+    href.AssignLiteral("unknown");
   }
 
   nsresult rv;
   nsCOMPtr<nsIScriptError> scriptError =
     do_CreateInstance(NS_SCRIPTERROR_CONTRACTID, &rv);
   NS_ENSURE_SUCCESS(rv, rv);
 
   uint64_t innerWindowID = mDocument->InnerWindowID();
--- a/layout/style/FontFaceSet.h
+++ b/layout/style/FontFaceSet.h
@@ -6,25 +6,25 @@
 
 #ifndef mozilla_dom_FontFaceSet_h
 #define mozilla_dom_FontFaceSet_h
 
 #include "mozilla/dom/FontFace.h"
 #include "mozilla/dom/FontFaceSetBinding.h"
 #include "mozilla/DOMEventTargetHelper.h"
 #include "gfxUserFontSet.h"
-#include "nsCSSFontFaceRule.h"
 #include "nsICSSLoaderObserver.h"
 
 struct gfxFontFaceSrc;
 class gfxFontSrcPrincipal;
 class gfxUserFontEntry;
 class nsFontFaceLoader;
 class nsIPrincipal;
 class nsPIDOMWindowInner;
+struct RawServoFontFaceRule;
 
 namespace mozilla {
 class PostTraversalTask;
 class SharedFontList;
 namespace dom {
 class FontFace;
 class Promise;
 } // namespace dom
@@ -124,17 +124,17 @@ public:
   // It's removed from the mLoaders set.
   void RemoveLoader(nsFontFaceLoader* aLoader);
 
   bool UpdateRules(const nsTArray<nsFontFaceRuleContainer>& aRules);
 
   nsPresContext* GetPresContext();
 
   // search for @font-face rule that matches a platform font entry
-  nsCSSFontFaceRule* FindRuleForEntry(gfxFontEntry* aFontEntry);
+  RawServoFontFaceRule* FindRuleForEntry(gfxFontEntry* aFontEntry);
 
   void IncrementGeneration(bool aIsRebuild = false);
 
   /**
    * Finds an existing entry in the user font cache or creates a new user
    * font entry for the given FontFace object.
    */
   static already_AddRefed<gfxUserFontEntry>
@@ -266,17 +266,17 @@ private:
   };
 
   static already_AddRefed<gfxUserFontEntry> FindOrCreateUserFontEntryFromFontFace(
                                                    const nsAString& aFamilyName,
                                                    FontFace* aFontFace,
                                                    SheetType aSheetType);
 
   // search for @font-face rule that matches a userfont font entry
-  nsCSSFontFaceRule* FindRuleForUserFontEntry(gfxUserFontEntry* aUserFontEntry);
+  RawServoFontFaceRule* FindRuleForUserFontEntry(gfxUserFontEntry* aUserFontEntry);
 
   nsresult StartLoad(gfxUserFontEntry* aUserFontEntry,
                      const gfxFontFaceSrc* aFontFaceSrc);
   gfxFontSrcPrincipal* GetStandardFontLoadPrincipal();
   nsresult CheckFontLoad(const gfxFontFaceSrc* aFontFaceSrc,
                          gfxFontSrcPrincipal** aPrincipal,
                          bool* aBypassCache);
   bool IsFontLoadAllowed(const gfxFontFaceSrc& aSrc);
--- a/layout/style/ServoArcTypeList.h
+++ b/layout/style/ServoArcTypeList.h
@@ -17,8 +17,9 @@ SERVO_ARC_TYPE(KeyframesRule, RawServoKe
 SERVO_ARC_TYPE(MediaList, RawServoMediaList)
 SERVO_ARC_TYPE(MediaRule, RawServoMediaRule)
 SERVO_ARC_TYPE(NamespaceRule, RawServoNamespaceRule)
 SERVO_ARC_TYPE(PageRule, RawServoPageRule)
 SERVO_ARC_TYPE(SupportsRule, RawServoSupportsRule)
 SERVO_ARC_TYPE(DocumentRule, RawServoDocumentRule)
 SERVO_ARC_TYPE(FontFeatureValuesRule, RawServoFontFeatureValuesRule)
 SERVO_ARC_TYPE(RuleNode, RawServoRuleNode)
+SERVO_ARC_TYPE(FontFaceRule, RawServoFontFaceRule)
--- a/layout/style/ServoBindingList.h
+++ b/layout/style/ServoBindingList.h
@@ -257,21 +257,20 @@ BASIC_RULE_FUNCS(Import)
 BASIC_RULE_FUNCS_WITHOUT_GETTER(Keyframe)
 BASIC_RULE_FUNCS(Keyframes)
 GROUP_RULE_FUNCS(Media)
 BASIC_RULE_FUNCS(Namespace)
 BASIC_RULE_FUNCS(Page)
 GROUP_RULE_FUNCS(Supports)
 GROUP_RULE_FUNCS(Document)
 BASIC_RULE_FUNCS(FontFeatureValues)
+BASIC_RULE_FUNCS(FontFace)
 #undef GROUP_RULE_FUNCS
 #undef BASIC_RULE_FUNCS
 #undef BASIC_RULE_FUNCS_WITHOUT_GETTER
-SERVO_BINDING_FUNC(Servo_CssRules_GetFontFaceRuleAt, nsCSSFontFaceRule*,
-                   ServoCssRulesBorrowed rules, uint32_t index)
 SERVO_BINDING_FUNC(Servo_CssRules_GetCounterStyleRuleAt, nsCSSCounterStyleRule*,
                    ServoCssRulesBorrowed rules, uint32_t index)
 SERVO_BINDING_FUNC(Servo_StyleRule_GetStyle, RawServoDeclarationBlockStrong,
                    RawServoStyleRuleBorrowed rule)
 SERVO_BINDING_FUNC(Servo_StyleRule_SetStyle, void,
                    RawServoStyleRuleBorrowed rule,
                    RawServoDeclarationBlockBorrowed declarations)
 SERVO_BINDING_FUNC(Servo_StyleRule_GetSelectorText, void,
@@ -338,16 +337,41 @@ SERVO_BINDING_FUNC(Servo_SupportsRule_Ge
 SERVO_BINDING_FUNC(Servo_DocumentRule_GetConditionText, void,
                    RawServoDocumentRuleBorrowed rule, nsAString* result)
 SERVO_BINDING_FUNC(Servo_FontFeatureValuesRule_GetFontFamily, void,
                    RawServoFontFeatureValuesRuleBorrowed rule,
                    nsAString* result)
 SERVO_BINDING_FUNC(Servo_FontFeatureValuesRule_GetValueText, void,
                    RawServoFontFeatureValuesRuleBorrowed rule,
                    nsAString* result)
+SERVO_BINDING_FUNC(Servo_FontFaceRule_CreateEmpty, RawServoFontFaceRuleStrong)
+SERVO_BINDING_FUNC(Servo_FontFaceRule_Clone, RawServoFontFaceRuleStrong,
+                   RawServoFontFaceRuleBorrowed rule)
+SERVO_BINDING_FUNC(Servo_FontFaceRule_GetSourceLocation, void,
+                   RawServoFontFaceRuleBorrowed rule,
+                   uint32_t* line, uint32_t* column)
+SERVO_BINDING_FUNC(Servo_FontFaceRule_Length, uint32_t,
+                   RawServoFontFaceRuleBorrowed rule)
+SERVO_BINDING_FUNC(Servo_FontFaceRule_IndexGetter, nsCSSFontDesc,
+                   RawServoFontFaceRuleBorrowed rule, uint32_t index)
+SERVO_BINDING_FUNC(Servo_FontFaceRule_GetDeclCssText, void,
+                   RawServoFontFaceRuleBorrowed rule, nsAString* result)
+SERVO_BINDING_FUNC(Servo_FontFaceRule_GetDescriptor, void,
+                   RawServoFontFaceRuleBorrowed rule,
+                   nsCSSFontDesc desc, nsCSSValueBorrowedMut result)
+SERVO_BINDING_FUNC(Servo_FontFaceRule_GetDescriptorCssText, void,
+                   RawServoFontFaceRuleBorrowed rule,
+                   nsCSSFontDesc desc, nsAString* result)
+SERVO_BINDING_FUNC(Servo_FontFaceRule_SetDescriptor, bool,
+                   RawServoFontFaceRuleBorrowed rule,
+                   nsCSSFontDesc desc, const nsACString* value,
+                   RawGeckoURLExtraData* data)
+SERVO_BINDING_FUNC(Servo_FontFaceRule_ResetDescriptor, void,
+                   RawServoFontFaceRuleBorrowed rule,
+                   nsCSSFontDesc desc)
 
 // Animations API
 SERVO_BINDING_FUNC(Servo_ParseProperty,
                    RawServoDeclarationBlockStrong,
                    nsCSSPropertyID property, const nsACString* value,
                    RawGeckoURLExtraData* data,
                    mozilla::ParsingMode parsing_mode,
                    nsCompatibility quirks_mode,
@@ -800,21 +824,16 @@ SERVO_BINDING_FUNC(Servo_ParseTransformI
                    RawGeckoGfxMatrix4x4* result);
 SERVO_BINDING_FUNC(Servo_ParseCounterStyleName, nsAtom*,
                    const nsACString* value);
 SERVO_BINDING_FUNC(Servo_ParseCounterStyleDescriptor, bool,
                    nsCSSCounterDesc aDescriptor,
                    const nsACString* aValue,
                    RawGeckoURLExtraData* aURLExtraData,
                    nsCSSValue* aResult);
-SERVO_BINDING_FUNC(Servo_ParseFontDescriptor, bool,
-                   nsCSSFontDesc desc_id,
-                   const nsAString* value,
-                   RawGeckoURLExtraData* data,
-                   nsCSSValueBorrowedMut);
 SERVO_BINDING_FUNC(Servo_ParseFontShorthandForMatching, bool,
                    const nsAString* value,
                    RawGeckoURLExtraData* data,
                    RefPtr<SharedFontList>* family,
                    nsCSSValueBorrowedMut style,
                    nsCSSValueBorrowedMut stretch,
                    nsCSSValueBorrowedMut weight);
 
--- a/layout/style/ServoBindingTypes.h
+++ b/layout/style/ServoBindingTypes.h
@@ -4,16 +4,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_ServoBindingTypes_h
 #define mozilla_ServoBindingTypes_h
 
 #include "mozilla/RefPtr.h"
 #include "mozilla/ServoTypes.h"
+#include "mozilla/SheetType.h"
 #include "mozilla/UniquePtr.h"
 #include "mozilla/gfx/Types.h"
 #include "nsCSSPropertyID.h"
 #include "nsStyleAutoArray.h"
 #include "nsTArray.h"
 
 struct RawServoAuthorStyles;
 struct RawServoStyleSet;
@@ -231,9 +232,16 @@ DECL_NULLABLE_BORROWED_REF_TYPE_FOR(RawS
 
 DEFINE_BOXED_TYPE(StyleSet, RawServoStyleSet);
 DEFINE_BOXED_TYPE(AuthorStyles, RawServoAuthorStyles);
 DEFINE_BOXED_TYPE(SelectorList, RawServoSelectorList);
 DEFINE_BOXED_TYPE(SourceSizeList, RawServoSourceSizeList);
 
 #undef DEFINE_BOXED_TYPE
 
+// used for associating sheet type with specific @font-face rules
+struct nsFontFaceRuleContainer
+{
+  RefPtr<RawServoFontFaceRule> mRule;
+  mozilla::SheetType mSheetType;
+};
+
 #endif // mozilla_ServoBindingTypes_h
--- a/layout/style/ServoBindings.cpp
+++ b/layout/style/ServoBindings.cpp
@@ -9,17 +9,16 @@
 #include "ChildIterator.h"
 #include "ErrorReporter.h"
 #include "GeckoProfiler.h"
 #include "gfxFontFamilyList.h"
 #include "gfxFontFeatures.h"
 #include "nsAnimationManager.h"
 #include "nsAttrValueInlines.h"
 #include "nsCSSCounterStyleRule.h"
-#include "nsCSSFontFaceRule.h"
 #include "nsCSSFrameConstructor.h"
 #include "nsCSSProps.h"
 #include "nsCSSParser.h"
 #include "nsCSSPseudoElements.h"
 #include "nsContentUtils.h"
 #include "nsDOMTokenList.h"
 #include "nsDeviceContext.h"
 #include "nsIContentInlines.h"
@@ -2573,43 +2572,16 @@ Gecko_CSSKeywordString(nsCSSKeyword aKey
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aLength);
   const nsCString& value = nsCSSKeywords::GetStringValue(aKeyword);
   *aLength = value.Length();
   return value.get();
 }
 
-nsCSSFontFaceRule*
-Gecko_CSSFontFaceRule_Create(uint32_t aLine, uint32_t aColumn)
-{
-  RefPtr<nsCSSFontFaceRule> rule = new nsCSSFontFaceRule(aLine, aColumn);
-  return rule.forget().take();
-}
-
-nsCSSFontFaceRule*
-Gecko_CSSFontFaceRule_Clone(const nsCSSFontFaceRule* aRule)
-{
-  RefPtr<css::Rule> rule = aRule->Clone();
-  return static_cast<nsCSSFontFaceRule*>(rule.forget().take());
-}
-
-void
-Gecko_CSSFontFaceRule_GetCssText(const nsCSSFontFaceRule* aRule,
-                                 nsAString* aResult)
-{
-  // GetCSSText serializes nsCSSValues, which have a heap write
-  // hazard when dealing with color values (nsCSSKeywords::AddRefTable)
-  // We only serialize on the main thread; assert to convince the analysis
-  // and prevent accidentally calling this elsewhere
-  MOZ_ASSERT(NS_IsMainThread());
-
-  aRule->GetCssText(*aResult);
-}
-
 void
 Gecko_AddPropertyToSet(nsCSSPropertyIDSetBorrowedMut aPropertySet,
                        nsCSSPropertyID aProperty)
 {
   aPropertySet->AddProperty(aProperty);
 }
 
 int32_t
@@ -2624,18 +2596,16 @@ Gecko_RegisterNamespace(nsAtom* aNamespa
   nsresult rv = nsContentUtils::NameSpaceManager()->RegisterNameSpace(str, id);
 
   if (NS_FAILED(rv)) {
     return -1;
   }
   return id;
 }
 
-NS_IMPL_FFI_REFCOUNTING(nsCSSFontFaceRule, CSSFontFaceRule);
-
 nsCSSCounterStyleRule*
 Gecko_CSSCounterStyle_Create(nsAtom* aName)
 {
   RefPtr<nsCSSCounterStyleRule> rule = new nsCSSCounterStyleRule(aName, 0, 0);
   return rule.forget().take();
 }
 
 nsCSSCounterStyleRule*
--- a/layout/style/ServoBindings.h
+++ b/layout/style/ServoBindings.h
@@ -58,17 +58,16 @@ namespace mozilla {
   class ServoElementSnapshotTable;
 }
 using mozilla::FontFamilyList;
 using mozilla::FontFamilyName;
 using mozilla::FontFamilyType;
 using mozilla::ServoElementSnapshot;
 using mozilla::SharedFontList;
 class nsCSSCounterStyleRule;
-class nsCSSFontFaceRule;
 struct nsMediaFeature;
 class nsSimpleContentList;
 struct nsStyleList;
 struct nsStyleImage;
 struct nsStyleGradientStop;
 class nsStyleGradient;
 class nsStyleCoord;
 struct nsStyleDisplay;
@@ -624,23 +623,16 @@ mozilla::ServoStyleSheet* Gecko_StyleShe
     const mozilla::ServoStyleSheet* aSheet,
     const mozilla::ServoStyleSheet* aNewParentSheet);
 void Gecko_StyleSheet_AddRef(const mozilla::ServoStyleSheet* aSheet);
 void Gecko_StyleSheet_Release(const mozilla::ServoStyleSheet* aSheet);
 
 nsCSSKeyword Gecko_LookupCSSKeyword(const uint8_t* string, uint32_t len);
 const char* Gecko_CSSKeywordString(nsCSSKeyword keyword, uint32_t* len);
 
-// Font face rule
-// Creates and returns a new (already-addrefed) nsCSSFontFaceRule object.
-nsCSSFontFaceRule* Gecko_CSSFontFaceRule_Create(uint32_t line, uint32_t column);
-nsCSSFontFaceRule* Gecko_CSSFontFaceRule_Clone(const nsCSSFontFaceRule* rule);
-void Gecko_CSSFontFaceRule_GetCssText(const nsCSSFontFaceRule* rule, nsAString* result);
-NS_DECL_FFI_REFCOUNTING(nsCSSFontFaceRule, CSSFontFaceRule);
-
 // Counter style rule
 // Creates and returns a new (already-addrefed) nsCSSCounterStyleRule object.
 nsCSSCounterStyleRule* Gecko_CSSCounterStyle_Create(nsAtom* name);
 nsCSSCounterStyleRule* Gecko_CSSCounterStyle_Clone(const nsCSSCounterStyleRule* rule);
 void Gecko_CSSCounterStyle_GetCssText(const nsCSSCounterStyleRule* rule, nsAString* result);
 NS_DECL_FFI_REFCOUNTING(nsCSSCounterStyleRule, CSSCounterStyleRule);
 
 bool Gecko_IsDocumentBody(RawGeckoElementBorrowed element);
--- a/layout/style/ServoBindings.toml
+++ b/layout/style/ServoBindings.toml
@@ -74,17 +74,16 @@ headers = [
     "mozilla/StaticPrefs.h",
     "mozilla/ServoBindings.h",
     "mozilla/ServoMediaList.h",
     "mozilla/ComputedStyle.h",
     "mozilla/ServoDeclarationBlock.h",
     "mozilla/ServoTraversalStatistics.h",
     "mozilla/SizeOfState.h",
     "nsCSSCounterStyleRule.h",
-    "nsCSSFontFaceRule.h",
     "nsContentUtils.h",
     "nsNameSpaceManager.h",
     "nsMediaFeatures.h",
     "nsXBLBinding.h",
 ]
 raw-lines = [
     # FIXME(emilio): Incrementally remove these "pub use"s. Probably
     # mozilla::css and mozilla::dom are easier.
@@ -276,17 +275,16 @@ whitelist-types = [
     "MediumFeaturesChangedResult",
     "nsAttrName",
     "nsAttrValue",
     "nscolor",
     "nsChangeHint",
     "nsCSSCounterDesc",
     "nsCSSCounterStyleRule",
     "nsCSSFontDesc",
-    "nsCSSFontFaceRule",
     "nsCSSKeyword",
     "nsCSSPropertyID",
     "nsCSSPropertyIDSet",
     "nsCSSProps",
     "nsCSSRect",
     "nsCSSRect_heap",
     "nsCSSShadowArray",
     "nsCSSValue",
@@ -492,16 +490,17 @@ structs-types = [
     "RawGeckoElement",
     "Element",
     "RawGeckoKeyframeList",
     "RawGeckoPropertyValuePairList",
     "RawGeckoComputedKeyframeValuesList",
     "RawGeckoFontFaceRuleList",
     "RawGeckoNode",
     "RawServoAnimationValue",
+    "RawServoFontFaceRule",
     "RawGeckoServoAnimationValueList",
     "RawServoMediaList",
     "RawServoStyleSheetContents",
     "RawServoDeclarationBlock",
     "RawServoStyleRule",
     "RawGeckoPresContext",
     "RawGeckoPresContextOwned",
     "RawGeckoStyleAnimationList",
@@ -531,17 +530,16 @@ structs-types = [
     "SheetParsingMode",
     "StyleBasicShapeType",
     "StyleShapeSource",
     "StyleTransition",
     "gfxFontFeatureValueSet",
     "nsCSSCounterDesc",
     "nsCSSCounterStyleRule",
     "nsCSSFontDesc",
-    "nsCSSFontFaceRule",
     "nsCSSKeyword",
     "nsCSSPropertyID",
     "nsCSSPropertyIDSet",
     "nsCSSRect",
     "nsCSSShadowArray",
     "nsCSSUnit",
     "nsCSSValue",
     "nsCSSValueSharedList",
--- a/layout/style/ServoCSSParser.cpp
+++ b/layout/style/ServoCSSParser.cpp
@@ -88,25 +88,16 @@ ServoCSSParser::ParseTransformIntoMatrix
                                          RawGeckoGfxMatrix4x4& aResult)
 {
   return Servo_ParseTransformIntoMatrix(&aValue,
                                         &aContains3DTransform,
                                         &aResult);
 }
 
 /* static */ bool
-ServoCSSParser::ParseFontDescriptor(nsCSSFontDesc aDescID,
-                                    const nsAString& aValue,
-                                    URLExtraData* aUrl,
-                                    nsCSSValue& aResult)
-{
-  return Servo_ParseFontDescriptor(aDescID, &aValue, aUrl, &aResult);
-}
-
-/* static */ bool
 ServoCSSParser::ParseFontShorthandForMatching(const nsAString& aValue,
                                               URLExtraData* aUrl,
                                               RefPtr<SharedFontList>& aList,
                                               nsCSSValue& aStyle,
                                               nsCSSValue& aStretch,
                                               nsCSSValue& aWeight)
 {
   return Servo_ParseFontShorthandForMatching(&aValue, aUrl, &aList,
--- a/layout/style/ServoCSSParser.h
+++ b/layout/style/ServoCSSParser.h
@@ -147,30 +147,16 @@ public:
    * @param aResult The output matrix. (output)
    * @return Whether the value was successfully parsed.
    */
   static bool ParseTransformIntoMatrix(const nsAString& aValue,
                                        bool& aContains3DTransform,
                                        RawGeckoGfxMatrix4x4& aResult);
 
   /**
-   * Parse a font descriptor.
-   *
-   * @param aDescID The font descriptor id.
-   * @param aValue The specified value.
-   * @param aUrl The parser url extra data.
-   * @param aResult The parsed result. (output)
-   * @return Whether the value was successfully parsed.
-   */
-  static bool ParseFontDescriptor(nsCSSFontDesc aDescID,
-                                  const nsAString& aValue,
-                                  URLExtraData* aUrl,
-                                  nsCSSValue& aResult);
-
-  /**
    * Parse a font shorthand for FontFaceSet matching, so we only care about
    * FontFamily, FontStyle, FontStretch, and FontWeight.
    *
    * @param aValue The specified value.
    * @param aUrl The parser url extra data.
    * @param aList The parsed FontFamily list. (output)
    * @param aStyle The parsed FontStyle. (output)
    * @param aStretch The parsed FontStretch. (output)
--- a/layout/style/ServoCSSRuleList.cpp
+++ b/layout/style/ServoCSSRuleList.cpp
@@ -7,26 +7,26 @@
 /* representation of CSSRuleList for stylo */
 
 #include "mozilla/ServoCSSRuleList.h"
 
 #include "mozilla/IntegerRange.h"
 #include "mozilla/ServoBindings.h"
 #include "mozilla/ServoDocumentRule.h"
 #include "mozilla/ServoImportRule.h"
+#include "mozilla/ServoFontFaceRule.h"
 #include "mozilla/ServoFontFeatureValuesRule.h"
 #include "mozilla/ServoKeyframesRule.h"
 #include "mozilla/ServoMediaRule.h"
 #include "mozilla/ServoNamespaceRule.h"
 #include "mozilla/ServoPageRule.h"
 #include "mozilla/ServoStyleRule.h"
 #include "mozilla/ServoStyleSheet.h"
 #include "mozilla/ServoSupportsRule.h"
 #include "nsCSSCounterStyleRule.h"
-#include "nsCSSFontFaceRule.h"
 
 using namespace mozilla::dom;
 
 namespace mozilla {
 
 ServoCSSRuleList::ServoCSSRuleList(already_AddRefed<ServoCssRules> aRawRules,
                                    ServoStyleSheet* aDirectOwnerStyleSheet)
   : mStyleSheet(aDirectOwnerStyleSheet)
@@ -48,23 +48,21 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Se
   tmp->DropAllRules();
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END_INHERITED(dom::CSSRuleList)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(ServoCSSRuleList,
                                                   dom::CSSRuleList)
   tmp->EnumerateInstantiatedRules([&](css::Rule* aRule) {
     if (!aRule->IsCCLeaf()) {
       NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mRules[i]");
       cb.NoteXPCOMChild(aRule);
-      // Note about @font-face and @counter-style rule again, since
-      // there is an indirect owning edge through Servo's struct that
-      // FontFaceRule / CounterStyleRule in Servo owns a Gecko
-      // nsCSSFontFaceRule / nsCSSCounterStyleRule object.
+      // Note about @counter-style rule again, since there is an indirect owning
+      // edge through Servo's struct that CounterStyleRule in Servo owns a Gecko
+      // nsCSSCounterStyleRule object.
       auto type = aRule->Type();
-      if (type == CSSRuleBinding::FONT_FACE_RULE ||
-          type == CSSRuleBinding::COUNTER_STYLE_RULE) {
+      if (type == CSSRuleBinding::COUNTER_STYLE_RULE) {
         NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mRawRules[i]");
         cb.NoteXPCOMChild(aRule);
       }
     }
   });
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 void
@@ -107,25 +105,22 @@ ServoCSSRuleList::GetRule(uint32_t aInde
       CASE_RULE(KEYFRAMES, Keyframes)
       CASE_RULE(MEDIA, Media)
       CASE_RULE(NAMESPACE, Namespace)
       CASE_RULE(PAGE, Page)
       CASE_RULE(SUPPORTS, Supports)
       CASE_RULE(DOCUMENT, Document)
       CASE_RULE(IMPORT, Import)
       CASE_RULE(FONT_FEATURE_VALUES, FontFeatureValues)
+      CASE_RULE(FONT_FACE, FontFace)
 #undef CASE_RULE
-      // For @font-face and @counter-style rules, the function returns
-      // a borrowed Gecko rule object directly, so we don't need to
-      // create anything here. But we still need to have the style sheet
-      // and parent rule set properly.
-      case CSSRuleBinding::FONT_FACE_RULE: {
-        ruleObj = Servo_CssRules_GetFontFaceRuleAt(mRawRules, aIndex);
-        break;
-      }
+      // For @counter-style rules, the function returns a borrowed Gecko
+      // rule object directly, so we don't need to create anything here.
+      // But we still need to have the style sheet and parent rule set
+      // properly.
       case CSSRuleBinding::COUNTER_STYLE_RULE: {
         ruleObj = Servo_CssRules_GetCounterStyleRuleAt(mRawRules, aIndex);
         break;
       }
       case CSSRuleBinding::KEYFRAME_RULE:
         MOZ_ASSERT_UNREACHABLE("keyframe rule cannot be here");
         return nullptr;
       default:
new file mode 100644
--- /dev/null
+++ b/layout/style/ServoFontFaceRule.cpp
@@ -0,0 +1,248 @@
+/* -*- 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 "mozilla/ServoFontFaceRule.h"
+
+#include "mozilla/dom/CSSFontFaceRuleBinding.h"
+#include "mozilla/dom/CSSStyleDeclarationBinding.h"
+
+using namespace mozilla;
+using namespace mozilla::dom;
+
+// -------------------------------------------
+// ServoFontFaceRuleDecl and related routines
+//
+
+// QueryInterface implementation for ServoFontFaceRuleDecl
+NS_INTERFACE_MAP_BEGIN(ServoFontFaceRuleDecl)
+  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
+  NS_INTERFACE_MAP_ENTRY(nsICSSDeclaration)
+  NS_INTERFACE_MAP_ENTRY(nsISupports)
+  // We forward the cycle collection interfaces to ContainingRule(), which is
+  // never null (in fact, we're part of that object!)
+  if (aIID.Equals(NS_GET_IID(nsCycleCollectionISupports)) ||
+      aIID.Equals(NS_GET_IID(nsXPCOMCycleCollectionParticipant))) {
+    return ContainingRule()->QueryInterface(aIID, aInstancePtr);
+  }
+  else
+NS_INTERFACE_MAP_END
+
+NS_IMPL_ADDREF_USING_AGGREGATOR(ServoFontFaceRuleDecl, ContainingRule())
+NS_IMPL_RELEASE_USING_AGGREGATOR(ServoFontFaceRuleDecl, ContainingRule())
+
+// helper for string GetPropertyValue and RemovePropertyValue
+void
+ServoFontFaceRuleDecl::GetPropertyValue(nsCSSFontDesc aFontDescID,
+                                        nsAString& aResult) const
+{
+  MOZ_ASSERT(aResult.IsEmpty());
+  Servo_FontFaceRule_GetDescriptorCssText(mRawRule, aFontDescID, &aResult);
+}
+
+void
+ServoFontFaceRuleDecl::GetCssText(nsAString& aCssText)
+{
+  aCssText.Truncate();
+  Servo_FontFaceRule_GetDeclCssText(mRawRule, &aCssText);
+}
+
+void
+ServoFontFaceRuleDecl::SetCssText(const nsAString& aCssText,
+                                  nsIPrincipal* aSubjectPrincipal,
+                                  ErrorResult& aRv)
+{
+  aRv.Throw(NS_ERROR_NOT_IMPLEMENTED); // bug 443978
+}
+
+NS_IMETHODIMP
+ServoFontFaceRuleDecl::GetPropertyValue(const nsAString& aPropName,
+                                        nsAString& aResult)
+{
+  aResult.Truncate();
+  GetPropertyValue(nsCSSProps::LookupFontDesc(aPropName), aResult);
+  return NS_OK;
+}
+
+already_AddRefed<dom::CSSValue>
+ServoFontFaceRuleDecl::GetPropertyCSSValue(const nsAString& aPropName,
+                                           ErrorResult& aRv)
+{
+  // ??? nsDOMCSSDeclaration returns null/NS_OK, but that seems wrong.
+  aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
+  return nullptr;
+}
+
+NS_IMETHODIMP
+ServoFontFaceRuleDecl::RemoveProperty(const nsAString& aPropName,
+                                      nsAString& aResult)
+{
+  nsCSSFontDesc descID = nsCSSProps::LookupFontDesc(aPropName);
+  NS_ASSERTION(descID >= eCSSFontDesc_UNKNOWN &&
+               descID < eCSSFontDesc_COUNT,
+               "LookupFontDesc returned value out of range");
+
+  aResult.Truncate();
+  if (descID != eCSSFontDesc_UNKNOWN) {
+    GetPropertyValue(descID, aResult);
+    Servo_FontFaceRule_ResetDescriptor(mRawRule, descID);
+  }
+  return NS_OK;
+}
+
+void
+ServoFontFaceRuleDecl::GetPropertyPriority(const nsAString& aPropName,
+                                           nsAString& aResult)
+{
+  // font descriptors do not have priorities at present
+  aResult.Truncate();
+}
+
+NS_IMETHODIMP
+ServoFontFaceRuleDecl::SetProperty(const nsAString& aPropName,
+                                   const nsAString& aValue,
+                                   const nsAString& aPriority,
+                                   nsIPrincipal* aSubjectPrincipal)
+{
+  // FIXME(heycam): If we are changing unicode-range, then a FontFace object
+  // representing this rule must have its mUnicodeRange value invalidated.
+
+  return NS_ERROR_NOT_IMPLEMENTED; // bug 443978
+}
+
+uint32_t
+ServoFontFaceRuleDecl::Length()
+{
+  return Servo_FontFaceRule_Length(mRawRule);
+}
+
+void
+ServoFontFaceRuleDecl::IndexedGetter(uint32_t aIndex, bool& aFound,
+                                     nsAString& aResult)
+{
+  nsCSSFontDesc id = Servo_FontFaceRule_IndexGetter(mRawRule, aIndex);
+  if (id != eCSSFontDesc_UNKNOWN) {
+    aFound = true;
+    aResult.AssignASCII(nsCSSProps::GetStringValue(id).get());
+  } else {
+    aFound = false;
+  }
+}
+
+css::Rule*
+ServoFontFaceRuleDecl::GetParentRule()
+{
+  return ContainingRule();
+}
+
+nsINode*
+ServoFontFaceRuleDecl::GetParentObject()
+{
+  return ContainingRule()->GetDocument();
+}
+
+JSObject*
+ServoFontFaceRuleDecl::WrapObject(JSContext *cx, JS::Handle<JSObject*> aGivenProto)
+{
+  return CSSStyleDeclarationBinding::Wrap(cx, this, aGivenProto);
+}
+
+// -------------------------------------------
+// ServoFontFaceRule
+//
+
+/* virtual */ already_AddRefed<css::Rule>
+ServoFontFaceRule::Clone() const
+{
+  RefPtr<RawServoFontFaceRule> rule = Servo_FontFaceRule_Clone(Raw()).Consume();
+  RefPtr<css::Rule> clone = new ServoFontFaceRule(rule.forget(),
+                                                  GetLineNumber(),
+                                                  GetColumnNumber());
+  return clone.forget();
+}
+
+NS_IMPL_CYCLE_COLLECTION_CLASS(ServoFontFaceRule)
+
+NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(ServoFontFaceRule,
+                                               mozilla::css::Rule)
+  // Keep this in sync with IsCCLeaf.
+
+  // Trace the wrapper for our declaration.  This just expands out
+  // NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER which we can't use
+  // directly because the wrapper is on the declaration, not on us.
+  tmp->mDecl.TraceWrapper(aCallbacks, aClosure);
+NS_IMPL_CYCLE_COLLECTION_TRACE_END
+
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(ServoFontFaceRule,
+                                                mozilla::css::Rule)
+  // Keep this in sync with IsCCLeaf.
+
+  // Unlink the wrapper for our declaraton.  This just expands out
+  // NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER which we can't use
+  // directly because the wrapper is on the declaration, not on us.
+  tmp->mDecl.ReleaseWrapper(static_cast<nsISupports*>(p));
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(ServoFontFaceRule,
+                                                  mozilla::css::Rule)
+  // Keep this in sync with IsCCLeaf.
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
+bool
+ServoFontFaceRule::IsCCLeaf() const
+{
+  if (!Rule::IsCCLeaf()) {
+    return false;
+  }
+
+  return !mDecl.PreservingWrapper();
+}
+
+NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED_0(ServoFontFaceRule, mozilla::css::Rule)
+
+#ifdef DEBUG
+void
+ServoFontFaceRule::List(FILE* out, int32_t aIndent) const
+{
+  nsAutoCString str;
+  for (int32_t i = 0; i < aIndent; i++) {
+    str.AppendLiteral("  ");
+  }
+  Servo_FontFaceRule_Debug(Raw(), &str);
+  fprintf_stderr(out, "%s\n", str.get());
+}
+#endif
+
+uint16_t
+ServoFontFaceRule::Type() const
+{
+  return CSSRuleBinding::FONT_FACE_RULE;
+}
+
+void
+ServoFontFaceRule::GetCssText(nsAString& aCssText) const
+{
+  aCssText.Truncate();
+  Servo_FontFaceRule_GetCssText(Raw(), &aCssText);
+}
+
+nsICSSDeclaration*
+ServoFontFaceRule::Style()
+{
+  return &mDecl;
+}
+
+/* virtual */ size_t
+ServoFontFaceRule::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
+{
+  return aMallocSizeOf(this);
+}
+
+/* virtual */ JSObject*
+ServoFontFaceRule::WrapObject(JSContext* aCx,
+                              JS::Handle<JSObject*> aGivenProto)
+{
+  return CSSFontFaceRuleBinding::Wrap(aCx, this, aGivenProto);
+}
new file mode 100644
--- /dev/null
+++ b/layout/style/ServoFontFaceRule.h
@@ -0,0 +1,112 @@
+/* -*- 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/. */
+
+#ifndef mozilla_ServoFontFaceRule_h
+#define mozilla_ServoFontFaceRule_h
+
+#include "mozilla/ServoBindingTypes.h"
+#include "mozilla/css/Rule.h"
+#include "nsICSSDeclaration.h"
+
+namespace mozilla {
+
+// A ServoFontFaceRuleDecl is always embeded in a ServoFontFaceRule.
+class ServoFontFaceRule;
+class ServoFontFaceRuleDecl final : public nsICSSDeclaration
+{
+public:
+  NS_DECL_ISUPPORTS_INHERITED
+  NS_DECL_NSIDOMCSSSTYLEDECLARATION_HELPER
+
+  already_AddRefed<dom::CSSValue>
+  GetPropertyCSSValue(const nsAString& aProp, ErrorResult& aRv) final;
+  using nsICSSDeclaration::GetPropertyCSSValue;
+
+  nsINode* GetParentObject() final;
+  void IndexedGetter(uint32_t aIndex, bool& aFound, nsAString& aPropName) final;
+
+  void GetPropertyValue(nsCSSFontDesc aFontDescID, nsAString& aResult) const;
+
+  JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) final;
+
+protected:
+  // For accessing the constructor.
+  friend class ServoFontFaceRule;
+
+  explicit ServoFontFaceRuleDecl(already_AddRefed<RawServoFontFaceRule> aDecl)
+    : mRawRule(Move(aDecl)) {}
+
+  ~ServoFontFaceRuleDecl() = default;
+
+  inline ServoFontFaceRule* ContainingRule();
+  inline const ServoFontFaceRule* ContainingRule() const;
+
+  RefPtr<RawServoFontFaceRule> mRawRule;
+
+private:
+  void* operator new(size_t size) CPP_THROW_NEW = delete;
+};
+
+class ServoFontFaceRule final : public css::Rule
+{
+public:
+  ServoFontFaceRule(already_AddRefed<RawServoFontFaceRule> aRawRule,
+                    uint32_t aLine, uint32_t aColumn)
+    : css::Rule(aLine, aColumn)
+    , mDecl(Move(aRawRule))
+  {}
+
+  ServoFontFaceRule(const ServoFontFaceRule&) = delete;
+
+  NS_DECL_ISUPPORTS_INHERITED
+  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(
+      ServoFontFaceRule, css::Rule)
+  bool IsCCLeaf() const final;
+
+  RawServoFontFaceRule* Raw() const { return mDecl.mRawRule; }
+
+  // WebIDL interface
+  uint16_t Type() const final;
+  void GetCssText(nsAString& aCssText) const final;
+  nsICSSDeclaration* Style();
+
+  // Methods of mozilla::css::Rule
+  already_AddRefed<css::Rule> Clone() const final;
+  size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf)
+    const final;
+
+  JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) final;
+
+#ifdef DEBUG
+  void List(FILE* out = stdout, int32_t aIndent = 0) const final;
+#endif
+
+private:
+  virtual ~ServoFontFaceRule() = default;
+
+  // For computing the offset of mDecl.
+  friend class ServoFontFaceRuleDecl;
+
+  ServoFontFaceRuleDecl mDecl;
+};
+
+inline ServoFontFaceRule*
+ServoFontFaceRuleDecl::ContainingRule()
+{
+  return reinterpret_cast<ServoFontFaceRule*>
+    (reinterpret_cast<char*>(this) - offsetof(ServoFontFaceRule, mDecl));
+}
+
+inline const ServoFontFaceRule*
+ServoFontFaceRuleDecl::ContainingRule() const
+{
+  return reinterpret_cast<const ServoFontFaceRule*>
+    (reinterpret_cast<const char*>(this) - offsetof(ServoFontFaceRule, mDecl));
+}
+
+} // namespace mozilla
+
+#endif // mozilla_ServoFontFaceRule_h
--- a/layout/style/moz.build
+++ b/layout/style/moz.build
@@ -27,17 +27,16 @@ EXPORTS += [
     'CounterStyleManager.h',
     'nsAnimationManager.h',
     'nsComputedDOMStylePropertyList.h',
     'nsCSSAnonBoxes.h',
     'nsCSSAnonBoxList.h',
     'nsCSSCounterDescList.h',
     'nsCSSCounterStyleRule.h',
     'nsCSSFontDescList.h',
-    'nsCSSFontFaceRule.h',
     'nsCSSKeywordList.h',
     'nsCSSKeywords.h',
     'nsCSSParser.h',
     'nsCSSPropAliasList.h',
     'nsCSSPropertyID.h',
     'nsCSSPropertyIDSet.h',
     'nsCSSPropList.h',
     'nsCSSProps.h',
@@ -85,16 +84,17 @@ EXPORTS.mozilla += [
     'ServoBindings.h',
     'ServoBindingTypes.h',
     'ServoCSSParser.h',
     'ServoCSSRuleList.h',
     'ServoDeclarationBlock.h',
     'ServoDocumentRule.h',
     'ServoElementSnapshot.h',
     'ServoElementSnapshotTable.h',
+    'ServoFontFaceRule.h',
     'ServoFontFeatureValuesRule.h',
     'ServoImportRule.h',
     'ServoKeyframeRule.h',
     'ServoKeyframesRule.h',
     'ServoMediaList.h',
     'ServoMediaRule.h',
     'ServoNamespaceRule.h',
     'ServoPageRule.h',
@@ -176,17 +176,16 @@ UNIFIED_SOURCES += [
     'ImageLoader.cpp',
     'LayerAnimationInfo.cpp',
     'Loader.cpp',
     'MediaList.cpp',
     'MediaQueryList.cpp',
     'nsAnimationManager.cpp',
     'nsComputedDOMStyle.cpp',
     'nsCSSCounterStyleRule.cpp',
-    'nsCSSFontFaceRule.cpp',
     'nsCSSKeywords.cpp',
     'nsCSSProps.cpp',
     'nsCSSScanner.cpp',
     'nsCSSValue.cpp',
     'nsDOMCSSAttrDeclaration.cpp',
     'nsDOMCSSDeclaration.cpp',
     'nsDOMCSSRect.cpp',
     'nsDOMCSSRGBColor.cpp',
@@ -207,16 +206,17 @@ UNIFIED_SOURCES += [
     'PreloadedStyleSheet.cpp',
     'Rule.cpp',
     'ServoBindings.cpp',
     'ServoCSSParser.cpp',
     'ServoCSSRuleList.cpp',
     'ServoDeclarationBlock.cpp',
     'ServoDocumentRule.cpp',
     'ServoElementSnapshot.cpp',
+    'ServoFontFaceRule.cpp',
     'ServoFontFeatureValuesRule.cpp',
     'ServoImportRule.cpp',
     'ServoKeyframeRule.cpp',
     'ServoKeyframesRule.cpp',
     'ServoMediaList.cpp',
     'ServoMediaRule.cpp',
     'ServoNamespaceRule.cpp',
     'ServoPageRule.cpp',
deleted file mode 100644
--- a/layout/style/nsCSSFontFaceRule.cpp
+++ /dev/null
@@ -1,429 +0,0 @@
-/* -*- 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/. */
-
-/* a Gecko @font-face rule */
-
-#include "nsCSSFontFaceRule.h"
-
-#include "mozilla/dom/CSSFontFaceRuleBinding.h"
-#include "mozilla/dom/CSSStyleDeclarationBinding.h"
-#include "nsStyleUtil.h"
-
-using namespace mozilla;
-using namespace mozilla::dom;
-
-// -------------------------------------------
-// nsCSSFontFaceStyleDecl and related routines
-//
-
-// Mapping from nsCSSFontDesc codes to CSSFontFaceDescriptors fields.
-nsCSSValue CSSFontFaceDescriptors::* const
-CSSFontFaceDescriptors::Fields[] = {
-#define CSS_FONT_DESC(name_, method_) &CSSFontFaceDescriptors::m##method_,
-#include "nsCSSFontDescList.h"
-#undef CSS_FONT_DESC
-};
-
-const nsCSSValue&
-CSSFontFaceDescriptors::Get(nsCSSFontDesc aFontDescID) const
-{
-  MOZ_ASSERT(aFontDescID > eCSSFontDesc_UNKNOWN &&
-             aFontDescID < eCSSFontDesc_COUNT);
-  return this->*CSSFontFaceDescriptors::Fields[aFontDescID];
-}
-
-nsCSSValue&
-CSSFontFaceDescriptors::Get(nsCSSFontDesc aFontDescID)
-{
-  MOZ_ASSERT(aFontDescID > eCSSFontDesc_UNKNOWN &&
-             aFontDescID < eCSSFontDesc_COUNT);
-  return this->*CSSFontFaceDescriptors::Fields[aFontDescID];
-}
-
-// QueryInterface implementation for nsCSSFontFaceStyleDecl
-NS_INTERFACE_MAP_BEGIN(nsCSSFontFaceStyleDecl)
-  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
-  NS_INTERFACE_MAP_ENTRY(nsICSSDeclaration)
-  NS_INTERFACE_MAP_ENTRY(nsISupports)
-  // We forward the cycle collection interfaces to ContainingRule(), which is
-  // never null (in fact, we're part of that object!)
-  if (aIID.Equals(NS_GET_IID(nsCycleCollectionISupports)) ||
-      aIID.Equals(NS_GET_IID(nsXPCOMCycleCollectionParticipant))) {
-    return ContainingRule()->QueryInterface(aIID, aInstancePtr);
-  }
-  else
-NS_INTERFACE_MAP_END
-
-NS_IMPL_ADDREF_USING_AGGREGATOR(nsCSSFontFaceStyleDecl, ContainingRule())
-NS_IMPL_RELEASE_USING_AGGREGATOR(nsCSSFontFaceStyleDecl, ContainingRule())
-
-// helper for string GetPropertyValue and RemovePropertyValue
-nsresult
-nsCSSFontFaceStyleDecl::GetPropertyValue(nsCSSFontDesc aFontDescID,
-                                         nsAString & aResult) const
-{
-  NS_ENSURE_ARG_RANGE(aFontDescID, eCSSFontDesc_UNKNOWN,
-                      eCSSFontDesc_COUNT - 1);
-
-  aResult.Truncate();
-  if (aFontDescID == eCSSFontDesc_UNKNOWN)
-    return NS_OK;
-
-  const nsCSSValue& val = mDescriptors.Get(aFontDescID);
-
-  if (val.GetUnit() == eCSSUnit_Null) {
-    // Avoid having to check no-value in the Family and Src cases below.
-    return NS_OK;
-  }
-
-  switch (aFontDescID) {
-  case eCSSFontDesc_Family: {
-      // we don't use nsCSSValue::AppendToString here because it doesn't
-      // canonicalize the way we want, and anyway it's overkill when
-      // we know we have eCSSUnit_String
-      NS_ASSERTION(val.GetUnit() == eCSSUnit_String, "unexpected unit");
-      nsDependentString family(val.GetStringBufferValue());
-      nsStyleUtil::AppendEscapedCSSString(family, aResult);
-      return NS_OK;
-    }
-
-  case eCSSFontDesc_Style:
-    val.AppendToString(eCSSProperty_font_style, aResult);
-    return NS_OK;
-
-  case eCSSFontDesc_Weight:
-    val.AppendToString(eCSSProperty_font_weight, aResult);
-    return NS_OK;
-
-  case eCSSFontDesc_Stretch:
-    val.AppendToString(eCSSProperty_font_stretch, aResult);
-    return NS_OK;
-
-  case eCSSFontDesc_FontFeatureSettings:
-    nsStyleUtil::AppendFontFeatureSettings(val, aResult);
-    return NS_OK;
-
-  case eCSSFontDesc_FontVariationSettings:
-    nsStyleUtil::AppendFontVariationSettings(val, aResult);
-    return NS_OK;
-
-  case eCSSFontDesc_FontLanguageOverride:
-    val.AppendToString(eCSSProperty_font_language_override, aResult);
-    return NS_OK;
-
-  case eCSSFontDesc_Display:
-    NS_ASSERTION(val.GetUnit() == eCSSUnit_Enumerated,
-                 "unknown unit for font-display descriptor");
-    AppendASCIItoUTF16(nsCSSProps::ValueToKeyword(val.GetIntValue(),
-                                       nsCSSProps::kFontDisplayKTable), aResult);
-    return NS_OK;
-
-  case eCSSFontDesc_Src:
-    nsStyleUtil::AppendSerializedFontSrc(val, aResult);
-    return NS_OK;
-
-  case eCSSFontDesc_UnicodeRange:
-    nsStyleUtil::AppendUnicodeRange(val, aResult);
-    return NS_OK;
-
-  case eCSSFontDesc_UNKNOWN:
-  case eCSSFontDesc_COUNT:
-    ;
-  }
-  NS_NOTREACHED("nsCSSFontFaceStyleDecl::GetPropertyValue: "
-                "out-of-range value got to the switch");
-  return NS_ERROR_INVALID_ARG;
-}
-
-
-void
-nsCSSFontFaceStyleDecl::GetCssText(nsAString & aCssText)
-{
-  GetCssTextImpl(aCssText);
-}
-
-void
-nsCSSFontFaceStyleDecl::GetCssTextImpl(nsAString& aCssText) const
-{
-  nsAutoString descStr;
-
-  aCssText.Truncate();
-  for (nsCSSFontDesc id = nsCSSFontDesc(eCSSFontDesc_UNKNOWN + 1);
-       id < eCSSFontDesc_COUNT;
-       id = nsCSSFontDesc(id + 1)) {
-    if (mDescriptors.Get(id).GetUnit() != eCSSUnit_Null &&
-        NS_SUCCEEDED(GetPropertyValue(id, descStr))) {
-      NS_ASSERTION(descStr.Length() > 0,
-                   "GetCssText: non-null unit, empty property value");
-      aCssText.AppendLiteral("  ");
-      aCssText.AppendASCII(nsCSSProps::GetStringValue(id).get());
-      aCssText.AppendLiteral(": ");
-      aCssText.Append(descStr);
-      aCssText.AppendLiteral(";\n");
-    }
-  }
-}
-
-void
-nsCSSFontFaceStyleDecl::SetCssText(const nsAString& aCssText,
-                                   nsIPrincipal* aSubjectPrincipal,
-                                   ErrorResult& aRv)
-{
-  aRv.Throw(NS_ERROR_NOT_IMPLEMENTED); // bug 443978
-}
-
-NS_IMETHODIMP
-nsCSSFontFaceStyleDecl::GetPropertyValue(const nsAString & propertyName,
-                                         nsAString & aResult)
-{
-  return GetPropertyValue(nsCSSProps::LookupFontDesc(propertyName), aResult);
-}
-
-already_AddRefed<dom::CSSValue>
-nsCSSFontFaceStyleDecl::GetPropertyCSSValue(const nsAString & propertyName,
-                                            ErrorResult& aRv)
-{
-  // ??? nsDOMCSSDeclaration returns null/NS_OK, but that seems wrong.
-  aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
-  return nullptr;
-}
-
-NS_IMETHODIMP
-nsCSSFontFaceStyleDecl::RemoveProperty(const nsAString & propertyName,
-                                       nsAString & aResult)
-{
-  nsCSSFontDesc descID = nsCSSProps::LookupFontDesc(propertyName);
-  NS_ASSERTION(descID >= eCSSFontDesc_UNKNOWN &&
-               descID < eCSSFontDesc_COUNT,
-               "LookupFontDesc returned value out of range");
-
-  if (descID == eCSSFontDesc_UNKNOWN) {
-    aResult.Truncate();
-  } else {
-    nsresult rv = GetPropertyValue(descID, aResult);
-    NS_ENSURE_SUCCESS(rv, rv);
-    mDescriptors.Get(descID).Reset();
-  }
-  return NS_OK;
-}
-
-void
-nsCSSFontFaceStyleDecl::GetPropertyPriority(const nsAString & propertyName,
-                                            nsAString & aResult)
-{
-  // font descriptors do not have priorities at present
-  aResult.Truncate();
-}
-
-NS_IMETHODIMP
-nsCSSFontFaceStyleDecl::SetProperty(const nsAString& propertyName,
-                                    const nsAString& value,
-                                    const nsAString& priority,
-                                    nsIPrincipal* aSubjectPrincipal)
-{
-  // FIXME(heycam): If we are changing unicode-range, then a FontFace object
-  // representing this rule must have its mUnicodeRange value invalidated.
-
-  return NS_ERROR_NOT_IMPLEMENTED; // bug 443978
-}
-
-uint32_t
-nsCSSFontFaceStyleDecl::Length()
-{
-  uint32_t len = 0;
-  for (nsCSSFontDesc id = nsCSSFontDesc(eCSSFontDesc_UNKNOWN + 1);
-       id < eCSSFontDesc_COUNT;
-       id = nsCSSFontDesc(id + 1)) {
-    if (mDescriptors.Get(id).GetUnit() != eCSSUnit_Null) {
-      len++;
-    }
-  }
-
-  return len;
-}
-
-void
-nsCSSFontFaceStyleDecl::IndexedGetter(uint32_t index, bool& aFound, nsAString & aResult)
-{
-  int32_t nset = -1;
-  for (nsCSSFontDesc id = nsCSSFontDesc(eCSSFontDesc_UNKNOWN + 1);
-       id < eCSSFontDesc_COUNT;
-       id = nsCSSFontDesc(id + 1)) {
-    if (mDescriptors.Get(id).GetUnit() != eCSSUnit_Null) {
-      nset++;
-      if (nset == int32_t(index)) {
-        aFound = true;
-        aResult.AssignASCII(nsCSSProps::GetStringValue(id).get());
-        return;
-      }
-    }
-  }
-  aFound = false;
-}
-
-css::Rule*
-nsCSSFontFaceStyleDecl::GetParentRule()
-{
-  return ContainingRule();
-}
-
-nsINode*
-nsCSSFontFaceStyleDecl::GetParentObject()
-{
-  return ContainingRule()->GetDocument();
-}
-
-JSObject*
-nsCSSFontFaceStyleDecl::WrapObject(JSContext *cx, JS::Handle<JSObject*> aGivenProto)
-{
-  return mozilla::dom::CSSStyleDeclarationBinding::Wrap(cx, this, aGivenProto);
-}
-
-// -------------------------------------------
-// nsCSSFontFaceRule
-//
-
-/* virtual */ already_AddRefed<css::Rule>
-nsCSSFontFaceRule::Clone() const
-{
-  RefPtr<css::Rule> clone = new nsCSSFontFaceRule(*this);
-  return clone.forget();
-}
-
-NS_IMPL_CYCLE_COLLECTION_CLASS(nsCSSFontFaceRule)
-
-NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(nsCSSFontFaceRule,
-                                               mozilla::css::Rule)
-  // Keep this in sync with IsCCLeaf.
-
-  // Trace the wrapper for our declaration.  This just expands out
-  // NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER which we can't use
-  // directly because the wrapper is on the declaration, not on us.
-  tmp->mDecl.TraceWrapper(aCallbacks, aClosure);
-NS_IMPL_CYCLE_COLLECTION_TRACE_END
-
-NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsCSSFontFaceRule,
-                                                mozilla::css::Rule)
-  // Keep this in sync with IsCCLeaf.
-
-  // Unlink the wrapper for our declaraton.  This just expands out
-  // NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER which we can't use
-  // directly because the wrapper is on the declaration, not on us.
-  tmp->mDecl.ReleaseWrapper(static_cast<nsISupports*>(p));
-NS_IMPL_CYCLE_COLLECTION_UNLINK_END
-
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsCSSFontFaceRule,
-                                                  mozilla::css::Rule)
-  // Keep this in sync with IsCCLeaf.
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
-
-bool
-nsCSSFontFaceRule::IsCCLeaf() const
-{
-  if (!Rule::IsCCLeaf()) {
-    return false;
-  }
-
-  return !mDecl.PreservingWrapper();
-}
-
-NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED_0(nsCSSFontFaceRule, mozilla::css::Rule)
-
-#ifdef DEBUG
-void
-nsCSSFontFaceRule::List(FILE* out, int32_t aIndent) const
-{
-  nsCString baseInd, descInd;
-  for (int32_t indent = aIndent; --indent >= 0; ) {
-    baseInd.AppendLiteral("  ");
-    descInd.AppendLiteral("  ");
-  }
-  descInd.AppendLiteral("  ");
-
-  nsString descStr;
-
-  fprintf_stderr(out, "%s@font-face {\n", baseInd.get());
-  for (nsCSSFontDesc id = nsCSSFontDesc(eCSSFontDesc_UNKNOWN + 1);
-       id < eCSSFontDesc_COUNT;
-       id = nsCSSFontDesc(id + 1))
-    if (mDecl.mDescriptors.Get(id).GetUnit() != eCSSUnit_Null) {
-      if (NS_FAILED(mDecl.GetPropertyValue(id, descStr)))
-        descStr.AssignLiteral("#<serialization error>");
-      else if (descStr.Length() == 0)
-        descStr.AssignLiteral("#<serialization missing>");
-      fprintf_stderr(out, "%s%s: %s\n",
-                     descInd.get(), nsCSSProps::GetStringValue(id).get(),
-                     NS_ConvertUTF16toUTF8(descStr).get());
-    }
-  fprintf_stderr(out, "%s}\n", baseInd.get());
-}
-#endif
-
-uint16_t
-nsCSSFontFaceRule::Type() const
-{
-  return CSSRuleBinding::FONT_FACE_RULE;
-}
-
-void
-nsCSSFontFaceRule::GetCssText(nsAString& aCssText) const
-{
-  nsAutoString propText;
-  mDecl.GetCssTextImpl(propText);
-
-  aCssText.AssignLiteral("@font-face {\n");
-  aCssText.Append(propText);
-  aCssText.Append('}');
-}
-
-nsICSSDeclaration*
-nsCSSFontFaceRule::Style()
-{
-  return &mDecl;
-}
-
-// Arguably these should forward to nsCSSFontFaceStyleDecl methods.
-void
-nsCSSFontFaceRule::SetDesc(nsCSSFontDesc aDescID, nsCSSValue const & aValue)
-{
-  NS_PRECONDITION(aDescID > eCSSFontDesc_UNKNOWN &&
-                  aDescID < eCSSFontDesc_COUNT,
-                  "aDescID out of range in nsCSSFontFaceRule::SetDesc");
-
-  // FIXME: handle dynamic changes
-
-  // FIXME(heycam): If we are changing unicode-range, then a FontFace object
-  // representing this rule must have its mUnicodeRange value invalidated.
-
-  mDecl.mDescriptors.Get(aDescID) = aValue;
-}
-
-void
-nsCSSFontFaceRule::GetDesc(nsCSSFontDesc aDescID, nsCSSValue & aValue)
-{
-  NS_PRECONDITION(aDescID > eCSSFontDesc_UNKNOWN &&
-                  aDescID < eCSSFontDesc_COUNT,
-                  "aDescID out of range in nsCSSFontFaceRule::GetDesc");
-
-  aValue = mDecl.mDescriptors.Get(aDescID);
-}
-
-/* virtual */ size_t
-nsCSSFontFaceRule::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
-{
-  return aMallocSizeOf(this);
-
-  // Measurement of the following members may be added later if DMD finds it is
-  // worthwhile:
-  // - mDecl
-}
-
-/* virtual */ JSObject*
-nsCSSFontFaceRule::WrapObject(JSContext* aCx,
-                              JS::Handle<JSObject*> aGivenProto)
-{
-  return CSSFontFaceRuleBinding::Wrap(aCx, this, aGivenProto);
-}
deleted file mode 100644
--- a/layout/style/nsCSSFontFaceRule.h
+++ /dev/null
@@ -1,145 +0,0 @@
-/* -*- 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/. */
-
-#ifndef nsCSSFontFaceRule_h
-#define nsCSSFontFaceRule_h
-
-#include "mozilla/css/Rule.h"
-#include "nsCSSValue.h"
-#include "nsICSSDeclaration.h"
-
-namespace mozilla {
-
-namespace dom {
-class DocGroup;
-} // namespace dom
-
-struct CSSFontFaceDescriptors
-{
-#define CSS_FONT_DESC(name_, method_) nsCSSValue m##method_;
-#include "nsCSSFontDescList.h"
-#undef CSS_FONT_DESC
-
-  const nsCSSValue& Get(nsCSSFontDesc aFontDescID) const;
-  nsCSSValue& Get(nsCSSFontDesc aFontDescID);
-
-private:
-  static nsCSSValue CSSFontFaceDescriptors::* const Fields[];
-};
-
-} // namespace mozilla
-
-// A nsCSSFontFaceStyleDecl is always embedded in a nsCSSFontFaceRule.
-class nsCSSFontFaceRule;
-class nsCSSFontFaceStyleDecl final : public nsICSSDeclaration
-{
-public:
-  NS_DECL_ISUPPORTS_INHERITED
-  NS_DECL_NSIDOMCSSSTYLEDECLARATION_HELPER
-  virtual already_AddRefed<mozilla::dom::CSSValue>
-  GetPropertyCSSValue(const nsAString& aProp, mozilla::ErrorResult& aRv)
-    override;
-  using nsICSSDeclaration::GetPropertyCSSValue;
-
-  virtual nsINode *GetParentObject() override;
-  virtual void IndexedGetter(uint32_t aIndex, bool& aFound, nsAString& aPropName) override;
-
-  nsresult GetPropertyValue(nsCSSFontDesc aFontDescID,
-                            nsAString & aResult) const;
-
-  virtual JSObject* WrapObject(JSContext *cx, JS::Handle<JSObject*> aGivenProto) override;
-
-protected:
-  ~nsCSSFontFaceStyleDecl() {}
-
-  friend class nsCSSFontFaceRule;
-
-  inline nsCSSFontFaceRule* ContainingRule();
-  inline const nsCSSFontFaceRule* ContainingRule() const;
-
-  mozilla::CSSFontFaceDescriptors mDescriptors;
-
-  // The actual implementation of GetCssText, so we can make it const.
-  // We can't make nsICSSDeclaration::GetCssText const, because some
-  // subclasses call non-const methods in their implementations.
-  void GetCssTextImpl(nsAString& aCssText) const;
-
-private:
-  // NOT TO BE IMPLEMENTED
-  // This object cannot be allocated on its own, only as part of
-  // nsCSSFontFaceRule.
-  void* operator new(size_t size) CPP_THROW_NEW;
-};
-
-class nsCSSFontFaceRule final : public mozilla::css::Rule
-{
-public:
-  nsCSSFontFaceRule(uint32_t aLineNumber, uint32_t aColumnNumber)
-    : mozilla::css::Rule(aLineNumber, aColumnNumber)
-  {
-  }
-
-  nsCSSFontFaceRule(const nsCSSFontFaceRule& aCopy)
-    // copy everything except our reference count
-    : mozilla::css::Rule(aCopy), mDecl(aCopy.mDecl)
-  {
-  }
-
-  NS_DECL_ISUPPORTS_INHERITED
-  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(nsCSSFontFaceRule,
-                                                         mozilla::css::Rule)
-  virtual bool IsCCLeaf() const override;
-
-#ifdef DEBUG
-  virtual void List(FILE* out = stdout, int32_t aIndent = 0) const override;
-#endif
-  virtual already_AddRefed<mozilla::css::Rule> Clone() const override;
-
-  void SetDesc(nsCSSFontDesc aDescID, nsCSSValue const & aValue);
-  void GetDesc(nsCSSFontDesc aDescID, nsCSSValue & aValue);
-
-  // WebIDL interface
-  uint16_t Type() const override;
-  void GetCssText(nsAString& aCssText) const override;
-  nsICSSDeclaration* Style();
-
-  virtual size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const override;
-
-  virtual JSObject* WrapObject(JSContext* aCx,
-                               JS::Handle<JSObject*> aGivenProto) override;
-
-  void GetDescriptors(mozilla::CSSFontFaceDescriptors& aDescriptors) const
-    { aDescriptors = mDecl.mDescriptors; }
-
-protected:
-  ~nsCSSFontFaceRule() {}
-
-  friend class nsCSSFontFaceStyleDecl;
-  nsCSSFontFaceStyleDecl mDecl;
-};
-
-// nsFontFaceRuleContainer - used for associating sheet type with
-// specific @font-face rules
-struct nsFontFaceRuleContainer {
-  RefPtr<nsCSSFontFaceRule> mRule;
-  mozilla::SheetType mSheetType;
-};
-
-inline nsCSSFontFaceRule*
-nsCSSFontFaceStyleDecl::ContainingRule()
-{
-  return reinterpret_cast<nsCSSFontFaceRule*>
-    (reinterpret_cast<char*>(this) - offsetof(nsCSSFontFaceRule, mDecl));
-}
-
-inline const nsCSSFontFaceRule*
-nsCSSFontFaceStyleDecl::ContainingRule() const
-{
-  return reinterpret_cast<const nsCSSFontFaceRule*>
-    (reinterpret_cast<const char*>(this) - offsetof(nsCSSFontFaceRule, mDecl));
-}
-
-#endif // nsCSSFontFaceRule_h
--- a/layout/style/test/mochitest.ini
+++ b/layout/style/test/mochitest.ini
@@ -227,16 +227,19 @@ support-files = flexbox_layout_testcases
 [test_font_face_parser.html]
 [test_font_family_parsing.html]
 [test_font_family_serialization.html]
 skip-if = !stylo
 [test_font_loading_api.html]
 support-files =
   BitPattern.woff
   file_font_loading_api_vframe.html
+# This test checks font loading state. When loaded second time, fonts may be
+# loaded synchronously, causing this test to fail in test-verify task.
+skip-if = verify
 [test_garbage_at_end_of_declarations.html]
 [test_grid_container_shorthands.html]
 [test_grid_item_shorthands.html]
 [test_grid_shorthand_serialization.html]
 [test_grid_computed_values.html]
 [test_group_insertRule.html]
 [test_hover_quirk.html]
 [test_html_attribute_computed_values.html]
--- a/layout/style/test/test_font_face_parser.html
+++ b/layout/style/test/test_font_face_parser.html
@@ -27,24 +27,24 @@ function runTest() {
     { rule: "@font-face { }",   d: {} },
     { rule: "@font-face {",     d: {} },
     { rule: "@font-face { ; }", d: {}, noncanonical: true },
 
     // Correct font-family.
     { rule: _("font-family: \"Mouse\";"), d: {"font-family" : "\"Mouse\""} },
     { rule: _("font-family: \"Mouse\""),  d: {"font-family" : "\"Mouse\""},
       noncanonical: true },
-    { rule: _("font-family: Mouse;"),  d: {"font-family" : "\"Mouse\"" },
+    { rule: _("font-family: Mouse;"),  d: {"font-family" : "Mouse" },
       noncanonical: true },
-    { rule: _("font-family: Mouse"),  d: {"font-family" : "\"Mouse\"" },
+    { rule: _("font-family: Mouse"),  d: {"font-family" : "Mouse" },
       noncanonical: true },
 
     // Correct but unusual font-family.
     { rule: _("font-family: Hoefler Text;"),
-      d: {"font-family" : "\"Hoefler Text\""},
+      d: {"font-family" : "Hoefler Text"},
       noncanonical: true },
 
     // Incorrect font-family.
     { rule: _("font-family:"),        d: {} },
     { rule: _("font-family \"Mouse\""), d: {} },
     { rule: _("font-family: *"),      d: {} },
     { rule: _("font-family: Mouse, Rat"), d: {} },
     { rule: _("font-family: sans-serif"), d: {} },
@@ -105,28 +105,28 @@ function runTest() {
       d: { "src" : "url(\"/fonts/Mouse\") format(\"truetype\")" } },
     { rule: _("src: url(\"/fonts/Mouse\") format(\"truetype\", \"opentype\");"),
       d: { "src" : "url(\"/fonts/Mouse\") format(\"truetype\", \"opentype\")" } },
 
     { rule: _("src: url(\"/fonts/Mouse\"), url(\"/fonts/Rat\");"),
       d: { "src" : "url(\"/fonts/Mouse\"), url(\"/fonts/Rat\")" } },
 
     { rule: _("src: local(Mouse), url(\"/fonts/Mouse\");"),
-      d: { "src" : "local(\"Mouse\"), url(\"/fonts/Mouse\")" },
+      d: { "src" : "local(Mouse), url(\"/fonts/Mouse\")" },
       noncanonical: true },
 
     { rule: _("src: local(\"老鼠\"), url(\"/fonts/Mouse\");"),
       d: { "src" : "local(\"老鼠\"), url(\"/fonts/Mouse\")" } },
 
     { rule: _("src: local(\"老鼠\"), url(\"/fonts/Mouse\") format(\"truetype\");"),
       d: { "src" : "local(\"老鼠\"), url(\"/fonts/Mouse\") format(\"truetype\")" } },
 
     // Correct but unusual src:
     { rule: _("src: local(Hoefler Text);"),
-      d: {"src" : "local(\"Hoefler Text\")"}, noncanonical: true },
+      d: {"src" : "local(Hoefler Text)"}, noncanonical: true },
 
     // Incorrect src:
     { rule: _("src:"), d: {} },
     { rule: _("src: \"/fonts/Mouse\";"), d: {} },
     { rule: _("src: /fonts/Mouse;"), d: {} },
     { rule: _("src: url(\"/fonts/Mouse\") format(truetype);"), d: {} },
     { rule: _("src: url(\"/fonts/Mouse\") format(\"truetype\",opentype);"), d: {} },
     { rule: _("src: local(*);"), d: {} },
@@ -138,25 +138,25 @@ function runTest() {
     // Repeated descriptors
     { rule: _("font-weight: 700; font-weight: 200;"),
       d: {"font-weight" : "200"},
       noncanonical: true },
     { rule: _("src: url(\"/fonts/Cat\"); src: url(\"/fonts/Mouse\");"),
       d: { "src" : "url(\"/fonts/Mouse\")" },
       noncanonical: true },
     { rule: _("src: local(Cat); src: local(Mouse)"),
-      d: { "src" : "local(\"Mouse\")" },
+      d: { "src" : "local(Mouse)" },
       noncanonical: true },
 
     // Correct parenthesis matching for local()
     { rule: _("src: local(Mouse); src: local(Cat(); src: local(Rat); )"),
-      d: { "src" : "local(\"Mouse\")" },
+      d: { "src" : "local(Mouse)" },
       noncanonical: true },
     { rule: _("src: local(Mouse); src: local(\"Cat\"; src: local(Rat); )"),
-      d: { "src" : "local(\"Mouse\")" },
+      d: { "src" : "local(Mouse)" },
       noncanonical: true },
 
     // Correct parenthesis matching for format()
     { rule: _("src: url(\"/fonts/Mouse\"); " +
 	      "src: url(\"/fonts/Cat\") format(Cat(); src: local(Rat); )"),
       d: { "src" : "url(\"/fonts/Mouse\")" },
       noncanonical: true },
     { rule: _("src: url(\"/fonts/Mouse\"); " +
--- a/layout/style/test/test_font_loading_api.html
+++ b/layout/style/test/test_font_loading_api.html
@@ -56,17 +56,17 @@ var invalidValues = {
   unicodeRange: "U+1????-2????",
   variant: "inherit",
   featureSettings: "dlig",
   display: "normal"
 };
 
 // Invalid font family names.
 var invalidFontFamilyNames = [
-  "", "\"\"", "sans-serif", "A, B", "inherit", "a 1"
+  "", "sans-serif", "A, B", "inherit", "a 1"
 ];
 
 // Font family list where at least one is likely to be available on
 // platforms we care about.
 var likelyPlatformFonts = "Helvetica Neue, Bitstream Vera Sans, Bitstream Vera Sans Roman, FreeSans, Free Sans, SwissA, DejaVu Sans, Arial";
 
 // Will hold an ArrayBuffer containing a valid font.
 var fontData;
--- a/layout/style/test/test_transitions_per_property.html
+++ b/layout/style/test/test_transitions_per_property.html
@@ -143,17 +143,18 @@ var supported_properties = {
     "fill": [ test_color_transition,
               test_currentcolor_transition ],
     "fill-opacity" : [ test_float_zeroToOne_transition,
                        // opacity is clamped in computed style
                        // (not parsing/interpolation)
                        test_float_zeroToOne_clamped ],
     "filter" : [ test_filter_transition ],
     "flex-basis": [ test_length_transition, test_percent_transition,
-                    test_length_clamped, test_percent_clamped ],
+                    test_length_clamped, test_percent_clamped,
+                    test_flex_basis_content_transition ],
     "flex-grow": [ test_float_zeroToOne_transition,
                    test_float_aboveOne_transition ],
     "flex-shrink": [ test_float_zeroToOne_transition,
                      test_float_aboveOne_transition ],
     "flood-color": [ test_color_transition,
                      test_currentcolor_transition ],
     "flood-opacity" : [ test_float_zeroToOne_transition,
                         // opacity is clamped in computed style
@@ -1164,16 +1165,41 @@ function test_length_clamped_or_unclampe
   div.style.setProperty("transition-property", prop, "");
   div.style.setProperty(prop, "100px", "");
   (is_clamped ? is : isnot)(cs.getPropertyValue(prop),
      numbers_are_pixels ? "0" : "0px",
      "length-valued property " + prop + ": clamping of negatives");
   div.style.setProperty("transition-timing-function", "linear", "");
 }
 
+// Test transition to/from the special 'flex-basis: content' keyword.
+function test_flex_basis_content_transition(prop) {
+  is(prop, "flex-basis", "this test function should only be called for 'flex-basis'");
+
+  // Test transition from length to 'content':
+  div.style.setProperty("transition-property", "none", "");
+  div.style.setProperty(prop, "8px", "");
+  is(cs.getPropertyValue(prop), "8px",
+     "property " + prop + ": computed value before transition to 'content'");
+  div.style.setProperty("transition-property", prop, "");
+  div.style.setProperty(prop, "content", "");
+  is(cs.getPropertyValue(prop), "content",
+     "property " + prop + ": transition to 'content' (should be discrete)");
+
+  // Test transition from 'content' to length:
+  div.style.setProperty("transition-property", "none", "");
+  div.style.setProperty(prop, "content", "");
+  is(cs.getPropertyValue(prop), "content",
+     "property " + prop + ": computed value before transition from 'content'");
+  div.style.setProperty("transition-property", prop, "");
+  div.style.setProperty(prop, "6px", "");
+  is(cs.getPropertyValue(prop), "6px",
+     "property " + prop + ": transition from 'content' (should be discrete)");
+}
+
 // Test using float values in the range [0, 1] (e.g. opacity)
 function test_float_zeroToOne_transition(prop) {
   div.style.setProperty("transition-property", "none", "");
   div.style.setProperty(prop, "0.3", "");
   is(cs.getPropertyValue(prop), "0.3",
      "float-valued property " + prop + ": computed value before transition");
   div.style.setProperty("transition-property", prop, "");
   div.style.setProperty(prop, "0.8", "");
--- a/layout/xul/nsMenuPopupFrame.cpp
+++ b/layout/xul/nsMenuPopupFrame.cpp
@@ -153,24 +153,20 @@ nsMenuPopupFrame::Init(nsIContent*      
   // XXX Hack. The popup's view should float above all other views,
   // so we use the nsView::SetFloating() to tell the view manager
   // about that constraint.
   nsView* ourView = GetView();
   nsViewManager* viewManager = ourView->GetViewManager();
   viewManager->SetViewFloating(ourView, true);
 
   mPopupType = ePopupTypePanel;
-  nsIDocument* doc = aContent->OwnerDoc();
-  int32_t namespaceID;
-  RefPtr<nsAtom> tag = doc->BindingManager()->ResolveTag(aContent, &namespaceID);
-  if (namespaceID == kNameSpaceID_XUL) {
-    if (tag == nsGkAtoms::menupopup || tag == nsGkAtoms::popup)
-      mPopupType = ePopupTypeMenu;
-    else if (tag == nsGkAtoms::tooltip)
-      mPopupType = ePopupTypeTooltip;
+  if (aContent->IsAnyOfXULElements(nsGkAtoms::menupopup, nsGkAtoms::popup)) {
+    mPopupType = ePopupTypeMenu;
+  } else if (aContent->IsXULElement(nsGkAtoms::tooltip)) {
+    mPopupType = ePopupTypeTooltip;
   }
 
   nsCOMPtr<nsIDocShellTreeItem> dsti = PresContext()->GetDocShell();
   if (dsti && dsti->ItemType() == nsIDocShellTreeItem::typeChrome) {
     mInContentShell = false;
   }
 
   // To improve performance, create the widget for the popup only if it is not
--- a/media/webrtc/signaling/src/media-conduit/MediaConduitInterface.h
+++ b/media/webrtc/signaling/src/media-conduit/MediaConduitInterface.h
@@ -379,16 +379,18 @@ public:
    * Function to attach Renderer end-point of the Media-Video conduit.
    * @param aRenderer : Reference to the concrete Video renderer implementation
    * Note: Multiple invocations of this API shall remove an existing renderer
    * and attaches the new to the Conduit.
    */
   virtual MediaConduitErrorCode AttachRenderer(RefPtr<mozilla::VideoRenderer> aRenderer) = 0;
   virtual void DetachRenderer() = 0;
 
+  virtual void DisableSsrcChanges() = 0;
+
   bool SetRemoteSSRC(unsigned int ssrc) override = 0;
 
   /**
    * Function to deliver a capture video frame for encoding and transport.
    * If the frame's timestamp is 0, it will be automatcally generated.
    *
    * NOTE: ConfigureSendMediaCodec() must be called before this function can
    *       be invoked. This ensures the inserted video-frames can be
--- a/media/webrtc/signaling/src/media-conduit/VideoConduit.cpp
+++ b/media/webrtc/signaling/src/media-conduit/VideoConduit.cpp
@@ -269,16 +269,18 @@ WebrtcVideoConduit::WebrtcVideoConduit(R
   , mDenoising(false)
   , mLockScaling(false)
   , mSpatialLayers(1)
   , mTemporalLayers(1)
   , mCodecMode(webrtc::kRealtimeVideo)
   , mCall(aCall) // refcounted store of the call object
   , mSendStreamConfig(this) // 'this' is stored but not  dereferenced in the constructor.
   , mRecvStreamConfig(this) // 'this' is stored but not  dereferenced in the constructor.
+  , mAllowSsrcChange(true)
+  , mWaitingForInitialSsrc(true)
   , mRecvSSRC(0)
   , mRecvSSRCSetInProgress(false)
   , mSendCodecPlugin(nullptr)
   , mRecvCodecPlugin(nullptr)
   , mVideoStatsTimer(NS_NewTimer())
 {
   mRecvStreamConfig.renderer = this;
 
@@ -873,33 +875,34 @@ WebrtcVideoConduit::ConfigureSendMediaCo
   }
 
   return condError;
 }
 
 bool
 WebrtcVideoConduit::SetRemoteSSRC(unsigned int ssrc)
 {
-  CSFLogDebug(LOGTAG, "%s: SSRC %u (0x%x)", __FUNCTION__, ssrc, ssrc);
-  mRecvStreamConfig.rtp.remote_ssrc = ssrc;
-
   unsigned int current_ssrc;
   if (!GetRemoteSSRC(&current_ssrc)) {
     return false;
   }
 
   if (current_ssrc == ssrc) {
     return true;
   }
 
   bool wasReceiving = mEngineReceiving;
   if (StopReceiving() != kMediaConduitNoError) {
     return false;
   }
 
+  CSFLogDebug(LOGTAG, "%s: SSRC %u (0x%x)", __FUNCTION__, ssrc, ssrc);
+  mRecvStreamConfig.rtp.remote_ssrc = ssrc;
+  mWaitingForInitialSsrc = false;
+
   // This will destroy mRecvStream and create a new one (argh, why can't we change
   // it without a full destroy?)
   // We're going to modify mRecvStream, we must lock.  Only modified on MainThread.
   // All non-MainThread users must lock before reading/using
   {
     MutexAutoLock lock(mCodecMutex);
     // On the next StartReceiving() or ConfigureRecvMediaCodec, force
     // building a new RecvStream to switch SSRCs.
@@ -1964,86 +1967,88 @@ WebrtcVideoConduit::DeliverPacket(const 
   }
 
   return kMediaConduitNoError;
 }
 
 MediaConduitErrorCode
 WebrtcVideoConduit::ReceivedRTPPacket(const void* data, int len, uint32_t ssrc)
 {
-  // Handle the unknown ssrc (and ssrc-not-signaled case).
-  // We can't just do this here; it has to happen on MainThread :-(
-  // We also don't want to drop the packet, nor stall this thread, so we hold
-  // the packet (and any following) for inserting once the SSRC is set.
-  bool queue = mRecvSSRCSetInProgress;
-  if (queue || mRecvSSRC != ssrc) {
-    // capture packet for insertion after ssrc is set -- do this before
-    // sending the runnable, since it may pull from this.  Since it
-    // dispatches back to us, it's less critial to do this here, but doesn't
-    // hurt.
-    UniquePtr<QueuedPacket> packet((QueuedPacket*) malloc(sizeof(QueuedPacket) + len-1));
-    packet->mLen = len;
-    memcpy(packet->mData, data, len);
-    CSFLogDebug(LOGTAG, "queuing packet: seq# %u, Len %d ",
-                (uint16_t)ntohs(((uint16_t*) packet->mData)[1]), packet->mLen);
-    if (queue) {
+  if (mAllowSsrcChange || mWaitingForInitialSsrc) {
+    // Handle the unknown ssrc (and ssrc-not-signaled case).
+    // We can't just do this here; it has to happen on MainThread :-(
+    // We also don't want to drop the packet, nor stall this thread, so we hold
+    // the packet (and any following) for inserting once the SSRC is set.
+    bool queue = mRecvSSRCSetInProgress;
+    if (queue || mRecvSSRC != ssrc) {
+      // capture packet for insertion after ssrc is set -- do this before
+      // sending the runnable, since it may pull from this.  Since it
+      // dispatches back to us, it's less critial to do this here, but doesn't
+      // hurt.
+      UniquePtr<QueuedPacket> packet((QueuedPacket*) malloc(sizeof(QueuedPacket) + len-1));
+      packet->mLen = len;
+      memcpy(packet->mData, data, len);
+      CSFLogDebug(LOGTAG, "queuing packet: seq# %u, Len %d ",
+                  (uint16_t)ntohs(((uint16_t*) packet->mData)[1]), packet->mLen);
+      if (queue) {
+        mQueuedPackets.AppendElement(Move(packet));
+        return kMediaConduitNoError;
+      }
+      // a new switch needs to be done
+      // any queued packets are from a previous switch that hasn't completed
+      // yet; drop them and only process the latest SSRC
+      mQueuedPackets.Clear();
       mQueuedPackets.AppendElement(Move(packet));
+
+      CSFLogDebug(LOGTAG, "%s: switching from SSRC %u to %u", __FUNCTION__,
+                  mRecvSSRC, ssrc);
+      // we "switch" here immediately, but buffer until the queue is released
+      mRecvSSRC = ssrc;
+      mRecvSSRCSetInProgress = true;
+      queue = true;
+
+      // Ensure lamba captures refs
+      RefPtr<WebrtcVideoConduit> self = this;
+      nsCOMPtr<nsIThread> thread;
+      if (NS_WARN_IF(NS_FAILED(NS_GetCurrentThread(getter_AddRefs(thread))))) {
+        return kMediaConduitRTPProcessingFailed;
+      }
+      NS_DispatchToMainThread(media::NewRunnableFrom([self, thread, ssrc]() mutable {
+            // Normally this is done in CreateOrUpdateMediaPipeline() for
+            // initial creation and renegotiation, but here we're rebuilding the
+            // Receive channel at a lower level.  This is needed whenever we're
+            // creating a GMPVideoCodec (in particular, H264) so it can communicate
+            // errors to the PC.
+            WebrtcGmpPCHandleSetter setter(self->mPCHandle);
+            self->SetRemoteSSRC(ssrc); // this will likely re-create the VideoReceiveStream
+            // We want to unblock the queued packets on the original thread
+            thread->Dispatch(media::NewRunnableFrom([self, ssrc]() mutable {
+                  if (ssrc == self->mRecvSSRC) {
+                    // SSRC is set; insert queued packets
+                    for (auto& packet : self->mQueuedPackets) {
+                      CSFLogDebug(LOGTAG, "Inserting queued packets: seq# %u, Len %d ",
+                                  (uint16_t)ntohs(((uint16_t*) packet->mData)[1]), packet->mLen);
+
+                      if (self->DeliverPacket(packet->mData, packet->mLen) != kMediaConduitNoError) {
+                        CSFLogError(LOGTAG, "%s RTP Processing Failed", __FUNCTION__);
+                        // Keep delivering and then clear the queue
+                      }
+                    }
+                    self->mQueuedPackets.Clear();
+                    // we don't leave inprogress until there are no changes in-flight
+                    self->mRecvSSRCSetInProgress = false;
+                  }
+                  // else this is an intermediate switch; another is in-flight
+
+                  return NS_OK;
+                }), NS_DISPATCH_NORMAL);
+            return NS_OK;
+          }));
       return kMediaConduitNoError;
     }
-    // a new switch needs to be done
-    // any queued packets are from a previous switch that hasn't completed
-    // yet; drop them and only process the latest SSRC
-    mQueuedPackets.Clear();
-    mQueuedPackets.AppendElement(Move(packet));
-
-    CSFLogDebug(LOGTAG, "%s: switching from SSRC %u to %u", __FUNCTION__,
-                mRecvSSRC, ssrc);
-    // we "switch" here immediately, but buffer until the queue is released
-    mRecvSSRC = ssrc;
-    mRecvSSRCSetInProgress = true;
-    queue = true;
-
-    // Ensure lamba captures refs
-    RefPtr<WebrtcVideoConduit> self = this;
-    nsCOMPtr<nsIThread> thread;
-    if (NS_WARN_IF(NS_FAILED(NS_GetCurrentThread(getter_AddRefs(thread))))) {
-      return kMediaConduitRTPProcessingFailed;
-    }
-    NS_DispatchToMainThread(media::NewRunnableFrom([self, thread, ssrc]() mutable {
-          // Normally this is done in CreateOrUpdateMediaPipeline() for
-          // initial creation and renegotiation, but here we're rebuilding the
-          // Receive channel at a lower level.  This is needed whenever we're
-          // creating a GMPVideoCodec (in particular, H264) so it can communicate
-          // errors to the PC.
-          WebrtcGmpPCHandleSetter setter(self->mPCHandle);
-          self->SetRemoteSSRC(ssrc); // this will likely re-create the VideoReceiveStream
-          // We want to unblock the queued packets on the original thread
-          thread->Dispatch(media::NewRunnableFrom([self, ssrc]() mutable {
-                if (ssrc == self->mRecvSSRC) {
-                  // SSRC is set; insert queued packets
-                  for (auto& packet : self->mQueuedPackets) {
-                    CSFLogDebug(LOGTAG, "Inserting queued packets: seq# %u, Len %d ",
-                                (uint16_t)ntohs(((uint16_t*) packet->mData)[1]), packet->mLen);
-
-                    if (self->DeliverPacket(packet->mData, packet->mLen) != kMediaConduitNoError) {
-                      CSFLogError(LOGTAG, "%s RTP Processing Failed", __FUNCTION__);
-                      // Keep delivering and then clear the queue
-                    }
-                  }
-                  self->mQueuedPackets.Clear();
-                  // we don't leave inprogress until there are no changes in-flight
-                  self->mRecvSSRCSetInProgress = false;
-                }
-                // else this is an intermediate switch; another is in-flight
-
-                return NS_OK;
-              }), NS_DISPATCH_NORMAL);
-          return NS_OK;
-        }));
-    return kMediaConduitNoError;
   }
 
   CSFLogVerbose(LOGTAG, "%s: seq# %u, Len %d, SSRC %u (0x%x) ", __FUNCTION__,
                 (uint16_t)ntohs(((uint16_t*) data)[1]), len,
                 (uint32_t) ntohl(((uint32_t*) data)[2]),
                 (uint32_t) ntohl(((uint32_t*) data)[2]));
 
   if (DeliverPacket(data, len) != kMediaConduitNoError) {
--- a/media/webrtc/signaling/src/media-conduit/VideoConduit.h
+++ b/media/webrtc/signaling/src/media-conduit/VideoConduit.h
@@ -305,16 +305,20 @@ public:
                              uint64_t* bytesReceived,
                              uint32_t* cumulativeLost,
                              int32_t* rttMs) override;
   bool GetRTCPSenderReport(DOMHighResTimeStamp* timestamp,
                            unsigned int* packetsSent,
                            uint64_t* bytesSent) override;
   uint64_t MozVideoLatencyAvg();
 
+  void DisableSsrcChanges() override {
+    mAllowSsrcChange = false;
+  }
+
 private:
   // Don't allow copying/assigning.
   WebrtcVideoConduit(const WebrtcVideoConduit&) = delete;
   void operator=(const WebrtcVideoConduit&) = delete;
 
   /** Shared statistics for receive and transmit video streams
    */
   class StreamStatistics {
@@ -532,16 +536,20 @@ private:
   // WEBRTC.ORG Call API
   RefPtr<WebRtcCallWrapper> mCall;
 
   webrtc::VideoSendStream::Config mSendStreamConfig;
   VideoEncoderConfigBuilder mEncoderConfig;
 
   webrtc::VideoReceiveStream::Config mRecvStreamConfig;
 
+  // Are SSRC changes without signaling allowed or not
+  bool mAllowSsrcChange;
+  bool mWaitingForInitialSsrc;
+
   // accessed on creation, and when receiving packets
   uint32_t mRecvSSRC; // this can change during a stream!
 
   // The runnable to set the SSRC is in-flight; queue packets until it's done.
   bool mRecvSSRCSetInProgress;
   struct QueuedPacket {
     int mLen;
     uint8_t mData[1];
--- a/media/webrtc/signaling/src/peerconnection/TransceiverImpl.cpp
+++ b/media/webrtc/signaling/src/peerconnection/TransceiverImpl.cpp
@@ -858,16 +858,25 @@ TransceiverImpl::UpdateVideoConduit()
   // for the remote_ssrc that will be used by the far-end sender.
   if (!mJsepTransceiver->mRecvTrack.GetSsrcs().empty()) {
     MOZ_MTLOG(ML_DEBUG, mPCHandle << "[" << mMid << "]: " << __FUNCTION__ <<
               " Setting remote SSRC " <<
               mJsepTransceiver->mRecvTrack.GetSsrcs().front());
     conduit->SetRemoteSSRC(mJsepTransceiver->mRecvTrack.GetSsrcs().front());
   }
 
+  // TODO (bug 1423041) once we pay attention to receiving MID's in RTP packets
+  // (see bug 1405495) we could make this depending on the presence of MID in
+  // the RTP packets instead of relying on the signaling.
+  if (mJsepTransceiver->HasBundleLevel() &&
+      (!mJsepTransceiver->mRecvTrack.GetNegotiatedDetails() ||
+       !mJsepTransceiver->mRecvTrack.GetNegotiatedDetails()->GetExt(webrtc::RtpExtension::kMIdUri))) {
+    conduit->DisableSsrcChanges();
+  }
+
   if (mJsepTransceiver->mRecvTrack.GetNegotiatedDetails() &&
       mJsepTransceiver->mRecvTrack.GetActive()) {
     const auto& details(*mJsepTransceiver->mRecvTrack.GetNegotiatedDetails());
 
     UpdateConduitRtpExtmap(details, LocalDirection::kRecv);
 
     PtrVector<VideoCodecConfig> configs;
     nsresult rv = NegotiatedDetailsToVideoCodecConfigs(details, &configs);
--- a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/BasicSelectionActionDelegate.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/BasicSelectionActionDelegate.java
@@ -23,17 +23,17 @@ import java.util.List;
 public class BasicSelectionActionDelegate implements ActionMode.Callback,
                                              GeckoSession.SelectionActionDelegate {
     private static final String LOGTAG = "GeckoBasicSelectionAction";
 
     private static final String[] FLOATING_TOOLBAR_ACTIONS = new String[] {
             ACTION_CUT, ACTION_COPY, ACTION_PASTE, ACTION_SELECT_ALL
     };
     private static final String[] FIXED_TOOLBAR_ACTIONS = new String[] {
-            ACTION_PASTE, ACTION_COPY, ACTION_CUT, ACTION_SELECT_ALL
+            ACTION_SELECT_ALL, ACTION_CUT, ACTION_COPY, ACTION_PASTE
     };
 
     protected final Activity mActivity;
     protected final boolean mUseFloatingToolbar;
     protected final Matrix mTempMatrix = new Matrix();
     protected final RectF mTempRect = new RectF();
 
     protected ActionMode mActionMode;
@@ -132,30 +132,45 @@ public class BasicSelectionActionDelegat
      * @param id Action ID.
      * @return True if the action was performed.
      */
     protected boolean performAction(final String id) {
         mResponse.respond(id);
 
         // Android behavior is to clear selection on copy.
         if (ACTION_COPY.equals(id)) {
+            if (mUseFloatingToolbar) {
+                clearSelection();
+            } else {
+                mActionMode.finish();
+            }
+        }
+        return true;
+    }
+
+    protected void clearSelection() {
+        if (mResponse != null) {
             if (isActionAvailable(ACTION_COLLAPSE_TO_END)) {
                 mResponse.respond(ACTION_COLLAPSE_TO_END);
             } else {
                 mResponse.respond(ACTION_UNSELECT);
             }
         }
-        return true;
     }
 
     @Override
     public boolean onCreateActionMode(final ActionMode actionMode, final Menu menu) {
         final String[] allActions = getAllActions();
         for (final String actionId : allActions) {
             if (isActionAvailable(actionId)) {
+                if (!mUseFloatingToolbar && (
+                        Build.VERSION.SDK_INT == 22 || Build.VERSION.SDK_INT == 23)) {
+                    // Android bug where onPrepareActionMode is not called initially.
+                    onPrepareActionMode(actionMode, menu);
+                }
                 return true;
             }
         }
         return false;
     }
 
     @Override
     public boolean onPrepareActionMode(final ActionMode actionMode, final Menu menu) {
@@ -183,16 +198,19 @@ public class BasicSelectionActionDelegat
     @Override
     public boolean onActionItemClicked(final ActionMode actionMode, final MenuItem menuItem) {
         final String[] allActions = getAllActions();
         return performAction(allActions[menuItem.getItemId()]);
     }
 
     @Override
     public void onDestroyActionMode(final ActionMode actionMode) {
+        if (!mUseFloatingToolbar) {
+            clearSelection();
+        }
         mSession = null;
         mSelection = null;
         mActions = null;
         mResponse = null;
         mActionMode = null;
     }
 
     public void onGetContentRect(final ActionMode mode, final View view, final Rect outRect) {
--- a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoSession.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoSession.java
@@ -1914,17 +1914,18 @@ public class GeckoSession extends LayerS
          * perform multiple actions at once.
          *
          * Once a {@link #onHideAction} call (with particular reasons) or another {@link
          * #onShowActionRequest} call is received, any previously received actions are no
          * longer unavailable.
          *
          * @param session The GeckoSession that initiated the callback.
          * @param selection Current selection attributes.
-         * @param actions List of built-in actions available.
+         * @param actions Array of built-in actions available; possible values
+         * come from the {@link #ACTION_CUT ACTION_*} constants.
          * @param response Callback object for performing built-in actions. For example,
          * {@code response.respond(actions[0])} performs the first action. May be used
          * multiple times to perform multiple actions at once.
          */
         void onShowActionRequest(GeckoSession session, Selection selection,
                                  @Action String[] actions, Response<String> response);
 
         @IntDef({HIDE_REASON_NO_SELECTION,
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -5386,18 +5386,18 @@ pref("network.trr.allow-rfc1918", false)
 // Use GET (rather than POST)
 pref("network.trr.useGET", false);
 // Before TRR is widely used the NS record for this host is fetched
 // from the DOH end point to ensure proper configuration
 pref("network.trr.confirmationNS", "example.com");
 // hardcode the resolution of the hostname in network.trr.uri instead of
 // relying on the system resolver to do it for you
 pref("network.trr.bootstrapAddress", "");
-// TRR blacklist entry expire time (in seconds). Default is 72 hours.
-pref("network.trr.blacklist-duration", 259200);
+// TRR blacklist entry expire time (in seconds). Default is 20 minutes.
+pref("network.trr.blacklist-duration", 1200);
 // Single TRR request timeout, in milliseconds
 pref("network.trr.request-timeout", 3000);
 // Allow AAAA entries to be used "early", before the A results are in
 pref("network.trr.early-AAAA", false);
 
 pref("captivedetect.canonicalURL", "http://detectportal.firefox.com/success.txt");
 pref("captivedetect.canonicalContent", "success\n");
 pref("captivedetect.maxWaitingTime", 5000);
--- a/python/mozbuild/mozbuild/backend/tup.py
+++ b/python/mozbuild/mozbuild/backend/tup.py
@@ -632,55 +632,66 @@ class TupOnly(CommonBackend, PartialBack
 
         dist_idl_backend_file = self._get_backend_file('dist/idl')
         for idl in manager.idls.values():
             dist_idl_backend_file.symlink_rule(idl['source'], output_group=self._installed_idls)
 
         backend_file = self._get_backend_file('xpcom/xpidl')
         backend_file.export_shell()
 
+        all_xpts = []
         for module, data in sorted(manager.modules.iteritems()):
-            dest, idls = data
+            _, idls = data
             cmd = [
                 '$(PYTHON_PATH)',
                 '$(PLY_INCLUDE)',
                 '-I$(IDL_PARSER_DIR)',
                 '-I$(IDL_PARSER_CACHE_DIR)',
                 '$(topsrcdir)/python/mozbuild/mozbuild/action/xpidl-process.py',
                 '--cache-dir', '$(IDL_PARSER_CACHE_DIR)',
                 '$(DIST)/idl',
                 '$(DIST)/include',
                 '$(DIST)/xpcrs',
-                '$(MOZ_OBJ_ROOT)/%s/components' % dest,
+                '.',
                 module,
             ]
             cmd.extend(sorted(idls))
 
-            outputs = ['$(MOZ_OBJ_ROOT)/%s/components/%s.xpt' % (dest, module)]
+            all_xpts.append('$(MOZ_OBJ_ROOT)/%s/%s.xpt' % (backend_file.relobjdir, module))
+            outputs = ['%s.xpt' % module]
             outputs.extend(['$(MOZ_OBJ_ROOT)/dist/include/%s.h' % f for f in sorted(idls)])
             outputs.extend(['$(MOZ_OBJ_ROOT)/dist/xpcrs/rt/%s.rs' % f for f in sorted(idls)])
             outputs.extend(['$(MOZ_OBJ_ROOT)/dist/xpcrs/bt/%s.rs' % f for f in sorted(idls)])
             backend_file.rule(
                 inputs=[
                     '$(MOZ_OBJ_ROOT)/xpcom/idl-parser/xpidl/xpidllex.py',
                     '$(MOZ_OBJ_ROOT)/xpcom/idl-parser/xpidl/xpidlyacc.py',
                     self._installed_idls,
                 ],
                 display='XPIDL %s' % module,
                 cmd=cmd,
                 outputs=outputs,
                 extra_outputs=[self._installed_files],
             )
 
-        for manifest, entries in manager.interface_manifests.items():
-            for xpt in entries:
-                self._manifest_entries[manifest].add('interfaces %s' % xpt)
-
-        for m in manager.chrome_manifests:
-            self._manifest_entries[m].add('manifest components/interfaces.manifest')
+        cpp_backend_file = self._get_backend_file('xpcom/typelib/xpt')
+        cpp_backend_file.export_shell()
+        cpp_backend_file.rule(
+            inputs=all_xpts,
+            display='XPIDL linkgen %o',
+            cmd=[
+                '$(PYTHON_PATH)',
+                '$(PLY_INCLUDE)',
+                '$(topsrcdir)/xpcom/typelib/xpt/tools/xpt.py',
+                'linkgen',
+                'XPTInfo.cpp',
+                '%f',
+            ],
+            outputs=['XPTInfo.cpp'],
+        )
 
     def _preprocess(self, backend_file, input_file, destdir=None, target=None):
         if target is None:
             target = mozpath.basename(input_file)
         # .css files use '%' as the preprocessor marker, which must be scaped as
         # '%%' in the Tupfile.
         marker = '%%' if target.endswith('.css') else '#'
 
--- a/security/sandbox/linux/launch/SandboxLaunch.cpp
+++ b/security/sandbox/linux/launch/SandboxLaunch.cpp
@@ -27,16 +27,17 @@
 #include "mozilla/Attributes.h"
 #include "mozilla/Move.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/SandboxReporter.h"
 #include "mozilla/SandboxSettings.h"
 #include "mozilla/Services.h"
 #include "mozilla/Unused.h"
 #include "nsCOMPtr.h"
+#include "nsDebug.h"
 #include "nsIGfxInfo.h"
 #include "nsString.h"
 #include "nsThreadUtils.h"
 #include "prenv.h"
 #include "sandbox/linux/system_headers/linux_syscalls.h"
 
 #ifdef MOZ_X11
 #ifndef MOZ_WIDGET_GTK
@@ -81,24 +82,40 @@ IsDisplayLocal()
 
     int domain;
     socklen_t optlen = static_cast<socklen_t>(sizeof(domain));
     int rv = getsockopt(xSocketFd, SOL_SOCKET, SO_DOMAIN, &domain, &optlen);
     if (NS_WARN_IF(rv != 0)) {
       return false;
     }
     MOZ_RELEASE_ASSERT(static_cast<size_t>(optlen) == sizeof(domain));
-    // There's one more wrinkle here: the network namespace also
-    // controls "abstract namespace" addresses in the Unix domain.
-    // Xorg seems to listen on both abstract and normal addresses, but
-    // prefers abstract. This mean that if there exists a server that
-    // uses only the abstract namespace, then it will break and we
-    // won't be able to detect that ahead of time.  So, hopefully it
-    // does not exist.
-    return domain == AF_LOCAL;
+    if (domain != AF_LOCAL) {
+      return false;
+    }
+    // There's one more complication: Xorg listens on named sockets
+    // (actual filesystem nodes) as well as abstract addresses (opaque
+    // octet strings scoped to the network namespace; this is a Linux
+    // extension).
+    //
+    // Inside a container environment (e.g., when running as a Snap
+    // package), it's possible that only the abstract addresses are
+    // accessible.  In that case, the display must be considered
+    // remote.  See also bug 1450740.
+    //
+    // Unfortunately, the Xorg client libraries prefer the abstract
+    // addresses, so this isn't directly detectable by inspecting the
+    // parent process's socket.  Instead, this checks for the
+    // directory the sockets are stored in, which typically won't
+    // exist in a container with a private /tmp that isn't running its
+    // own X server.
+    if (access("/tmp/.X11-unix", X_OK) != 0) {
+      NS_ERROR("/tmp/.X11-unix is inaccessible; can't isolate network"
+               " namespace in content processes");
+      return false;
+    }
   }
 #endif
 
   // Assume that other backends (e.g., Wayland) will not use the
   // network namespace.
   return true;
 }
 
--- a/security/sandbox/win/src/sandboxbroker/sandboxBroker.cpp
+++ b/security/sandbox/win/src/sandboxbroker/sandboxBroker.cpp
@@ -693,16 +693,21 @@ SandboxBroker::SetSecurityLevelForPlugin
     delayedIntegrityLevel = sandbox::INTEGRITY_LEVEL_LOW;
   } else {
     jobLevel = sandbox::JOB_NONE;
     accessTokenLevel = sandbox::USER_NON_ADMIN;
     initialIntegrityLevel = sandbox::INTEGRITY_LEVEL_MEDIUM;
     delayedIntegrityLevel = sandbox::INTEGRITY_LEVEL_MEDIUM;
   }
 
+#ifndef NIGHTLY_BUILD
+  // We are experimenting with using restricting SIDs in the nightly builds
+  mPolicy->SetDoNotUseRestrictingSIDs();
+#endif
+
   sandbox::ResultCode result = SetJobLevel(mPolicy, jobLevel,
                                            0 /* ui_exceptions */);
   SANDBOX_ENSURE_SUCCESS(result,
                          "Setting job level failed, have you set memory limit when jobLevel == JOB_NONE?");
 
   result = mPolicy->SetTokenLevel(sandbox::USER_RESTRICTED_SAME_ACCESS,
                                   accessTokenLevel);
   SANDBOX_ENSURE_SUCCESS(result,
--- a/services/sync/modules/UIState.jsm
+++ b/services/sync/modules/UIState.jsm
@@ -192,21 +192,18 @@ const UIStateInternal = {
     } catch (e) {
       // Not fetching the profile is sad but the FxA logs will already have noise.
       return null;
     }
   },
 
   _setLastSyncTime(state) {
     if (state.status == UIState.STATUS_SIGNED_IN) {
-      try {
-        state.lastSync = new Date(Services.prefs.getCharPref("services.sync.lastSync", null));
-      } catch (_) {
-        state.lastSync = null;
-      }
+      const lastSync = Services.prefs.getCharPref("services.sync.lastSync", null);
+      state.lastSync = lastSync ? new Date(lastSync) : null;
     }
   },
 
   async _loginFailed() {
     // First ask FxA if it thinks the user needs re-authentication. In practice,
     // this check is probably canonical (ie, we probably don't really need
     // the check below at all as we drop local session info on the first sign
     // of a problem) - but we keep it for now to keep the risk down.
--- a/servo/PULL_REQUEST_TEMPLATE.md
+++ b/servo/PULL_REQUEST_TEMPLATE.md
@@ -1,14 +1,15 @@
 <!-- Please describe your changes on the following line: -->
 
 
 ---
 <!-- Thank you for contributing to Servo! Please replace each `[ ]` by `[X]` when the step is complete, and replace `__` with appropriate data: -->
 - [ ] `./mach build -d` does not report any errors
+- [ ] `./mach build-geckolib` does not report any errors
 - [ ] `./mach test-tidy` does not report any errors
 - [ ] These changes fix #__ (github issue number if applicable).
 
 <!-- Either: -->
 - [ ] There are tests for these changes OR
 - [ ] These changes do not require tests because _____
 
 <!-- Also, please make sure that "Allow edits from maintainers" checkbox is checked, so that we can help you if you get stuck somewhere along the way.-->
--- a/servo/components/layout/construct.rs
+++ b/servo/components/layout/construct.rs
@@ -47,21 +47,20 @@ use style::computed_values::display::T a
 use style::computed_values::empty_cells::T as EmptyCells;
 use style::computed_values::float::T as Float;
 use style::computed_values::list_style_position::T as ListStylePosition;
 use style::computed_values::position::T as Position;
 use style::context::SharedStyleContext;
 use style::dom::TElement;
 use style::logical_geometry::Direction;
 use style::properties::ComputedValues;
-use style::properties::longhands::list_style_image;
 use style::selector_parser::{PseudoElement, RestyleDamage};
 use style::servo::restyle_damage::ServoRestyleDamage;
-use style::values::Either;
 use style::values::computed::counters::ContentItem;
+use style::values::generics::url::UrlOrNone as ImageUrlOrNone;
 use table::TableFlow;
 use table_caption::TableCaptionFlow;
 use table_cell::TableCellFlow;
 use table_colgroup::TableColGroupFlow;
 use table_row::TableRowFlow;
 use table_rowgroup::TableRowGroupFlow;
 use table_wrapper::TableWrapperFlow;
 use text::TextRunScanner;
@@ -1273,23 +1272,23 @@ impl<'a, ConcreteThreadSafeLayoutNode: T
     /// Builds a flow for a node with `display: list-item`. This yields a `ListItemFlow` with
     /// possibly other `BlockFlow`s or `InlineFlow`s underneath it.
     fn build_flow_for_list_item(&mut self,
                                 node: &ConcreteThreadSafeLayoutNode,
                                 flotation: Float)
                                 -> ConstructionResult {
         let flotation = FloatKind::from_property(flotation);
         let marker_fragments = match node.style(self.style_context()).get_list().list_style_image {
-            list_style_image::computed_value::T(Either::First(ref url_value)) => {
+            ImageUrlOrNone::Url(ref url_value) => {
                 let image_info = Box::new(ImageFragmentInfo::new(
                     url_value.url().map(|u| u.clone()), node, &self.layout_context
                 ));
                 vec![Fragment::new(node, SpecificFragmentInfo::Image(image_info), self.layout_context)]
             }
-            list_style_image::computed_value::T(Either::Second(_none)) => {
+            ImageUrlOrNone::None => {
                 match ListStyleTypeContent::from_list_style_type(node.style(self.style_context())
                                                                      .get_list()
                                                                      .list_style_type) {
                     ListStyleTypeContent::None => Vec::new(),
                     ListStyleTypeContent::StaticText(ch) => {
                         let text = format!("{}\u{a0}", ch);
                         let mut unscanned_marker_fragments = LinkedList::new();
                         unscanned_marker_fragments.push_back(Fragment::new(
--- a/servo/components/style/font_face.rs
+++ b/servo/components/style/font_face.rs
@@ -8,27 +8,27 @@
 
 #![deny(missing_docs)]
 
 #[cfg(feature = "gecko")]
 use computed_values::{font_stretch, font_style, font_weight};
 use cssparser::{AtRuleParser, DeclarationListParser, DeclarationParser, Parser};
 use cssparser::{SourceLocation, CowRcStr};
 use error_reporting::{ContextualParseError, ParseErrorReporter};
-#[cfg(feature = "gecko")] use gecko_bindings::structs::CSSFontFaceDescriptors;
 #[cfg(feature = "gecko")] use cssparser::UnicodeRange;
 use parser::{ParserContext, ParserErrorContext, Parse};
 #[cfg(feature = "gecko")]
 use properties::longhands::font_language_override;
 use selectors::parser::SelectorParseErrorKind;
 use shared_lock::{SharedRwLockReadGuard, ToCssWithGuard};
 use std::fmt::{self, Write};
 use str::CssStringWriter;
 use style_traits::{Comma, CssWriter, OneOrMoreSeparated, ParseError};
 use style_traits::{StyleParseErrorKind, ToCss};
+use style_traits::values::SequenceWriter;
 use values::computed::font::FamilyName;
 #[cfg(feature = "gecko")]
 use values::specified::font::{SpecifiedFontFeatureSettings, SpecifiedFontVariationSettings};
 use values::specified::url::SpecifiedUrl;
 
 /// A source for a font-face rule.
 #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
 #[derive(Clone, Debug, Eq, PartialEq, ToCss)]
@@ -44,25 +44,41 @@ impl OneOrMoreSeparated for Source {
     type S = Comma;
 }
 
 /// A `UrlSource` represents a font-face source that has been specified with a
 /// `url()` function.
 ///
 /// <https://drafts.csswg.org/css-fonts/#src-desc>
 #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
-#[derive(Clone, Debug, Eq, PartialEq, ToCss)]
+#[derive(Clone, Debug, Eq, PartialEq)]
 pub struct UrlSource {
     /// The specified url.
     pub url: SpecifiedUrl,
     /// The format hints specified with the `format()` function.
-    #[css(skip)]
     pub format_hints: Vec<String>,
 }
 
+impl ToCss for UrlSource {
+    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write {
+        self.url.to_css(dest)?;
+        if !self.format_hints.is_empty() {
+            dest.write_str(" format(")?;
+            {
+                let mut writer = SequenceWriter::new(dest, ", ");
+                for hint in self.format_hints.iter() {
+                    writer.item(hint)?;
+                }
+            }
+            dest.write_char(')')?;
+        }
+        Ok(())
+    }
+}
+
 /// A font-display value for a @font-face rule.
 /// The font-display descriptor determines how a font face is displayed based
 /// on whether and when it is downloaded and ready to use.
 #[allow(missing_docs)]
 #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
 #[derive(Clone, Copy, Debug, Eq, MallocSizeOf, Parse, PartialEq)]
 #[derive(ToComputedValue, ToCss)]
 pub enum FontDisplay {
@@ -249,51 +265,36 @@ macro_rules! font_face_descriptors_commo
                 #[$doc]
                 pub $ident: Option<$ty>,
             )*
             /// Line and column of the @font-face rule source code.
             pub source_location: SourceLocation,
         }
 
         impl FontFaceRuleData {
-            fn empty(location: SourceLocation) -> Self {
+            /// Create an empty font-face rule
+            pub fn empty(location: SourceLocation) -> Self {
                 FontFaceRuleData {
                     $(
                         $ident: None,
                     )*
                     source_location: location,
                 }
             }
 
-            /// Convert to Gecko types
-            #[cfg(feature = "gecko")]
-            pub fn set_descriptors(self, descriptors: &mut CSSFontFaceDescriptors) {
-                $(
-                    if let Some(value) = self.$ident {
-                        descriptors.$gecko_ident.set_from(value)
-                    }
-                )*
-                // Leave unset descriptors to eCSSUnit_Null,
-                // FontFaceSet::FindOrCreateUserFontEntryFromFontFace does the defaulting
-                // to initial values.
-            }
-        }
-
-        impl ToCssWithGuard for FontFaceRuleData {
-            // Serialization of FontFaceRule is not specced.
-            fn to_css(&self, _guard: &SharedRwLockReadGuard, dest: &mut CssStringWriter) -> fmt::Result {
-                dest.write_str("@font-face {\n")?;
+            /// Serialization of declarations in the FontFaceRule
+            pub fn decl_to_css(&self, dest: &mut CssStringWriter) -> fmt::Result {
                 $(
                     if let Some(ref value) = self.$ident {
                         dest.write_str(concat!("  ", $name, ": "))?;
                         ToCss::to_css(value, &mut CssWriter::new(dest))?;
                         dest.write_str(";\n")?;
                     }
                 )*
-                dest.write_str("}")
+                Ok(())
             }
         }
 
        impl<'a, 'b, 'i> DeclarationParser<'i> for FontFaceRuleParser<'a, 'b> {
            type Declaration = ();
            type Error = StyleParseErrorKind<'i>;
 
            fn parse_value<'t>(&mut self, name: CowRcStr<'i>, input: &mut Parser<'i, 't>)
@@ -312,16 +313,25 @@ macro_rules! font_face_descriptors_commo
                     _ => return Err(input.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone())))
                 }
                 Ok(())
             }
         }
     }
 }
 
+impl ToCssWithGuard for FontFaceRuleData {
+    // Serialization of FontFaceRule is not specced.
+    fn to_css(&self, _guard: &SharedRwLockReadGuard, dest: &mut CssStringWriter) -> fmt::Result {
+        dest.write_str("@font-face {\n")?;
+        self.decl_to_css(dest)?;
+        dest.write_str("}")
+    }
+}
+
 macro_rules! font_face_descriptors {
     (
         mandatory descriptors = [
             $( #[$m_doc: meta] $m_name: tt $m_ident: ident / $m_gecko_ident: ident: $m_ty: ty, )*
         ]
         optional descriptors = [
             $( #[$o_doc: meta] $o_name: tt $o_ident: ident / $o_gecko_ident: ident: $o_ty: ty, )*
         ]
--- a/servo/components/style/gecko/arc_types.rs
+++ b/servo/components/style/gecko/arc_types.rs
@@ -3,33 +3,33 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 //! This file lists all arc FFI types and defines corresponding addref
 //! and release functions. This list corresponds to ServoArcTypeList.h
 //! file in Gecko.
 
 #![allow(non_snake_case, missing_docs)]
 
-use gecko_bindings::bindings::{RawServoFontFeatureValuesRule, RawServoImportRule, RawServoSupportsRule};
-use gecko_bindings::bindings::{RawServoKeyframe, RawServoKeyframesRule};
+use gecko_bindings::bindings::{RawServoFontFeatureValuesRule, RawServoImportRule};
+use gecko_bindings::bindings::{RawServoKeyframe, RawServoKeyframesRule, RawServoSupportsRule};
 use gecko_bindings::bindings::{RawServoMediaRule, RawServoNamespaceRule, RawServoPageRule};
 use gecko_bindings::bindings::{RawServoRuleNode, RawServoRuleNodeStrong, RawServoDocumentRule};
 use gecko_bindings::bindings::ServoCssRules;
-use gecko_bindings::structs::{RawServoAnimationValue, RawServoDeclarationBlock, RawServoStyleRule};
-use gecko_bindings::structs::{RawServoMediaList, RawServoStyleSheetContents};
+use gecko_bindings::structs::{RawServoAnimationValue, RawServoDeclarationBlock, RawServoFontFaceRule};
+use gecko_bindings::structs::{RawServoMediaList, RawServoStyleRule, RawServoStyleSheetContents};
 use gecko_bindings::sugar::ownership::{HasArcFFI, HasFFI, Strong};
 use media_queries::MediaList;
 use properties::{ComputedValues, PropertyDeclarationBlock};
 use properties::animated_properties::AnimationValue;
 use rule_tree::StrongRuleNode;
 use servo_arc::{Arc, ArcBorrow};
 use shared_lock::Locked;
 use std::{mem, ptr};
 use stylesheets::{CssRules, StylesheetContents, StyleRule, ImportRule, KeyframesRule, MediaRule};
-use stylesheets::{FontFeatureValuesRule, NamespaceRule, PageRule, SupportsRule, DocumentRule};
+use stylesheets::{FontFaceRule, FontFeatureValuesRule, NamespaceRule, PageRule, SupportsRule, DocumentRule};
 use stylesheets::keyframes_rule::Keyframe;
 
 macro_rules! impl_arc_ffi {
     ($servo_type:ty => $gecko_type:ty [$addref:ident, $release:ident]) => {
         unsafe impl HasFFI for $servo_type {
             type FFIType = $gecko_type;
         }
         unsafe impl HasArcFFI for $servo_type {}
@@ -86,16 +86,19 @@ impl_arc_ffi!(Locked<SupportsRule> => Ra
               [Servo_SupportsRule_AddRef, Servo_SupportsRule_Release]);
 
 impl_arc_ffi!(Locked<DocumentRule> => RawServoDocumentRule
               [Servo_DocumentRule_AddRef, Servo_DocumentRule_Release]);
 
 impl_arc_ffi!(Locked<FontFeatureValuesRule> => RawServoFontFeatureValuesRule
               [Servo_FontFeatureValuesRule_AddRef, Servo_FontFeatureValuesRule_Release]);
 
+impl_arc_ffi!(Locked<FontFaceRule> => RawServoFontFaceRule
+              [Servo_FontFaceRule_AddRef, Servo_FontFaceRule_Release]);
+
 // RuleNode is a Arc-like type but it does not use Arc.
 
 impl StrongRuleNode {
     pub fn into_strong(self) -> RawServoRuleNodeStrong {
         let ptr = self.ptr();
         mem::forget(self);
         unsafe { mem::transmute(ptr) }
     }
--- a/servo/components/style/gecko/conversions.rs
+++ b/servo/components/style/gecko/conversions.rs
@@ -12,18 +12,19 @@ use app_units::Au;
 use gecko::values::{convert_rgba_to_nscolor, GeckoStyleCoordConvertible};
 use gecko_bindings::bindings::{Gecko_CreateGradient, Gecko_SetGradientImageValue, Gecko_SetLayerImageImageValue};
 use gecko_bindings::bindings::{Gecko_InitializeImageCropRect, Gecko_SetImageElement};
 use gecko_bindings::structs::{self, nsCSSUnit, nsStyleCoord_CalcValue};
 use gecko_bindings::structs::{nsStyleImage, nsresult, SheetType};
 use gecko_bindings::sugar::ns_style_coord::{CoordDataValue, CoordData, CoordDataMut};
 use std::f32::consts::PI;
 use stylesheets::{Origin, RulesMutateError};
-use values::computed::{Angle, CalcLengthOrPercentage, ComputedImageUrl, Gradient, Image};
+use values::computed::{Angle, CalcLengthOrPercentage, Gradient, Image};
 use values::computed::{Integer, LengthOrPercentage, LengthOrPercentageOrAuto, Percentage, TextAlign};
+use values::computed::url::ComputedImageUrl;
 use values::generics::box_::VerticalAlign;
 use values::generics::grid::{TrackListValue, TrackSize};
 use values::generics::image::{CompatMode, Image as GenericImage, GradientItem};
 use values::generics::rect::Rect;
 
 impl From<CalcLengthOrPercentage> for nsStyleCoord_CalcValue {
     fn from(other: CalcLengthOrPercentage) -> nsStyleCoord_CalcValue {
         let has_percentage = other.percentage.is_some();
@@ -588,21 +589,21 @@ pub mod basic_shape {
 
     use gecko::values::GeckoStyleCoordConvertible;
     use gecko_bindings::structs;
     use gecko_bindings::structs::{StyleBasicShape, StyleBasicShapeType, StyleFillRule};
     use gecko_bindings::structs::{StyleGeometryBox, StyleShapeSource, StyleShapeSourceType};
     use gecko_bindings::structs::{nsStyleCoord, nsStyleCorners};
     use gecko_bindings::sugar::ns_style_coord::{CoordDataMut, CoordDataValue};
     use std::borrow::Borrow;
-    use values::computed::ComputedUrl;
     use values::computed::basic_shape::{BasicShape, ClippingShape, FloatAreaShape, ShapeRadius};
     use values::computed::border::{BorderCornerRadius, BorderRadius};
     use values::computed::length::LengthOrPercentage;
     use values::computed::position;
+    use values::computed::url::ComputedUrl;
     use values::generics::basic_shape::{BasicShape as GenericBasicShape, InsetRect, Polygon};
     use values::generics::basic_shape::{Circle, Ellipse, FillRule};
     use values::generics::basic_shape::{GeometryBox, ShapeBox, ShapeSource};
     use values::generics::border::BorderRadius as GenericBorderRadius;
     use values::generics::rect::Rect;
 
     impl StyleShapeSource {
         /// Convert StyleShapeSource to ShapeSource except URL and Image
--- a/servo/components/style/gecko/generated/bindings.rs
+++ b/servo/components/style/gecko/generated/bindings.rs
@@ -33,16 +33,17 @@ use gecko_bindings::structs::RawGeckoDoc
 use gecko_bindings::structs::RawGeckoElement;
 use gecko_bindings::structs::Element;
 use gecko_bindings::structs::RawGeckoKeyframeList;
 use gecko_bindings::structs::RawGeckoPropertyValuePairList;
 use gecko_bindings::structs::RawGeckoComputedKeyframeValuesList;
 use gecko_bindings::structs::RawGeckoFontFaceRuleList;
 use gecko_bindings::structs::RawGeckoNode;
 use gecko_bindings::structs::RawServoAnimationValue;
+use gecko_bindings::structs::RawServoFontFaceRule;
 use gecko_bindings::structs::RawGeckoServoAnimationValueList;
 use gecko_bindings::structs::RawServoMediaList;
 use gecko_bindings::structs::RawServoStyleSheetContents;
 use gecko_bindings::structs::RawServoDeclarationBlock;
 use gecko_bindings::structs::RawServoStyleRule;
 use gecko_bindings::structs::RawGeckoPresContext;
 use gecko_bindings::structs::RawGeckoPresContextOwned;
 use gecko_bindings::structs::RawGeckoStyleAnimationList;
@@ -72,17 +73,16 @@ use gecko_bindings::structs::ServoStyleS
 use gecko_bindings::structs::SheetParsingMode;
 use gecko_bindings::structs::StyleBasicShapeType;
 use gecko_bindings::structs::StyleShapeSource;
 use gecko_bindings::structs::StyleTransition;
 use gecko_bindings::structs::gfxFontFeatureValueSet;
 use gecko_bindings::structs::nsCSSCounterDesc;
 use gecko_bindings::structs::nsCSSCounterStyleRule;
 use gecko_bindings::structs::nsCSSFontDesc;
-use gecko_bindings::structs::nsCSSFontFaceRule;
 use gecko_bindings::structs::nsCSSKeyword;
 use gecko_bindings::structs::nsCSSPropertyID;
 use gecko_bindings::structs::nsCSSPropertyIDSet;
 use gecko_bindings::structs::nsCSSRect;
 use gecko_bindings::structs::nsCSSShadowArray;
 use gecko_bindings::structs::nsCSSUnit;
 use gecko_bindings::structs::nsCSSValue;
 use gecko_bindings::structs::nsCSSValueSharedList;
@@ -425,16 +425,19 @@ pub type RawServoFontFeatureValuesRuleBo
 pub type RawServoFontFeatureValuesRuleBorrowedOrNull<'a> = Option<&'a RawServoFontFeatureValuesRule>;
 enum RawServoFontFeatureValuesRuleVoid { }
 pub struct RawServoFontFeatureValuesRule(RawServoFontFeatureValuesRuleVoid);
 pub type RawServoRuleNodeStrong = ::gecko_bindings::sugar::ownership::Strong<RawServoRuleNode>;
 pub type RawServoRuleNodeBorrowed<'a> = &'a RawServoRuleNode;
 pub type RawServoRuleNodeBorrowedOrNull<'a> = Option<&'a RawServoRuleNode>;
 enum RawServoRuleNodeVoid { }
 pub struct RawServoRuleNode(RawServoRuleNodeVoid);
+pub type RawServoFontFaceRuleStrong = ::gecko_bindings::sugar::ownership::Strong<RawServoFontFaceRule>;
+pub type RawServoFontFaceRuleBorrowed<'a> = &'a RawServoFontFaceRule;
+pub type RawServoFontFaceRuleBorrowedOrNull<'a> = Option<&'a RawServoFontFaceRule>;
 
 extern "C" {
     pub fn Gecko_EnsureTArrayCapacity(
         aArray: *mut ::std::os::raw::c_void,
         aCapacity: usize,
         aElementSize: usize,
     );
 }
@@ -537,16 +540,22 @@ extern "C" {
 }
 extern "C" {
     pub fn Servo_RuleNode_AddRef(ptr: RawServoRuleNodeBorrowed);
 }
 extern "C" {
     pub fn Servo_RuleNode_Release(ptr: RawServoRuleNodeBorrowed);
 }
 extern "C" {
+    pub fn Servo_FontFaceRule_AddRef(ptr: RawServoFontFaceRuleBorrowed);
+}
+extern "C" {
+    pub fn Servo_FontFaceRule_Release(ptr: RawServoFontFaceRuleBorrowed);
+}
+extern "C" {
     pub fn Servo_StyleSet_Drop(ptr: RawServoStyleSetOwned);
 }
 extern "C" {
     pub fn Servo_AuthorStyles_Drop(ptr: RawServoAuthorStylesOwned);
 }
 extern "C" {
     pub fn Servo_SelectorList_Drop(ptr: RawServoSelectorListOwned);
 }
@@ -1559,31 +1568,16 @@ extern "C" {
 }
 extern "C" {
     pub fn Gecko_CSSKeywordString(
         keyword: nsCSSKeyword,
         len: *mut u32,
     ) -> *const ::std::os::raw::c_char;
 }
 extern "C" {
-    pub fn Gecko_CSSFontFaceRule_Create(line: u32, column: u32) -> *mut nsCSSFontFaceRule;
-}
-extern "C" {
-    pub fn Gecko_CSSFontFaceRule_Clone(rule: *const nsCSSFontFaceRule) -> *mut nsCSSFontFaceRule;
-}
-extern "C" {
-    pub fn Gecko_CSSFontFaceRule_GetCssText(rule: *const nsCSSFontFaceRule, result: *mut nsAString);
-}
-extern "C" {
-    pub fn Gecko_CSSFontFaceRule_AddRef(aPtr: *mut nsCSSFontFaceRule);
-}
-extern "C" {
-    pub fn Gecko_CSSFontFaceRule_Release(aPtr: *mut nsCSSFontFaceRule);
-}
-extern "C" {
     pub fn Gecko_CSSCounterStyle_Create(name: *mut nsAtom) -> *mut nsCSSCounterStyleRule;
 }
 extern "C" {
     pub fn Gecko_CSSCounterStyle_Clone(
         rule: *const nsCSSCounterStyleRule,
     ) -> *mut nsCSSCounterStyleRule;
 }
 extern "C" {
@@ -2457,17 +2451,28 @@ extern "C" {
         rule: RawServoFontFeatureValuesRuleBorrowed,
         result: *mut nsAString,
     );
 }
 extern "C" {
     pub fn Servo_CssRules_GetFontFaceRuleAt(
         rules: ServoCssRulesBorrowed,
         index: u32,
-    ) -> *mut nsCSSFontFaceRule;
+        line: *mut u32,
+        column: *mut u32,
+    ) -> RawServoFontFaceRuleStrong;
+}
+extern "C" {
+    pub fn Servo_FontFaceRule_Debug(rule: RawServoFontFaceRuleBorrowed, result: *mut nsACString);
+}
+extern "C" {
+    pub fn Servo_FontFaceRule_GetCssText(
+        rule: RawServoFontFaceRuleBorrowed,
+        result: *mut nsAString,
+    );
 }
 extern "C" {
     pub fn Servo_CssRules_GetCounterStyleRuleAt(
         rules: ServoCssRulesBorrowed,
         index: u32,
     ) -> *mut nsCSSCounterStyleRule;
 }
 extern "C" {
@@ -2608,16 +2613,74 @@ extern "C" {
 }
 extern "C" {
     pub fn Servo_FontFeatureValuesRule_GetValueText(
         rule: RawServoFontFeatureValuesRuleBorrowed,
         result: *mut nsAString,
     );
 }
 extern "C" {
+    pub fn Servo_FontFaceRule_CreateEmpty() -> RawServoFontFaceRuleStrong;
+}
+extern "C" {
+    pub fn Servo_FontFaceRule_Clone(
+        rule: RawServoFontFaceRuleBorrowed,
+    ) -> RawServoFontFaceRuleStrong;
+}
+extern "C" {
+    pub fn Servo_FontFaceRule_GetSourceLocation(
+        rule: RawServoFontFaceRuleBorrowed,
+        line: *mut u32,
+        column: *mut u32,
+    );
+}
+extern "C" {
+    pub fn Servo_FontFaceRule_Length(rule: RawServoFontFaceRuleBorrowed) -> u32;
+}
+extern "C" {
+    pub fn Servo_FontFaceRule_IndexGetter(
+        rule: RawServoFontFaceRuleBorrowed,
+        index: u32,
+    ) -> nsCSSFontDesc;
+}
+extern "C" {
+    pub fn Servo_FontFaceRule_GetDeclCssText(
+        rule: RawServoFontFaceRuleBorrowed,
+        result: *mut nsAString,
+    );
+}
+extern "C" {
+    pub fn Servo_FontFaceRule_GetDescriptor(
+        rule: RawServoFontFaceRuleBorrowed,
+        desc: nsCSSFontDesc,
+        result: nsCSSValueBorrowedMut,
+    );
+}
+extern "C" {
+    pub fn Servo_FontFaceRule_GetDescriptorCssText(
+        rule: RawServoFontFaceRuleBorrowed,
+        desc: nsCSSFontDesc,
+        result: *mut nsAString,
+    );
+}
+extern "C" {
+    pub fn Servo_FontFaceRule_SetDescriptor(
+        rule: RawServoFontFaceRuleBorrowed,
+        desc: nsCSSFontDesc,
+        value: *const nsACString,
+        data: *mut RawGeckoURLExtraData,
+    ) -> bool;
+}
+extern "C" {
+    pub fn Servo_FontFaceRule_ResetDescriptor(
+        rule: RawServoFontFaceRuleBorrowed,
+        desc: nsCSSFontDesc,
+    );
+}
+extern "C" {
     pub fn Servo_ParseProperty(
         property: nsCSSPropertyID,
         value: *const nsACString,
         data: *mut RawGeckoURLExtraData,
         parsing_mode: ParsingMode,
         quirks_mode: nsCompatibility,
         loader: *mut Loader,
     ) -> RawServoDeclarationBlockStrong;
@@ -3261,24 +3324,16 @@ extern "C" {
     pub fn Servo_ParseCounterStyleDescriptor(
         aDescriptor: nsCSSCounterDesc,
         aValue: *const nsACString,
         aURLExtraData: *mut RawGeckoURLExtraData,
         aResult: *mut nsCSSValue,
     ) -> bool;
 }
 extern "C" {
-    pub fn Servo_ParseFontDescriptor(
-        desc_id: nsCSSFontDesc,
-        value: *const nsAString,
-        data: *mut RawGeckoURLExtraData,
-        arg1: nsCSSValueBorrowedMut,
-    ) -> bool;
-}
-extern "C" {
     pub fn Servo_ParseFontShorthandForMatching(
         value: *const nsAString,
         data: *mut RawGeckoURLExtraData,
         family: *mut RefPtr<SharedFontList>,
         style: nsCSSValueBorrowedMut,
         stretch: nsCSSValueBorrowedMut,
         weight: nsCSSValueBorrowedMut,
     ) -> bool;
--- a/servo/components/style/gecko/generated/structs.rs
+++ b/servo/components/style/gecko/generated/structs.rs
@@ -13373,171 +13373,16 @@ pub mod root {
                 )
             );
         }
         impl Clone for ServoTraversalStatistics {
             fn clone(&self) -> Self {
                 *self
             }
         }
-        #[repr(C)]
-        #[derive(Debug)]
-        pub struct CSSFontFaceDescriptors {
-            pub mFamily: root::nsCSSValue,
-            pub mStyle: root::nsCSSValue,
-            pub mWeight: root::nsCSSValue,
-            pub mStretch: root::nsCSSValue,
-            pub mSrc: root::nsCSSValue,
-            pub mUnicodeRange: root::nsCSSValue,
-            pub mFontFeatureSettings: root::nsCSSValue,
-            pub mFontVariationSettings: root::nsCSSValue,
-            pub mFontLanguageOverride: root::nsCSSValue,
-            pub mDisplay: root::nsCSSValue,
-        }
-        extern "C" {
-            #[link_name = "\u{1}_ZN7mozilla22CSSFontFaceDescriptors6FieldsE"]
-            pub static mut CSSFontFaceDescriptors_Fields: [*const root::nsCSSValue; 0usize];
-        }
-        #[test]
-        fn bindgen_test_layout_CSSFontFaceDescriptors() {
-            assert_eq!(
-                ::std::mem::size_of::<CSSFontFaceDescriptors>(),
-                160usize,
-                concat!("Size of: ", stringify!(CSSFontFaceDescriptors))
-            );
-            assert_eq!(
-                ::std::mem::align_of::<CSSFontFaceDescriptors>(),
-                8usize,
-                concat!("Alignment of ", stringify!(CSSFontFaceDescriptors))
-            );
-            assert_eq!(
-                unsafe {
-                    &(*(::std::ptr::null::<CSSFontFaceDescriptors>())).mFamily as *const _ as usize
-                },
-                0usize,
-                concat!(
-                    "Offset of field: ",
-                    stringify!(CSSFontFaceDescriptors),
-                    "::",
-                    stringify!(mFamily)
-                )
-            );
-            assert_eq!(
-                unsafe {
-                    &(*(::std::ptr::null::<CSSFontFaceDescriptors>())).mStyle as *const _ as usize
-                },
-                16usize,
-                concat!(
-                    "Offset of field: ",
-                    stringify!(CSSFontFaceDescriptors),
-                    "::",
-                    stringify!(mStyle)
-                )
-            );
-            assert_eq!(
-                unsafe {
-                    &(*(::std::ptr::null::<CSSFontFaceDescriptors>())).mWeight as *const _ as usize
-                },
-                32usize,
-                concat!(
-                    "Offset of field: ",
-                    stringify!(CSSFontFaceDescriptors),
-                    "::",
-                    stringify!(mWeight)
-                )
-            );
-            assert_eq!(
-                unsafe {
-                    &(*(::std::ptr::null::<CSSFontFaceDescriptors>())).mStretch as *const _ as usize
-                },
-                48usize,
-                concat!(
-                    "Offset of field: ",
-                    stringify!(CSSFontFaceDescriptors),
-                    "::",
-                    stringify!(mStretch)
-                )
-            );
-            assert_eq!(
-                unsafe {
-                    &(*(::std::ptr::null::<CSSFontFaceDescriptors>())).mSrc as *const _ as usize
-                },
-                64usize,
-                concat!(
-                    "Offset of field: ",
-                    stringify!(CSSFontFaceDescriptors),
-                    "::",
-                    stringify!(mSrc)
-                )
-            );
-            assert_eq!(
-                unsafe {
-                    &(*(::std::ptr::null::<CSSFontFaceDescriptors>())).mUnicodeRange as *const _
-                        as usize
-                },
-                80usize,
-                concat!(
-                    "Offset of field: ",
-                    stringify!(CSSFontFaceDescriptors),
-                    "::",
-                    stringify!(mUnicodeRange)
-                )
-            );
-            assert_eq!(
-                unsafe {
-                    &(*(::std::ptr::null::<CSSFontFaceDescriptors>())).mFontFeatureSettings
-                        as *const _ as usize
-                },
-                96usize,
-                concat!(
-                    "Offset of field: ",
-                    stringify!(CSSFontFaceDescriptors),
-                    "::",
-                    stringify!(mFontFeatureSettings)
-                )
-            );
-            assert_eq!(
-                unsafe {
-                    &(*(::std::ptr::null::<CSSFontFaceDescriptors>())).mFontVariationSettings
-                        as *const _ as usize
-                },
-                112usize,
-                concat!(
-                    "Offset of field: ",
-                    stringify!(CSSFontFaceDescriptors),
-                    "::",
-                    stringify!(mFontVariationSettings)
-                )
-            );
-            assert_eq!(
-                unsafe {
-                    &(*(::std::ptr::null::<CSSFontFaceDescriptors>())).mFontLanguageOverride
-                        as *const _ as usize
-                },
-                128usize,
-                concat!(
-                    "Offset of field: ",
-                    stringify!(CSSFontFaceDescriptors),
-                    "::",
-                    stringify!(mFontLanguageOverride)
-                )
-            );
-            assert_eq!(
-                unsafe {
-                    &(*(::std::ptr::null::<CSSFontFaceDescriptors>())).mDisplay as *const _ as usize
-                },
-                144usize,
-                concat!(
-                    "Offset of field: ",
-                    stringify!(CSSFontFaceDescriptors),
-                    "::",
-                    stringify!(mDisplay)
-                )
-            );
-        }
         pub mod intl {
             #[allow(unused_imports)]
             use self::super::super::super::root;
             #[repr(C)]
             #[derive(Debug)]
             pub struct LineBreaker {
                 pub mRefCnt: root::nsAutoRefCnt,
             }
@@ -23539,16 +23384,26 @@ pub mod root {
     pub struct RawServoMediaList {
         _unused: [u8; 0],
     }
     impl Clone for RawServoMediaList {
         fn clone(&self) -> Self {
             *self
         }
     }
+    #[repr(C)]
+    #[derive(Debug, Copy)]
+    pub struct RawServoFontFaceRule {
+        _unused: [u8; 0],
+    }
+    impl Clone for RawServoFontFaceRule {
+        fn clone(&self) -> Self {
+            *self
+        }
+    }
     pub mod nsStyleTransformMatrix {
         #[allow(unused_imports)]
         use self::super::super::root;
         #[repr(u8)]
         #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
         pub enum MatrixTransformOperator {
             Interpolate = 0,
             Accumulate = 1,
@@ -23622,16 +23477,59 @@ pub mod root {
     pub type RawGeckoFontFaceRuleListBorrowedMut = *mut root::RawGeckoFontFaceRuleList;
     pub type RawGeckoAnimationPropertySegmentBorrowed =
         *const root::RawGeckoAnimationPropertySegment;
     pub type RawGeckoComputedTimingBorrowed = *const root::RawGeckoComputedTiming;
     pub type RawGeckoServoStyleRuleListBorrowedMut = *mut root::RawGeckoServoStyleRuleList;
     pub type RawGeckoCSSPropertyIDListBorrowed = *const root::RawGeckoCSSPropertyIDList;
     pub type RawGeckoStyleChildrenIteratorBorrowedMut = *mut root::RawGeckoStyleChildrenIterator;
     #[repr(C)]
+    #[derive(Debug)]
+    pub struct nsFontFaceRuleContainer {
+        pub mRule: root::RefPtr<root::RawServoFontFaceRule>,
+        pub mSheetType: root::mozilla::SheetType,
+    }
+    #[test]
+    fn bindgen_test_layout_nsFontFaceRuleContainer() {
+        assert_eq!(
+            ::std::mem::size_of::<nsFontFaceRuleContainer>(),
+            16usize,
+            concat!("Size of: ", stringify!(nsFontFaceRuleContainer))
+        );
+        assert_eq!(
+            ::std::mem::align_of::<nsFontFaceRuleContainer>(),
+            8usize,
+            concat!("Alignment of ", stringify!(nsFontFaceRuleContainer))
+        );
+        assert_eq!(
+            unsafe {
+                &(*(::std::ptr::null::<nsFontFaceRuleContainer>())).mRule as *const _ as usize
+            },
+            0usize,
+            concat!(
+                "Offset of field: ",
+                stringify!(nsFontFaceRuleContainer),
+                "::",
+                stringify!(mRule)
+            )
+        );
+        assert_eq!(
+            unsafe {
+                &(*(::std::ptr::null::<nsFontFaceRuleContainer>())).mSheetType as *const _ as usize
+            },
+            8usize,
+            concat!(
+                "Offset of field: ",
+                stringify!(nsFontFaceRuleContainer),
+                "::",
+                stringify!(mSheetType)
+            )
+        );
+    }
+    #[repr(C)]
     #[derive(Debug, Copy)]
     pub struct nsDOMStyleSheetSetList {
         _unused: [u8; 0],
     }
     impl Clone for nsDOMStyleSheetSetList {
         fn clone(&self) -> Self {
             *self
         }
@@ -39868,150 +39766,16 @@ pub mod root {
                 "Offset of field: ",
                 stringify!(nsCSSCounterStyleRule),
                 "::",
                 stringify!(mGeneration)
             )
         );
     }
     #[repr(C)]
-    #[derive(Debug)]
-    pub struct nsCSSFontFaceStyleDecl {
-        pub _base: root::nsICSSDeclaration,
-        pub mDescriptors: root::mozilla::CSSFontFaceDescriptors,
-    }
-    #[test]
-    fn bindgen_test_layout_nsCSSFontFaceStyleDecl() {
-        assert_eq!(
-            ::std::mem::size_of::<nsCSSFontFaceStyleDecl>(),
-            192usize,
-            concat!("Size of: ", stringify!(nsCSSFontFaceStyleDecl))
-        );
-        assert_eq!(
-            ::std::mem::align_of::<nsCSSFontFaceStyleDecl>(),
-            8usize,
-            concat!("Alignment of ", stringify!(nsCSSFontFaceStyleDecl))
-        );
-        assert_eq!(
-            unsafe {
-                &(*(::std::ptr::null::<nsCSSFontFaceStyleDecl>())).mDescriptors as *const _ as usize
-            },
-            32usize,
-            concat!(
-                "Offset of field: ",
-                stringify!(nsCSSFontFaceStyleDecl),
-                "::",
-                stringify!(mDescriptors)
-            )
-        );
-    }
-    #[repr(C)]
-    #[derive(Debug)]
-    pub struct nsCSSFontFaceRule {
-        pub _base: root::mozilla::css::Rule,
-        pub mDecl: root::nsCSSFontFaceStyleDecl,
-    }
-    #[repr(C)]
-    #[derive(Debug, Copy)]
-    pub struct nsCSSFontFaceRule_cycleCollection {
-        pub _base: root::mozilla::css::Rule_cycleCollection,
-    }
-    #[test]
-    fn bindgen_test_layout_nsCSSFontFaceRule_cycleCollection() {
-        assert_eq!(
-            ::std::mem::size_of::<nsCSSFontFaceRule_cycleCollection>(),
-            16usize,
-            concat!("Size of: ", stringify!(nsCSSFontFaceRule_cycleCollection))
-        );
-        assert_eq!(
-            ::std::mem::align_of::<nsCSSFontFaceRule_cycleCollection>(),
-            8usize,
-            concat!(
-                "Alignment of ",
-                stringify!(nsCSSFontFaceRule_cycleCollection)
-            )
-        );
-    }
-    impl Clone for nsCSSFontFaceRule_cycleCollection {
-        fn clone(&self) -> Self {
-            *self
-        }
-    }
-    extern "C" {
-        #[link_name = "\u{1}_ZN17nsCSSFontFaceRule21_cycleCollectorGlobalE"]
-        pub static mut nsCSSFontFaceRule__cycleCollectorGlobal:
-            root::nsCSSFontFaceRule_cycleCollection;
-    }
-    #[test]
-    fn bindgen_test_layout_nsCSSFontFaceRule() {
-        assert_eq!(
-            ::std::mem::size_of::<nsCSSFontFaceRule>(),
-            256usize,
-            concat!("Size of: ", stringify!(nsCSSFontFaceRule))
-        );
-        assert_eq!(
-            ::std::mem::align_of::<nsCSSFontFaceRule>(),
-            8usize,
-            concat!("Alignment of ", stringify!(nsCSSFontFaceRule))
-        );
-        assert_eq!(
-            unsafe { &(*(::std::ptr::null::<nsCSSFontFaceRule>())).mDecl as *const _ as usize },
-            64usize,
-            concat!(
-                "Offset of field: ",
-                stringify!(nsCSSFontFaceRule),
-                "::",
-                stringify!(mDecl)
-            )
-        );
-    }
-    #[repr(C)]
-    #[derive(Debug)]
-    pub struct nsFontFaceRuleContainer {
-        pub mRule: root::RefPtr<root::nsCSSFontFaceRule>,
-        pub mSheetType: root::mozilla::SheetType,
-    }
-    #[test]
-    fn bindgen_test_layout_nsFontFaceRuleContainer() {
-        assert_eq!(
-            ::std::mem::size_of::<nsFontFaceRuleContainer>(),
-            16usize,
-            concat!("Size of: ", stringify!(nsFontFaceRuleContainer))
-        );
-        assert_eq!(
-            ::std::mem::align_of::<nsFontFaceRuleContainer>(),
-            8usize,
-            concat!("Alignment of ", stringify!(nsFontFaceRuleContainer))
-        );
-        assert_eq!(
-            unsafe {
-                &(*(::std::ptr::null::<nsFontFaceRuleContainer>())).mRule as *const _ as usize
-            },
-            0usize,
-            concat!(
-                "Offset of field: ",
-                stringify!(nsFontFaceRuleContainer),
-                "::",
-                stringify!(mRule)
-            )
-        );
-        assert_eq!(
-            unsafe {
-                &(*(::std::ptr::null::<nsFontFaceRuleContainer>())).mSheetType as *const _ as usize
-            },
-            8usize,
-            concat!(
-                "Offset of field: ",
-                stringify!(nsFontFaceRuleContainer),
-                "::",
-                stringify!(mSheetType)
-            )
-        );
-    }
-    #[repr(C)]
     #[derive(Debug, Copy)]
     pub struct nsHtml5StringParser {
         _unused: [u8; 0],
     }
     impl Clone for nsHtml5StringParser {
         fn clone(&self) -> Self {
             *self
         }
@@ -47142,16 +46906,35 @@ pub mod root {
             8usize,
             concat!(
                 "Alignment of template specialization: ",
                 stringify!(root::nsTArray<*mut root::nsISupports>)
             )
         );
     }
     #[test]
+    fn __bindgen_test_layout_RefPtr_open0_RawServoFontFaceRule_close0_instantiation() {
+        assert_eq!(
+            ::std::mem::size_of::<root::RefPtr<root::RawServoFontFaceRule>>(),
+            8usize,
+            concat!(
+                "Size of template specialization: ",
+                stringify!(root::RefPtr<root::RawServoFontFaceRule>)
+            )
+        );
+        assert_eq!(
+            ::std::mem::align_of::<root::RefPtr<root::RawServoFontFaceRule>>(),
+            8usize,
+            concat!(
+                "Alignment of template specialization: ",
+                stringify!(root::RefPtr<root::RawServoFontFaceRule>)
+            )
+        );
+    }
+    #[test]
     fn __bindgen_test_layout_RefPtr_open0_RawServoAnimationValue_close0_instantiation_1() {
         assert_eq!(
             ::std::mem::size_of::<root::RefPtr<root::RawServoAnimationValue>>(),
             8usize,
             concat!(
                 "Size of template specialization: ",
                 stringify!(root::RefPtr<root::RawServoAnimationValue>)
             )
@@ -49301,35 +49084,16 @@ pub mod root {
             8usize,
             concat!(
                 "Alignment of template specialization: ",
                 stringify!(root::RefPtr<root::nsAtom>)
             )
         );
     }
     #[test]
-    fn __bindgen_test_layout_RefPtr_open0_nsCSSFontFaceRule_close0_instantiation() {
-        assert_eq!(
-            ::std::mem::size_of::<root::RefPtr<root::nsCSSFontFaceRule>>(),
-            8usize,
-            concat!(
-                "Size of template specialization: ",
-                stringify!(root::RefPtr<root::nsCSSFontFaceRule>)
-            )
-        );
-        assert_eq!(
-            ::std::mem::align_of::<root::RefPtr<root::nsCSSFontFaceRule>>(),
-            8usize,
-            concat!(
-                "Alignment of template specialization: ",
-                stringify!(root::RefPtr<root::nsCSSFontFaceRule>)
-            )
-        );
-    }
-    #[test]
     fn __bindgen_test_layout_nsRefPtrHashKey_open0_nsAtom_close0_instantiation_5() {
         assert_eq!(
             ::std::mem::size_of::<root::nsRefPtrHashKey<root::nsAtom>>(),
             16usize,
             concat!(
                 "Size of template specialization: ",
                 stringify!(root::nsRefPtrHashKey<root::nsAtom>)
             )
--- a/servo/components/style/gecko/rules.rs
+++ b/servo/components/style/gecko/rules.rs
@@ -3,50 +3,47 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 //! Bindings for CSS Rule objects
 
 use byteorder::{BigEndian, WriteBytesExt};
 use computed_values::{font_stretch, font_style, font_weight};
 use counter_style::{self, CounterBound};
 use cssparser::UnicodeRange;
-use font_face::{FontFaceRuleData, Source, FontDisplay, FontWeight};
+use font_face::{Source, FontDisplay, FontWeight};
 use gecko_bindings::bindings;
-use gecko_bindings::structs::{self, nsCSSFontFaceRule, nsCSSValue};
+use gecko_bindings::structs::{self, nsCSSValue};
 use gecko_bindings::structs::{nsCSSCounterDesc, nsCSSCounterStyleRule};
 use gecko_bindings::sugar::ns_css_value::ToNsCssValue;
 use gecko_bindings::sugar::refptr::{RefPtr, UniqueRefPtr};
 use nsstring::nsString;
 use properties::longhands::font_language_override;
 use shared_lock::{ToCssWithGuard, SharedRwLockReadGuard};
 use std::fmt::{self, Write};
 use std::str;
 use str::CssStringWriter;
 use values::computed::font::FamilyName;
 use values::generics::font::FontTag;
 use values::specified::font::{SpecifiedFontVariationSettings, SpecifiedFontFeatureSettings};
 
-/// A @font-face rule
-pub type FontFaceRule = RefPtr<nsCSSFontFaceRule>;
-
-impl ToNsCssValue for FamilyName {
+impl<'a> ToNsCssValue for &'a FamilyName {
     fn convert(self, nscssvalue: &mut nsCSSValue) {
         nscssvalue.set_string_from_atom(&self.name)
     }
 }
 
 impl ToNsCssValue for font_weight::T {
     fn convert(self, nscssvalue: &mut nsCSSValue) {
         nscssvalue.set_integer(self.0 as i32)
     }
 }
 
-impl ToNsCssValue for FontWeight {
+impl<'a> ToNsCssValue for &'a FontWeight {
     fn convert(self, nscssvalue: &mut nsCSSValue) {
-        match self {
+        match *self {
             FontWeight::Normal =>
                 nscssvalue.set_enum(structs::NS_STYLE_FONT_WEIGHT_NORMAL as i32),
             FontWeight::Bold =>
                 nscssvalue.set_enum(structs::NS_STYLE_FONT_WEIGHT_BOLD as i32),
             FontWeight::Weight(weight) => nscssvalue.set_integer(weight.0 as i32),
         }
     }
 }
@@ -54,49 +51,49 @@ impl ToNsCssValue for FontWeight {
 impl ToNsCssValue for FontTag {
     fn convert(self, nscssvalue: &mut nsCSSValue) {
         let mut raw = [0u8; 4];
         (&mut raw[..]).write_u32::<BigEndian>(self.0).unwrap();
         nscssvalue.set_string(str::from_utf8(&raw).unwrap());
     }
 }
 
-impl ToNsCssValue for SpecifiedFontFeatureSettings {
+impl<'a> ToNsCssValue for &'a SpecifiedFontFeatureSettings {
     fn convert(self, nscssvalue: &mut nsCSSValue) {
         if self.0.is_empty() {
             nscssvalue.set_normal();
             return;
         }
 
-        nscssvalue.set_pair_list(self.0.into_iter().map(|entry| {
+        nscssvalue.set_pair_list(self.0.iter().map(|entry| {
             let mut index = nsCSSValue::null();
             index.set_integer(entry.value.value());
             (entry.tag.into(), index)
         }))
     }
 }
 
-impl ToNsCssValue for SpecifiedFontVariationSettings {
+impl<'a> ToNsCssValue for &'a SpecifiedFontVariationSettings {
     fn convert(self, nscssvalue: &mut nsCSSValue) {
         if self.0.is_empty() {
             nscssvalue.set_normal();
             return;
         }
 
-        nscssvalue.set_pair_list(self.0.into_iter().map(|entry| {
+        nscssvalue.set_pair_list(self.0.iter().map(|entry| {
             let mut value = nsCSSValue::null();
             value.set_number(entry.value.into());
             (entry.tag.into(), value)
         }))
     }
 }
 
-impl ToNsCssValue for font_language_override::SpecifiedValue {
+impl<'a> ToNsCssValue for &'a font_language_override::SpecifiedValue {
     fn convert(self, nscssvalue: &mut nsCSSValue) {
-        match self {
+        match *self {
             font_language_override::SpecifiedValue::Normal => nscssvalue.set_normal(),
             font_language_override::SpecifiedValue::Override(ref lang) => nscssvalue.set_string(&*lang),
             // This path is unreachable because the descriptor is only specified by the user.
             font_language_override::SpecifiedValue::System(_) => unreachable!(),
         }
     }
 }
 
@@ -104,19 +101,19 @@ macro_rules! map_enum {
     (
         $(
             $prop:ident {
                 $($servo:ident => $gecko:ident,)+
             }
         )+
     ) => {
         $(
-            impl ToNsCssValue for $prop::T {
+            impl<'a> ToNsCssValue for &'a $prop::T {
                 fn convert(self, nscssvalue: &mut nsCSSValue) {
-                    nscssvalue.set_enum(match self {
+                    nscssvalue.set_enum(match *self {
                         $( $prop::T::$servo => structs::$gecko as i32, )+
                     })
                 }
             }
         )+
     }
 }
 
@@ -135,105 +132,71 @@ map_enum! {
         SemiCondensed   => NS_FONT_STRETCH_SEMI_CONDENSED,
         SemiExpanded    => NS_FONT_STRETCH_SEMI_EXPANDED,
         Expanded        => NS_FONT_STRETCH_EXPANDED,
         ExtraExpanded   => NS_FONT_STRETCH_EXTRA_EXPANDED,
         UltraExpanded   => NS_FONT_STRETCH_ULTRA_EXPANDED,
     }
 }
 
-impl ToNsCssValue for Vec<Source> {
+impl<'a> ToNsCssValue for &'a Vec<Source> {
     fn convert(self, nscssvalue: &mut nsCSSValue) {
         let src_len = self.iter().fold(0, |acc, src| {
             acc + match *src {
                 // Each format hint takes one position in the array of mSrc.
                 Source::Url(ref url) => url.format_hints.len() + 1,
                 Source::Local(_) => 1,
             }
         });
         let mut target_srcs =
             nscssvalue.set_array(src_len as i32).as_mut_slice().iter_mut();
         macro_rules! next { () => {
             target_srcs.next().expect("Length of target_srcs should be enough")
         } }
-        for src in self.into_iter() {
-            match src {
-                Source::Url(url) => {
+        for src in self.iter() {
+            match *src {
+                Source::Url(ref url) => {
                     next!().set_url(&url.url);
                     for hint in url.format_hints.iter() {
                         next!().set_font_format(&hint);
                     }
                 }
-                Source::Local(family) => {
+                Source::Local(ref family) => {
                     next!().set_local_font(&family.name);
                 }
             }
         }
         debug_assert!(target_srcs.next().is_none(), "Should have filled all slots");
     }
 }
 
-impl ToNsCssValue for Vec<UnicodeRange> {
+impl<'a> ToNsCssValue for &'a Vec<UnicodeRange> {
     fn convert(self, nscssvalue: &mut nsCSSValue) {
         let target_ranges = nscssvalue
             .set_array((self.len() * 2) as i32)
             .as_mut_slice().chunks_mut(2);
         for (range, target) in self.iter().zip(target_ranges) {
             target[0].set_integer(range.start as i32);
             target[1].set_integer(range.end as i32);
         }
     }
 }
 
-impl ToNsCssValue for FontDisplay {
+impl<'a> ToNsCssValue for &'a FontDisplay {
     fn convert(self, nscssvalue: &mut nsCSSValue) {
-        nscssvalue.set_enum(match self {
+        nscssvalue.set_enum(match *self {
             FontDisplay::Auto => structs::NS_FONT_DISPLAY_AUTO,
             FontDisplay::Block => structs::NS_FONT_DISPLAY_BLOCK,
             FontDisplay::Swap => structs::NS_FONT_DISPLAY_SWAP,
             FontDisplay::Fallback => structs::NS_FONT_DISPLAY_FALLBACK,
             FontDisplay::Optional => structs::NS_FONT_DISPLAY_OPTIONAL,
         } as i32)
     }
 }
 
-impl FontFaceRule {
-    /// Ask Gecko to deep clone the nsCSSFontFaceRule, and then construct
-    /// a FontFaceRule object from it.
-    pub fn deep_clone_from_gecko(&self) -> FontFaceRule {
-        let result = unsafe {
-            UniqueRefPtr::from_addrefed(
-                bindings::Gecko_CSSFontFaceRule_Clone(self.get()))
-        };
-        result.get()
-    }
-}
-
-impl From<FontFaceRuleData> for FontFaceRule {
-    fn from(data: FontFaceRuleData) -> FontFaceRule {
-        let mut result = unsafe {
-            UniqueRefPtr::from_addrefed(bindings::Gecko_CSSFontFaceRule_Create(
-                data.source_location.line as u32, data.source_location.column as u32
-            ))
-        };
-        data.set_descriptors(&mut result.mDecl.mDescriptors);
-        result.get()
-    }
-}
-
-impl ToCssWithGuard for FontFaceRule {
-    fn to_css(&self, _guard: &SharedRwLockReadGuard, dest: &mut CssStringWriter) -> fmt::Result {
-        let mut css_text = nsString::new();
-        unsafe {
-            bindings::Gecko_CSSFontFaceRule_GetCssText(self.get(), &mut *css_text);
-        }
-        write!(dest, "{}", css_text)
-    }
-}
-
 /// A @counter-style rule
 pub type CounterStyleRule = RefPtr<nsCSSCounterStyleRule>;
 
 impl CounterStyleRule {
     /// Ask Gecko to deep clone the nsCSSCounterStyleRule, and then construct
     /// a CounterStyleRule object from it.
     pub fn deep_clone_from_gecko(&self) -> CounterStyleRule {
         let result = unsafe {
--- a/servo/components/style/gecko_bindings/sugar/refptr.rs
+++ b/servo/components/style/gecko_bindings/sugar/refptr.rs
@@ -255,18 +255,16 @@ macro_rules! impl_refcount {
             }
             unsafe fn release(&self) {
                 ::gecko_bindings::bindings::$release(self as *const _ as *mut _)
             }
         }
     );
 }
 
-impl_refcount!(::gecko_bindings::structs::nsCSSFontFaceRule,
-               Gecko_CSSFontFaceRule_AddRef, Gecko_CSSFontFaceRule_Release);
 impl_refcount!(::gecko_bindings::structs::nsCSSCounterStyleRule,
                Gecko_CSSCounterStyleRule_AddRef, Gecko_CSSCounterStyleRule_Release);
 
 // Companion of NS_DECL_THREADSAFE_FFI_REFCOUNTING.
 //
 // Gets you a free RefCounted impl implemented via FFI.
 macro_rules! impl_threadsafe_refcount {
     ($t:ty, $addref:ident, $release:ident) => (
--- a/servo/components/style/properties/gecko.mako.rs
+++ b/servo/components/style/properties/gecko.mako.rs
@@ -61,16 +61,17 @@ use values::{self, CustomIdent, Either, 
 use values::computed::{NonNegativeLength, ToComputedValue, Percentage};
 use values::computed::font::{FontSize, SingleFontFamily};
 use values::computed::effects::{BoxShadow, Filter, SimpleShadow};
 use values::computed::outline::OutlineStyle;
 use values::generics::column::ColumnCount;
 use values::generics::position::ZIndex;
 use values::generics::text::MozTabSize;
 use values::generics::transform::TransformStyle;
+use values::generics::url::UrlOrNone;
 use computed_values::border_style;
 
 pub mod style_structs {
     % for style_struct in data.style_structs:
     pub use super::${style_struct.gecko_struct_name} as ${style_struct.name};
     % endfor
 }
 
@@ -932,20 +933,20 @@ def set_gecko_property(ffi_name, expr):
         BorderCornerRadius::new(width, height)
     }
 </%def>
 
 <%def name="impl_css_url(ident, gecko_ffi_name)">
     #[allow(non_snake_case)]
     pub fn set_${ident}(&mut self, v: longhands::${ident}::computed_value::T) {
         match v {
-            Either::First(url) => {
+            UrlOrNone::Url(ref url) => {
                 self.gecko.${gecko_ffi_name}.set_move(url.url_value.clone())
             }
-            Either::Second(_none) => {
+            UrlOrNone::None => {
                 unsafe {
                     self.gecko.${gecko_ffi_name}.clear();
                 }
             }
         }
     }
     #[allow(non_snake_case)]
     pub fn copy_${ident}_from(&mut self, other: &Self) {
@@ -956,25 +957,24 @@ def set_gecko_property(ffi_name, expr):
     #[allow(non_snake_case)]
     pub fn reset_${ident}(&mut self, other: &Self) {
         self.copy_${ident}_from(other)
     }
 
     #[allow(non_snake_case)]
     pub fn clone_${ident}(&self) -> longhands::${ident}::computed_value::T {
         use values::specified::url::SpecifiedUrl;
-        use values::None_;
 
         if self.gecko.${gecko_ffi_name}.mRawPtr.is_null() {
-            Either::Second(None_)
+            UrlOrNone::none()
         } else {
             unsafe {
                 let ref gecko_url_value = *self.gecko.${gecko_ffi_name}.mRawPtr;
-                Either::First(SpecifiedUrl::from_url_value_data(&gecko_url_value._base)
-                        .expect("${gecko_ffi_name} could not convert to SpecifiedUrl"))
+                UrlOrNone::Url(SpecifiedUrl::from_url_value_data(&gecko_url_value._base)
+                               .expect("${gecko_ffi_name} could not convert to SpecifiedUrl"))
             }
         }
     }
 </%def>
 
 <%
 transform_functions = [
     ("Matrix3D", "matrix3d", ["number"] * 16),
@@ -1464,17 +1464,17 @@ impl Clone for ${style_struct.gecko_stru
         "Position": impl_position,
         "RGBAColor": impl_rgba_color,
         "SVGLength": impl_svg_length,
         "SVGOpacity": impl_svg_opacity,
         "SVGPaint": impl_svg_paint,
         "SVGWidth": impl_svg_length,
         "Transform": impl_transform,
         "TransformOrigin": impl_transform_origin,
-        "UrlOrNone": impl_css_url,
+        "url::UrlOrNone": impl_css_url,
     }
 
     def longhand_method(longhand):
         args = dict(ident=longhand.ident, gecko_ffi_name=longhand.gecko_ffi_name)
 
         # get the method and pass additional keyword or type-specific arguments
         if longhand.logical:
             method = impl_logical
@@ -4056,24 +4056,23 @@ fn static_assert() {
     <% impl_simple_image_array_property("attachment", "background", "mImage", "mAttachment", "Background") %>
     <% impl_simple_image_array_property("blend_mode", "background", "mImage", "mBlendMode", "Background") %>
 </%self:impl_trait>
 
 <%self:impl_trait style_struct_name="List"
                   skip_longhands="list-style-image list-style-type quotes -moz-image-region">
 
     pub fn set_list_style_image(&mut self, image: longhands::list_style_image::computed_value::T) {
-        use values::Either;
         match image {
-            longhands::list_style_image::computed_value::T(Either::Second(_none)) => {
+            UrlOrNone::None => {
                 unsafe {
                     Gecko_SetListStyleImageNone(&mut self.gecko);
                 }
             }
-            longhands::list_style_image::computed_value::T(Either::First(ref url)) => {
+            UrlOrNone::Url(ref url) => {
                 unsafe {
                     Gecko_SetListStyleImageImageValue(&mut self.gecko, url.image_value.get());
                 }
                 // We don't need to record this struct as uncacheable, like when setting
                 // background-image to a url() value, since only properties in reset structs
                 // are re-used from the applicable declaration cache, and the List struct
                 // is an inherited struct.
             }
@@ -4085,30 +4084,26 @@ fn static_assert() {
     }
 
     pub fn reset_list_style_image(&mut self, other: &Self) {
         self.copy_list_style_image_from(other)
     }
 
     pub fn clone_list_style_image(&self) -> longhands::list_style_image::computed_value::T {
         use values::specified::url::SpecifiedImageUrl;
-        use values::{Either, None_};
-
-        longhands::list_style_image::computed_value::T(
-            match self.gecko.mListStyleImage.mRawPtr.is_null() {
-                true => Either::Second(None_),
-                false => {
-                    unsafe {
-                        let ref gecko_image_request = *self.gecko.mListStyleImage.mRawPtr;
-                        Either::First(SpecifiedImageUrl::from_image_request(gecko_image_request)
-                                      .expect("mListStyleImage could not convert to SpecifiedImageUrl"))
-                    }
-                }
-            }
-        )
+
+        if self.gecko.mListStyleImage.mRawPtr.is_null() {
+            return UrlOrNone::None;
+        }
+
+        unsafe {
+            let ref gecko_image_request = *self.gecko.mListStyleImage.mRawPtr;
+            UrlOrNone::Url(SpecifiedImageUrl::from_image_request(gecko_image_request)
+                           .expect("mListStyleImage could not convert to SpecifiedImageUrl"))
+        }
     }
 
     pub fn set_list_style_type(&mut self, v: longhands::list_style_type::computed_value::T, device: &Device) {
         use gecko_bindings::bindings::Gecko_SetCounterStyleToString;
         use nsstring::{nsACString, nsCStr};
         use self::longhands::list_style_type::computed_value::T;
         match v {
             T::CounterStyle(s) => s.to_gecko_value(&mut self.gecko.mCounterStyle, device),
--- a/servo/components/style/properties/helpers/animated_properties.mako.rs
+++ b/servo/components/style/properties/helpers/animated_properties.mako.rs
@@ -31,28 +31,29 @@ use std::mem::{self, ManuallyDrop};
 use style_traits::ParseError;
 use super::ComputedValues;
 use values::{CSSFloat, CustomIdent, Either};
 use values::animated::{Animate, Procedure, ToAnimatedValue, ToAnimatedZero};
 use values::animated::color::RGBA as AnimatedRGBA;
 use values::animated::effects::Filter as AnimatedFilter;
 use values::animated::effects::FilterList as AnimatedFilterList;
 use values::computed::{Angle, CalcLengthOrPercentage};
-use values::computed::{ClipRect, Context, ComputedUrl};
+use values::computed::{ClipRect, Context};
 use values::computed::{Length, LengthOrPercentage, LengthOrPercentageOrAuto};
 use values::computed::{LengthOrPercentageOrNone, MaxLength};
 use values::computed::{NonNegativeNumber, Number, NumberOrPercentage, Percentage};
 use values::computed::length::NonNegativeLengthOrPercentage;
 use values::computed::ToComputedValue;
 use values::computed::transform::{DirectionVector, Matrix, Matrix3D};
 use values::computed::transform::TransformOperation as ComputedTransformOperation;
 use values::computed::transform::Transform as ComputedTransform;
 use values::computed::transform::Rotate as ComputedRotate;
 use values::computed::transform::Translate as ComputedTranslate;
 use values::computed::transform::Scale as ComputedScale;
+use values::computed::url::ComputedUrl;
 use values::generics::transform::{self, Rotate, Translate, Scale, Transform, TransformOperation};
 use values::distance::{ComputeSquaredDistance, SquaredDistance};
 use values::generics::font::{FontSettings as GenericFontSettings, FontTag, VariationValue};
 use values::computed::font::FontVariationSettings;
 use values::generics::effects::Filter;
 use values::generics::position as generic_position;
 use values::generics::svg::{SVGLength,  SvgLengthOrPercentageOrNumber, SVGPaint};
 use values::generics::svg::{SVGPaintKind, SVGStrokeDashArray, SVGOpacity};
--- a/servo/components/style/properties/longhand/box.mako.rs
+++ b/servo/components/style/properties/longhand/box.mako.rs
@@ -608,17 +608,17 @@
                             -moz-window-titlebar-maximized
                          """,
                          gecko_ffi_name="mAppearance",
                          gecko_constant_prefix="ThemeWidgetType_NS_THEME",
                          products="gecko",
                          spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-appearance)",
                          animation_value_type="discrete")}
 
-${helpers.predefined_type("-moz-binding", "UrlOrNone", "Either::Second(None_)",
+${helpers.predefined_type("-moz-binding", "url::UrlOrNone", "computed::url::UrlOrNone::none()",
                           products="gecko",
                           animation_value_type="none",
                           gecko_ffi_name="mBinding",
                           spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-binding)")}
 
 ${helpers.single_keyword("-moz-orient",
                           "inline block horizontal vertical",
                           products="gecko",
--- a/servo/components/style/properties/longhand/inherited_svg.mako.rs
+++ b/servo/components/style/properties/longhand/inherited_svg.mako.rs
@@ -107,27 +107,27 @@
 
 // Section 14 - Clipping, Masking and Compositing
 ${helpers.single_keyword("clip-rule", "nonzero evenodd",
                          products="gecko",
                          gecko_enum_prefix="StyleFillRule",
                          animation_value_type="discrete",
                          spec="https://www.w3.org/TR/SVG11/masking.html#ClipRuleProperty")}
 
-${helpers.predefined_type("marker-start", "UrlOrNone", "Either::Second(None_)",
+${helpers.predefined_type("marker-start", "url::UrlOrNone", "computed::url::UrlOrNone::none()",
                           products="gecko",
                           animation_value_type="discrete",
                           spec="https://www.w3.org/TR/SVG2/painting.html#VertexMarkerProperties")}
 
-${helpers.predefined_type("marker-mid", "UrlOrNone", "Either::Second(None_)",
+${helpers.predefined_type("marker-mid", "url::UrlOrNone", "computed::url::UrlOrNone::none()",
                           products="gecko",
                           animation_value_type="discrete",
                           spec="https://www.w3.org/TR/SVG2/painting.html#VertexMarkerProperties")}
 
-${helpers.predefined_type("marker-end", "UrlOrNone", "Either::Second(None_)",
+${helpers.predefined_type("marker-end", "url::UrlOrNone", "computed::url::UrlOrNone::none()",
                           products="gecko",
                           animation_value_type="discrete",
                           spec="https://www.w3.org/TR/SVG2/painting.html#VertexMarkerProperties")}
 
 ${helpers.predefined_type("paint-order", "SVGPaintOrder", "computed::SVGPaintOrder::normal()",
                           products="gecko",
                           animation_value_type="discrete",
                           spec="https://www.w3.org/TR/SVG2/painting.html#PaintOrder")}
--- a/servo/components/style/properties/longhand/list.mako.rs
+++ b/servo/components/style/properties/longhand/list.mako.rs
@@ -36,19 +36,19 @@
         animation_value_type="discrete",
         boxed=True,
         spec="https://drafts.csswg.org/css-lists/#propdef-list-style-type",
         servo_restyle_damage="rebuild_and_reflow",
     )}
 % endif
 
 ${helpers.predefined_type("list-style-image",
-                          "ListStyleImage",
-                          initial_value="specified::ListStyleImage::none()",
-                          initial_specified_value="specified::ListStyleImage::none()",
+                          "url::ImageUrlOrNone",
+                          initial_value="computed::url::ImageUrlOrNone::none()",
+                          initial_specified_value="specified::url::ImageUrlOrNone::none()",
                           animation_value_type="discrete",
                           spec="https://drafts.csswg.org/css-lists/#propdef-list-style-image",
                           servo_restyle_damage="rebuild_and_reflow")}
 
 ${helpers.predefined_type("quotes",
                           "Quotes",
                           "computed::Quotes::get_initial_value()",
                           animation_value_type="discrete",
--- a/servo/components/style/properties/shorthand/inherited_svg.mako.rs
+++ b/servo/components/style/properties/shorthand/inherited_svg.mako.rs
@@ -2,17 +2,17 @@
  * 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/. */
 
 <%namespace name="helpers" file="/helpers.mako.rs" />
 
 <%helpers:shorthand name="marker" products="gecko"
     sub_properties="marker-start marker-end marker-mid"
     spec="https://www.w3.org/TR/SVG2/painting.html#MarkerShorthand">
-    use values::specified::UrlOrNone;
+    use values::specified::url::UrlOrNone;
 
     pub fn parse_value<'i, 't>(
         context: &ParserContext,
         input: &mut Parser<'i, 't>,
     ) -> Result<Longhands, ParseError<'i>> {
         use parser::Parse;
         let url = UrlOrNone::parse(context, input)?;
 
--- a/servo/components/style/properties/shorthand/list.mako.rs
+++ b/servo/components/style/properties/shorthand/list.mako.rs
@@ -4,17 +4,17 @@
 
 <%namespace name="helpers" file="/helpers.mako.rs" />
 
 <%helpers:shorthand name="list-style"
                     sub_properties="list-style-position list-style-image list-style-type"
                     derive_serialize="True"
                     spec="https://drafts.csswg.org/css-lists/#propdef-list-style">
     use properties::longhands::{list_style_image, list_style_position, list_style_type};
-    use values::{Either, None_};
+    use values::specified::url::ImageUrlOrNone;
 
     pub fn parse_value<'i, 't>(
         context: &ParserContext,
         input: &mut Parser<'i, 't>,
     ) -> Result<Longhands, ParseError<'i>> {
         // `none` is ambiguous until we've finished parsing the shorthands, so we count the number
         // of times we see it.
         let mut nones = 0u8;
@@ -71,38 +71,38 @@
 
         // If there are two `none`s, then we can't have a type or image; if there is one `none`,
         // then we can't have both a type *and* an image; if there is no `none` then we're fine as
         // long as we parsed something.
         match (any, nones, list_style_type, image) {
             (true, 2, None, None) => {
                 Ok(expanded! {
                     list_style_position: position,
-                    list_style_image: list_style_image::SpecifiedValue(Either::Second(None_)),
+                    list_style_image: ImageUrlOrNone::none(),
                     list_style_type: list_style_type_none(),
                 })
             }
             (true, 1, None, Some(image)) => {
                 Ok(expanded! {
                     list_style_position: position,
                     list_style_image: image,
                     list_style_type: list_style_type_none(),
                 })
             }
             (true, 1, Some(list_style_type), None) => {
                 Ok(expanded! {
                     list_style_position: position,
-                    list_style_image: list_style_image::SpecifiedValue(Either::Second(None_)),
+                    list_style_image: ImageUrlOrNone::none(),
                     list_style_type: list_style_type,
                 })
             }
             (true, 1, None, None) => {
                 Ok(expanded! {
                     list_style_position: position,
-                    list_style_image: list_style_image::SpecifiedValue(Either::Second(None_)),
+                    list_style_image: ImageUrlOrNone::none(),
                     list_style_type: list_style_type_none(),
                 })
             }
             (true, 0, list_style_type, image) => {
                 Ok(expanded! {
                     list_style_position: position,
                     list_style_image: unwrap_or_initial!(list_style_image, image),
                     list_style_type: unwrap_or_initial!(list_style_type),
--- a/servo/components/style/stylesheets/font_face_rule.rs
+++ b/servo/components/style/stylesheets/font_face_rule.rs
@@ -1,24 +1,7 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-// TODO(emilio): unify this, components/style/font_face.rs, and
-// components/style/gecko/rules.rs
 #![allow(missing_docs)]
 
-#[cfg(feature = "servo")]
 pub use font_face::FontFaceRuleData as FontFaceRule;
-#[cfg(feature = "gecko")]
-pub use gecko::rules::FontFaceRule;
-
-impl FontFaceRule {
-    #[cfg(feature = "servo")]
-    pub fn clone_conditionally_gecko_or_servo(&self) -> FontFaceRule {
-        self.clone()
-    }
-
-    #[cfg(feature = "gecko")]
-    pub fn clone_conditionally_gecko_or_servo(&self) -> FontFaceRule {
-        self.deep_clone_from_gecko()
-    }
-}
--- a/servo/components/style/stylesheets/mod.rs
+++ b/servo/components/style/stylesheets/mod.rs
@@ -301,18 +301,17 @@ impl DeepCloneWithLock for CssRule {
             },
             CssRule::Media(ref arc) => {
                 let rule = arc.read_with(guard);
                 CssRule::Media(Arc::new(
                     lock.wrap(rule.deep_clone_with_lock(lock, guard, params))))
             },
             CssRule::FontFace(ref arc) => {
                 let rule = arc.read_with(guard);
-                CssRule::FontFace(Arc::new(lock.wrap(
-                    rule.clone_conditionally_gecko_or_servo())))
+                CssRule::FontFace(Arc::new(lock.wrap(rule.clone())))
             },
             CssRule::FontFeatureValues(ref arc) => {
                 let rule = arc.read_with(guard);
                 CssRule::FontFeatureValues(Arc::new(lock.wrap(rule.clone())))
             },
             CssRule::CounterStyle(ref arc) => {
                 let rule = arc.read_with(guard);
                 CssRule::CounterStyle(Arc::new(lock.wrap(
--- a/servo/components/style/stylist.rs
+++ b/servo/components/style/stylist.rs
@@ -1607,17 +1607,17 @@ pub struct ExtraStyleData {
     pub counter_styles: PrecomputedHashMap<Atom, Arc<Locked<CounterStyleRule>>>,
 
     /// A map of effective page rules.
     #[cfg(feature = "gecko")]
     pub pages: Vec<Arc<Locked<PageRule>>>,
 }
 
 // FIXME(emilio): This is kind of a lie, and relies on us not cloning
-// nsCSSFontFaceRules or nsCSSCounterStyleRules OMT (which we don't).
+// nsCSSCounterStyleRules OMT (which we don't).
 #[cfg(feature = "gecko")]
 unsafe impl Sync for ExtraStyleData {}
 #[cfg(feature = "gecko")]
 unsafe impl Send for ExtraStyleData {}
 
 #[cfg(feature = "gecko")]
 impl ExtraStyleData {
     /// Add the given @font-face rule.
--- a/servo/components/style/values/animated/mod.rs
+++ b/servo/components/style/values/animated/mod.rs
@@ -8,20 +8,20 @@
 //! computed values and need yet another intermediate representation. This
 //! module's raison d'être is to ultimately contain all these types.
 
 use app_units::Au;
 use euclid::{Point2D, Size2D};
 use smallvec::SmallVec;
 use values::computed::Angle as ComputedAngle;
 use values::computed::BorderCornerRadius as ComputedBorderCornerRadius;
-#[cfg(feature = "servo")]
-use values::computed::ComputedUrl;
 use values::computed::MaxLength as ComputedMaxLength;
 use values::computed::MozLength as ComputedMozLength;
+#[cfg(feature = "servo")]
+use values::computed::url::ComputedUrl;
 use values::specified::url::SpecifiedUrl;
 
 pub mod color;
 pub mod effects;
 
 /// Animate from one value to another.
 ///
 /// This trait is derivable with `#[derive(Animate)]`. The derived
--- a/servo/components/style/values/computed/basic_shape.rs
+++ b/servo/components/style/values/computed/basic_shape.rs
@@ -4,17 +4,18 @@
 
 //! CSS handling for the computed value of
 //! [`basic-shape`][basic-shape]s
 //!
 //! [basic-shape]: https://drafts.csswg.org/css-shapes/#typedef-basic-shape
 
 use std::fmt::{self, Write};
 use style_traits::{CssWriter, ToCss};
-use values::computed::{LengthOrPercentage, ComputedUrl, Image};
+use values::computed::{LengthOrPercentage, Image};
+use values::computed::url::ComputedUrl;
 use values::generics::basic_shape::{BasicShape as GenericBasicShape};
 use values::generics::basic_shape::{Circle as GenericCircle, ClippingShape as GenericClippingShape};
 use values::generics::basic_shape::{Ellipse as GenericEllipse, FloatAreaShape as GenericFloatAreaShape};
 use values::generics::basic_shape::{InsetRect as GenericInsetRect, ShapeRadius as GenericShapeRadius};
 
 /// A computed clipping shape.
 pub type ClippingShape = GenericClippingShape<BasicShape, ComputedUrl>;
 
--- a/servo/components/style/values/computed/image.rs
+++ b/servo/components/style/values/computed/image.rs
@@ -7,21 +7,22 @@
 //!
 //! [image]: https://drafts.csswg.org/css-images/#image-values
 
 use cssparser::RGBA;
 use std::f32::consts::PI;
 use std::fmt::{self, Write};
 use style_traits::{CssWriter, ToCss};
 use values::{Either, None_};
-use values::computed::{Angle, ComputedImageUrl, Context};
+use values::computed::{Angle, Context};
 use values::computed::{Length, LengthOrPercentage, NumberOrPercentage, ToComputedValue};
 #[cfg(feature = "gecko")]
 use values::computed::Percentage;
 use values::computed::position::Position;
+use values::computed::url::ComputedImageUrl;
 use values::generics::image::{CompatMode, ColorStop as GenericColorStop, EndingShape as GenericEndingShape};
 use values::generics::image::{Gradient as GenericGradient, GradientItem as GenericGradientItem};
 use values::generics::image::{Image as GenericImage, GradientKind as GenericGradientKind};
 use values::generics::image::{LineDirection as GenericLineDirection, MozImageRect as GenericMozImageRect};
 use values::specified::image::LineDirection as SpecifiedLineDirection;
 use values::specified::position::{X, Y};
 
 /// A computed image layer.
--- a/servo/components/style/values/computed/length.rs
+++ b/servo/components/style/values/computed/length.rs
@@ -16,17 +16,18 @@ use super::{Number, ToComputedValue, Con
 use values::{Auto, CSSFloat, Either, Normal, specified};
 use values::animated::{Animate, Procedure, ToAnimatedValue, ToAnimatedZero};
 use values::distance::{ComputeSquaredDistance, SquaredDistance};
 use values::generics::NonNegative;
 use values::specified::length::{AbsoluteLength, FontBaseSize, FontRelativeLength};
 use values::specified::length::ViewportPercentageLength;
 
 pub use super::image::Image;
-pub use values::specified::{Angle, BorderStyle, Time, UrlOrNone};
+pub use values::specified::url::UrlOrNone;
+pub use values::specified::{Angle, BorderStyle, Time};
 
 impl ToComputedValue for specified::NoCalcLength {
     type ComputedValue = CSSPixelLength;
 
     #[inline]
     fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
         match *self {
             specified::NoCalcLength::Absolute(length) =>
--- a/servo/components/style/values/computed/list.rs
+++ b/servo/components/style/values/computed/list.rs
@@ -1,15 +1,15 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 //! `list` computed values.
 
-pub use values::specified::list::{ListStyleImage, Quotes};
+pub use values::specified::list::Quotes;
 #[cfg(feature = "gecko")]
 pub use values::specified::list::ListStyleType;
 
 impl Quotes {
     /// Initial value for `quotes`.
     ///
     /// FIXME(emilio): This should ideally not allocate.
     #[inline]
--- a/servo/components/style/values/computed/mod.rs
+++ b/servo/components/style/values/computed/mod.rs
@@ -57,17 +57,17 @@ pub use self::inherited_box::{Orientatio
 pub use self::gecko::ScrollSnapPoint;
 pub use self::rect::LengthOrNumberRect;
 pub use super::{Auto, Either, None_};
 pub use super::specified::{BorderStyle, TextDecorationLine};
 pub use self::length::{CalcLengthOrPercentage, Length, LengthOrNumber, LengthOrPercentage};
 pub use self::length::{LengthOrPercentageOrAuto, LengthOrPercentageOrNone, MaxLength, MozLength};
 pub use self::length::{CSSPixelLength, ExtremumLength, NonNegativeLength};
 pub use self::length::{NonNegativeLengthOrPercentage, NonNegativeLengthOrPercentageOrAuto};
-pub use self::list::{ListStyleImage, Quotes};
+pub use self::list::Quotes;
 #[cfg(feature = "gecko")]
 pub use self::list::ListStyleType;
 pub use self::outline::OutlineStyle;
 pub use self::percentage::Percentage;
 pub use self::pointing::{CaretColor, Cursor};
 #[cfg(feature = "gecko")]
 pub use self::pointing::CursorImage;
 pub use self::position::{GridAutoFlow, GridTemplateAreas, Position, ZIndex};
@@ -76,17 +76,16 @@ pub use self::svg::{SVGPaintOrder, SVGSt
 pub use self::svg::MozContextProperties;
 pub use self::table::XSpan;
 pub use self::text::{InitialLetter, LetterSpacing, LineHeight, MozTabSize};
 pub use self::text::{TextAlign, TextEmphasisPosition, TextEmphasisStyle, TextOverflow, WordSpacing};
 pub use self::time::Time;
 pub use self::transform::{Rotate, Scale, TimingFunction, Transform, TransformOperation};
 pub use self::transform::{TransformOrigin, TransformStyle, Translate};
 pub use self::ui::MozForceBrokenImageIcon;
-pub use self::url::{ComputedUrl, ComputedImageUrl};
 
 #[cfg(feature = "gecko")]
 pub mod align;
 pub mod angle;
 pub mod background;
 pub mod basic_shape;
 pub mod border;
 #[path = "box.rs"]
@@ -109,24 +108,17 @@ pub mod pointing;
 pub mod position;
 pub mod rect;
 pub mod svg;
 pub mod table;
 pub mod text;
 pub mod time;
 pub mod transform;
 pub mod ui;
-
-/// Common handling for the computed value CSS url() values.
-pub mod url {
-#[cfg(feature = "servo")]
-pub use ::servo::url::{ComputedUrl, ComputedImageUrl};
-#[cfg(feature = "gecko")]
-pub use ::gecko::url::{ComputedUrl, ComputedImageUrl};
-}
+pub mod url;
 
 /// A `Context` is all the data a specified value could ever need to compute
 /// itself and be transformed to a computed value.
 pub struct Context<'a> {
     /// Whether the current element is the root element.
     pub is_root_element: bool,
 
     /// Values accessed through this need to be in the properties "computed
@@ -634,14 +626,8 @@ impl ClipRectOrAuto {
     /// Check if it is auto
     pub fn is_auto(&self) -> bool {
         match *self {
             Either::Second(_) => true,
             _ => false
         }
     }
 }
-
-/// <url> | <none>
-pub type UrlOrNone = Either<ComputedUrl, None_>;
-
-/// <url> | <none> for image
-pub type ImageUrlOrNone = Either<ComputedImageUrl, None_>;
--- a/servo/components/style/values/computed/svg.rs
+++ b/servo/components/style/values/computed/svg.rs
@@ -1,19 +1,20 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 //! Computed types for SVG properties.
 
 use app_units::Au;
 use values::RGBA;
-use values::computed::{ComputedUrl, LengthOrPercentage, NonNegativeLength};
+use values::computed::{LengthOrPercentage, NonNegativeLength};
 use values::computed::{NonNegativeNumber, NonNegativeLengthOrPercentage, Number};
 use values::computed::Opacity;
+use values::computed::url::ComputedUrl;
 use values::generics::svg as generic;
 
 pub use values::specified::SVGPaintOrder;
 
 pub use values::specified::MozContextProperties;
 
 /// Computed SVG Paint value
 pub type SVGPaint = generic::SVGPaint<RGBA, ComputedUrl>;
new file mode 100644
--- /dev/null
+++ b/servo/components/style/values/computed/url.rs
@@ -0,0 +1,18 @@
+/* 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/. */
+
+//! Common handling for the computed value CSS url() values.
+
+use values::generics::url::UrlOrNone as GenericUrlOrNone;
+
+#[cfg(feature = "servo")]
+pub use ::servo::url::{ComputedUrl, ComputedImageUrl};
+#[cfg(feature = "gecko")]
+pub use ::gecko::url::{ComputedUrl, ComputedImageUrl};
+
+/// Computed <url> | <none>
+pub type UrlOrNone = GenericUrlOrNone<ComputedUrl>;
+
+/// Computed image <url> | <none>
+pub type ImageUrlOrNone = GenericUrlOrNone<ComputedImageUrl>;
--- a/servo/components/style/values/generics/mod.rs
+++ b/servo/components/style/values/generics/mod.rs
@@ -27,16 +27,17 @@ pub mod grid;
 pub mod image;
 pub mod pointing;
 pub mod position;
 pub mod rect;
 pub mod size;
 pub mod svg;
 pub mod text;
 pub mod transform;
+pub mod url;
 
 // https://drafts.csswg.org/css-counter-styles/#typedef-symbols-type
 #[allow(missing_docs)]
 #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
 #[derive(Clone, Copy, Debug, Eq, MallocSizeOf, Parse, PartialEq)]
 #[derive(ToComputedValue, ToCss)]
 pub enum SymbolsType {
     Cyclic,
new file mode 100644
--- /dev/null
+++ b/servo/components/style/values/generics/url.rs
@@ -0,0 +1,39 @@
+/* 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/. */
+
+//! Generic types for url properties.
+
+use cssparser::Parser;
+use parser::{Parse, ParserContext};
+use style_traits::ParseError;
+
+/// An image url or none, used for example in list-style-image
+#[derive(Animate, Clone, ComputeSquaredDistance, Debug, MallocSizeOf)]
+#[derive(PartialEq, ToAnimatedValue, ToAnimatedZero, ToComputedValue, ToCss)]
+pub enum UrlOrNone<Url> {
+    /// `none`
+    None,
+    /// `A URL`
+    Url(Url),
+}
+
+impl<Url> UrlOrNone<Url> {
+    /// Initial "none" value for properties such as `list-style-image`
+    pub fn none() -> Self {
+        UrlOrNone::None
+    }
+}
+
+impl<Url> Parse for UrlOrNone<Url> where Url: Parse {
+    fn parse<'i, 't>(
+        context: &ParserContext,
+        input: &mut Parser<'i, 't>,
+    ) -> Result<UrlOrNone<Url>, ParseError<'i>> {
+        if let Ok(url) = input.try(|input| Url::parse(context, input)) {
+            return Ok(UrlOrNone::Url(url));
+        }
+        input.expect_ident_matching("none")?;
+        Ok(UrlOrNone::None)
+    }
+}
--- a/servo/components/style/values/specified/list.rs
+++ b/servo/components/style/values/specified/list.rs
@@ -3,22 +3,20 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 //! `list` specified values.
 
 use cssparser::{Parser, Token};
 use parser::{Parse, ParserContext};
 use std::fmt::{self, Write};
 use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss};
-use values::{Either, None_};
 #[cfg(feature = "gecko")]
 use values::CustomIdent;
 #[cfg(feature = "gecko")]
 use values::generics::CounterStyleOrNone;
-use values::specified::ImageUrlOrNone;
 
 /// Specified and computed `list-style-type` property.
 #[cfg(feature = "gecko")]
 #[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq, ToComputedValue, ToCss)]
 pub enum ListStyleType {
     /// <counter-style> | none
     CounterStyle(CounterStyleOrNone),
     /// <string>
@@ -68,41 +66,16 @@ impl Parse for ListStyleType {
         if let Ok(style) = input.try(|i| CounterStyleOrNone::parse(context, i)) {
             return Ok(ListStyleType::CounterStyle(style))
         }
 
         Ok(ListStyleType::String(input.expect_string()?.as_ref().to_owned()))
     }
 }
 
-/// Specified and computed `list-style-image` property.
-#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToCss)]
-pub struct ListStyleImage(pub ImageUrlOrNone);
-
-// FIXME(nox): This is wrong, there are different types for specified
-// and computed URLs in Servo.
-trivial_to_computed_value!(ListStyleImage);
-
-impl ListStyleImage {
-    /// Initial specified value for `list-style-image`.
-    #[inline]
-    pub fn none() -> ListStyleImage {
-        ListStyleImage(Either::Second(None_))
-    }
-}
-
-impl Parse for ListStyleImage {
-    fn parse<'i, 't>(
-        context: &ParserContext,
-        input: &mut Parser<'i, 't>,
-    ) -> Result<ListStyleImage, ParseError<'i>> {
-        ImageUrlOrNone::parse(context, input).map(ListStyleImage)
-    }
-}
-
 /// Specified and computed `quote` property.
 ///
 /// FIXME(emilio): It's a shame that this allocates all the time it's computed,
 /// probably should just be refcounted.
 #[derive(Clone, Debug, MallocSizeOf, PartialEq, ToComputedValue)]
 pub struct Quotes(pub Box<[(Box<str>, Box<str>)]>);
 
 impl ToCss for Quotes {
--- a/servo/components/style/values/specified/mod.rs
+++ b/servo/components/style/values/specified/mod.rs
@@ -6,22 +6,21 @@
 //!
 //! TODO(emilio): Enhance docs.
 
 use Prefix;
 use context::QuirksMode;
 use cssparser::{Parser, Token, serialize_identifier};
 use num_traits::One;
 use parser::{ParserContext, Parse};
-use self::url::{SpecifiedImageUrl, SpecifiedUrl};
 use std::f32;
 use std::fmt::{self, Write};
 use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss};
 use style_traits::values::specified::AllowedNumericType;
-use super::{Auto, CSSFloat, CSSInteger, Either, None_};
+use super::{Auto, CSSFloat, CSSInteger, Either};
 use super::computed::{Context, ToComputedValue};
 use super::generics::{GreaterThanOrEqualToOne, NonNegative};
 use super::generics::grid::{GridLine as GenericGridLine, TrackBreadth as GenericTrackBreadth};
 use super::generics::grid::{TrackSize as GenericTrackSize, TrackList as GenericTrackList};
 use values::serialize_atom_identifier;
 use values::specified::calc::CalcNode;
 
 pub use properties::animated_properties::TransitionProperty;
@@ -51,17 +50,17 @@ pub use self::image::{ColorStop, EndingS
 pub use self::image::{GradientItem, GradientKind, Image, ImageLayer, MozImageRect};
 pub use self::inherited_box::ImageOrientation;
 pub use self::length::{AbsoluteLength, CalcLengthOrPercentage, CharacterWidth};
 pub use self::length::{FontRelativeLength, Length, LengthOrNumber};
 pub use self::length::{LengthOrPercentage, LengthOrPercentageOrAuto};
 pub use self::length::{LengthOrPercentageOrNone, MaxLength, MozLength};
 pub use self::length::{NoCalcLength, ViewportPercentageLength};
 pub use self::length::{NonNegativeLengthOrPercentage, NonNegativeLengthOrPercentageOrAuto};
-pub use self::list::{ListStyleImage, Quotes};
+pub use self::list::Quotes;
 #[cfg(feature = "gecko")]
 pub use self::list::ListStyleType;
 pub use self::outline::OutlineStyle;
 pub use self::rect::LengthOrNumberRect;
 pub use self::percentage::Percentage;
 pub use self::pointing::{CaretColor, Cursor};
 #[cfg(feature = "gecko")]
 pub use self::pointing::CursorImage;
@@ -109,24 +108,17 @@ pub mod position;
 pub mod rect;
 pub mod source_size_list;
 pub mod svg;
 pub mod table;
 pub mod text;
 pub mod time;
 pub mod transform;
 pub mod ui;
-
-/// Common handling for the specified value CSS url() values.
-pub mod url {
-#[cfg(feature = "servo")]
-pub use ::servo::url::{SpecifiedUrl, SpecifiedImageUrl};
-#[cfg(feature = "gecko")]
-pub use ::gecko::url::{SpecifiedUrl, SpecifiedImageUrl};
-}
+pub mod url;
 
 /// Parse a `<number>` value, with a given clamping mode.
 fn parse_number_with_clamping_mode<'i, 't>(
     context: &ParserContext,
     input: &mut Parser<'i, 't>,
     clamping_mode: AllowedNumericType,
 ) -> Result<Number, ParseError<'i>> {
     let location = input.current_source_location();
@@ -514,22 +506,16 @@ pub type PositiveInteger = GreaterThanOr
 
 impl Parse for PositiveInteger {
     #[inline]
     fn parse<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i>> {
         Integer::parse_positive(context, input).map(GreaterThanOrEqualToOne::<Integer>)
     }
 }
 
-#[allow(missing_docs)]
-pub type UrlOrNone = Either<SpecifiedUrl, None_>;
-
-/// The specified value of a `<url>` for image or `none`.
-pub type ImageUrlOrNone = Either<SpecifiedImageUrl, None_>;
-
 /// The specified value of a grid `<track-breadth>`
 pub type TrackBreadth = GenericTrackBreadth<LengthOrPercentage>;
 
 /// The specified value of a grid `<track-size>`
 pub type TrackSize = GenericTrackSize<LengthOrPercentage>;
 
 /// The specified value of a grid `<track-list>`
 /// (could also be `<auto-track-list>` or `<explicit-track-list>`)
--- a/servo/components/style/values/specified/svg.rs
+++ b/servo/components/style/values/specified/svg.rs
@@ -7,18 +7,19 @@
 use cssparser::Parser;
 use parser::{Parse, ParserContext};
 use std::fmt::{self, Write};
 use style_traits::{CommaWithSpace, CssWriter, ParseError, Separator};
 use style_traits::{StyleParseErrorKind, ToCss};
 use values::CustomIdent;
 use values::generics::svg as generic;
 use values::specified::{LengthOrPercentage, NonNegativeLengthOrPercentage, NonNegativeNumber};
-use values::specified::{Number, Opacity, SpecifiedUrl};
+use values::specified::{Number, Opacity};
 use values::specified::color::RGBAColor;
+use values::specified::url::SpecifiedUrl;
 
 /// Specified SVG Paint value
 pub type SVGPaint = generic::SVGPaint<RGBAColor, SpecifiedUrl>;
 
 
 /// Specified SVG Paint Kind value
 pub type SVGPaintKind = generic::SVGPaintKind<RGBAColor, SpecifiedUrl>;
 
new file mode 100644
--- /dev/null
+++ b/servo/components/style/values/specified/url.rs
@@ -0,0 +1,18 @@
+/* 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/. */
+
+//! Common handling for the specified value CSS url() values.
+
+use values::generics::url::UrlOrNone as GenericUrlOrNone;
+
+#[cfg(feature = "servo")]
+pub use ::servo::url::{SpecifiedUrl, SpecifiedImageUrl};
+#[cfg(feature = "gecko")]
+pub use ::gecko::url::{SpecifiedUrl, SpecifiedImageUrl};
+
+/// Specified <url> | <none>
+pub type UrlOrNone = GenericUrlOrNone<SpecifiedUrl>;
+
+/// Specified image <url> | <none>
+pub type ImageUrlOrNone = GenericUrlOrNone<SpecifiedImageUrl>;
--- a/servo/ports/geckolib/glue.rs
+++ b/servo/ports/geckolib/glue.rs
@@ -1,13 +1,13 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-use cssparser::{ParseErrorKind, Parser, ParserInput};
+use cssparser::{ParseErrorKind, Parser, ParserInput, SourceLocation};
 use cssparser::ToCss as ParserToCss;
 use env_logger::Builder;
 use malloc_size_of::MallocSizeOfOps;
 use selectors::NthIndexCache;
 use selectors::matching::{MatchingContext, MatchingMode, matches_selector};
 use servo_arc::{Arc, ArcBorrow, RawOffsetArc};
 use smallvec::SmallVec;
 use std::cell::RefCell;
@@ -36,16 +36,17 @@ use style::gecko::traversal::RecalcStyle
 use style::gecko::wrapper::{GeckoElement, GeckoNode};
 use style::gecko_bindings::bindings;
 use style::gecko_bindings::bindings::{RawGeckoElementBorrowed, RawGeckoElementBorrowedOrNull, RawGeckoNodeBorrowed};
 use style::gecko_bindings::bindings::{RawGeckoKeyframeListBorrowed, RawGeckoKeyframeListBorrowedMut};
 use style::gecko_bindings::bindings::{RawServoAuthorStyles, RawServoAuthorStylesBorrowed};
 use style::gecko_bindings::bindings::{RawServoAuthorStylesBorrowedMut, RawServoAuthorStylesOwned};
 use style::gecko_bindings::bindings::{RawServoDeclarationBlockBorrowed, RawServoDeclarationBlockStrong};
 use style::gecko_bindings::bindings::{RawServoDocumentRule, RawServoDocumentRuleBorrowed};
+use style::gecko_bindings::bindings::{RawServoFontFaceRuleBorrowed, RawServoFontFaceRuleStrong};
 use style::gecko_bindings::bindings::{RawServoFontFeatureValuesRule, RawServoFontFeatureValuesRuleBorrowed};
 use style::gecko_bindings::bindings::{RawServoImportRule, RawServoImportRuleBorrowed};
 use style::gecko_bindings::bindings::{RawServoKeyframe, RawServoKeyframeBorrowed, RawServoKeyframeStrong};
 use style::gecko_bindings::bindings::{RawServoKeyframesRule, RawServoKeyframesRuleBorrowed};
 use style::gecko_bindings::bindings::{RawServoMediaListBorrowed, RawServoMediaListStrong};
 use style::gecko_bindings::bindings::{RawServoMediaRule, RawServoMediaRuleBorrowed};
 use style::gecko_bindings::bindings::{RawServoNamespaceRule, RawServoNamespaceRuleBorrowed};
 use style::gecko_bindings::bindings::{RawServoPageRule, RawServoPageRuleBorrowed};
@@ -86,27 +87,28 @@ use style::gecko_bindings::bindings::nsC
 use style::gecko_bindings::bindings::nsTArrayBorrowed_uintptr_t;
 use style::gecko_bindings::bindings::nsTimingFunctionBorrowed;
 use style::gecko_bindings::bindings::nsTimingFunctionBorrowedMut;
 use style::gecko_bindings::structs;
 use style::gecko_bindings::structs::{CallerType, CSSPseudoElementType, CompositeOperation};
 use style::gecko_bindings::structs::{Loader, LoaderReusableStyleSheets};
 use style::gecko_bindings::structs::{RawServoStyleRule, ComputedStyleStrong, RustString};
 use style::gecko_bindings::structs::{ServoStyleSheet, SheetLoadData, SheetParsingMode, nsAtom, nsCSSPropertyID};
-use style::gecko_bindings::structs::{nsCSSFontDesc, nsCSSFontFaceRule, nsCSSCounterStyleRule};
+use style::gecko_bindings::structs::{nsCSSFontDesc, nsCSSCounterStyleRule};
 use style::gecko_bindings::structs::{nsRestyleHint, nsChangeHint, PropertyValuePair};
 use style::gecko_bindings::structs::AtomArray;
 use style::gecko_bindings::structs::IterationCompositeOperation;
 use style::gecko_bindings::structs::MallocSizeOf as GeckoMallocSizeOf;
 use style::gecko_bindings::structs::OriginFlags;
 use style::gecko_bindings::structs::OriginFlags_Author;
 use style::gecko_bindings::structs::OriginFlags_User;
 use style::gecko_bindings::structs::OriginFlags_UserAgent;
 use style::gecko_bindings::structs::RawGeckoGfxMatrix4x4;
 use style::gecko_bindings::structs::RawGeckoPresContextOwned;
+use style::gecko_bindings::structs::RawServoFontFaceRule;
 use style::gecko_bindings::structs::RawServoSelectorList;
 use style::gecko_bindings::structs::RawServoSourceSizeList;
 use style::gecko_bindings::structs::SeenPtrs;
 use style::gecko_bindings::structs::ServoElementSnapshotTable;
 use style::gecko_bindings::structs::ServoStyleSetSizes;
 use style::gecko_bindings::structs::ServoTraversalFlags;
 use style::gecko_bindings::structs::StyleRuleInclusion;
 use style::gecko_bindings::structs::URLExtraData;
@@ -135,18 +137,18 @@ use style::properties::animated_properti
 use style::properties::animated_properties::compare_property_priority;
 use style::rule_cache::RuleCacheConditions;
 use style::rule_tree::{CascadeLevel, StrongRuleNode, StyleSource};
 use style::selector_parser::{PseudoElementCascadeType, SelectorImpl};
 use style::shared_lock::{SharedRwLockReadGuard, StylesheetGuards, ToCssWithGuard, Locked};
 use style::string_cache::{Atom, WeakAtom};
 use style::style_adjuster::StyleAdjuster;
 use style::stylesheets::{CssRule, CssRules, CssRuleType, CssRulesHelpers, DocumentRule};
-use style::stylesheets::{FontFeatureValuesRule, ImportRule, KeyframesRule, MediaRule};
-use style::stylesheets::{NamespaceRule, Origin, OriginSet, PageRule, StyleRule};
+use style::stylesheets::{FontFaceRule, FontFeatureValuesRule, ImportRule, KeyframesRule};
+use style::stylesheets::{MediaRule, NamespaceRule, Origin, OriginSet, PageRule, StyleRule};
 use style::stylesheets::{StylesheetContents, SupportsRule};
 use style::stylesheets::StylesheetLoader as StyleStylesheetLoader;
 use style::stylesheets::keyframes_rule::{Keyframe, KeyframeSelector, KeyframesStepValue};
 use style::stylesheets::supports_rule::parse_condition_or_declaration;
 use style::stylist::{add_size_of_ua_cache, AuthorStylesEnabled, RuleInclusion, Stylist};
 use style::thread_state;
 use style::timer::Timer;
 use style::traversal::DomTraversal;
@@ -1741,16 +1743,22 @@ impl_group_rule_funcs! { (Document, Docu
 }
 
 impl_basic_rule_funcs! { (FontFeatureValues, FontFeatureValuesRule, RawServoFontFeatureValuesRule),
     getter: Servo_CssRules_GetFontFeatureValuesRuleAt,
     debug: Servo_FontFeatureValuesRule_Debug,
     to_css: Servo_FontFeatureValuesRule_GetCssText,
 }
 
+impl_basic_rule_funcs! { (FontFace, FontFaceRule, RawServoFontFaceRule),
+    getter: Servo_CssRules_GetFontFaceRuleAt,
+    debug: Servo_FontFaceRule_Debug,
+    to_css: Servo_FontFaceRule_GetCssText,
+}
+
 macro_rules! impl_getter_for_embedded_rule {
     ($getter:ident: $name:ident -> $ty:ty) => {
         #[no_mangle]
         pub extern "C" fn $getter(rules: ServoCssRulesBorrowed, index: u32) -> *mut $ty
         {
             let global_style_data = &*GLOBAL_STYLE_DATA;
             let guard = global_style_data.shared_lock.read();
             let rules = Locked::<CssRules>::as_arc(&rules).read_with(&guard);
@@ -1758,18 +1766,16 @@ macro_rules! impl_getter_for_embedded_ru
                 CssRule::$name(ref rule) => rule.read_with(&guard).get(),
                 _ => unreachable!(concat!(stringify!($getter), " should only be called on a ",
                                           stringify!($name), " rule")),
             }
         }
     }
 }
 
-impl_getter_for_embedded_rule!(Servo_CssRules_GetFontFaceRuleAt:
-                               FontFace -> nsCSSFontFaceRule);
 impl_getter_for_embedded_rule!(Servo_CssRules_GetCounterStyleRuleAt:
                                CounterStyle -> nsCSSCounterStyleRule);
 
 #[no_mangle]
 pub extern "C" fn Servo_StyleRule_GetStyle(rule: RawServoStyleRuleBorrowed) -> RawServoDeclarationBlockStrong {
     read_locked_arc(rule, |rule: &StyleRule| {
         rule.block.clone().into_strong()
     })
@@ -2179,16 +2185,265 @@ pub extern "C" fn Servo_FontFeatureValue
     result: *mut nsAString,
 ) {
     read_locked_arc(rule, |rule: &FontFeatureValuesRule| {
         rule.value_to_css(&mut CssWriter::new(unsafe { result.as_mut().unwrap() })).unwrap();
     })
 }
 
 #[no_mangle]
+pub extern "C" fn Servo_FontFaceRule_CreateEmpty() -> RawServoFontFaceRuleStrong {
+    let global_style_data = &*GLOBAL_STYLE_DATA;
+    // XXX This is not great. We should split FontFace descriptor data
+    // from the rule, so that we don't need to create the rule like this
+    // and the descriptor data itself can be hold in UniquePtr from the
+    // Gecko side. See bug 1450904.
+    Arc::new(global_style_data.shared_lock.wrap(FontFaceRule::empty(SourceLocation {
+        line: 0,
+        column: 0,
+    }))).into_strong()
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_FontFaceRule_Clone(
+    rule: RawServoFontFaceRuleBorrowed,
+) -> RawServoFontFaceRuleStrong {
+    let clone = read_locked_arc(rule, |rule: &FontFaceRule| rule.clone());
+    let global_style_data = &*GLOBAL_STYLE_DATA;
+    Arc::new(global_style_data.shared_lock.wrap(clone)).into_strong()
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_FontFaceRule_GetSourceLocation(
+    rule: RawServoFontFaceRuleBorrowed,
+    line: *mut u32,
+    column: *mut u32,
+) {
+    read_locked_arc(rule, |rule: &FontFaceRule| {
+        let location = rule.source_location;
+        *line.as_mut().unwrap() = location.line as u32;
+        *column.as_mut().unwrap() = location.column as u32;
+    });
+}
+
+macro_rules! apply_font_desc_list {
+    ($apply_macro:ident) => {
+        $apply_macro! {
+            valid: [
+                eCSSFontDesc_Family => family,
+                eCSSFontDesc_Style => style,
+                eCSSFontDesc_Weight => weight,
+                eCSSFontDesc_Stretch => stretch,
+                eCSSFontDesc_Src => sources,
+                eCSSFontDesc_UnicodeRange => unicode_range,
+                eCSSFontDesc_FontFeatureSettings => feature_settings,
+                eCSSFontDesc_FontVariationSettings => variation_settings,
+                eCSSFontDesc_FontLanguageOverride => language_override,
+                eCSSFontDesc_Display => display,
+            ]
+            invalid: [
+                eCSSFontDesc_UNKNOWN,
+                eCSSFontDesc_COUNT,
+            ]
+        }
+    }
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_FontFaceRule_Length(
+    rule: RawServoFontFaceRuleBorrowed,
+) -> u32 {
+    read_locked_arc(rule, |rule: &FontFaceRule| {
+        let mut result = 0;
+        macro_rules! count_values {
+            (
+                valid: [$($v_enum_name:ident => $field:ident,)*]
+                invalid: [$($i_enum_name:ident,)*]
+            ) => {
+                $(if rule.$field.is_some() {
+                    result += 1;
+                })*
+            }
+        }
+        apply_font_desc_list!(count_values);
+        result
+    })
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_FontFaceRule_IndexGetter(
+    rule: RawServoFontFaceRuleBorrowed,
+    index: u32,
+) -> nsCSSFontDesc {
+    read_locked_arc(rule, |rule: &FontFaceRule| {
+        let mut count = 0;
+        macro_rules! lookup_index {
+            (
+                valid: [$($v_enum_name:ident => $field:ident,)*]
+                invalid: [$($i_enum_name:ident,)*]
+            ) => {
+                $(if rule.$field.is_some() {
+                    count += 1;
+                    if count - 1 == index {
+                        return nsCSSFontDesc::$v_enum_name;
+                    }
+                })*
+            }
+        }
+        apply_font_desc_list!(lookup_index);
+        return nsCSSFontDesc::eCSSFontDesc_UNKNOWN;
+    })
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_FontFaceRule_GetDeclCssText(
+    rule: RawServoFontFaceRuleBorrowed,
+    result: *mut nsAString,
+) {
+    read_locked_arc(rule, |rule: &FontFaceRule| {
+        rule.decl_to_css(result.as_mut().unwrap()).unwrap();
+    })
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_FontFaceRule_GetDescriptor(
+    rule: RawServoFontFaceRuleBorrowed,
+    desc: nsCSSFontDesc,
+    result: nsCSSValueBorrowedMut,
+) {
+    read_locked_arc(rule, |rule: &FontFaceRule| {
+        macro_rules! to_css_value {
+            (
+                valid: [$($v_enum_name:ident => $field:ident,)*]
+                invalid: [$($i_enum_name:ident,)*]
+            ) => {
+                match desc {
+                    $(
+                        nsCSSFontDesc::$v_enum_name => {
+                            if let Some(ref value) = rule.$field {
+                                result.set_from(value);
+                            }
+                        }
+                    )*
+                    $(
+                        nsCSSFontDesc::$i_enum_name => {
+                            debug_assert!(false, "not a valid font descriptor");
+                        }
+                    )*
+                }
+            }
+        }
+        apply_font_desc_list!(to_css_value)
+    })
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_FontFaceRule_GetDescriptorCssText(
+    rule: RawServoFontFaceRuleBorrowed,
+    desc: nsCSSFontDesc,
+    result: *mut nsAString,
+) {
+    read_locked_arc(rule, |rule: &FontFaceRule| {
+        let mut writer = CssWriter::new(result.as_mut().unwrap());
+        macro_rules! to_css_text {
+            (
+                valid: [$($v_enum_name:ident => $field:ident,)*]
+                invalid: [$($i_enum_name:ident,)*]
+            ) => {
+                match desc {
+                    $(
+                        nsCSSFontDesc::$v_enum_name => {
+                            if let Some(ref value) = rule.$field {
+                                value.to_css(&mut writer).unwrap();
+                            }
+                        }
+                    )*
+                    $(
+                        nsCSSFontDesc::$i_enum_name => {
+                            debug_assert!(false, "not a valid font descriptor");
+                        }
+                    )*
+                }
+            }
+        }
+        apply_font_desc_list!(to_css_text)
+    })
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_FontFaceRule_SetDescriptor(
+    rule: RawServoFontFaceRuleBorrowed,
+    desc: nsCSSFontDesc,
+    value: *const nsACString,
+    data: *mut URLExtraData,
+) -> bool {
+    let value = value.as_ref().unwrap().as_str_unchecked();
+    let mut input = ParserInput::new(&value);
+    let mut parser = Parser::new(&mut input);
+    let url_data = RefPtr::from_ptr_ref(&data);
+    let context = ParserContext::new(
+        Origin::Author,
+        url_data,
+        Some(CssRuleType::FontFace),
+        ParsingMode::DEFAULT,
+        QuirksMode::NoQuirks,
+    );
+
+    write_locked_arc(rule, |rule: &mut FontFaceRule| {
+        macro_rules! to_css_text {
+            (
+                valid: [$($v_enum_name:ident => $field:ident,)*]
+                invalid: [$($i_enum_name:ident,)*]
+            ) => {
+                match desc {
+                    $(
+                        nsCSSFontDesc::$v_enum_name => {
+                            if let Ok(value) = parser.parse_entirely(|i| Parse::parse(&context, i)) {
+                                rule.$field = Some(value);
+                                true
+                            } else {
+                                false
+                            }
+                        }
+                    )*
+                    $(
+                        nsCSSFontDesc::$i_enum_name => {
+                            debug_assert!(false, "not a valid font descriptor");
+                            false
+                        }
+                    )*
+                }
+            }
+        }
+        apply_font_desc_list!(to_css_text)
+    })
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_FontFaceRule_ResetDescriptor(
+    rule: RawServoFontFaceRuleBorrowed,
+    desc: nsCSSFontDesc,
+) {
+    write_locked_arc(rule, |rule: &mut FontFaceRule| {
+        macro_rules! reset_desc {
+            (
+                valid: [$($v_enum_name:ident => $field:ident,)*]
+                invalid: [$($i_enum_name:ident,)*]
+            ) => {
+                match desc {
+                    $(nsCSSFontDesc::$v_enum_name => rule.$field = None,)*
+                    $(nsCSSFontDesc::$i_enum_name => debug_assert!(false, "not a valid font descriptor"),)*
+                }
+            }
+        }
+        apply_font_desc_list!(reset_desc)
+    })
+}
+
+#[no_mangle]
 pub unsafe extern "C" fn Servo_ComputedValues_GetForAnonymousBox(
     parent_style_or_null: ComputedStyleBorrowedOrNull,
     pseudo_tag: *mut nsAtom,
     raw_data: RawServoStyleSetBorrowed,
 ) -> ComputedStyleStrong {
     let global_style_data = &*GLOBAL_STYLE_DATA;
     let guard = global_style_data.shared_lock.read();
     let guards = StylesheetGuards::same(&guard);
@@ -4325,35 +4580,32 @@ pub unsafe extern "C" fn Servo_StyleSet_
 #[no_mangle]
 pub extern "C" fn Servo_StyleSet_GetFontFaceRules(
     raw_data: RawServoStyleSetBorrowed,
     rules: RawGeckoFontFaceRuleListBorrowedMut,
 ) {
     let data = PerDocumentStyleData::from_ffi(raw_data).borrow();
     debug_assert_eq!(rules.len(), 0);
 
-    let global_style_data = &*GLOBAL_STYLE_DATA;
-    let guard = global_style_data.shared_lock.read();
-
     let len: u32 = data
         .stylist
         .iter_extra_data_origins()
         .map(|(d, _)| d.font_faces.len() as u32)
         .sum();
 
     // Reversed iterator because Gecko expects rules to appear sorted
     // UserAgent first, Author last.
     let font_face_iter = data
         .stylist
         .iter_extra_data_origins_rev()
         .flat_map(|(d, o)| d.font_faces.iter().zip(iter::repeat(o)));
 
     unsafe { rules.set_len(len) };
     for (src, dest) in font_face_iter.zip(rules.iter_mut()) {
-        dest.mRule = src.0.read_with(&guard).clone().forget();
+        dest.mRule.set_arc_leaky(src.0.clone());
         dest.mSheetType = src.1.into();
     }
 }
 
 #[no_mangle]
 pub extern "C" fn Servo_StyleSet_GetCounterStyleRule(
     raw_data: RawServoStyleSetBorrowed,
     name: *mut nsAtom,
@@ -4843,93 +5095,16 @@ pub extern "C" fn Servo_ParseTransformIn
 
     let result = unsafe { result.as_mut() }.expect("not a valid matrix");
     let contain_3d = unsafe { contain_3d.as_mut() }.expect("not a valid bool");
     *result = m.to_row_major_array();
     *contain_3d = is_3d;
     true
 }
 
-// https://drafts.csswg.org/css-font-loading/#dom-fontface-fontface
-#[no_mangle]
-pub extern "C" fn Servo_ParseFontDescriptor(
-    desc_id: nsCSSFontDesc,
-    value: *const nsAString,
-    data: *mut URLExtraData,
-    result: nsCSSValueBorrowedMut,
-) -> bool {
-    use cssparser::UnicodeRange;
-    use self::nsCSSFontDesc::*;
-    use style::computed_values::{font_stretch, font_style};
-    use style::font_face::{FontDisplay, FontWeight, Source};
-    use style::properties::longhands::font_language_override;
-    use style::values::computed::font::FamilyName;
-    use style::values::specified::font::{SpecifiedFontFeatureSettings, SpecifiedFontVariationSettings};
-
-    let string = unsafe { (*value).to_string() };
-    let mut input = ParserInput::new(&string);
-    let mut parser = Parser::new(&mut input);
-    let url_data = unsafe { RefPtr::from_ptr_ref(&data) };
-    let context = ParserContext::new(
-        Origin::Author,
-        url_data,
-        Some(CssRuleType::FontFace),
-        ParsingMode::DEFAULT,
-        QuirksMode::NoQuirks,
-    );
-
-    macro_rules! parse_font_desc {
-        (
-            valid = [ $( $v_enum_name: ident / $t: ty, )* ]
-            invalid = [ $( $i_enum_name: ident, )* ]
-        ) => {
-            match desc_id {
-                $(
-                    $v_enum_name => {
-                        let f = match parser.parse_entirely(|i| <$t as Parse>::parse(&context, i)) {
-                            Ok(f) => f,
-                            Err(..) => return false,
-                        };
-                        result.set_from(f);
-                    },
-                )*
-                $(
-                    $i_enum_name => {
-                        debug_assert!(false, "$i_enum_name is not a valid font descriptor");
-                        return false;
-                    },
-                )*
-            }
-        }
-    }
-
-    // We implement the parser of each arm according to the implementation of @font-face rule.
-    // see component/style/font_face.rs for more detail.
-    parse_font_desc!(
-        valid = [
-            eCSSFontDesc_Family / FamilyName,
-            eCSSFontDesc_Style / font_style::T,
-            eCSSFontDesc_Weight / FontWeight,
-            eCSSFontDesc_Stretch / font_stretch::T,
-            eCSSFontDesc_Src / Vec<Source>,
-            eCSSFontDesc_UnicodeRange / Vec<UnicodeRange>,
-            eCSSFontDesc_FontFeatureSettings / SpecifiedFontFeatureSettings,
-            eCSSFontDesc_FontVariationSettings / SpecifiedFontVariationSettings,
-            eCSSFontDesc_FontLanguageOverride / font_language_override::SpecifiedValue,
-            eCSSFontDesc_Display / FontDisplay,
-        ]
-        invalid = [
-            eCSSFontDesc_UNKNOWN,
-            eCSSFontDesc_COUNT,
-        ]
-    );
-
-    true
-}
-
 #[no_mangle]
 pub extern "C" fn Servo_ParseFontShorthandForMatching(
     value: *const nsAString,
     data: *mut URLExtraData,
     family: *mut structs::RefPtr<structs::SharedFontList>,
     style: nsCSSValueBorrowedMut,
     stretch: nsCSSValueBorrowedMut,
     weight: nsCSSValueBorrowedMut
@@ -4957,21 +5132,21 @@ pub extern "C" fn Servo_ParseFontShortha
 
     // The system font is not acceptable, so we return false.
     let family = unsafe { &mut *family };
     match font.font_family {
         FontFamily::Values(list) => family.set_move(list.0),
         FontFamily::System(_) => return false,
     }
     style.set_from(match font.font_style {
-        font_style::SpecifiedValue::Keyword(kw) => kw,
+        font_style::SpecifiedValue::Keyword(ref kw) => kw,
         font_style::SpecifiedValue::System(_) => return false,
     });
     stretch.set_from(match font.font_stretch {
-        font_stretch::SpecifiedValue::Keyword(kw) => kw,
+        font_stretch::SpecifiedValue::Keyword(ref kw) => kw,
         font_stretch::SpecifiedValue::System(_) => return false,
     });
     match font.font_weight {
         FontWeight::Weight(w) => weight.set_from(w),
         FontWeight::Normal => weight.set_enum(structs::NS_STYLE_FONT_WEIGHT_NORMAL as i32),
         FontWeight::Bold => weight.set_enum(structs::NS_STYLE_FONT_WEIGHT_BOLD as i32),
         // Resolve relative font weights against the initial of font-weight
         // (normal, which is equivalent to 400).
--- a/servo/tests/unit/style/properties/serialization.rs
+++ b/servo/tests/unit/style/properties/serialization.rs
@@ -488,29 +488,27 @@ mod shorthand_serialization {
 
             let serialization = block.to_css_string();
 
             assert_eq!(serialization, "border: 4px solid;");
         }
     }
 
     mod list_style {
-        use style::properties::longhands::list_style_image::SpecifiedValue as ListStyleImage;
         use style::properties::longhands::list_style_position::SpecifiedValue as ListStylePosition;
         use style::properties::longhands::list_style_type::SpecifiedValue as ListStyleType;
-        use style::values::Either;
+        use style::values::generics::url::UrlOrNone as ImageUrlOrNone;
         use super::*;
 
         #[test]
         fn list_style_should_show_all_properties_when_values_are_set() {
             let mut properties = Vec::new();
 
             let position = ListStylePosition::Inside;
-            let image =
-                ListStyleImage(Either::First(SpecifiedUrl::new_for_testing("http://servo/test.png")));
+            let image = ImageUrlOrNone::Url(SpecifiedUrl::new_for_testing("http://servo/test.png"));
             let style_type = ListStyleType::Disc;
 
             properties.push(PropertyDeclaration::ListStylePosition(position));
 
             #[cfg(feature = "gecko")]
             properties.push(PropertyDeclaration::ListStyleImage(Box::new(image)));
             #[cfg(not(feature = "gecko"))]
             properties.push(PropertyDeclaration::ListStyleImage(image));
--- a/testing/gtest/mozilla/MozGTestBench.cpp
+++ b/testing/gtest/mozilla/MozGTestBench.cpp
@@ -13,17 +13,17 @@
 #define MOZ_GTEST_NUM_ITERATIONS 5
 
 using mozilla::TimeStamp;
 
 namespace mozilla {
 void GTestBench(const char* aSuite, const char* aName,
                 const std::function<void()>& aTest)
 {
-#ifdef DEBUG
+#if defined(DEBUG) || defined(MOZ_ASAN)
   // Run the test to make sure that it doesn't fail but don't log
   // any measurements since it's not an optimized build.
   aTest();
 #else
   bool shouldAlert = bool(getenv("PERFHERDER_ALERTING_ENABLED"));
   std::vector<int> durations;
 
   for (int i=0; i<MOZ_GTEST_NUM_ITERATIONS; i++) {
--- a/third_party/rust/cssparser/.cargo-checksum.json
+++ b/third_party/rust/cssparser/.cargo-checksum.json
@@ -1,1 +1,1 @@
-{"files":{".travis.yml":"f1fb4b65964c81bc1240544267ea334f554ca38ae7a74d57066f4d47d2b5d568","Cargo.toml":"7807f16d417eb1a6ede56cd4ba2da6c5c63e4530289b3f0848f4b154e18eba02","LICENSE":"fab3dd6bdab226f1c08630b1dd917e11fcb4ec5e1e020e2c16f83a0a13863e85","README.md":"c5781e673335f37ed3d7acb119f8ed33efdf6eb75a7094b7da2abe0c3230adb8","build.rs":"b29fc57747f79914d1c2fb541e2bb15a003028bb62751dcb901081ccc174b119","build/match_byte.rs":"2c84b8ca5884347d2007f49aecbd85b4c7582085526e2704399817249996e19b","docs/.nojekyll":"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855","docs/404.html":"025861f76f8d1f6d67c20ab624c6e418f4f824385e2dd8ad8732c4ea563c6a2e","docs/index.html":"025861f76f8d1f6d67c20ab624c6e418f4f824385e2dd8ad8732c4ea563c6a2e","src/color.rs":"c60f1b0ab7a2a6213e434604ee33f78e7ef74347f325d86d0b9192d8225ae1cc","src/cow_rc_str.rs":"541216f8ef74ee3cc5cbbc1347e5f32ed66588c401851c9a7d68b867aede1de0","src/from_bytes.rs":"331fe63af2123ae3675b61928a69461b5ac77799fff3ce9978c55cf2c558f4ff","src/lib.rs":"46c377e0c9a75780d5cb0bcf4dfb960f0fb2a996a13e7349bb111b9082252233","src/macros.rs":"adb9773c157890381556ea83d7942dcc676f99eea71abbb6afeffee1e3f28960","src/nth.rs":"5c70fb542d1376cddab69922eeb4c05e4fcf8f413f27563a2af50f72a47c8f8c","src/parser.rs":"9ed4aec998221eb2d2ba99db2f9f82a02399fb0c3b8500627f68f5aab872adde","src/rules_and_declarations.rs":"be2c4f3f3bb673d866575b6cb6084f1879dff07356d583ca9a3595f63b7f916f","src/serializer.rs":"4ccfc9b4fe994aab3803662bbf31cc25052a6a39531073a867b14b224afe42dd","src/size_of_tests.rs":"e5f63c8c18721cc3ff7a5407e84f9889ffa10e66da96e8510a696c3e00ad72d5","src/tests.rs":"80b02c80ab0fd580dad9206615c918e0db7dff63dfed0feeedb66f317d24b24b","src/tokenizer.rs":"429b2cba419cf8b923fbcc32d3bd34c0b39284ebfcb9fc29b8eb8643d8d5f312","src/unicode_range.rs":"c1c4ed2493e09d248c526ce1ef8575a5f8258da3962b64ffc814ef3bdf9780d0"},"package":"8a807ac3ab7a217829c2a3b65732b926b2befe6a35f33b4bf8b503692430f223"}
\ No newline at end of file
+{"files":{".travis.yml":"e8f586288c39dbaebefdd391f68376e58f3a4c568a8dc3cd97c4a362194716dd","Cargo.toml":"efd6f23ab6df0f7bffa3b56a166aff55aa5d082fa4084ba3f443e86833e783e1","LICENSE":"fab3dd6bdab226f1c08630b1dd917e11fcb4ec5e1e020e2c16f83a0a13863e85","README.md":"c5781e673335f37ed3d7acb119f8ed33efdf6eb75a7094b7da2abe0c3230adb8","build.rs":"1068e899b617cf99eee953f745d83f88859cc0cef0297fcc3310b6bd24474761","build/match_byte.rs":"0f7d39170e746d59deebf7894086f63ee39ea61fb161d24b0c7a3c1db3945e0d","docs/.nojekyll":"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855","docs/404.html":"025861f76f8d1f6d67c20ab624c6e418f4f824385e2dd8ad8732c4ea563c6a2e","docs/index.html":"025861f76f8d1f6d67c20ab624c6e418f4f824385e2dd8ad8732c4ea563c6a2e","src/color.rs":"c60f1b0ab7a2a6213e434604ee33f78e7ef74347f325d86d0b9192d8225ae1cc","src/cow_rc_str.rs":"541216f8ef74ee3cc5cbbc1347e5f32ed66588c401851c9a7d68b867aede1de0","src/from_bytes.rs":"331fe63af2123ae3675b61928a69461b5ac77799fff3ce9978c55cf2c558f4ff","src/lib.rs":"46c377e0c9a75780d5cb0bcf4dfb960f0fb2a996a13e7349bb111b9082252233","src/macros.rs":"adb9773c157890381556ea83d7942dcc676f99eea71abbb6afeffee1e3f28960","src/nth.rs":"5c70fb542d1376cddab69922eeb4c05e4fcf8f413f27563a2af50f72a47c8f8c","src/parser.rs":"9ed4aec998221eb2d2ba99db2f9f82a02399fb0c3b8500627f68f5aab872adde","src/rules_and_declarations.rs":"be2c4f3f3bb673d866575b6cb6084f1879dff07356d583ca9a3595f63b7f916f","src/serializer.rs":"4ccfc9b4fe994aab3803662bbf31cc25052a6a39531073a867b14b224afe42dd","src/size_of_tests.rs":"e5f63c8c18721cc3ff7a5407e84f9889ffa10e66da96e8510a696c3e00ad72d5","src/tests.rs":"80b02c80ab0fd580dad9206615c918e0db7dff63dfed0feeedb66f317d24b24b","src/tokenizer.rs":"429b2cba419cf8b923fbcc32d3bd34c0b39284ebfcb9fc29b8eb8643d8d5f312","src/unicode_range.rs":"191d50a1588e5c88608b84cfe9279def71f495f8e016fa093f90399bbd2b635f"},"package":"b906ac3f6108d8d0bfd4158469abe5909df1497116c8400346b5e08944579edd"}
\ No newline at end of file
--- a/third_party/rust/cssparser/.travis.yml
+++ b/third_party/rust/cssparser/.travis.yml
@@ -7,11 +7,12 @@ rust:
 script:
   - cargo build --verbose
   - cargo test --verbose
   - cargo doc --verbose
   - cargo test --features heapsize
   - cargo test --features dummy_match_byte
   - if [ "$TRAVIS_RUST_VERSION" == "nightly" ]; then cargo test --features bench; fi
   - if [ "$TRAVIS_RUST_VERSION" == "nightly" ]; then cargo test --features "bench dummy_match_byte"; fi
+  - cd macros && cargo build --verbose
 
 notifications:
   webhooks: http://build.servo.org:54856/travis
--- a/third_party/rust/cssparser/Cargo.toml
+++ b/third_party/rust/cssparser/Cargo.toml
@@ -7,17 +7,17 @@
 #
 # If you believe there's an error in this file please file an
 # issue against the rust-lang/cargo repository. If you're
 # editing this file be aware that the upstream Cargo.toml
 # will likely look very different (and much more reasonable)
 
 [package]
 name = "cssparser"
-version = "0.23.2"
+version = "0.23.4"
 authors = ["Simon Sapin <simon.sapin@exyr.org>"]
 build = "build.rs"
 exclude = ["src/css-parsing-tests/**", "src/big-data-url.css"]
 description = "Rust implementation of CSS Syntax Level 3"
 documentation = "https://docs.rs/cssparser/"
 readme = "README.md"
 keywords = ["css", "syntax", "parser"]
 license = "MPL-2.0"
@@ -28,17 +28,17 @@ version = "0.3"
 [dependencies.dtoa-short]
 version = "0.3"
 
 [dependencies.heapsize]
 version = ">= 0.3, < 0.5"
 optional = true
 
 [dependencies.itoa]
-version = "0.3"
+version = "0.4"
 
 [dependencies.matches]
 version = "0.1"
 
 [dependencies.phf]
 version = "0.7"
 
 [dependencies.procedural-masquerade]
@@ -46,24 +46,28 @@ version = "0.1"
 
 [dependencies.serde]
 version = "1.0"
 optional = true
 
 [dependencies.smallvec]
 version = "0.6"
 [dev-dependencies.difference]
-version = "1.0"
+version = "2.0"
 
 [dev-dependencies.encoding_rs]
 version = "0.7"
 
 [dev-dependencies.rustc-serialize]
 version = "0.3"
+[build-dependencies.proc-macro2]
+version = "0.2"
+
 [build-dependencies.quote]
-version = "0.3"
+version = "0.4.1"
 
 [build-dependencies.syn]
-version = "0.11"
+version = "0.12"
+features = ["extra-traits", "fold"]
 
 [features]
 bench = []
 dummy_match_byte = []
--- a/third_party/rust/cssparser/build.rs
+++ b/third_party/rust/cssparser/build.rs
@@ -1,14 +1,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
+#[macro_use]
 extern crate quote;
+#[macro_use]
 extern crate syn;
+extern crate proc_macro2;
 
 use std::env;
 use std::path::Path;
 
 
 #[cfg(feature = "dummy_match_byte")]
 mod codegen {
     use std::path::Path;
--- a/third_party/rust/cssparser/build/match_byte.rs
+++ b/third_party/rust/cssparser/build/match_byte.rs
@@ -1,271 +1,178 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-use quote::{ToTokens, Tokens};
+use quote::ToTokens;
 use std::fs::File;
 use std::io::{Read, Write};
 use std::path::Path;
-use std::vec;
-use std::iter;
 use syn;
+use syn::fold::Fold;
+
+use proc_macro2::TokenStream;
+
+struct MatchByteParser {
+}
 
 pub fn expand(from: &Path, to: &Path) {
     let mut source = String::new();
     File::open(from).unwrap().read_to_string(&mut source).unwrap();
-    let tts = syn::parse_token_trees(&source).expect("Parsing rules.rs module");
-    let mut tokens = Tokens::new();
-    tokens.append_all(expand_tts(tts));
+    let ast = syn::parse_file(&source).expect("Parsing rules.rs module");
+    let mut m = MatchByteParser {};
+    let ast = m.fold_file(ast);
 
-    let code = tokens.to_string().replace("{ ", "{\n").replace(" }", "\n}");
+    let code = ast.into_tokens().to_string().replace("{ ", "{\n").replace(" }", "\n}");
     File::create(to).unwrap().write_all(code.as_bytes()).unwrap();
 }
 
-fn expand_tts(tts: Vec<syn::TokenTree>) -> Vec<syn::TokenTree> {
-    use syn::*;
-    let mut expanded = Vec::new();
-    let mut tts = tts.into_iter();
-    while let Some(tt) = tts.next() {
-        match tt {
-            TokenTree::Token(Token::Ident(ident)) => {
-                if ident != "match_byte" {
-                    expanded.push(TokenTree::Token(Token::Ident(ident)));
-                    continue;
-                }
-
-                match tts.next() {
-                    Some(TokenTree::Token(Token::Not)) => {},
-                    other => {
-                        expanded.push(TokenTree::Token(Token::Ident(ident)));
-                        if let Some(other) = other {
-                            expanded.push(other);
-                        }
-                        continue;
-                    }
-                }
-
-                let tts = match tts.next() {
-                    Some(TokenTree::Delimited(Delimited { tts, .. })) => tts,
-                    other => {
-                        expanded.push(TokenTree::Token(Token::Ident(ident)));
-                        expanded.push(TokenTree::Token(Token::Not));
-                        if let Some(other) = other {
-                            expanded.push(other);
-                        }
-                        continue;
-                    }
-                };
-
-                let (to_be_matched, table, cases, wildcard_binding) = parse_match_bytes_macro(tts);
-                let expr = expand_match_bytes_macro(to_be_matched,
-                                                    &table,
-                                                    cases,
-                                                    wildcard_binding);
-
-                let tts = syn::parse_token_trees(&expr)
-                    .expect("parsing macro expansion as token trees");
-                expanded.extend(expand_tts(tts));
-            }
-            TokenTree::Delimited(Delimited { delim, tts }) => {
-                expanded.push(TokenTree::Delimited(Delimited {
-                    delim: delim,
-                    tts: expand_tts(tts),
-                }))
-            }
-            other => expanded.push(other),
-        }
-    }
-    expanded
+struct MatchByte {
+    expr: syn::Expr,
+    arms: Vec<syn::Arm>,
 }
 
-/// Parses a token tree corresponding to the `match_byte` macro.
+impl syn::synom::Synom for MatchByte {
+    named!(parse -> Self, do_parse!(
+        expr: syn!(syn::Expr) >>
+        punct!(,) >>
+        arms: many0!(syn!(syn::Arm)) >> (
+            MatchByte {
+                expr,
+                arms
+            }
+        )
+    ));
+}
+
+fn get_byte_from_expr_lit(expr: &Box<syn::Expr>) -> u8 {
+    match **expr {
+        syn::Expr::Lit(syn::ExprLit { ref lit, .. }) => {
+            if let syn::Lit::Byte(ref byte) = *lit {
+                byte.value()
+            }
+            else {
+                panic!("Found a pattern that wasn't a byte")
+            }
+        },
+        _ => unreachable!(),
+    }
+}
+
+
+/// Expand a TokenStream corresponding to the `match_byte` macro.
 ///
 /// ## Example
 ///
 /// ```rust
 /// match_byte! { tokenizer.next_byte_unchecked(),
 ///     b'a'..b'z' => { ... }
 ///     b'0'..b'9' => { ... }
 ///     b'\n' | b'\\' => { ... }
 ///     foo => { ... }
-/// }
-///
-/// Returns:
-///  * The token tree that contains the expression to be matched (in this case
-///    `tokenizer.next_byte_unchecked()`.
+///  }
+///  ```
 ///
-///  * The table with the different cases per byte, each entry in the table
-///    contains a non-zero integer representing a different arm of the
-///    match expression.
-///
-///  * The list of cases containing the expansion of the arms of the match
-///    expression.
-///
-///  * An optional identifier to which the wildcard pattern is matched (`foo` in
-///    this case).
-///
-fn parse_match_bytes_macro(tts: Vec<syn::TokenTree>) -> (Vec<syn::TokenTree>, [u8; 256], Vec<Case>, Option<syn::Ident>) {
-    let mut tts = tts.into_iter();
+fn expand_match_byte(body: &TokenStream) -> syn::Expr {
+    let match_byte: MatchByte = syn::parse2(body.clone()).unwrap();
+    let expr = match_byte.expr;
+    let mut cases = Vec::new();
+    let mut table = [0u8; 256];
+    let mut match_body = Vec::new();
+    let mut wildcard = None;
+
+    for (i, ref arm) in match_byte.arms.iter().enumerate() {
+        let case_id = i + 1;
+        let index = case_id as isize;
+        let name = syn::Ident::from(format!("Case{}", case_id));
 
-    // Grab the thing we're matching, until we find a comma.
-    let mut left_hand_side = vec![];
-    loop {
-        match tts.next() {
-            Some(syn::TokenTree::Token(syn::Token::Comma)) => break,
-            Some(other) => left_hand_side.push(other),
-            None => panic!("Expected not to run out of tokens looking for a comma"),
+        for pat in &arm.pats {
+            match pat {
+                &syn::Pat::Lit(syn::PatLit{ref expr}) => {
+                    let value = get_byte_from_expr_lit(expr);
+                    if table[value as usize] == 0 {
+                        table[value as usize] = case_id as u8;
+                    }
+                },
+                &syn::Pat::Range(syn::PatRange { ref lo, ref hi, .. }) => {
+                    let lo = get_byte_from_expr_lit(lo);
+                    let hi = get_byte_from_expr_lit(hi);
+                    for value in lo..hi {
+                        if table[value as usize] == 0 {
+                            table[value as usize] = case_id as u8;
+                        }
+                    }
+                    if table[hi as usize] == 0 {
+                        table[hi as usize] = case_id as u8;
+                    }
+                },
+                &syn::Pat::Wild(_) => {
+                    for byte in table.iter_mut() {
+                        if *byte == 0 {
+                            *byte = case_id as u8;
+                        }
+                    }
+                },
+                &syn::Pat::Ident(syn::PatIdent { ident, .. }) => {
+                    assert_eq!(wildcard, None);
+                    wildcard = Some(ident);
+                    for byte in table.iter_mut() {
+                        if *byte == 0 {
+                            *byte = case_id as u8;
+                        }
+                    }
+                },
+                _ => {
+                    panic!("Unexpected pattern: {:?}. Buggy code ?", pat);
+                }
+            }
         }
+        cases.push(quote!(#name = #index));
+        let body = &arm.body;
+        match_body.push(quote!(Case::#name => { #body }))
     }
-
-    let mut cases = vec![];
-    let mut table = [0u8; 256];
+    let en = quote!(enum Case {
+        #(#cases),*
+    });
 
-    let mut tts = tts.peekable();
-    let mut case_id: u8 = 1;
-    let mut binding = None;
-    while tts.len() > 0 {
-        cases.push(parse_case(&mut tts, &mut table, &mut binding, case_id));
+    let mut table_content = Vec::new();
+    for entry in table.iter() {
+        let name: syn::Path = syn::parse_str(&format!("Case::Case{}", entry)).unwrap();
+        table_content.push(name);
+    }
+    let table = quote!(static __CASES: [Case; 256] = [#(#table_content),*];);
 
-        // Allow an optional comma between cases.
-        match tts.peek() {
-            Some(&syn::TokenTree::Token(syn::Token::Comma)) => {
-                tts.next();
-            },
-            _ => {},
-        }
+    let expr = if let Some(binding) = wildcard {
+        quote!({ #en #table let #binding = #expr; match __CASES[#binding as usize] { #(#match_body),* }})
+    } else {
+        quote!({ #en #table match __CASES[#expr as usize] { #(#match_body),* }})
+    };
 
-        case_id += 1;
-    }
-
-    (left_hand_side, table, cases, binding)
+    syn::parse2(expr.into()).unwrap()
 }
 
-#[derive(Debug)]
-struct Case(Vec<syn::TokenTree>);
-
-/// Parses a single pattern => expression, and returns the case, filling in the
-/// table with the case id for every byte that matched.
-///
-/// The `binding` parameter is the identifier that is used by the wildcard
-/// pattern.
-fn parse_case(tts: &mut iter::Peekable<vec::IntoIter<syn::TokenTree>>,
-              table: &mut [u8; 256],
-              binding: &mut Option<syn::Ident>,
-              case_id: u8)
-              -> Case {
-    // The last byte checked, as part of this pattern, to properly detect
-    // ranges.
-    let mut last_byte: Option<u8> = None;
-
-    // Loop through the pattern filling with bytes the table.
-    loop {
-        match tts.next() {
-            Some(syn::TokenTree::Token(syn::Token::Literal(syn::Lit::Byte(byte)))) => {
-                table[byte as usize] = case_id;
-                last_byte = Some(byte);
-            }
-            Some(syn::TokenTree::Token(syn::Token::BinOp(syn::BinOpToken::Or))) => {
-                last_byte = None; // This pattern is over.
-            },
-            Some(syn::TokenTree::Token(syn::Token::DotDotDot)) => {
-                assert!(last_byte.is_some(), "Expected closed range!");
-                match tts.next() {
-                    Some(syn::TokenTree::Token(syn::Token::Literal(syn::Lit::Byte(byte)))) => {
-                        for b in last_byte.take().unwrap()..byte {
-                            if table[b as usize] == 0 {
-                                table[b as usize] = case_id;
-                            }
-                        }
-                        if table[byte as usize] == 0 {
-                            table[byte as usize] = case_id;
-                        }
-                    }
-                    other => panic!("Expected closed range, got: {:?}", other),
-                }
-            },
-            Some(syn::TokenTree::Token(syn::Token::FatArrow)) => break,
-            Some(syn::TokenTree::Token(syn::Token::Ident(ident))) => {
-                assert_eq!(last_byte, None, "I don't support ranges with identifiers!");
-                assert_eq!(*binding, None);
-                for byte in table.iter_mut() {
-                    if *byte == 0 {
-                        *byte = case_id;
-                    }
-                }
-                *binding = Some(ident)
-            }
-            Some(syn::TokenTree::Token(syn::Token::Underscore)) => {
-                assert_eq!(last_byte, None);
-                for byte in table.iter_mut() {
-                    if *byte == 0 {
-                        *byte = case_id;
-                    }
+impl Fold for MatchByteParser {
+    fn fold_stmt(&mut self, stmt: syn::Stmt) -> syn::Stmt {
+        match stmt {
+            syn::Stmt::Item(syn::Item::Macro(syn::ItemMacro{ ref mac, .. })) => {
+                if mac.path == parse_quote!(match_byte) {
+                    return syn::fold::fold_stmt(self, syn::Stmt::Expr(expand_match_byte(&mac.tts)))
                 }
             },
-            other => panic!("Expected literal byte, got: {:?}", other),
+            _ => {}
         }
+
+        syn::fold::fold_stmt(self, stmt)
     }
 
-    match tts.next() {
-        Some(syn::TokenTree::Delimited(syn::Delimited { delim: syn::DelimToken::Brace, tts })) => {
-            Case(tts)
+    fn fold_expr(&mut self, expr: syn::Expr) -> syn::Expr {
+        match expr {
+            syn::Expr::Macro(syn::ExprMacro{ ref mac, .. }) => {
+                if mac.path == parse_quote!(match_byte) {
+                    return syn::fold::fold_expr(self, expand_match_byte(&mac.tts))
+                }
+            },
+            _ => {}
         }
-        other => panic!("Expected case with braces after fat arrow, got: {:?}", other),
+
+        syn::fold::fold_expr(self, expr)
     }
 }
-
-fn expand_match_bytes_macro(to_be_matched: Vec<syn::TokenTree>,
-                            table: &[u8; 256],
-                            cases: Vec<Case>,
-                            binding: Option<syn::Ident>)
-                            -> String {
-    use std::fmt::Write;
-
-    assert!(!to_be_matched.is_empty());
-    assert!(table.iter().all(|b| *b != 0), "Incomplete pattern? Bogus code!");
-
-    // We build the expression with text since it's easier.
-    let mut expr = "{\n".to_owned();
-    expr.push_str("enum Case {\n");
-    for (i, _) in cases.iter().enumerate() {
-        write!(&mut expr, "Case{} = {},", i + 1, i + 1).unwrap();
-    }
-    expr.push_str("}\n"); // enum Case
-
-    expr.push_str("static __CASES: [Case; 256] = [");
-    for byte in table.iter() {
-        write!(&mut expr, "Case::Case{}, ", *byte).unwrap();
-    }
-    expr.push_str("];\n");
-
-    let mut tokens = Tokens::new();
-    let to_be_matched = syn::Delimited {
-        delim: if binding.is_some() { syn::DelimToken::Brace } else { syn::DelimToken::Paren },
-        tts: to_be_matched
-    };
-    to_be_matched.to_tokens(&mut tokens);
-
-    if let Some(ref binding) = binding {
-        write!(&mut expr, "let {} = {};\n", binding.to_string(), tokens.as_str()).unwrap();
-    }
-
-    write!(&mut expr, "match __CASES[{} as usize] {{", match binding {
-        Some(binding) => binding.to_string(),
-        None => tokens.to_string(),
-    }).unwrap();
-
-    for (i, case) in cases.into_iter().enumerate() {
-        let mut case_tokens = Tokens::new();
-        let case = syn::Delimited {
-            delim: syn::DelimToken::Brace,
-            tts: case.0
-        };
-        case.to_tokens(&mut case_tokens);
-        write!(&mut expr, "Case::Case{} => {},\n", i + 1, case_tokens.as_str()).unwrap();
-    }
-    expr.push_str("}\n"); // match
-
-    expr.push_str("}\n"); // top
-
-    expr
-}
--- a/third_party/rust/cssparser/src/unicode_range.rs
+++ b/third_party/rust/cssparser/src/unicode_range.rs
@@ -1,17 +1,16 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 //! https://drafts.csswg.org/css-syntax/#urange
 
 use {Parser, ToCss, BasicParseError};
 use std::char;
-use std::cmp;
 use std::fmt;
 use tokenizer::Token;
 
 /// One contiguous range of code points.
 ///
 /// Can not be empty. Can represent a single code point when start == end.
 #[derive(PartialEq, Eq, Clone, Hash)]
 pub struct UnicodeRange {
@@ -161,38 +160,15 @@ fn consume_question_marks(text: &mut &[u
 impl fmt::Debug for UnicodeRange {
     fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
         self.to_css(formatter)
     }
 }
 
 impl ToCss for UnicodeRange {
     fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
-        dest.write_str("U+")?;
-
-        // How many bits are 0 at the end of start and also 1 at the end of end.
-        let bits = cmp::min(self.start.trailing_zeros(), (!self.end).trailing_zeros());
-
-        let question_marks = bits / 4;
-
-        // How many lower bits can be represented as question marks
-        let bits = question_marks * 4;
-
-        let truncated_start = self.start >> bits;
-        let truncated_end = self.end >> bits;
-        if truncated_start == truncated_end {
-            // Bits not covered by question marks are the same in start and end,
-            // we can use the question mark syntax.
-            if truncated_start != 0 {
-                write!(dest, "{:X}", truncated_start)?;
-            }
-            for _ in 0..question_marks {
-                dest.write_str("?")?;
-            }
-        } else {
-            write!(dest, "{:X}", self.start)?;
-            if self.end != self.start {
-                write!(dest, "-{:X}", self.end)?;
-            }
+        write!(dest, "U+{:X}", self.start)?;
+        if self.end != self.start {
+            write!(dest, "-{:X}", self.end)?;
         }
         Ok(())
     }
 }
new file mode 100644
--- /dev/null
+++ b/third_party/rust/itoa-0.3.1/.cargo-checksum.json
@@ -0,0 +1,1 @@
+{"files":{".travis.yml":"a2b867b2e28af9bde20a669a6ff0f366ecc5150b89314cd7ec97ed95bb427547","Cargo.toml":"82b9e862ca8c12656987883e7339d992b770b2a8b23a9cd9ceb5ae0083252687","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"e18259ab3aa7f39a194795bdad8039b3c5fd544f6dd922526c9326c44842b76d","README.md":"f2b713cdc7ade373c4a733c09693cecd288201ec76bde725de65b4ff74530284","benches/bench.rs":"3e7075b70a899ab1e926403856afeb04b34a254b234bbca834f6136a703008a3","performance.png":"a6e70647a44084e65cedaaff3633b0624b37e0f0a84457362c1e078fb56c877d","src/lib.rs":"16169ef9fc6c6a6521daff8fefdfc1b54f4ce145763b9733308d6631dad4d14e","tests/test.rs":"9c7629f758e2833757c15617cd8c1ec2a2fb8437865d05b5d20abb07279d35ea"},"package":"eb2f404fbc66fd9aac13e998248505e7ecb2ad8e44ab6388684c5fb11c6c251c"}
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/third_party/rust/itoa-0.3.1/.travis.yml
@@ -0,0 +1,6 @@
+sudo: false
+
+language: rust
+
+rust:
+  - nightly
new file mode 100644
--- /dev/null
+++ b/third_party/rust/itoa-0.3.1/Cargo.toml
@@ -0,0 +1,9 @@
+[package]
+name = "itoa"
+version = "0.3.1"
+authors = ["David Tolnay <dtolnay@gmail.com>"]
+license = "MIT/Apache-2.0"
+description = "Fast functions for printing integer primitives to an io::Write"
+repository = "https://github.com/dtolnay/itoa"
+documentation = "https://github.com/dtolnay/itoa"
+categories = ["value-formatting"]
new file mode 100644
--- /dev/null
+++ b/third_party/rust/itoa-0.3.1/LICENSE-APACHE
@@ -0,0 +1,201 @@
+                              Apache License
+                        Version 2.0, January 2004
+                     http://www.apache.org/licenses/
+
+TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+1. Definitions.
+
+   "License" shall mean the terms and conditions for use, reproduction,
+   and distribution as defined by Sections 1 through 9 of this document.
+
+   "Licensor" shall mean the copyright owner or entity authorized by
+   the copyright owner that is granting the License.
+
+   "Legal Entity" shall mean the union of the acting entity and all
+   other entities that control, are controlled by, or are under common
+   control with that entity. For the purposes of this definition,
+   "control" means (i) the power, direct or indirect, to cause the
+   direction or management of such entity, whether by contract or
+   otherwise, or (ii) ownership of fifty percent (50%) or more of the
+   outstanding shares, or (iii) beneficial ownership of such entity.
+
+   "You" (or "Your") shall mean an individual or Legal Entity
+   exercising permissions granted by this License.
+
+   "Source" form shall mean the preferred form for making modifications,
+   including but not limited to software source code, documentation
+   source, and configuration files.
+
+   "Object" form shall mean any form resulting from mechanical
+   transformation or translation of a Source form, including but
+   not limited to compiled object code, generated documentation,
+   and conversions to other media types.
+
+   "Work" shall mean the work of authorship, whether in Source or
+   Object form, made available under the License, as indicated by a
+   copyright notice that is included in or attached to the work
+   (an example is provided in the Appendix below).
+
+   "Derivative Works" shall mean any work, whether in Source or Object
+   form, that is based on (or derived from) the Work and for which the
+   editorial revisions, annotations, elaborations, or other modifications
+   represent, as a whole, an original work of authorship. For the purposes
+   of this License, Derivative Works shall not include works that remain
+   separable from, or merely link (or bind by name) to the interfaces of,
+   the Work and Derivative Works thereof.
+
+   "Contribution" shall mean any work of authorship, including
+   the original version of the Work and any modifications or additions
+   to that Work or Derivative Works thereof, that is intentionally
+   submitted to Licensor for inclusion in the Work by the copyright owner
+   or by an individual or Legal Entity authorized to submit on behalf of
+   the copyright owner. For the purposes of this definition, "submitted"
+   means any form of electronic, verbal, or written communication sent
+   to the Licensor or its representatives, including but not limited to
+   communication on electronic mailing lists, source code control systems,
+   and issue tracking systems that are managed by, or on behalf of, the
+   Licensor for the purpose of discussing and improving the Work, but
+   excluding communication that is conspicuously marked or otherwise
+   designated in writing by the copyright owner as "Not a Contribution."
+
+   "Contributor" shall mean Licensor and any individual or Legal Entity
+   on behalf of whom a Contribution has been received by Licensor and
+   subsequently incorporated within the Work.
+
+2. Grant of Copyright License. Subject to the terms and conditions of
+   this License, each Contributor hereby grants to You a perpetual,
+   worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+   copyright license to reproduce, prepare Derivative Works of,
+   publicly display, publicly perform, sublicense, and distribute the
+   Work and such Derivative Works in Source or Object form.
+
+3. Grant of Patent License. Subject to the terms and conditions of
+   this License, each Contributor hereby grants to You a perpetual,
+   worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+   (except as stated in this section) patent license to make, have made,
+   use, offer to sell, sell, import, and otherwise transfer the Work,
+   where such license applies only to those patent claims licensable
+   by such Contributor that are necessarily infringed by their
+   Contribution(s) alone or by combination of their Contribution(s)
+   with the Work to which such Contribution(s) was submitted. If You
+   institute patent litigation against any entity (including a
+   cross-claim or counterclaim in a lawsuit) alleging that the Work
+   or a Contribution incorporated within the Work constitutes direct
+   or contributory patent infringement, then any patent licenses
+   granted to You under this License for that Work shall terminate
+   as of the date such litigation is filed.
+
+4. Redistribution. You may reproduce and distribute copies of the
+   Work or Derivative Works thereof in any medium, with or without
+   modifications, and in Source or Object form, provided that You
+   meet the following conditions:
+
+   (a) You must give any other recipients of the Work or
+       Derivative Works a copy of this License; and
+
+   (b) You must cause any modified files to carry prominent notices
+       stating that You changed the files; and
+
+   (c) You must retain, in the Source form of any Derivative Works
+       that You distribute, all copyright, patent, trademark, and
+       attribution notices from the Source form of the Work,
+       excluding those notices that do not pertain to any part of
+       the Derivative Works; and
+
+   (d) If the Work includes a "NOTICE" text file as part of its
+       distribution, then any Derivative Works that You distribute must
+       include a readable copy of the attribution notices contained
+       within such NOTICE file, excluding those notices that do not
+       pertain to any part of the Derivative Works, in at least one
+       of the following places: within a NOTICE text file distributed
+       as part of the Derivative Works; within the Source form or
+       documentation, if provided along with the Derivative Works; or,
+       within a display generated by the Derivative Works, if and
+       wherever such third-party notices normally appear. The contents
+       of the NOTICE file are for informational purposes only and
+       do not modify the License. You may add Your own attribution
+       notices within Derivative Works that You distribute, alongside
+       or as an addendum to the NOTICE text from the Work, provided
+       that such additional attribution notices cannot be construed
+       as modifying the License.
+
+   You may add Your own copyright statement to Your modifications and
+   may provide additional or different license terms and conditions
+   for use, reproduction, or distribution of Your modifications, or
+   for any such Derivative Works as a whole, provided Your use,
+   reproduction, and distribution of the Work otherwise complies with
+   the conditions stated in this License.
+
+5. Submission of Contributions. Unless You explicitly state otherwise,
+   any Contribution intentionally submitted for inclusion in the Work
+   by You to the Licensor shall be under the terms and conditions of
+   this License, without any additional terms or conditions.
+   Notwithstanding the above, nothing herein shall supersede or modify
+   the terms of any separate license agreement you may have executed
+   with Licensor regarding such Contributions.
+
+6. Trademarks. This License does not grant permission to use the trade
+   names, trademarks, service marks, or product names of the Licensor,
+   except as required for reasonable and customary use in describing the
+   origin of the Work and reproducing the content of the NOTICE file.
+
+7. Disclaimer of Warranty. Unless required by applicable law or
+   agreed to in writing, Licensor provides the Work (and each
+   Contributor provides its Contributions) on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+   implied, including, without limitation, any warranties or conditions
+   of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+   PARTICULAR PURPOSE. You are solely responsible for determining the
+   appropriateness of using or redistributing the Work and assume any
+   risks associated with Your exercise of permissions under this License.
+
+8. Limitation of Liability. In no event and under no legal theory,
+   whether in tort (including negligence), contract, or otherwise,
+   unless required by applicable law (such as deliberate and grossly
+   negligent acts) or agreed to in writing, shall any Contributor be
+   liable to You for damages, including any direct, indirect, special,
+   incidental, or consequential damages of any character arising as a
+   result of this License or out of the use or inability to use the
+   Work (including but not limited to damages for loss of goodwill,
+   work stoppage, computer failure or malfunction, or any and all
+   other commercial damages or losses), even if such Contributor
+   has been advised of the possibility of such damages.
+
+9. Accepting Warranty or Additional Liability. While redistributing
+   the Work or Derivative Works thereof, You may choose to offer,
+   and charge a fee for, acceptance of support, warranty, indemnity,
+   or other liability obligations and/or rights consistent with this
+   License. However, in accepting such obligations, You may act only
+   on Your own behalf and on Your sole responsibility, not on behalf
+   of any other Contributor, and only if You agree to indemnify,
+   defend, and hold each Contributor harmless for any liability
+   incurred by, or claims asserted against, such Contributor by reason
+   of your accepting any such warranty or additional liability.
+
+END OF TERMS AND CONDITIONS
+
+APPENDIX: How to apply the Apache License to your work.
+
+   To apply the Apache License to your work, attach the following
+   boilerplate notice, with the fields enclosed by brackets "[]"
+   replaced with your own identifying information. (Don't include
+   the brackets!)  The text should be enclosed in the appropriate
+   comment syntax for the file format. We also recommend that a
+   file or class name and description of purpose be included on the
+   same "printed page" as the copyright notice for easier
+   identification within third-party archives.
+
+Copyright [yyyy] [name of copyright owner]
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+	http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
new file mode 100644
--- /dev/null
+++ b/third_party/rust/itoa-0.3.1/LICENSE-MIT
@@ -0,0 +1,25 @@
+Copyright (c) 2016 Itoa Developers
+
+Permission is hereby granted, free of charge, to any
+person obtaining a copy of this software and associated
+documentation files (the "Software"), to deal in the
+Software without restriction, including without
+limitation the rights to use, copy, modify, merge,
+publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software
+is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice
+shall be included in all copies or substantial portions
+of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
+ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
+TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
+SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
+IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
new file mode 100644
--- /dev/null
+++ b/third_party/rust/itoa-0.3.1/README.md
@@ -0,0 +1,70 @@
+itoa
+====
+
+[![Build Status](https://api.travis-ci.org/dtolnay/itoa.svg?branch=master)](https://travis-ci.org/dtolnay/itoa)
+[![Latest Version](https://img.shields.io/crates/v/itoa.svg)](https://crates.io/crates/itoa)
+
+This crate provides fast functions for printing integer primitives to an
+[`io::Write`](https://doc.rust-lang.org/std/io/trait.Write.html). The
+implementation comes straight from
+[libcore](https://github.com/rust-lang/rust/blob/b8214dc6c6fc20d0a660fb5700dca9ebf51ebe89/src/libcore/fmt/num.rs#L201-L254)
+but avoids the performance penalty of going through
+[`fmt::Formatter`](https://doc.rust-lang.org/std/fmt/struct.Formatter.html).
+
+See also [`dtoa`](https://github.com/dtolnay/dtoa) for printing floating point
+primitives.
+
+## Performance
+
+![performance](https://raw.githubusercontent.com/dtolnay/itoa/master/performance.png)
+
+## Functions
+
+```rust
+extern crate itoa;
+
+// write to a vector or other io::Write
+let mut buf = Vec::new();
+itoa::write(&mut buf, 128u64)?;
+println!("{:?}", buf);
+
+// write to a stack buffer
+let mut bytes = [b'\0'; 20];
+let n = itoa::write(&mut bytes[..], 128u64)?;
+println!("{:?}", &bytes[..n]);
+```
+
+The function signature is:
+
+```rust
+fn write<W: io::Write, V: itoa::Integer>(writer: W, value: V) -> io::Result<usize>
+```
+
+where `itoa::Integer` is implemented for `i8`, `u8`, `i16`, `u16`, `i32`, `u32`,
+`i64`, `u64`, `isize` and `usize`. The return value gives the number of bytes
+written.
+
+## Dependency
+
+Itoa is available on [crates.io](https://crates.io/crates/itoa). Use the
+following in `Cargo.toml`:
+
+```toml
+[dependencies]
+itoa = "0.3"
+```
+
+## License
+
+Licensed under either of
+
+ * Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
+ * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
+
+at your option.
+
+### Contribution
+
+Unless you explicitly state otherwise, any contribution intentionally submitted
+for inclusion in itoa by you, as defined in the Apache-2.0 license, shall be
+dual licensed as above, without any additional terms or conditions.
new file mode 100644
--- /dev/null
+++ b/third_party/rust/itoa-0.3.1/benches/bench.rs
@@ -0,0 +1,52 @@
+#![feature(test)]
+#![allow(non_snake_case)]
+
+extern crate itoa;
+extern crate test;
+
+macro_rules! benches {
+    ($($name:ident($value:expr),)*) => {
+        mod bench_itoa {
+            use test::{Bencher, black_box};
+            $(
+                #[bench]
+                fn $name(b: &mut Bencher) {
+                    use itoa;
+
+                    let mut buf = Vec::with_capacity(20);
+
+                    b.iter(|| {
+                        buf.clear();
+                        itoa::write(&mut buf, black_box($value)).unwrap()
+                    });
+                }
+            )*
+        }
+
+        mod bench_fmt {
+            use test::{Bencher, black_box};
+            $(
+                #[bench]
+                fn $name(b: &mut Bencher) {
+                    use std::io::Write;
+
+                    let mut buf = Vec::with_capacity(20);
+
+                    b.iter(|| {
+                        buf.clear();
+                        write!(&mut buf, "{}", black_box($value)).unwrap()
+                    });
+                }
+            )*
+        }
+    }
+}
+
+benches!(
+    bench_0u64(0u64),
+    bench_HALFu64(<u32>::max_value() as u64),
+    bench_MAXu64(<u64>::max_value()),
+
+    bench_0i16(0i16),
+    bench_MINi16(<i16>::min_value()),
+);
rename from third_party/rust/itoa/performance.png
rename to third_party/rust/itoa-0.3.1/performance.png
new file mode 100644
--- /dev/null
+++ b/third_party/rust/itoa-0.3.1/src/lib.rs
@@ -0,0 +1,101 @@
+// Copyright 2016 Itoa Developers
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use std::{io, mem, ptr, slice};
+
+#[inline]
+pub fn write<W: io::Write, V: Integer>(wr: W, value: V) -> io::Result<usize> {
+    value.write(wr)
+}
+
+pub trait Integer {
+    fn write<W: io::Write>(self, W) -> io::Result<usize>;
+}
+
+const DEC_DIGITS_LUT: &'static[u8] =
+    b"0001020304050607080910111213141516171819\
+      2021222324252627282930313233343536373839\
+      4041424344454647484950515253545556575859\
+      6061626364656667686970717273747576777879\
+      8081828384858687888990919293949596979899";
+
+// Adaptation of the original implementation at
+// https://github.com/rust-lang/rust/blob/b8214dc6c6fc20d0a660fb5700dca9ebf51ebe89/src/libcore/fmt/num.rs#L188-L266
+macro_rules! impl_Integer {
+    ($($t:ident),* as $conv_fn:ident) => ($(
+    impl Integer for $t {
+        #[allow(unused_comparisons)]
+        fn write<W: io::Write>(self, mut wr: W) -> io::Result<usize> {
+            let is_nonnegative = self >= 0;
+            let mut n = if is_nonnegative {
+                self as $conv_fn
+            } else {
+                try!(wr.write_all(b"-"));
+                // convert the negative num to positive by summing 1 to it's 2 complement
+                (!(self as $conv_fn)).wrapping_add(1)
+            };
+            let mut buf: [u8; 20] = unsafe { mem::uninitialized() };
+            let mut curr = buf.len() as isize;
+            let buf_ptr = buf.as_mut_ptr();
+            let lut_ptr = DEC_DIGITS_LUT.as_ptr();
+
+            unsafe {
+                // eagerly decode 4 characters at a time
+                if <$t>::max_value() as u64 >= 10000 {
+                    while n >= 10000 {
+                        let rem = (n % 10000) as isize;
+                        n /= 10000;
+
+                        let d1 = (rem / 100) << 1;
+                        let d2 = (rem % 100) << 1;
+                        curr -= 4;
+                        ptr::copy_nonoverlapping(lut_ptr.offset(d1), buf_ptr.offset(curr), 2);
+                        ptr::copy_nonoverlapping(lut_ptr.offset(d2), buf_ptr.offset(curr + 2), 2);
+                    }
+                }
+
+                // if we reach here numbers are <= 9999, so at most 4 chars long
+                let mut n = n as isize; // possibly reduce 64bit math
+
+                // decode 2 more chars, if > 2 chars
+                if n >= 100 {
+                    let d1 = (n % 100) << 1;
+                    n /= 100;
+                    curr -= 2;
+                    ptr::copy_nonoverlapping(lut_ptr.offset(d1), buf_ptr.offset(curr), 2);
+                }
+
+                // decode last 1 or 2 chars
+                if n < 10 {
+                    curr -= 1;
+                    *buf_ptr.offset(curr) = (n as u8) + 48;
+                } else {
+                    let d1 = n << 1;
+                    curr -= 2;
+                    ptr::copy_nonoverlapping(lut_ptr.offset(d1), buf_ptr.offset(curr), 2);
+                }
+            }
+
+            let mut len = buf.len() - curr as usize;
+            try!(wr.write_all(unsafe { slice::from_raw_parts(buf_ptr.offset(curr), len) }));
+            if !is_nonnegative {
+                len += 1;
+            }
+            Ok(len)
+        }
+    })*);
+}
+
+impl_Integer!(i8, u8, i16, u16, i32, u32 as u32);
+impl_Integer!(i64, u64 as u64);
+#[cfg(target_pointer_width = "16")]
+impl_Integer!(isize, usize as u16);
+#[cfg(target_pointer_width = "32")]
+impl_Integer!(isize, usize as u32);
+#[cfg(target_pointer_width = "64")]
+impl_Integer!(isize, usize as u64);
new file mode 100644
--- /dev/null
+++ b/third_party/rust/itoa-0.3.1/tests/test.rs
@@ -0,0 +1,25 @@
+#![allow(non_snake_case)]
+
+extern crate itoa;
+
+macro_rules! test {
+    ($($name:ident($value:expr, $expected:expr),)*) => {
+        $(
+            #[test]
+            fn $name() {
+                let mut buf = [b'\0'; 20];
+                let len = itoa::write(&mut buf[..], $value).unwrap();
+                assert_eq!(&buf[0..len], $expected.as_bytes());
+            }
+        )*
+    }
+}
+
+test!(
+    test_0u64(0u64, "0"),
+    test_HALFu64(<u32>::max_value() as u64, "4294967295"),
+    test_MAXu64(<u64>::max_value(), "18446744073709551615"),
+
+    test_0i16(0i16, "0"),
+    test_MINi16(<i16>::min_value(), "-32768"),
+);
--- a/third_party/rust/itoa/.cargo-checksum.json
+++ b/third_party/rust/itoa/.cargo-checksum.json
@@ -1,1 +1,1 @@
-{"files":{".travis.yml":"a2b867b2e28af9bde20a669a6ff0f366ecc5150b89314cd7ec97ed95bb427547","Cargo.toml":"82b9e862ca8c12656987883e7339d992b770b2a8b23a9cd9ceb5ae0083252687","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"e18259ab3aa7f39a194795bdad8039b3c5fd544f6dd922526c9326c44842b76d","README.md":"f2b713cdc7ade373c4a733c09693cecd288201ec76bde725de65b4ff74530284","benches/bench.rs":"3e7075b70a899ab1e926403856afeb04b34a254b234bbca834f6136a703008a3","performance.png":"a6e70647a44084e65cedaaff3633b0624b37e0f0a84457362c1e078fb56c877d","src/lib.rs":"16169ef9fc6c6a6521daff8fefdfc1b54f4ce145763b9733308d6631dad4d14e","tests/test.rs":"9c7629f758e2833757c15617cd8c1ec2a2fb8437865d05b5d20abb07279d35ea"},"package":"eb2f404fbc66fd9aac13e998248505e7ecb2ad8e44ab6388684c5fb11c6c251c"}
\ No newline at end of file
+{"files":{".travis.yml":"9d502b83053fa50b10c712dbb1c7356accf1cc2734f50ae5d75f7c8fdd7a6da5","Cargo.toml":"7a7c831ec60febe8ffdf8d56c436a2dfeb486c870d2f148a8849a2d06122b635","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"e18259ab3aa7f39a194795bdad8039b3c5fd544f6dd922526c9326c44842b76d","README.md":"57b4b49c5ceea9cf94e3856537c01cc1a7e3d071b04d5f5d45155259bfcb83b3","benches/bench.rs":"4cce63083f2e362db65dfad767b777cc39e6555fe635b8551aa485c63080773e","src/lib.rs":"503dc2b0c33f1477f1b6744d103ead1456e91cd1147689a0d0c9e6569225feb4","src/udiv128.rs":"efafb66d62b8229478a448ffb3e11a8ad57f5b336aadd59d5b3bf945cfffba96","tests/test.rs":"55a4b5f21b0f1322d7a42d837ca9f2766eb25416cfcdd9514220fb3bebc0f586"},"package":"c069bbec61e1ca5a596166e55dfe4773ff745c3d16b700013bcaff9a6df2c682"}
\ No newline at end of file
--- a/third_party/rust/itoa/.travis.yml
+++ b/third_party/rust/itoa/.travis.yml
@@ -1,6 +1,19 @@
 sudo: false
-
 language: rust
 
-rust:
-  - nightly
+matrix:
+  include:
+    - rust: stable
+    - rust: beta
+    - rust: nightly
+      env:
+      - FEATURES="i128"
+      - BUILD_BENCH="true"
+
+script:
+  - cargo build --verbose --features "$FEATURES"
+  - cargo test --verbose --features "$FEATURES"
+  - cargo build --verbose --no-default-features --features "$FEATURES"
+  - cargo test --verbose --no-default-features --features "$FEATURES"
+  - if [ "$BUILD_BENCH" == "true" ]; then cargo bench --verbose --no-run --features "$FEATURES"; fi
+
--- a/third_party/rust/itoa/Cargo.toml
+++ b/third_party/rust/itoa/Cargo.toml
@@ -1,9 +1,28 @@
+# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
+#
+# When uploading crates to the registry Cargo will automatically
+# "normalize" Cargo.toml files for maximal compatibility
+# with all versions of Cargo and also rewrite `path` dependencies
+# to registry (e.g. crates.io) dependencies
+#
+# If you believe there's an error in this file please file an
+# issue against the rust-lang/cargo repository. If you're
+# editing this file be aware that the upstream Cargo.toml
+# will likely look very different (and much more reasonable)
+
 [package]
 name = "itoa"
-version = "0.3.1"
+version = "0.4.1"
 authors = ["David Tolnay <dtolnay@gmail.com>"]
-license = "MIT/Apache-2.0"
+exclude = ["performance.png"]
 description = "Fast functions for printing integer primitives to an io::Write"
+documentation = "https://github.com/dtolnay/itoa"
+readme = "README.md"
+categories = ["value-formatting"]
+license = "MIT/Apache-2.0"
 repository = "https://github.com/dtolnay/itoa"
-documentation = "https://github.com/dtolnay/itoa"
-categories = ["value-formatting"]
+
+[features]
+default = ["std"]
+i128 = []
+std = []
--- a/third_party/rust/itoa/README.md
+++ b/third_party/rust/itoa/README.md
@@ -1,25 +1,26 @@
 itoa
 ====
 
 [![Build Status](https://api.travis-ci.org/dtolnay/itoa.svg?branch=master)](https://travis-ci.org/dtolnay/itoa)
 [![Latest Version](https://img.shields.io/crates/v/itoa.svg)](https://crates.io/crates/itoa)
 
 This crate provides fast functions for printing integer primitives to an
-[`io::Write`](https://doc.rust-lang.org/std/io/trait.Write.html). The
+[`io::Write`](https://doc.rust-lang.org/std/io/trait.Write.html) or a
+[`fmt::Write`](https://doc.rust-lang.org/core/fmt/trait.Write.html). The
 implementation comes straight from
 [libcore](https://github.com/rust-lang/rust/blob/b8214dc6c6fc20d0a660fb5700dca9ebf51ebe89/src/libcore/fmt/num.rs#L201-L254)
 but avoids the performance penalty of going through
 [`fmt::Formatter`](https://doc.rust-lang.org/std/fmt/struct.Formatter.html).
 
 See also [`dtoa`](https://github.com/dtolnay/dtoa) for printing floating point
 primitives.
 
-## Performance
+## Performance (lower is better)
 
 ![performance](https://raw.githubusercontent.com/dtolnay/itoa/master/performance.png)
 
 ## Functions
 
 ```rust
 extern crate itoa;
 
@@ -27,36 +28,47 @@ extern crate itoa;
 let mut buf = Vec::new();
 itoa::write(&mut buf, 128u64)?;
 println!("{:?}", buf);
 
 // write to a stack buffer
 let mut bytes = [b'\0'; 20];
 let n = itoa::write(&mut bytes[..], 128u64)?;
 println!("{:?}", &bytes[..n]);
+
+// write to a String
+let mut s = String::new();
+itoa::fmt(&mut s, 128u64)?;
+println!("{}", s);
 ```
 
-The function signature is:
+The function signatures are:
 
 ```rust
-fn write<W: io::Write, V: itoa::Integer>(writer: W, value: V) -> io::Result<usize>
+fn write<W: io::Write, V: itoa::Integer>(writer: W, value: V) -> io::Result<usize>;
+
+fn fmt<W: fmt::Write, V: itoa::Integer>(writer: W, value: V) -> fmt::Result;
 ```
 
 where `itoa::Integer` is implemented for `i8`, `u8`, `i16`, `u16`, `i32`, `u32`,
-`i64`, `u64`, `isize` and `usize`. The return value gives the number of bytes
-written.
+`i64`, `u64`, `i128`, `u128`, `isize` and `usize`. 128-bit integer support is
+only available with the nightly compiler when the `i128` feature is enabled for
+this crate. The return value gives the number of bytes written.
+
+The `write` function is only available when the `std` feature is enabled
+(default is enabled).
 
 ## Dependency
 
 Itoa is available on [crates.io](https://crates.io/crates/itoa). Use the
 following in `Cargo.toml`:
 
 ```toml
 [dependencies]
-itoa = "0.3"
+itoa = "0.4"
 ```
 
 ## License
 
 Licensed under either of
 
  * Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
  * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
--- a/third_party/rust/itoa/benches/bench.rs
+++ b/third_party/rust/itoa/benches/bench.rs
@@ -1,52 +1,86 @@
+#![cfg_attr(feature = "i128", feature(i128_type, i128))]
+
+#![cfg_attr(feature = "cargo-clippy", allow(cast_lossless))]
+
 #![feature(test)]
 #![allow(non_snake_case)]
 
 extern crate itoa;
 extern crate test;
 
 macro_rules! benches {
-    ($($name:ident($value:expr),)*) => {
-        mod bench_itoa {
+    (
+        $(
+            $(#[$attr:meta])*
+            $name:ident($value:expr)
+        ),*
+    ) => {
+        mod bench_itoa_write {
             use test::{Bencher, black_box};
             $(
+                $(#[$attr])*
                 #[bench]
                 fn $name(b: &mut Bencher) {
                     use itoa;
 
-                    let mut buf = Vec::with_capacity(20);
+                    let mut buf = Vec::with_capacity(40);
 
                     b.iter(|| {
                         buf.clear();
                         itoa::write(&mut buf, black_box($value)).unwrap()
                     });
                 }
             )*
         }
 
-        mod bench_fmt {
+        mod bench_itoa_fmt {
             use test::{Bencher, black_box};
             $(
+                $(#[$attr])*
+                #[bench]
+                fn $name(b: &mut Bencher) {
+                    use itoa;
+
+                    let mut buf = String::with_capacity(40);
+
+                    b.iter(|| {
+                        buf.clear();
+                        itoa::fmt(&mut buf, black_box($value)).unwrap()
+                    });
+                }
+            )*
+        }
+
+        mod bench_std_fmt {
+            use test::{Bencher, black_box};
+            $(
+                $(#[$attr])*
                 #[bench]
                 fn $name(b: &mut Bencher) {
                     use std::io::Write;
 
-                    let mut buf = Vec::with_capacity(20);
+                    let mut buf = Vec::with_capacity(40);
 
                     b.iter(|| {
                         buf.clear();
                         write!(&mut buf, "{}", black_box($value)).unwrap()
                     });
                 }
             )*
         }
     }
 }
 
-benches!(
-    bench_0u64(0u64),
-    bench_HALFu64(<u32>::max_value() as u64),
-    bench_MAXu64(<u64>::max_value()),
+benches!{
+    bench_u64_0(0u64),
+    bench_u64_half(<u32>::max_value() as u64),
+    bench_u64_max(<u64>::max_value()),
 
-    bench_0i16(0i16),
-    bench_MINi16(<i16>::min_value()),
-);
+    bench_i16_0(0i16),
+    bench_i16_min(<i16>::min_value()),
+
+    #[cfg(feature = "i128")]
+    bench_u128_0(0u128),
+    #[cfg(feature = "i128")]
+    bench_u128_max(<u128>::max_value())
+}
--- a/third_party/rust/itoa/src/lib.rs
+++ b/third_party/rust/itoa/src/lib.rs
@@ -1,101 +1,261 @@
 // Copyright 2016 Itoa Developers
 //
 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-use std::{io, mem, ptr, slice};
+#![doc(html_root_url = "https://docs.rs/itoa/0.4.1")]
+
+#![cfg_attr(not(feature = "std"), no_std)]
+
+#![cfg_attr(feature = "i128", feature(i128_type, i128))]
+
+#![cfg_attr(feature = "cargo-clippy", allow(cast_lossless, unreadable_literal))]
 
+#[cfg(feature = "i128")]
+mod udiv128;
+
+#[cfg(feature = "std")]
+use std::{fmt, io, mem, ptr, slice, str};
+
+#[cfg(not(feature = "std"))]
+use core::{fmt, mem, ptr, slice, str};
+
+/// Write integer to an `io::Write`.
+#[cfg(feature = "std")]
 #[inline]
 pub fn write<W: io::Write, V: Integer>(wr: W, value: V) -> io::Result<usize> {
     value.write(wr)
 }
 
-pub trait Integer {
+/// Write integer to an `fmt::Write`.
+#[inline]
+pub fn fmt<W: fmt::Write, V: Integer>(wr: W, value: V) -> fmt::Result {
+    value.fmt(wr)
+}
+
+// Seal to prevent downstream implementations of the Integer trait.
+mod private {
+    pub trait Sealed {}
+}
+
+/// An integer that can be formatted by `itoa::write` and `itoa::fmt`.
+///
+/// This trait is sealed and cannot be implemented for types outside of itoa.
+pub trait Integer: private::Sealed {
+    // Not public API.
+    #[doc(hidden)]
+    #[cfg(feature = "std")]
     fn write<W: io::Write>(self, W) -> io::Result<usize>;
+
+    // Not public API.
+    #[doc(hidden)]
+    fn fmt<W: fmt::Write>(self, W) -> fmt::Result;
+}
+
+trait IntegerPrivate<B> {
+    fn write_to(self, buf: &mut B) -> &[u8];
 }
 
 const DEC_DIGITS_LUT: &'static[u8] =
     b"0001020304050607080910111213141516171819\
       2021222324252627282930313233343536373839\
       4041424344454647484950515253545556575859\
       6061626364656667686970717273747576777879\
       8081828384858687888990919293949596979899";
 
 // Adaptation of the original implementation at
 // https://github.com/rust-lang/rust/blob/b8214dc6c6fc20d0a660fb5700dca9ebf51ebe89/src/libcore/fmt/num.rs#L188-L266
+macro_rules! impl_IntegerCommon {
+    ($max_len:expr, $t:ident) => {
+        impl Integer for $t {
+            #[cfg(feature = "std")]
+            #[inline]
+            fn write<W: io::Write>(self, mut wr: W) -> io::Result<usize> {
+                let mut buf: [u8; $max_len] = unsafe { mem::uninitialized() };
+                let bytes = self.write_to(&mut buf);
+                try!(wr.write_all(bytes));
+                Ok(bytes.len())
+            }
+
+            #[inline]
+            fn fmt<W: fmt::Write>(self, mut wr: W) -> fmt::Result {
+                let mut buf: [u8; $max_len] = unsafe { mem::uninitialized() };
+                let bytes = self.write_to(&mut buf);
+                wr.write_str(unsafe { str::from_utf8_unchecked(bytes) })
+            }
+        }
+
+        impl private::Sealed for $t {}
+    };
+}
+
 macro_rules! impl_Integer {
-    ($($t:ident),* as $conv_fn:ident) => ($(
-    impl Integer for $t {
-        #[allow(unused_comparisons)]
-        fn write<W: io::Write>(self, mut wr: W) -> io::Result<usize> {
-            let is_nonnegative = self >= 0;
-            let mut n = if is_nonnegative {
-                self as $conv_fn
-            } else {
-                try!(wr.write_all(b"-"));
-                // convert the negative num to positive by summing 1 to it's 2 complement
-                (!(self as $conv_fn)).wrapping_add(1)
-            };
-            let mut buf: [u8; 20] = unsafe { mem::uninitialized() };
-            let mut curr = buf.len() as isize;
-            let buf_ptr = buf.as_mut_ptr();
-            let lut_ptr = DEC_DIGITS_LUT.as_ptr();
+    ($($max_len:expr => $t:ident),* as $conv_fn:ident) => {$(
+        impl_IntegerCommon!($max_len, $t);
+
+        impl IntegerPrivate<[u8; $max_len]> for $t {
+            #[allow(unused_comparisons)]
+            #[inline]
+            fn write_to(self, buf: &mut [u8; $max_len]) -> &[u8] {
+                let is_nonnegative = self >= 0;
+                let mut n = if is_nonnegative {
+                    self as $conv_fn
+                } else {
+                    // convert the negative num to positive by summing 1 to it's 2 complement
+                    (!(self as $conv_fn)).wrapping_add(1)
+                };
+                let mut curr = buf.len() as isize;
+                let buf_ptr = buf.as_mut_ptr();
+                let lut_ptr = DEC_DIGITS_LUT.as_ptr();
+
+                unsafe {
+                    // need at least 16 bits for the 4-characters-at-a-time to work.
+                    if mem::size_of::<$t>() >= 2 {
+                        // eagerly decode 4 characters at a time
+                        while n >= 10000 {
+                            let rem = (n % 10000) as isize;
+                            n /= 10000;
 
-            unsafe {
-                // eagerly decode 4 characters at a time
-                if <$t>::max_value() as u64 >= 10000 {
-                    while n >= 10000 {
-                        let rem = (n % 10000) as isize;
-                        n /= 10000;
+                            let d1 = (rem / 100) << 1;
+                            let d2 = (rem % 100) << 1;
+                            curr -= 4;
+                            ptr::copy_nonoverlapping(lut_ptr.offset(d1), buf_ptr.offset(curr), 2);
+                            ptr::copy_nonoverlapping(lut_ptr.offset(d2), buf_ptr.offset(curr + 2), 2);
+                        }
+                    }
+
+                    // if we reach here numbers are <= 9999, so at most 4 chars long
+                    let mut n = n as isize; // possibly reduce 64bit math
 
-                        let d1 = (rem / 100) << 1;
-                        let d2 = (rem % 100) << 1;
-                        curr -= 4;
+                    // decode 2 more chars, if > 2 chars
+                    if n >= 100 {
+                        let d1 = (n % 100) << 1;
+                        n /= 100;
+                        curr -= 2;
                         ptr::copy_nonoverlapping(lut_ptr.offset(d1), buf_ptr.offset(curr), 2);
-                        ptr::copy_nonoverlapping(lut_ptr.offset(d2), buf_ptr.offset(curr + 2), 2);
+                    }
+
+                    // decode last 1 or 2 chars
+                    if n < 10 {
+                        curr -= 1;
+                        *buf_ptr.offset(curr) = (n as u8) + b'0';
+                    } else {
+                        let d1 = n << 1;
+                        curr -= 2;
+                        ptr::copy_nonoverlapping(lut_ptr.offset(d1), buf_ptr.offset(curr), 2);
+                    }
+
+                    if !is_nonnegative {
+                        curr -= 1;
+                        *buf_ptr.offset(curr) = b'-';
                     }
                 }
 
-                // if we reach here numbers are <= 9999, so at most 4 chars long
-                let mut n = n as isize; // possibly reduce 64bit math
+                let len = buf.len() - curr as usize;
+                unsafe { slice::from_raw_parts(buf_ptr.offset(curr), len) }
+            }
+        }
+    )*};
+}
+
+const I8_MAX_LEN: usize = 4;
+const U8_MAX_LEN: usize = 3;
+const I16_MAX_LEN: usize = 6;
+const U16_MAX_LEN: usize = 5;
+const I32_MAX_LEN: usize = 11;
+const U32_MAX_LEN: usize = 10;
+const I64_MAX_LEN: usize = 20;
+const U64_MAX_LEN: usize = 20;
+
+impl_Integer!(
+    I8_MAX_LEN => i8,
+    U8_MAX_LEN => u8,
+    I16_MAX_LEN => i16,
+    U16_MAX_LEN => u16,
+    I32_MAX_LEN => i32,
+    U32_MAX_LEN => u32 
+    as u32);
+
+impl_Integer!(I64_MAX_LEN => i64, U64_MAX_LEN => u64 as u64);
+
+#[cfg(target_pointer_width = "16")]
+impl_Integer!(I16_MAX_LEN => isize, U16_MAX_LEN => usize as u16);
+
+#[cfg(target_pointer_width = "32")]
+impl_Integer!(I32_MAX_LEN => isize, U32_MAX_LEN => usize as u32);
+
+#[cfg(target_pointer_width = "64")]
+impl_Integer!(I64_MAX_LEN => isize, U64_MAX_LEN => usize as u64);
+
+#[cfg(all(feature = "i128"))]
+macro_rules! impl_Integer128 {
+    ($($max_len:expr => $t:ident),*) => {$(
+        impl_IntegerCommon!($max_len, $t);