Merge inbound to mozilla-central. a=merge
authorMargareta Eliza Balazs <ebalazs@mozilla.com>
Thu, 18 Oct 2018 13:20:43 +0300
changeset 497640 8f709fd4aa46
parent 497639 e7d5d82e766d (current diff)
parent 497637 3eb04f5363eb (diff)
child 497641 8efe26839243
child 497657 704e640a83c2
child 497664 9e81f9458926
push id9996
push userarchaeopteryx@coole-files.de
push dateThu, 18 Oct 2018 18:37:15 +0000
treeherdermozilla-beta@8efe26839243 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone64.0a1
first release with
nightly linux32
8f709fd4aa46 / 64.0a1 / 20181018123730 / files
nightly linux64
8f709fd4aa46 / 64.0a1 / 20181018123730 / files
nightly mac
8f709fd4aa46 / 64.0a1 / 20181018123730 / files
nightly win32
8f709fd4aa46 / 64.0a1 / 20181018123730 / files
nightly win64
8f709fd4aa46 / 64.0a1 / 20181018123730 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge inbound to mozilla-central. a=merge
build/moz.configure/toolchain.configure
testing/web-platform/meta/infrastructure/reftest/reftest_or_0.html.ini
testing/web-platform/meta/svg/shapes/ellipse-04.svg.ini
testing/web-platform/meta/wasm/idlharness.any.js.ini
testing/web-platform/tests/wasm/idlharness.any.js
--- a/browser/app/firefox.exe.manifest
+++ b/browser/app/firefox.exe.manifest
@@ -14,16 +14,26 @@
                         name="Microsoft.Windows.Common-Controls"
                         version="6.0.0.0"
                         processorArchitecture="*"
                         publicKeyToken="6595b64144ccf1df"
                         language="*"
                 />
         </dependentAssembly>
 </dependency>
+<dependency>
+        <dependentAssembly>
+                <assemblyIdentity
+                        type="win32"
+                        name="mozglue"
+                        version="1.0.0.0"
+                        language="*"
+                />
+        </dependentAssembly>
+</dependency>
 <ms_asmv3:trustInfo xmlns:ms_asmv3="urn:schemas-microsoft-com:asm.v3">
   <ms_asmv3:security>
     <ms_asmv3:requestedPrivileges>
       <ms_asmv3:requestedExecutionLevel level="asInvoker" uiAccess="false" />
     </ms_asmv3:requestedPrivileges>
   </ms_asmv3:security>
 </ms_asmv3:trustInfo>
   <ms_asmv3:application xmlns:ms_asmv3="urn:schemas-microsoft-com:asm.v3">
--- a/browser/config/mozconfigs/win32/mingwclang
+++ b/browser/config/mozconfigs/win32/mingwclang
@@ -25,16 +25,17 @@ unset MAKECAB
 #    CARGO
 . "$topsrcdir/browser/config/mozconfigs/common"
 
 # MinGW Stuff
 ac_add_options --target=i686-w64-mingw32
 ac_add_options --with-toolchain-prefix=i686-w64-mingw32-
 
 ac_add_options --disable-warnings-as-errors
+MOZ_COPY_PDBS=1
 
 # Temporary config settings until we get these working on mingw
 ac_add_options --disable-accessibility # https://sourceforge.net/p/mingw-w64/bugs/648/
 
 # For now, we'll disable the sandbox, until we get get Bug 1461421 figured out
 ac_add_options --disable-sandbox
 
 # These aren't supported on mingw at this time
--- a/browser/config/mozconfigs/win64/mingwclang
+++ b/browser/config/mozconfigs/win64/mingwclang
@@ -25,16 +25,17 @@ unset MAKECAB
 #    CARGO
 . "$topsrcdir/browser/config/mozconfigs/common"
 
 # MinGW Stuff
 ac_add_options --target=x86_64-w64-mingw32
 ac_add_options --with-toolchain-prefix=x86_64-w64-mingw32-
 
 ac_add_options --disable-warnings-as-errors
+MOZ_COPY_PDBS=1
 
 # Temporary config settings until we get these working on mingw
 ac_add_options --disable-accessibility # https://sourceforge.net/p/mingw-w64/bugs/648/
 
 # For now, we'll disable the sandbox, until we get get Bug 1461421 figured out
 ac_add_options --disable-sandbox
 
 # These aren't supported on mingw at this time
--- a/browser/modules/Sanitizer.jsm
+++ b/browser/modules/Sanitizer.jsm
@@ -706,16 +706,24 @@ async function sanitizeOnShutdown(progre
 }
 
 async function sanitizeSessionPrincipals() {
   if (Services.prefs.getIntPref(PREF_COOKIE_LIFETIME,
                                 Ci.nsICookieService.ACCEPT_NORMALLY) != Ci.nsICookieService.ACCEPT_SESSION) {
     return;
   }
 
+  // When PREF_COOKIE_LIFETIME is set to ACCEPT_SESSION, any new cookie will be
+  // marked as session only. But we don't touch the existing ones. For this
+  // reason, here we delete any existing cookie, at shutdown.
+  await new Promise(resolve => {
+    Services.clearData.deleteData(Ci.nsIClearDataService.CLEAR_COOKIES,
+                                  resolve);
+  });
+
   let principals = await new Promise(resolve => {
     quotaManagerService.getUsage(request => {
       if (request.resultCode != Cr.NS_OK) {
         // We are probably shutting down. We don't want to propagate the
         // error, rejecting the promise.
         resolve([]);
         return;
       }
--- a/build/moz.configure/toolchain.configure
+++ b/build/moz.configure/toolchain.configure
@@ -1121,21 +1121,23 @@ include('compile-checks.configure')
          try_compile(body='static_assert(sizeof(void *) == 8, "")',
                      check_msg='for 64-bit OS'))
 def check_have_64_bit(have_64_bit, compiler_have_64_bit):
     if have_64_bit != compiler_have_64_bit:
         configure_error('The target compiler does not agree with configure '
                         'about the target bitness.')
 
 
-@depends(c_compiler)
-def default_debug_flags(compiler_info):
+@depends(c_compiler, target)
+def default_debug_flags(compiler_info, target):
     # Debug info is ON by default.
     if compiler_info.type in ('msvc', 'clang-cl'):
         return '-Zi'
+    elif target.kernel == 'WINNT' and compiler_info.type == 'clang':
+        return '-g -gcodeview'
     return '-g'
 
 
 option(env='MOZ_DEBUG_FLAGS',
        nargs=1,
        help='Debug compiler flags')
 
 imply_option('--enable-debug-symbols',
@@ -1987,21 +1989,26 @@ add_old_configure_assignment('LIBFUZZER_
 
 # Shared library building
 # ==============================================================
 
 # XXX: The use of makefile constructs in these variables is awful.
 @depends(target, c_compiler)
 def make_shared_library(target, compiler):
     if target.os == 'WINNT':
-        if compiler.type in ('gcc', 'clang'):
+        if compiler.type == 'gcc':
             return namespace(
                 mkshlib=['$(CXX)', '$(DSO_LDOPTS)', '-o', '$@'],
                 mkcshlib=['$(CC)', '$(DSO_LDOPTS)', '-o', '$@'],
             )
+        elif compiler.type == 'clang':
+            return namespace(
+                mkshlib=['$(CXX)', '$(DSO_LDOPTS)', '-Wl,-pdb,$(LINK_PDBFILE)', '-o', '$@'],
+                mkcshlib=['$(CC)', '$(DSO_LDOPTS)', '-Wl,-pdb,$(LINK_PDBFILE)', '-o', '$@'],
+            )
         else:
             linker = [
                 '$(LINKER)',
                 '-NOLOGO', '-DLL',
                 '-OUT:$@',
                 '-PDB:$(LINK_PDBFILE)',
                 '$(DSO_LDOPTS)'
             ]
--- a/build/win64/mozconfig.asan
+++ b/build/win64/mozconfig.asan
@@ -2,16 +2,17 @@
 
 if [ -d "$topsrcdir/clang" ]; then
     CLANG_LIB_DIR="$(cd $topsrcdir/clang/lib/clang/* && cd lib/windows && pwd)"
 
     export LIB=$LIB:$CLANG_LIB_DIR
     mk_export_correct_style LIB
     export LDFLAGS="clang_rt.asan_dynamic-x86_64.lib clang_rt.asan_dynamic_runtime_thunk-x86_64.lib"
 
+    export MOZ_COPY_PDBS=1
     export LLVM_SYMBOLIZER="$topsrcdir/clang/bin/llvm-symbolizer.exe"
     export MOZ_CLANG_RT_ASAN_LIB_PATH="${CLANG_LIB_DIR}/clang_rt.asan_dynamic-x86_64.dll"
 fi
 
 # Enable ASan specific code and build workarounds
 ac_add_options --enable-address-sanitizer
 
 # Mandatory options required for ASan builds
--- a/config/rules.mk
+++ b/config/rules.mk
@@ -121,16 +121,19 @@ MKSHLIB			= $(MKCSHLIB)
 endif
 
 EMBED_MANIFEST_AT=2
 
 endif # MKSHLIB
 endif # FORCE_SHARED_LIB
 
 ifeq ($(OS_ARCH),WINNT)
+
+LINK_PDBFILE ?= $(basename $(@F)).pdb
+
 ifndef GNU_CC
 
 #
 # Unless we're building SIMPLE_PROGRAMS, all C++ files share a PDB file per
 # directory. For parallel builds, this PDB file is shared and locked by
 # MSPDBSRV.EXE, starting with MSVC8 SP1. If you're using MSVC 7.1 or MSVC8
 # without SP1, don't do parallel builds.
 #
@@ -142,30 +145,35 @@ ifndef GNU_CC
 ifdef SIMPLE_PROGRAMS
 COMPILE_PDB_FLAG ?= -Fd$(basename $(@F)).pdb
 else
 COMPILE_PDB_FLAG ?= -Fdgenerated.pdb -FS
 endif
 COMPILE_CFLAGS += $(COMPILE_PDB_FLAG)
 COMPILE_CXXFLAGS += $(COMPILE_PDB_FLAG)
 
-LINK_PDBFILE ?= $(basename $(@F)).pdb
 ifdef MOZ_DEBUG
 CODFILE=$(basename $(@F)).cod
 endif
 
 endif # !GNU_CC
 endif # WINNT
 
 ifeq (arm-Darwin,$(CPU_ARCH)-$(OS_TARGET))
 ifdef PROGRAM
 MOZ_PROGRAM_LDFLAGS += -Wl,-rpath -Wl,@executable_path/Frameworks
 endif
 endif
 
+ifeq ($(OS_ARCH),WINNT)
+ifeq ($(CC_TYPE),clang)
+MOZ_PROGRAM_LDFLAGS += -Wl,-pdb,$(dir $@)/$(LINK_PDBFILE)
+endif
+endif
+
 ifeq ($(HOST_OS_ARCH),WINNT)
 HOST_PDBFILE=$(basename $(@F)).pdb
 HOST_PDB_FLAG ?= -Fd$(HOST_PDBFILE)
 HOST_CFLAGS += $(HOST_PDB_FLAG)
 HOST_CXXFLAGS += $(HOST_PDB_FLAG)
 HOST_C_LDFLAGS += $(HOST_PDB_FLAG)
 HOST_CXX_LDFLAGS += $(HOST_PDB_FLAG)
 endif
@@ -814,23 +822,23 @@ endif
 endif
 
 ifdef MOZ_AUTOMATION
 ifeq (,$(filter 1,$(MOZ_AUTOMATION_BUILD_SYMBOLS)))
 DUMP_SYMS_TARGETS :=
 endif
 endif
 
-ifdef MOZ_CRASHREPORTER
-$(foreach file,$(DUMP_SYMS_TARGETS),$(eval $(call syms_template,$(file),$(notdir $(file))_syms.track)))
-else ifneq (,$(and $(LLVM_SYMBOLIZER),$(filter WINNT,$(OS_ARCH)),$(MOZ_AUTOMATION)))
+ifdef MOZ_COPY_PDBS
 PDB_FILES = $(addsuffix .pdb,$(basename $(DUMP_SYMS_TARGETS)))
 PDB_DEST ?= $(FINAL_TARGET)
 PDB_TARGET = syms
 INSTALL_TARGETS += PDB
+else ifdef MOZ_CRASHREPORTER
+$(foreach file,$(DUMP_SYMS_TARGETS),$(eval $(call syms_template,$(file),$(notdir $(file))_syms.track)))
 endif
 
 cargo_host_flag := --target=$(RUST_HOST_TARGET)
 cargo_target_flag := --target=$(RUST_TARGET)
 
 # Permit users to pass flags to cargo from their mozconfigs (e.g. --color=always).
 cargo_build_flags = $(CARGOFLAGS)
 ifndef MOZ_DEBUG_RUST
--- a/config/version_win.pl
+++ b/config/version_win.pl
@@ -269,22 +269,24 @@ print RCFILE qq{
 // Use module.ver to explicitly set these values
 
 // Do not edit this file. Changes won't affect the build.
 
 };
 
 my $versionlevel=0;
 my $insideversion=0;
+my $has_manifest=0;
 if (open(RCINCLUDE, "<$rcinclude")) 
 {
 	print RCFILE "// From included resource $rcinclude\n";
 #	my $mstring="";
 	while (<RCINCLUDE>) 
 	{
+		$has_manifest = 1 if /^1 (24|RT_MANIFEST) "$binary.manifest"/;
 		$_ =~ s/\@MOZ_APP_DISPLAYNAME\@/$displayname/g;
 		print RCFILE $_;
 #		my $instr=$_;
 #		chomp($instr);
 #		$mstring .= "$instr\;";
 	}
 	close(RCINCLUDE);
 #	$mstring =~ s/\/\*.*\*\///g;
@@ -326,16 +328,20 @@ if (open(RCINCLUDE, "<$rcinclude"))
 #		}
 #	}
 	
 }
 
 my $fileflags = join(' | ', @fileflags);
 
 print RCFILE qq{
+1 RT_MANIFEST "$binary.manifest"
+} if !$has_manifest && $binary =~ /\.exe$/ && -e "$objdir/$binary.manifest";
+
+print RCFILE qq{
 
 
 /////////////////////////////////////////////////////////////////////////////
 //
 // Version
 //
 
 1 VERSIONINFO
--- a/devtools/client/inspector/inspector.js
+++ b/devtools/client/inspector/inspector.js
@@ -871,17 +871,17 @@ Inspector.prototype = {
       hideTabstripe: true
     });
 
     // defaultTab may also be an empty string or a tab id that doesn't exist anymore
     // (e.g. it was a tab registered by an addon that has been uninstalled).
     let defaultTab = Services.prefs.getCharPref("devtools.inspector.activeSidebar");
 
     if (this.is3PaneModeEnabled && defaultTab === "ruleview") {
-      defaultTab = "computedview";
+      defaultTab = "layoutview";
     }
 
     // Append all side panels
 
     await this.addRuleView({ defaultTab });
 
     // Inject a lazy loaded react tab by exposing a fake React object
     // with a lazy defined Tab thanks to `panel` being a function
--- a/devtools/client/inspector/rules/test/browser.ini
+++ b/devtools/client/inspector/rules/test/browser.ini
@@ -176,16 +176,17 @@ skip-if = (os == "win" && debug) # bug 9
 [browser_rules_grid-highlighter-on-navigate.js]
 [browser_rules_grid-highlighter-on-reload.js]
 [browser_rules_grid-highlighter-restored-after-reload.js]
 [browser_rules_grid-toggle_01.js]
 [browser_rules_grid-toggle_01b.js]
 [browser_rules_grid-toggle_02.js]
 [browser_rules_grid-toggle_03.js]
 [browser_rules_grid-toggle_04.js]
+[browser_rules_grid-toggle_05.js]
 [browser_rules_gridline-names-autocomplete.js]
 [browser_rules_guessIndentation.js]
 [browser_rules_highlight-used-fonts.js]
 [browser_rules_inherited-properties_01.js]
 [browser_rules_inherited-properties_02.js]
 [browser_rules_inherited-properties_03.js]
 [browser_rules_inherited-properties_04.js]
 [browser_rules_inline-source-map.js]
--- a/devtools/client/inspector/rules/test/browser_rules_grid-toggle_01.js
+++ b/devtools/client/inspector/rules/test/browser_rules_grid-toggle_01.js
@@ -24,35 +24,37 @@ add_task(async function() {
   const {inspector, view} = await openRuleView();
   const highlighters = view.highlighters;
 
   await selectNode("#grid", inspector);
   const container = getRuleViewProperty(view, "#grid", "display").valueSpan;
   const gridToggle = container.querySelector(".ruleview-grid");
 
   info("Checking the initial state of the CSS grid toggle in the rule-view.");
-  ok(gridToggle, "Grid highlighter toggle is visible.");
+  ok(!gridToggle.hasAttribute("disabled"), "Grid highlighter toggle is not disabled.");
   ok(!gridToggle.classList.contains("active"),
     "Grid highlighter toggle button is not active.");
   ok(!highlighters.gridHighlighters.size, "No CSS grid highlighter is shown.");
 
   info("Toggling ON the CSS grid highlighter from the rule-view.");
   const onHighlighterShown = highlighters.once("grid-highlighter-shown");
   gridToggle.click();
   await onHighlighterShown;
 
   info("Checking the CSS grid highlighter is created and toggle button is active in " +
     "the rule-view.");
+  ok(!gridToggle.hasAttribute("disabled"), "Grid highlighter toggle is not disabled.");
   ok(gridToggle.classList.contains("active"),
     "Grid highlighter toggle is active.");
   is(highlighters.gridHighlighters.size, 1, "CSS grid highlighter is shown.");
 
   info("Toggling OFF the CSS grid highlighter from the rule-view.");
   const onHighlighterHidden = highlighters.once("grid-highlighter-hidden");
   gridToggle.click();
   await onHighlighterHidden;
 
   info("Checking the CSS grid highlighter is not shown and toggle button is not active " +
     "in the rule-view.");
+  ok(!gridToggle.hasAttribute("disabled"), "Grid highlighter toggle is not disabled.");
   ok(!gridToggle.classList.contains("active"),
     "Grid highlighter toggle button is not active.");
   ok(!highlighters.gridHighlighters.size, "No CSS grid highlighter is shown.");
 });
--- a/devtools/client/inspector/rules/test/browser_rules_grid-toggle_01b.js
+++ b/devtools/client/inspector/rules/test/browser_rules_grid-toggle_01b.js
@@ -24,35 +24,37 @@ add_task(async function() {
   const {inspector, view} = await openRuleView();
   const highlighters = view.highlighters;
 
   await selectNode("#grid", inspector);
   const container = getRuleViewProperty(view, "#grid", "display").valueSpan;
   const gridToggle = container.querySelector(".ruleview-grid");
 
   info("Checking the initial state of the CSS grid toggle in the rule-view.");
-  ok(gridToggle, "Grid highlighter toggle is visible.");
+  ok(!gridToggle.hasAttribute("disabled"), "Grid highlighter toggle is not disabled.");
   ok(!gridToggle.classList.contains("active"),
     "Grid highlighter toggle button is not active.");
   ok(!highlighters.gridHighlighters.size, "No CSS grid highlighter is shown.");
 
   info("Toggling ON the CSS grid highlighter from the rule-view.");
   const onHighlighterShown = highlighters.once("grid-highlighter-shown");
   gridToggle.click();
   await onHighlighterShown;
 
   info("Checking the CSS grid highlighter is created and toggle button is active in " +
     "the rule-view.");
+  ok(!gridToggle.hasAttribute("disabled"), "Grid highlighter toggle is not disabled.");
   ok(gridToggle.classList.contains("active"),
     "Grid highlighter toggle is active.");
   is(highlighters.gridHighlighters.size, 1, "CSS grid highlighter is shown.");
 
   info("Toggling OFF the CSS grid highlighter from the rule-view.");
   const onHighlighterHidden = highlighters.once("grid-highlighter-hidden");
   gridToggle.click();
   await onHighlighterHidden;
 
   info("Checking the CSS grid highlighter is not shown and toggle button is not active " +
     "in the rule-view.");
+  ok(!gridToggle.hasAttribute("disabled"), "Grid highlighter toggle is not disabled.");
   ok(!gridToggle.classList.contains("active"),
     "Grid highlighter toggle button is not active.");
   ok(!highlighters.gridHighlighters.size, "No CSS grid highlighter is shown.");
 });
--- a/devtools/client/inspector/rules/test/browser_rules_grid-toggle_02.js
+++ b/devtools/client/inspector/rules/test/browser_rules_grid-toggle_02.js
@@ -29,17 +29,19 @@ add_task(async function() {
 
   await selectNode("#grid", inspector);
   const container = getRuleViewProperty(view, "#grid", "display").valueSpan;
   const gridToggle = container.querySelector(".ruleview-grid");
   const overriddenContainer = getRuleViewProperty(view, "div, ul", "display").valueSpan;
   const overriddenGridToggle = overriddenContainer.querySelector(".ruleview-grid");
 
   info("Checking the initial state of the CSS grid toggle in the rule-view.");
-  ok(gridToggle && overriddenGridToggle, "Grid highlighter toggles are visible.");
+  ok(!gridToggle.hasAttribute("disabled"), "Grid highlighter toggle is not disabled.");
+  ok(!overriddenGridToggle.hasAttribute("disabled"),
+    "Grid highlighter toggle is not disabled.");
   ok(!gridToggle.classList.contains("active") &&
     !overriddenGridToggle.classList.contains("active"),
     "Grid highlighter toggle buttons are not active.");
   ok(!highlighters.gridHighlighters.size, "No CSS grid highlighter is shown.");
 
   info("Toggling ON the CSS grid highlighter from the overridden rule in the rule-view.");
   const onHighlighterShown = highlighters.once("grid-highlighter-shown");
   overriddenGridToggle.click();
--- a/devtools/client/inspector/rules/test/browser_rules_grid-toggle_03.js
+++ b/devtools/client/inspector/rules/test/browser_rules_grid-toggle_03.js
@@ -30,42 +30,43 @@ add_task(async function() {
 
   info("Selecting the first grid container.");
   await selectNode("#grid1", inspector);
   let container = getRuleViewProperty(view, ".grid", "display").valueSpan;
   let gridToggle = container.querySelector(".ruleview-grid");
 
   info("Checking the state of the CSS grid toggle for the first grid container in the " +
     "rule-view.");
-  ok(gridToggle, "Grid highlighter toggle is visible.");
+  ok(!gridToggle.hasAttribute("disabled"), "Grid highlighter toggle is not disabled.");
   ok(!gridToggle.classList.contains("active"),
     "Grid highlighter toggle button is not active.");
   ok(!highlighters.gridHighlighters.size, "No CSS grid highlighter is shown.");
 
   info("Toggling ON the CSS grid highlighter for the first grid container from the " +
     "rule-view.");
   let onHighlighterShown = highlighters.once("grid-highlighter-shown");
   gridToggle.click();
   await onHighlighterShown;
 
   info("Checking the CSS grid highlighter is created and toggle button is active in " +
     "the rule-view.");
+  ok(!gridToggle.hasAttribute("disabled"), "Grid highlighter toggle is not disabled.");
   ok(gridToggle.classList.contains("active"),
     "Grid highlighter toggle is active.");
   is(highlighters.gridHighlighters.size, 1, "CSS grid highlighter is shown.");
 
   info("Selecting the second grid container.");
   await selectNode("#grid2", inspector);
   const firstGridHighterShown = highlighters.gridHighlighters.keys().next().value;
   container = getRuleViewProperty(view, ".grid", "display").valueSpan;
   gridToggle = container.querySelector(".ruleview-grid");
 
   info("Checking the state of the CSS grid toggle for the second grid container in the " +
     "rule-view.");
-  ok(gridToggle, "Grid highlighter toggle is visible.");
+  ok(!gridToggle.hasAttribute("disabled"), "Grid highlighter toggle is not disabled.");
   ok(!gridToggle.classList.contains("active"),
     "Grid highlighter toggle button is not active.");
   is(highlighters.gridHighlighters.size, 1, "CSS grid highlighter is still shown.");
 
   info("Toggling ON the CSS grid highlighter for the second grid container from the " +
     "rule-view.");
   onHighlighterShown = highlighters.once("grid-highlighter-shown");
   gridToggle.click();
@@ -80,12 +81,12 @@ add_task(async function() {
 
   info("Selecting the first grid container.");
   await selectNode("#grid1", inspector);
   container = getRuleViewProperty(view, ".grid", "display").valueSpan;
   gridToggle = container.querySelector(".ruleview-grid");
 
   info("Checking the state of the CSS grid toggle for the first grid container in the " +
     "rule-view.");
-  ok(gridToggle, "Grid highlighter toggle is visible.");
+  ok(!gridToggle.hasAttribute("disabled"), "Grid highlighter toggle is not disabled.");
   ok(!gridToggle.classList.contains("active"),
     "Grid highlighter toggle button is not active.");
 });
--- a/devtools/client/inspector/rules/test/browser_rules_grid-toggle_04.js
+++ b/devtools/client/inspector/rules/test/browser_rules_grid-toggle_04.js
@@ -24,35 +24,37 @@ add_task(async function() {
   const {inspector, view} = await openRuleView();
   const highlighters = view.highlighters;
 
   await selectNode("#grid", inspector);
   const container = getRuleViewProperty(view, "#grid", "display").valueSpan;
   const gridToggle = container.querySelector(".ruleview-grid");
 
   info("Checking the initial state of the CSS grid toggle in the rule-view.");
-  ok(gridToggle, "Grid highlighter toggle is visible.");
+  ok(!gridToggle.hasAttribute("disabled"), "Grid highlighter toggle is not disabled.");
   ok(!gridToggle.classList.contains("active"),
     "Grid highlighter toggle button is not active.");
   ok(!highlighters.gridHighlighters.size, "No CSS grid highlighter is shown.");
 
   info("Toggling ON the CSS grid highlighter from the rule-view.");
   const onHighlighterShown = highlighters.once("grid-highlighter-shown");
   gridToggle.click();
   await onHighlighterShown;
 
   info("Checking the CSS grid highlighter is created and toggle button is active in " +
     "the rule-view.");
+  ok(!gridToggle.hasAttribute("disabled"), "Grid highlighter toggle is not disabled.");
   ok(gridToggle.classList.contains("active"),
     "Grid highlighter toggle is active.");
   is(highlighters.gridHighlighters.size, 1, "CSS grid highlighter is shown.");
 
   info("Toggling OFF the CSS grid highlighter from the rule-view.");
   const onHighlighterHidden = highlighters.once("grid-highlighter-hidden");
   gridToggle.click();
   await onHighlighterHidden;
 
   info("Checking the CSS grid highlighter is not shown and toggle button is not active " +
     "in the rule-view.");
+  ok(!gridToggle.hasAttribute("disabled"), "Grid highlighter toggle is not disabled.");
   ok(!gridToggle.classList.contains("active"),
     "Grid highlighter toggle button is not active.");
   ok(!highlighters.gridHighlighters.size, "No CSS grid highlighter is shown.");
 });
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/rules/test/browser_rules_grid-toggle_05.js
@@ -0,0 +1,102 @@
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test that the grid toggle is hidden when the maximum number of grid highlighters
+// have been reached.
+
+const TEST_URI = `
+  <style type='text/css'>
+    .grid {
+      display: grid;
+    }
+  </style>
+  <div id="grid1" class="grid">
+    <div class="cell1">cell1</div>
+    <div class="cell2">cell2</div>
+  </div>
+  <div id="grid2" class="grid">
+    <div class="cell1">cell1</div>
+    <div class="cell2">cell2</div>
+  </div>
+  <div id="grid3" class="grid">
+    <div class="cell1">cell1</div>
+    <div class="cell2">cell2</div>
+  </div>
+`;
+
+add_task(async function() {
+  await pushPref("devtools.gridinspector.maxHighlighters", 2);
+  await addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
+  const { inspector, gridInspector } = await openLayoutView();
+  const ruleView = selectRuleView(inspector);
+  const { document: doc } = gridInspector;
+  const { highlighters } = inspector;
+
+  await selectNode("#grid1", inspector);
+  const gridList = doc.getElementById("grid-list");
+  const checkbox2 = gridList.children[1].querySelector("input");
+  const checkbox3 = gridList.children[2].querySelector("input");
+  const container = getRuleViewProperty(ruleView, ".grid", "display").valueSpan;
+  const gridToggle = container.querySelector(".ruleview-grid");
+
+  info("Checking the initial state of the CSS grid toggle in the rule-view.");
+  ok(!gridToggle.hasAttribute("disabled"), "Grid highlighter toggle is not disabled.");
+  ok(!gridToggle.classList.contains("active"),
+    "Grid highlighter toggle button is not active.");
+  ok(!highlighters.gridHighlighters.size, "No CSS grid highlighter is shown.");
+
+  info("Toggling ON the CSS grid highlighter for #grid2.");
+  let onHighlighterShown = highlighters.once("grid-highlighter-shown");
+  checkbox2.click();
+  await onHighlighterShown;
+
+  info("Checking the CSS grid toggle for #grid1 is not disabled and not active.");
+  ok(!gridToggle.hasAttribute("disabled"), "Grid highlighter toggle is not disabled.");
+  ok(!gridToggle.classList.contains("active"),
+    "Grid highlighter toggle button is not active.");
+  is(highlighters.gridHighlighters.size, 1, "CSS grid highlighter is shown.");
+
+  info("Toggling ON the CSS grid highlighter for #grid3.");
+  onHighlighterShown = highlighters.once("grid-highlighter-shown");
+  checkbox3.click();
+  await onHighlighterShown;
+
+  info("Checking the CSS grid toggle for #grid1 is disabled.");
+  ok(gridToggle.hasAttribute("disabled"), "Grid highlighter toggle is disabled.");
+  is(highlighters.gridHighlighters.size, 2, "CSS grid highlighters are shown.");
+
+  info("Toggling OFF the CSS grid highlighter for #grid3.");
+  let onHighlighterHidden = highlighters.once("grid-highlighter-hidden");
+  checkbox3.click();
+  await onHighlighterHidden;
+
+  info("Checking the CSS grid toggle for #grid1 is not disabled and not active.");
+  ok(!gridToggle.hasAttribute("disabled"), "Grid highlighter toggle is not disabled.");
+  ok(!gridToggle.classList.contains("active"),
+    "Grid highlighter toggle button is not active.");
+  is(highlighters.gridHighlighters.size, 1, "CSS grid highlighter is shown.");
+
+  info("Toggling ON the CSS grid highlighter for #grid1 from the rule-view.");
+  onHighlighterShown = highlighters.once("grid-highlighter-shown");
+  gridToggle.click();
+  await onHighlighterShown;
+
+  info("Checking the CSS grid toggle for #grid1 is not disabled.");
+  ok(!gridToggle.hasAttribute("disabled"), "Grid highlighter toggle is not disabled.");
+  ok(gridToggle.classList.contains("active"), "Grid highlighter toggle is active.");
+  is(highlighters.gridHighlighters.size, 2, "CSS grid highlighters are shown.");
+
+  info("Toggling OFF the CSS grid highlighter for #grid1 from the rule-view.");
+  onHighlighterHidden = highlighters.once("grid-highlighter-hidden");
+  gridToggle.click();
+  await onHighlighterHidden;
+
+  info("Checking the CSS grid toggle for #grid1 is not disabled and not active.");
+  ok(!gridToggle.hasAttribute("disabled"), "Grid highlighter toggle is not disabled.");
+  ok(!gridToggle.classList.contains("active"),
+    "Grid highlighter toggle button is not active.");
+  is(highlighters.gridHighlighters.size, 1, "CSS grid highlighter is shown.");
+});
--- a/devtools/client/inspector/rules/views/text-property-editor.js
+++ b/devtools/client/inspector/rules/views/text-property-editor.js
@@ -71,16 +71,17 @@ function TextPropertyEditor(ruleEditor, 
   this.ruleEditor = ruleEditor;
   this.ruleView = this.ruleEditor.ruleView;
   this.cssProperties = this.ruleView.cssProperties;
   this.doc = this.ruleEditor.doc;
   this.popup = this.ruleView.popup;
   this.prop = property;
   this.prop.editor = this;
   this.browserWindow = this.doc.defaultView.top;
+
   this._populatedComputed = false;
   this._hasPendingClick = false;
   this._clickedElementOptions = null;
 
   this.toolbox = this.ruleView.inspector.toolbox;
   this.telemetry = this.toolbox.telemetry;
 
   this.getGridlineNames = this.getGridlineNames.bind(this);
@@ -516,32 +517,32 @@ TextPropertyEditor.prototype = {
     if (this.ruleEditor.isEditable) {
       for (const angleSpan of this.angleSwatchSpans) {
         angleSpan.on("unit-change", this._onSwatchCommit);
         const title = l10n("rule.angleSwatch.tooltip");
         angleSpan.setAttribute("title", title);
       }
     }
 
+    const nodeFront = this.ruleView.inspector.selection.nodeFront;
+
     const flexToggle = this.valueSpan.querySelector(".ruleview-flex");
     if (flexToggle) {
       flexToggle.setAttribute("title", l10n("rule.flexToggle.tooltip"));
-      if (this.ruleView.highlighters.flexboxHighlighterShown ===
-          this.ruleView.inspector.selection.nodeFront) {
-        flexToggle.classList.add("active");
-      }
+      flexToggle.classList.toggle("active",
+        this.ruleView.highlighters.flexboxHighlighterShown === nodeFront);
     }
 
     const gridToggle = this.valueSpan.querySelector(".ruleview-grid");
     if (gridToggle) {
       gridToggle.setAttribute("title", l10n("rule.gridToggle.tooltip"));
-      if (this.ruleView.highlighters.gridHighlighters.has(
-            this.ruleView.inspector.selection.nodeFront)) {
-        gridToggle.classList.add("active");
-      }
+      gridToggle.classList.toggle("active",
+        this.ruleView.highlighters.gridHighlighters.has(nodeFront));
+      gridToggle.toggleAttribute("disabled",
+        !this.ruleView.highlighters.canGridHighlighterToggle(nodeFront));
     }
 
     const shapeToggle = this.valueSpan.querySelector(".ruleview-shapeswatch");
     if (shapeToggle) {
       const mode = "css" + name.split("-").map(s => {
         return s[0].toUpperCase() + s.slice(1);
       }).join("");
       shapeToggle.setAttribute("data-mode", mode);
--- a/devtools/client/inspector/shared/highlighters-overlay.js
+++ b/devtools/client/inspector/shared/highlighters-overlay.js
@@ -23,16 +23,18 @@ class HighlightersOverlay {
    * @param  {Inspector} inspector
    *         Inspector toolbox panel.
    */
   constructor(inspector) {
     this.inspector = inspector;
     this.highlighterUtils = this.inspector.toolbox.highlighterUtils;
     this.store = this.inspector.store;
     this.telemetry = inspector.telemetry;
+    this.maxGridHighlighters =
+      Services.prefs.getIntPref("devtools.gridinspector.maxHighlighters");
 
     // Collection of instantiated highlighter actors like FlexboxHighlighter,
     // ShapesHighlighter and GeometryEditorHighlighter.
     this.highlighters = {};
     // Map of grid container NodeFront to their instantiated grid highlighter actors.
     this.gridHighlighters = new Map();
     // Array of reusable grid highlighters that have been instantiated and are not
     // associated with any NodeFront.
@@ -427,29 +429,26 @@ class HighlightersOverlay {
    * @param  {Object} options
    *         Object used for passing options to the grid highlighter.
    * @param. {String|null} trigger
    *         String name matching "grid" or "rule" to indicate where the
    *         grid highlighter was toggled on from. "grid" represents the grid view
    *         "rule" represents the rule view.
    */
   async showGridHighlighter(node, options, trigger) {
-    const maxHighlighters =
-      Services.prefs.getIntPref("devtools.gridinspector.maxHighlighters");
-
     // When the grid highlighter has the given node, it is probably called with new
     // highlighting options, so skip any extra grid highlighter handling.
     if (!this.gridHighlighters.has(node)) {
-      if (maxHighlighters === 1) {
+      if (this.maxGridHighlighters === 1) {
         // Only one grid highlighter can be shown at a time. Hides any instantiated
         // grid highlighters.
         for (const nodeFront of this.gridHighlighters.keys()) {
           await this.hideGridHighlighter(nodeFront);
         }
-      } else if (this.gridHighlighters.size === maxHighlighters) {
+      } else if (this.gridHighlighters.size === this.maxGridHighlighters) {
         // The maximum number of grid highlighters shown have been reached. Don't show
         // any additional grid highlighters.
         return;
       }
     }
 
     const highlighter = await this._getGridHighlighter(node);
     if (!highlighter) {
@@ -491,27 +490,27 @@ class HighlightersOverlay {
    * @param  {NodeFront} node
    *         The NodeFront of the grid container element to unhighlight.
    */
   async hideGridHighlighter(node) {
     if (!this.gridHighlighters.has(node)) {
       return;
     }
 
-    this._toggleRuleViewIcon(node, false, ".ruleview-grid");
-
     // Hide the highlighter and put it in the pool of extra grid highlighters
     // so that it can be reused.
     const highlighter = this.gridHighlighters.get(node);
     await highlighter.hide();
     this.extraGridHighlighterPool.push(highlighter);
 
     this.state.grids.delete(node);
     this.gridHighlighters.delete(node);
 
+    this._toggleRuleViewIcon(node, false, ".ruleview-grid");
+
     // Emit the NodeFront of the grid container element that the grid highlighter was
     // hidden for.
     this.emit("grid-highlighter-hidden", node);
   }
 
   /**
    * Show the box model highlighter for the given node.
    *
@@ -785,22 +784,32 @@ class HighlightersOverlay {
    * @param  {NodeFront} node
    *         The NodeFront of the element with a shape to highlight.
    * @param  {Boolean} active
    *         Whether or not the shape icon should be active.
    * @param  {String} selector
    *         The selector of the rule view icon to toggle.
    */
   _toggleRuleViewIcon(node, active, selector) {
-    if (this.inspector.selection.nodeFront != node) {
+    const ruleViewEl = this.inspector.getPanel("ruleview").view.element;
+
+    if (this.inspector.selection.nodeFront !== node) {
+      if (selector === ".ruleview-grid") {
+        for (const icon of ruleViewEl.querySelectorAll(selector)) {
+          if (this.canGridHighlighterToggle(this.inspector.selection.nodeFront)) {
+            icon.removeAttribute("disabled");
+          } else {
+            icon.setAttribute("disabled", true);
+          }
+        }
+      }
+
       return;
     }
 
-    const ruleViewEl = this.inspector.getPanel("ruleview").view.element;
-
     for (const icon of ruleViewEl.querySelectorAll(selector)) {
       icon.classList.toggle("active", active);
     }
   }
 
   /**
    * Toggle the class "active" on the given shape point in the rule view if the current
    * inspector selection is highlighted by the shapes highlighter.
--- a/devtools/client/inspector/test/browser_inspector_sidebarstate.js
+++ b/devtools/client/inspector/test/browser_inspector_sidebarstate.js
@@ -7,37 +7,37 @@ const TEST_URI = "data:text/html;charset
   "<h1>browser_inspector_sidebarstate.js</h1>";
 const OPTOUT = Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTOUT;
 
 const TELEMETRY_DATA = [
   {
     timestamp: null,
     category: "devtools.main",
     method: "tool_timer",
-    object: "computedview",
-    value: null,
-    extra: {
-      time_open: ""
-    }
-  },
-  {
-    timestamp: null,
-    category: "devtools.main",
-    method: "tool_timer",
     object: "layoutview",
     value: null,
     extra: {
       time_open: ""
     }
   },
   {
     timestamp: null,
     category: "devtools.main",
     method: "tool_timer",
-    object: "ruleview",
+    object: "fontinspector",
+    value: null,
+    extra: {
+      time_open: ""
+    }
+  },
+  {
+    timestamp: null,
+    category: "devtools.main",
+    method: "tool_timer",
+    object: "computedview",
     value: null,
     extra: {
       time_open: ""
     }
   }
 ];
 
 add_task(async function() {
@@ -45,41 +45,41 @@ add_task(async function() {
   Services.telemetry.clearEvents();
 
   // Ensure no events have been logged
   const snapshot = Services.telemetry.snapshotEvents(OPTOUT, true);
   ok(!snapshot.parent, "No events have been logged for the main process");
 
   let { inspector, toolbox } = await openInspectorForURL(TEST_URI);
 
+  info("Selecting font inspector.");
+  inspector.sidebar.select("fontinspector");
+
+  is(inspector.sidebar.getCurrentTabID(), "fontinspector",
+    "Font Inspector is selected");
+
   info("Selecting computed view.");
   inspector.sidebar.select("computedview");
 
   is(inspector.sidebar.getCurrentTabID(), "computedview",
     "Computed View is selected");
 
-  info("Selecting layout view.");
-  inspector.sidebar.select("layoutview");
-
-  is(inspector.sidebar.getCurrentTabID(), "layoutview",
-    "Layout View is selected");
-
   info("Closing inspector.");
   await toolbox.destroy();
 
   info("Re-opening inspector.");
   inspector = (await openInspector()).inspector;
 
   if (!inspector.sidebar.getCurrentTabID()) {
     info("Default sidebar still to be selected, adding select listener.");
     await inspector.sidebar.once("select");
   }
 
-  is(inspector.sidebar.getCurrentTabID(), "layoutview",
-     "Layout view is selected by default.");
+  is(inspector.sidebar.getCurrentTabID(), "computedview",
+     "Computed view is selected by default.");
 
   checkTelemetryResults();
 });
 
 function checkTelemetryResults() {
   const snapshot = Services.telemetry.snapshotEvents(OPTOUT, true);
   const events = snapshot.parent.filter(event => event[1] === "devtools.main" &&
                                                  event[2] === "tool_timer"
--- a/devtools/client/preferences/devtools-client.js
+++ b/devtools/client/preferences/devtools-client.js
@@ -28,17 +28,17 @@ pref("devtools.command-button-screenshot
 pref("devtools.command-button-rulers.enabled", false);
 pref("devtools.command-button-measure.enabled", false);
 pref("devtools.command-button-noautohide.enabled", false);
 
 // Inspector preferences
 // Enable the Inspector
 pref("devtools.inspector.enabled", true);
 // What was the last active sidebar in the inspector
-pref("devtools.inspector.activeSidebar", "ruleview");
+pref("devtools.inspector.activeSidebar", "layoutview");
 pref("devtools.inspector.remote", false);
 
 // Enable the 3 pane mode in the inspector
 pref("devtools.inspector.three-pane-enabled", true);
 // Enable the 3 pane mode in the chrome inspector
 pref("devtools.inspector.chrome.three-pane-enabled", false);
 // Collapse pseudo-elements by default in the rule-view
 pref("devtools.inspector.show_pseudo_elements", false);
--- a/devtools/client/shared/test/browser_telemetry_sidebar.js
+++ b/devtools/client/shared/test/browser_telemetry_sidebar.js
@@ -14,17 +14,17 @@ const TOOL_DELAY = 200;
 const DATA = [
   {
     timestamp: null,
     category: "devtools.main",
     method: "sidepanel_changed",
     object: "inspector",
     value: null,
     extra: {
-      oldpanel: "computedview",
+      oldpanel: "layoutview",
       newpanel: "animationinspector"
     }
   },
   {
     timestamp: null,
     category: "devtools.main",
     method: "sidepanel_changed",
     object: "inspector",
@@ -151,18 +151,18 @@ function testSidebar(toolbox) {
   });
 }
 
 function checkResults() {
   // For help generating these tests use generateTelemetryTests("DEVTOOLS_")
   // here.
   checkTelemetry("DEVTOOLS_INSPECTOR_OPENED_COUNT", "", [1, 0, 0], "array");
   checkTelemetry("DEVTOOLS_RULEVIEW_OPENED_COUNT", "", [1, 0, 0], "array");
-  checkTelemetry("DEVTOOLS_COMPUTEDVIEW_OPENED_COUNT", "", [3, 0, 0], "array");
-  checkTelemetry("DEVTOOLS_LAYOUTVIEW_OPENED_COUNT", "", [2, 0, 0], "array");
+  checkTelemetry("DEVTOOLS_COMPUTEDVIEW_OPENED_COUNT", "", [2, 0, 0], "array");
+  checkTelemetry("DEVTOOLS_LAYOUTVIEW_OPENED_COUNT", "", [3, 0, 0], "array");
   checkTelemetry("DEVTOOLS_FONTINSPECTOR_OPENED_COUNT", "", [2, 0, 0], "array");
   checkTelemetry("DEVTOOLS_COMPUTEDVIEW_TIME_ACTIVE_SECONDS", "", null, "hasentries");
   checkTelemetry("DEVTOOLS_LAYOUTVIEW_TIME_ACTIVE_SECONDS", "", null, "hasentries");
   checkTelemetry("DEVTOOLS_FONTINSPECTOR_TIME_ACTIVE_SECONDS", "", null, "hasentries");
 }
 
 function checkEventTelemetry() {
   const snapshot = Services.telemetry.snapshotEvents(OPTOUT, true);
--- a/devtools/client/themes/rules.css
+++ b/devtools/client/themes/rules.css
@@ -435,16 +435,21 @@
   border-radius: 0;
 }
 
 .ruleview-grid {
   background: url("chrome://devtools/skin/images/grid.svg");
   border-radius: 0;
 }
 
+.ruleview-grid[disabled] {
+  cursor: default;
+  opacity: 0.5;
+}
+
 .ruleview-shape-point.active,
 .ruleview-shapeswatch.active + .ruleview-shape > .ruleview-shape-point:hover {
   background-color: var(--rule-highlight-background-color);
 }
 
 .ruleview-colorswatch::before {
   content: '';
   background-color: #eee;
--- a/dom/base/TimeoutExecutor.cpp
+++ b/dom/base/TimeoutExecutor.cpp
@@ -51,32 +51,28 @@ TimeoutExecutor::ScheduleDelayed(const T
   MOZ_DIAGNOSTIC_ASSERT(mDeadline.IsNull());
   MOZ_DIAGNOSTIC_ASSERT(mMode == Mode::None);
   MOZ_DIAGNOSTIC_ASSERT(!aMinDelay.IsZero() ||
                         aDeadline > (aNow + mAllowedEarlyFiringTime));
 
   nsresult rv = NS_OK;
 
   if (!mTimer) {
-    mTimer = NS_NewTimer();
+    mTimer = NS_NewTimer(mOwner->EventTarget());
     NS_ENSURE_TRUE(mTimer, NS_ERROR_OUT_OF_MEMORY);
 
     uint32_t earlyMicros = 0;
     MOZ_ALWAYS_SUCCEEDS(mTimer->GetAllowedEarlyFiringMicroseconds(&earlyMicros));
     mAllowedEarlyFiringTime = TimeDuration::FromMicroseconds(earlyMicros);
+  } else {
+    // Always call Cancel() in case we are re-using a timer.
+    rv = mTimer->Cancel();
+    NS_ENSURE_SUCCESS(rv, rv);
   }
 
-  // Always call Cancel() in case we are re-using a timer.  Otherwise
-  // the subsequent SetTarget() may fail.
-  rv = mTimer->Cancel();
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  rv = mTimer->SetTarget(mOwner->EventTarget());
-  NS_ENSURE_SUCCESS(rv, rv);
-
   // Calculate the delay based on the deadline and current time.  If we have
   // a minimum delay set then clamp to that value.
   //
   // Note, we don't actually adjust our mDeadline for the minimum delay, just
   // the nsITimer value.  This is necessary to avoid lots of needless
   // rescheduling if more deadlines come in between now and the minimum delay
   // firing time.
   TimeDuration delay = TimeDuration::Max(aMinDelay, aDeadline - aNow);
--- a/dom/file/FileReader.cpp
+++ b/dom/file/FileReader.cpp
@@ -526,24 +526,23 @@ FileReader::WrapObject(JSContext* aCx, J
 {
   return FileReader_Binding::Wrap(aCx, this, aGivenProto);
 }
 
 void
 FileReader::StartProgressEventTimer()
 {
   if (!mProgressNotifier) {
-    mProgressNotifier = NS_NewTimer();
+    mProgressNotifier = NS_NewTimer(mTarget);
   }
 
   if (mProgressNotifier) {
     mProgressEventWasDelayed = false;
     mTimerIsActive = true;
     mProgressNotifier->Cancel();
-    mProgressNotifier->SetTarget(mTarget);
     mProgressNotifier->InitWithCallback(this, NS_PROGRESS_EVENT_INTERVAL,
                                         nsITimer::TYPE_ONE_SHOT);
   }
 }
 
 void
 FileReader::ClearProgressEventTimer()
 {
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -1587,17 +1587,17 @@ ContentParent::MarkAsDead()
 {
   MarkAsTroubled();
   mIsAlive = false;
 }
 
 void
 ContentParent::OnChannelError()
 {
-  RefPtr<ContentParent> content(this);
+  RefPtr<ContentParent> kungFuDeathGrip(this);
   PContentParent::OnChannelError();
 }
 
 void
 ContentParent::OnChannelConnected(int32_t pid)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
@@ -1733,24 +1733,24 @@ DelayedDeleteSubprocess(GeckoChildProces
 
 // This runnable only exists to delegate ownership of the
 // ContentParent to this runnable, until it's deleted by the event
 // system.
 struct DelayedDeleteContentParentTask : public Runnable
 {
   explicit DelayedDeleteContentParentTask(ContentParent* aObj)
     : Runnable("dom::DelayedDeleteContentParentTask")
-    , mObj(aObj)
+    , mKungFuDeathGrip(aObj)
   {
   }
 
   // No-op
   NS_IMETHOD Run() override { return NS_OK; }
 
-  RefPtr<ContentParent> mObj;
+  RefPtr<ContentParent> mKungFuDeathGrip;
 };
 
 } // namespace
 
 void
 ContentParent::ActorDestroy(ActorDestroyReason why)
 {
   if (mForceKillTimer) {
--- a/dom/security/featurepolicy/FeaturePolicyUtils.cpp
+++ b/dom/security/featurepolicy/FeaturePolicyUtils.cpp
@@ -18,26 +18,26 @@ struct FeatureMap {
 };
 
 /*
  * IMPORTANT: Do not change this list without review from a DOM peer _AND_ a
  * DOM Security peer!
  */
 static FeatureMap sSupportedFeatures[] = {
   { "autoplay", FeaturePolicyUtils::FeaturePolicyValue::eAll },
-  { "camera", FeaturePolicyUtils::FeaturePolicyValue::eAll  },
-  { "encrypted-media", FeaturePolicyUtils::FeaturePolicyValue::eAll  },
-  { "fullscreen", FeaturePolicyUtils::FeaturePolicyValue::eAll  },
-  { "geolocation", FeaturePolicyUtils::FeaturePolicyValue::eAll  },
-  { "microphone", FeaturePolicyUtils::FeaturePolicyValue::eAll  },
-  { "midi", FeaturePolicyUtils::FeaturePolicyValue::eAll  },
-  { "payment", FeaturePolicyUtils::FeaturePolicyValue::eAll  },
+  { "camera", FeaturePolicyUtils::FeaturePolicyValue::eAll },
+  { "encrypted-media", FeaturePolicyUtils::FeaturePolicyValue::eAll },
+  { "fullscreen", FeaturePolicyUtils::FeaturePolicyValue::eAll },
+  { "geolocation", FeaturePolicyUtils::FeaturePolicyValue::eAll },
+  { "microphone", FeaturePolicyUtils::FeaturePolicyValue::eAll },
+  { "midi", FeaturePolicyUtils::FeaturePolicyValue::eAll },
+  { "payment", FeaturePolicyUtils::FeaturePolicyValue::eAll },
   // TODO: not supported yet!!!
-  { "speaker", FeaturePolicyUtils::FeaturePolicyValue::eAll  },
-  { "vr", FeaturePolicyUtils::FeaturePolicyValue::eAll  },
+  { "speaker", FeaturePolicyUtils::FeaturePolicyValue::eAll },
+  { "vr", FeaturePolicyUtils::FeaturePolicyValue::eAll },
 };
 
 /* static */ bool
 FeaturePolicyUtils::IsSupportedFeature(const nsAString& aFeatureName)
 {
   uint32_t numFeatures = (sizeof(sSupportedFeatures) / sizeof(sSupportedFeatures[0]));
   for (uint32_t i = 0; i < numFeatures; ++i) {
     if (aFeatureName.LowerCaseEqualsASCII(sSupportedFeatures[i].mFeatureName)) {
--- a/dom/xhr/XMLHttpRequestMainThread.cpp
+++ b/dom/xhr/XMLHttpRequestMainThread.cpp
@@ -3137,23 +3137,23 @@ XMLHttpRequestMainThread::SetTimeout(uin
   }
 
   mTimeoutMilliseconds = aTimeout;
   if (mRequestSentTime) {
     StartTimeoutTimer();
   }
 }
 
-void
-XMLHttpRequestMainThread::SetTimerEventTarget(nsITimer* aTimer)
+nsIEventTarget*
+XMLHttpRequestMainThread::GetTimerEventTarget()
 {
   if (nsCOMPtr<nsIGlobalObject> global = GetOwnerGlobal()) {
-    nsCOMPtr<nsIEventTarget> target = global->EventTargetFor(TaskCategory::Other);
-    aTimer->SetTarget(target);
-  }
+    return global->EventTargetFor(TaskCategory::Other);
+  }
+  return nullptr;
 }
 
 nsresult
 XMLHttpRequestMainThread::DispatchToMainThread(already_AddRefed<nsIRunnable> aRunnable)
 {
   if (nsCOMPtr<nsIGlobalObject> global = GetOwnerGlobal()) {
     nsCOMPtr<nsIEventTarget> target = global->EventTargetFor(TaskCategory::Other);
     MOZ_ASSERT(target);
@@ -3178,18 +3178,17 @@ XMLHttpRequestMainThread::StartTimeoutTi
     mTimeoutTimer->Cancel();
   }
 
   if (!mTimeoutMilliseconds) {
     return;
   }
 
   if (!mTimeoutTimer) {
-    mTimeoutTimer = NS_NewTimer();
-    SetTimerEventTarget(mTimeoutTimer);
+    mTimeoutTimer = NS_NewTimer(GetTimerEventTarget());
   }
   uint32_t elapsed =
     (uint32_t)((PR_Now() - mRequestSentTime) / PR_USEC_PER_MSEC);
   mTimeoutTimer->InitWithCallback(
     this,
     mTimeoutMilliseconds > elapsed ? mTimeoutMilliseconds - elapsed : 0,
     nsITimer::TYPE_ONE_SHOT
   );
@@ -3596,18 +3595,17 @@ XMLHttpRequestMainThread::StopProgressEv
     mProgressNotifier->Cancel();
   }
 }
 
 void
 XMLHttpRequestMainThread::StartProgressEventTimer()
 {
   if (!mProgressNotifier) {
-    mProgressNotifier = NS_NewTimer();
-    SetTimerEventTarget(mProgressNotifier);
+    mProgressNotifier = NS_NewTimer(GetTimerEventTarget());
   }
   if (mProgressNotifier) {
     mProgressTimerIsActive = true;
     mProgressNotifier->Cancel();
     mProgressNotifier->InitWithCallback(this, NS_PROGRESS_EVENT_INTERVAL,
                                         nsITimer::TYPE_ONE_SHOT);
   }
 }
@@ -3623,18 +3621,17 @@ XMLHttpRequestMainThread::MaybeStartSync
   }
 
   // If we are in a beforeunload or a unload event, we must force a timeout.
   TimeDuration diff = (TimeStamp::NowLoRes() - doc->GetPageUnloadingEventTimeStamp());
   if (diff.ToMilliseconds() > MAX_SYNC_TIMEOUT_WHEN_UNLOADING) {
     return eErrorOrExpired;
   }
 
-  mSyncTimeoutTimer = NS_NewTimer();
-  SetTimerEventTarget(mSyncTimeoutTimer);
+  mSyncTimeoutTimer = NS_NewTimer(GetTimerEventTarget());
   if (!mSyncTimeoutTimer) {
     return eErrorOrExpired;
   }
 
   uint32_t timeout = MAX_SYNC_TIMEOUT_WHEN_UNLOADING - diff.ToMilliseconds();
   nsresult rv = mSyncTimeoutTimer->InitWithCallback(this, timeout,
                                                     nsITimer::TYPE_ONE_SHOT);
   return NS_FAILED(rv) ? eErrorOrExpired : eTimerStarted;
--- a/dom/xhr/XMLHttpRequestMainThread.h
+++ b/dom/xhr/XMLHttpRequestMainThread.h
@@ -509,17 +509,17 @@ protected:
 
   void StartProgressEventTimer();
   void StopProgressEventTimer();
 
   void MaybeCreateBlobStorage();
 
   nsresult OnRedirectVerifyCallback(nsresult result);
 
-  void SetTimerEventTarget(nsITimer* aTimer);
+  nsIEventTarget* GetTimerEventTarget();
 
   nsresult DispatchToMainThread(already_AddRefed<nsIRunnable> aRunnable);
 
   void DispatchOrStoreEvent(DOMEventTargetHelper* aTarget, Event* aEvent);
 
   already_AddRefed<nsXMLHttpRequestXPCOMifier> EnsureXPCOMifier();
 
   void SuspendEventDispatching();
--- a/gfx/thebes/gfxPlatform.cpp
+++ b/gfx/thebes/gfxPlatform.cpp
@@ -732,16 +732,17 @@ WebRenderMemoryReporter::CollectReports(
       helper.Report(aReport.images, "resource-cache/images");
       helper.Report(aReport.rasterized_blobs, "resource-cache/rasterized-blobs");
 
       // GPU Memory.
       helper.ReportTexture(aReport.gpu_cache_textures, "gpu-cache");
       helper.ReportTexture(aReport.vertex_data_textures, "vertex-data");
       helper.ReportTexture(aReport.render_target_textures, "render-targets");
       helper.ReportTexture(aReport.texture_cache_textures, "texture-cache");
+      helper.ReportTexture(aReport.depth_target_textures, "depth-targets");
 
       FinishAsyncMemoryReport();
     },
     [](mozilla::ipc::ResponseRejectReason aReason) {
       FinishAsyncMemoryReport();
     }
   );
 
--- a/gfx/webrender/res/brush.glsl
+++ b/gfx/webrender/res/brush.glsl
@@ -17,16 +17,17 @@ void brush_vs(
 );
 
 #define VECS_PER_SEGMENT                    2
 
 #define BRUSH_FLAG_PERSPECTIVE_INTERPOLATION    1
 #define BRUSH_FLAG_SEGMENT_RELATIVE             2
 #define BRUSH_FLAG_SEGMENT_REPEAT_X             4
 #define BRUSH_FLAG_SEGMENT_REPEAT_Y             8
+#define BRUSH_FLAG_TEXEL_RECT                  16
 
 void main(void) {
     // Load the brush instance from vertex attributes.
     int prim_header_address = aData.x;
     int clip_address = aData.y;
     int segment_index = aData.z & 0xffff;
     int edge_flags = (aData.z >> 16) & 0xff;
     int brush_flags = (aData.z >> 24) & 0xff;
--- a/gfx/webrender/res/brush_image.glsl
+++ b/gfx/webrender/res/brush_image.glsl
@@ -46,17 +46,17 @@ void brush_vs(
     VertexInfo vi,
     int prim_address,
     RectWithSize prim_rect,
     RectWithSize segment_rect,
     ivec4 user_data,
     mat4 transform,
     PictureTask pic_task,
     int brush_flags,
-    vec4 texel_rect
+    vec4 segment_data
 ) {
     ImageBrushData image_data = fetch_image_data(prim_address);
 
     // If this is in WR_FEATURE_TEXTURE_RECT mode, the rect and size use
     // non-normalized texture coordinates.
 #ifdef WR_FEATURE_TEXTURE_RECT
     vec2 texture_size = vec2(1, 1);
 #else
@@ -71,29 +71,28 @@ void brush_vs(
     vec2 stretch_size = image_data.stretch_size;
 
     // If this segment should interpolate relative to the
     // segment, modify the parameters for that.
     if ((brush_flags & BRUSH_FLAG_SEGMENT_RELATIVE) != 0) {
         local_rect = segment_rect;
         stretch_size = local_rect.size;
 
-        // Note: Here we can assume that texels in device
-        //       space map to local space, due to how border-image
-        //       works. That assumption may not hold if this
-        //       is used for other purposes in the future.
         if ((brush_flags & BRUSH_FLAG_SEGMENT_REPEAT_X) != 0) {
-            stretch_size.x = (texel_rect.z - texel_rect.x) / pic_task.common_data.device_pixel_scale;
+            stretch_size.x = (segment_data.z - segment_data.x);
         }
         if ((brush_flags & BRUSH_FLAG_SEGMENT_REPEAT_Y) != 0) {
-            stretch_size.y = (texel_rect.w - texel_rect.y) / pic_task.common_data.device_pixel_scale;
+            stretch_size.y = (segment_data.w - segment_data.y);
         }
 
-        uv0 = res.uv_rect.p0 + texel_rect.xy;
-        uv1 = res.uv_rect.p0 + texel_rect.zw;
+        // If the extra data is a texel rect, modify the UVs.
+        if ((brush_flags & BRUSH_FLAG_TEXEL_RECT) != 0) {
+            uv0 = res.uv_rect.p0 + segment_data.xy;
+            uv1 = res.uv_rect.p0 + segment_data.zw;
+        }
     }
 
     vUv.z = res.layer;
 
     // Handle case where the UV coords are inverted (e.g. from an
     // external image).
     vec2 min_uv = min(uv0, uv1);
     vec2 max_uv = max(uv0, uv1);
--- a/gfx/webrender/res/ps_split_composite.glsl
+++ b/gfx/webrender/res/ps_split_composite.glsl
@@ -5,34 +5,31 @@
 #include shared,prim_shared
 
 varying vec3 vUv;
 flat varying vec4 vUvSampleBounds;
 
 #ifdef WR_VERTEX_SHADER
 struct SplitGeometry {
     vec2 local[4];
-    RectWithSize local_rect;
 };
 
 SplitGeometry fetch_split_geometry(int address) {
     ivec2 uv = get_gpu_cache_uv(address);
 
     vec4 data0 = TEXEL_FETCH(sGpuCache, uv, 0, ivec2(0, 0));
     vec4 data1 = TEXEL_FETCH(sGpuCache, uv, 0, ivec2(1, 0));
-    vec4 data2 = TEXEL_FETCH(sGpuCache, uv, 0, ivec2(2, 0));
 
     SplitGeometry geo;
     geo.local = vec2[4](
         data0.xy,
         data0.zw,
         data1.xy,
         data1.zw
     );
-    geo.local_rect = RectWithSize(data2.xy, data2.zw);
 
     return geo;
 }
 
 vec2 bilerp(vec2 a, vec2 b, vec2 c, vec2 d, float s, float t) {
     vec2 x = mix(a, b, t);
     vec2 y = mix(c, d, t);
     return mix(x, y, s);
@@ -93,17 +90,17 @@ void main(void) {
     vec2 min_uv = min(uv0, uv1);
     vec2 max_uv = max(uv0, uv1);
 
     vUvSampleBounds = vec4(
         min_uv + vec2(0.5),
         max_uv - vec2(0.5)
     ) / texture_size.xyxy;
 
-    vec2 f = (local_pos - geometry.local_rect.p0) / geometry.local_rect.size;
+    vec2 f = (local_pos - ph.local_rect.p0) / ph.local_rect.size;
 
     f = bilerp(
         extra_data.st_tl, extra_data.st_tr,
         extra_data.st_bl, extra_data.st_br,
         f.y, f.x
     );
     vec2 uv = mix(uv0, uv1, f);
 
--- a/gfx/webrender/src/batch.rs
+++ b/gfx/webrender/src/batch.rs
@@ -15,22 +15,23 @@ use gpu_types::{ClipMaskInstance, SplitC
 use gpu_types::{PrimitiveInstanceData, RasterizationSpace, GlyphInstance};
 use gpu_types::{PrimitiveHeader, PrimitiveHeaderIndex, TransformPaletteId, TransformPalette};
 use internal_types::{FastHashMap, SavedTargetIndex, TextureSource};
 use picture::{PictureCompositeMode, PicturePrimitive, PictureSurface};
 use plane_split::{BspSplitter, Clipper, Polygon, Splitter};
 use prim_store::{BrushKind, BrushPrimitive, BrushSegmentTaskId, DeferredResolve};
 use prim_store::{EdgeAaSegmentMask, ImageSource, PrimitiveIndex};
 use prim_store::{VisibleGradientTile, PrimitiveInstance};
-use prim_store::{BorderSource, Primitive, PrimitiveDetails};
+use prim_store::{BrushSegment, BorderSource, Primitive, PrimitiveDetails};
 use render_task::{RenderTaskAddress, RenderTaskId, RenderTaskTree};
 use renderer::{BlendMode, ImageBufferKind, ShaderColorMode};
 use renderer::BLOCKS_PER_UV_RECT;
 use resource_cache::{CacheItem, GlyphFetchResult, ImageRequest, ResourceCache, ImageProperties};
 use scene::FilterOpHelpers;
+use smallvec::SmallVec;
 use std::{f32, i32, usize};
 use tiling::{RenderTargetContext};
 use util::{MatrixHelpers, TransformedRectKind};
 
 // Special sentinel value recognized by the shader. It is considered to be
 // a dummy task that doesn't mask out anything.
 const OPAQUE_TASK_ADDRESS: RenderTaskAddress = RenderTaskAddress(0x7fff);
 
@@ -414,16 +415,25 @@ impl AlphaBatchContainer {
                     self.alpha_batches.push(other_batch);
                     min_batch_index = self.alpha_batches.len();
                 }
             }
         }
     }
 }
 
+/// Each segment can optionally specify a per-segment
+/// texture set and one user data field.
+#[derive(Debug, Copy, Clone)]
+struct SegmentInstanceData {
+    textures: BatchTextures,
+    user_data: i32,
+    is_opaque_override: Option<bool>,
+}
+
 /// Encapsulates the logic of building batches for items that are blended.
 pub struct AlphaBatchBuilder {
     pub batch_list: BatchList,
     glyph_fetch_buffer: Vec<GlyphFetchResult>,
     target_rect: DeviceIntRect,
     can_merge: bool,
 }
 
@@ -546,17 +556,16 @@ impl AlphaBatchBuilder {
                 transform.transform_point3d(&poly.points[0].cast()).unwrap(),
                 transform.transform_point3d(&poly.points[1].cast()).unwrap(),
                 transform.transform_point3d(&poly.points[2].cast()).unwrap(),
                 transform.transform_point3d(&poly.points[3].cast()).unwrap(),
             ];
             let gpu_blocks = [
                 [local_points[0].x, local_points[0].y, local_points[1].x, local_points[1].y].into(),
                 [local_points[2].x, local_points[2].y, local_points[3].x, local_points[3].y].into(),
-                pic_metadata.local_rect.into(),
             ];
 
             let gpu_handle = gpu_cache.push_per_frame_blocks(&gpu_blocks);
             let key = BatchKey::new(
                 BatchKind::SplitComposite,
                 BlendMode::PremultipliedAlpha,
                 BatchTextures::no_texture(),
             );
@@ -1098,47 +1107,39 @@ impl AlphaBatchBuilder {
                             clip_task_address,
                             gpu_cache,
                             &mut self.batch_list,
                             &prim_header,
                             prim_headers,
                         );
                     }
                     _ => {
-                        // TODO(gw): As an interim step, just return one value for the
-                        //           per-segment user data. In the future, this method
-                        //           will be expanded to optionally return a list of
-                        //           (BatchTextures, user_data) per segment, which will
-                        //           allow a different texture / render task to be used
-                        //           per segment.
-                        if let Some((batch_kind, textures, user_data, segment_user_data)) = brush.get_batch_params(
-                                ctx.resource_cache,
-                                gpu_cache,
-                                deferred_resolves,
-                                ctx.prim_store.chase_id == Some(prim_instance.prim_index),
+                        if let Some(params) = brush.get_batch_params(
+                            ctx.resource_cache,
+                            gpu_cache,
+                            deferred_resolves,
+                            ctx.prim_store.chase_id == Some(prim_instance.prim_index),
                         ) {
-                            let prim_header_index = prim_headers.push(&prim_header, user_data);
+                            let prim_header_index = prim_headers.push(&prim_header, params.prim_user_data);
                             if cfg!(debug_assertions) && ctx.prim_store.chase_id == Some(prim_instance.prim_index) {
                                 println!("\t{:?} {:?}, task relative bounds {:?}",
-                                    batch_kind, prim_header_index, bounding_rect);
+                                    params.batch_kind, prim_header_index, bounding_rect);
                             }
 
                             self.add_brush_to_batch(
                                 brush,
+                                &params,
                                 prim_instance,
-                                batch_kind,
                                 specified_blend_mode,
                                 non_segmented_blend_mode,
-                                textures,
                                 prim_header_index,
                                 clip_task_address,
                                 bounding_rect,
                                 transform_kind,
                                 render_tasks,
-                                segment_user_data,
                             );
                         }
                     }
                 }
             }
             PrimitiveDetails::TextRun(ref text_cpu) => {
                 let subpx_dir = text_cpu.used_font.get_subpx_dir();
 
@@ -1265,90 +1266,157 @@ impl AlphaBatchBuilder {
         self.batch_list.push_single_instance(
             batch_key,
             bounding_rect,
             prim_instance.prim_index,
             PrimitiveInstanceData::from(base_instance),
         );
     }
 
+    /// Add a single segment instance to a batch.
+    fn add_segment_to_batch(
+        &mut self,
+        segment: &BrushSegment,
+        segment_data: &SegmentInstanceData,
+        segment_index: i32,
+        batch_kind: BrushBatchKind,
+        prim_instance: &PrimitiveInstance,
+        prim_header_index: PrimitiveHeaderIndex,
+        alpha_blend_mode: BlendMode,
+        bounding_rect: &WorldRect,
+        transform_kind: TransformedRectKind,
+        render_tasks: &RenderTaskTree,
+    ) {
+        let clip_task_address = match segment.clip_task_id {
+            BrushSegmentTaskId::RenderTaskId(id) =>
+                render_tasks.get_task_address(id),
+            BrushSegmentTaskId::Opaque => OPAQUE_TASK_ADDRESS,
+            BrushSegmentTaskId::Empty => return,
+        };
+
+        // If the segment instance data specifies opacity for that
+        // segment, use it. Otherwise, assume opacity for the segment
+        // from the overall primitive opacity.
+        let is_segment_opaque = match segment_data.is_opaque_override {
+            Some(is_opaque) => is_opaque,
+            None => prim_instance.opacity.is_opaque,
+        };
+
+        let is_inner = segment.edge_flags.is_empty();
+        let needs_blending = !is_segment_opaque ||
+                             segment.clip_task_id.needs_blending() ||
+                             (!is_inner && transform_kind == TransformedRectKind::Complex);
+
+        let instance = PrimitiveInstanceData::from(BrushInstance {
+            segment_index,
+            edge_flags: segment.edge_flags,
+            clip_task_address,
+            brush_flags: BrushFlags::PERSPECTIVE_INTERPOLATION | segment.brush_flags,
+            prim_header_index,
+            user_data: segment_data.user_data,
+        });
+
+        let batch_key = BatchKey {
+            blend_mode: if needs_blending { alpha_blend_mode } else { BlendMode::None },
+            kind: BatchKind::Brush(batch_kind),
+            textures: segment_data.textures,
+        };
+
+        self.batch_list.push_single_instance(
+            batch_key,
+            bounding_rect,
+            prim_instance.prim_index,
+            instance,
+        );
+    }
+
+    /// Add any segment(s) from a brush to batches.
     fn add_brush_to_batch(
         &mut self,
         brush: &BrushPrimitive,
+        params: &BrushBatchParameters,
         prim_instance: &PrimitiveInstance,
-        batch_kind: BrushBatchKind,
         alpha_blend_mode: BlendMode,
         non_segmented_blend_mode: BlendMode,
-        textures: BatchTextures,
         prim_header_index: PrimitiveHeaderIndex,
         clip_task_address: RenderTaskAddress,
         bounding_rect: &WorldRect,
         transform_kind: TransformedRectKind,
         render_tasks: &RenderTaskTree,
-        user_data: i32,
     ) {
-        let base_instance = BrushInstance {
-            prim_header_index,
-            clip_task_address,
-            segment_index: 0,
-            edge_flags: EdgeAaSegmentMask::all(),
-            brush_flags: BrushFlags::PERSPECTIVE_INTERPOLATION,
-            user_data,
-        };
-
-        match brush.segment_desc {
-            Some(ref segment_desc) => {
-                for (i, segment) in segment_desc.segments.iter().enumerate() {
-                    let is_inner = segment.edge_flags.is_empty();
-                    let needs_blending = !prim_instance.opacity.is_opaque ||
-                                         segment.clip_task_id.needs_blending() ||
-                                         (!is_inner && transform_kind == TransformedRectKind::Complex);
-
-                    let clip_task_address = match segment.clip_task_id {
-                        BrushSegmentTaskId::RenderTaskId(id) =>
-                            render_tasks.get_task_address(id),
-                        BrushSegmentTaskId::Opaque => OPAQUE_TASK_ADDRESS,
-                        BrushSegmentTaskId::Empty => continue,
-                    };
-
-                    let instance = PrimitiveInstanceData::from(BrushInstance {
-                        segment_index: i as i32,
-                        edge_flags: segment.edge_flags,
-                        clip_task_address,
-                        brush_flags: base_instance.brush_flags | segment.brush_flags,
-                        ..base_instance
-                    });
-
-                    let batch_key = BatchKey {
-                        blend_mode: if needs_blending { alpha_blend_mode } else { BlendMode::None },
-                        kind: BatchKind::Brush(batch_kind),
-                        textures,
-                    };
-
-                    self.batch_list.push_single_instance(
-                        batch_key,
+        match (&brush.segment_desc, &params.segment_data) {
+            (Some(ref segment_desc), SegmentDataKind::Instanced(ref segment_data)) => {
+                // In this case, we have both a list of segments, and a list of
+                // per-segment instance data. Zip them together to build batches.
+                debug_assert_eq!(segment_desc.segments.len(), segment_data.len());
+                for (segment_index, (segment, segment_data)) in segment_desc.segments
+                    .iter()
+                    .zip(segment_data.iter())
+                    .enumerate() {
+                    self.add_segment_to_batch(
+                        segment,
+                        segment_data,
+                        segment_index as i32,
+                        params.batch_kind,
+                        prim_instance,
+                        prim_header_index,
+                        alpha_blend_mode,
                         bounding_rect,
-                        prim_instance.prim_index,
-                        instance,
+                        transform_kind,
+                        render_tasks,
                     );
                 }
             }
-            None => {
+            (Some(ref segment_desc), SegmentDataKind::Shared(ref segment_data)) => {
+                // A list of segments, but the per-segment data is common
+                // between all segments.
+                for (segment_index, segment) in segment_desc.segments
+                    .iter()
+                    .enumerate() {
+                    self.add_segment_to_batch(
+                        segment,
+                        segment_data,
+                        segment_index as i32,
+                        params.batch_kind,
+                        prim_instance,
+                        prim_header_index,
+                        alpha_blend_mode,
+                        bounding_rect,
+                        transform_kind,
+                        render_tasks,
+                    );
+                }
+            }
+            (None, SegmentDataKind::Shared(ref segment_data)) => {
+                // No segments, and thus no per-segment instance data.
                 let batch_key = BatchKey {
                     blend_mode: non_segmented_blend_mode,
-                    kind: BatchKind::Brush(batch_kind),
-                    textures,
+                    kind: BatchKind::Brush(params.batch_kind),
+                    textures: segment_data.textures,
                 };
+                let instance = PrimitiveInstanceData::from(BrushInstance {
+                    segment_index: 0,
+                    edge_flags: EdgeAaSegmentMask::all(),
+                    clip_task_address,
+                    brush_flags: BrushFlags::PERSPECTIVE_INTERPOLATION,
+                    prim_header_index,
+                    user_data: segment_data.user_data,
+                });
                 self.batch_list.push_single_instance(
                     batch_key,
                     bounding_rect,
                     prim_instance.prim_index,
-                    PrimitiveInstanceData::from(base_instance),
+                    PrimitiveInstanceData::from(instance),
                 );
             }
+            (None, SegmentDataKind::Instanced(..)) => {
+                // We should never hit the case where there are no segments,
+                // but a list of segment instance data.
+                unreachable!();
+            }
         }
     }
 }
 
 fn add_gradient_tiles(
     prim_instance: &PrimitiveInstance,
     visible_tiles: &[VisibleGradientTile],
     stops_handle: &GpuCacheHandle,
@@ -1421,24 +1489,76 @@ fn get_image_tile_params(
                 RasterizationSpace::Local as i32,
                 0,
             ],
             gpu_cache.get_address(&cache_item.uv_rect_handle),
         ))
     }
 }
 
+/// Either a single texture / user data for all segments,
+/// or a list of one per segment.
+enum SegmentDataKind {
+    Shared(SegmentInstanceData),
+    Instanced(SmallVec<[SegmentInstanceData; 8]>),
+}
+
+/// The parameters that are specific to a kind of brush,
+/// used by the common method to add a brush to batches.
+struct BrushBatchParameters {
+    batch_kind: BrushBatchKind,
+    prim_user_data: [i32; 3],
+    segment_data: SegmentDataKind,
+}
+
+impl BrushBatchParameters {
+    /// This brush instance has a list of per-segment
+    /// instance data.
+    fn instanced(
+        batch_kind: BrushBatchKind,
+        prim_user_data: [i32; 3],
+        segment_data: SmallVec<[SegmentInstanceData; 8]>,
+    ) -> Self {
+        BrushBatchParameters {
+            batch_kind,
+            prim_user_data,
+            segment_data: SegmentDataKind::Instanced(segment_data),
+        }
+    }
+
+    /// This brush instance shares the per-segment data
+    /// across all segments.
+    fn shared(
+        batch_kind: BrushBatchKind,
+        textures: BatchTextures,
+        prim_user_data: [i32; 3],
+        segment_user_data: i32,
+    ) -> Self {
+        BrushBatchParameters {
+            batch_kind,
+            prim_user_data,
+            segment_data: SegmentDataKind::Shared(
+                SegmentInstanceData {
+                    textures,
+                    user_data: segment_user_data,
+                    is_opaque_override: None,
+                }
+            ),
+        }
+    }
+}
+
 impl BrushPrimitive {
     fn get_batch_params(
         &self,
         resource_cache: &ResourceCache,
         gpu_cache: &mut GpuCache,
         deferred_resolves: &mut Vec<DeferredResolve>,
         is_chased: bool,
-    ) -> Option<(BrushBatchKind, BatchTextures, [i32; 3], i32)> {
+    ) -> Option<BrushBatchParameters> {
         match self.kind {
             BrushKind::Image { request, ref source, .. } => {
                 let cache_item = match *source {
                     ImageSource::Default => {
                         resolve_image(
                             request,
                             resource_cache,
                             gpu_cache,
@@ -1458,129 +1578,150 @@ impl BrushPrimitive {
                     println!("\tsource {:?}", cache_item);
                 }
 
                 if cache_item.texture_id == TextureSource::Invalid {
                     None
                 } else {
                     let textures = BatchTextures::color(cache_item.texture_id);
 
-                    Some((
+                    Some(BrushBatchParameters::shared(
                         BrushBatchKind::Image(get_buffer_kind(cache_item.texture_id)),
                         textures,
                         [
                             ShaderColorMode::Image as i32,
                             RasterizationSpace::Local as i32,
                             0,
                         ],
                         cache_item.uv_rect_handle.as_int(gpu_cache),
                     ))
                 }
             }
             BrushKind::LineDecoration { ref handle, style, .. } => {
                 match style {
                     LineStyle::Solid => {
-                        Some((
+                        Some(BrushBatchParameters::shared(
                             BrushBatchKind::Solid,
                             BatchTextures::no_texture(),
                             [0; 3],
                             0,
                         ))
                     }
                     LineStyle::Dotted |
                     LineStyle::Dashed |
                     LineStyle::Wavy => {
                         let rt_cache_entry = resource_cache
                             .get_cached_render_task(handle.as_ref().unwrap());
                         let cache_item = resource_cache.get_texture_cache_item(&rt_cache_entry.handle);
                         let textures = BatchTextures::color(cache_item.texture_id);
-                        Some((
+                        Some(BrushBatchParameters::shared(
                             BrushBatchKind::Image(get_buffer_kind(cache_item.texture_id)),
                             textures,
                             [
                                 ShaderColorMode::Image as i32,
                                 RasterizationSpace::Local as i32,
                                 0,
                             ],
                             cache_item.uv_rect_handle.as_int(gpu_cache),
                         ))
                     }
                 }
             }
             BrushKind::Border { ref source, .. } => {
-                let cache_item = match *source {
+                match *source {
                     BorderSource::Image(request) => {
-                        resolve_image(
+                        let cache_item = resolve_image(
                             request,
                             resource_cache,
                             gpu_cache,
                             deferred_resolves,
-                        )
+                        );
+
+                        if cache_item.texture_id == TextureSource::Invalid {
+                            return None;
+                        }
+
+                        let textures = BatchTextures::color(cache_item.texture_id);
+
+                        Some(BrushBatchParameters::shared(
+                            BrushBatchKind::Image(get_buffer_kind(cache_item.texture_id)),
+                            textures,
+                            [
+                                ShaderColorMode::Image as i32,
+                                RasterizationSpace::Local as i32,
+                                0,
+                            ],
+                            cache_item.uv_rect_handle.as_int(gpu_cache),
+                        ))
                     }
-                    BorderSource::Border { ref handle, .. } => {
-                        let rt_handle = match *handle {
-                            Some(ref handle) => handle,
-                            None => return None,
-                        };
-                        let rt_cache_entry = resource_cache
-                            .get_cached_render_task(rt_handle);
-                        resource_cache.get_texture_cache_item(&rt_cache_entry.handle)
-                    }
-                };
+                    BorderSource::Border { ref segments, .. } => {
+                        let mut segment_data = SmallVec::new();
+
+                        // Collect the segment instance data from each render
+                        // task for each valid edge / corner of the border.
 
-                if cache_item.texture_id == TextureSource::Invalid {
-                    None
-                } else {
-                    let textures = BatchTextures::color(cache_item.texture_id);
+                        for segment in segments {
+                            let rt_cache_entry = resource_cache
+                                .get_cached_render_task(segment.handle.as_ref().unwrap());
+                            let cache_item = resource_cache
+                                .get_texture_cache_item(&rt_cache_entry.handle);
+                            segment_data.push(
+                                SegmentInstanceData {
+                                    textures: BatchTextures::color(cache_item.texture_id),
+                                    user_data: cache_item.uv_rect_handle.as_int(gpu_cache),
+                                    is_opaque_override: Some(segment.is_opaque),
+                                }
+                            );
+                        }
 
-                    Some((
-                        BrushBatchKind::Image(get_buffer_kind(cache_item.texture_id)),
-                        textures,
-                        [
-                            ShaderColorMode::Image as i32,
-                            RasterizationSpace::Local as i32,
-                            0,
-                        ],
-                        cache_item.uv_rect_handle.as_int(gpu_cache),
-                    ))
+                        Some(BrushBatchParameters::instanced(
+                            BrushBatchKind::Image(ImageBufferKind::Texture2DArray),
+                            [
+                                ShaderColorMode::Image as i32,
+                                RasterizationSpace::Local as i32,
+                                0,
+                            ],
+                            segment_data,
+                        ))
+                    }
                 }
             }
             BrushKind::Picture { .. } => {
                 panic!("bug: get_batch_key is handled at higher level for pictures");
             }
             BrushKind::Solid { .. } => {
-                Some((
+                Some(BrushBatchParameters::shared(
                     BrushBatchKind::Solid,
                     BatchTextures::no_texture(),
                     [0; 3],
                     0,
                 ))
             }
             BrushKind::Clear => {
-                Some((
+                Some(BrushBatchParameters::shared(
                     BrushBatchKind::Solid,
                     BatchTextures::no_texture(),
                     [0; 3],
                     0,
                 ))
             }
             BrushKind::RadialGradient { ref stops_handle, .. } => {
-                Some((
+                Some(BrushBatchParameters::shared(
                     BrushBatchKind::RadialGradient,
                     BatchTextures::no_texture(),
                     [
                         stops_handle.as_int(gpu_cache),
                         0,
                         0,
                     ],
                     0,
                 ))
             }
             BrushKind::LinearGradient { ref stops_handle, .. } => {
-                Some((
+                Some(BrushBatchParameters::shared(
                     BrushBatchKind::LinearGradient,
                     BatchTextures::no_texture(),
                     [
                         stops_handle.as_int(gpu_cache),
                         0,
                         0,
                     ],
                     0,
@@ -1626,17 +1767,17 @@ impl BrushPrimitive {
 
                 let kind = BrushBatchKind::YuvImage(
                     buffer_kind,
                     format,
                     color_depth,
                     color_space,
                 );
 
-                Some((
+                Some(BrushBatchParameters::shared(
                     kind,
                     textures,
                     [
                         uv_rect_addresses[0],
                         uv_rect_addresses[1],
                         uv_rect_addresses[2],
                     ],
                     0,
--- a/gfx/webrender/src/border.rs
+++ b/gfx/webrender/src/border.rs
@@ -1,23 +1,23 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 use api::{BorderRadius, BorderSide, BorderStyle, ColorF, ColorU, DeviceRect, DeviceSize};
 use api::{LayoutSideOffsets, LayoutSizeAu, LayoutPrimitiveInfo, LayoutToDeviceScale};
-use api::{DeviceVector2D, DevicePoint, DeviceIntSize, LayoutRect, LayoutSize, NormalBorder};
+use api::{DeviceVector2D, DevicePoint, LayoutRect, LayoutSize, NormalBorder, DeviceIntSize};
 use api::{AuHelpers};
-use app_units::Au;
 use ellipse::Ellipse;
-use euclid::SideOffsets2D;
 use display_list_flattener::DisplayListFlattener;
 use gpu_types::{BorderInstance, BorderSegment, BrushFlags};
-use prim_store::{BrushKind, BrushPrimitive, BrushSegment, BrushSegmentVec};
-use prim_store::{EdgeAaSegmentMask, PrimitiveContainer, ScrollNodeAndClipChain};
+use prim_store::{BorderSegmentInfo, BrushKind, BrushPrimitive, BrushSegment, BrushSegmentVec};
+use prim_store::{EdgeAaSegmentMask, PrimitiveContainer, ScrollNodeAndClipChain, BrushSegmentDescriptor};
+use render_task::{RenderTaskCacheKey, RenderTaskCacheKeyKind};
+use smallvec::SmallVec;
 use util::{lerp, RectHelpers};
 
 // Using 2048 as the maximum radius in device space before which we
 // start stretching is up for debate.
 // the value must be chosen so that the corners will not use an
 // unreasonable amount of memory but should allow crisp corners in the
 // common cases.
 
@@ -88,48 +88,40 @@ impl From<BorderSide> for BorderSideAu {
     fn from(side: BorderSide) -> Self {
         BorderSideAu {
             color: side.color.into(),
             style: side.style,
         }
     }
 }
 
+/// Cache key that uniquely identifies a border
+/// edge in the render task cache.
 #[derive(Clone, Debug, Hash, PartialEq, Eq)]
 #[cfg_attr(feature = "capture", derive(Serialize))]
 #[cfg_attr(feature = "replay", derive(Deserialize))]
-pub struct BorderCacheKey {
-    pub left: BorderSideAu,
-    pub right: BorderSideAu,
-    pub top: BorderSideAu,
-    pub bottom: BorderSideAu,
-    pub radius: BorderRadiusAu,
-    pub widths: SideOffsets2D<Au>,
+pub struct BorderEdgeCacheKey {
+    pub side: BorderSideAu,
+    pub size: LayoutSizeAu,
     pub do_aa: bool,
-    pub scale: Au,
+    pub segment: BorderSegment,
 }
 
-impl BorderCacheKey {
-    pub fn new(border: &NormalBorder, widths: &LayoutSideOffsets) -> Self {
-        BorderCacheKey {
-            left: border.left.into(),
-            top: border.top.into(),
-            right: border.right.into(),
-            bottom: border.bottom.into(),
-            widths: SideOffsets2D::new(
-                Au::from_f32_px(widths.top),
-                Au::from_f32_px(widths.right),
-                Au::from_f32_px(widths.bottom),
-                Au::from_f32_px(widths.left),
-            ),
-            radius: border.radius.into(),
-            do_aa: border.do_aa,
-            scale: Au(0),
-        }
-    }
+/// Cache key that uniquely identifies a border
+/// corner in the render task cache.
+#[derive(Clone, Debug, Hash, PartialEq, Eq)]
+#[cfg_attr(feature = "capture", derive(Serialize))]
+#[cfg_attr(feature = "replay", derive(Deserialize))]
+pub struct BorderCornerCacheKey {
+    pub widths: LayoutSizeAu,
+    pub radius: LayoutSizeAu,
+    pub side0: BorderSideAu,
+    pub side1: BorderSideAu,
+    pub segment: BorderSegment,
+    pub do_aa: bool,
 }
 
 pub fn ensure_no_corner_overlap(
     radius: &mut BorderRadius,
     rect: &LayoutRect,
 ) {
     let mut ratio = 1.0;
     let top_left_radius = &mut radius.top_left;
@@ -172,38 +164,40 @@ pub fn ensure_no_corner_overlap(
     }
 }
 
 impl<'a> DisplayListFlattener<'a> {
     pub fn add_normal_border(
         &mut self,
         info: &LayoutPrimitiveInfo,
         border: &NormalBorder,
-        widths: &LayoutSideOffsets,
+        widths: LayoutSideOffsets,
         clip_and_scroll: ScrollNodeAndClipChain,
     ) {
         let mut border = *border;
         ensure_no_corner_overlap(&mut border.radius, &info.rect);
 
-        let prim = BrushPrimitive::new(
-            BrushKind::new_border(border, *widths),
-            None,
+        let prim = create_normal_border_prim(
+            &info.rect,
+            border,
+            widths,
         );
 
         self.add_primitive(
             clip_and_scroll,
             info,
             Vec::new(),
             PrimitiveContainer::Brush(prim),
         );
     }
 }
 
 pub trait BorderSideHelpers {
     fn border_color(&self, is_inner_border: bool) -> ColorF;
+    fn is_opaque(&self) -> bool;
 }
 
 impl BorderSideHelpers for BorderSide {
     fn border_color(&self, is_inner_border: bool) -> ColorF {
         let lighter = match self.style {
             BorderStyle::Inset => is_inner_border,
             BorderStyle::Outset => !is_inner_border,
             _ => return self.color,
@@ -222,16 +216,21 @@ impl BorderSideHelpers for BorderSide {
         if self.color.r != 0.0 || self.color.g != 0.0 || self.color.b != 0.0 {
             let scale = if lighter { 1.0 } else { 2.0 / 3.0 };
             return self.color.scale_rgb(scale)
         }
 
         let black = if lighter { 0.7 } else { 0.3 };
         ColorF::new(black, black, black, self.color.a)
     }
+
+    /// Returns true if all pixels in this border style are opaque.
+    fn is_opaque(&self) -> bool {
+        self.color.a >= 1.0 && self.style.is_opaque()
+    }
 }
 
 /// The kind of border corner clip.
 #[repr(C)]
 #[derive(Copy, Debug, Clone, PartialEq)]
 pub enum BorderClipKind {
     DashCorner = 1,
     DashEdge = 2,
@@ -512,109 +511,37 @@ struct DotInfo {
 }
 
 impl DotInfo {
     fn new(arc_pos: f32, diameter: f32) -> DotInfo {
         DotInfo { arc_pos, diameter }
     }
 }
 
-#[derive(Debug)]
-pub struct BorderSegmentInfo {
-    task_rect: DeviceRect,
-    segment: BorderSegment,
-    radius: DeviceSize,
-    widths: DeviceSize,
-}
-
-bitflags! {
-    /// Whether we depend on the available size for the border (effectively in
-    /// the local rect of the primitive), and in which direction.
-    ///
-    /// Note that this relies on the corners being only dependent on the border
-    /// widths and radius.
-    ///
-    /// This is not just a single boolean to allow instance caching for border
-    /// boxes where one of the directions differ but not the one on the affected
-    /// border is.
-    ///
-    /// This allows sharing instances for stuff like paragraphs of different
-    /// heights separated by horizontal borders.
-    pub struct AvailableSizeDependence : u8 {
-        /// There's a dependence on the vertical direction, that is, at least
-        /// one of the right or left edges is dashed or dotted.
-        const VERTICAL = 1 << 0;
-        /// Same but for the horizontal direction.
-        const HORIZONTAL = 1 << 1;
-    }
-}
-
-/// This is the data that describes a single border with (up to) four sides and
-/// four corners.
-///
-/// This object gets created for each border primitive at least once. Note,
-/// however, that the instances this produces via `build_instances()` can and
-/// will be shared by multiple borders, as long as they share the same cache
-/// key.
-///
-/// Segments, however, also get build once per primitive.
-///
-/// So the important invariant to preserve when going through this code is that
-/// the result of `build_instances()` would remain invariant for a given cache
-/// key.
-///
-/// That means, then, that `border_segments` can't depend at all on something
-/// that isn't on the key like the available_size, while the brush segments can
-/// (and will, since we skip painting empty segments caused by things like edges
-/// getting constrained by huge border-radius).
-///
-/// Note that the cache key is not only `BorderCacheKey`, but also a
-/// `size` from `RenderTaskCacheKey`, which will always be zero unless
-/// `available_size_dependence` is non-empty, which is effectively just dashed
-/// and dotted borders for now, since the spacing between the dash and dots
-/// changes depending on that size.
-#[derive(Debug)]
-pub struct BorderRenderTaskInfo {
-    pub border_segments: Vec<BorderSegmentInfo>,
-    pub size: DeviceIntSize,
-    pub available_size_dependence: AvailableSizeDependence,
-    do_aa: bool,
-}
-
-#[derive(PartialEq, Eq)]
-enum DependsOnAvailableSize {
-    No,
-    Yes,
-}
-
 /// Information needed to place and draw a border edge.
 #[derive(Debug)]
 struct EdgeInfo {
     /// Offset in local space to place the edge from origin.
     local_offset: f32,
     /// Size of the edge in local space.
     local_size: f32,
-    /// Size in device pixels needed in the render task.
-    device_size: f32,
-    /// Whether this edge depends on the available size.
-    depends_on_available_size: bool,
+    /// Local stretch size for this edge (repeat past this).
+    stretch_size: f32,
 }
 
 impl EdgeInfo {
     fn new(
         local_offset: f32,
         local_size: f32,
-        device_size: f32,
-        depends_on_avail_size: DependsOnAvailableSize,
+        stretch_size: f32,
     ) -> Self {
         Self {
             local_offset,
             local_size,
-            device_size,
-            depends_on_available_size: depends_on_avail_size == DependsOnAvailableSize::Yes,
+            stretch_size,
         }
     }
 }
 
 // Given a side width and the available space, compute the half-dash (half of
 // the 'on' segment) and the count of them for a given segment.
 fn compute_half_dash(side_width: f32, total_size: f32) -> (f32, u32) {
     let half_dash = side_width * 1.5;
@@ -640,458 +567,254 @@ fn compute_half_dash(side_width: f32, to
 
 // Get the needed size in device pixels for an edge,
 // based on the border style of that edge. This is used
 // to determine how big the render task should be.
 fn get_edge_info(
     style: BorderStyle,
     side_width: f32,
     avail_size: f32,
-    scale: f32,
 ) -> EdgeInfo {
     // To avoid division by zero below.
-    if side_width <= 0.0 {
-        return EdgeInfo::new(0.0, 0.0, 0.0, DependsOnAvailableSize::No);
+    if side_width <= 0.0 || avail_size <= 0.0 {
+        return EdgeInfo::new(0.0, 0.0, 0.0);
     }
 
     match style {
         BorderStyle::Dashed => {
             // Basically, two times the dash size.
             let (half_dash, _num_half_dashes) =
                 compute_half_dash(side_width, avail_size);
-            let device_size = (2.0 * 2.0 * half_dash * scale).round();
-            EdgeInfo::new(0., avail_size, device_size, DependsOnAvailableSize::Yes)
+            let stretch_size = 2.0 * 2.0 * half_dash;
+            EdgeInfo::new(0., avail_size, stretch_size)
         }
         BorderStyle::Dotted => {
             let dot_and_space_size = 2.0 * side_width;
             if avail_size < dot_and_space_size * 0.75 {
-                return EdgeInfo::new(0.0, 0.0, 0.0, DependsOnAvailableSize::Yes);
+                return EdgeInfo::new(0.0, 0.0, 0.0);
             }
             let approx_dot_count = avail_size / dot_and_space_size;
             let dot_count = approx_dot_count.floor().max(1.0);
             let used_size = dot_count * dot_and_space_size;
             let extra_space = avail_size - used_size;
-            let device_size = dot_and_space_size * scale;
+            let stretch_size = dot_and_space_size;
             let offset = (extra_space * 0.5).round();
-            EdgeInfo::new(offset, used_size, device_size, DependsOnAvailableSize::Yes)
+            EdgeInfo::new(offset, used_size, stretch_size)
         }
         _ => {
-            EdgeInfo::new(0.0, avail_size, 8.0, DependsOnAvailableSize::No)
+            EdgeInfo::new(0.0, avail_size, 8.0)
         }
     }
 }
 
-impl BorderRenderTaskInfo {
-    pub fn new(
-        rect: &LayoutRect,
-        border: &NormalBorder,
-        widths: &LayoutSideOffsets,
-        scale: LayoutToDeviceScale,
-        brush_segments: &mut BrushSegmentVec,
-    ) -> Option<Self> {
-        let mut border_segments = Vec::new();
-
-        let dp_width_top = (widths.top * scale.0).ceil();
-        let dp_width_bottom = (widths.bottom * scale.0).ceil();
-        let dp_width_left = (widths.left * scale.0).ceil();
-        let dp_width_right = (widths.right * scale.0).ceil();
-
-        let dp_corner_tl = (border.radius.top_left * scale).ceil();
-        let dp_corner_tr = (border.radius.top_right * scale).ceil();
-        let dp_corner_bl = (border.radius.bottom_left * scale).ceil();
-        let dp_corner_br = (border.radius.bottom_right * scale).ceil();
-
-        let dp_size_tl = DeviceSize::new(
-            dp_corner_tl.width.max(dp_width_left),
-            dp_corner_tl.height.max(dp_width_top),
-        );
-        let dp_size_tr = DeviceSize::new(
-            dp_corner_tr.width.max(dp_width_right),
-            dp_corner_tr.height.max(dp_width_top),
-        );
-        let dp_size_br = DeviceSize::new(
-            dp_corner_br.width.max(dp_width_right),
-            dp_corner_br.height.max(dp_width_bottom),
-        );
-        let dp_size_bl = DeviceSize::new(
-            dp_corner_bl.width.max(dp_width_left),
-            dp_corner_bl.height.max(dp_width_bottom),
-        );
-
-        let local_size_tl = LayoutSize::new(
-            border.radius.top_left.width.max(widths.left),
-            border.radius.top_left.height.max(widths.top),
-        );
-        let local_size_tr = LayoutSize::new(
-            border.radius.top_right.width.max(widths.right),
-            border.radius.top_right.height.max(widths.top),
-        );
-        let local_size_br = LayoutSize::new(
-            border.radius.bottom_right.width.max(widths.right),
-            border.radius.bottom_right.height.max(widths.bottom),
-        );
-        let local_size_bl = LayoutSize::new(
-            border.radius.bottom_left.width.max(widths.left),
-            border.radius.bottom_left.height.max(widths.bottom),
-        );
-
-        let top_edge_info = get_edge_info(
-            border.top.style,
-            widths.top,
-            rect.size.width - local_size_tl.width - local_size_tr.width,
-            scale.0,
-        );
-        let bottom_edge_info = get_edge_info(
-            border.bottom.style,
-            widths.bottom,
-            rect.size.width - local_size_bl.width - local_size_br.width,
-            scale.0,
-        );
-        let inner_width = top_edge_info.device_size.max(bottom_edge_info.device_size).ceil();
-
-        let left_edge_info = get_edge_info(
-            border.left.style,
-            widths.left,
-            rect.size.height - local_size_tl.height - local_size_bl.height,
-            scale.0,
-        );
-        let right_edge_info = get_edge_info(
-            border.right.style,
-            widths.right,
-            rect.size.height - local_size_tr.height - local_size_br.height,
-            scale.0,
-        );
-
-        let inner_height = left_edge_info.device_size.max(right_edge_info.device_size).ceil();
-
-        let size = DeviceSize::new(
-            dp_size_tl.width.max(dp_size_bl.width) + inner_width + dp_size_tr.width.max(dp_size_br.width),
-            dp_size_tl.height.max(dp_size_tr.height) + inner_height + dp_size_bl.height.max(dp_size_br.height),
-        );
-
-        if size.width == 0.0 || size.height == 0.0 {
-            return None;
-        }
+/// Create the set of border segments and render task
+/// cache keys for a given CSS border.
+fn create_border_segments(
+    rect: &LayoutRect,
+    border: &NormalBorder,
+    widths: &LayoutSideOffsets,
+    border_segments: &mut SmallVec<[BorderSegmentInfo; 8]>,
+    brush_segments: &mut BrushSegmentVec,
+) {
+    let local_size_tl = LayoutSize::new(
+        border.radius.top_left.width.max(widths.left),
+        border.radius.top_left.height.max(widths.top),
+    );
+    let local_size_tr = LayoutSize::new(
+        border.radius.top_right.width.max(widths.right),
+        border.radius.top_right.height.max(widths.top),
+    );
+    let local_size_br = LayoutSize::new(
+        border.radius.bottom_right.width.max(widths.right),
+        border.radius.bottom_right.height.max(widths.bottom),
+    );
+    let local_size_bl = LayoutSize::new(
+        border.radius.bottom_left.width.max(widths.left),
+        border.radius.bottom_left.height.max(widths.bottom),
+    );
 
-        let mut size_dependence = AvailableSizeDependence::empty();
-        if top_edge_info.depends_on_available_size ||
-            bottom_edge_info.depends_on_available_size
-        {
-            size_dependence.insert(AvailableSizeDependence::HORIZONTAL);
-        }
-
-        if left_edge_info.depends_on_available_size ||
-            right_edge_info.depends_on_available_size
-        {
-            size_dependence.insert(AvailableSizeDependence::VERTICAL);
-        }
-
-        add_edge_segment(
-            LayoutRect::from_floats(
-                rect.origin.x,
-                rect.origin.y + local_size_tl.height + left_edge_info.local_offset,
-                rect.origin.x + widths.left,
-                rect.origin.y + local_size_tl.height + left_edge_info.local_offset + left_edge_info.local_size,
-            ),
-            DeviceRect::from_floats(
-                0.0,
-                dp_size_tl.height,
-                dp_width_left,
-                dp_size_tl.height + left_edge_info.device_size,
-            ),
-            &border.left,
-            BorderSegment::Left,
-            EdgeAaSegmentMask::LEFT | EdgeAaSegmentMask::RIGHT,
-            &mut border_segments,
-            BrushFlags::SEGMENT_RELATIVE | BrushFlags::SEGMENT_REPEAT_Y,
-            brush_segments,
-        );
+    let top_edge_info = get_edge_info(
+        border.top.style,
+        widths.top,
+        rect.size.width - local_size_tl.width - local_size_tr.width,
+    );
+    let bottom_edge_info = get_edge_info(
+        border.bottom.style,
+        widths.bottom,
+        rect.size.width - local_size_bl.width - local_size_br.width,
+    );
 
-        add_edge_segment(
-            LayoutRect::from_floats(
-                rect.origin.x + local_size_tl.width + top_edge_info.local_offset,
-                rect.origin.y,
-                rect.origin.x + local_size_tl.width + top_edge_info.local_offset + top_edge_info.local_size,
-                rect.origin.y + widths.top,
-            ),
-            DeviceRect::from_floats(
-                dp_size_tl.width,
-                0.0,
-                dp_size_tl.width + top_edge_info.device_size,
-                dp_width_top,
-            ),
-            &border.top,
-            BorderSegment::Top,
-            EdgeAaSegmentMask::TOP | EdgeAaSegmentMask::BOTTOM,
-            &mut border_segments,
-            BrushFlags::SEGMENT_RELATIVE | BrushFlags::SEGMENT_REPEAT_X,
-            brush_segments,
-        );
-
-        add_edge_segment(
-            LayoutRect::from_floats(
-                rect.origin.x + rect.size.width - widths.right,
-                rect.origin.y + local_size_tr.height + right_edge_info.local_offset,
-                rect.origin.x + rect.size.width,
-                rect.origin.y + local_size_tr.height + right_edge_info.local_offset + right_edge_info.local_size,
-            ),
-            DeviceRect::from_floats(
-                size.width - dp_width_right,
-                dp_size_tr.height,
-                size.width,
-                dp_size_tr.height + right_edge_info.device_size,
-            ),
-            &border.right,
-            BorderSegment::Right,
-            EdgeAaSegmentMask::RIGHT | EdgeAaSegmentMask::LEFT,
-            &mut border_segments,
-            BrushFlags::SEGMENT_RELATIVE | BrushFlags::SEGMENT_REPEAT_Y,
-            brush_segments,
-        );
-
-        add_edge_segment(
-            LayoutRect::from_floats(
-                rect.origin.x + local_size_bl.width + bottom_edge_info.local_offset,
-                rect.origin.y + rect.size.height - widths.bottom,
-                rect.origin.x + local_size_bl.width + bottom_edge_info.local_offset + bottom_edge_info.local_size,
-                rect.origin.y + rect.size.height,
-            ),
-            DeviceRect::from_floats(
-                dp_size_bl.width,
-                size.height - dp_width_bottom,
-                dp_size_bl.width + bottom_edge_info.device_size,
-                size.height,
-            ),
-            &border.bottom,
-            BorderSegment::Bottom,
-            EdgeAaSegmentMask::BOTTOM | EdgeAaSegmentMask::TOP,
-            &mut border_segments,
-            BrushFlags::SEGMENT_RELATIVE | BrushFlags::SEGMENT_REPEAT_X,
-            brush_segments,
-        );
+    let left_edge_info = get_edge_info(
+        border.left.style,
+        widths.left,
+        rect.size.height - local_size_tl.height - local_size_bl.height,
+    );
+    let right_edge_info = get_edge_info(
+        border.right.style,
+        widths.right,
+        rect.size.height - local_size_tr.height - local_size_br.height,
+    );
 
-        add_corner_segment(
-            LayoutRect::from_floats(
-                rect.origin.x,
-                rect.origin.y,
-                rect.origin.x + local_size_tl.width,
-                rect.origin.y + local_size_tl.height,
-            ),
-            DeviceRect::from_floats(
-                0.0,
-                0.0,
-                dp_size_tl.width,
-                dp_size_tl.height,
-            ),
-            &border.left,
-            &border.top,
-            DeviceSize::new(dp_width_left, dp_width_top),
-            dp_corner_tl,
-            BorderSegment::TopLeft,
-            EdgeAaSegmentMask::TOP | EdgeAaSegmentMask::LEFT,
-            &mut border_segments,
-            brush_segments,
-        );
-
-        add_corner_segment(
-            LayoutRect::from_floats(
-                rect.origin.x + rect.size.width - local_size_tr.width,
-                rect.origin.y,
-                rect.origin.x + rect.size.width,
-                rect.origin.y + local_size_tr.height,
-            ),
-            DeviceRect::from_floats(
-                size.width - dp_size_tr.width,
-                0.0,
-                size.width,
-                dp_size_tr.height,
-            ),
-            &border.top,
-            &border.right,
-            DeviceSize::new(dp_width_right, dp_width_top),
-            dp_corner_tr,
-            BorderSegment::TopRight,
-            EdgeAaSegmentMask::TOP | EdgeAaSegmentMask::RIGHT,
-            &mut border_segments,
-            brush_segments,
-        );
-
-        add_corner_segment(
-            LayoutRect::from_floats(
-                rect.origin.x + rect.size.width - local_size_br.width,
-                rect.origin.y + rect.size.height - local_size_br.height,
-                rect.origin.x + rect.size.width,
-                rect.origin.y + rect.size.height,
-            ),
-            DeviceRect::from_floats(
-                size.width - dp_size_br.width,
-                size.height - dp_size_br.height,
-                size.width,
-                size.height,
-            ),
-            &border.right,
-            &border.bottom,
-            DeviceSize::new(dp_width_right, dp_width_bottom),
-            dp_corner_br,
-            BorderSegment::BottomRight,
-            EdgeAaSegmentMask::BOTTOM | EdgeAaSegmentMask::RIGHT,
-            &mut border_segments,
-            brush_segments,
-        );
-
-        add_corner_segment(
-            LayoutRect::from_floats(
-                rect.origin.x,
-                rect.origin.y + rect.size.height - local_size_bl.height,
-                rect.origin.x + local_size_bl.width,
-                rect.origin.y + rect.size.height,
-            ),
-            DeviceRect::from_floats(
-                0.0,
-                size.height - dp_size_bl.height,
-                dp_size_bl.width,
-                size.height,
-            ),
-            &border.bottom,
-            &border.left,
-            DeviceSize::new(dp_width_left, dp_width_bottom),
-            dp_corner_bl,
-            BorderSegment::BottomLeft,
-            EdgeAaSegmentMask::BOTTOM | EdgeAaSegmentMask::LEFT,
-            &mut border_segments,
-            brush_segments,
-        );
+    add_edge_segment(
+        LayoutRect::from_floats(
+            rect.origin.x,
+            rect.origin.y + local_size_tl.height + left_edge_info.local_offset,
+            rect.origin.x + widths.left,
+            rect.origin.y + local_size_tl.height + left_edge_info.local_offset + left_edge_info.local_size,
+        ),
+        &left_edge_info,
+        border.left,
+        widths.left,
+        BorderSegment::Left,
+        EdgeAaSegmentMask::LEFT | EdgeAaSegmentMask::RIGHT,
+        brush_segments,
+        border_segments,
+        border.do_aa,
+    );
+    add_edge_segment(
+        LayoutRect::from_floats(
+            rect.origin.x + local_size_tl.width + top_edge_info.local_offset,
+            rect.origin.y,
+            rect.origin.x + local_size_tl.width + top_edge_info.local_offset + top_edge_info.local_size,
+            rect.origin.y + widths.top,
+        ),
+        &top_edge_info,
+        border.top,
+        widths.top,
+        BorderSegment::Top,
+        EdgeAaSegmentMask::TOP | EdgeAaSegmentMask::BOTTOM,
+        brush_segments,
+        border_segments,
+        border.do_aa,
+    );
+    add_edge_segment(
+        LayoutRect::from_floats(
+            rect.origin.x + rect.size.width - widths.right,
+            rect.origin.y + local_size_tr.height + right_edge_info.local_offset,
+            rect.origin.x + rect.size.width,
+            rect.origin.y + local_size_tr.height + right_edge_info.local_offset + right_edge_info.local_size,
+        ),
+        &right_edge_info,
+        border.right,
+        widths.right,
+        BorderSegment::Right,
+        EdgeAaSegmentMask::RIGHT | EdgeAaSegmentMask::LEFT,
+        brush_segments,
+        border_segments,
+        border.do_aa,
+    );
+    add_edge_segment(
+        LayoutRect::from_floats(
+            rect.origin.x + local_size_bl.width + bottom_edge_info.local_offset,
+            rect.origin.y + rect.size.height - widths.bottom,
+            rect.origin.x + local_size_bl.width + bottom_edge_info.local_offset + bottom_edge_info.local_size,
+            rect.origin.y + rect.size.height,
+        ),
+        &bottom_edge_info,
+        border.bottom,
+        widths.bottom,
+        BorderSegment::Bottom,
+        EdgeAaSegmentMask::BOTTOM | EdgeAaSegmentMask::TOP,
+        brush_segments,
+        border_segments,
+        border.do_aa,
+    );
 
-        Some(BorderRenderTaskInfo {
-            border_segments,
-            size: size.to_i32(),
-            available_size_dependence: size_dependence,
-            do_aa: border.do_aa,
-        })
-    }
-
-    /// Returns the cache key size for this task, based on our available size
-    /// dependence computed earlier.
-    #[inline]
-    pub fn cache_key_size(
-        &self,
-        local_size: &LayoutSize,
-        scale: LayoutToDeviceScale,
-    ) -> DeviceIntSize {
-        let mut size = DeviceIntSize::zero();
-        if self.available_size_dependence.is_empty() {
-            return size;
-        }
-
-        let device_size = (*local_size * scale).to_i32();
-        if self.available_size_dependence.contains(AvailableSizeDependence::VERTICAL) {
-            size.height = device_size.height;
-        }
-        if self.available_size_dependence.contains(AvailableSizeDependence::HORIZONTAL) {
-            size.width = device_size.width;
-        }
-        size
-    }
-
-    pub fn build_instances(&self, border: &NormalBorder) -> Vec<BorderInstance> {
-        let mut instances = Vec::new();
-
-        for info in &self.border_segments {
-            let (side0, side1, flip0, flip1) = match info.segment {
-                BorderSegment::Left => (&border.left, &border.left, false, false),
-                BorderSegment::Top => (&border.top, &border.top, false, false),
-                BorderSegment::Right => (&border.right, &border.right, true, true),
-                BorderSegment::Bottom => (&border.bottom, &border.bottom, true, true),
-                BorderSegment::TopLeft => (&border.left, &border.top, false, false),
-                BorderSegment::TopRight => (&border.top, &border.right, false, true),
-                BorderSegment::BottomRight => (&border.right, &border.bottom, true, true),
-                BorderSegment::BottomLeft => (&border.bottom, &border.left, true, false),
-            };
-
-            let style0 = if side0.style.is_hidden() {
-                side1.style
-            } else {
-                side0.style
-            };
-            let style1 = if side1.style.is_hidden() {
-                side0.style
-            } else {
-                side1.style
-            };
-
-            let color0 = side0.border_color(flip0);
-            let color1 = side1.border_color(flip1);
-
-            add_segment(
-                info.task_rect,
-                style0,
-                style1,
-                color0,
-                color1,
-                info.segment,
-                &mut instances,
-                info.widths,
-                info.radius,
-                self.do_aa,
-            );
-        }
-
-        instances
-    }
-
-    /// Computes the maximum scale that we allow for this set of border parameters.
-    /// capping the scale will result in rendering very large corners at a lower
-    /// resolution and stretching them, so they will have the right shape, but
-    /// blurrier.
-    pub fn get_max_scale(
-        radii: &BorderRadius,
-        widths: &LayoutSideOffsets
-    ) -> LayoutToDeviceScale {
-        let r = radii.top_left.width
-            .max(radii.top_left.height)
-            .max(radii.top_right.width)
-            .max(radii.top_right.height)
-            .max(radii.bottom_left.width)
-            .max(radii.bottom_left.height)
-            .max(radii.bottom_right.width)
-            .max(radii.bottom_right.height)
-            .max(widths.top)
-            .max(widths.bottom)
-            .max(widths.left)
-            .max(widths.right);
-
-        LayoutToDeviceScale::new(MAX_BORDER_RESOLUTION as f32 / r)
-    }
+    add_corner_segment(
+        LayoutRect::from_floats(
+            rect.origin.x,
+            rect.origin.y,
+            rect.origin.x + local_size_tl.width,
+            rect.origin.y + local_size_tl.height,
+        ),
+        border.left,
+        border.top,
+        LayoutSize::new(widths.left, widths.top),
+        border.radius.top_left,
+        BorderSegment::TopLeft,
+        EdgeAaSegmentMask::TOP | EdgeAaSegmentMask::LEFT,
+        brush_segments,
+        border_segments,
+        border.do_aa,
+    );
+    add_corner_segment(
+        LayoutRect::from_floats(
+            rect.origin.x + rect.size.width - local_size_tr.width,
+            rect.origin.y,
+            rect.origin.x + rect.size.width,
+            rect.origin.y + local_size_tr.height,
+        ),
+        border.top,
+        border.right,
+        LayoutSize::new(widths.right, widths.top),
+        border.radius.top_right,
+        BorderSegment::TopRight,
+        EdgeAaSegmentMask::TOP | EdgeAaSegmentMask::RIGHT,
+        brush_segments,
+        border_segments,
+        border.do_aa,
+    );
+    add_corner_segment(
+        LayoutRect::from_floats(
+            rect.origin.x + rect.size.width - local_size_br.width,
+            rect.origin.y + rect.size.height - local_size_br.height,
+            rect.origin.x + rect.size.width,
+            rect.origin.y + rect.size.height,
+        ),
+        border.right,
+        border.bottom,
+        LayoutSize::new(widths.right, widths.bottom),
+        border.radius.bottom_right,
+        BorderSegment::BottomRight,
+        EdgeAaSegmentMask::BOTTOM | EdgeAaSegmentMask::RIGHT,
+        brush_segments,
+        border_segments,
+        border.do_aa,
+    );
+    add_corner_segment(
+        LayoutRect::from_floats(
+            rect.origin.x,
+            rect.origin.y + rect.size.height - local_size_bl.height,
+            rect.origin.x + local_size_bl.width,
+            rect.origin.y + rect.size.height,
+        ),
+        border.bottom,
+        border.left,
+        LayoutSize::new(widths.left, widths.bottom),
+        border.radius.bottom_left,
+        BorderSegment::BottomLeft,
+        EdgeAaSegmentMask::BOTTOM | EdgeAaSegmentMask::LEFT,
+        brush_segments,
+        border_segments,
+        border.do_aa,
+    );
 }
 
-fn add_brush_segment(
-    image_rect: LayoutRect,
-    task_rect: DeviceRect,
-    brush_flags: BrushFlags,
-    edge_flags: EdgeAaSegmentMask,
-    brush_segments: &mut BrushSegmentVec,
-) {
-    if image_rect.size.width <= 0. || image_rect.size.width <= 0. {
-        return;
-    }
+/// Computes the maximum scale that we allow for this set of border parameters.
+/// capping the scale will result in rendering very large corners at a lower
+/// resolution and stretching them, so they will have the right shape, but
+/// blurrier.
+pub fn get_max_scale_for_border(
+    radii: &BorderRadius,
+    widths: &LayoutSideOffsets
+) -> LayoutToDeviceScale {
+    let r = radii.top_left.width
+        .max(radii.top_left.height)
+        .max(radii.top_right.width)
+        .max(radii.top_right.height)
+        .max(radii.bottom_left.width)
+        .max(radii.bottom_left.height)
+        .max(radii.bottom_right.width)
+        .max(radii.bottom_right.height)
+        .max(widths.top)
+        .max(widths.bottom)
+        .max(widths.left)
+        .max(widths.right);
 
-    brush_segments.push(
-        BrushSegment::new(
-            image_rect,
-            /* may_need_clip_mask = */ true,
-            edge_flags,
-            [
-                task_rect.origin.x,
-                task_rect.origin.y,
-                task_rect.origin.x + task_rect.size.width,
-                task_rect.origin.y + task_rect.size.height,
-            ],
-            brush_flags,
-        )
-    );
+    LayoutToDeviceScale::new(MAX_BORDER_RESOLUTION as f32 / r)
 }
 
 fn add_segment(
     task_rect: DeviceRect,
     style0: BorderStyle,
     style1: BorderStyle,
     color0: ColorF,
     color1: ColorF,
@@ -1204,81 +927,233 @@ fn add_segment(
                 _ => {
                     instances.push(base_instance);
                 }
             }
         }
     }
 }
 
+/// Add a corner segment (if valid) to the list of
+/// border segments for this primitive.
 fn add_corner_segment(
     image_rect: LayoutRect,
-    task_rect: DeviceRect,
-    side0: &BorderSide,
-    side1: &BorderSide,
-    widths: DeviceSize,
-    radius: DeviceSize,
+    side0: BorderSide,
+    side1: BorderSide,
+    widths: LayoutSize,
+    radius: LayoutSize,
     segment: BorderSegment,
     edge_flags: EdgeAaSegmentMask,
-    border_segments: &mut Vec<BorderSegmentInfo>,
     brush_segments: &mut BrushSegmentVec,
+    border_segments: &mut SmallVec<[BorderSegmentInfo; 8]>,
+    do_aa: bool,
 ) {
     if side0.color.a <= 0.0 && side1.color.a <= 0.0 {
         return;
     }
 
     if widths.width <= 0.0 && widths.height <= 0.0 {
         return;
     }
 
     if side0.style.is_hidden() && side1.style.is_hidden() {
         return;
     }
 
-    border_segments.push(BorderSegmentInfo {
-        task_rect,
-        segment,
-        radius,
-        widths,
-    });
+    if image_rect.size.width <= 0. || image_rect.size.height <= 0. {
+        return;
+    }
+
+    let is_opaque =
+        side0.is_opaque() &&
+        side1.is_opaque() &&
+        radius.width <= 0.0 &&
+        radius.height <= 0.0;
 
-    add_brush_segment(
-        image_rect,
-        task_rect,
-        BrushFlags::SEGMENT_RELATIVE,
-        edge_flags,
-        brush_segments,
+    brush_segments.push(
+        BrushSegment::new(
+            image_rect,
+            /* may_need_clip_mask = */ true,
+            edge_flags,
+            [0.0; 4],
+            BrushFlags::SEGMENT_RELATIVE,
+        )
     );
+
+    border_segments.push(BorderSegmentInfo {
+        handle: None,
+        local_task_size: image_rect.size,
+        is_opaque,
+        cache_key: RenderTaskCacheKey {
+            size: DeviceIntSize::zero(),
+            kind: RenderTaskCacheKeyKind::BorderCorner(
+                BorderCornerCacheKey {
+                    do_aa,
+                    side0: side0.into(),
+                    side1: side1.into(),
+                    segment,
+                    radius: radius.to_au(),
+                    widths: widths.to_au(),
+                }
+            ),
+        },
+    });
 }
 
+/// Add an edge segment (if valid) to the list of
+/// border segments for this primitive.
 fn add_edge_segment(
     image_rect: LayoutRect,
-    task_rect: DeviceRect,
-    side: &BorderSide,
+    edge_info: &EdgeInfo,
+    side: BorderSide,
+    width: f32,
     segment: BorderSegment,
     edge_flags: EdgeAaSegmentMask,
-    border_segments: &mut Vec<BorderSegmentInfo>,
-    brush_flags: BrushFlags,
     brush_segments: &mut BrushSegmentVec,
+    border_segments: &mut SmallVec<[BorderSegmentInfo; 8]>,
+    do_aa: bool,
 ) {
     if side.color.a <= 0.0 {
         return;
     }
 
     if side.style.is_hidden() {
         return;
     }
 
+    let (size, brush_flags) = match segment {
+        BorderSegment::Left | BorderSegment::Right => {
+            (LayoutSize::new(width, edge_info.stretch_size), BrushFlags::SEGMENT_REPEAT_Y)
+        }
+        BorderSegment::Top | BorderSegment::Bottom => {
+            (LayoutSize::new(edge_info.stretch_size, width), BrushFlags::SEGMENT_REPEAT_X)
+        }
+        _ => {
+            unreachable!();
+        }
+    };
+
+    if image_rect.size.width <= 0. || image_rect.size.height <= 0. {
+        return;
+    }
+
+    let is_opaque = side.is_opaque();
+
+    brush_segments.push(
+        BrushSegment::new(
+            image_rect,
+            /* may_need_clip_mask = */ true,
+            edge_flags,
+            [0.0, 0.0, size.width, size.height],
+            BrushFlags::SEGMENT_RELATIVE | brush_flags,
+        )
+    );
+
     border_segments.push(BorderSegmentInfo {
-        task_rect,
-        segment,
-        radius: DeviceSize::zero(),
-        widths: task_rect.size,
+        handle: None,
+        local_task_size: size,
+        is_opaque,
+        cache_key: RenderTaskCacheKey {
+            size: DeviceIntSize::zero(),
+            kind: RenderTaskCacheKeyKind::BorderEdge(
+                BorderEdgeCacheKey {
+                    do_aa,
+                    side: side.into(),
+                    size: size.to_au(),
+                    segment,
+                },
+            ),
+        },
     });
+}
+
+/// Build the set of border instances needed to draw a border
+/// segment into the render task cache.
+pub fn build_border_instances(
+    cache_key: &RenderTaskCacheKey,
+    border: &NormalBorder,
+    scale: LayoutToDeviceScale,
+) -> Vec<BorderInstance> {
+    let mut instances = Vec::new();
+
+    let (segment, widths, radius) = match cache_key.kind {
+        RenderTaskCacheKeyKind::BorderEdge(ref key) => {
+            (key.segment, LayoutSize::from_au(key.size), LayoutSize::zero())
+        }
+        RenderTaskCacheKeyKind::BorderCorner(ref key) => {
+            (key.segment, LayoutSize::from_au(key.widths), LayoutSize::from_au(key.radius))
+        }
+        _ => {
+            unreachable!();
+        }
+    };
 
-    add_brush_segment(
-        image_rect,
-        task_rect,
-        brush_flags,
-        edge_flags,
-        brush_segments,
+    let (side0, side1, flip0, flip1) = match segment {
+        BorderSegment::Left => (&border.left, &border.left, false, false),
+        BorderSegment::Top => (&border.top, &border.top, false, false),
+        BorderSegment::Right => (&border.right, &border.right, true, true),
+        BorderSegment::Bottom => (&border.bottom, &border.bottom, true, true),
+        BorderSegment::TopLeft => (&border.left, &border.top, false, false),
+        BorderSegment::TopRight => (&border.top, &border.right, false, true),
+        BorderSegment::BottomRight => (&border.right, &border.bottom, true, true),
+        BorderSegment::BottomLeft => (&border.bottom, &border.left, true, false),
+    };
+
+    let style0 = if side0.style.is_hidden() {
+        side1.style
+    } else {
+        side0.style
+    };
+    let style1 = if side1.style.is_hidden() {
+        side0.style
+    } else {
+        side1.style
+    };
+
+    let color0 = side0.border_color(flip0);
+    let color1 = side1.border_color(flip1);
+
+    let widths = (widths * scale).ceil();
+    let radius = (radius * scale).ceil();
+
+    add_segment(
+        DeviceRect::new(DevicePoint::zero(), cache_key.size.to_f32()),
+        style0,
+        style1,
+        color0,
+        color1,
+        segment,
+        &mut instances,
+        widths,
+        radius,
+        border.do_aa,
     );
+
+    instances
 }
+
+pub fn create_normal_border_prim(
+    local_rect: &LayoutRect,
+    border: NormalBorder,
+    widths: LayoutSideOffsets,
+) -> BrushPrimitive {
+    let mut brush_segments = BrushSegmentVec::new();
+    let mut border_segments = SmallVec::new();
+
+    create_border_segments(
+        local_rect,
+        &border,
+        &widths,
+        &mut border_segments,
+        &mut brush_segments,
+    );
+
+    BrushPrimitive::new(
+        BrushKind::new_border(
+            border,
+            widths,
+            border_segments,
+        ),
+        Some(BrushSegmentDescriptor {
+            segments: brush_segments,
+        }),
+    )
+}
--- a/gfx/webrender/src/clip_scroll_tree.rs
+++ b/gfx/webrender/src/clip_scroll_tree.rs
@@ -400,18 +400,18 @@ impl ClipScrollTree {
             }
             SpatialNodeType::ScrollFrame(scrolling_info) => {
                 pt.new_level(format!("ScrollFrame"));
                 pt.add_item(format!("index: {:?}", index));
                 pt.add_item(format!("viewport: {:?}", scrolling_info.viewport_rect));
                 pt.add_item(format!("scrollable_size: {:?}", scrolling_info.scrollable_size));
                 pt.add_item(format!("scroll offset: {:?}", scrolling_info.offset));
             }
-            SpatialNodeType::ReferenceFrame(ref info) => {
-                pt.new_level(format!("ReferenceFrame {:?}", info.resolved_transform));
+            SpatialNodeType::ReferenceFrame(ref _info) => {
+                pt.new_level(format!("ReferenceFrame"));
                 pt.add_item(format!("index: {:?}", index));
             }
         }
 
         pt.add_item(format!("world_viewport_transform: {:?}", node.world_viewport_transform));
         pt.add_item(format!("world_content_transform: {:?}", node.world_content_transform));
         pt.add_item(format!("coordinate_system_id: {:?}", node.coordinate_system_id));
 
--- a/gfx/webrender/src/device/gl.rs
+++ b/gfx/webrender/src/device/gl.rs
@@ -1,25 +1,26 @@
 /* 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 super::super::shader_source;
-use api::{ColorF, ImageFormat};
+use api::{ColorF, ImageFormat, MemoryReport};
 use api::{DeviceIntPoint, DeviceIntRect, DeviceUintRect, DeviceUintSize};
 use api::TextureTarget;
 #[cfg(any(feature = "debug_renderer", feature="capture"))]
 use api::ImageDescriptor;
 use euclid::Transform3D;
 use gleam::gl;
-use internal_types::{FastHashMap, RenderTargetInfo};
+use internal_types::{FastHashMap, LayerIndex, RenderTargetInfo};
 use log::Level;
 use smallvec::SmallVec;
 use std::cell::RefCell;
 use std::cmp;
+use std::collections::hash_map::Entry;
 use std::fs::File;
 use std::io::Read;
 use std::marker::PhantomData;
 use std::mem;
 use std::ops::Add;
 use std::path::PathBuf;
 use std::ptr;
 use std::rc::Rc;
@@ -437,55 +438,61 @@ impl ExternalTexture {
 pub struct Texture {
     id: gl::GLuint,
     target: gl::GLuint,
     layer_count: i32,
     format: ImageFormat,
     width: u32,
     height: u32,
     filter: TextureFilter,
-    render_target: Option<RenderTargetInfo>,
-    fbo_ids: Vec<FBOId>,
-    depth_rb: Option<RBOId>,
+    /// Framebuffer Objects, one for each layer of the texture, allowing this
+    /// texture to be rendered to. Empty if this texture is not used as a render
+    /// target.
+    fbos: Vec<FBOId>,
+    /// Same as the above, but with a depth buffer attached.
+    ///
+    /// FBOs are cheap to create but expensive to reconfigure (since doing so
+    /// invalidates framebuffer completeness caching). Moreover, rendering with
+    /// a depth buffer attached but the depth write+test disabled relies on the
+    /// driver to optimize it out of the rendering pass, which most drivers
+    /// probably do but, according to jgilbert, is best not to rely on.
+    ///
+    /// So we lazily generate a second list of FBOs with depth. This list is
+    /// empty if this texture is not used as a render target _or_ if it is, but
+    /// the depth buffer has never been requested.
+    ///
+    /// Note that we always fill fbos, and then lazily create fbos_with_depth
+    /// when needed. We could make both lazy (i.e. render targets would have one
+    /// or the other, but not both, unless they were actually used in both
+    /// configurations). But that would complicate a lot of logic in this module,
+    /// and FBOs are cheap enough to create.
+    fbos_with_depth: Vec<FBOId>,
     last_frame_used: FrameId,
 }
 
 impl Texture {
     pub fn get_dimensions(&self) -> DeviceUintSize {
         DeviceUintSize::new(self.width, self.height)
     }
 
-    pub fn get_render_target_layer_count(&self) -> usize {
-        self.fbo_ids.len()
-    }
-
     pub fn get_layer_count(&self) -> i32 {
         self.layer_count
     }
 
     pub fn get_format(&self) -> ImageFormat {
         self.format
     }
 
     #[cfg(any(feature = "debug_renderer", feature = "capture"))]
     pub fn get_filter(&self) -> TextureFilter {
         self.filter
     }
 
-    #[cfg(any(feature = "debug_renderer", feature = "capture"))]
-    pub fn get_render_target(&self) -> Option<RenderTargetInfo> {
-        self.render_target.clone()
-    }
-
-    pub fn has_depth(&self) -> bool {
-        self.depth_rb.is_some()
-    }
-
-    pub fn get_rt_info(&self) -> Option<&RenderTargetInfo> {
-        self.render_target.as_ref()
+    pub fn supports_depth(&self) -> bool {
+        !self.fbos_with_depth.is_empty()
     }
 
     pub fn used_in_frame(&self, frame_id: FrameId) -> bool {
         self.last_frame_used == frame_id
     }
 
     /// Returns true if this texture was used within `threshold` frames of
     /// the current frame.
@@ -708,16 +715,32 @@ pub struct Capabilities {
 }
 
 #[derive(Clone, Debug)]
 pub enum ShaderError {
     Compilation(String, String), // name, error message
     Link(String, String),        // name, error message
 }
 
+/// A refcounted depth target, which may be shared by multiple textures across
+/// the device.
+struct SharedDepthTarget {
+    /// The Render Buffer Object representing the depth target.
+    rbo_id: RBOId,
+    /// Reference count. When this drops to zero, the RBO is deleted.
+    refcount: usize,
+}
+
+#[cfg(feature = "debug")]
+impl Drop for SharedDepthTarget {
+    fn drop(&mut self) {
+        debug_assert!(thread::panicking() || self.refcount == 0);
+    }
+}
+
 pub struct Device {
     gl: Rc<gl::Gl>,
     // device state
     bound_textures: [gl::GLuint; 16],
     bound_program: gl::GLuint,
     bound_vao: gl::GLuint,
     bound_read_fbo: FBOId,
     bound_draw_fbo: FBOId,
@@ -730,16 +753,22 @@ pub struct Device {
 
     // HW or API capabilities
     #[cfg(feature = "debug_renderer")]
     capabilities: Capabilities,
 
     bgra_format_internal: gl::GLuint,
     bgra_format_external: gl::GLuint,
 
+    /// Map from texture dimensions to shared depth buffers for render targets.
+    ///
+    /// Render targets often have the same width/height, so we can save memory
+    /// by sharing these across targets.
+    depth_targets: FastHashMap<DeviceUintSize, SharedDepthTarget>,
+
     // debug
     inside_frame: bool,
 
     // resources
     resource_override_path: Option<PathBuf>,
 
     max_texture_size: u32,
     renderer_name: String,
@@ -754,16 +783,45 @@ pub struct Device {
     /// otherwise are on some drivers, particularly ANGLE), If it's not
     /// supported, we fall back to glTexImage*.
     supports_texture_storage: bool,
 
     // GL extensions
     extensions: Vec<String>,
 }
 
+/// Contains the parameters necessary to bind a texture-backed draw target.
+#[derive(Clone, Copy)]
+pub struct TextureDrawTarget<'a> {
+    /// The target texture.
+    pub texture: &'a Texture,
+    /// The slice within the texture array to draw to.
+    pub layer: LayerIndex,
+    /// Whether to draw with the texture's associated depth target.
+    pub with_depth: bool,
+}
+
+/// Contains the parameters necessary to bind a texture-backed read target.
+#[derive(Clone, Copy)]
+pub struct TextureReadTarget<'a> {
+    /// The source texture.
+    pub texture: &'a Texture,
+    /// The slice within the texture array to read from.
+    pub layer: LayerIndex,
+}
+
+impl<'a> From<TextureDrawTarget<'a>> for TextureReadTarget<'a> {
+    fn from(t: TextureDrawTarget<'a>) -> Self {
+        TextureReadTarget {
+            texture: t.texture,
+            layer: t.layer,
+        }
+    }
+}
+
 impl Device {
     pub fn new(
         gl: Rc<gl::Gl>,
         resource_override_path: Option<PathBuf>,
         upload_method: UploadMethod,
         cached_programs: Option<Rc<ProgramCache>>,
     ) -> Device {
         let mut max_texture_size = [0];
@@ -824,16 +882,18 @@ impl Device {
             #[cfg(feature = "debug_renderer")]
             capabilities: Capabilities {
                 supports_multisampling: false, //TODO
             },
 
             bgra_format_internal,
             bgra_format_external,
 
+            depth_targets: FastHashMap::default(),
+
             bound_textures: [0; 16],
             bound_program: 0,
             bound_vao: 0,
             bound_read_fbo: FBOId(0),
             bound_draw_fbo: FBOId(0),
             program_mode_id: UniformLocation::INVALID,
             default_read_fbo: 0,
             default_draw_fbo: 0,
@@ -1001,40 +1061,44 @@ impl Device {
         debug_assert!(self.inside_frame);
 
         if self.bound_read_fbo != fbo_id {
             self.bound_read_fbo = fbo_id;
             fbo_id.bind(self.gl(), FBOTarget::Read);
         }
     }
 
-    pub fn bind_read_target(&mut self, texture_and_layer: Option<(&Texture, i32)>) {
-        let fbo_id = texture_and_layer.map_or(FBOId(self.default_read_fbo), |texture_and_layer| {
-            texture_and_layer.0.fbo_ids[texture_and_layer.1 as usize]
+    pub fn bind_read_target(&mut self, texture_target: Option<TextureReadTarget>) {
+        let fbo_id = texture_target.map_or(FBOId(self.default_read_fbo), |target| {
+            target.texture.fbos[target.layer]
         });
 
         self.bind_read_target_impl(fbo_id)
     }
 
     fn bind_draw_target_impl(&mut self, fbo_id: FBOId) {
         debug_assert!(self.inside_frame);
 
         if self.bound_draw_fbo != fbo_id {
             self.bound_draw_fbo = fbo_id;
             fbo_id.bind(self.gl(), FBOTarget::Draw);
         }
     }
 
     pub fn bind_draw_target(
         &mut self,
-        texture_and_layer: Option<(&Texture, i32)>,
+        texture_target: Option<TextureDrawTarget>,
         dimensions: Option<DeviceUintSize>,
     ) {
-        let fbo_id = texture_and_layer.map_or(FBOId(self.default_draw_fbo), |texture_and_layer| {
-            texture_and_layer.0.fbo_ids[texture_and_layer.1 as usize]
+        let fbo_id = texture_target.map_or(FBOId(self.default_draw_fbo), |target| {
+            if target.with_depth {
+                target.texture.fbos_with_depth[target.layer]
+            } else {
+                target.texture.fbos[target.layer]
+            }
         });
 
         self.bind_draw_target_impl(fbo_id);
 
         if let Some(dimensions) = dimensions {
             self.gl.viewport(
                 0,
                 0,
@@ -1227,19 +1291,18 @@ impl Device {
         let mut texture = Texture {
             id: self.gl.gen_textures(1)[0],
             target: get_gl_target(target),
             width,
             height,
             layer_count,
             format,
             filter,
-            render_target,
-            fbo_ids: vec![],
-            depth_rb: None,
+            fbos: vec![],
+            fbos_with_depth: vec![],
             last_frame_used: self.frame_id,
         };
         self.bind_texture(DEFAULT_TEXTURE, &texture);
         self.set_texture_parameters(texture.target, filter);
 
         // Allocate storage.
         let desc = self.gl_describe_format(texture.format);
         let is_array = match texture.target {
@@ -1304,17 +1367,20 @@ impl Device {
                     desc.external,
                     desc.pixel_type,
                     None,
                 ),
         }
 
         // Set up FBOs, if required.
         if let Some(rt_info) = render_target {
-            self.init_fbos(&mut texture, rt_info);
+            self.init_fbos(&mut texture, false);
+            if rt_info.has_depth {
+                self.init_fbos(&mut texture, true);
+            }
         }
 
         texture
     }
 
     fn set_texture_parameters(&mut self, target: gl::GLuint, filter: TextureFilter) {
         let mag_filter = match filter {
             TextureFilter::Nearest => gl::NEAREST,
@@ -1344,35 +1410,39 @@ impl Device {
         dst: &mut Texture,
         src: &Texture,
     ) {
         debug_assert!(self.inside_frame);
         debug_assert!(dst.width >= src.width);
         debug_assert!(dst.height >= src.height);
 
         let rect = DeviceIntRect::new(DeviceIntPoint::zero(), src.get_dimensions().to_i32());
-        for (read_fbo, draw_fbo) in src.fbo_ids.iter().zip(&dst.fbo_ids) {
+        for (read_fbo, draw_fbo) in src.fbos.iter().zip(&dst.fbos) {
             self.bind_read_target_impl(*read_fbo);
             self.bind_draw_target_impl(*draw_fbo);
             self.blit_render_target(rect, rect);
         }
         self.bind_read_target(None);
     }
 
     /// Notifies the device that the contents of a render target are no longer
     /// needed.
+    ///
+    /// FIXME(bholley): We could/should invalidate the depth targets earlier
+    /// than the color targets, i.e. immediately after each pass.
     pub fn invalidate_render_target(&mut self, texture: &Texture) {
-        let attachments: &[gl::GLenum] = if texture.has_depth() {
-            &[gl::COLOR_ATTACHMENT0, gl::DEPTH_ATTACHMENT]
+        let (fbos, attachments) = if texture.supports_depth() {
+            (&texture.fbos_with_depth,
+             &[gl::COLOR_ATTACHMENT0, gl::DEPTH_ATTACHMENT] as &[gl::GLenum])
         } else {
-            &[gl::COLOR_ATTACHMENT0]
+            (&texture.fbos, &[gl::COLOR_ATTACHMENT0] as &[gl::GLenum])
         };
 
         let original_bound_fbo = self.bound_draw_fbo;
-        for fbo_id in texture.fbo_ids.iter() {
+        for fbo_id in fbos.iter() {
             // Note: The invalidate extension may not be supported, in which
             // case this is a no-op. That's ok though, because it's just a
             // hint.
             self.bind_external_draw_target(*fbo_id);
             self.gl.invalidate_framebuffer(gl::FRAMEBUFFER, attachments);
         }
         self.bind_external_draw_target(original_bound_fbo);
     }
@@ -1381,52 +1451,38 @@ impl Device {
     ///
     /// This method adds or removes a depth target as necessary.
     pub fn reuse_render_target<T: Texel>(
         &mut self,
         texture: &mut Texture,
         rt_info: RenderTargetInfo,
     ) {
         texture.last_frame_used = self.frame_id;
-        texture.render_target = Some(rt_info);
 
-        // If the depth target requirements changed, just drop the FBOs and
-        // reinitialize.
-        //
-        // FIXME(bholley): I have a patch to do this better.
-        if rt_info.has_depth != texture.has_depth() {
-            self.deinit_fbos(texture);
-            self.init_fbos(texture, rt_info);
+        // Add depth support if needed.
+        if rt_info.has_depth && !texture.supports_depth() {
+            self.init_fbos(texture, true);
         }
     }
 
-    fn init_fbos(&mut self, texture: &mut Texture, rt_info: RenderTargetInfo) {
-        // Generate the FBOs.
-        assert!(texture.fbo_ids.is_empty());
-        texture.fbo_ids.extend(
-            self.gl.gen_framebuffers(texture.layer_count).into_iter().map(FBOId)
-        );
+    fn init_fbos(&mut self, texture: &mut Texture, with_depth: bool) {
+        let (fbos, depth_rb) = if with_depth {
+            let depth_target = self.acquire_depth_target(texture.get_dimensions());
+            (&mut texture.fbos_with_depth, Some(depth_target))
+        } else {
+            (&mut texture.fbos, None)
+        };
 
-        // Optionally generate a depth target.
-        if rt_info.has_depth {
-            let renderbuffer_ids = self.gl.gen_renderbuffers(1);
-            let depth_rb = renderbuffer_ids[0];
-            texture.depth_rb = Some(RBOId(depth_rb));
-            self.gl.bind_renderbuffer(gl::RENDERBUFFER, depth_rb);
-            self.gl.renderbuffer_storage(
-                gl::RENDERBUFFER,
-                gl::DEPTH_COMPONENT24,
-                texture.width as _,
-                texture.height as _,
-            );
-        }
+        // Generate the FBOs.
+        assert!(fbos.is_empty());
+        fbos.extend(self.gl.gen_framebuffers(texture.layer_count).into_iter().map(FBOId));
 
         // Bind the FBOs.
         let original_bound_fbo = self.bound_draw_fbo;
-        for (fbo_index, &fbo_id) in texture.fbo_ids.iter().enumerate() {
+        for (fbo_index, &fbo_id) in fbos.iter().enumerate() {
             self.bind_external_draw_target(fbo_id);
             match texture.target {
                 gl::TEXTURE_2D_ARRAY => {
                     self.gl.framebuffer_texture_layer(
                         gl::DRAW_FRAMEBUFFER,
                         gl::COLOR_ATTACHMENT0,
                         texture.id,
                         0,
@@ -1440,44 +1496,72 @@ impl Device {
                         gl::COLOR_ATTACHMENT0,
                         texture.target,
                         texture.id,
                         0,
                     )
                 }
             }
 
-            if let Some(depth_rb) = texture.depth_rb {
+            if let Some(depth_rb) = depth_rb {
                 self.gl.framebuffer_renderbuffer(
                     gl::DRAW_FRAMEBUFFER,
                     gl::DEPTH_ATTACHMENT,
                     gl::RENDERBUFFER,
                     depth_rb.0,
                 );
             }
         }
         self.bind_external_draw_target(original_bound_fbo);
     }
 
-    fn deinit_fbos(&mut self, texture: &mut Texture) {
-        if let Some(RBOId(depth_rb)) = texture.depth_rb.take() {
-            self.gl.delete_renderbuffers(&[depth_rb]);
-            texture.depth_rb = None;
-        }
-
-        if !texture.fbo_ids.is_empty() {
-            let fbo_ids: Vec<_> = texture
-                .fbo_ids
+    fn deinit_fbos(&mut self, fbos: &mut Vec<FBOId>) {
+        if !fbos.is_empty() {
+            let fbo_ids: SmallVec<[gl::GLuint; 8]> = fbos
                 .drain(..)
                 .map(|FBOId(fbo_id)| fbo_id)
                 .collect();
             self.gl.delete_framebuffers(&fbo_ids[..]);
         }
     }
 
+    fn acquire_depth_target(&mut self, dimensions: DeviceUintSize) -> RBOId {
+        let gl = &self.gl;
+        let target = self.depth_targets.entry(dimensions).or_insert_with(|| {
+            let renderbuffer_ids = gl.gen_renderbuffers(1);
+            let depth_rb = renderbuffer_ids[0];
+            gl.bind_renderbuffer(gl::RENDERBUFFER, depth_rb);
+            gl.renderbuffer_storage(
+                gl::RENDERBUFFER,
+                gl::DEPTH_COMPONENT24,
+                dimensions.width as _,
+                dimensions.height as _,
+            );
+            SharedDepthTarget {
+                rbo_id: RBOId(depth_rb),
+                refcount: 0,
+            }
+        });
+        target.refcount += 1;
+        target.rbo_id
+    }
+
+    fn release_depth_target(&mut self, dimensions: DeviceUintSize) {
+        let mut entry = match self.depth_targets.entry(dimensions) {
+            Entry::Occupied(x) => x,
+            Entry::Vacant(..) => panic!("Releasing unknown depth target"),
+        };
+        debug_assert!(entry.get().refcount != 0);
+        entry.get_mut().refcount -= 1;
+        if entry.get().refcount == 0 {
+            let t = entry.remove();
+            self.gl.delete_renderbuffers(&[t.rbo_id.0]);
+        }
+    }
+
     pub fn blit_render_target(&mut self, src_rect: DeviceIntRect, dest_rect: DeviceIntRect) {
         debug_assert!(self.inside_frame);
 
         self.gl.blit_framebuffer(
             src_rect.origin.x,
             src_rect.origin.y,
             src_rect.origin.x + src_rect.size.width,
             src_rect.origin.y + src_rect.size.height,
@@ -1487,17 +1571,23 @@ impl Device {
             dest_rect.origin.y + dest_rect.size.height,
             gl::COLOR_BUFFER_BIT,
             gl::LINEAR,
         );
     }
 
     pub fn delete_texture(&mut self, mut texture: Texture) {
         debug_assert!(self.inside_frame);
-        self.deinit_fbos(&mut texture);
+        let had_depth = texture.supports_depth();
+        self.deinit_fbos(&mut texture.fbos);
+        self.deinit_fbos(&mut texture.fbos_with_depth);
+        if had_depth {
+            self.release_depth_target(texture.get_dimensions());
+        }
+
         self.gl.delete_textures(&[texture.id]);
 
         for bound_texture in &mut self.bound_textures {
             if *bound_texture == texture.id {
                 *bound_texture = 0
             }
         }
 
@@ -2312,16 +2402,28 @@ impl Device {
             },
             ImageFormat::RG8 => FormatDesc {
                 internal: gl::RG8,
                 external: gl::RG,
                 pixel_type: gl::UNSIGNED_BYTE,
             },
         }
     }
+
+    /// Generates a memory report for the resources managed by the device layer.
+    pub fn report_memory(&self) -> MemoryReport {
+        let mut report = MemoryReport::default();
+        for dim in self.depth_targets.keys() {
+            // DEPTH24 textures generally reserve 3 bytes for depth and 1 byte
+            // for stencil, so we measure them as 32 bytes.
+            let pixels: u32 = dim.width * dim.height;
+            report.depth_target_textures += (pixels as usize) * 4;
+        }
+        report
+    }
 }
 
 struct FormatDesc {
     internal: gl::GLenum,
     external: gl::GLuint,
     pixel_type: gl::GLuint,
 }
 
--- a/gfx/webrender/src/display_list_flattener.rs
+++ b/gfx/webrender/src/display_list_flattener.rs
@@ -1,16 +1,16 @@
 
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 use api::{AlphaType, BorderDetails, BorderDisplayItem, BuiltDisplayListIter, ClipAndScrollInfo};
 use api::{ClipId, ColorF, ComplexClipRegion, DeviceIntPoint, DeviceIntRect, DeviceIntSize};
-use api::{DevicePixelScale, DeviceUintRect, DisplayItemRef, ExtendMode, ExternalScrollId};
+use api::{DisplayItemRef, ExtendMode, ExternalScrollId};
 use api::{FilterOp, FontInstanceKey, GlyphInstance, GlyphOptions, RasterSpace, GradientStop};
 use api::{IframeDisplayItem, ImageKey, ImageRendering, ItemRange, LayoutPoint, ColorDepth};
 use api::{LayoutPrimitiveInfo, LayoutRect, LayoutSize, LayoutTransform, LayoutVector2D};
 use api::{LineOrientation, LineStyle, NinePatchBorderSource, PipelineId};
 use api::{PropertyBinding, ReferenceFrame, RepeatMode, ScrollFrameDisplayItem, ScrollSensitivity};
 use api::{Shadow, SpecificDisplayItem, StackingContext, StickyFrameDisplayItem, TexelRect};
 use api::{ClipMode, TransformStyle, YuvColorSpace, YuvData};
 use clip::{ClipChainId, ClipRegion, ClipItemKey, ClipStore};
@@ -27,17 +27,17 @@ use picture::{PictureCompositeMode, Pict
 use prim_store::{BrushKind, BrushPrimitive, BrushSegmentDescriptor, PrimitiveInstance};
 use prim_store::{EdgeAaSegmentMask, ImageSource, PrimitiveOpacity, PrimitiveKey};
 use prim_store::{BorderSource, BrushSegment, BrushSegmentVec, PrimitiveContainer, PrimitiveIndex, PrimitiveStore};
 use prim_store::{OpacityBinding, ScrollNodeAndClipChain, TextRunPrimitive};
 use render_backend::{DocumentView};
 use resource_cache::{FontInstanceMap, ImageRequest};
 use scene::{Scene, ScenePipeline, StackingContextHelpers};
 use scene_builder::DocumentResources;
-use spatial_node::{SpatialNodeType, StickyFrameInfo};
+use spatial_node::{StickyFrameInfo};
 use std::{f32, mem};
 use std::collections::vec_deque::VecDeque;
 use tiling::{CompositeOps};
 use util::{MaxRect, RectHelpers};
 
 #[derive(Debug, Copy, Clone)]
 struct ClipNode {
     id: ClipChainId,
@@ -204,17 +204,16 @@ impl<'a> DisplayListFlattener<'a> {
             root_prim_index: PrimitiveIndex(0),
         };
 
         flattener.push_root(
             root_pipeline_id,
             &root_pipeline.viewport_size,
             &root_pipeline.content_size,
         );
-        flattener.setup_viewport_offset(view.inner_rect, view.accumulated_scale_factor());
         flattener.flatten_root(root_pipeline, &root_pipeline.viewport_size);
 
         debug_assert!(flattener.sc_stack.is_empty());
 
         new_scene.root_pipeline_id = Some(root_pipeline_id);
         new_scene.pipeline_epochs = scene.pipeline_epochs.clone();
         new_scene.pipelines = scene.pipelines.clone();
 
@@ -1252,30 +1251,16 @@ impl<'a> DisplayListFlattener<'a> {
         match parent_id {
             Some(ref parent_id) =>
                 self.id_to_index_mapper.map_to_parent_clip_chain(reference_frame_id, parent_id),
             _ => self.id_to_index_mapper.add_clip_chain(reference_frame_id, ClipChainId::NONE, 0),
         }
         index
     }
 
-    pub fn setup_viewport_offset(
-        &mut self,
-        inner_rect: DeviceUintRect,
-        device_pixel_scale: DevicePixelScale,
-    ) {
-        let viewport_offset = (inner_rect.origin.to_vector().to_f32() / device_pixel_scale).round();
-        let root_id = self.clip_scroll_tree.root_reference_frame_index();
-        let root_node = &mut self.clip_scroll_tree.spatial_nodes[root_id.0];
-        if let SpatialNodeType::ReferenceFrame(ref mut info) = root_node.node_type {
-            info.resolved_transform =
-                LayoutVector2D::new(viewport_offset.x, viewport_offset.y).into();
-        }
-    }
-
     pub fn push_root(
         &mut self,
         pipeline_id: PipelineId,
         viewport_size: &LayoutSize,
         content_size: &LayoutSize,
     ) {
         if let ChasePrimitive::Index(prim_index) = self.config.chase_primitive {
             println!("Chasing {:?} by index", prim_index);
@@ -1479,17 +1464,20 @@ impl<'a> DisplayListFlattener<'a> {
                             info.rect = info.rect.translate(&pending_shadow.shadow.offset);
                             info.clip_rect = info.clip_rect.translate(&pending_shadow.shadow.offset);
 
                             // Construct and add a primitive for the given shadow.
                             let shadow_prim_instance = self.create_primitive(
                                 &info,
                                 pending_primitive.clip_and_scroll.clip_chain_id,
                                 pending_primitive.clip_and_scroll.spatial_node_index,
-                                pending_primitive.container.create_shadow(&pending_shadow.shadow),
+                                pending_primitive.container.create_shadow(
+                                    &pending_shadow.shadow,
+                                    &info.rect,
+                                ),
                             );
 
                             // Add the new primitive to the shadow picture.
                             prims.push(shadow_prim_instance);
                         }
                     }
 
                     // No point in adding a shadow here if there were no primitives
@@ -1682,17 +1670,19 @@ impl<'a> DisplayListFlattener<'a> {
                     repeat_horizontal: RepeatMode,
                     repeat_vertical: RepeatMode
                 ) {
                     if uv_rect.uv1.x > uv_rect.uv0.x &&
                        uv_rect.uv1.y > uv_rect.uv0.y {
 
                         // Use segment relative interpolation for all
                         // instances in this primitive.
-                        let mut brush_flags = BrushFlags::SEGMENT_RELATIVE;
+                        let mut brush_flags =
+                            BrushFlags::SEGMENT_RELATIVE |
+                            BrushFlags::SEGMENT_TEXEL_RECT;
 
                         // Enable repeat modes on the segment.
                         if repeat_horizontal == RepeatMode::Repeat {
                             brush_flags |= BrushFlags::SEGMENT_REPEAT_X;
                         }
                         if repeat_vertical == RepeatMode::Repeat {
                             brush_flags |= BrushFlags::SEGMENT_REPEAT_Y;
                         }
@@ -1840,17 +1830,22 @@ impl<'a> DisplayListFlattener<'a> {
                 };
 
                 let prim = PrimitiveContainer::Brush(
                     BrushPrimitive::new(brush_kind, Some(descriptor))
                 );
                 self.add_primitive(clip_and_scroll, info, Vec::new(), prim);
             }
             BorderDetails::Normal(ref border) => {
-                self.add_normal_border(info, border, &border_item.widths, clip_and_scroll);
+                self.add_normal_border(
+                    info,
+                    border,
+                    border_item.widths,
+                    clip_and_scroll,
+                );
             }
         }
     }
 
     pub fn create_brush_kind_for_gradient(
         &mut self,
         info: &LayoutPrimitiveInfo,
         start_point: LayoutPoint,
--- a/gfx/webrender/src/gpu_glyph_renderer.rs
+++ b/gfx/webrender/src/gpu_glyph_renderer.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/. */
 
 //! GPU glyph rasterization using Pathfinder.
 
 use api::{DeviceIntPoint, DeviceIntRect, DeviceUintSize, FontRenderMode};
 use api::{ImageFormat, TextureTarget};
 use debug_colors;
-use device::{Device, Texture, TextureFilter, VAO};
+use device::{Device, Texture, TextureDrawTarget, TextureFilter, VAO};
 use euclid::{Point2D, Size2D, Transform3D, TypedVector2D, Vector2D};
 use internal_types::RenderTargetInfo;
 use pathfinder_gfx_utils::ShelfBinPacker;
 use profiler::GpuProfileTag;
 use renderer::{self, ImageBufferKind, Renderer, RendererError, RendererStats};
 use renderer::{TextureSampler, VertexArrayKind, ShaderPrecacheFlags};
 use shade::{LazilyCompiledShader, ShaderKind};
 use tiling::GlyphJob;
@@ -189,17 +189,21 @@ impl Renderer {
             1,
         );
         self.device.upload_texture_immediate(&path_info_texture, &path_info_texels);
 
         self.gpu_glyph_renderer.vector_stencil.bind(&mut self.device,
                                                     projection,
                                                     &mut self.renderer_errors);
 
-        self.device.bind_draw_target(Some((&current_page.texture, 0)), Some(*target_size));
+        self.device.bind_draw_target(Some(TextureDrawTarget {
+            texture: &current_page.texture,
+            layer: 0,
+            with_depth: false,
+        }), Some(*target_size));
         self.device.clear_target(Some([0.0, 0.0, 0.0, 0.0]), None, None);
 
         self.device.set_blend(true);
         self.device.set_blend_mode_subpixel_pass1();
 
         let mut instance_data = vec![];
         for (path_id, &glyph_id) in glyph_indices.iter().enumerate() {
             let glyph = &glyphs[glyph_id];
--- a/gfx/webrender/src/gpu_types.rs
+++ b/gfx/webrender/src/gpu_types.rs
@@ -84,17 +84,17 @@ pub struct BlurInstance {
 #[repr(C)]
 #[cfg_attr(feature = "capture", derive(Serialize))]
 #[cfg_attr(feature = "replay", derive(Deserialize))]
 pub struct ScalingInstance {
     pub task_address: RenderTaskAddress,
     pub src_task_address: RenderTaskAddress,
 }
 
-#[derive(Debug, Copy, Clone, PartialEq)]
+#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
 #[repr(C)]
 #[cfg_attr(feature = "capture", derive(Serialize))]
 #[cfg_attr(feature = "replay", derive(Deserialize))]
 pub enum BorderSegment {
     TopLeft,
     TopRight,
     BottomRight,
     BottomLeft,
@@ -313,16 +313,18 @@ bitflags! {
         const PERSPECTIVE_INTERPOLATION = 0x1;
         /// Do interpolation relative to segment rect,
         /// rather than primitive rect.
         const SEGMENT_RELATIVE = 0x2;
         /// Repeat UVs horizontally.
         const SEGMENT_REPEAT_X = 0x4;
         /// Repeat UVs vertically.
         const SEGMENT_REPEAT_Y = 0x8;
+        /// The extra segment data is a texel rect.
+        const SEGMENT_TEXEL_RECT = 0x10;
     }
 }
 
 // TODO(gw): Some of these fields can be moved to the primitive
 //           header since they are constant, and some can be
 //           compressed to a smaller size.
 #[repr(C)]
 pub struct BrushInstance {
--- a/gfx/webrender/src/internal_types.rs
+++ b/gfx/webrender/src/internal_types.rs
@@ -33,16 +33,29 @@ pub type FastHashSet<K> = HashSet<K, Bui
 /// thread maintains a map from cache texture ID to native texture.
 ///
 /// We never reuse IDs, so we use a u64 here to be safe.
 #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
 #[cfg_attr(feature = "capture", derive(Serialize))]
 #[cfg_attr(feature = "replay", derive(Deserialize))]
 pub struct CacheTextureId(pub u64);
 
+/// Canonical type for texture layer indices.
+///
+/// WebRender is currently not very consistent about layer index types. Some
+/// places use i32 (since that's the type used in various OpenGL APIs), some
+/// places use u32 (since having it be signed is non-sensical, but the
+/// underlying graphics APIs generally operate on 32-bit integers) and some
+/// places use usize (since that's most natural in Rust).
+///
+/// Going forward, we aim to us usize throughout the codebase, since that allows
+/// operations like indexing without a cast, and convert to the required type in
+/// the device module when making calls into the platform layer.
+pub type LayerIndex = usize;
+
 /// Identifies a render pass target that is persisted until the end of the frame.
 ///
 /// By default, only the targets of the immediately-preceding pass are bound as
 /// inputs to the next pass. However, tasks can opt into having their target
 /// preserved in a list until the end of the frame, and this type specifies the
 /// index in that list.
 #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
 #[cfg_attr(feature = "capture", derive(Serialize))]
--- a/gfx/webrender/src/platform/windows/font.rs
+++ b/gfx/webrender/src/platform/windows/font.rs
@@ -126,19 +126,30 @@ impl FontContext {
     }
 
     pub fn add_native_font(&mut self, font_key: &FontKey, font_handle: dwrote::FontDescriptor) {
         if self.fonts.contains_key(font_key) {
             return;
         }
 
         let system_fc = dwrote::FontCollection::system();
-        let font = match system_fc.get_font_from_descriptor(&font_handle) {
-            Some(font) => font,
-            None => { panic!("missing descriptor {:?}", font_handle) }
+        // A version of get_font_from_descriptor() that panics early to help with bug 1455848
+        let font = if let Some(family) = system_fc.get_font_family_by_name(&font_handle.family_name) {
+            let font = family.get_first_matching_font(font_handle.weight, font_handle.stretch, font_handle.style);
+            // Exact matches only here
+            if font.weight() == font_handle.weight &&
+                font.stretch() == font_handle.stretch &&
+                font.style() == font_handle.style
+            {
+                font
+            } else {
+                panic!("font mismatch for descriptor {:?} {:?}", font_handle, font.to_descriptor())
+            }
+        } else {
+            panic!("missing font family for descriptor {:?}", font_handle)
         };
         let face = font.create_font_face();
         self.fonts.insert(*font_key, face);
     }
 
     pub fn delete_font(&mut self, font_key: &FontKey) {
         if let Some(_) = self.fonts.remove(font_key) {
             self.variations.retain(|k, _| k.0 != *font_key);
--- a/gfx/webrender/src/prim_store.rs
+++ b/gfx/webrender/src/prim_store.rs
@@ -2,35 +2,35 @@
  * 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 api::{AlphaType, BorderRadius, BuiltDisplayList, ClipMode, ColorF, PictureRect};
 use api::{DeviceIntRect, DeviceIntSize, DevicePixelScale, ExtendMode, DeviceRect, PictureToRasterTransform};
 use api::{FilterOp, GlyphInstance, GradientStop, ImageKey, ImageRendering, ItemRange, TileOffset};
 use api::{RasterSpace, LayoutPoint, LayoutRect, LayoutSideOffsets, LayoutSize, LayoutToWorldTransform};
 use api::{LayoutVector2D, PremultipliedColorF, PropertyBinding, Shadow, YuvColorSpace, YuvFormat};
-use api::{DeviceIntSideOffsets, WorldPixel, BoxShadowClipMode, LayoutToWorldScale, NormalBorder, WorldRect};
+use api::{DeviceIntSideOffsets, WorldPixel, BoxShadowClipMode, NormalBorder, WorldRect, LayoutToWorldScale};
 use api::{PicturePixel, RasterPixel, ColorDepth, LineStyle, LineOrientation, LayoutSizeAu, AuHelpers};
 use app_units::Au;
-use border::{BorderCacheKey, BorderRenderTaskInfo};
+use border::{get_max_scale_for_border, build_border_instances, create_normal_border_prim};
 use clip_scroll_tree::{ClipScrollTree, CoordinateSystemId, SpatialNodeIndex};
 use clip::{ClipNodeFlags, ClipChainId, ClipChainInstance, ClipItem, ClipNodeCollector};
 use euclid::{TypedTransform3D, TypedRect, TypedScale};
 use frame_builder::{FrameBuildingContext, FrameBuildingState, PictureContext, PictureState};
 use frame_builder::PrimitiveContext;
 use glyph_rasterizer::{FontInstance, FontTransform, GlyphKey, FONT_SIZE_LIMIT};
 use gpu_cache::{GpuBlockData, GpuCache, GpuCacheAddress, GpuCacheHandle, GpuDataRequest,
                 ToGpuBlocks};
 use gpu_types::BrushFlags;
 use image::{for_each_tile, for_each_repetition};
 use intern;
 use picture::{PictureCompositeMode, PicturePrimitive};
 #[cfg(debug_assertions)]
 use render_backend::FrameId;
-use render_task::{BlitSource, RenderTask, RenderTaskCacheKey};
+use render_task::{BlitSource, RenderTask, RenderTaskCacheKey, to_cache_size};
 use render_task::{RenderTaskCacheKeyKind, RenderTaskId, RenderTaskCacheEntryHandle};
 use renderer::{MAX_VERTEX_TEXTURE_WIDTH};
 use resource_cache::{ImageProperties, ImageRequest, ResourceCache};
 use scene::SceneProperties;
 use std::{cmp, fmt, mem, usize};
 use util::{ScaleOffset, MatrixHelpers, pack_as_float, project_rect, raster_rect_to_device_pixels};
 use smallvec::SmallVec;
 
@@ -340,23 +340,31 @@ pub struct VisibleImageTile {
 
 #[derive(Debug)]
 pub struct VisibleGradientTile {
     pub handle: GpuCacheHandle,
     pub local_rect: LayoutRect,
     pub local_clip_rect: LayoutRect,
 }
 
+/// Information about how to cache a border segment,
+/// along with the current render task cache entry.
+#[derive(Debug)]
+pub struct BorderSegmentInfo {
+    pub handle: Option<RenderTaskCacheEntryHandle>,
+    pub local_task_size: LayoutSize,
+    pub cache_key: RenderTaskCacheKey,
+    pub is_opaque: bool,
+}
+
 #[derive(Debug)]
 pub enum BorderSource {
     Image(ImageRequest),
     Border {
-        handle: Option<RenderTaskCacheEntryHandle>,
-        cache_key: BorderCacheKey,
-        task_info: Option<BorderRenderTaskInfo>,
+        segments: SmallVec<[BorderSegmentInfo; 8]>,
         border: NormalBorder,
         widths: LayoutSideOffsets,
     },
 }
 
 #[derive(Debug)]
 pub enum BrushKind {
     Solid {
@@ -451,28 +459,29 @@ impl BrushKind {
         BrushKind::Solid {
             color,
             opacity_binding: OpacityBinding::new(),
         }
     }
 
     // Construct a brush that is a border with `border` style and `widths`
     // dimensions.
-    pub fn new_border(mut border: NormalBorder, widths: LayoutSideOffsets) -> BrushKind {
+    pub fn new_border(
+        mut border: NormalBorder,
+        widths: LayoutSideOffsets,
+        segments: SmallVec<[BorderSegmentInfo; 8]>,
+    ) -> BrushKind {
         // FIXME(emilio): Is this the best place to do this?
         border.normalize(&widths);
 
-        let cache_key = BorderCacheKey::new(&border, &widths);
         BrushKind::Border {
             source: BorderSource::Border {
                 border,
                 widths,
-                cache_key,
-                task_info: None,
-                handle: None,
+                segments,
             }
         }
     }
 
     // Construct a brush that is an image wisth `stretch_size` dimensions and
     // `color`.
     pub fn new_image(
         request: ImageRequest,
@@ -1407,17 +1416,21 @@ impl PrimitiveContainer {
                 }
             }
         }
     }
 
     // Create a clone of this PrimitiveContainer, applying whatever
     // changes are necessary to the primitive to support rendering
     // it as part of the supplied shadow.
-    pub fn create_shadow(&self, shadow: &Shadow) -> PrimitiveContainer {
+    pub fn create_shadow(
+        &self,
+        shadow: &Shadow,
+        prim_rect: &LayoutRect,
+    ) -> PrimitiveContainer {
         match *self {
             PrimitiveContainer::TextRun(ref info) => {
                 let mut font = FontInstance {
                     color: shadow.color.into(),
                     ..info.specified_font.clone()
                 };
                 if shadow.blur_radius > 0.0 {
                     font.disable_subpixel_aa();
@@ -1435,32 +1448,35 @@ impl PrimitiveContainer {
                 match brush.kind {
                     BrushKind::Solid { .. } => {
                         PrimitiveContainer::Brush(BrushPrimitive::new(
                             BrushKind::new_solid(shadow.color),
                             None,
                         ))
                     }
                     BrushKind::Border { ref source } => {
-                        let source = match *source {
+                        let prim = match *source {
                             BorderSource::Image(request) => {
-                                BrushKind::Border {
-                                    source: BorderSource::Image(request)
-                                }
-                            },
+                                BrushPrimitive::new(
+                                    BrushKind::Border {
+                                        source: BorderSource::Image(request)
+                                    },
+                                    None,
+                                )
+                            }
                             BorderSource::Border { border, widths, .. } => {
                                 let border = border.with_color(shadow.color);
-                                BrushKind::new_border(border, widths)
-
+                                create_normal_border_prim(
+                                    prim_rect,
+                                    border,
+                                    widths,
+                                )
                             }
                         };
-                        PrimitiveContainer::Brush(BrushPrimitive::new(
-                            source,
-                            None,
-                        ))
+                        PrimitiveContainer::Brush(prim)
                     }
                     BrushKind::LineDecoration { style, orientation, wavy_line_thickness, .. } => {
                         PrimitiveContainer::Brush(BrushPrimitive::new_line_decoration(
                             shadow.color,
                             style,
                             orientation,
                             wavy_line_thickness,
                         ))
@@ -1955,23 +1971,16 @@ impl PrimitiveStore {
                 Some(rect) => rect,
                 None => {
                     return false;
                 }
             };
 
             prim_instance.clipped_world_rect = Some(clipped_world_rect);
 
-            prim.build_prim_segments_if_needed(
-                prim_instance,
-                pic_state,
-                frame_state,
-                frame_context,
-            );
-
             prim.update_clip_task(
                 prim_instance,
                 prim_context,
                 clipped_world_rect,
                 pic_state.raster_spatial_node_index,
                 &clip_chain,
                 pic_state,
                 frame_context,
@@ -2834,19 +2843,56 @@ impl Primitive {
                                         image_properties.descriptor.is_opaque;
 
                                     frame_state.resource_cache.request_image(
                                         request,
                                         frame_state.gpu_cache,
                                     );
                                 }
                             }
-                            BorderSource::Border { .. } => {
-                                // Handled earlier since we need to update the segment
-                                // descriptor *before* update_clip_task() is called.
+                            BorderSource::Border { ref border, ref widths, ref mut segments, .. } => {
+                                // TODO(gw): When drawing in screen raster mode, we should also incorporate a
+                                //           scale factor from the world transform to get an appropriately
+                                //           sized border task.
+                                let world_scale = LayoutToWorldScale::new(1.0);
+                                let mut scale = world_scale * frame_context.device_pixel_scale;
+                                let max_scale = get_max_scale_for_border(&border.radius, widths);
+                                scale.0 = scale.0.min(max_scale.0);
+
+                                // For each edge and corner, request the render task by content key
+                                // from the render task cache. This ensures that the render task for
+                                // this segment will be available for batching later in the frame.
+                                for segment in segments {
+                                    // Update the cache key device size based on requested scale.
+                                    segment.cache_key.size = to_cache_size(segment.local_task_size * scale);
+
+                                    segment.handle = Some(frame_state.resource_cache.request_render_task(
+                                        segment.cache_key.clone(),
+                                        frame_state.gpu_cache,
+                                        frame_state.render_tasks,
+                                        None,
+                                        segment.is_opaque,
+                                        |render_tasks| {
+                                            let task = RenderTask::new_border_segment(
+                                                segment.cache_key.size,
+                                                build_border_instances(
+                                                    &segment.cache_key,
+                                                    border,
+                                                    scale,
+                                                ),
+                                            );
+
+                                            let task_id = render_tasks.add(task);
+
+                                            pic_state.tasks.push(task_id);
+
+                                            task_id
+                                        }
+                                    ));
+                                }
                             }
                         }
                     }
                     BrushKind::RadialGradient {
                         stops_range,
                         center,
                         start_radius,
                         end_radius,
@@ -3087,105 +3133,16 @@ impl Primitive {
                     println!("\tcreated task {:?} with device rect {:?}",
                         clip_task_id, device_rect);
                 }
                 prim_instance.clip_task_id = Some(clip_task_id);
                 pic_state.tasks.push(clip_task_id);
             }
         }
     }
-
-    fn build_prim_segments_if_needed(
-        &mut self,
-        prim_instance: &mut PrimitiveInstance,
-        pic_state: &mut PictureState,
-        frame_state: &mut FrameBuildingState,
-        frame_context: &FrameBuildingContext,
-    ) {
-        let brush = match self.details {
-            PrimitiveDetails::Brush(ref mut brush) => brush,
-            PrimitiveDetails::TextRun(..) => return,
-        };
-
-        if let BrushKind::Border {
-            source: BorderSource::Border {
-                ref border,
-                ref mut cache_key,
-                ref widths,
-                ref mut handle,
-                ref mut task_info,
-                ..
-            }
-        } = brush.kind {
-            // TODO(gw): When drawing in screen raster mode, we should also incorporate a
-            //           scale factor from the world transform to get an appropriately
-            //           sized border task.
-            let world_scale = LayoutToWorldScale::new(1.0);
-            let mut scale = world_scale * frame_context.device_pixel_scale;
-            let max_scale = BorderRenderTaskInfo::get_max_scale(&border.radius, &widths);
-            scale.0 = scale.0.min(max_scale.0);
-            let scale_au = Au::from_f32_px(scale.0);
-
-            // NOTE(emilio): This `needs_update` relies on the local rect for a
-            // given primitive being immutable. If that changes, this code
-            // should probably handle changes to it as well, retaining the old
-            // size in cache_key.
-            let needs_update = scale_au != cache_key.scale;
-
-            let mut new_segments = BrushSegmentVec::new();
-
-            let local_rect = &self.metadata.local_rect;
-            if needs_update {
-                cache_key.scale = scale_au;
-
-                *task_info = BorderRenderTaskInfo::new(
-                    local_rect,
-                    border,
-                    widths,
-                    scale,
-                    &mut new_segments,
-                );
-            }
-
-            *handle = task_info.as_ref().map(|task_info| {
-                frame_state.resource_cache.request_render_task(
-                    RenderTaskCacheKey {
-                        size: task_info.cache_key_size(&local_rect.size, scale),
-                        kind: RenderTaskCacheKeyKind::Border(cache_key.clone()),
-                    },
-                    frame_state.gpu_cache,
-                    frame_state.render_tasks,
-                    None,
-                    false,          // todo
-                    |render_tasks| {
-                        let task = RenderTask::new_border(
-                            task_info.size,
-                            task_info.build_instances(border),
-                        );
-
-                        let task_id = render_tasks.add(task);
-
-                        pic_state.tasks.push(task_id);
-
-                        task_id
-                    }
-                )
-            });
-
-            if needs_update {
-                brush.segment_desc = Some(BrushSegmentDescriptor {
-                    segments: new_segments,
-                });
-
-                // The segments have changed, so force the GPU cache to
-                // re-upload the primitive information.
-                frame_state.gpu_cache.invalidate(&mut prim_instance.gpu_location);
-            }
-        }
-    }
 }
 
 pub fn get_raster_rects(
     pic_rect: PictureRect,
     map_to_raster: &SpaceMapper<PicturePixel, RasterPixel>,
     map_to_world: &SpaceMapper<RasterPixel, WorldPixel>,
     prim_bounding_rect: WorldRect,
     device_pixel_scale: DevicePixelScale,
--- a/gfx/webrender/src/render_backend.rs
+++ b/gfx/webrender/src/render_backend.rs
@@ -272,16 +272,22 @@ impl Document {
                 tx.send(self.get_scroll_node_state()).unwrap();
             }
             FrameMsg::UpdateDynamicProperties(property_bindings) => {
                 self.dynamic_properties.set_properties(property_bindings);
             }
             FrameMsg::AppendDynamicProperties(property_bindings) => {
                 self.dynamic_properties.add_properties(property_bindings);
             }
+            FrameMsg::SetPinchZoom(factor) => {
+                if self.view.pinch_zoom_factor != factor.get() {
+                    self.view.pinch_zoom_factor = factor.get();
+                    self.frame_is_valid = false;
+                }
+            }
         }
 
         DocumentOps::nop()
     }
 
     fn build_frame(
         &mut self,
         resource_cache: &mut ResourceCache,
@@ -503,19 +509,16 @@ impl RenderBackend {
 
         match message {
             SceneMsg::UpdateEpoch(pipeline_id, epoch) => {
                 txn.epoch_updates.push((pipeline_id, epoch));
             }
             SceneMsg::SetPageZoom(factor) => {
                 doc.view.page_zoom_factor = factor.get();
             }
-            SceneMsg::SetPinchZoom(factor) => {
-                doc.view.pinch_zoom_factor = factor.get();
-            }
             SceneMsg::SetWindowParameters {
                 window_size,
                 inner_rect,
                 device_pixel_ratio,
             } => {
                 doc.view.window_size = window_size;
                 doc.view.inner_rect = inner_rect;
                 doc.view.device_pixel_ratio = device_pixel_ratio;
--- a/gfx/webrender/src/render_task.rs
+++ b/gfx/webrender/src/render_task.rs
@@ -2,28 +2,28 @@
  * 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 api::{DeviceIntPoint, DeviceIntRect, DeviceIntSize, DeviceSize, DeviceIntSideOffsets};
 use api::{DevicePixelScale, ImageDescriptor, ImageFormat};
 use api::{LineStyle, LineOrientation, LayoutSize};
 #[cfg(feature = "pathfinder")]
 use api::FontRenderMode;
-use border::BorderCacheKey;
+use border::{BorderCornerCacheKey, BorderEdgeCacheKey};
 use box_shadow::{BoxShadowCacheKey};
 use clip::{ClipDataStore, ClipItem, ClipStore, ClipNodeRange};
 use clip_scroll_tree::SpatialNodeIndex;
 use device::TextureFilter;
 #[cfg(feature = "pathfinder")]
 use euclid::{TypedPoint2D, TypedVector2D};
 use freelist::{FreeList, FreeListHandle, WeakFreeListHandle};
 use glyph_rasterizer::GpuGlyphCacheKey;
 use gpu_cache::{GpuCache, GpuCacheAddress, GpuCacheHandle};
 use gpu_types::{BorderInstance, ImageSource, UvRectKind};
-use internal_types::{CacheTextureId, FastHashMap, SavedTargetIndex};
+use internal_types::{CacheTextureId, FastHashMap, LayerIndex, SavedTargetIndex};
 #[cfg(feature = "pathfinder")]
 use pathfinder_partitioner::mesh::Mesh;
 use picture::PictureCacheKey;
 use prim_store::{PrimitiveIndex, ImageCacheKey, LineDecorationCacheKey};
 #[cfg(feature = "debugger")]
 use print_tree::{PrintTreePrinter};
 use render_backend::FrameId;
 use resource_cache::{CacheItem, ResourceCache};
@@ -108,17 +108,17 @@ impl RenderTaskTree {
         }
 
         // Sanity check - can be relaxed if needed
         match task.location {
             RenderTaskLocation::Fixed(..) => {
                 debug_assert!(pass_index == passes.len() - 1);
             }
             RenderTaskLocation::Dynamic(..) |
-            RenderTaskLocation::TextureCache(..) => {
+            RenderTaskLocation::TextureCache { .. } => {
                 debug_assert!(pass_index < passes.len() - 1);
             }
         }
 
         let pass_index = if task.is_global_cached_task() {
             0
         } else {
             pass_index
@@ -187,17 +187,24 @@ pub enum RenderTaskLocation {
     /// The second member specifies the width and height of the task
     /// output, and the first member is initially left as `None`. During the
     /// build phase, we invoke `RenderTargetList::alloc()` and store the
     /// resulting location in the first member. That location identifies the
     /// render target and the offset of the allocated region within that target.
     Dynamic(Option<(DeviceIntPoint, RenderTargetIndex)>, DeviceIntSize),
     /// The output of the `RenderTask` will be persisted beyond this frame, and
     /// thus should be drawn into the `TextureCache`.
-    TextureCache(CacheTextureId, i32, DeviceIntRect),
+    TextureCache {
+        /// Which texture in the texture cache should be drawn into.
+        texture: CacheTextureId,
+        /// The target layer in the above texture.
+        layer: LayerIndex,
+        /// The target region within the above layer.
+        rect: DeviceIntRect,
+    },
 }
 
 #[derive(Debug)]
 #[cfg_attr(feature = "capture", derive(Serialize))]
 #[cfg_attr(feature = "replay", derive(Deserialize))]
 pub struct CacheMaskTask {
     actual_rect: DeviceIntRect,
     pub root_spatial_node_index: SpatialNodeIndex,
@@ -381,17 +388,17 @@ impl RenderTask {
         content_origin: DeviceIntPoint,
         children: Vec<RenderTaskId>,
         uv_rect_kind: UvRectKind,
         root_spatial_node_index: SpatialNodeIndex,
     ) -> Self {
         let size = match location {
             RenderTaskLocation::Dynamic(_, size) => size,
             RenderTaskLocation::Fixed(rect) => rect.size,
-            RenderTaskLocation::TextureCache(_, _, rect) => rect.size,
+            RenderTaskLocation::TextureCache { rect, .. } => rect.size,
         };
 
         render_task_sanity_check(&size);
 
         let can_merge = size.width as f32 >= unclipped_size.width &&
                         size.height as f32 >= unclipped_size.height;
 
         RenderTask {
@@ -650,17 +657,17 @@ impl RenderTask {
                 target_kind,
                 uv_rect_handle: GpuCacheHandle::new(),
                 uv_rect_kind,
             }),
             clear_mode,
         )
     }
 
-    pub fn new_border(
+    pub fn new_border_segment(
         size: DeviceIntSize,
         instances: Vec<BorderInstance>,
     ) -> Self {
         RenderTask::with_dynamic_location(
             size,
             Vec::new(),
             RenderTaskKind::Border(BorderTask {
                 instances,
@@ -834,17 +841,17 @@ impl RenderTask {
             }
         }
     }
 
     pub fn get_dynamic_size(&self) -> DeviceIntSize {
         match self.location {
             RenderTaskLocation::Fixed(..) => DeviceIntSize::zero(),
             RenderTaskLocation::Dynamic(_, size) => size,
-            RenderTaskLocation::TextureCache(_, _, rect) => rect.size,
+            RenderTaskLocation::TextureCache { rect, .. } => rect.size,
         }
     }
 
     pub fn get_target_rect(&self) -> (DeviceIntRect, RenderTargetIndex) {
         match self.location {
             RenderTaskLocation::Fixed(rect) => {
                 (rect, RenderTargetIndex(0))
             }
@@ -863,17 +870,17 @@ impl RenderTask {
             //           to mark a task as unused explicitly. This
             //           would allow us to restore this debug check.
             RenderTaskLocation::Dynamic(Some((origin, target_index)), size) => {
                 (DeviceIntRect::new(origin, size), target_index)
             }
             RenderTaskLocation::Dynamic(None, _) => {
                 (DeviceIntRect::zero(), RenderTargetIndex(0))
             }
-            RenderTaskLocation::TextureCache(_, layer, rect) => {
+            RenderTaskLocation::TextureCache {layer, rect, .. } => {
                 (rect, RenderTargetIndex(layer as usize))
             }
         }
     }
 
     pub fn target_kind(&self) -> RenderTargetKind {
         match self.kind {
             RenderTaskKind::Readback(..) => RenderTargetKind::Color,
@@ -1039,33 +1046,34 @@ impl RenderTask {
 
     /// Mark this render task for keeping the results alive up until the end of the frame.
     pub fn mark_for_saving(&mut self) {
         match self.location {
             RenderTaskLocation::Fixed(..) |
             RenderTaskLocation::Dynamic(..) => {
                 self.saved_index = Some(SavedTargetIndex::PENDING);
             }
-            RenderTaskLocation::TextureCache(..) => {
+            RenderTaskLocation::TextureCache { .. } => {
                 panic!("Unable to mark a permanently cached task for saving!");
             }
         }
     }
 }
 
 #[derive(Clone, Debug, Hash, PartialEq, Eq)]
 #[cfg_attr(feature = "capture", derive(Serialize))]
 #[cfg_attr(feature = "replay", derive(Deserialize))]
 pub enum RenderTaskCacheKeyKind {
     BoxShadow(BoxShadowCacheKey),
     Image(ImageCacheKey),
     #[allow(dead_code)]
     Glyph(GpuGlyphCacheKey),
     Picture(PictureCacheKey),
-    Border(BorderCacheKey),
+    BorderEdge(BorderEdgeCacheKey),
+    BorderCorner(BorderCornerCacheKey),
     LineDecoration(LineDecorationCacheKey),
 }
 
 #[derive(Clone, Debug, Hash, PartialEq, Eq)]
 #[cfg_attr(feature = "capture", derive(Serialize))]
 #[cfg_attr(feature = "replay", derive(Deserialize))]
 pub struct RenderTaskCacheKey {
     pub size: DeviceIntSize,
@@ -1156,17 +1164,17 @@ impl RenderTaskCache {
 
             if let Some(pending_render_task_id) = entry.pending_render_task_id.take() {
                 let render_task = &mut render_tasks[pending_render_task_id];
                 let target_kind = render_task.target_kind();
 
                 // Find out what size to alloc in the texture cache.
                 let size = match render_task.location {
                     RenderTaskLocation::Fixed(..) |
-                    RenderTaskLocation::TextureCache(..) => {
+                    RenderTaskLocation::TextureCache { .. } => {
                         panic!("BUG: dynamic task was expected");
                     }
                     RenderTaskLocation::Dynamic(_, size) => size,
                 };
 
                 // Select the right texture page to allocate from.
                 let image_format = match target_kind {
                     RenderTargetKind::Color => ImageFormat::BGRA8,
@@ -1198,21 +1206,21 @@ impl RenderTaskCache {
 
                 // Get the allocation details in the texture cache, and store
                 // this in the render task. The renderer will draw this
                 // task into the appropriate layer and rect of the texture
                 // cache on this frame.
                 let (texture_id, texture_layer, uv_rect) =
                     texture_cache.get_cache_location(&entry.handle);
 
-                render_task.location = RenderTaskLocation::TextureCache(
-                    texture_id,
-                    texture_layer,
-                    uv_rect.to_i32()
-                );
+                render_task.location = RenderTaskLocation::TextureCache {
+                    texture: texture_id,
+                    layer: texture_layer,
+                    rect: uv_rect.to_i32(),
+                };
             }
         }
     }
 
     pub fn request_render_task<F>(
         &mut self,
         key: RenderTaskCacheKey,
         texture_cache: &mut TextureCache,
--- a/gfx/webrender/src/renderer.rs
+++ b/gfx/webrender/src/renderer.rs
@@ -31,17 +31,17 @@ use api::{RenderApiSender, RenderNotifie
 use api::{channel};
 use api::DebugCommand;
 use api::channel::PayloadReceiverHelperMethods;
 use batch::{BatchKind, BatchTextures, BrushBatchKind};
 #[cfg(any(feature = "capture", feature = "replay"))]
 use capture::{CaptureConfig, ExternalCaptureImage, PlainExternalImage};
 use debug_colors;
 use device::{DepthFunction, Device, FrameId, Program, UploadMethod, Texture, PBO};
-use device::{ExternalTexture, FBOId, TextureSlot};
+use device::{ExternalTexture, FBOId, TextureDrawTarget, TextureReadTarget, TextureSlot};
 use device::{ShaderError, TextureFilter,
              VertexUsageHint, VAO, VBO, CustomVAO};
 use device::{ProgramCache, ReadPixelsFormat};
 #[cfg(feature = "debug_renderer")]
 use euclid::rect;
 use euclid::Transform3D;
 use frame_builder::{ChasePrimitive, FrameBuilderConfig};
 use gleam::gl;
@@ -49,17 +49,17 @@ use glyph_rasterizer::{GlyphFormat, Glyp
 use gpu_cache::{GpuBlockData, GpuCacheUpdate, GpuCacheUpdateList};
 #[cfg(feature = "debug_renderer")]
 use gpu_cache::GpuDebugChunk;
 #[cfg(feature = "pathfinder")]
 use gpu_glyph_renderer::GpuGlyphRenderer;
 use gpu_types::ScalingInstance;
 use internal_types::{TextureSource, ORTHO_FAR_PLANE, ORTHO_NEAR_PLANE, ResourceCacheError};
 use internal_types::{CacheTextureId, DebugOutput, FastHashMap, RenderedDocument, ResultMsg};
-use internal_types::{TextureUpdateList, TextureUpdateOp, TextureUpdateSource};
+use internal_types::{LayerIndex, TextureUpdateList, TextureUpdateOp, TextureUpdateSource};
 use internal_types::{RenderTargetInfo, SavedTargetIndex};
 use prim_store::DeferredResolve;
 use profiler::{BackendProfileCounters, FrameProfileCounters,
                GpuProfileTag, RendererProfileCounters, RendererProfileTimers};
 use device::query::GpuProfiler;
 use rayon::{ThreadPool, ThreadPoolBuilder};
 use record::ApiRecordingReceiver;
 use render_backend::RenderBackend;
@@ -1332,17 +1332,21 @@ impl GpuCacheTexture {
                 rows_dirty
             }
             GpuCacheBus::Scatter { ref program, ref vao, count, .. } => {
                 device.disable_depth();
                 device.set_blend(false);
                 device.bind_program(program);
                 device.bind_custom_vao(vao);
                 device.bind_draw_target(
-                    Some((texture, 0)),
+                    Some(TextureDrawTarget {
+                        texture,
+                        layer: 0,
+                        with_depth: false,
+                    }),
                     Some(texture.get_dimensions()),
                 );
                 device.draw_nonindexed_points(0, count as _);
                 0
             }
         }
     }
 }
@@ -2913,17 +2917,17 @@ impl Renderer {
             }
         }
 
         self.profile_counters.vertices.add(6 * data.len());
     }
 
     fn handle_readback_composite(
         &mut self,
-        render_target: Option<(&Texture, i32)>,
+        render_target: Option<TextureDrawTarget>,
         framebuffer_size: DeviceUintSize,
         scissor_rect: Option<DeviceIntRect>,
         source: &RenderTask,
         backdrop: &RenderTask,
         readback: &RenderTask,
     ) {
         if scissor_rect.is_some() {
             self.device.disable_scissor();
@@ -2946,34 +2950,38 @@ impl Renderer {
             RenderTaskKind::Picture(ref task_info) => task_info.content_origin,
             _ => panic!("bug: composite on non-picture?"),
         };
 
         // Bind the FBO to blit the backdrop to.
         // Called per-instance in case the layer (and therefore FBO)
         // changes. The device will skip the GL call if the requested
         // target is already bound.
-        let cache_draw_target = (cache_texture, readback_layer.0 as i32);
+        let cache_draw_target = TextureDrawTarget {
+            texture: cache_texture,
+            layer: readback_layer.0 as usize,
+            with_depth: false,
+        };
         self.device.bind_draw_target(Some(cache_draw_target), None);
 
         let mut src = DeviceIntRect::new(
             source_screen_origin + (backdrop_rect.origin - backdrop_screen_origin),
             readback_rect.size,
         );
         let mut dest = readback_rect.to_i32();
 
         // Need to invert the y coordinates and flip the image vertically when
         // reading back from the framebuffer.
         if render_target.is_none() {
             src.origin.y = framebuffer_size.height as i32 - src.size.height - src.origin.y;
             dest.origin.y += dest.size.height;
             dest.size.height = -dest.size.height;
         }
 
-        self.device.bind_read_target(render_target);
+        self.device.bind_read_target(render_target.map(|r| r.into()));
         self.device.blit_render_target(src, dest);
 
         // Restore draw target to current pass render target + layer.
         // Note: leaving the viewport unchanged, it's not a part of FBO state
         self.device.bind_draw_target(render_target, None);
 
         if scissor_rect.is_some() {
             self.device.enable_scissor();
@@ -2992,32 +3000,32 @@ impl Renderer {
         let _timer = self.gpu_profile.start_timer(GPU_TAG_BLIT);
 
         // TODO(gw): For now, we don't bother batching these by source texture.
         //           If if ever shows up as an issue, we can easily batch them.
         for blit in blits {
             let source_rect = match blit.source {
                 BlitJobSource::Texture(texture_id, layer, source_rect) => {
                     // A blit from a texture into this target.
-                    let src_texture = self.texture_resolver
+                    let texture = self.texture_resolver
                         .resolve(&texture_id)
                         .expect("BUG: invalid source texture");
-                    self.device.bind_read_target(Some((src_texture, layer)));
+                    self.device.bind_read_target(Some(TextureReadTarget { texture, layer: layer as usize }));
                     source_rect
                 }
                 BlitJobSource::RenderTask(task_id) => {
                     // A blit from the child render task into this target.
                     // TODO(gw): Support R8 format here once we start
                     //           creating mips for alpha masks.
-                    let src_texture = self.texture_resolver
+                    let texture = self.texture_resolver
                         .resolve(&TextureSource::PrevPassColor)
                         .expect("BUG: invalid source texture");
                     let source = &render_tasks[task_id];
                     let (source_rect, layer) = source.get_target_rect();
-                    self.device.bind_read_target(Some((src_texture, layer.0 as i32)));
+                    self.device.bind_read_target(Some(TextureReadTarget { texture, layer: layer.0 }));
                     source_rect
                 }
             };
             debug_assert_eq!(source_rect.size, blit.target_rect.size);
             self.device.blit_render_target(
                 source_rect,
                 blit.target_rect,
             );
@@ -3054,33 +3062,33 @@ impl Renderer {
             VertexArrayKind::Scale,
             &BatchTextures::no_texture(),
             stats,
         );
     }
 
     fn draw_color_target(
         &mut self,
-        render_target: Option<(&Texture, i32)>,
+        render_target: Option<TextureDrawTarget>,
         target: &ColorRenderTarget,
         framebuffer_target_rect: DeviceUintRect,
         target_size: DeviceUintSize,
         depth_is_ready: bool,
         clear_color: Option<[f32; 4]>,
         render_tasks: &RenderTaskTree,
         projection: &Transform3D<f32>,
         frame_id: FrameId,
         stats: &mut RendererStats,
     ) {
         self.profile_counters.color_targets.inc();
         let _gm = self.gpu_profile.start_marker("color target");
 
         // sanity check for the depth buffer
-        if let Some((texture, _)) = render_target {
-            assert!(texture.has_depth() >= target.needs_depth());
+        if let Some(t) = render_target {
+            assert!(t.texture.supports_depth() >= target.needs_depth());
         }
 
         let framebuffer_kind = if render_target.is_none() {
             FramebufferKind::Main
         } else {
             FramebufferKind::Other
         };
 
@@ -3367,27 +3375,27 @@ impl Renderer {
                 };
                 let (src_rect, _) = render_tasks[output.task_id].get_target_rect();
                 let mut dest_rect = DeviceIntRect::new(DeviceIntPoint::zero(), output_size);
 
                 // Invert Y coordinates, to correctly convert between coordinate systems.
                 dest_rect.origin.y += dest_rect.size.height;
                 dest_rect.size.height *= -1;
 
-                self.device.bind_read_target(render_target);
+                self.device.bind_read_target(render_target.map(|r| r.into()));
                 self.device.bind_external_draw_target(fbo_id);
                 self.device.blit_render_target(src_rect, dest_rect);
                 handler.unlock(output.pipeline_id);
             }
         }
     }
 
     fn draw_alpha_target(
         &mut self,
-        render_target: (&Texture, i32),
+        render_target: TextureDrawTarget,
         target: &AlphaRenderTarget,
         target_size: DeviceUintSize,
         projection: &Transform3D<f32>,
         render_tasks: &RenderTaskTree,
         stats: &mut RendererStats,
     ) {
         self.profile_counters.alpha_targets.inc();
         let _gm = self.gpu_profile.start_marker("alpha target");
@@ -3522,17 +3530,17 @@ impl Renderer {
         }
 
         self.gpu_profile.finish_sampler(alpha_sampler);
     }
 
     fn draw_texture_cache_target(
         &mut self,
         texture: &CacheTextureId,
-        layer: i32,
+        layer: LayerIndex,
         target: &TextureCacheRenderTarget,
         render_tasks: &RenderTaskTree,
         stats: &mut RendererStats,
     ) {
         let texture_source = TextureSource::TextureCache(*texture);
         let (target_size, projection) = {
             let texture = self.texture_resolver
                 .resolve(&texture_source)
@@ -3556,18 +3564,21 @@ impl Renderer {
 
         // Handle any Pathfinder glyphs.
         let stencil_page = self.stencil_glyphs(&target.glyphs, &projection, &target_size, stats);
 
         {
             let texture = self.texture_resolver
                 .resolve(&texture_source)
                 .expect("BUG: invalid target texture");
-            self.device
-                .bind_draw_target(Some((texture, layer)), Some(target_size));
+            self.device.bind_draw_target(Some(TextureDrawTarget {
+                texture,
+                layer,
+                with_depth: false,
+            }), Some(target_size));
         }
 
         self.device.disable_depth();
         self.device.disable_depth_write();
         self.set_blend(false, FramebufferKind::Other);
 
         for rect in &target.clears {
             self.device.clear_target(Some([0.0, 0.0, 0.0, 0.0]), None, Some(*rect));
@@ -3796,25 +3807,25 @@ impl Renderer {
         }
 
         counters.targets_used.inc();
 
         // Try finding a match in the existing pool. If there's no match, we'll
         // create a new texture.
         let selector = TargetSelector {
             size: list.max_size,
-            num_layers: list.targets.len() as _,
+            num_layers: list.targets.len(),
             format: list.format,
         };
         let index = self.texture_resolver.render_target_pool
             .iter()
             .position(|texture| {
                 selector == TargetSelector {
                     size: texture.get_dimensions(),
-                    num_layers: texture.get_render_target_layer_count(),
+                    num_layers: texture.get_layer_count() as usize,
                     format: texture.get_format(),
                 }
             });
 
         let rt_info = RenderTargetInfo { has_depth: list.needs_depth() };
         let texture = if let Some(idx) = index {
             let mut t = self.texture_resolver.render_target_pool.swap_remove(idx);
             self.device.reuse_render_target::<u8>(&mut t, rt_info);
@@ -3975,17 +3986,21 @@ impl Renderer {
                             alpha.max_size.width as f32,
                             0.0,
                             alpha.max_size.height as f32,
                             ORTHO_NEAR_PLANE,
                             ORTHO_FAR_PLANE,
                         );
 
                         self.draw_alpha_target(
-                            (&alpha_tex.as_ref().unwrap().texture, target_index as i32),
+                            TextureDrawTarget {
+                                texture: &alpha_tex.as_ref().unwrap().texture,
+                                layer: target_index,
+                                with_depth: false,
+                            },
                             target,
                             alpha.max_size,
                             &projection,
                             &frame.render_tasks,
                             stats,
                         );
                     }
 
@@ -3997,17 +4012,21 @@ impl Renderer {
                             color.max_size.width as f32,
                             0.0,
                             color.max_size.height as f32,
                             ORTHO_NEAR_PLANE,
                             ORTHO_FAR_PLANE,
                         );
 
                         self.draw_color_target(
-                            Some((&color_tex.as_ref().unwrap().texture, target_index as i32)),
+                            Some(TextureDrawTarget {
+                                texture: &color_tex.as_ref().unwrap().texture,
+                                layer: target_index,
+                                with_depth: target.needs_depth(),
+                            }),
                             target,
                             frame.inner_rect,
                             color.max_size,
                             false,
                             Some([0.0, 0.0, 0.0, 0.0]),
                             &frame.render_tasks,
                             &projection,
                             frame_id,
@@ -4101,34 +4120,33 @@ impl Renderer {
             return;
         }
 
         let mut spacing = 16;
         let mut size = 512;
         let fb_width = framebuffer_size.width as i32;
         let num_layers: i32 = self.texture_resolver.render_target_pool
             .iter()
-            .map(|texture| texture.get_render_target_layer_count() as i32)
+            .map(|texture| texture.get_layer_count() as i32)
             .sum();
 
         if num_layers * (size + spacing) > fb_width {
             let factor = fb_width as f32 / (num_layers * (size + spacing)) as f32;
             size = (size as f32 * factor) as i32;
             spacing = (spacing as f32 * factor) as i32;
         }
 
         let mut target_index = 0;
         for texture in &self.texture_resolver.render_target_pool {
             let dimensions = texture.get_dimensions();
             let src_rect = DeviceIntRect::new(DeviceIntPoint::zero(), dimensions.to_i32());
 
-            let layer_count = texture.get_render_target_layer_count();
-            for layer_index in 0 .. layer_count {
-                self.device
-                    .bind_read_target(Some((texture, layer_index as i32)));
+            let layer_count = texture.get_layer_count() as usize;
+            for layer in 0 .. layer_count {
+                self.device.bind_read_target(Some(TextureReadTarget { texture, layer }));
                 let x = fb_width - (spacing + size) * (target_index + 1);
                 let y = spacing;
 
                 let dest_rect = rect(x, y, size, size);
                 self.device.blit_render_target(src_rect, dest_rect);
                 target_index += 1;
             }
         }
@@ -4163,19 +4181,19 @@ impl Renderer {
                 0
             };
             let dimensions = texture.get_dimensions();
             let src_rect = DeviceIntRect::new(
                 DeviceIntPoint::zero(),
                 DeviceIntSize::new(dimensions.width as i32, dimensions.height as i32),
             );
 
-            let layer_count = texture.get_layer_count();
-            for layer_index in 0 .. layer_count {
-                self.device.bind_read_target(Some((texture, layer_index)));
+            let layer_count = texture.get_layer_count() as usize;
+            for layer in 0 .. layer_count {
+                self.device.bind_read_target(Some(TextureReadTarget { texture, layer}));
 
                 let x = fb_width - (spacing + size) * (i as i32 + 1);
 
                 // If we have more targets than fit on one row in screen, just early exit.
                 if x > fb_width {
                     return;
                 }
 
@@ -4272,17 +4290,17 @@ impl Renderer {
         pixels
     }
 
     pub fn read_gpu_cache(&mut self) -> (DeviceUintSize, Vec<u8>) {
         let texture = self.gpu_cache_texture.texture.as_ref().unwrap();
         let size = texture.get_dimensions();
         let mut texels = vec![0; (size.width * size.height * 16) as usize];
         self.device.begin_frame();
-        self.device.bind_read_target(Some((texture, 0)));
+        self.device.bind_read_target(Some(TextureReadTarget { texture, layer: 0 }));
         self.device.read_pixels_into(
             DeviceUintRect::new(DeviceUintPoint::zero(), size),
             ReadPixelsFormat::Standard(ImageFormat::RGBAF32),
             &mut texels,
         );
         self.device.bind_read_target(None);
         self.device.end_frame();
         (size, texels)
@@ -4357,16 +4375,19 @@ impl Renderer {
         report.vertex_data_textures += self.prim_header_f_texture.size_in_bytes();
         report.vertex_data_textures += self.prim_header_i_texture.size_in_bytes();
         report.vertex_data_textures += self.transforms_texture.size_in_bytes();
         report.vertex_data_textures += self.render_task_texture.size_in_bytes();
 
         // Texture cache and render target GPU memory.
         report += self.texture_resolver.report_memory();
 
+        // Textures held internally within the device layer.
+        report += self.device.report_memory();
+
         report
     }
 
     // Sets the blend mode. Blend is unconditionally set if the "show overdraw" debugging mode is
     // enabled.
     fn set_blend(&self, mut blend: bool, framebuffer_kind: FramebufferKind) {
         if framebuffer_kind == FramebufferKind::Main &&
                 self.debug_flags.contains(DebugFlags::SHOW_OVERDRAW) {
@@ -4626,17 +4647,16 @@ impl RendererStats {
 #[cfg(any(feature = "capture", feature = "replay"))]
 #[cfg_attr(feature = "capture", derive(Serialize))]
 #[cfg_attr(feature = "replay", derive(Deserialize))]
 struct PlainTexture {
     data: String,
     size: (u32, u32, i32),
     format: ImageFormat,
     filter: TextureFilter,
-    render_target: Option<RenderTargetInfo>,
 }
 
 
 #[cfg(any(feature = "capture", feature = "replay"))]
 #[cfg_attr(feature = "capture", derive(Serialize))]
 #[cfg_attr(feature = "replay", derive(Deserialize))]
 struct PlainRenderer {
     gpu_cache: PlainTexture,
@@ -4736,24 +4756,24 @@ impl Renderer {
                 .unwrap();
         }
 
         PlainTexture {
             data: short_path,
             size: (rect.size.width, rect.size.height, texture.get_layer_count()),
             format: texture.get_format(),
             filter: texture.get_filter(),
-            render_target: texture.get_render_target(),
         }
     }
 
     #[cfg(feature = "replay")]
     fn load_texture(
         target: TextureTarget,
         plain: &PlainTexture,
+        rt_info: Option<RenderTargetInfo>,
         root: &PathBuf,
         device: &mut Device
     ) -> (Texture, Vec<u8>)
     {
         use std::fs::File;
         use std::io::Read;
 
         let mut texels = Vec::new();
@@ -4763,17 +4783,17 @@ impl Renderer {
             .unwrap();
 
         let texture = device.create_texture(
             target,
             plain.format,
             plain.size.0,
             plain.size.1,
             plain.filter,
-            plain.render_target,
+            rt_info,
             plain.size.2,
         );
         device.upload_texture_immediate(&texture, &texels);
 
         (texture, texels)
     }
 
     #[cfg(feature = "capture")]
@@ -4934,29 +4954,31 @@ impl Renderer {
             for (_id, texture) in self.texture_resolver.texture_cache_map.drain() {
                 self.device.delete_texture(texture);
             }
             for (id, texture) in renderer.textures {
                 info!("\t{}", texture.data);
                 let t = Self::load_texture(
                     TextureTarget::Array,
                     &texture,
+                    Some(RenderTargetInfo { has_depth: false }),
                     &root,
                     &mut self.device
                 );
                 self.texture_resolver.texture_cache_map.insert(id, t.0);
             }
 
             info!("loading gpu cache");
             if let Some(t) = self.gpu_cache_texture.texture.take() {
                 self.device.delete_texture(t);
             }
             let (t, gpu_cache_data) = Self::load_texture(
                 TextureTarget::Default,
                 &renderer.gpu_cache,
+                Some(RenderTargetInfo { has_depth: false }),
                 &root,
                 &mut self.device,
             );
             self.gpu_cache_texture.texture = Some(t);
             match self.gpu_cache_texture.bus {
                 GpuCacheBus::PixelBuffer { ref mut rows, ref mut cpu_blocks, .. } => {
                     let dim = self.gpu_cache_texture.texture.as_ref().unwrap().get_dimensions();
                     let blocks = unsafe {
@@ -4991,21 +5013,21 @@ impl Renderer {
                     Entry::Vacant(e) => {
                         //TODO: provide a way to query both the layer count and the filter from external images
                         let (layer_count, filter) = (1, TextureFilter::Linear);
                         let plain_tex = PlainTexture {
                             data: e.key().clone(),
                             size: (descriptor.size.width, descriptor.size.height, layer_count),
                             format: descriptor.format,
                             filter,
-                            render_target: None,
                         };
                         let t = Self::load_texture(
                             target,
                             &plain_tex,
+                            None,
                             &root,
                             &mut self.device
                         );
                         let extex = t.0.into_external();
                         self.owned_external_images.insert(key, extex.clone());
                         e.insert(extex.internal_id()).clone()
                     }
                 };
--- a/gfx/webrender/src/spatial_node.rs
+++ b/gfx/webrender/src/spatial_node.rs
@@ -117,17 +117,16 @@ impl SpatialNode {
         source_perspective: Option<LayoutTransform>,
         origin_in_parent_reference_frame: LayoutVector2D,
         pipeline_id: PipelineId,
     ) -> Self {
         let identity = LayoutTransform::identity();
         let source_perspective = source_perspective.map_or_else(
             LayoutFastTransform::identity, |perspective| perspective.into());
         let info = ReferenceFrameInfo {
-            resolved_transform: LayoutFastTransform::identity(),
             source_transform: source_transform.unwrap_or(PropertyBinding::Value(identity)),
             source_perspective,
             origin_in_parent_reference_frame,
             invertible: true,
         };
         Self::new(pipeline_id, parent_index, SpatialNodeType:: ReferenceFrame(info))
     }
 
@@ -251,26 +250,26 @@ impl SpatialNode {
             SpatialNodeType::ReferenceFrame(ref mut info) => {
                 // Resolve the transform against any property bindings.
                 let source_transform = scene_properties.resolve_layout_transform(&info.source_transform);
                 // Do a change-basis operation on the perspective matrix using
                 // the scroll offset.
                 let scrolled_perspective = info.source_perspective
                     .pre_translate(&state.parent_accumulated_scroll_offset)
                     .post_translate(-state.parent_accumulated_scroll_offset);
-                info.resolved_transform =
+                let resolved_transform =
                     LayoutFastTransform::with_vector(info.origin_in_parent_reference_frame)
                     .pre_mul(&source_transform.into())
                     .pre_mul(&scrolled_perspective);
 
                 // The transformation for this viewport in world coordinates is the transformation for
                 // our parent reference frame, plus any accumulated scrolling offsets from nodes
                 // between our reference frame and this node. Finally, we also include
                 // whatever local transformation this reference frame provides.
-                let relative_transform = info.resolved_transform
+                let relative_transform = resolved_transform
                     .post_translate(state.parent_accumulated_scroll_offset)
                     .to_transform()
                     .with_destination::<LayoutPixel>();
                 self.world_viewport_transform =
                     state.parent_reference_frame_transform.pre_mul(&relative_transform.into());
                 self.world_content_transform = self.world_viewport_transform;
 
                 info.invertible = self.world_viewport_transform.is_invertible();
@@ -617,20 +616,16 @@ impl ScrollFrameInfo {
             external_id: self.external_id,
         }
     }
 }
 
 /// Contains information about reference frames.
 #[derive(Copy, Clone, Debug)]
 pub struct ReferenceFrameInfo {
-    /// The transformation that establishes this reference frame, relative to the parent
-    /// reference frame. The origin of the reference frame is included in the transformation.
-    pub resolved_transform: LayoutFastTransform,
-
     /// The source transform and perspective matrices provided by the stacking context
     /// that forms this reference frame. We maintain the property binding information
     /// here so that we can resolve the animated transform and update the tree each
     /// frame.
     pub source_transform: PropertyBinding<LayoutTransform>,
     pub source_perspective: LayoutFastTransform,
 
     /// The original, not including the transform and relative to the parent reference frame,
--- a/gfx/webrender/src/texture_cache.rs
+++ b/gfx/webrender/src/texture_cache.rs
@@ -4,17 +4,17 @@
 
 use api::{DeviceUintPoint, DeviceUintRect, DeviceUintSize};
 use api::{ExternalImageType, ImageData, ImageFormat};
 use api::ImageDescriptor;
 use device::TextureFilter;
 use freelist::{FreeList, FreeListHandle, UpsertResult, WeakFreeListHandle};
 use gpu_cache::{GpuCache, GpuCacheHandle};
 use gpu_types::{ImageSource, UvRectKind};
-use internal_types::{CacheTextureId, TextureUpdateList, TextureUpdateSource};
+use internal_types::{CacheTextureId, LayerIndex, TextureUpdateList, TextureUpdateSource};
 use internal_types::{RenderTargetInfo, TextureSource, TextureUpdate, TextureUpdateOp};
 use profiler::{ResourceProfileCounter, TextureCacheProfileCounters};
 use render_backend::FrameId;
 use resource_cache::CacheItem;
 use std::cell::Cell;
 use std::cmp;
 use std::mem;
 use std::rc::Rc;
@@ -536,22 +536,24 @@ impl TextureCache {
                     uv_rect: DeviceUintRect::new(origin, entry.size),
                     texture_layer: layer_index as i32,
                 }
             }
             None => panic!("BUG: handle not requested earlier in frame"),
         }
     }
 
-    // A more detailed version of get(). This allows access to the actual
-    // device rect of the cache allocation.
+    /// A more detailed version of get(). This allows access to the actual
+    /// device rect of the cache allocation.
+    ///
+    /// Returns a tuple identifying the texture, the layer, and the region.
     pub fn get_cache_location(
         &self,
         handle: &TextureCacheHandle,
-    ) -> (CacheTextureId, i32, DeviceUintRect) {
+    ) -> (CacheTextureId, LayerIndex, DeviceUintRect) {
         let handle = handle
             .entry
             .as_ref()
             .expect("BUG: handle not requested earlier in frame");
 
         let entry = self.entries
             .get_opt(handle)
             .expect("BUG: was dropped from cache or not updated!");
@@ -562,17 +564,17 @@ impl TextureCache {
             }
             EntryKind::Cache {
                 layer_index,
                 origin,
                 ..
             } => (layer_index, origin),
         };
         (entry.texture_id,
-         layer_index as i32,
+         layer_index as usize,
          DeviceUintRect::new(origin, entry.size))
     }
 
     pub fn mark_unused(&mut self, handle: &TextureCacheHandle) {
         if let Some(ref handle) = handle.entry {
             if let Some(entry) = self.entries.get_opt_mut(handle) {
                 // Set a very low last accessed frame to make it very likely that this entry
                 // will get cleaned up next time we try to expire entries.
--- a/gfx/webrender/src/tiling.rs
+++ b/gfx/webrender/src/tiling.rs
@@ -280,20 +280,18 @@ impl<T: RenderTarget> RenderTargetList<T
 
     pub fn needs_depth(&self) -> bool {
         self.targets.iter().any(|target| target.needs_depth())
     }
 
     pub fn check_ready(&self, t: &Texture) {
         assert_eq!(t.get_dimensions(), self.max_size);
         assert_eq!(t.get_format(), self.format);
-        assert_eq!(t.get_render_target_layer_count(), self.targets.len());
         assert_eq!(t.get_layer_count() as usize, self.targets.len());
-        assert_eq!(t.has_depth(), t.get_rt_info().unwrap().has_depth);
-        assert_eq!(t.has_depth(), self.needs_depth());
+        assert!(t.supports_depth() >= self.needs_depth());
     }
 }
 
 /// Frame output information for a given pipeline ID.
 /// Storing the task ID allows the renderer to find
 /// the target rect within the render target that this
 /// pipeline exists at.
 #[cfg_attr(feature = "capture", derive(Serialize))]
@@ -816,17 +814,17 @@ impl TextureCacheRenderTarget {
 pub enum RenderPassKind {
     /// The final pass to the main frame buffer, where we have a single color
     /// target for display to the user.
     MainFramebuffer(ColorRenderTarget),
     /// An intermediate pass, where we may have multiple targets.
     OffScreen {
         alpha: RenderTargetList<AlphaRenderTarget>,
         color: RenderTargetList<ColorRenderTarget>,
-        texture_cache: FastHashMap<(CacheTextureId, i32), TextureCacheRenderTarget>,
+        texture_cache: FastHashMap<(CacheTextureId, usize), TextureCacheRenderTarget>,
     },
 }
 
 /// A render pass represents a set of rendering operations that don't depend on one
 /// another.
 ///
 /// A render pass can have several render targets if there wasn't enough space in one
 /// target to do all of the rendering for that pass. See `RenderTargetList`.
@@ -945,18 +943,18 @@ impl RenderPass {
                 for &task_id in &self.tasks {
                     let (target_kind, texture_target) = {
                         let task = &mut render_tasks[task_id];
                         let target_kind = task.target_kind();
 
                         // Find a target to assign this task to, or create a new
                         // one if required.
                         let texture_target = match task.location {
-                            RenderTaskLocation::TextureCache(texture_id, layer, _) => {
-                                Some((texture_id, layer))
+                            RenderTaskLocation::TextureCache { texture, layer, .. } => {
+                                Some((texture, layer))
                             }
                             RenderTaskLocation::Fixed(..) => {
                                 None
                             }
                             RenderTaskLocation::Dynamic(ref mut origin, size) => {
                                 let alloc_size = DeviceUintSize::new(size.width as u32, size.height as u32);
                                 let (alloc_origin, target_index) =  match target_kind {
                                     RenderTargetKind::Color => color.allocate(alloc_size),
--- a/gfx/webrender_api/src/api.rs
+++ b/gfx/webrender_api/src/api.rs
@@ -227,17 +227,17 @@ impl Transaction {
         self.frame_ops.push(FrameMsg::ScrollNodeWithId(origin, id, clamp));
     }
 
     pub fn set_page_zoom(&mut self, page_zoom: ZoomFactor) {
         self.scene_ops.push(SceneMsg::SetPageZoom(page_zoom));
     }
 
     pub fn set_pinch_zoom(&mut self, pinch_zoom: ZoomFactor) {
-        self.scene_ops.push(SceneMsg::SetPinchZoom(pinch_zoom));
+        self.frame_ops.push(FrameMsg::SetPinchZoom(pinch_zoom));
     }
 
     pub fn set_pan(&mut self, pan: DeviceIntPoint) {
         self.frame_ops.push(FrameMsg::SetPan(pan));
     }
 
     /// Generate a new frame. When it's done and a RenderNotifier has been set
     /// in `webrender::Renderer`, [new_frame_ready()][notifier] gets called.
@@ -518,17 +518,16 @@ pub struct AddFontInstance {
     pub variations: Vec<FontVariation>,
 }
 
 // Frame messages affect building the scene.
 #[derive(Clone, Deserialize, Serialize)]
 pub enum SceneMsg {
     UpdateEpoch(PipelineId, Epoch),
     SetPageZoom(ZoomFactor),
-    SetPinchZoom(ZoomFactor),
     SetRootPipeline(PipelineId),
     RemovePipeline(PipelineId),
     SetDisplayList {
         list_descriptor: BuiltDisplayListDescriptor,
         epoch: Epoch,
         pipeline_id: PipelineId,
         background: Option<ColorF>,
         viewport_size: LayoutSize,
@@ -549,25 +548,25 @@ pub enum FrameMsg {
     HitTest(Option<PipelineId>, WorldPoint, HitTestFlags, MsgSender<HitTestResult>),
     SetPan(DeviceIntPoint),
     EnableFrameOutput(PipelineId, bool),
     Scroll(ScrollLocation, WorldPoint),
     ScrollNodeWithId(LayoutPoint, ExternalScrollId, ScrollClamping),
     GetScrollNodeState(MsgSender<Vec<ScrollNodeState>>),
     UpdateDynamicProperties(DynamicProperties),
     AppendDynamicProperties(DynamicProperties),
+    SetPinchZoom(ZoomFactor),
 }
 
 impl fmt::Debug for SceneMsg {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         f.write_str(match *self {
             SceneMsg::UpdateEpoch(..) => "SceneMsg::UpdateEpoch",
             SceneMsg::SetDisplayList { .. } => "SceneMsg::SetDisplayList",
             SceneMsg::SetPageZoom(..) => "SceneMsg::SetPageZoom",
-            SceneMsg::SetPinchZoom(..) => "SceneMsg::SetPinchZoom",
             SceneMsg::RemovePipeline(..) => "SceneMsg::RemovePipeline",
             SceneMsg::SetWindowParameters { .. } => "SceneMsg::SetWindowParameters",
             SceneMsg::SetRootPipeline(..) => "SceneMsg::SetRootPipeline",
         })
     }
 }
 
 impl fmt::Debug for FrameMsg {
@@ -577,16 +576,17 @@ impl fmt::Debug for FrameMsg {
             FrameMsg::HitTest(..) => "FrameMsg::HitTest",
             FrameMsg::SetPan(..) => "FrameMsg::SetPan",
             FrameMsg::Scroll(..) => "FrameMsg::Scroll",
             FrameMsg::ScrollNodeWithId(..) => "FrameMsg::ScrollNodeWithId",
             FrameMsg::GetScrollNodeState(..) => "FrameMsg::GetScrollNodeState",
             FrameMsg::EnableFrameOutput(..) => "FrameMsg::EnableFrameOutput",
             FrameMsg::UpdateDynamicProperties(..) => "FrameMsg::UpdateDynamicProperties",
             FrameMsg::AppendDynamicProperties(..) => "FrameMsg::AppendDynamicProperties",
+            FrameMsg::SetPinchZoom(..) => "FrameMsg::SetPinchZoom",
         })
     }
 }
 
 bitflags!{
     /// Bit flags for WR stages to store in a capture.
     // Note: capturing `FRAME` without `SCENE` is not currently supported.
     #[derive(Deserialize, Serialize)]
@@ -786,16 +786,17 @@ pub struct MemoryReport {
     pub rasterized_blobs: usize,
     //
     // GPU memory.
     //
     pub gpu_cache_textures: usize,
     pub vertex_data_textures: usize,
     pub render_target_textures: usize,
     pub texture_cache_textures: usize,
+    pub depth_target_textures: usize,
 }
 
 impl ::std::ops::AddAssign for MemoryReport {
     fn add_assign(&mut self, other: MemoryReport) {
         self.primitive_stores += other.primitive_stores;
         self.clip_stores += other.clip_stores;
         self.gpu_cache_metadata += other.gpu_cache_metadata;
         self.gpu_cache_cpu_mirror += other.gpu_cache_cpu_mirror;
@@ -803,16 +804,17 @@ impl ::std::ops::AddAssign for MemoryRep
         self.hit_testers += other.hit_testers;
         self.fonts += other.fonts;
         self.images += other.images;
         self.rasterized_blobs += other.rasterized_blobs;
         self.gpu_cache_textures += other.gpu_cache_textures;
         self.vertex_data_textures += other.vertex_data_textures;
         self.render_target_textures += other.render_target_textures;
         self.texture_cache_textures += other.texture_cache_textures;
+        self.depth_target_textures += other.depth_target_textures;
     }
 }
 
 /// A C function that takes a pointer to a heap allocation and returns its size.
 ///
 /// This is borrowed from the malloc_size_of crate, upon which we want to avoid
 /// a dependency from WebRender.
 pub type VoidPtrToSizeFn = unsafe extern "C" fn(ptr: *const c_void) -> usize;
--- a/gfx/webrender_api/src/display_item.rs
+++ b/gfx/webrender_api/src/display_item.rs
@@ -430,16 +430,39 @@ pub enum BorderStyle {
     Inset = 8,
     Outset = 9,
 }
 
 impl BorderStyle {
     pub fn is_hidden(&self) -> bool {
         *self == BorderStyle::Hidden || *self == BorderStyle::None
     }
+
+    /// Returns true if the border style itself is opaque. Other
+    /// factors (such as color, or border radii) may mean that
+    /// the border segment isn't opaque regardless of this.
+    pub fn is_opaque(&self) -> bool {
+        match *self {
+            BorderStyle::None |
+            BorderStyle::Double |
+            BorderStyle::Dotted |
+            BorderStyle::Dashed |
+            BorderStyle::Hidden => {
+                false
+            }
+
+            BorderStyle::Solid |
+            BorderStyle::Groove |
+            BorderStyle::Ridge |
+            BorderStyle::Inset |
+            BorderStyle::Outset => {
+                true
+            }
+        }
+    }
 }
 
 #[repr(u32)]
 #[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
 pub enum BoxShadowClipMode {
     Outset = 0,
     Inset = 1,
 }
--- a/gfx/webrender_bindings/revision.txt
+++ b/gfx/webrender_bindings/revision.txt
@@ -1,1 +1,1 @@
-98d507003c07c003ef0e0297dc4d29ee896a5868
+74f265e447d2927c27d4320c676779956d39eaf0
--- a/gfx/webrender_bindings/webrender_ffi_generated.h
+++ b/gfx/webrender_bindings/webrender_ffi_generated.h
@@ -537,31 +537,33 @@ struct MemoryReport {
   uintptr_t hit_testers;
   uintptr_t fonts;
   uintptr_t images;
   uintptr_t rasterized_blobs;
   uintptr_t gpu_cache_textures;
   uintptr_t vertex_data_textures;
   uintptr_t render_target_textures;
   uintptr_t texture_cache_textures;
+  uintptr_t depth_target_textures;
 
   bool operator==(const MemoryReport& aOther) const {
     return primitive_stores == aOther.primitive_stores &&
            clip_stores == aOther.clip_stores &&
            gpu_cache_metadata == aOther.gpu_cache_metadata &&
            gpu_cache_cpu_mirror == aOther.gpu_cache_cpu_mirror &&
            render_tasks == aOther.render_tasks &&
            hit_testers == aOther.hit_testers &&
            fonts == aOther.fonts &&
            images == aOther.images &&
            rasterized_blobs == aOther.rasterized_blobs &&
            gpu_cache_textures == aOther.gpu_cache_textures &&
            vertex_data_textures == aOther.vertex_data_textures &&
            render_target_textures == aOther.render_target_textures &&
-           texture_cache_textures == aOther.texture_cache_textures;
+           texture_cache_textures == aOther.texture_cache_textures &&
+           depth_target_textures == aOther.depth_target_textures;
   }
 };
 
 template<typename T, typename U>
 struct TypedSize2D {
   T width;
   T height;
 
--- a/ipc/app/plugin-container.exe.manifest
+++ b/ipc/app/plugin-container.exe.manifest
@@ -14,16 +14,26 @@
                         name="Microsoft.Windows.Common-Controls"
                         version="6.0.0.0"
                         processorArchitecture="*"
                         publicKeyToken="6595b64144ccf1df"
                         language="*"
                 />
         </dependentAssembly>
 </dependency>
+<dependency>
+        <dependentAssembly>
+                <assemblyIdentity
+                        type="win32"
+                        name="mozglue"
+                        version="1.0.0.0"
+                        language="*"
+                />
+        </dependentAssembly>
+</dependency>
   <ms_asmv3:trustInfo xmlns:ms_asmv3="urn:schemas-microsoft-com:asm.v3">
     <ms_asmv3:security>
       <ms_asmv3:requestedPrivileges>
         <ms_asmv3:requestedExecutionLevel level="asInvoker" uiAccess="false" />
       </ms_asmv3:requestedPrivileges>
     </ms_asmv3:security>
   </ms_asmv3:trustInfo>
   <ms_asmv3:application xmlns:ms_asmv3="urn:schemas-microsoft-com:asm.v3">
--- a/ipc/glue/BackgroundChild.h
+++ b/ipc/glue/BackgroundChild.h
@@ -2,17 +2,16 @@
 /* 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_ipc_backgroundchild_h__
 #define mozilla_ipc_backgroundchild_h__
 
-#include "base/process.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/ipc/Transport.h"
 
 namespace mozilla {
 namespace dom {
 
 class BlobImpl;
 class ContentChild;
@@ -42,17 +41,16 @@ class PBackgroundChild;
 //
 // The PBackgroundChild actor and all its sub-protocol actors will be
 // automatically destroyed when its designated thread completes.
 class BackgroundChild final
 {
   friend class mozilla::dom::ContentChild;
   friend class mozilla::dom::ContentParent;
 
-  typedef base::ProcessId ProcessId;
   typedef mozilla::ipc::Transport Transport;
 
 public:
   // See above.
   static PBackgroundChild*
   GetForCurrentThread();
 
   // See above.
--- a/js/src/builtin/Array.cpp
+++ b/js/src/builtin/Array.cpp
@@ -407,19 +407,16 @@ js::GetElementsWithAdder(JSContext* cx, 
         if (!adder->append(cx, val)) {
             return false;
         }
     }
 
     return true;
 }
 
-static bool
-ObjectMayHaveExtraIndexedProperties(JSObject* obj);
-
 static inline bool
 IsPackedArrayOrNoExtraIndexedProperties(JSObject* obj, uint64_t length)
 {
     return (IsPackedArray(obj) && obj->as<ArrayObject>().length() == length) ||
            !ObjectMayHaveExtraIndexedProperties(obj);
 }
 
 static bool
@@ -1045,18 +1042,18 @@ ObjectMayHaveExtraIndexedOwnProperties(J
                              obj->getClass(), INT_TO_JSID(0), obj);
 }
 
 /*
  * Whether obj may have indexed properties anywhere besides its dense
  * elements. This includes other indexed properties in its shape hierarchy, and
  * indexed properties or elements along its prototype chain.
  */
-static bool
-ObjectMayHaveExtraIndexedProperties(JSObject* obj)
+bool
+js::ObjectMayHaveExtraIndexedProperties(JSObject* obj)
 {
     MOZ_ASSERT_IF(obj->hasDynamicPrototype(), !obj->isNative());
 
     if (ObjectMayHaveExtraIndexedOwnProperties(obj)) {
         return true;
     }
 
     do {
--- a/js/src/builtin/Array.h
+++ b/js/src/builtin/Array.h
@@ -191,16 +191,19 @@ ArrayConstructor(JSContext* cx, unsigned
 
 // Like Array constructor, but doesn't perform GetPrototypeFromConstructor.
 extern bool
 array_construct(JSContext* cx, unsigned argc, Value* vp);
 
 extern bool
 IsCrossRealmArrayConstructor(JSContext* cx, const Value& v, bool* result);
 
+extern bool
+ObjectMayHaveExtraIndexedProperties(JSObject* obj);
+
 class MOZ_NON_TEMPORARY_CLASS ArraySpeciesLookup final
 {
     /*
      * An ArraySpeciesLookup holds the following:
      *
      *  Array.prototype (arrayProto_)
      *      To ensure that the incoming array has the standard proto.
      *
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/bug1494537.js
@@ -0,0 +1,125 @@
+setJitCompilerOption("ion.forceinlineCaches", 1);
+
+let offsets = [213, 559, 255, 515, 30, 507, 252, 329, 487, 7];
+
+function update_index(i, j) {
+    var offset = offsets[j % offsets.length];
+    return i + offset;
+}
+
+function compute_index(initial, count) {
+    for (var i = 0; i < count; i++) {
+        initial = update_index(initial, i);
+    }
+    return initial;
+}
+
+// This is written so that the IC added in the bug activates.
+function mutate_array(array, count, epsilon = 0) {
+    var index = 0;
+    for (var i = 0; i < count; i++) {
+        index = update_index(index, i);
+        array[index] = i + epsilon;
+    }
+    return array[offsets[0]+offsets[1]] === (1 + epsilon) &&
+           array[10] === undefined;
+}
+
+// Monomorphizing mutate_array to ensure we get the IC chains we want
+function create_variant(variant) {
+    var source = mutate_array.toString().replace("mutate_array", "mutate_array_"+variant);
+    return source;
+}
+
+function test_basic() {
+    eval(create_variant("basic"));
+    var x = [];
+
+    var count = 100;
+    assertEq(mutate_array_basic(x, count), true);
+    var end = compute_index(0, count);
+    assertEq(x[end], count - 1);
+    assertEq(x[end - 1], undefined);
+}
+
+// Ensure the IC respects frozen.
+function test_frozen() {
+    eval(create_variant("frozen"));
+    var x = [];
+    Object.freeze(x);
+
+    var count = 100;
+    assertEq(mutate_array_frozen(x, count), false);
+    assertEq(x.length, 0);
+
+    var end = compute_index(0, count);
+
+    var y = [];
+    assertEq(mutate_array_frozen(y, count), true);
+    assertEq(y[end], count - 1);
+    Object.freeze(y);
+
+    // After a mutated array is frozen, can't subsequently modify elements
+    assertEq(mutate_array_frozen(x, count, 10), false);
+    assertEq(y[end], count - 1);
+}
+
+// Let's make sure updates to the array happen as expected.
+function test_update() {
+    eval(create_variant("update"));
+
+    var x = [];
+    var count = 100;
+    assertEq(mutate_array_update(x, count), true);
+    var end = compute_index(0, count);
+    assertEq(x[end], count - 1);
+    assertEq(x[end - 1], undefined);
+
+    var epsilon = 2;
+    mutate_array_update(x, 200, epsilon);
+    assertEq(x[end], count -1 + epsilon)
+}
+
+// Elements may be non-writable, let us not write them.
+function test_nonwritable() {
+    eval(create_variant("nonwritable"));
+    var x = [];
+    var count = 100;
+    var index = compute_index(0, 10);
+    Object.defineProperty(x, index, {value: -10, writable: false});
+    mutate_array_nonwritable(x, count);
+    assertEq(x[index], -10);
+}
+
+// Random indices can get setters, let's make sure we honour those.
+function test_setter() {
+    eval(create_variant("setter"));
+    var x = [];
+    var count = 100;
+    var index = compute_index(0, 80);
+    var sigil = 0;
+    Object.defineProperty(x, index, {set(newVal) {sigil++; }});
+    mutate_array_setter(x, count);
+    assertEq(sigil, 1);
+    assertEq(x[index], undefined);
+}
+
+// Ensure indexes on the prototype don't break things;
+//
+function test_proto_indices() {
+    eval(create_variant("proto_indices"));
+    var x = [];
+    var count = 100;
+    var index = compute_index(0, 80);
+    x.__proto__[index] = "hello";
+    mutate_array_proto_indices(x, count);
+    assertEq(x.__proto__[index], "hello");
+    assertEq(x[index], 79);
+}
+
+test_basic();
+test_frozen();
+test_update();
+test_nonwritable();
+test_setter();
+test_proto_indices();
--- a/js/src/jit/BaselineCacheIRCompiler.cpp
+++ b/js/src/jit/BaselineCacheIRCompiler.cpp
@@ -1875,16 +1875,43 @@ BaselineCacheIRCompiler::emitCallProxySe
         return false;
     }
 
     stubFrame.leave(masm);
     return true;
 }
 
 bool
+BaselineCacheIRCompiler::emitCallAddOrUpdateSparseElementHelper()
+{
+    Register obj = allocator.useRegister(masm, reader.objOperandId());
+    Register id = allocator.useRegister(masm, reader.int32OperandId());
+    ValueOperand val = allocator.useValueRegister(masm, reader.valOperandId());
+    bool strict = reader.readBool();
+    AutoScratchRegister scratch(allocator, masm);
+
+    allocator.discardStack(masm);
+
+    AutoStubFrame stubFrame(*this);
+    stubFrame.enter(masm, scratch);
+
+    masm.Push(Imm32(strict));
+    masm.Push(val);
+    masm.Push(id);
+    masm.Push(obj);
+
+    if (!callVM(masm, AddOrUpdateSparseElementHelperInfo)) {
+        return false;
+    }
+    stubFrame.leave(masm);
+    return true;
+}
+
+
+bool
 BaselineCacheIRCompiler::emitMegamorphicSetElement()
 {
     Register obj = allocator.useRegister(masm, reader.objOperandId());
     ValueOperand idVal = allocator.useValueRegister(masm, reader.valOperandId());
     ValueOperand val = allocator.useValueRegister(masm, reader.valOperandId());
     bool strict = reader.readBool();
 
     allocator.discardStack(masm);
--- a/js/src/jit/CacheIR.cpp
+++ b/js/src/jit/CacheIR.cpp
@@ -3445,16 +3445,19 @@ SetPropIRGenerator::tryAttachStub()
                 return true;
             }
             if (tryAttachSetDenseElementHole(obj, objId, index, indexId, rhsValId)) {
                 return true;
             }
             if (tryAttachSetTypedElement(obj, objId, index, indexId, rhsValId)) {
                 return true;
             }
+            if (tryAttachAddOrUpdateSparseElement(obj, objId, index, indexId, rhsValId)) {
+                return true;
+            }
             return false;
         }
         return false;
     }
     return false;
 }
 
 static void
@@ -4061,16 +4064,99 @@ SetPropIRGenerator::tryAttachSetDenseEle
 
     // Type inference uses JSID_VOID for the element types.
     typeCheckInfo_.set(nobj->group(), JSID_VOID);
 
     trackAttached(isAdd ? "AddDenseElement" : "StoreDenseElementHole");
     return true;
 }
 
+// Add an IC for adding or updating a sparse array element.
+bool
+SetPropIRGenerator::tryAttachAddOrUpdateSparseElement(HandleObject obj, ObjOperandId objId,
+                                                      uint32_t index, Int32OperandId indexId,
+                                                      ValOperandId rhsId)
+{
+    JSOp op = JSOp(*pc_);
+    MOZ_ASSERT(IsPropertySetOp(op) || IsPropertyInitOp(op));
+
+    if (op != JSOP_SETELEM && op != JSOP_STRICTSETELEM) {
+        return false;
+    }
+
+    if (!obj->isNative()) {
+        return false;
+    }
+    RootedNativeObject nobj(cx_, &obj->as<NativeObject>());
+
+    // We cannot attach a stub to a non-extensible object
+    if (!nobj->isExtensible()) {
+        return false;
+    }
+
+    // Stub doesn't handle negative indices.
+    if (index > INT_MAX) {
+        return false;
+    }
+
+    // We also need to be past the end of the dense capacity, to ensure sparse.
+    if (index < nobj->getDenseInitializedLength()) {
+        return false;
+    }
+
+    // Only handle Array objects in this stub.
+    if (!nobj->is<ArrayObject>()) {
+        return false;
+    }
+    RootedArrayObject aobj(cx_, &obj->as<ArrayObject>());
+
+    // Don't attach if we're adding to an array with non-writable length.
+    bool isAdd = (index >= aobj->length());
+    if (isAdd && !aobj->lengthIsWritable()) {
+        return false;
+    }
+
+    // Indexed properties on the prototype chain aren't handled by the helper.
+    if (ObjectMayHaveExtraIndexedProperties(aobj->staticPrototype())) {
+        return false;
+    }
+
+    // Ensure we are still talking about an array class.
+    writer.guardClass(objId, GuardClassKind::Array);
+
+    // The helper we are going to call only applies to non-dense elements.
+    writer.guardIndexGreaterThanDenseInitLength(objId, indexId);
+
+    // Guard extensible: We may be trying to add a new element, and so we'd best
+    // be able to do so safely.
+    writer.guardIsExtensible(objId);
+
+    // Ensures we are able to efficiently able to map to an integral jsid.
+    writer.guardIndexIsNonNegative(indexId);
+
+    // Shape guard the prototype chain to avoid shadowing indexes from appearing.
+    // Dense elements may appear on the prototype chain (and prototypes may
+    // have a different notion of which elements are dense), but they can
+    // only be data properties, so our specialized Set handler is ok to bind
+    // to them.
+    ShapeGuardProtoChain(writer, obj, objId);
+
+    // Ensure that if we're adding an element to the object, the object's
+    // length is writable.
+    writer.guardIndexIsValidUpdateOrAdd(objId, indexId);
+
+    writer.callAddOrUpdateSparseElementHelper(objId, indexId, rhsId,
+                                              /* strict = */op == JSOP_STRICTSETELEM);
+    writer.returnFromIC();
+
+    trackAttached("AddOrUpdateSparseElement");
+    return true;
+}
+
+
 bool
 SetPropIRGenerator::tryAttachSetTypedElement(HandleObject obj, ObjOperandId objId,
                                              uint32_t index, Int32OperandId indexId,
                                              ValOperandId rhsId)
 {
     if (!obj->is<TypedArrayObject>() && !IsPrimitiveArrayTypedObject(obj)) {
         return false;
     }
--- a/js/src/jit/CacheIR.h
+++ b/js/src/jit/CacheIR.h
@@ -196,16 +196,17 @@ extern const char* const CacheKindNames[
     _(GuardIsInt32Index)                  \
     _(GuardType)                          \
     _(GuardShape)                         \
     _(GuardGroup)                         \
     _(GuardProto)                         \
     _(GuardClass)                         /* Guard an object class, per GuardClassKind */ \
     _(GuardAnyClass)                      /* Guard an arbitrary class for an object */ \
     _(GuardCompartment)                   \
+    _(GuardIsExtensible)                  \
     _(GuardIsNativeFunction)              \
     _(GuardIsNativeObject)                \
     _(GuardIsProxy)                       \
     _(GuardHasProxyHandler)               \
     _(GuardNotDOMProxy)                   \
     _(GuardSpecificObject)                \
     _(GuardSpecificAtom)                  \
     _(GuardSpecificSymbol)                \
@@ -217,16 +218,19 @@ extern const char* const CacheKindNames[
     _(GuardNoUnboxedExpando)              \
     _(GuardAndLoadUnboxedExpando)         \
     _(GuardAndGetIndexFromString)         \
     _(GuardAndGetNumberFromString)        \
     _(GuardAndGetIterator)                \
     _(GuardHasGetterSetter)               \
     _(GuardGroupHasUnanalyzedNewScript)   \
     _(GuardIndexIsNonNegative)            \
+    _(GuardIndexGreaterThanDenseCapacity) \
+    _(GuardIndexGreaterThanArrayLength)   \
+    _(GuardIndexIsValidUpdateOrAdd)       \
     _(GuardIndexGreaterThanDenseInitLength) \
     _(GuardTagNotEqual)                   \
     _(GuardXrayExpandoShapeAndDefaultProto) \
     _(GuardFunctionPrototype)             \
     _(GuardNoAllocationMetadataBuilder)   \
     _(GuardObjectGroupNotPretenured)      \
     _(LoadStackValue)                     \
     _(LoadObject)                         \
@@ -262,16 +266,17 @@ extern const char* const CacheKindNames[
     _(ArrayPush)                          \
     _(ArrayJoinResult)                    \
     _(StoreTypedElement)                  \
     _(CallNativeSetter)                   \
     _(CallScriptedSetter)                 \
     _(CallSetArrayLength)                 \
     _(CallProxySet)                       \
     _(CallProxySetByValue)                \
+    _(CallAddOrUpdateSparseElementHelper) \
     _(CallInt32ToString)                  \
     _(CallNumberToString)                 \
                                           \
     /* The *Result ops load a value into the cache's result register. */ \
     _(LoadFixedSlotResult)                \
     _(LoadDynamicSlotResult)              \
     _(LoadUnboxedPropertyResult)          \
     _(LoadTypedObjectResult)              \
@@ -760,16 +765,19 @@ class MOZ_RAII CacheIRWriter : public JS
     void guardCompartment(ObjOperandId obj, JSObject* global, JS::Compartment* compartment) {
         assertSameCompartment(global);
         writeOpWithOperandId(CacheOp::GuardCompartment, obj);
         // Add a reference to a global in the compartment to keep it alive.
         addStubField(uintptr_t(global), StubField::Type::JSObject);
         // Use RawWord, because compartments never move and it can't be GCed.
         addStubField(uintptr_t(compartment), StubField::Type::RawWord);
     }
+    void guardIsExtensible(ObjOperandId obj) {
+        writeOpWithOperandId(CacheOp::GuardIsExtensible, obj);
+    }
     void guardNoDetachedTypedObjects() {
         writeOp(CacheOp::GuardNoDetachedTypedObjects);
     }
     void guardFrameHasNoArgumentsObject() {
         writeOp(CacheOp::GuardFrameHasNoArgumentsObject);
     }
 
     Int32OperandId guardAndGetIndexFromString(StringOperandId str) {
@@ -806,16 +814,28 @@ class MOZ_RAII CacheIRWriter : public JS
 
     void guardIndexIsNonNegative(Int32OperandId index) {
         writeOpWithOperandId(CacheOp::GuardIndexIsNonNegative, index);
     }
     void guardIndexGreaterThanDenseInitLength(ObjOperandId obj, Int32OperandId index) {
         writeOpWithOperandId(CacheOp::GuardIndexGreaterThanDenseInitLength, obj);
         writeOperandId(index);
     }
+    void guardIndexGreaterThanDenseCapacity(ObjOperandId obj, Int32OperandId index) {
+        writeOpWithOperandId(CacheOp::GuardIndexGreaterThanDenseCapacity, obj);
+        writeOperandId(index);
+    }
+    void guardIndexGreaterThanArrayLength(ObjOperandId obj, Int32OperandId index) {
+        writeOpWithOperandId(CacheOp::GuardIndexGreaterThanArrayLength, obj);
+        writeOperandId(index);
+    }
+    void guardIndexIsValidUpdateOrAdd(ObjOperandId obj, Int32OperandId index) {
+        writeOpWithOperandId(CacheOp::GuardIndexIsValidUpdateOrAdd, obj);
+        writeOperandId(index);
+    }
     void guardTagNotEqual(ValueTagOperandId lhs, ValueTagOperandId rhs) {
         writeOpWithOperandId(CacheOp::GuardTagNotEqual, lhs);
         writeOperandId(rhs);
     }
 
     void loadFrameCalleeResult() {
         writeOp(CacheOp::LoadFrameCalleeResult);
     }
@@ -1036,16 +1056,22 @@ class MOZ_RAII CacheIRWriter : public JS
         buffer_.writeByte(uint32_t(strict));
     }
     void callProxySetByValue(ObjOperandId obj, ValOperandId id, ValOperandId rhs, bool strict) {
         writeOpWithOperandId(CacheOp::CallProxySetByValue, obj);
         writeOperandId(id);
         writeOperandId(rhs);
         buffer_.writeByte(uint32_t(strict));
     }
+    void callAddOrUpdateSparseElementHelper(ObjOperandId obj, Int32OperandId id, ValOperandId rhs, bool strict) {
+        writeOpWithOperandId(CacheOp::CallAddOrUpdateSparseElementHelper, obj);
+        writeOperandId(id);
+        writeOperandId(rhs);
+        buffer_.writeByte(uint32_t(strict));
+    }
     StringOperandId callInt32ToString(Int32OperandId id) {
         StringOperandId res(nextOperandId_++);
         writeOpWithOperandId(CacheOp::CallInt32ToString, id);
         writeOperandId(res);
         return res;
     }
     StringOperandId callNumberToString(ValOperandId id) {
         StringOperandId res(nextOperandId_++);
@@ -1748,16 +1774,20 @@ class MOZ_RAII SetPropIRGenerator : publ
     bool tryAttachSetDenseElement(HandleObject obj, ObjOperandId objId, uint32_t index,
                                   Int32OperandId indexId, ValOperandId rhsId);
     bool tryAttachSetTypedElement(HandleObject obj, ObjOperandId objId, uint32_t index,
                                   Int32OperandId indexId, ValOperandId rhsId);
 
     bool tryAttachSetDenseElementHole(HandleObject obj, ObjOperandId objId, uint32_t index,
                                       Int32OperandId indexId, ValOperandId rhsId);
 
+    bool tryAttachAddOrUpdateSparseElement(HandleObject obj, ObjOperandId objId, uint32_t index,
+                                           Int32OperandId indexId, ValOperandId rhsId);
+
+
     bool tryAttachGenericProxy(HandleObject obj, ObjOperandId objId, HandleId id,
                                ValOperandId rhsId, bool handleDOMProxies);
     bool tryAttachDOMProxyShadowed(HandleObject obj, ObjOperandId objId, HandleId id,
                                    ValOperandId rhsId);
     bool tryAttachDOMProxyUnshadowed(HandleObject obj, ObjOperandId objId, HandleId id,
                                      ValOperandId rhsId);
     bool tryAttachDOMProxyExpando(HandleObject obj, ObjOperandId objId, HandleId id,
                                   ValOperandId rhsId);
--- a/js/src/jit/CacheIRCompiler.cpp
+++ b/js/src/jit/CacheIRCompiler.cpp
@@ -1730,16 +1730,45 @@ CacheIRCompiler::emitGuardClass()
         masm.branchTestObjClassNoSpectreMitigations(Assembler::NotEqual, obj, clasp, scratch,
                                                     failure->label());
     }
 
     return true;
 }
 
 bool
+CacheIRCompiler::emitGuardIsExtensible()
+{
+    Register obj = allocator.useRegister(masm, reader.objOperandId());
+    AutoScratchRegister scratch(allocator, masm);
+
+    FailurePath* failure;
+    if (!addFailurePath(&failure)) {
+        return false;
+    }
+
+    Address shape(obj, ShapedObject::offsetOfShape());
+    masm.loadPtr(shape, scratch);
+
+    Address baseShape(scratch, Shape::offsetOfBaseShape());
+    masm.loadPtr(baseShape, scratch);
+
+    Address baseShapeFlags(scratch, BaseShape::offsetOfFlags());
+    masm.loadPtr(baseShapeFlags, scratch);
+
+    masm.and32(Imm32(js::BaseShape::NOT_EXTENSIBLE), scratch);
+
+    // Spectre-style checks are not needed here because we do not
+    // interpret data based on this check.
+    masm.branch32(Assembler::Equal, scratch, Imm32(js::BaseShape::NOT_EXTENSIBLE),
+                  failure->label());
+    return true;
+}
+
+bool
 CacheIRCompiler::emitGuardIsNativeFunction()
 {
     Register obj = allocator.useRegister(masm, reader.objOperandId());
     JSNative nativeFunc = reinterpret_cast<JSNative>(reader.pointer());
     AutoScratchRegister scratch(allocator, masm);
 
     FailurePath* failure;
     if (!addFailurePath(&failure)) {
@@ -2831,27 +2860,110 @@ CacheIRCompiler::emitGuardIndexGreaterTh
     FailurePath* failure;
     if (!addFailurePath(&failure)) {
         return false;
     }
 
     // Load obj->elements.
     masm.loadPtr(Address(obj, NativeObject::offsetOfElements()), scratch);
 
-    // Ensure index >= capacity.
+    // Ensure index >= initLength.
     Label outOfBounds;
     Address capacity(scratch, ObjectElements::offsetOfInitializedLength());
     masm.spectreBoundsCheck32(index, capacity, scratch2, &outOfBounds);
     masm.jump(failure->label());
     masm.bind(&outOfBounds);
 
     return true;
 }
 
 bool
+CacheIRCompiler::emitGuardIndexGreaterThanDenseCapacity()
+{
+    Register obj = allocator.useRegister(masm, reader.objOperandId());
+    Register index = allocator.useRegister(masm, reader.int32OperandId());
+    AutoScratchRegister scratch(allocator, masm);
+    AutoScratchRegister scratch2(allocator, masm);
+
+    FailurePath* failure;
+    if (!addFailurePath(&failure)) {
+        return false;
+    }
+
+    // Load obj->elements.
+    masm.loadPtr(Address(obj, NativeObject::offsetOfElements()), scratch);
+
+    // Ensure index >= capacity.
+    Label outOfBounds;
+    Address capacity(scratch, ObjectElements::offsetOfCapacity());
+    masm.spectreBoundsCheck32(index, capacity, scratch2, &outOfBounds);
+    masm.jump(failure->label());
+    masm.bind(&outOfBounds);
+
+    return true;
+}
+
+bool
+CacheIRCompiler::emitGuardIndexGreaterThanArrayLength()
+{
+    Register obj = allocator.useRegister(masm, reader.objOperandId());
+    Register index = allocator.useRegister(masm, reader.int32OperandId());
+    AutoScratchRegister scratch(allocator, masm);
+    AutoScratchRegister scratch2(allocator, masm);
+
+    FailurePath* failure;
+    if (!addFailurePath(&failure)) {
+        return false;
+    }
+
+    // Load obj->elements.
+    masm.loadPtr(Address(obj, NativeObject::offsetOfElements()), scratch);
+
+    // Ensure index >= length;
+    Label outOfBounds;
+    Address length(scratch, ObjectElements::offsetOfLength());
+    masm.spectreBoundsCheck32(index, length, scratch2, &outOfBounds);
+    masm.jump(failure->label());
+    masm.bind(&outOfBounds);
+    return true;
+}
+
+bool
+CacheIRCompiler::emitGuardIndexIsValidUpdateOrAdd()
+{
+    Register obj = allocator.useRegister(masm, reader.objOperandId());
+    Register index = allocator.useRegister(masm, reader.int32OperandId());
+    AutoScratchRegister scratch(allocator, masm);
+    AutoScratchRegister scratch2(allocator, masm);
+
+    FailurePath* failure;
+    if (!addFailurePath(&failure)) {
+        return false;
+    }
+
+    // Load obj->elements.
+    masm.loadPtr(Address(obj, NativeObject::offsetOfElements()), scratch);
+
+    Label success;
+
+    // If length is writable, branch to &success.  All indices are writable.
+    Address flags(scratch, ObjectElements::offsetOfFlags());
+    masm.branchTest32(Assembler::Zero, flags,
+                      Imm32(ObjectElements::Flags::NONWRITABLE_ARRAY_LENGTH),
+                      &success);
+
+    // Otherwise, ensure index is in bounds.
+    Address length(scratch, ObjectElements::offsetOfLength());
+    masm.spectreBoundsCheck32(index, length, scratch2,
+                              /* failure = */ failure->label());
+    masm.bind(&success);
+    return true;
+}
+
+bool
 CacheIRCompiler::emitGuardTagNotEqual()
 {
     Register lhs = allocator.useRegister(masm, reader.valueTagOperandId());
     Register rhs = allocator.useRegister(masm, reader.valueTagOperandId());
 
     FailurePath* failure;
     if (!addFailurePath(&failure)) {
         return false;
--- a/js/src/jit/CacheIRCompiler.h
+++ b/js/src/jit/CacheIRCompiler.h
@@ -27,30 +27,34 @@ namespace jit {
     _(GuardIsString)                      \
     _(GuardIsSymbol)                      \
     _(GuardIsNumber)                      \
     _(GuardIsInt32)                       \
     _(GuardIsInt32Index)                  \
     _(GuardType)                          \
     _(GuardClass)                         \
     _(GuardGroupHasUnanalyzedNewScript)   \
+    _(GuardIsExtensible)                  \
     _(GuardIsNativeFunction)              \
     _(GuardFunctionPrototype)             \
     _(GuardIsNativeObject)                \
     _(GuardIsProxy)                       \
     _(GuardNotDOMProxy)                   \
     _(GuardSpecificInt32Immediate)        \
     _(GuardMagicValue)                    \
     _(GuardNoUnboxedExpando)              \
     _(GuardAndLoadUnboxedExpando)         \
     _(GuardNoDetachedTypedObjects)        \
     _(GuardNoDenseElements)               \
     _(GuardAndGetNumberFromString)        \
     _(GuardAndGetIndexFromString)         \
     _(GuardIndexIsNonNegative)            \
+    _(GuardIndexGreaterThanDenseCapacity) \
+    _(GuardIndexGreaterThanArrayLength)   \
+    _(GuardIndexIsValidUpdateOrAdd)       \
     _(GuardIndexGreaterThanDenseInitLength) \
     _(GuardTagNotEqual)                   \
     _(GuardXrayExpandoShapeAndDefaultProto)\
     _(GuardNoAllocationMetadataBuilder)   \
     _(GuardObjectGroupNotPretenured)      \
     _(LoadObject)                         \
     _(LoadProto)                          \
     _(LoadEnclosingEnvironment)           \
--- a/js/src/jit/IonCacheIRCompiler.cpp
+++ b/js/src/jit/IonCacheIRCompiler.cpp
@@ -2261,16 +2261,38 @@ IonCacheIRCompiler::emitCallProxySetByVa
     masm.Push(val);
     masm.Push(idVal);
     masm.Push(obj);
 
     return callVM(masm, ProxySetPropertyByValueInfo);
 }
 
 bool
+IonCacheIRCompiler::emitCallAddOrUpdateSparseElementHelper()
+{
+    AutoSaveLiveRegisters save(*this);
+
+    Register obj = allocator.useRegister(masm, reader.objOperandId());
+    Register id = allocator.useRegister(masm, reader.int32OperandId());
+    ValueOperand val = allocator.useValueRegister(masm, reader.valOperandId());
+    bool strict = reader.readBool();
+
+    Label done;
+    prepareVMCall(masm, save);
+
+    masm.Push(Imm32(strict));
+    masm.Push(val);
+    masm.Push(id);
+    masm.Push(obj);
+
+    return callVM(masm, AddOrUpdateSparseElementHelperInfo);
+}
+
+
+bool
 IonCacheIRCompiler::emitMegamorphicSetElement()
 {
     AutoSaveLiveRegisters save(*this);
 
     Register obj = allocator.useRegister(masm, reader.objOperandId());
     ConstantOrRegister idVal = allocator.useConstantOrRegister(masm, reader.valOperandId());
     ConstantOrRegister val = allocator.useConstantOrRegister(masm, reader.valOperandId());
     bool strict = reader.readBool();
--- a/js/src/jit/VMFunctions.cpp
+++ b/js/src/jit/VMFunctions.cpp
@@ -2067,10 +2067,15 @@ const VMFunction ProxyHasInfo = Function
 typedef bool (*ProxyHasOwnFn)(JSContext*, HandleObject, HandleValue, MutableHandleValue);
 const VMFunction ProxyHasOwnInfo = FunctionInfo<ProxyHasOwnFn>(ProxyHasOwn, "ProxyHasOwn");
 
 typedef bool (*NativeGetElementFn)(JSContext*, HandleNativeObject, HandleValue, int32_t,
                                    MutableHandleValue);
 const VMFunction NativeGetElementInfo =
     FunctionInfo<NativeGetElementFn>(NativeGetElement, "NativeGetProperty");
 
+typedef bool (*AddOrUpdateSparseElementHelperFn)(JSContext* cx, HandleArrayObject obj,
+                                                 int32_t int_id, HandleValue v, bool strict);
+const VMFunction AddOrUpdateSparseElementHelperInfo =
+    FunctionInfo<AddOrUpdateSparseElementHelperFn>(AddOrUpdateSparseElementHelper, "AddOrUpdateSparseElementHelper");
+
 } // namespace jit
 } // namespace js
--- a/js/src/jit/VMFunctions.h
+++ b/js/src/jit/VMFunctions.h
@@ -977,15 +977,17 @@ extern const VMFunction ProxyGetProperty
 extern const VMFunction ProxyGetPropertyByValueInfo;
 extern const VMFunction ProxySetPropertyInfo;
 extern const VMFunction ProxySetPropertyByValueInfo;
 extern const VMFunction ProxyHasInfo;
 extern const VMFunction ProxyHasOwnInfo;
 
 extern const VMFunction NativeGetElementInfo;
 
+extern const VMFunction AddOrUpdateSparseElementHelperInfo;
+
 // TailCall VMFunctions
 extern const VMFunction DoConcatStringObjectInfo;
 
 } // namespace jit
 } // namespace js
 
 #endif /* jit_VMFunctions_h */
--- a/js/src/jit/x86/Assembler-x86.h
+++ b/js/src/jit/x86/Assembler-x86.h
@@ -454,30 +454,21 @@ class Assembler : public AssemblerX86Sha
     }
 
     void vhaddpd(FloatRegister src, FloatRegister dest) {
         MOZ_ASSERT(HasSSE3());
         MOZ_ASSERT(src.size() == 16);
         MOZ_ASSERT(dest.size() == 16);
         masm.vhaddpd_rr(src.encoding(), dest.encoding());
     }
-    void vsubpd(const Operand& src1, FloatRegister src0, FloatRegister dest) {
+    void vsubpd(FloatRegister src1, FloatRegister src0, FloatRegister dest) {
         MOZ_ASSERT(HasSSE2());
         MOZ_ASSERT(src0.size() == 16);
         MOZ_ASSERT(dest.size() == 16);
-        switch (src1.kind()) {
-          case Operand::MEM_REG_DISP:
-            masm.vsubpd_mr(src1.disp(), src1.base(), src0.encoding(), dest.encoding());
-            break;
-          case Operand::MEM_ADDRESS32:
-            masm.vsubpd_mr(src1.address(), src0.encoding(), dest.encoding());
-            break;
-          default:
-            MOZ_CRASH("unexpected operand kind");
-        }
+        masm.vsubpd_rr(src1.encoding(), src0.encoding(), dest.encoding());
     }
 
     void vpunpckldq(FloatRegister src1, FloatRegister src0, FloatRegister dest) {
         MOZ_ASSERT(HasSSE2());
         MOZ_ASSERT(src0.size() == 16);
         MOZ_ASSERT(src1.size() == 16);
         MOZ_ASSERT(dest.size() == 16);
         masm.vpunpckldq_rr(src1.encoding(), src0.encoding(), dest.encoding());
--- a/js/src/jit/x86/BaseAssembler-x86.h
+++ b/js/src/jit/x86/BaseAssembler-x86.h
@@ -171,24 +171,16 @@ class BaseAssemblerX86 : public BaseAsse
     {
         twoByteOpSimdFlags("vhaddpd", VEX_PD, OP2_HADDPD, src, dst);
     }
 
     void vsubpd_rr(XMMRegisterID src1, XMMRegisterID src0, XMMRegisterID dst)
     {
         twoByteOpSimd("vsubpd", VEX_PD, OP2_SUBPS_VpsWps, src1, src0, dst);
     }
-    void vsubpd_mr(int32_t offset, RegisterID base, XMMRegisterID src0, XMMRegisterID dst)
-    {
-        twoByteOpSimd("vsubpd", VEX_PD, OP2_SUBPS_VpsWps, offset, base, src0, dst);
-    }
-    void vsubpd_mr(const void* address, XMMRegisterID src0, XMMRegisterID dst)
-    {
-        twoByteOpSimd("vsubpd", VEX_PD, OP2_SUBPS_VpsWps, address, src0, dst);
-    }
 
     void vpunpckldq_rr(XMMRegisterID src1, XMMRegisterID src0, XMMRegisterID dst) {
         twoByteOpSimd("vpunpckldq", VEX_PD, OP2_PUNPCKLDQ, src1, src0, dst);
     }
     void vpunpckldq_mr(int32_t offset, RegisterID base, XMMRegisterID src0, XMMRegisterID dst)
     {
         twoByteOpSimd("vpunpckldq", VEX_PD, OP2_PUNPCKLDQ, offset, base, src0, dst);
     }
--- a/js/src/jit/x86/MacroAssembler-x86.cpp
+++ b/js/src/jit/x86/MacroAssembler-x86.cpp
@@ -1115,25 +1115,16 @@ MacroAssembler::wasmTruncateFloat32ToUIn
     if (isSaturating) {
         bind(oolRejoin);
     }
 }
 
 // ========================================================================
 // Convert floating point.
 
-// vpunpckldq requires 16-byte boundary for memory operand.
-// See convertUInt64ToDouble for the details.
-MOZ_ALIGNED_DECL(static const uint64_t, 16) TO_DOUBLE[4] = {
-    0x4530000043300000LL,
-    0x0LL,
-    0x4330000000000000LL,
-    0x4530000000000000LL
-};
-
 bool
 MacroAssembler::convertUInt64ToDoubleNeedsTemp()
 {
     return HasSSE3();
 }
 
 void
 MacroAssembler::convertUInt64ToDouble(Register64 src, FloatRegister dest, Register temp)
@@ -1182,28 +1173,44 @@ MacroAssembler::convertUInt64ToDouble(Re
     vpunpckldq(ScratchSimd128Reg, dest128, dest128);
 
     // Unpack and interleave dest and a constant C1 to dest:
     //   C1       = 0x 00000000 00000000  45300000 43300000
     //   dest     = 0x 45300000 HHHHHHHH  43300000 LLLLLLLL
     // here, each 64-bit part of dest represents following double:
     //   HI(dest) = 0x 1.00000HHHHHHHH * 2**84 == 2**84 + 0x HHHHHHHH 00000000
     //   LO(dest) = 0x 1.00000LLLLLLLL * 2**52 == 2**52 + 0x 00000000 LLLLLLLL
-    movePtr(ImmWord((uintptr_t)TO_DOUBLE), temp);
-    vpunpckldq(Operand(temp, 0), dest128, dest128);
+    // See convertUInt64ToDouble for the details.
+    static const int32_t CST1[4] = {
+        0x43300000,
+        0x45300000,
+        0x0,
+        0x0,
+    };
+
+    loadConstantSimd128Int(SimdConstant::CreateX4(CST1), ScratchSimd128Reg);
+    vpunpckldq(ScratchSimd128Reg, dest128, dest128);
 
     // Subtract a constant C2 from dest, for each 64-bit part:
     //   C2       = 0x 45300000 00000000  43300000 00000000
     // here, each 64-bit part of C2 represents following double:
     //   HI(C2)   = 0x 1.0000000000000 * 2**84 == 2**84
     //   LO(C2)   = 0x 1.0000000000000 * 2**52 == 2**52
     // after the operation each 64-bit part of dest represents following:
     //   HI(dest) = double(0x HHHHHHHH 00000000)
     //   LO(dest) = double(0x 00000000 LLLLLLLL)
-    vsubpd(Operand(temp, sizeof(uint64_t) * 2), dest128, dest128);
+    static const int32_t CST2[4] = {
+        0x0,
+        0x43300000,
+        0x0,
+        0x45300000,
+    };
+
+    loadConstantSimd128Int(SimdConstant::CreateX4(CST2), ScratchSimd128Reg);
+    vsubpd(ScratchSimd128Reg, dest128, dest128);
 
     // Add HI(dest) and LO(dest) in double and store it into LO(dest),
     //   LO(dest) = double(0x HHHHHHHH 00000000) + double(0x 00000000 LLLLLLLL)
     //            = double(0x HHHHHHHH LLLLLLLL)
     //            = double(src)
     vhaddpd(dest128, dest128);
 }
 
--- a/js/src/vm/NativeObject.cpp
+++ b/js/src/vm/NativeObject.cpp
@@ -2102,16 +2102,58 @@ DefineNonexistentProperty(JSContext* cx,
         if (!AddDataProperty(cx, obj, id, v)) {
             return false;
         }
     }
 
     return result.succeed();
 }
 
+bool
+js::AddOrUpdateSparseElementHelper(JSContext* cx, HandleArrayObject obj, int32_t int_id,
+                                   HandleValue v, bool strict)
+{
+    MOZ_ASSERT(INT_FITS_IN_JSID(int_id));
+    RootedId id(cx, INT_TO_JSID(int_id));
+
+    // This helper doesn't handle the case where the index may be in the dense elements
+    MOZ_ASSERT(int_id >= 0);
+    MOZ_ASSERT(uint32_t(int_id) >= obj->getDenseInitializedLength());
+
+    // First decide if this is an add or an update. Because the IC guards have
+    // already ensured this exists exterior to the dense array range, and the
+    // prototype checks have ensured there are no indexes on the prototype, we
+    // can use the shape lineage to find the element if it exists:
+    RootedShape shape(cx, obj->lastProperty()->search(cx, id));
+
+    // If we didn't find the shape, we're on the add path: delegate to
+    // AddSparseElement:
+    if (shape == nullptr) {
+        Rooted<PropertyDescriptor> desc(cx);
+        desc.setDataDescriptor(v, JSPROP_ENUMERATE);
+        desc.assertComplete();
+
+        return AddOrChangeProperty<IsAddOrChange::Add>(cx, obj, id, desc);
+    }
+
+    // At this point we're updating a property: See SetExistingProperty
+    if (shape->writable() && shape->isDataProperty()) {
+        // While all JSID_INT properties use a single TI entry,
+        // nothing yet has inspected the updated value so we *must* use setSlotWithType().
+        obj->setSlotWithType(cx, shape, v, /* overwriting = */ true);
+        return true;
+    }
+
+    // We don't know exactly what this object looks like, hit the slowpath.
+    RootedValue receiver(cx, ObjectValue(*obj));
+    JS::ObjectOpResult result;
+    return SetProperty(cx, obj, id, v, receiver, result) &&
+           result.checkStrictErrorOrWarning(cx, obj, id, strict);
+}
+
 
 /*** [[HasProperty]] *****************************************************************************/
 
 // ES6 draft rev31 9.1.7.1 OrdinaryHasProperty
 bool
 js::NativeHasProperty(JSContext* cx, HandleNativeObject obj, HandleId id, bool* foundp)
 {
     RootedNativeObject pobj(cx, obj);
--- a/js/src/vm/NativeObject.h
+++ b/js/src/vm/NativeObject.h
@@ -1617,16 +1617,20 @@ NativeGetElement(JSContext* cx, HandleNa
 bool
 SetPropertyByDefining(JSContext* cx, HandleId id, HandleValue v, HandleValue receiver,
                       ObjectOpResult& result);
 
 bool
 SetPropertyOnProto(JSContext* cx, HandleObject obj, HandleId id, HandleValue v,
                    HandleValue receiver, ObjectOpResult& result);
 
+bool
+AddOrUpdateSparseElementHelper(JSContext* cx, HandleArrayObject obj, int32_t int_id,
+                               HandleValue v, bool strict);
+
 /*
  * Indicates whether an assignment operation is qualified (`x.y = 0`) or
  * unqualified (`y = 0`). In strict mode, the latter is an error if no such
  * variable already exists.
  *
  * Used as an argument to NativeSetProperty.
  */
 enum QualifiedBool {
--- a/js/src/vm/Shape.h
+++ b/js/src/vm/Shape.h
@@ -1154,18 +1154,20 @@ class Shape : public gc::TenuredCell
 
     MOZ_ALWAYS_INLINE Shape* search(JSContext* cx, jsid id);
     MOZ_ALWAYS_INLINE Shape* searchLinear(jsid id);
 
     void fixupAfterMovingGC();
     void fixupGetterSetterForBarrier(JSTracer* trc);
     void updateBaseShapeAfterMovingGC();
 
+    // For JIT usage.
+    static inline size_t offsetOfBaseShape() { return offsetof(Shape, base_); }
+
 #ifdef DEBUG
-    // For JIT usage.
     static inline size_t offsetOfImmutableFlags() { return offsetof(Shape, immutableFlags); }
     static inline uint32_t fixedSlotsMask() { return FIXED_SLOTS_MASK; }
 #endif
 
   private:
     void fixupDictionaryShapeAfterMovingGC();
     void fixupShapeTreeAfterMovingGC();
 
--- a/js/src/vm/ShapedObject.h
+++ b/js/src/vm/ShapedObject.h
@@ -6,16 +6,18 @@
 
 #ifndef vm_ShapedObject_h
 #define vm_ShapedObject_h
 
 #include "vm/JSObject.h"
 
 namespace js {
 
+namespace jit { class CacheIRCompiler; }
+
 /*
  * Shaped objects are a variant of JSObject that use a GCPtrShape for their
  * |shapeOrExpando_| field. All objects that point to a js::Shape as their
  * |shapeOrExpando_| field should use this as their subclass.
  *
  * NOTE: shape()->getObjectClass() must equal getClass().
  */
 class ShapedObject : public JSObject
@@ -53,16 +55,18 @@ class ShapedObject : public JSObject
     static JSObject* fromShapeFieldPointer(uintptr_t p) {
         return reinterpret_cast<JSObject*>(p - ShapedObject::offsetOfShape());
     }
 
   private:
     // See JSObject::offsetOfGroup() comment.
     friend class js::jit::MacroAssembler;
 
+    friend class js::jit::CacheIRCompiler;
+
     static constexpr size_t offsetOfShape() {
         static_assert(offsetOfShapeOrExpando() == offsetof(shadow::Object, shape),
                       "shadow shape must match actual shape");
         return offsetOfShapeOrExpando();
     }
 };
 
 } // namespace js
--- a/js/src/vm/StructuredClone.cpp
+++ b/js/src/vm/StructuredClone.cpp
@@ -361,17 +361,16 @@ class SCInput {
     SCInput(JSContext* cx, JSStructuredCloneData& data);
 
     JSContext* context() const { return cx; }
 
     static void getPtr(uint64_t data, void** ptr);
     static void getPair(uint64_t data, uint32_t* tagp, uint32_t* datap);
 
     MOZ_MUST_USE bool read(uint64_t* p);
-    MOZ_MUST_USE bool readNativeEndian(uint64_t* p);
     MOZ_MUST_USE bool readPair(uint32_t* tagp, uint32_t* datap);
     MOZ_MUST_USE bool readDouble(double* p);
     MOZ_MUST_USE bool readBytes(void* p, size_t nbytes);
     MOZ_MUST_USE bool readChars(Latin1Char* p, size_t nchars);
     MOZ_MUST_USE bool readChars(char16_t* p, size_t nchars);
     MOZ_MUST_USE bool readPtr(void**);
 
     MOZ_MUST_USE bool get(uint64_t* p);
@@ -698,28 +697,16 @@ SCInput::read(uint64_t* p)
         return reportTruncated();
     }
     *p = NativeEndian::swapFromLittleEndian(point.peek());
     point.next();
     return true;
 }
 
 bool
-SCInput::readNativeEndian(uint64_t* p)
-{
-    if (!point.canPeek()) {
-        *p = 0;  // initialize to shut GCC up
-        return reportTruncated();
-    }
-    *p = point.peek();
-    point.next();
-    return true;
-}
-
-bool
 SCInput::readPair(uint32_t* tagp, uint32_t* datap)
 {
     uint64_t u;
     bool ok = read(&u);
     if (ok) {
         *tagp = uint32_t(u >> 32);
         *datap = uint32_t(u);
     }
@@ -849,17 +836,17 @@ SCInput::getPtr(uint64_t data, void** pt
 {
     *ptr = reinterpret_cast<void*>(NativeEndian::swapFromLittleEndian(data));
 }
 
 bool
 SCInput::readPtr(void** p)
 {
     uint64_t u;
-    if (!readNativeEndian(&u)) {
+    if (!read(&u)) {
         return false;
     }
     *p = reinterpret_cast<void*>(u);
     return true;
 }
 
 SCOutput::SCOutput(JSContext* cx, JS::StructuredCloneScope scope)
   : cx(cx), buf(scope)
--- a/js/xpconnect/shell/xpcshell.exe.manifest
+++ b/js/xpconnect/shell/xpcshell.exe.manifest
@@ -7,16 +7,26 @@
 <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
 <assemblyIdentity
         version="1.0.0.0"
         processorArchitecture="*"
         name="Mozilla.xpcshell"
         type="win32"
 />
 <description>XPConnect Shell</description>
+<dependency>
+        <dependentAssembly>
+                <assemblyIdentity
+                        type="win32"
+                        name="mozglue"
+                        version="1.0.0.0"
+                        language="*"
+                />
+        </dependentAssembly>
+</dependency>
 <ms_asmv3:trustInfo xmlns:ms_asmv3="urn:schemas-microsoft-com:asm.v3">
   <ms_asmv3:security>
     <ms_asmv3:requestedPrivileges>
       <ms_asmv3:requestedExecutionLevel level="asInvoker" uiAccess="false" />
     </ms_asmv3:requestedPrivileges>
   </ms_asmv3:security>
 </ms_asmv3:trustInfo>
   <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
--- a/moz.configure
+++ b/moz.configure
@@ -34,16 +34,22 @@ option('--enable-artifact-build-symbols'
 set_config('MOZ_ARTIFACT_BUILD_SYMBOLS',
            depends_if('--enable-artifact-build-symbols')(lambda _: True))
 
 @depends('--enable-artifact-builds')
 def imply_disable_compile_environment(value):
     if value:
         return False
 
+option(env='MOZ_COPY_PDBS',
+    help='For builds that do not support symbols in the normal fashion,'
+         ' generate and copy them into the resulting build archive.')
+
+set_config('MOZ_COPY_PDBS', depends_if('MOZ_COPY_PDBS')(lambda _: True))
+
 imply_option('--enable-compile-environment', imply_disable_compile_environment)
 
 option('--disable-compile-environment',
        help='Disable compiler/library checks')
 
 @depends('--disable-compile-environment')
 def compile_environment(compile_env):
     if compile_env:
--- a/mozglue/build/Makefile.in
+++ b/mozglue/build/Makefile.in
@@ -6,11 +6,17 @@
 # For FORCE_SHARED_LIB
 include $(topsrcdir)/config/config.mk
 
 ifeq (WINNT,$(OS_TARGET))
 mozglue.def: mozglue.def.in $(GLOBAL_DEPS)
 	$(call py_action,preprocessor,$(if $(MOZ_REPLACE_MALLOC),-DMOZ_REPLACE_MALLOC) $(ACDEFINES) $< -o $@)
 
 GARBAGE += mozglue.def
+
+# Rebuild mozglue.dll if the manifest changes - it's included by mozglue.rc.
+# (this dependency should really be just for mozglue.dll, not other targets)
+# Note the manifest file exists in the tree, so we use the explicit filename
+# here.
+EXTRA_DEPS += mozglue.dll.manifest
 endif
 
 include $(topsrcdir)/mozglue/build/replace_malloc.mk
--- a/mozglue/build/moz.build
+++ b/mozglue/build/moz.build
@@ -30,16 +30,17 @@ if CONFIG['MOZ_UBSAN']:
     ]
 
 if CONFIG['OS_TARGET'] == 'WINNT':
     DEFFILE = '!mozglue.def'
     # We'll break the DLL blocklist if we immediately load user32.dll
     DELAYLOAD_DLLS += [
         'user32.dll',
     ]
+    RCINCLUDE = 'mozglue.rc'
 
     if CONFIG['MOZ_PGO'] and CONFIG['CC_TYPE'] == 'clang-cl':
         SOURCES += ['cygprofile.cpp']
         SOURCES['cygprofile.cpp'].pgo_generate_only = True
 
     if CONFIG['CC_TYPE'] == "msvc":
         CFLAGS += ['-guard:cf']
         CXXFLAGS += ['-guard:cf']
new file mode 100644
--- /dev/null
+++ b/mozglue/build/mozglue.dll.manifest
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
+<assemblyIdentity
+    version="1.0.0.0"
+    name="mozglue"
+    type="win32"
+/>
+<file name="mozglue.dll"/>
+</assembly>
new file mode 100644
--- /dev/null
+++ b/mozglue/build/mozglue.rc
@@ -0,0 +1,6 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+1 RT_MANIFEST "mozglue.dll.manifest"
--- a/parser/html/nsHtml5StreamParser.cpp
+++ b/parser/html/nsHtml5StreamParser.cpp
@@ -172,24 +172,23 @@ nsHtml5StreamParser::nsHtml5StreamParser
   , mInterrupted(false)
   , mTerminatedMutex("nsHtml5StreamParser mTerminatedMutex")
   , mEventTarget(nsHtml5Module::GetStreamParserThread()->SerialEventTarget())
   , mExecutorFlusher(new nsHtml5ExecutorFlusher(aExecutor))
   , mLoadFlusher(new nsHtml5LoadFlusher(aExecutor))
   , mFeedChardet(false)
   , mInitialEncodingWasFromParentFrame(false)
   , mHasHadErrors(false)
-  , mFlushTimer(NS_NewTimer())
+  , mFlushTimer(NS_NewTimer(mEventTarget))
   , mFlushTimerMutex("nsHtml5StreamParser mFlushTimerMutex")
   , mFlushTimerArmed(false)
   , mFlushTimerEverFired(false)
   , mMode(aMode)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
-  mFlushTimer->SetTarget(mEventTarget);
 #ifdef DEBUG
   mAtomTable.SetPermittedLookupEventTarget(mEventTarget);
 #endif
   mTokenizer->setInterner(&mAtomTable);
   mTokenizer->setEncodingDeclarationHandler(this);
 
   if (aMode == VIEW_SOURCE_HTML || aMode == VIEW_SOURCE_XML) {
     nsHtml5Highlighter* highlighter =
--- a/python/mozbuild/mozbuild/action/check_binary.py
+++ b/python/mozbuild/mozbuild/action/check_binary.py
@@ -34,16 +34,17 @@ HOST = {
 
 TARGET = {
     'MOZ_LIBSTDCXX_VERSION':
         buildconfig.substs.get('MOZ_LIBSTDCXX_TARGET_VERSION'),
     'platform': buildconfig.substs['OS_TARGET'],
     'readelf': '{}readelf'.format(
         buildconfig.substs.get('TOOLCHAIN_PREFIX', '')),
     'nm': '{}nm'.format(buildconfig.substs.get('TOOLCHAIN_PREFIX', '')),
+    'readobj': '{}readobj'.format(buildconfig.substs.get('TOOLCHAIN_PREFIX', '')),
 }
 
 if buildconfig.substs.get('HAVE_64BIT_BUILD'):
     GUESSED_NSMODULE_SIZE = 8
 else:
     GUESSED_NSMODULE_SIZE = 4
 
 
@@ -185,17 +186,34 @@ def check_nsmodules(target, binary):
                 name = data[3].split(' = ')[0].split('@@')[0].split('@')[0] \
                               .lstrip('?')
                 if name.endswith('_NSModule') or name in (
                         '__start_kPStaticModules',
                         '__stop_kPStaticModules'):
                     symbols.append((int(data[2], 16), GUESSED_NSMODULE_SIZE,
                                     name))
     else:
-        for line in get_output(target['nm'], '-P', binary):
+        # MinGW-Clang, when building pdbs, doesn't include the symbol table into
+        # the final module. To get the NSModule info, we can look at the exported
+        # symbols. (#1475562)
+        if buildconfig.substs['OS_ARCH'] == 'WINNT' and \
+           buildconfig.substs['HOST_OS_ARCH'] != 'WINNT':
+            readobj_output = get_output(target['readobj'], '-coff-exports', binary)
+            # Transform the output of readobj into nm-like output
+            output = []
+            for line in readobj_output:
+                if "Name" in line:
+                    name = line.replace("Name:", "").strip()
+                elif "RVA" in line:
+                    rva = line.replace("RVA:", "").strip()
+                    output.append("%s r %s" % (name, rva))
+        else:
+            output = get_output(target['nm'], '-P', binary)
+
+        for line in output:
             data = line.split()
             # Some symbols may not have a size listed at all.
             if len(data) == 3:
                 data.append('0')
             if len(data) == 4:
                 sym, _, addr, size = data
                 # NSModules symbols end with _NSModule or _NSModuleE when
                 # C++-mangled.
--- a/taskcluster/scripts/misc/build-clang-7-mingw.sh
+++ b/taskcluster/scripts/misc/build-clang-7-mingw.sh
@@ -270,16 +270,21 @@ build_windres() {
                                                 --disable-multilib \
                                                 --disable-nls \
                                                 --target=$machine-w64-mingw32
   make $make_flags
 
   # Manually install only nm and windres
   cp binutils/windres $INSTALL_DIR/bin/$machine-w64-mingw32-windres
   cp binutils/nm-new $INSTALL_DIR/bin/$machine-w64-mingw32-nm
+
+  pushd $INSTALL_DIR/bin/
+  ln -s llvm-readobj $machine-w64-mingw32-readobj
+  popd
+
   popd
 }
 
 export PATH=$INSTALL_DIR/bin:$PATH
 
 prepare
 
 # gets a bit too verbose here
--- a/testing/marionette/reftest.js
+++ b/testing/marionette/reftest.js
@@ -92,17 +92,17 @@ reftest.Runner = class {
     this.reftestWin = reftestWin;
     return reftestWin;
   }
 
   async openWindow() {
     let reftestWin = this.parentWindow.open(
         "chrome://marionette/content/reftest.xul",
         "reftest",
-        "chrome,dialog,height=600,width=600");
+        "chrome,height=600,width=600");
 
     await new Promise(resolve => {
       reftestWin.addEventListener("load", resolve, {once: true});
     });
 
     let browser = reftestWin.document.createElementNS(XUL_NS, "xul:browser");
     browser.permanentKey = {};
     browser.setAttribute("id", "browser");
--- a/testing/marionette/reftest.xul
+++ b/testing/marionette/reftest.xul
@@ -1,2 +1,5 @@
-<window id="reftest" xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+<window id="reftest"
+        xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+        hidechrome="true"
+        style="background-color:white; overflow:hidden">
 </window>
\ No newline at end of file
--- a/testing/mozbase/mozdevice/tests/manifest.ini
+++ b/testing/mozbase/mozdevice/tests/manifest.ini
@@ -1,6 +1,7 @@
 [DEFAULT]
 subsuite = mozbase, os == "linux"
 skip-if = python == 3
 [test_socket_connection.py]
 [test_is_app_installed.py]
 [test_chown.py]
+[test_escape_command_line.py]
new file mode 100644
--- /dev/null
+++ b/testing/mozbase/mozdevice/tests/test_escape_command_line.py
@@ -0,0 +1,23 @@
+#!/usr/bin/env python
+
+from __future__ import absolute_import
+
+import mozunit
+
+
+def test_escape_command_line(mock_adb_object, redirect_stdout_and_assert):
+    """Test _escape_command_line."""
+    cases = {
+        # expected output : test input
+        'adb shell ls -l': ['adb', 'shell', 'ls', '-l'],
+        'adb shell "ls -l"': ['adb', 'shell', 'ls -l'],
+        '-e "if (true)"': ['-e', 'if (true)'],
+        '-e "if (x === \\"hello\\")"': ['-e', 'if (x === "hello")'],
+        '-e "if (x === \'hello\')"': ['-e', "if (x === 'hello')"],
+    }
+    for expected, input in cases.items():
+        assert mock_adb_object._escape_command_line(input) == expected
+
+
+if __name__ == '__main__':
+    mozunit.main()
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/background-fetch/abort.https.window.js.ini
@@ -0,0 +1,7 @@
+[abort.https.window.html]
+  [Calling BackgroundFetchRegistration.abort sets the correct fields and responses are still available]
+    expected: FAIL
+
+  [Aborting the same registration twice fails]
+    expected: FAIL
+
--- a/testing/web-platform/meta/background-fetch/fetch.https.window.js.ini
+++ b/testing/web-platform/meta/background-fetch/fetch.https.window.js.ini
@@ -33,8 +33,11 @@
     expected: FAIL
 
   [Requests with text/json content type require CORS Preflight and succeed.]
     expected: FAIL
 
   [Requests with PUT method require CORS Preflight and succeed.]
     expected: FAIL
 
+  [Registration object gets updated values when a background fetch completes.]
+    expected: FAIL
+
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/css/compositing/mix-blend-mode/mix-blend-mode-parent-element-overflow-scroll.html.ini
@@ -0,0 +1,3 @@
+[mix-blend-mode-parent-element-overflow-scroll.html]
+  disabled:
+    if os == "linux": https://bugzilla.mozilla.org/show_bug.cgi?id=1499779
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/css/css-properties-values-api/self-utils.html.ini
@@ -0,0 +1,7 @@
+[self-utils.html]
+  [Default initial values of generated properties are valid (self-test).]
+    expected: FAIL
+
+  [Generated properties respect inherits flag]
+    expected: FAIL
+
--- a/testing/web-platform/meta/dom/interfaces.html.ini
+++ b/testing/web-platform/meta/dom/interfaces.html.ini
@@ -83,32 +83,30 @@
 
   [Range interface: existence and properties of interface object]
     expected: FAIL
 
   [Range interface: existence and properties of interface prototype object]
     expected: FAIL
 
 
-
 [interfaces.html?1-1000]
   [Test driver]
     expected: FAIL
 
 
 [interfaces.html?1001-last]
   [Test driver]
     expected: FAIL
 
 
 [interfaces.html?exclude=Node]
   [Test driver]
     expected: FAIL
 
-
   [Document interface: attribute origin]
     expected: FAIL
 
   [Document interface: new Document() must inherit property "origin" with the proper type]
     expected: FAIL
 
   [Document interface: xmlDoc must inherit property "origin" with the proper type]
     expected: FAIL
@@ -170,17 +168,16 @@
   [Range interface: existence and properties of interface prototype object]
     expected: FAIL
 
 
 [interfaces.html?include=Node]
 
 [interfaces.html?exclude=Node]
   prefs: [dom.window.event.enabled:true]
-
   [Document interface: attribute origin]
     expected: FAIL
 
   [Document interface: new Document() must inherit property "origin" with the proper type]
     expected: FAIL
 
   [Document interface: xmlDoc must inherit property "origin" with the proper type]
     expected: FAIL
@@ -237,8 +234,11 @@
     expected: FAIL
 
   [Range interface: existence and properties of interface object]
     expected: FAIL
 
   [Range interface: existence and properties of interface prototype object]
     expected: FAIL
 
+  [Document interface: existence and properties of interface prototype object's @@unscopables property]
+    expected: FAIL
+
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/feature-policy/experimental-features/lazyload/lazyload-image-attribute-on-sanity-check-tentative.sub.html.ini
@@ -0,0 +1,4 @@
+[lazyload-image-attribute-on-sanity-check-tentative.sub.html]
+  [Verify 'lazyload' attribute state 'on' works as expected: image loads only when in viewport.]
+    expected: FAIL
+
--- a/testing/web-platform/meta/feature-policy/picture-in-picture-allowed-by-feature-policy-attribute-redirect-on-load.https.sub.html.ini
+++ b/testing/web-platform/meta/feature-policy/picture-in-picture-allowed-by-feature-policy-attribute-redirect-on-load.https.sub.html.ini
@@ -1,7 +1,10 @@
 [picture-in-picture-allowed-by-feature-policy-attribute-redirect-on-load.https.sub.html]
   [Feature-Policy allow="picture-in-picture" allows same-origin navigation in an iframe.]
     expected: FAIL
 
   [Untitled]
     expected: FAIL
 
+  [Feature-Policy allow="picture-in-picture" disallows cross-origin navigation in an iframe.]
+    expected: FAIL
+
--- a/testing/web-platform/meta/feature-policy/picture-in-picture-disabled-by-feature-policy.https.sub.html.ini
+++ b/testing/web-platform/meta/feature-policy/picture-in-picture-disabled-by-feature-policy.https.sub.html.ini
@@ -1,4 +1,13 @@
 [picture-in-picture-disabled-by-feature-policy.https.sub.html]
   [Untitled]
     expected: FAIL
 
+  [Feature-Policy header: picture-in-picture "none" disallows cross-origin iframes.]
+    expected: FAIL
+
+  [Feature-Policy header: picture-in-picture "none" disallows same-origin iframes.]
+    expected: FAIL
+
+  [Feature-Policy header: picture-in-picture "none" disallows the top-level document.]
+    expected: FAIL
+
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/feature-policy/reporting/generic-sensor-reporting.https.html.ini
@@ -0,0 +1,4 @@
+[generic-sensor-reporting.https.html]
+  [Generic Sensor Report Format]
+    expected: FAIL
+
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/fetch/api/request/destination/fetch-destination.https.html.ini
@@ -0,0 +1,5 @@
+[fetch-destination.https.html]
+  [HTMLLinkElement with rel=preload and as=audio fetches with a "audio" Request.destination]
+    expected:
+      if not debug and webrender and e10s and (os == "linux") and (version == "Ubuntu 16.04") and (processor == "x86_64") and (bits == 64): FAIL
+
--- a/testing/web-platform/meta/html/rendering/bindings/the-textarea-element-0/cols-zero.html.ini
+++ b/testing/web-platform/meta/html/rendering/bindings/the-textarea-element-0/cols-zero.html.ini
@@ -1,5 +1,3 @@
 [cols-zero.html]
   disabled:
     if verify and (os == "mac"): fails in verify mode
-  expected:
-    if os == "mac": FAIL
--- a/testing/web-platform/meta/html/rendering/bindings/the-textarea-element-0/rows-zero.html.ini
+++ b/testing/web-platform/meta/html/rendering/bindings/the-textarea-element-0/rows-zero.html.ini
@@ -1,5 +1,3 @@
 [rows-zero.html]
   disabled:
     if verify and (os == "mac"): fails in verify mode
-  expected:
-    if e10s and (os == "mac") and (version == "OS X 10.10.5") and (processor == "x86_64") and (bits == 64): FAIL
--- a/testing/web-platform/meta/html/rendering/non-replaced-elements/lists/ol-type-unsupported-invalid.html.ini
+++ b/testing/web-platform/meta/html/rendering/non-replaced-elements/lists/ol-type-unsupported-invalid.html.ini
@@ -1,5 +1,3 @@
 [ol-type-unsupported-invalid.html]
   disabled:
     if verify and (os == "mac"): fails in verify mode
-  expected:
-    if e10s and (os == "mac") and (version == "OS X 10.10.5") and (processor == "x86_64") and (bits == 64): FAIL
--- a/testing/web-platform/meta/html/rendering/non-replaced-elements/lists/ul-type-supported-xhtml.xhtml.ini
+++ b/testing/web-platform/meta/html/rendering/non-replaced-elements/lists/ul-type-supported-xhtml.xhtml.ini
@@ -1,5 +1,3 @@
 [ul-type-supported-xhtml.xhtml]
   disabled:
     if verify and (os == "mac"): fails in verify mode
-  expected:
-    if e10s and (os == "mac") and (version == "OS X 10.10.5") and (processor == "x86_64") and (bits == 64): FAIL
--- a/testing/web-platform/meta/html/rendering/non-replaced-elements/lists/ul-type-supported.html.ini
+++ b/testing/web-platform/meta/html/rendering/non-replaced-elements/lists/ul-type-supported.html.ini
@@ -1,5 +1,3 @@
 [ul-type-supported.html]
   disabled:
     if verify and (os == "mac"): fails in verify mode
-  expected:
-    if e10s and (os == "mac") and (version == "OS X 10.10.5") and (processor == "x86_64") and (bits == 64): FAIL
--- a/testing/web-platform/meta/html/rendering/non-replaced-elements/lists/ul-type-unsupported-invalid.html.ini
+++ b/testing/web-platform/meta/html/rendering/non-replaced-elements/lists/ul-type-unsupported-invalid.html.ini
@@ -1,5 +1,3 @@
 [ul-type-unsupported-invalid.html]
   disabled:
     if verify and (os == "mac"): fails in verify mode
-  expected:
-    if e10s and (os == "mac") and (version == "OS X 10.10.5") and (processor == "x86_64") and (bits == 64): FAIL
--- a/testing/web-platform/meta/html/rendering/the-details-element/details-before.html.ini
+++ b/testing/web-platform/meta/html/rendering/the-details-element/details-before.html.ini
@@ -1,7 +1,4 @@
 [details-before.html]
   disabled:
     if verify and (os == "mac"): fails in verify mode
-  expected:
-    if debug and not webrender and e10s and (os == "mac") and (version == "OS X 10.10.5") and (processor == "x86_64") and (bits == 64): PASS
-    if not debug and not webrender and e10s and (os == "mac") and (version == "OS X 10.10.5") and (processor == "x86_64") and (bits == 64): PASS
-    FAIL
+  expected: FAIL
--- a/testing/web-platform/meta/html/semantics/embedded-content/the-iframe-element/iframe-with-base.html.ini
+++ b/testing/web-platform/meta/html/semantics/embedded-content/the-iframe-element/iframe-with-base.html.ini
@@ -1,5 +1,3 @@
 [iframe-with-base.html]
   disabled:
     if verify and (os == "mac"): fails in verify mode
-  expected:
-    if e10s and (os == "mac") and (version == "OS X 10.10.5") and (processor == "x86_64") and (bits == 64): FAIL
--- a/testing/web-platform/meta/html/semantics/embedded-content/the-img-element/document-adopt-base-url.html.ini
+++ b/testing/web-platform/meta/html/semantics/embedded-content/the-img-element/document-adopt-base-url.html.ini
@@ -1,5 +1,3 @@
 [document-adopt-base-url.html]
   disabled:
     if verify and (os == "mac"): fails in verify mode
-  expected:
-    if os == "mac": FAIL
deleted file mode 100644
--- a/testing/web-platform/meta/infrastructure/reftest/reftest_or_0.html.ini
+++ /dev/null
@@ -1,4 +0,0 @@
-[reftest_or_0.html]
-  expected:
-    if debug and not webrender and e10s and (os == "mac") and (version == "OS X 10.10.5") and (processor == "x86_64") and (bits == 64): FAIL
-    if not debug and not webrender and e10s and (os == "mac") and (version == "OS X 10.10.5") and (processor == "x86_64") and (bits == 64): FAIL
--- a/testing/web-platform/meta/mozilla-sync
+++ b/testing/web-platform/meta/mozilla-sync
@@ -1,2 +1,2 @@
-local: 05a38ce912d57ee269bd0af8f0b3ced1bb5f068b
-upstream: 9f2daa2a5d08d0d44f680501af2b62292f6264f0
+local: db5952ad2f9586639fd0aeea8a5cd2d9967c101e
+upstream: 37d83def16bacfa66abac065f8f5adc8f7e7a4fc
--- a/testing/web-platform/meta/picture-in-picture/idlharness.window.js.ini
+++ b/testing/web-platform/meta/picture-in-picture/idlharness.window.js.ini
@@ -105,8 +105,14 @@
     expected: FAIL
 
   [EnterPictureInPictureEvent interface: attribute pictureInPictureWindow]
     expected: FAIL
 
   [EnterPictureInPictureEvent interface: existence and properties of interface object]
     expected: FAIL
 
+  [HTMLVideoElement interface: operation requestPictureInPicture()]
+    expected: FAIL
+
+  [HTMLVideoElement interface: video must inherit property "requestPictureInPicture()" with the proper type]
+    expected: FAIL
+
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/service-workers/service-worker/service-worker-header.https.html.ini
@@ -0,0 +1,4 @@
+[service-worker-header.https.html]
+  [A request to fetch service worker importScripts() resources during the update should not have Service-Worker header]
+    expected: TIMEOUT
+
deleted file mode 100644
--- a/testing/web-platform/meta/svg/shapes/ellipse-04.svg.ini
+++ /dev/null
@@ -1,3 +0,0 @@
-[ellipse-04.svg]
-  expected:
-    if os == "mac": FAIL
--- a/testing/web-platform/meta/svg/shapes/line-dasharray.svg.ini
+++ b/testing/web-platform/meta/svg/shapes/line-dasharray.svg.ini
@@ -1,4 +1,2 @@
 [line-dasharray.svg]
-  expected:
-    if os == "mac": FAIL
   restart-after: true
--- a/testing/web-platform/meta/trusted-types/Document-write.tentative.html.ini
+++ b/testing/web-platform/meta/trusted-types/Document-write.tentative.html.ini
@@ -1,4 +1,7 @@
 [Document-write.tentative.html]
   [document.write with html assigned via policy (successful URL transformation).]
     expected: FAIL
 
+  [document.write with html assigned via policy (successful transformation).]
+    expected: FAIL
+
--- a/testing/web-platform/meta/url/failure.html.ini
+++ b/testing/web-platform/meta/url/failure.html.ini
@@ -477,8 +477,44 @@
     expected: FAIL
 
   [Location's href: a// should throw]
     expected: FAIL
 
   [window.open(): a// should throw]
     expected: FAIL
 
+  [sendBeacon(): https://� should throw]
+    expected: FAIL
+
+  [window.open(): https://� should throw]
+    expected: FAIL
+
+  [Location's href: https://� should throw]
+    expected: FAIL
+
+  [XHR: https://� should throw]
+    expected: FAIL
+
+  [URL's constructor's base argument: https://� should throw]
+    expected: FAIL
+
+  [URL's href: https://� should throw]
+    expected: FAIL
+
+  [sendBeacon(): https://� should throw]
+    expected: FAIL
+
+  [window.open(): https://� should throw]
+    expected: FAIL
+
+  [Location's href: https://� should throw]
+    expected: FAIL
+
+  [XHR: https://� should throw]
+    expected: FAIL
+
+  [URL's constructor's base argument: https://� should throw]
+    expected: FAIL
+
+  [URL's href: https://� should throw]
+    expected: FAIL
+
deleted file mode 100644
--- a/testing/web-platform/meta/wasm/idlharness.any.js.ini
+++ /dev/null
@@ -1,368 +0,0 @@
-[idlharness.any.html]
-  [Module interface: existence and properties of interface object]
-    expected: FAIL
-
-  [Module interface object length]
-    expected: FAIL
-
-  [Module interface object name]
-    expected: FAIL
-
-  [Module interface: existence and properties of interface prototype object]
-    expected: FAIL
-
-  [Module interface: existence and properties of interface prototype object's "constructor" property]
-    expected: FAIL
-
-  [Module interface: existence and properties of interface prototype object's @@unscopables property]
-    expected: FAIL
-
-  [Module interface: operation exports(Module)]
-    expected: FAIL
-
-  [Module interface: operation imports(Module)]
-    expected: FAIL
-
-  [Module interface: operation customSections(Module, USVString)]
-    expected: FAIL
-
-  [Module must be primary interface of mod]
-    expected: FAIL
-
-  [Stringification of mod]
-    expected: FAIL
-
-  [Instance interface: existence and properties of interface object]
-    expected: FAIL
-
-  [Instance interface object length]
-    expected: FAIL
-
-  [Instance interface object name]
-    expected: FAIL
-
-  [Instance interface: existence and properties of interface prototype object]
-    expected: FAIL
-
-  [Instance interface: existence and properties of interface prototype object's "constructor" property]
-    expected: FAIL
-
-  [Instance interface: existence and properties of interface prototype object's @@unscopables property]
-    expected: FAIL
-
-  [Instance interface: attribute exports]
-    expected: FAIL
-
-  [Instance must be primary interface of instance]
-    expected: FAIL
-
-  [Stringification of instance]
-    expected: FAIL
-
-  [Instance interface: instance must inherit property "exports" with the proper type]
-    expected: FAIL
-
-  [Memory interface: existence and properties of interface object]
-    expected: FAIL
-
-  [Memory interface object length]
-    expected: FAIL
-
-  [Memory interface object name]
-    expected: FAIL
-
-  [Memory interface: existence and properties of interface prototype object]
-    expected: FAIL
-
-  [Memory interface: existence and properties of interface prototype object's "constructor" property]
-    expected: FAIL
-
-  [Memory interface: existence and properties of interface prototype object's @@unscopables property]
-    expected: FAIL
-
-  [Memory interface: operation grow(unsigned long)]
-    expected: FAIL
-
-  [Memory interface: attribute buffer]
-    expected: FAIL
-
-  [Memory must be primary interface of memory]
-    expected: FAIL
-
-  [Stringification of memory]
-    expected: FAIL
-
-  [Memory interface: memory must inherit property "grow(unsigned long)" with the proper type]
-    expected: FAIL
-
-  [Memory interface: calling grow(unsigned long) on memory with too few arguments must throw TypeError]
-    expected: FAIL
-
-  [Memory interface: memory must inherit property "buffer" with the proper type]
-    expected: FAIL
-
-  [Table interface: existence and properties of interface object]
-    expected: FAIL
-
-  [Table interface object length]
-    expected: FAIL
-
-  [Table interface object name]
-    expected: FAIL
-
-  [Table interface: existence and properties of interface prototype object]
-    expected: FAIL
-
-  [Table interface: existence and properties of interface prototype object's "constructor" property]
-    expected: FAIL
-
-  [Table interface: existence and properties of interface prototype object's @@unscopables property]
-    expected: FAIL
-
-  [Table interface: operation grow(unsigned long)]
-    expected: FAIL
-
-  [Table interface: operation get(unsigned long)]
-    expected: FAIL
-
-  [Table interface: operation set(unsigned long, Function)]
-    expected: FAIL
-
-  [Table interface: attribute length]
-    expected: FAIL
-
-  [Global interface: existence and properties of interface object]
-    expected: FAIL
-
-  [Global interface object length]
-    expected: FAIL
-
-  [Global interface object name]
-    expected: FAIL
-
-  [Global interface: existence and properties of interface prototype object]
-    expected: FAIL
-
-  [Global interface: existence and properties of interface prototype object's "constructor" property]
-    expected: FAIL
-
-  [Global interface: existence and properties of interface prototype object's @@unscopables property]
-    expected: FAIL
-
-  [Global interface: operation valueOf()]
-    expected: FAIL
-
-  [Global interface: attribute value]
-    expected: FAIL
-
-  [CompileError interface: existence and properties of interface object]
-    expected: FAIL
-
-  [CompileError interface object length]
-    expected: FAIL
-
-  [CompileError interface object name]
-    expected: FAIL
-
-  [CompileError interface: existence and properties of interface prototype object]
-    expected: FAIL
-
-  [CompileError interface: existence and properties of interface prototype object's "constructor" property]
-    expected: FAIL
-
-  [CompileError interface: existence and properties of interface prototype object's @@unscopables property]
-    expected: FAIL
-
-  [LinkError interface: existence and properties of interface object]
-    expected: FAIL
-
-  [LinkError interface object length]
-    expected: FAIL
-
-  [LinkError interface object name]
-    expected: FAIL
-
-  [LinkError interface: existence and properties of interface prototype object]
-    expected: FAIL
-
-  [LinkError interface: existence and properties of interface prototype object's "constructor" property]
-    expected: FAIL
-
-  [LinkError interface: existence and properties of interface prototype object's @@unscopables property]
-    expected: FAIL
-
-  [RuntimeError interface: existence and properties of interface object]
-    expected: FAIL
-
-  [RuntimeError interface object length]
-    expected: FAIL
-
-  [RuntimeError interface object name]
-    expected: FAIL
-
-  [RuntimeError interface: existence and properties of interface prototype object]
-    expected: FAIL
-
-  [RuntimeError interface: existence and properties of interface prototype object's "constructor" property]
-    expected: FAIL
-
-  [RuntimeError interface: existence and properties of interface prototype object's @@unscopables property]
-    expected: FAIL
-
-
-[idlharness.any.worker.html]
-  [Module interface: existence and properties of interface object]
-    expected: FAIL
-
-  [Module interface object length]
-    expected: FAIL
-
-  [Module interface object name]
-    expected: FAIL
-
-  [Module interface: existence and properties of interface prototype object]
-    expected: FAIL
-
-  [Module interface: existence and properties of interface prototype object's "constructor" property]
-    expected: FAIL
-
-  [Module interface: existence and properties of interface prototype object's @@unscopables property]
-    expected: FAIL
-
-  [Module interface: operation exports(Module)]
-    expected: FAIL
-
-  [Module interface: operation imports(Module)]
-    expected: FAIL
-
-  [Module interface: operation customSections(Module, USVString)]
-    expected: FAIL
-
-  [Module must be primary interface of mod]
-    expected: FAIL
-
-  [Stringification of mod]
-    expected: FAIL
-
-  [Instance interface: existence and properties of interface object]
-    expected: FAIL
-
-  [Instance interface object length]
-    expected: FAIL
-
-  [Instance interface object name]
-    expected: FAIL
-
-  [Instance interface: existence and properties of interface prototype object]
-    expected: FAIL
-
-  [Instance interface: existence and properties of interface prototype object's "constructor" property]
-    expected: FAIL
-
-  [Instance interface: existence and properties of interface prototype object's @@unscopables property]
-    expected: FAIL
-
-  [Instance interface: attribute exports]
-    expected: FAIL
-
-  [Instance must be primary interface of instance]
-    expected: FAIL
-
-  [Stringification of instance]
-    expected: FAIL
-
-  [Instance interface: instance must inherit property "exports" with the proper type]
-    expected: FAIL
-
-  [Memory interface: existence and properties of interface object]
-    expected: FAIL
-
-  [Memory interface object length]
-    expected: FAIL
-
-  [Memory interface object name]
-    expected: FAIL
-
-  [Memory interface: existence and properties of interface prototype object]
-    expected: FAIL
-
-  [Memory interface: existence and properties of interface prototype object's "constructor" property]
-    expected: FAIL
-
-  [Memory interface: existence and properties of interface prototype object's @@unscopables property]
-    expected: FAIL
-
-  [Memory interface: operation grow(unsigned long)]
-    expected: FAIL
-
-  [Memory interface: attribute buffer]
-    expected: FAIL
-
-  [Memory must be primary interface of memory]
-    expected: FAIL
-
-  [Stringification of memory]
-    expected: FAIL
-
-  [Memory interface: memory must inherit property "grow(unsigned long)" with the proper type]
-    expected: FAIL
-
-  [Memory interface: calling grow(unsigned long) on memory with too few arguments must throw TypeError]
-    expected: FAIL
-
-  [Memory interface: memory must inherit property "buffer" with the proper type]
-    expected: FAIL
-
-  [Table interface: existence and properties of interface object]
-    expected: FAIL
-
-  [Table interface object length]
-    expected: FAIL
-
-  [Table interface object name]
-    expected: FAIL
-
-  [Table interface: existence and properties of interface prototype object]
-    expected: FAIL
-
-  [Table interface: existence and properties of interface prototype object's "constructor" property]
-    expected: FAIL
-
-  [Table interface: existence and properties of interface prototype object's @@unscopables property]
-    expected: FAIL
-
-  [Table interface: operation grow(unsigned long)]
-    expected: FAIL
-
-  [Table interface: operation get(unsigned long)]
-    expected: FAIL
-
-  [Table interface: operation set(unsigned long, Function)]
-    expected: FAIL
-
-  [Table interface: attribute length]
-    expected: FAIL
-
-  [Global interface: existence and properties of interface object]
-    expected: FAIL
-
-  [Global interface object length]
-    expected: FAIL
-
-  [Global interface object name]
-    expected: FAIL
-
-  [Global interface: existence and properties of interface prototype object]
-    expected: FAIL
-
-  [Global interface: existence and properties of interface prototype object's "constructor" property]
-    expected: FAIL
-
-  [Global interface: existence and properties of interface prototype object's @@unscopables property]
-    expected: FAIL
-
-  [Global interface: operation valueOf()]
-    expected: FAIL
-
-  [Global interface: attribute value]
-    expected: FAIL
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/webrtc/RTCDTMFSender-ontonechange-long.https.html.ini
@@ -0,0 +1,9 @@
+[RTCDTMFSender-ontonechange-long.https.html]
+  [insertDTMF with duration greater than 6000 should be clamped to 6000]
+    disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1420640
+    expected:
+      if debug and not webrender and e10s and (os == "linux") and (version == "Ubuntu 16.04") and (processor == "x86_64") and (bits == 64): FAIL
+      if debug and not webrender and e10s and (os == "linux") and (version == "Ubuntu 16.04") and (processor == "x86") and (bits == 32): FAIL
+      if not debug and not webrender and e10s and (os == "linux") and (version == "Ubuntu 16.04") and (processor == "x86_64") and (bits == 64): FAIL
+      if not debug and webrender and e10s and (os == "linux") and (version == "Ubuntu 16.04") and (processor == "x86_64") and (bits == 64): FAIL
+
--- a/testing/web-platform/meta/webvtt/rendering/cues-with-video/processing-model/selectors/default_styles/italic_object_default_font-style.html.ini
+++ b/testing/web-platform/meta/webvtt/rendering/cues-with-video/processing-model/selectors/default_styles/italic_object_default_font-style.html.ini
@@ -1,5 +1,3 @@
 [italic_object_default_font-style.html]
   disabled:
     if verify and (os == "mac"): fails in verify mode
-  expected:
-    if os == "mac": FAIL
--- a/testing/web-platform/meta/webvtt/rendering/cues-with-video/processing-model/selectors/default_styles/underline_object_default_font-style.html.ini
+++ b/testing/web-platform/meta/webvtt/rendering/cues-with-video/processing-model/selectors/default_styles/underline_object_default_font-style.html.ini
@@ -1,5 +1,3 @@
 [underline_object_default_font-style.html]
   disabled:
     if verify and (os == "mac"): fails in verify mode
-  expected:
-    if os == "mac": FAIL
--- a/testing/web-platform/meta/webvtt/rendering/cues-with-video/processing-model/too_many_cues.html.ini
+++ b/testing/web-platform/meta/webvtt/rendering/cues-with-video/processing-model/too_many_cues.html.ini
@@ -1,2 +1,5 @@
 [too_many_cues.html]
-  expected: FAIL
+  expected:
+    if debug and not webrender and e10s and (os == "mac") and (version == "OS X 10.10.5") and (processor == "x86_64") and (bits == 64): PASS
+    if not debug and not webrender and e10s and (os == "mac") and (version == "OS X 10.10.5") and (processor == "x86_64") and (bits == 64): PASS
+    FAIL
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/background-fetch/abort.https.window.js
@@ -0,0 +1,59 @@
+// META: script=/service-workers/service-worker/resources/test-helpers.sub.js
+// META: script=resources/utils.js
+'use strict';
+
+// Covers basic functionality provided by BackgroundFetchManager.abort().
+// https://wicg.github.io/background-fetch/#background-fetch-registration-abort
+
+backgroundFetchTest(async (test, backgroundFetch) => {
+  const registration = await backgroundFetch.fetch(
+      uniqueId(),
+      ['resources/feature-name.txt', '/serviceworker/resources/slow-response.php']);
+
+  assert_true(await registration.abort());
+  assert_false(await registration.abort());
+
+}, 'Aborting the same registration twice fails');
+
+backgroundFetchTest(async (test, backgroundFetch) => {
+  const registration = await backgroundFetch.fetch(
+      uniqueId(),
+      ['resources/feature-name.txt', '/serviceworker/resources/slow-response.php']);
+
+  await new Promise(resolve => {
+    let aborted = false;
+    const expectedResultText = 'Background Fetch';
+
+    registration.onprogress = async event => {
+      if (event.target.downloaded < expectedResultText.length)
+        return;
+
+      if (aborted)
+        return;
+
+      // Abort after the first file has been downloaded and check the results.
+
+      aborted = true;
+      assert_true(await registration.abort());
+
+      const {type, eventRegistration, results} = await getMessageFromServiceWorker();
+
+      assert_equals(eventRegistration.result, 'failure');
+      assert_equals(eventRegistration.failureReason, 'aborted');
+      assert_equals(registration.result, 'failure');
+      assert_equals(registration.failureReason, 'aborted');
+
+      assert_equals(type, 'backgroundfetchabort');
+
+      // The abort might have gone through before the first result was persisted.
+      if (results.length === 1) {
+        assert_true(results[0].url.includes('resources/feature-name.txt'));
+        assert_equals(results[0].status, 200);
+        assert_equals(results[0].text, expectedResultText);
+      }
+
+      resolve();
+    };
+  });
+
+}, 'Calling BackgroundFetchRegistration.abort sets the correct fields and responses are still available');
\ No newline at end of file
--- a/testing/web-platform/tests/background-fetch/fetch.https.window.js
+++ b/testing/web-platform/tests/background-fetch/fetch.https.window.js
@@ -142,16 +142,34 @@ backgroundFetchTest(async (test, backgro
   assert_true(results[0].url.includes('resources/feature-name.txt'));
   assert_equals(results[0].status, 200);
   assert_equals(results[0].text, 'Background Fetch');
 
 }, 'Using Background Fetch to successfully fetch a single resource');
 
 backgroundFetchTest(async (test, backgroundFetch) => {
   const registrationId = uniqueId();
+  const registration =
+    await backgroundFetch.fetch(registrationId, 'resources/feature-name.txt');
+
+  assert_equals(registration.result, '');
+  assert_equals(registration.failureReason, '');
+
+  const {type, eventRegistration, results} =
+    await getMessageFromServiceWorker();
+  assert_equals('backgroundfetchsuccess', type);
+
+  assert_equals(eventRegistration.id, registration.id);
+  assert_equals(registration.result, 'success');
+  assert_equals(registration.failureReason, '');
+
+}, 'Registration object gets updated values when a background fetch completes.');
+
+backgroundFetchTest(async (test, backgroundFetch) => {
+  const registrationId = uniqueId();
 
   // Very large download total that will definitely exceed the quota.
   const options = {downloadTotal: Number.MAX_SAFE_INTEGER};
   await promise_rejects(
     test, 'QUOTA_EXCEEDED_ERR',
     backgroundFetch.fetch(registrationId, 'resources/feature-name.txt', options),
     'This fetch should have thrown a quota exceeded error');
 
@@ -235,16 +253,19 @@ backgroundFetchTest(async (test, backgro
   assert_true(results[0].url.includes('resources/missing-cat.txt'));
   assert_equals(results[0].status, 404);
   assert_equals(results[0].text, '');
 
   assert_equals(eventRegistration.id, registration.id);
   assert_equals(eventRegistration.result, 'failure');
   assert_equals(eventRegistration.failureReason, 'bad-status');
 
+  assert_equals(registration.result, 'failure');
+  assert_equals(registration.failureReason, 'bad-status');
+
 }, 'Using Background Fetch to fetch a non-existent resource should fail.');
 
 backgroundFetchTest(async (test, backgroundFetch) => {
   const registration = await backgroundFetch.fetch(
                          'my-id',
                          ['https://example.com', 'http://example.com']);
 
   const {type, eventRegistration, results} = await getMessageFromServiceWorker();
--- a/testing/web-platform/tests/background-fetch/service_workers/sw.js
+++ b/testing/web-platform/tests/background-fetch/service_workers/sw.js
@@ -22,8 +22,9 @@ function handleBackgroundFetchUpdateEven
         const registrationCopy = cloneRegistration(event.registration);
         sendMessageToDocument(
           { type: event.type, eventRegistration: registrationCopy, results })
       }));
 }
 
 self.addEventListener('backgroundfetchsuccess', handleBackgroundFetchUpdateEvent);
 self.addEventListener('backgroundfetchfail', handleBackgroundFetchUpdateEvent);
+self.addEventListener('backgroundfetchabort', handleBackgroundFetchUpdateEvent);
--- a/testing/web-platform/tests/css/css-properties-values-api/registered-property-computation.html
+++ b/testing/web-platform/tests/css/css-properties-values-api/registered-property-computation.html
@@ -1,117 +1,115 @@
-<!DOCTYPE HTML>
+<!DOCTYPE html>
 <link rel="help" href="https://drafts.css-houdini.org/css-properties-values-api/#calculation-of-computed-values" />
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="./resources/utils.js"></script>
 
 <style>
 #divWithFontSizeSet, #parentDiv {
     font-size: 10px;
 }
-#divWithFontSizeSet, #divWithFontSizeInherited {
-    --length-1: 12px;
-    --length-2: 13vw;
-    --length-3: 14em;
-    --length-4: 15vmin;
-    --length-5: calc(16px - 7em + 10vh);
-    --length-6: var(--length-3);
-    --length-percentage-1: 17em;
-    --length-percentage-2: 18%;
-    --length-percentage-3: calc(19em - 2%);
-    --csv-1: 10px, 3em;
-    --csv-2: 4em ,9px;
-    --csv-3: 8em;
-    --csv-4: 3% , 10vmax  , 22px;
-    --csv-5: calc(50% + 1em), 4px;
-    --csv-6: calc(13% + 37px);
-    --list-1: 10px 3em;
-    --list-2: 4em 9px;
-    --list-3: 3% 10vmax 22px;
-    --list-4: calc(50% + 1em) 4px;
-    --transform-function-1: translateX(2px);
-    --transform-function-2: translateX(10em);
-    --transform-function-3: translateX(calc(11em + 10%));
-    --transform-function-4: translateX(10%) scale(2);
-}
 </style>
 
 <div id=divWithFontSizeSet></div>
 <div id=parentDiv>
     <div id=divWithFontSizeInherited></div>
 </div>
+<div id="ref"></div>
 
 <script>
-test(() => {
-    CSS.registerProperty({name: '--length-1', syntax: '<length>', initialValue: '0px', inherits: false});
-    CSS.registerProperty({name: '--length-2', syntax: '<length>', initialValue: '0px', inherits: false});
-    CSS.registerProperty({name: '--length-3', syntax: '<length>', initialValue: '0px', inherits: false});
-    CSS.registerProperty({name: '--length-4', syntax: '<length>', initialValue: '0px', inherits: false});
-    CSS.registerProperty({name: '--length-5', syntax: '<length>', initialValue: '0px', inherits: false});
-    CSS.registerProperty({name: '--length-6', syntax: '<length>', initialValue: '0px', inherits: false});
-    CSS.registerProperty({name: '--length-percentage-1', syntax: '<length-percentage>', initialValue: '0px', inherits: false});
-    CSS.registerProperty({name: '--length-percentage-2', syntax: '<length-percentage>', initialValue: '0px', inherits: false});
-    CSS.registerProperty({name: '--length-percentage-3', syntax: '<length-percentage>', initialValue: '0px', inherits: false});
-    CSS.registerProperty({name: '--csv-1', syntax: '<length>#', initialValue: '0px', inherits: false});
-    CSS.registerProperty({name: '--csv-2', syntax: '<length>#', initialValue: '0px', inherits: false});
-    CSS.registerProperty({name: '--csv-3', syntax: '<length>#', initialValue: '0px', inherits: false});
-    CSS.registerProperty({name: '--csv-4', syntax: '<length-percentage>#', initialValue: '0px', inherits: false});
-    CSS.registerProperty({name: '--csv-5', syntax: '<length-percentage>#', initialValue: '0px', inherits: false});
-    CSS.registerProperty({name: '--csv-6', syntax: '<length-percentage>#', initialValue: '0px', inherits: false});
-    CSS.registerProperty({name: '--list-1', syntax: '<length>+', initialValue: '0px', inherits: false});
-    CSS.registerProperty({name: '--list-2', syntax: '<length>+', initialValue: '0px', inherits: false});
-    CSS.registerProperty({name: '--list-3', syntax: '<length-percentage>+', initialValue: '0px', inherits: false});
-    CSS.registerProperty({name: '--list-4', syntax: '<length-percentage>+', initialValue: '0px', inherits: false});
-    CSS.registerProperty({name: '--transform-function-1', syntax: '<transform-function>', initialValue: 'translateX(0px)', inherits: false});
-    CSS.registerProperty({name: '--transform-function-2', syntax: '<transform-function>', initialValue: 'translateX(0px)', inherits: false});
-    CSS.registerProperty({name: '--transform-function-3', syntax: '<transform-function>', initialValue: 'translateX(0px)', inherits: false});
-    CSS.registerProperty({name: '--transform-function-4', syntax: '<transform-function>+', initialValue: 'translateX(0px)', inherits: false});
-}, "CSS.registerProperty");
+
+for (let element of [divWithFontSizeSet, divWithFontSizeInherited]) {
+    let id = element.id;
+
+    // Generate a property and temporarily set its value. Then call 'fn' with
+    // the name of the generated property.
+    function with_custom_property(reg, value, fn) {
+        let name = generate_property(reg);
+
+        // Because we want to include the parsing step, insert a stylesheet
+        // node with textContent.
+        let node = document.createElement('style');
+        node.textContent = `#${id} { ${name}: ${value}; }`;
+        document.body.append(node);
+
+        try {
+            fn(name);
+        } finally {
+            node.remove();
+        }
+    }
 
-for (var element of [divWithFontSizeSet, divWithFontSizeInherited]) {
-    var id = element.id;
-    var computedStyle = getComputedStyle(element);
+    function assert_computed_value(syntax, value, expected) {
+        with_custom_property(syntax, value, (name) => {
+            let actual = getComputedStyle(element).getPropertyValue(name);
+            assert_equals(actual, expected);
+        });
+    }
+
+    // Computes an absolute reference value for some length.
+    //
+    // E.g. to figure out how many pixels '10vh' is, do length_ref('10vh').
+    function length_ref(value, refnode = ref) {
+        try {
+            // The reference property 'min-height' is chosen arbitrarily, but
+            // avoid properties with "resolved value is used value"-behavior
+            // [1], as it may affect rounding, and custom properties do not
+            // have this behavior.
+            //
+            // [1] https://drafts.csswg.org/cssom/#resolved-values
+            const ref_property = 'min-height';
+            refnode.style = `${ref_property}: ${value}`;
+            return getComputedStyle(refnode).getPropertyValue(ref_property);
+        } finally {
+            refnode.style = '';
+        }
+    }
 
     test(function() {
-        assert_equals(computedStyle.getPropertyValue('--length-1'), '12px');
-        assert_equals(computedStyle.getPropertyValue('--length-2'), '104px');
-        assert_equals(computedStyle.getPropertyValue('--length-3'), '140px');
-        assert_equals(computedStyle.getPropertyValue('--length-4'), '90px');
-        assert_equals(computedStyle.getPropertyValue('--length-5'), '6px');
-        assert_equals(computedStyle.getPropertyValue('--length-6'), '140px');
+        assert_computed_value('<length>', '12px', '12px');
+        assert_computed_value('<length>', '13vw', length_ref('13vw'));
+        assert_computed_value('<length>', '14em', '140px');
+        assert_computed_value('<length>', '15vmin', length_ref('15vmin'));
+        assert_computed_value('<length>', 'calc(16px - 7em + 10vh)', length_ref('calc(10vh - 54px)'));
+        with_custom_property('<length>', '14em', (name) => {
+            assert_computed_value('<length>', `var(${name})`, '140px');
+        });
     }, "<length> values are computed correctly for " + id);
 
     test(function() {
-        assert_equals(computedStyle.getPropertyValue('--length-percentage-1'), '170px');
-        assert_equals(computedStyle.getPropertyValue('--length-percentage-2'), '18%');
-        assert_equals(computedStyle.getPropertyValue('--length-percentage-3'), 'calc(190px + -2%)');
+        assert_computed_value('<length-percentage>', '17em', '170px');
+        assert_computed_value('<length-percentage>', '18%', '18%');
+        assert_computed_value('<length-percentage>', 'calc(19em - 2%)', 'calc(190px + -2%)');
     }, "<length-percentage> values are computed correctly for " + id);
 
     test(function() {
-        assert_equals(computedStyle.getPropertyValue('--csv-1'), '10px, 30px');
-        assert_equals(computedStyle.getPropertyValue('--csv-2'), '40px, 9px');
-        assert_equals(computedStyle.getPropertyValue('--csv-3'), '80px');
+        assert_computed_value('<length>#', '10px, 3em', '10px, 30px');
+        assert_computed_value('<length>#', '10px, 3em', '10px, 30px');
+        assert_computed_value('<length>#', '4em ,9px', '40px, 9px');
+        assert_computed_value('<length>#', '8em', '80px');
     }, "<length># values are computed correctly for " + id);
 
     test(function() {
-        assert_equals(computedStyle.getPropertyValue('--csv-4'), '3%, 80px, 22px');
-        assert_equals(computedStyle.getPropertyValue('--csv-5'), 'calc(10px + 50%), 4px');
-        assert_equals(computedStyle.getPropertyValue('--csv-6'), 'calc(37px + 13%)');
+        assert_computed_value('<length-percentage>#', '3% , 10vmax  , 22px', ['3%', length_ref('10vmax'), '22px'].join(', '));
+        assert_computed_value('<length-percentage>#', 'calc(50% + 1em), 4px', 'calc(10px + 50%), 4px');
+        assert_computed_value('<length-percentage>#', 'calc(13% + 37px)', 'calc(37px + 13%)');
     }, "<length-percentage># values are computed correctly for " + id);
 
     test(function() {
-        assert_equals(computedStyle.getPropertyValue('--list-1'), '10px 30px');
-        assert_equals(computedStyle.getPropertyValue('--list-2'), '40px 9px');
+        assert_computed_value('<length>+', '10px 3em', '10px 30px');
+        assert_computed_value('<length>+', '4em 9px', '40px 9px');
     }, "<length>+ values are computed correctly for " + id);
 
     test(function() {
-        assert_equals(computedStyle.getPropertyValue('--list-3'), '3% 80px 22px');
-        assert_equals(computedStyle.getPropertyValue('--list-4'), 'calc(10px + 50%) 4px');
+        assert_computed_value('<length-percentage>+', '3% 10vmax 22px', ['3%', length_ref('10vmax'), '22px'].join(' '));
+        assert_computed_value('<length-percentage>+', 'calc(50% + 1em) 4px', 'calc(10px + 50%) 4px');
     }, "<length-percentage>+ values are computed correctly for " + id);
 
     test(function() {
-        assert_equals(computedStyle.getPropertyValue('--transform-function-1'), 'translateX(2px)');
-        assert_equals(computedStyle.getPropertyValue('--transform-function-2'), 'translateX(100px)');
-        assert_equals(computedStyle.getPropertyValue('--transform-function-3'), 'translateX(calc(110px + 10%))');
-        assert_equals(computedStyle.getPropertyValue('--transform-function-4'), 'translateX(10%) scale(2)');
+        assert_computed_value('<transform-function>', 'translateX(2px)', 'translateX(2px)');
+        assert_computed_value('<transform-function>', 'translateX(10em)', 'translateX(100px)');
+        assert_computed_value('<transform-function>', 'translateX(calc(11em + 10%))', 'translateX(calc(110px + 10%))');
+        assert_computed_value('<transform-function>+', 'translateX(10%) scale(2)', 'translateX(10%) scale(2)');
     }, "<transform-function> values are computed correctly for " + id);
 }
 </script>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-properties-values-api/resources/utils.js
@@ -0,0 +1,86 @@
+let next_property_id = 1;
+
+// Generate a unique property name on the form --prop-N.
+function generate_name() {
+  return `--prop-${next_property_id++}`;
+}
+
+// Produce a compatible initial value for the specified syntax.
+function any_initial_value(syntax) {
+  let components = syntax.split('|').map(x => x.trim())
+  let first_component = components[0];
+
+  if (first_component.endsWith('+') || first_component.endsWith('#'))
+    first_component = first_component.slice(0, -1);
+
+  switch (first_component) {
+    case '*':
+    case '<custom-ident>':
+      return 'NULL';
+    case '<angle>':
+      return '0deg';
+    case '<color>':
+      return 'rgb(0, 0, 0)';
+    case '<image>':
+    case '<url>':
+      return 'url(0)';
+    case '<integer>':
+    case '<length-percentage>':
+    case '<length>':
+    case '<number>':
+      return '0';
+    case '<percentage>':
+      return '0%';
+    case '<resolution>':
+      return '0dpi';
+    case '<time>':
+      return '0s';
+    case '<transform-function>':
+    case '<transform-list>':
+      return 'matrix(0, 0, 0, 0, 0, 0)';
+    default:
+      // We assume syntax is a specific custom ident.
+      return first_component;
+  }
+}
+
+// Registers a unique property on the form '--prop-N' and returns the name.
+// Any value except 'syntax' may be omitted, in which case the property will
+// not inherit, and some undefined (but compatible) initial value will be
+// generated. If a single string is used as the argument, it is assumed to be
+// the syntax.
+function generate_property(reg) {
+  let syntax = typeof(reg) === 'string' ? reg : reg.syntax;
+  let initial = typeof(reg.initialValue) === 'undefined' ? any_initial_value(syntax)
+                                                         : reg.initialValue;
+  let inherits = typeof(reg.inherits) === 'undefined' ? false : reg.inherits;
+
+  let name = generate_name();
+  CSS.registerProperty({
+    name: name,
+    syntax: syntax,
+    initialValue: initial,
+    inherits: inherits
+  });
+  return name;
+}
+
+function all_syntaxes() {
+  return [
+    '*',
+    '<angle>',
+    '<color>',
+    '<custom-ident>',
+    '<image>',
+    '<integer>',
+    '<length-percentage>',
+    '<length>',
+    '<number>',
+    '<percentage>',
+    '<resolution>',
+    '<time>',
+    '<transform-function>',
+    '<transform-list>',
+    '<url>'
+  ]
+}
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-properties-values-api/self-utils.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<title>Self-test for utils.js</title>
+<link rel="help" href="https://drafts.css-houdini.org/css-properties-values-api-1/">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="./resources/utils.js"></script>
+<div id=outer><div id=inner></div></div>
+<script>
+
+test(function(){
+  let syntaxes = all_syntaxes().concat([
+    'foo',
+    'bar | <length>',
+    '<angle> | <length>'
+  ]);
+  // Don't throw:
+  syntaxes.forEach(generate_property);
+}, 'Default initial values of generated properties are valid (self-test).');
+
+test(function(){
+  try {
+    let inherited = generate_property({ syntax: '<length>', inherits: true });
+    let non_inherited = generate_property({ syntax: '<length>', inherits: false, initialValue: '5px' });
+    outer.style = `${inherited}: 10px; ${non_inherited}: 11px;`;
+    assert_equals(getComputedStyle(outer).getPropertyValue(inherited), '10px');
+    assert_equals(getComputedStyle(outer).getPropertyValue(non_inherited), '11px');
+    assert_equals(getComputedStyle(inner).getPropertyValue(inherited), '10px');
+    assert_equals(getComputedStyle(inner).getPropertyValue(non_inherited), '5px');
+  } finally {
+    outer.style = '';
+    inner.style = '';
+  }
+}, 'Generated properties respect inherits flag');
+
+</script>
--- a/testing/web-platform/tests/css/css-properties-values-api/typedom.tentative.html
+++ b/testing/web-platform/tests/css/css-properties-values-api/typedom.tentative.html
@@ -216,127 +216,129 @@ function test_style_property_map_get(tes
     }, name_fn('styleMap'));
 }
 
 test_style_property_map_get(function(styleDecl, propertyMap){
     let name1 = gen_prop('<length>', '100px');
     let name2 = gen_prop('<length>', '0px');
     styleDecl.setProperty(name2, `var(${name1})`);
     assert_true(propertyMap.get(name2) instanceof CSSUnparsedValue);
-}, name => `${name}.get returns CSSUnparsedValue for value with var references`);
+}, name => `StylePropertyMap.get returns CSSUnparsedValue for value with var references (${name})`);
 
 test_style_property_map_get(function(styleDecl, propertyMap){
     let name1 = gen_prop('<length>', '100px');
     let name2 = gen_prop('<length>#', '0px');
     styleDecl.setProperty(name2, `1px, var(${name1}), 3px`);
     assert_true(propertyMap.get(name2) instanceof CSSUnparsedValue);
-}, name => `${name}.get returns CSSUnparsedValue for value with var references in list`);
+}, name => `StylePropertyMap.get returns CSSUnparsedValue for value with var references in list (${name})`);
 
 test_style_property_map_get(function(styleDecl, propertyMap){
     assert_attribute_get_type(styleDecl, propertyMap, '*', 'if(){}', CSSUnparsedValue);
-}, name => `${name}.get returns CSSUnparsedValue for *`);
+}, name => `StylePropertyMap.get returns CSSUnparsedValue for * (${name})`);
 
 test_style_property_map_get(function(styleDecl, propertyMap){
     assert_attribute_get_type(styleDecl, propertyMap, '<angle>', '42deg', CSSUnitValue);
-}, name => `${name}.get returns CSSUnitValue for <angle>`);
+}, name => `StylePropertyMap.get returns CSSUnitValue for <angle> (${name})`);
 
 test_style_property_map_get(function(styleDecl, propertyMap){
     assert_attribute_get_type(styleDecl, propertyMap, '<color>', '#fefefe', CSSStyleValue);
-}, name => `${name}.get returns CSSStyleValue for <color>`);
+}, name => `StylePropertyMap.get returns CSSStyleValue for <color> (${name})`);
 
 test_style_property_map_get(function(styleDecl, propertyMap){
     assert_attribute_get_type(styleDecl, propertyMap, '<custom-ident>', 'none', CSSKeywordValue);
-}, name => `${name}.get returns CSSKeywordValue for <custom-ident>`);
+}, name => `StylePropertyMap.get returns CSSKeywordValue for <custom-ident> (${name})`);
 
 test_style_property_map_get(function(styleDecl, propertyMap){
     assert_attribute_get_type(styleDecl, propertyMap, '<image>', 'url(thing.png)', CSSImageValue);
-}, name => `${name}.get returns CSSImageValue for <image>`);
+}, name => `StylePropertyMap.get returns CSSImageValue for <image> (${name})`);
 
 test_style_property_map_get(function(styleDecl, propertyMap){
     assert_attribute_get_type(styleDecl, propertyMap, '<integer>', '100', CSSUnitValue);
-}, name => `${name}.get returns CSSUnitValue for <integer>`);
+}, name => `StylePropertyMap.get returns CSSUnitValue for <integer> (${name})`);
 
 test_style_property_map_get(function(styleDecl, propertyMap){
     assert_attribute_get_type(styleDecl, propertyMap, '<length-percentage>', '10%', CSSUnitValue);
-}, name => `${name}.get returns CSSUnitValue for <length-percentage> [10%]`);
+}, name => `StylePropertyMap.get returns CSSUnitValue for <length-percentage> [10%] (${name})`);
 
 test_style_property_map_get(function(styleDecl, propertyMap){
     assert_attribute_get_type(styleDecl, propertyMap, '<length-percentage>', '10px', CSSUnitValue);
-}, name => `${name}.get returns CSSUnitValue for <length-percentage> [10px]`);
+}, name => `StylePropertyMap.get returns CSSUnitValue for <length-percentage> [10px] (${name})`);
 
 test_style_property_map_get(function(styleDecl, propertyMap){
     assert_attribute_get_type(styleDecl, propertyMap, '<length-percentage>', 'calc(10px + 10%)', CSSMathSum);
-}, name => `${name}.get returns CSSMathSum for <length-percentage> [calc(10px + 10%)]`);
+}, name => `StylePropertyMap.get returns CSSMathSum for <length-percentage> [calc(10px + 10%)] (${name})`);
 
 test_style_property_map_get(function(styleDecl, propertyMap){
     assert_attribute_get_type(styleDecl, propertyMap, '<length>', '10px', CSSUnitValue);
-}, name => `${name}.get returns CSSUnitValue for <length>`);
+}, name => `StylePropertyMap.get returns CSSUnitValue for <length> (${name})`);
 
 test_style_property_map_get(function(styleDecl, propertyMap){
     assert_attribute_get_type(styleDecl, propertyMap, '<number>', '42', CSSUnitValue);
-}, name => `${name}.get returns CSSUnitValue for <number>`);
+}, name => `StylePropertyMap.get returns CSSUnitValue for <number> (${name})`);
 
 test_style_property_map_get(function(styleDecl, propertyMap){
     assert_attribute_get_type(styleDecl, propertyMap, '<percentage>', '10%', CSSUnitValue);
-}, name => `${name}.get returns CSSUnitValue for <percentage>`);
+}, name => `StylePropertyMap.get returns CSSUnitValue for <percentage> (${name})`);
 
 test_style_property_map_get(function(styleDecl, propertyMap){
     assert_attribute_get_type(styleDecl, propertyMap, '<resolution>', '300dpi', CSSUnitValue);
-}, name => `${name}.get returns CSSUnitValue for <resolution>`);
+}, name => `StylePropertyMap.get returns CSSUnitValue for <resolution> (${name})`);
 
 test_style_property_map_get(function(styleDecl, propertyMap){
     assert_attribute_get_type(styleDecl, propertyMap, '<time>', '42s', CSSUnitValue);
-}, name => `${name}.get returns CSSUnitValue for <time>`);
+}, name => `StylePropertyMap.get returns CSSUnitValue for <time> (${name})`);
 
 test_style_property_map_get(function(styleDecl, propertyMap){
     assert_attribute_get_type(styleDecl, propertyMap, '<url>', 'url(a)', CSSStyleValue);
-}, name => `${name}.get returns CSSStyleValue for <url>`);
+}, name => `StylePropertyMap.get returns CSSStyleValue for <url> (${name})`);
 
 test_style_property_map_get(function(styleDecl, propertyMap){
     assert_attribute_get_type(styleDecl, propertyMap, 'thing1 | THING2', 'thing1', CSSKeywordValue);
-}, name => `${name}.get returns CSSKeywordValue for thing1 | THING2`);
+}, name => `StylePropertyMap.get returns CSSKeywordValue for thing1 | THING2 (${name})`);
 
 test_style_property_map_get(function(styleDecl, propertyMap){
     assert_attribute_get_type(styleDecl, propertyMap, '<length>+', '10px 20px', CSSUnitValue);
-}, name => `${name}.get returns CSSUnitValue for <length>+`);
+}, name => `StylePropertyMap.get returns CSSUnitValue for <length>+ (${name})`);
 
 test_style_property_map_get(function(styleDecl, propertyMap){
     assert_attribute_get_type(styleDecl, propertyMap, '<length>#', '10px 20px', CSSUnitValue);
-}, name => `${name}.get returns CSSUnitValue for <length>#`);
+}, name => `StylePropertyMap.get returns CSSUnitValue for <length># (${name})`);
 
 // attributeStyleMap.getAll
 
 test_style_property_map_get(function(styleDecl, propertyMap){
     let name = gen_prop('<length>+', '0px');
     styleDecl.setProperty(name, '10px 20px 30px');
     assert_equals(propertyMap.getAll(name).length, 3);
     assert_true(propertyMap.getAll(name).every(x => x instanceof CSSUnitValue));
-}, name => `${name}.getAll returns a list of CSSUnitValues for <length>+`);
+}, name => `StylePropertyMap.getAll returns a list of CSSUnitValues for <length>+ (${name})`);
 
 test_style_property_map_get(function(styleDecl, propertyMap){
     let name = gen_prop('<length>#', '0px');
     styleDecl.setProperty(name, '10px, 20px, 30px');
     assert_equals(propertyMap.getAll(name).length, 3);
     assert_true(propertyMap.getAll(name).every(x => x instanceof CSSUnitValue));
-}, name => `${name}.getAll returns a list of CSSUnitValues for <length>#`);
+}, name => `StylePropertyMap.getAll returns a list of CSSUnitValues for <length># (${name})`);
 
 // StylePropertyMap.set
 
 function test_style_property_map_set_using_property_map(propertyMapName, propertyMap, options) {
     test(function(){
         let name = gen_prop(options.syntax, options.initialValue);
         propertyMap.clear();
 
+        let ensureArray = v => v.constructor === Array ? v : [v];
+
         for (let value of options.shouldAccept)
-            propertyMap.set(name, value);
+            propertyMap.set(name, ...ensureArray(value));
 
         for (let value of options.shouldReject) {
-            assert_throws(new TypeError(), () => propertyMap.set(name, value));
+            assert_throws(new TypeError(), () => propertyMap.set(name, ...ensureArray(value)));
         }
-    }, `${propertyMapName}.set accepts correct CSSUnitValues for ${options.syntax}`);
+    }, `StylePropertyMap.set accepts correct CSSStyleValues for ${options.syntax} (${propertyMapName})`);
 }
 
 // Verify that the correct CSSStyleValues are accepted/rejected for a registered
 // property with the specified syntax.
 //
 // The same test is performed twice: once for attributeStyleMap, and once
 // for styleMap.
 function test_style_property_map_set(options) {
@@ -355,108 +357,402 @@ test_style_property_map_set({
     shouldAccept: [unparsed('thing')],
     shouldReject: [CSS.px(15), keyword('none')],
 });
 
 test_style_property_map_set({
     syntax: '<angle>',
     initialValue: '0deg',
     shouldAccept: [CSS.deg(42), CSS.turn(2), '42deg'],
-    shouldReject: [unparsed('42deg'), CSS.px(15), '50px'],
+    shouldReject: [unparsed('42deg'), CSS.px(15), '50px', [CSS.deg(15), '10deg']],
 });
 
 test_style_property_map_set({
     syntax: '<custom-ident>',
     initialValue: 'none',
     shouldAccept: [keyword('foo'), 'foo'],
-    shouldReject: [unparsed('foo'), CSS.px(15), '15px'],
+    shouldReject: [unparsed('foo'), CSS.px(15), '15px', [keyword('foo'), 'foo']],
 });
 
 test_style_property_map_set({
     syntax: '<image>',
     initialValue: 'url(a)',
     shouldAccept: [url_image('url(b)'), 'url(b)'],
-    shouldReject: [unparsed('url(b)'), CSS.px(100), '50px'],
+    shouldReject: [unparsed('url(b)'), CSS.px(100), '50px', [url_image('url(1)'), 'url(2)']],
 });
 
 test_style_property_map_set({
     syntax: '<integer>',
     initialValue: '0',
     shouldAccept: [CSS.number(1), CSS.number(-42), '1', '-42'],
-    shouldReject: [unparsed('42'), CSS.px(100), '50px'],
+    shouldReject: [unparsed('42'), CSS.px(100), '50px', [CSS.number(42), '42']],
 });
 
 test_style_property_map_set({
     syntax: '<length-percentage>',
     initialValue: '0px',
     shouldAccept: [CSS.percent(10), CSS.px(1), CSS.em(1), '10px', '10%'],
-    shouldReject: [unparsed('10%'), unparsed('10px'), CSS.dpi(1), 'url(b)'],
+    shouldReject: [unparsed('10%'), unparsed('10px'), CSS.dpi(1), 'url(b)', [CSS.percent(10), '10%']],
 });
 
 test_style_property_map_set({
     syntax: '<length>',
     initialValue: '0px',
     shouldAccept: [CSS.px(10), CSS.em(10), CSS.vh(200), sum(CSS.px(10), CSS.em(20)), '10em', 'calc(10px + 10em)'],
-    shouldReject: [unparsed('10px'), CSS.percent(1), 'url(b)'],
+    shouldReject: [unparsed('10px'), CSS.percent(1), 'url(b)', [CSS.em(10), '10px']],
 });
 
 test_style_property_map_set({
     syntax: '<number>',
     initialValue: '0',
     shouldAccept: [CSS.number(1337), CSS.number(-42.5), '1337', '-42.5'],
-    shouldReject: [unparsed('42'), CSS.px(15), '#fef'],
+    shouldReject: [unparsed('42'), CSS.px(15), '#fef', [CSS.number(-42.5), '42.5']],
 });
 
 test_style_property_map_set({
     syntax: '<percentage>',
     initialValue: '0%',
     shouldAccept: [CSS.percent(10), '10%'],
-    shouldReject: [unparsed('10%'), CSS.px(1), '#fef'],
+    shouldReject: [unparsed('10%'), CSS.px(1), '#fef', [CSS.percent(10), '1%']],
 });
 
 test_style_property_map_set({
     syntax: '<resolution>',
     initialValue: '0dpi',
     shouldAccept: [CSS.dpi(100), CSS.dpcm(10), CSS.dppx(50), '100dpi'],
-    shouldReject: [unparsed('42'), CSS.px(15), '#fef'],
+    shouldReject: [unparsed('42'), CSS.px(15), '#fef', [CSS.dpi(1), '2dpi']],
 });
 
 test_style_property_map_set({
     syntax: '<time>',
     initialValue: '0s',
     shouldAccept: [CSS.s(42), CSS.ms(16), '16ms'],
-    shouldReject: [unparsed('42s'), CSS.px(15), '#fef'],
+    shouldReject: [unparsed('42s'), CSS.px(15), '#fef', [CSS.s(5), '6s']],
 });
 
 test_style_property_map_set({
     syntax: '<url>',
     initialValue: 'url(a)',
     shouldAccept: [url_image('url(b)')],
-    shouldReject: [unparsed('url(b)'), CSS.px(100), '#fef'],
+    shouldReject: [unparsed('url(b)'), CSS.px(100), '#fef', [url_image('url(1)'), 'url(2)']],
 });
 
 test_style_property_map_set({
     syntax: '<transform-list>',
     initialValue: 'translateX(0px)',
     shouldAccept: [CSSStyleValue.parse('transform', 'translateX(10px)')],
     shouldReject: [unparsed('transformX(10px'), CSS.px(100), '#fef'],
 });
 
 test_style_property_map_set({
     syntax: 'none | thing | THING',
     initialValue: 'none',
     shouldAccept: [keyword('thing'), keyword('THING'), 'thing'],
-    shouldReject: [unparsed('thing'), CSS.px(15), keyword('notathing'), 'notathing'],
+    shouldReject: [unparsed('thing'), CSS.px(15), keyword('notathing'), 'notathing', [keyword('thing'), keyword('thing')]],
 });
 
 test_style_property_map_set({
     syntax: '<angle> | <length>',
     initialValue: '0deg',
     shouldAccept: [CSS.deg(42), CSS.turn(2), CSS.px(10), CSS.em(10), '10deg', '10px'],
-    shouldReject: [unparsed('42deg'), unparsed('20px'), CSS.s(1), '#fef'],
+    shouldReject: [unparsed('42deg'), unparsed('20px'), CSS.s(1), '#fef', [CSS.deg(42), '21deg']],
+});
+
+// StylePropertyMap.set for list-valued properties:
+
+test_style_property_map_set({
+    syntax: '<angle>+',
+    initialValue: '0deg',
+    shouldAccept: [CSS.deg(15), [CSS.deg(15), '10deg'], '15deg 10deg'],
+    shouldReject: [[CSS.deg(15), CSS.px(10)], '15deg 10px'],
+});
+
+test_style_property_map_set({
+    syntax: '<custom-ident>+',
+    initialValue: 'none',
+    shouldAccept: [keyword('foo'), [keyword('foo'), 'bar'], 'foo bar'],
+    shouldReject: [[keyword('foo'), CSS.px(10)], 'foo 10px'],
+});
+
+test_style_property_map_set({
+    syntax: '<image>+',
+    initialValue: 'url(a)',
+    shouldAccept: [url_image('url(1)'), [url_image('url(1)'), 'url(2)'], 'url(1) url(2)'],
+    shouldReject: [[url_image('url(1)'), CSS.px(10)], 'url(1) 10px'],
+});
+
+test_style_property_map_set({
+    syntax: '<integer>+',
+    initialValue: '0',
+    shouldAccept: [CSS.number(42), [CSS.number(42), '42'], '42 42'],
+    shouldReject: [[CSS.number(42), keyword('noint')], '42 noint'],
+});
+
+test_style_property_map_set({
+    syntax: '<length-percentage>+',
+    initialValue: '0px',
+    shouldAccept: [CSS.percent(10), [CSS.percent(10), '10%']],
+    shouldReject: [[CSS.percent(10), keyword('nolength')], '10% nolength'],
+});
+
+test_style_property_map_set({
+    syntax: '<length>+',
+    initialValue: '0px',
+    shouldAccept: [CSS.em(10), [CSS.em(10), '10px']],
+    shouldReject: [[CSS.em(10), keyword('nolength'), '10em nolength']],
+});
+
+test_style_property_map_set({
+    syntax: '<number>+',
+    initialValue: '0',
+    shouldAccept: [CSS.number(-42.5), [CSS.number(-42.5), '42.5'], '-42.5 42.5'],
+    shouldReject: [[CSS.number(-42.5), CSS.px(10)], '-42.5 10px'],
+});
+
+test_style_property_map_set({
+    syntax: '<percentage>+',
+    initialValue: '0%',
+    shouldAccept: [CSS.percent(10), [CSS.percent(10), '1%'], '10% 1%'],
+    shouldReject: [[CSS.percent(10), keyword('foo')], '10% foo'],
+});
+
+test_style_property_map_set({
+    syntax: '<resolution>+',
+    initialValue: '0dpi',
+    shouldAccept: [CSS.dpi(1), [CSS.dpi(1), '2dpi'], '1dpi 2dpi'],
+    shouldReject: [[CSS.dpi(1), keyword('foo')], '1dpi foo'],
+});
+
+test_style_property_map_set({
+    syntax: '<time>+',
+    initialValue: '0s',
+    shouldAccept: [CSS.s(5), [CSS.s(5), '6s'], '5s 6s'],
+    shouldReject: [[CSS.s(5), keyword('foo')], '5s foo'],
+});
+
+test_style_property_map_set({
+    syntax: '<url>+',
+    initialValue: 'url(a)',
+    shouldAccept: [url_image('url(1)'), [url_image('url(1)'), 'url(2)'], 'url(1) url(2)'],
+    shouldReject: [[url_image('url(1)'), CSS.px(10)], 'url(1) 10px'],
+});
+
+test_style_property_map_set({
+    syntax: 'thing+',
+    initialValue: 'thing',
+    shouldAccept: [keyword('thing'), [keyword('thing'), 'thing'], 'thing thing'],
+    shouldReject: [[keyword('thing'), CSS.px(10)], 'thing 10px'],
+});
+
+test_style_property_map_set({
+    syntax: '<length>#',
+    initialValue: '0px',
+    shouldAccept: [CSS.em(10), [CSS.em(10), '10px']],
+    shouldReject: [[CSS.em(10), keyword('nolength'), '10em nolength']],
+});
+
+function test_append_for_property_map(propertyMapName, propertyMap, options) {
+    test(function(){
+        let name = gen_prop(options.syntax, options.initialValue);
+
+        let ensureArray = v => v.constructor === Array ? v : [v];
+
+        for (let value of options.values) {
+            propertyMap.clear();
+
+            if (value.base !== null)
+                propertyMap.set(name, ...ensureArray(value.base));
+
+            // If 'null' is expected, it means we expect the append to fail.
+            if (value.expect !== null) {
+                propertyMap.append(name, ...ensureArray(value.append));
+                let actual = Array.from(propertyMap.getAll(name)).join(' ');
+                assert_equals(actual, value.expect);
+            } else {
+                assert_throws(new TypeError(), () => propertyMap.append(name, ...ensureArray(value.append)));
+            }
+        }
+    }, `StylePropertyMap.append accepts correct CSSStyleValues for ${options.syntax} (${propertyMapName})`);
+}
+
+// Verify that the correct CSSStyleValues are accepted/rejected when
+// appending values to list-valued properties.
+//
+// The same test is performed twice: once for attributeStyleMap, and once
+// for styleMap.
+function test_append(options) {
+    test_append_for_property_map('attributeStyleMap', target.attributeStyleMap, options);
+    test_append_for_property_map('styleMap', style.sheet.rules[0].styleMap, options);
+}
+
+test_append({
+    syntax: '<angle>+',
+    initialValue: '0deg',
+    values: [
+        { base: [CSS.deg(1)], append: [CSS.px(1)], expect: null },
+        { base: [CSS.deg(1)], append: [CSS.deg(2), CSS.px(1)], expect: null },
+        { base: [CSS.deg(1)], append: [CSS.deg(2), '1px'], expect: null },
+        { base: [CSS.deg(1)], append: [CSS.turn(2), CSS.deg(3)], expect: '1deg 2turn 3deg' },
+        { base: [CSS.deg(1), CSS.deg(2)], append: [CSS.deg(3)], expect: '1deg 2deg 3deg' },
+        { base: [CSS.deg(1)], append: [CSS.deg(2), '3deg'], expect: '1deg 2deg 3deg' },
+        { base: [CSS.deg(1)], append: [CSS.deg(2), '3turn 4deg'], expect: '1deg 2deg 3turn 4deg' },
+        { base: null, append: [CSS.deg(1), '2deg'], expect: '1deg 2deg' },
+    ],
+});
+
+test_append({
+    syntax: '<custom-ident>+',
+    initialValue: 'none',
+    values: [
+        { base: [keyword('foo')], append: [CSS.px(1)], expect: null },
+        { base: [keyword('foo')], append: [keyword('bar'), CSS.px(1)], expect: null },
+        { base: [keyword('foo')], append: [keyword('bar'), '1px'], expect: null },
+        { base: [keyword('foo')], append: [keyword('bar'), keyword('baz')], expect: 'foo bar baz' },
+        { base: [keyword('foo'), keyword('bar')], append: [keyword('baz')], expect: 'foo bar baz' },
+        { base: [keyword('foo')], append: [keyword('bar'), 'baz'], expect: 'foo bar baz' },
+        { base: [keyword('foo')], append: [keyword('bar'), 'baz zim'], expect: 'foo bar baz zim' },
+        { base: null, append: [keyword('foo'), 'bar'], expect: 'foo bar' },
+    ],
+});
+
+['<image>+', '<url>+'].forEach((syntax) => {
+    test_append({
+        syntax: syntax,
+        initialValue: 'url(0)',
+        values: [
+            { base: [url_image('url("1")')], append: [CSS.px(1)], expect: null },
+            { base: [url_image('url("1")')], append: [url_image('url("2")'), CSS.px(1)], expect: null },
+            { base: [url_image('url("1")')], append: [url_image('url("2")'), '1px'], expect: null },
+            { base: [url_image('url("1")')], append: [url_image('url("2")'), url_image('url("3")')], expect: 'url("1") url("2") url("3")' },
+            { base: [url_image('url("1")'), url_image('url("2")')], append: [url_image('url("3")')], expect: 'url("1") url("2") url("3")' },
+            { base: [url_image('url("1")')], append: [url_image('url("2")'), 'url("3")'], expect: 'url("1") url("2") url("3")' },
+            { base: [url_image('url("1")')], append: [url_image('url("2")'), 'url("3") url("4")'], expect: 'url("1") url("2") url("3") url("4")' },
+            { base: null, append: [url_image('url("1")'), 'url("2")'], expect: 'url("1") url("2")' },
+        ],
+    });
+});
+
+test_append({
+    syntax: '<integer>+',
+    initialValue: '0',
+    values: [
+        { base: [CSS.number(1)], append: [CSS.px(1)], expect: null },
+        { base: [CSS.number(1)], append: [CSS.number(2), CSS.px(1)], expect: null },
+        { base: [CSS.number(1)], append: [CSS.number(2), 'noint'], expect: null },
+        { base: [CSS.number(1)], append: [CSS.number(2), CSS.number(3)], expect: '1 2 3' },
+        { base: [CSS.number(1), CSS.number(2)], append: [CSS.number(3)], expect: '1 2 3' },
+        { base: [CSS.number(1)], append: [CSS.number(2), '3'], expect: '1 2 3' },
+        { base: [CSS.number(1)], append: [CSS.number(2), '3 4'], expect: '1 2 3 4' },
+        { base: null, append: [CSS.number(1), '2'], expect: '1 2' },
+    ],
+});
+
+test_append({
+    syntax: '<length-percentage>+',
+    initialValue: '0px',
+    values: [
+        { base: [CSS.px(1)], append: [keyword('nolength')], expect: null },
+        { base: [CSS.px(1)], append: [CSS.px(2), keyword('nolength')], expect: null },
+        { base: [CSS.px(1)], append: [CSS.px(2), 'nolength'], expect: null },
+        { base: [CSS.px(1)], append: [CSS.px(2), CSS.percent(3)], expect: '1px 2px 3%' },
+        { base: [CSS.px(1), CSS.px(2)], append: [CSS.percent(3)], expect: '1px 2px 3%' },
+        { base: [CSS.px(1)], append: [CSS.percent(2), '3px'], expect: '1px 2% 3px' },
+        { base: [CSS.px(1)], append: [CSS.px(2), '3% 4px'], expect: '1px 2px 3% 4px' },
+        { base: null, append: [CSS.px(1), '2%'], expect: '1px 2%' },
+    ],
+});
+
+test_append({
+    syntax: '<length>+',
+    initialValue: '0',
+    values: [
+        { base: [CSS.px(1)], append: [keyword('nolength')], expect: null },
+        { base: [CSS.px(1)], append: [CSS.px(2), keyword('nolength')], expect: null },
+        { base: [CSS.px(1)], append: [CSS.px(2), 'nolength'], expect: null },
+        { base: [CSS.px(1)], append: [CSS.em(2), CSS.px(3)], expect: '1px 2em 3px' },
+        { base: [CSS.px(1), CSS.em(2)], append: [CSS.vh(3)], expect: '1px 2em 3vh' },
+        { base: [CSS.px(1)], append: [CSS.em(2), '3px'], expect: '1px 2em 3px' },
+        { base: [CSS.px(1)], append: [CSS.px(2), '3em 4cm'], expect: '1px 2px 3em 4cm' },
+        { base: null, append: [CSS.vh(1), '2px'], expect: '1vh 2px' },
+    ],
+});
+
+test_append({
+    syntax: '<number>+',
+    initialValue: '0',
+    values: [
+        { base: [CSS.number(-1)], append: [keyword('NaN')], expect: null },
+        { base: [CSS.number(-1)], append: [CSS.number(2.5), keyword('NaN')], expect: null },
+        { base: [CSS.number(-1)], append: [CSS.number(2.5), '1px'], expect: null },
+        { base: [CSS.number(-1)], append: [CSS.number(2.5), CSS.number(3.2)], expect: '-1 2.5 3.2' },
+        { base: [CSS.number(-1), CSS.number(2.5)], append: [CSS.number(3.2)], expect: '-1 2.5 3.2' },
+        { base: [CSS.number(-1)], append: [CSS.number(2.5), '3.2'], expect: '-1 2.5 3.2' },
+        { base: [CSS.number(-1)], append: [CSS.number(2.5), '3.2 4'], expect: '-1 2.5 3.2 4' },
+        { base: null, append: [CSS.number(-1), '2.5'], expect: '-1 2.5' },
+    ],
+});
+
+test_append({
+    syntax: '<percentage>+',
+    initialValue: '0%',
+    values: [
+        { base: [CSS.percent(1)], append: [CSS.px(1)], expect: null },
+        { base: [CSS.percent(1)], append: [CSS.percent(2), CSS.px(1)], expect: null },
+        { base: [CSS.percent(1)], append: [CSS.percent(2), '1px'], expect: null },
+        { base: [CSS.percent(1)], append: [CSS.percent(2), CSS.percent(3)], expect: '1% 2% 3%' },
+        { base: [CSS.percent(1), CSS.percent(2)], append: [CSS.percent(3)], expect: '1% 2% 3%' },
+        { base: [CSS.percent(1)], append: [CSS.percent(2), '3%'], expect: '1% 2% 3%' },
+        { base: [CSS.percent(1)], append: [CSS.percent(2), '3% 4%'], expect: '1% 2% 3% 4%' },
+        { base: null, append: [CSS.percent(1), '2%'], expect: '1% 2%' },
+    ],
+});
+
+test_append({
+    syntax: '<resolution>+',
+    initialValue: '0dpi',
+    values: [
+        { base: [CSS.dpi(1)], append: [CSS.px(1)], expect: null },
+        { base: [CSS.dpi(1)], append: [CSS.dpi(2), CSS.px(1)], expect: null },
+        { base: [CSS.dpi(1)], append: [CSS.dpi(2), '1px'], expect: null },
+        { base: [CSS.dpi(1)], append: [CSS.dpi(2), CSS.dpi(3)], expect: '1dpi 2dpi 3dpi' },
+        { base: [CSS.dpi(1), CSS.dpi(2)], append: [CSS.dpi(3)], expect: '1dpi 2dpi 3dpi' },
+        { base: [CSS.dpi(1)], append: [CSS.dpi(2), '3dpi'], expect: '1dpi 2dpi 3dpi' },
+        { base: [CSS.dpi(1)], append: [CSS.dpi(2), '3dpi 4dpi'], expect: '1dpi 2dpi 3dpi 4dpi' },
+        { base: null, append: [CSS.dpi(1), '2dpi'], expect: '1dpi 2dpi' },
+    ],
+});
+
+test_append({
+    syntax: '<time>+',
+    initialValue: '0s',
+    values: [
+        { base: [CSS.s(1)], append: [CSS.px(1)], expect: null },
+        { base: [CSS.s(1)], append: [CSS.s(2), CSS.px(1)], expect: null },
+        { base: [CSS.s(1)], append: [CSS.ms(2), '1px'], expect: null },
+        { base: [CSS.s(1)], append: [CSS.ms(2), CSS.s(3)], expect: '1s 2ms 3s' },
+        { base: [CSS.s(1), CSS.s(2)], append: [CSS.s(3)], expect: '1s 2s 3s' },
+        { base: [CSS.s(1)], append: [CSS.s(2), '3s'], expect: '1s 2s 3s' },
+        { base: [CSS.s(1)], append: [CSS.s(2), '3ms 4s'], expect: '1s 2s 3ms 4s' },
+        { base: null, append: [CSS.s(1), '2s'], expect: '1s 2s' },
+    ],
+});
+
+test_append({
+    syntax: 'foo+',
+    initialValue: 'foo',
+    values: [
+        { base: [keyword('foo')], append: [CSS.px(1)], expect: null },
+        { base: [keyword('foo')], append: [keyword('foo'), CSS.px(1)], expect: null },
+        { base: [keyword('foo')], append: [keyword('foo'), '1px'], expect: null },
+        { base: [keyword('foo')], append: [keyword('foo'), keyword('foo')], expect: 'foo foo foo' },
+        { base: [keyword('foo'), keyword('foo')], append: [keyword('foo')], expect: 'foo foo foo' },
+        { base: [keyword('foo')], append: [keyword('foo'), 'foo'], expect: 'foo foo foo' },
+        { base: [keyword('foo')], append: [keyword('foo'), 'foo foo'], expect: 'foo foo foo foo' },
+        { base: null, append: [keyword('foo'), keyword('foo')], expect: 'foo foo' },
+    ],
 });
 
 // CSSStyleValue.parse/parseAll
 
 function assert_parsed_type(prop, value, expected) {
     let parse_value = CSSStyleValue.parse(prop, value);
     let parse_all_value = CSSStyleValue.parseAll(prop, value);
 
--- a/testing/web-platform/tests/css/css-text/text-transform/reference/text-transform-capitalize-001-ref.html
+++ b/testing/web-platform/tests/css/css-text/text-transform/reference/text-transform-capitalize-001-ref.html
@@ -1,10 +1,10 @@
 <!DOCTYPE html>
-<html  lang="en" >
+<html lang="en">
 <head>
 <meta charset="utf-8"/>
 <title>CSS3 Text, text transform: capitalize, Latin Basic and Latin-1</title>
 <link rel='author' title='Richard Ishida' href='mailto:ishida@w3.org'>
 <style type='text/css'>
 @font-face {
 	font-family: 'webfont';
 	src: url('/fonts/DoulosSIL-R.woff') format('woff');
@@ -14,13 +14,10 @@
 .test, .ref { font-size: 200%; line-height: 2.5em; font-family: 'Doulos SIL', 'Noto Serif', 'Noto Sans', webfont, sans-serif; }
 .test span, .ref span { margin-right: 1em; white-space: nowrap; }
 </style>
 </head>
 <body>
 <p class="instructions">The text is arranged in pairs of three-letter 'words'. Test passes if the words in each pair match<br>
 If you are missing a font glyph for a character, ignore that pair, but report which characters were ignored.</p>
 <div class="test"><span>Aaa Aaa</span> <span>Bbb Bbb</span> <span>Ccc Ccc</span> <span>Ddd Ddd</span> <span>Eee Eee</span> <span>Fff Fff</span> <span>Ggg Ggg</span> <span>Hhh Hhh</span> <span>Iii Iii</span> <span>Jjj Jjj</span> <span>Kkk Kkk</span> <span>Lll Lll</span> <span>Mmm Mmm</span> <span>Nnn Nnn</span> <span>Ooo Ooo</span> <span>Ppp Ppp</span> <span>Qqq Qqq</span> <span>Rrr Rrr</span> <span>Sss Sss</span> <span>Ttt Ttt</span> <span>Uuu Uuu</span> <span>Vvv Vvv</span> <span>Www Www</span> <span>Xxx Xxx</span> <span>Yyy Yyy</span> <span>Zzz Zzz</span> <span title="U+00B5">&#x039C;&#x00B5;&#x00B5; &#x039C;&#x00B5;&#x00B5;</span>  <span title="U+00E0">&#x00C0;&#x00E0;&#x00E0; &#x00C0;&#x00E0;&#x00E0;</span>  <span title="U+00E1">&#x00C1;&#x00E1;&#x00E1; &#x00C1;&#x00E1;&#x00E1;</span>  <span title="U+00E2">&#x00C2;&#x00E2;&#x00E2; &#x00C2;&#x00E2;&#x00E2;</span>  <span title="U+00E3">&#x00C3;&#x00E3;&#x00E3; &#x00C3;&#x00E3;&#x00E3;</span>  <span title="U+00E4">&#x00C4;&#x00E4;&#x00E4; &#x00C4;&#x00E4;&#x00E4;</span>  <span title="U+00E5">&#x00C5;&#x00E5;&#x00E5; &#x00C5;&#x00E5;&#x00E5;</span>  <span title="U+00E6">&#x00C6;&#x00E6;&#x00E6; &#x00C6;&#x00E6;&#x00E6;</span>  <span title="U+00E7">&#x00C7;&#x00E7;&#x00E7; &#x00C7;&#x00E7;&#x00E7;</span>  <span title="U+00E8">&#x00C8;&#x00E8;&#x00E8; &#x00C8;&#x00E8;&#x00E8;</span>  <span title="U+00E9">&#x00C9;&#x00E9;&#x00E9; &#x00C9;&#x00E9;&#x00E9;</span>  <span title="U+00EA">&#x00CA;&#x00EA;&#x00EA; &#x00CA;&#x00EA;&#x00EA;</span>  <span title="U+00EB">&#x00CB;&#x00EB;&#x00EB; &#x00CB;&#x00EB;&#x00EB;</span>  <span title="U+00EC">&#x00CC;&#x00EC;&#x00EC; &#x00CC;&#x00EC;&#x00EC;</span>  <span title="U+00ED">&#x00CD;&#x00ED;&#x00ED; &#x00CD;&#x00ED;&#x00ED;</span>  <span title="U+00EE">&#x00CE;&#x00EE;&#x00EE; &#x00CE;&#x00EE;&#x00EE;</span>  <span title="U+00EF">&#x00CF;&#x00EF;&#x00EF; &#x00CF;&#x00EF;&#x00EF;</span>  <span title="U+00F0">&#x00D0;&#x00F0;&#x00F0; &#x00D0;&#x00F0;&#x00F0;</span>  <span title="U+00F1">&#x00D1;&#x00F1;&#x00F1; &#x00D1;&#x00F1;&#x00F1;</span>  <span title="U+00F2">&#x00D2;&#x00F2;&#x00F2; &#x00D2;&#x00F2;&#x00F2;</span>  <span title="U+00F3">&#x00D3;&#x00F3;&#x00F3; &#x00D3;&#x00F3;&#x00F3;</span>  <span title="U+00F4">&#x00D4;&#x00F4;&#x00F4; &#x00D4;&#x00F4;&#x00F4;</span>  <span title="U+00F5">&#x00D5;&#x00F5;&#x00F5; &#x00D5;&#x00F5;&#x00F5;</span>  <span title="U+00F6">&#x00D6;&#x00F6;&#x00F6; &#x00D6;&#x00F6;&#x00F6;</span>  <span title="U+00F8">&#x00D8;&#x00F8;&#x00F8; &#x00D8;&#x00F8;&#x00F8;</span>  <span title="U+00F9">&#x00D9;&#x00F9;&#x00F9; &#x00D9;&#x00F9;&#x00F9;</span>  <span title="U+00FA">&#x00DA;&#x00FA;&#x00FA; &#x00DA;&#x00FA;&#x00FA;</span>  <span title="U+00FB">&#x00DB;&#x00FB;&#x00FB; &#x00DB;&#x00FB;&#x00FB;</span>  <span title="U+00FC">&#x00DC;&#x00FC;&#x00FC; &#x00DC;&#x00FC;&#x00FC;</span>  <span title="U+00FD">&#x00DD;&#x00FD;&#x00FD; &#x00DD;&#x00FD;&#x00FD;</span>  <span title="U+00FE">&#x00DE;&#x00FE;&#x00FE; &#x00DE;&#x00FE;&#x00FE;</span>  <span title="U+00FF">&#x0178;&#x00FF;&#x00FF; &#x0178;&#x00FF;&#x00FF;</span>  </div>
-<!--Notes:
-Tip: To identify the characters where differences occur, in order to report problem characters,either mouse over to reveal a tooltip, or copy and paste the sequence into a tool such as <a href='http://r12a.github.io/uniview/' target='_blank'>UniView</a> or the <a href='http://r12a.github.io/apps/conversion/' target='_blank'>Unicode Conversion Tool</a>.
--->
 </body>
-</html>
\ No newline at end of file
+</html>
--- a/testing/web-platform/tests/css/css-text/text-transform/reference/text-transform-capitalize-003-ref.html
+++ b/testing/web-platform/tests/css/css-text/text-transform/reference/text-transform-capitalize-003-ref.html
@@ -1,10 +1,10 @@
 <!DOCTYPE html>
-<html  lang="en" >
+<html lang="en">
 <head>
 <meta charset="utf-8"/>
 <title>CSS3 Text, text transform:  capitalize, Latin Extended Additional</title>
 <link rel='author' title='Richard Ishida' href='mailto:ishida@w3.org'>
 <style type='text/css'>
 @font-face {
 	font-family: 'webfont';
 	src: url('/fonts/DoulosSIL-R.woff') format('woff');
@@ -14,13 +14,10 @@
 .test, .ref { font-size: 200%; line-height: 2.5em; font-family: 'Doulos SIL', 'Noto Sans', webfont, sans-serif; }
 .test span, .ref span { margin-right: 1em; white-space: nowrap; }
 </style>
 </head>
 <body>
 <p class="instructions">The text is arranged in pairs of three-letter 'words'. Test passes if the words in each pair match<br>
 If you are missing a font glyph for a character, ignore that pair, but report which characters were ignored.</p>
 <div class="test"><span title="U+1E01">&#x1E00;&#x1E01;&#x1E01; &#x1E00;&#x1E01;&#x1E01;</span>  <span title="U+1E03">&#x1E02;&#x1E03;&#x1E03; &#x1E02;&#x1E03;&#x1E03;</span>  <span title="U+1E05">&#x1E04;&#x1E05;&#x1E05; &#x1E04;&#x1E05;&#x1E05;</span>  <span title="U+1E07">&#x1E06;&#x1E07;&#x1E07; &#x1E06;&#x1E07;&#x1E07;</span>  <span title="U+1E09">&#x1E08;&#x1E09;&#x1E09; &#x1E08;&#x1E09;&#x1E09;</span>  <span title="U+1E0B">&#x1E0A;&#x1E0B;&#x1E0B; &#x1E0A;&#x1E0B;&#x1E0B;</span>  <span title="U+1E0D">&#x1E0C;&#x1E0D;&#x1E0D; &#x1E0C;&#x1E0D;&#x1E0D;</span>  <span title="U+1E0F">&#x1E0E;&#x1E0F;&#x1E0F; &#x1E0E;&#x1E0F;&#x1E0F;</span>  <span title="U+1E11">&#x1E10;&#x1E11;&#x1E11; &#x1E10;&#x1E11;&#x1E11;</span>  <span title="U+1E13">&#x1E12;&#x1E13;&#x1E13; &#x1E12;&#x1E13;&#x1E13;</span>  <span title="U+1E15">&#x1E14;&#x1E15;&#x1E15; &#x1E14;&#x1E15;&#x1E15;</span>  <span title="U+1E17">&#x1E16;&#x1E17;&#x1E17; &#x1E16;&#x1E17;&#x1E17;</span>  <span title="U+1E19">&#x1E18;&#x1E19;&#x1E19; &#x1E18;&#x1E19;&#x1E19;</span>  <span title="U+1E1B">&#x1E1A;&#x1E1B;&#x1E1B; &#x1E1A;&#x1E1B;&#x1E1B;</span>  <span title="U+1E1D">&#x1E1C;&#x1E1D;&#x1E1D; &#x1E1C;&#x1E1D;&#x1E1D;</span>  <span title="U+1E1F">&#x1E1E;&#x1E1F;&#x1E1F; &#x1E1E;&#x1E1F;&#x1E1F;</span>  <span title="U+1E21">&#x1E20;&#x1E21;&#x1E21; &#x1E20;&#x1E21;&#x1E21;</span>  <span title="U+1E23">&#x1E22;&#x1E23;&#x1E23; &#x1E22;&#x1E23;&#x1E23;</span>  <span title="U+1E25">&#x1E24;&#x1E25;&#x1E25; &#x1E24;&#x1E25;&#x1E25;</span>  <span title="U+1E27">&#x1E26;&#x1E27;&#x1E27; &#x1E26;&#x1E27;&#x1E27;</span>  <span title="U+1E29">&#x1E28;&#x1E29;&#x1E29; &#x1E28;&#x1E29;&#x1E29;</span>  <span title="U+1E2B">&#x1E2A;&#x1E2B;&#x1E2B; &#x1E2A;&#x1E2B;&#x1E2B;</span>  <span title="U+1E2D">&#x1E2C;&#x1E2D;&#x1E2D; &#x1E2C;&#x1E2D;&#x1E2D;</span>  <span title="U+1E2F">&#x1E2E;&#x1E2F;&#x1E2F; &#x1E2E;&#x1E2F;&#x1E2F;</span>  <span title="U+1E31">&#x1E30;&#x1E31;&#x1E31; &#x1E30;&#x1E31;&#x1E31;</span>  <span title="U+1E33">&#x1E32;&#x1E33;&#x1E33; &#x1E32;&#x1E33;&#x1E33;</span>  <span title="U+1E35">&#x1E34;&#x1E35;&#x1E35; &#x1E34;&#x1E35;&#x1E35;</span>  <span title="U+1E37">&#x1E36;&#x1E37;&#x1E37; &#x1E36;&#x1E37;&#x1E37;</span>  <span title="U+1E39">&#x1E38;&#x1E39;&#x1E39; &#x1E38;&#x1E39;&#x1E39;</span>  <span title="U+1E3B">&#x1E3A;&#x1E3B;&#x1E3B; &#x1E3A;&#x1E3B;&#x1E3B;</span>  <span title="U+1E3D">&#x1E3C;&#x1E3D;&#x1E3D; &#x1E3C;&#x1E3D;&#x1E3D;</span>  <span title="U+1E3F">&#x1E3E;&#x1E3F;&#x1E3F; &#x1E3E;&#x1E3F;&#x1E3F;</span>  <span title="U+1E41">&#x1E40;&#x1E41;&#x1E41; &#x1E40;&#x1E41;&#x1E41;</span>  <span title="U+1E43">&#x1E42;&#x1E43;&#x1E43; &#x1E42;&#x1E43;&#x1E43;</span>  <span title="U+1E45">&#x1E44;&#x1E45;&#x1E45; &#x1E44;&#x1E45;&#x1E45;</span>  <span title="U+1E47">&#x1E46;&#x1E47;&#x1E47; &#x1E46;&#x1E47;&#x1E47;</span>  <span title="U+1E49">&#x1E48;&#x1E49;&#x1E49; &#x1E48;&#x1E49;&#x1E49;</span>  <span title="U+1E4B">&#x1E4A;&#x1E4B;&#x1E4B; &#x1E4A;&#x1E4B;&#x1E4B;</span>  <span title="U+1E4D">&#x1E4C;&#x1E4D;&#x1E4D; &#x1E4C;&#x1E4D;&#x1E4D;</span>  <span title="U+1E4F">&#x1E4E;&#x1E4F;&#x1E4F; &#x1E4E;&#x1E4F;&#x1E4F;</span>  <span title="U+1E51">&#x1E50;&#x1E51;&#x1E51; &#x1E50;&#x1E51;&#x1E51;</span>  <span title="U+1E53">&#x1E52;&#x1E53;&#x1E53; &#x1E52;&#x1E53;&#x1E53;</span>  <span title="U+1E55">&#x1E54;&#x1E55;&#x1E55; &#x1E54;&#x1E55;&#x1E55;</span>  <span title="U+1E57">&#x1E56;&#x1E57;&#x1E57; &#x1E56;&#x1E57;&#x1E57;</span>  <span title="U+1E59">&#x1E58;&#x1E59;&#x1E59; &#x1E58;&#x1E59;&#x1E59;</span>  <span title="U+1E5B">&#x1E5A;&#x1E5B;&#x1E5B; &#x1E5A;&#x1E5B;&#x1E5B;</span>  <span title="U+1E5D">&#x1E5C;&#x1E5D;&#x1E5D; &#x1E5C;&#x1E5D;&#x1E5D;</span>  <span title="U+1E5F">&#x1E5E;&#x1E5F;&#x1E5F; &#x1E5E;&#x1E5F;&#x1E5F;</span>  <span title="U+1E61">&#x1E60;&#x1E61;&#x1E61; &#x1E60;&#x1E61;&#x1E61;</span>  <span title="U+1E63">&#x1E62;&#x1E63;&#x1E63; &#x1E62;&#x1E63;&#x1E63;</span>  <span title="U+1E65">&#x1E64;&#x1E65;&#x1E65; &#x1E64;&#x1E65;&#x1E65;</span>  <span title="U+1E67">&#x1E66;&#x1E67;&#x1E67; &#x1E66;&#x1E67;&#x1E67;</span>  <span title="U+1E69">&#x1E68;&#x1E69;&#x1E69; &#x1E68;&#x1E69;&#x1E69;</span>  <span title="U+1E6B">&#x1E6A;&#x1E6B;&#x1E6B; &#x1E6A;&#x1E6B;&#x1E6B;</span>  <span title="U+1E6D">&#x1E6C;&#x1E6D;&#x1E6D; &#x1E6C;&#x1E6D;&#x1E6D;</span>  <span title="U+1E6F">&#x1E6E;&#x1E6F;&#x1E6F; &#x1E6E;&#x1E6F;&#x1E6F;</span>  <span title="U+1E71">&#x1E70;&#x1E71;&#x1E71; &#x1E70;&#x1E71;&#x1E71;</span>  <span title="U+1E73">&#x1E72;&#x1E73;&#x1E73; &#x1E72;&#x1E73;&#x1E73;</span>  <span title="U+1E75">&#x1E74;&#x1E75;&#x1E75; &#x1E74;&#x1E75;&#x1E75;</span>  <span title="U+1E77">&#x1E76;&#x1E77;&#x1E77; &#x1E76;&#x1E77;&#x1E77;</span>  <span title="U+1E79">&#x1E78;&#x1E79;&#x1E79; &#x1E78;&#x1E79;&#x1E79;</span>  <span title="U+1E7B">&#x1E7A;&#x1E7B;&#x1E7B; &#x1E7A;&#x1E7B;&#x1E7B;</span>  <span title="U+1E7D">&#x1E7C;&#x1E7D;&#x1E7D; &#x1E7C;&#x1E7D;&#x1E7D;</span>  <span title="U+1E7F">&#x1E7E;&#x1E7F;&#x1E7F; &#x1E7E;&#x1E7F;&#x1E7F;</span>  <span title="U+1E81">&#x1E80;&#x1E81;&#x1E81; &#x1E80;&#x1E81;&#x1E81;</span>  <span title="U+1E83">&#x1E82;&#x1E83;&#x1E83; &#x1E82;&#x1E83;&#x1E83;</span>  <span title="U+1E85">&#x1E84;&#x1E85;&#x1E85; &#x1E84;&#x1E85;&#x1E85;</span>  <span title="U+1E87">&#x1E86;&#x1E87;&#x1E87; &#x1E86;&#x1E87;&#x1E87;</span>  <span title="U+1E89">&#x1E88;&#x1E89;&#x1E89; &#x1E88;&#x1E89;&#x1E89;</span>  <span title="U+1E8B">&#x1E8A;&#x1E8B;&#x1E8B; &#x1E8A;&#x1E8B;&#x1E8B;</span>  <span title="U+1E8D">&#x1E8C;&#x1E8D;&#x1E8D; &#x1E8C;&#x1E8D;&#x1E8D;</span>  <span title="U+1E8F">&#x1E8E;&#x1E8F;&#x1E8F; &#x1E8E;&#x1E8F;&#x1E8F;</span>  <span title="U+1E91">&#x1E90;&#x1E91;&#x1E91; &#x1E90;&#x1E91;&#x1E91;</span>  <span title="U+1E93">&#x1E92;&#x1E93;&#x1E93; &#x1E92;&#x1E93;&#x1E93;</span>  <span title="U+1E95">&#x1E94;&#x1E95;&#x1E95; &#x1E94;&#x1E95;&#x1E95;</span>  <span title="U+1E9B">&#x1E60;&#x1E9B;&#x1E9B; &#x1E60;&#x1E9B;&#x1E9B;</span>  <span title="U+1EA1">&#x1EA0;&#x1EA1;&#x1EA1; &#x1EA0;&#x1EA1;&#x1EA1;</span>  <span title="U+1EA3">&#x1EA2;&#x1EA3;&#x1EA3; &#x1EA2;&#x1EA3;&#x1EA3;</span>  <span title="U+1EA5">&#x1EA4;&#x1EA5;&#x1EA5; &#x1EA4;&#x1EA5;&#x1EA5;</span>  <span title="U+1EA7">&#x1EA6;&#x1EA7;&#x1EA7; &#x1EA6;&#x1EA7;&#x1EA7;</span>  <span title="U+1EA9">&#x1EA8;&#x1EA9;&#x1EA9; &#x1EA8;&#x1EA9;&#x1EA9;</span>  <span title="U+1EAB">&#x1EAA;&#x1EAB;&#x1EAB; &#x1EAA;&#x1EAB;&#x1EAB;</span>  <span title="U+1EAD">&#x1EAC;&#x1EAD;&#x1EAD; &#x1EAC;&#x1EAD;&#x1EAD;</span>  <span title="U+1EAF">&#x1EAE;&#x1EAF;&#x1EAF; &#x1EAE;&#x1EAF;&#x1EAF;</span>  <span title="U+1EB1">&#x1EB0;&#x1EB1;&#x1EB1; &#x1EB0;&#x1EB1;&#x1EB1;</span>  <span title="U+1EB3">&#x1EB2;&#x1EB3;&#x1EB3; &#x1EB2;&#x1EB3;&#x1EB3;</span>  <span title="U+1EB5">&#x1EB4;&#x1EB5;&#x1EB5; &#x1EB4;&#x1EB5;&#x1EB5;</span>  <span title="U+1EB7">&#x1EB6;&#x1EB7;&#x1EB7; &#x1EB6;&#x1EB7;&#x1EB7;</span>  <span title="U+1EB9">&#x1EB8;&#x1EB9;&#x1EB9; &#x1EB8;&#x1EB9;&#x1EB9;</span>  <span title="U+1EBB">&#x1EBA;&#x1EBB;&#x1EBB; &#x1EBA;&#x1EBB;&#x1EBB;</span>  <span title="U+1EBD">&#x1EBC;&#x1EBD;&#x1EBD; &#x1EBC;&#x1EBD;&#x1EBD;</span>  <span title="U+1EBF">&#x1EBE;&#x1EBF;&#x1EBF; &#x1EBE;&#x1EBF;&#x1EBF;</span>  <span title="U+1EC1">&#x1EC0;&#x1EC1;&#x1EC1; &#x1EC0;&#x1EC1;&#x1EC1;</span>  <span title="U+1EC3">&#x1EC2;&#x1EC3;&#x1EC3; &#x1EC2;&#x1EC3;&#x1EC3;</span>  <span title="U+1EC5">&#x1EC4;&#x1EC5;&#x1EC5; &#x1EC4;&#x1EC5;&#x1EC5;</span>  <span title="U+1EC7">&#x1EC6;&#x1EC7;&#x1EC7; &#x1EC6;&#x1EC7;&#x1EC7;</span>  <span title="U+1EC9">&#x1EC8;&#x1EC9;&#x1EC9; &#x1EC8;&#x1EC9;&#x1EC9;</span>  <span title="U+1ECB">&#x1ECA;&#x1ECB;&#x1ECB; &#x1ECA;&#x1ECB;&#x1ECB;</span>  <span title="U+1ECD">&#x1ECC;&#x1ECD;&#x1ECD; &#x1ECC;&#x1ECD;&#x1ECD;</span>  <span title="U+1ECF">&#x1ECE;&#x1ECF;&#x1ECF; &#x1ECE;&#x1ECF;&#x1ECF;</span>  <span title="U+1ED1">&#x1ED0;&#x1ED1;&#x1ED1; &#x1ED0;&#x1ED1;&#x1ED1;</span>  <span title="U+1ED3">&#x1ED2;&#x1ED3;&#x1ED3; &#x1ED2;&#x1ED3;&#x1ED3;</span>  <span title="U+1ED5">&#x1ED4;&#x1ED5;&#x1ED5; &#x1ED4;&#x1ED5;&#x1ED5;</span>  <span title="U+1ED7">&#x1ED6;&#x1ED7;&#x1ED7; &#x1ED6;&#x1ED7;&#x1ED7;</span>  <span title="U+1ED9">&#x1ED8;&#x1ED9;&#x1ED9; &#x1ED8;&#x1ED9;&#x1ED9;</span>  <span title="U+1EDB">&#x1EDA;&#x1EDB;&#x1EDB; &#x1EDA;&#x1EDB;&#x1EDB;</span>  <span title="U+1EDD">&#x1EDC;&#x1EDD;&#x1EDD; &#x1EDC;&#x1EDD;&#x1EDD;</span>  <span title="U+1EDF">&#x1EDE;&#x1EDF;&#x1EDF; &#x1EDE;&#x1EDF;&#x1EDF;</span>  <span title="U+1EE1">&#x1EE0;&#x1EE1;&#x1EE1; &#x1EE0;&#x1EE1;&#x1EE1;</span>  <span title="U+1EE3">&#x1EE2;&#x1EE3;&#x1EE3; &#x1EE2;&#x1EE3;&#x1EE3;</span>  <span title="U+1EE5">&#x1EE4;&#x1EE5;&#x1EE5; &#x1EE4;&#x1EE5;&#x1EE5;</span>  <span title="U+1EE7">&#x1EE6;&#x1EE7;&#x1EE7; &#x1EE6;&#x1EE7;&#x1EE7;</span>  <span title="U+1EE9">&#x1EE8;&#x1EE9;&#x1EE9; &#x1EE8;&#x1EE9;&#x1EE9;</span>  <span title="U+1EEB">&#x1EEA;&#x1EEB;&#x1EEB; &#x1EEA;&#x1EEB;&#x1EEB;</span>  <span title="U+1EED">&#x1EEC;&#x1EED;&#x1EED; &#x1EEC;&#x1EED;&#x1EED;</span>  <span title="U+1EEF">&#x1EEE;&#x1EEF;&#x1EEF; &#x1EEE;&#x1EEF;&#x1EEF;</span>  <span title="U+1EF1">&#x1EF0;&#x1EF1;&#x1EF1; &#x1EF0;&#x1EF1;&#x1EF1;</span>  <span title="U+1EF3">&#x1EF2;&#x1EF3;&#x1EF3; &#x1EF2;&#x1EF3;&#x1EF3;</span>  <span title="U+1EF5">&#x1EF4;&#x1EF5;&#x1EF5; &#x1EF4;&#x1EF5;&#x1EF5;</span>  <span title="U+1EF7">&#x1EF6;&#x1EF7;&#x1EF7; &#x1EF6;&#x1EF7;&#x1EF7;</span>  <span title="U+1EF9">&#x1EF8;&#x1EF9;&#x1EF9; &#x1EF8;&#x1EF9;&#x1EF9;</span>  <span title="U+1EFB">&#x1EFA;&#x1EFB;&#x1EFB; &#x1EFA;&#x1EFB;&#x1EFB;</span>  <span title="U+1EFD">&#x1EFC;&#x1EFD;&#x1EFD; &#x1EFC;&#x1EFD;&#x1EFD;</span>  <span title="U+1EFF">&#x1EFE;&#x1EFF;&#x1EFF; &#x1EFE;&#x1EFF;&#x1EFF;</span></div>
-<!--Notes:
-Tip: To identify the characters where differences occur, in order to report problem characters,either mouse over to reveal a tooltip, or copy and paste the sequence into a tool such as <a href='http://r12a.github.io/uniview/' target='_blank'>UniView</a> or the <a href='http://r12a.github.io/apps/conversion/' target='_blank'>Unicode Conversion Tool</a>.
--->
 </body>
-</html>
\ No newline at end of file
+</html>
--- a/testing/web-platform/tests/css/css-text/text-transform/reference/text-transform-capitalize-005-ref.html
+++ b/testing/web-platform/tests/css/css-text/text-transform/reference/text-transform-capitalize-005-ref.html
@@ -1,10 +1,10 @@
 <!DOCTYPE html>
-<html  lang="en" >
+<html lang="en">
 <head>
 <meta charset="utf-8"/>
 <title>CSS3 Text, text transform: capitalize, Latin Extended-A</title>
 <link rel='author' title='Richard Ishida' href='mailto:ishida@w3.org'>
 <style type='text/css'>
 @font-face {
 	font-family: 'webfont';
 	src: url('/fonts/DoulosSIL-R.woff') format('woff');
@@ -14,13 +14,10 @@
 .test, .ref { font-size: 200%; line-height: 2.5em; font-family: 'Doulos SIL', 'Noto Sans', webfont, sans-serif; }
 .test span, .ref span { margin-right: 1em; white-space: nowrap; }
 </style>
 </head>
 <body>
 <p class="instructions">The text is arranged in pairs of three-letter 'words'. Test passes if the words in each pair match<br>
 If you are missing a font glyph for a character, ignore that pair, but report which characters were ignored.</p>
 <div class="test"><span title="U+0101">&#x0100;&#x0101;&#x0101; &#x0100;&#x0101;&#x0101;</span> <span title="U+0103">&#x0102;&#x0103;&#x0103; &#x0102;&#x0103;&#x0103;</span> <span title="U+0105">&#x0104;&#x0105;&#x0105; &#x0104;&#x0105;&#x0105;</span> <span title="U+0107">&#x0106;&#x0107;&#x0107; &#x0106;&#x0107;&#x0107;</span> <span title="U+0109">&#x0108;&#x0109;&#x0109; &#x0108;&#x0109;&#x0109;</span> <span title="U+010B">&#x010A;&#x010B;&#x010B; &#x010A;&#x010B;&#x010B;</span> <span title="U+010D">&#x010C;&#x010D;&#x010D; &#x010C;&#x010D;&#x010D;</span> <span title="U+010F">&#x010E;&#x010F;&#x010F; &#x010E;&#x010F;&#x010F;</span> <span title="U+0111">&#x0110;&#x0111;&#x0111; &#x0110;&#x0111;&#x0111;</span> <span title="U+0113">&#x0112;&#x0113;&#x0113; &#x0112;&#x0113;&#x0113;</span> <span title="U+0115">&#x0114;&#x0115;&#x0115; &#x0114;&#x0115;&#x0115;</span> <span title="U+0117">&#x0116;&#x0117;&#x0117; &#x0116;&#x0117;&#x0117;</span> <span title="U+0119">&#x0118;&#x0119;&#x0119; &#x0118;&#x0119;&#x0119;</span> <span title="U+011B">&#x011A;&#x011B;&#x011B; &#x011A;&#x011B;&#x011B;</span> <span title="U+011D">&#x011C;&#x011D;&#x011D; &#x011C;&#x011D;&#x011D;</span> <span title="U+011F">&#x011E;&#x011F;&#x011F; &#x011E;&#x011F;&#x011F;</span> <span title="U+0121">&#x0120;&#x0121;&#x0121; &#x0120;&#x0121;&#x0121;</span> <span title="U+0123">&#x0122;&#x0123;&#x0123; &#x0122;&#x0123;&#x0123;</span> <span title="U+0125">&#x0124;&#x0125;&#x0125; &#x0124;&#x0125;&#x0125;</span> <span title="U+0127">&#x0126;&#x0127;&#x0127; &#x0126;&#x0127;&#x0127;</span> <span title="U+0129">&#x0128;&#x0129;&#x0129; &#x0128;&#x0129;&#x0129;</span> <span title="U+012B">&#x012A;&#x012B;&#x012B; &#x012A;&#x012B;&#x012B;</span> <span title="U+012D">&#x012C;&#x012D;&#x012D; &#x012C;&#x012D;&#x012D;</span> <span title="U+012F">&#x012E;&#x012F;&#x012F; &#x012E;&#x012F;&#x012F;</span> <span title="U+0131">&#x0049;&#x0131;&#x0131; &#x0049;&#x0131;&#x0131;</span> <span title="U+0133">&#x0132;&#x0133;&#x0133; &#x0132;&#x0133;&#x0133;</span> <span title="U+0135">&#x0134;&#x0135;&#x0135; &#x0134;&#x0135;&#x0135;</span> <span title="U+0137">&#x0136;&#x0137;&#x0137; &#x0136;&#x0137;&#x0137;</span> <span title="U+013A">&#x0139;&#x013A;&#x013A; &#x0139;&#x013A;&#x013A;</span> <span title="U+013C">&#x013B;&#x013C;&#x013C; &#x013B;&#x013C;&#x013C;</span> <span title="U+013E">&#x013D;&#x013E;&#x013E; &#x013D;&#x013E;&#x013E;</span> <span title="U+0140">&#x013F;&#x0140;&#x0140; &#x013F;&#x0140;&#x0140;</span> <span title="U+0142">&#x0141;&#x0142;&#x0142; &#x0141;&#x0142;&#x0142;</span> <span title="U+0144">&#x0143;&#x0144;&#x0144; &#x0143;&#x0144;&#x0144;</span> <span title="U+0146">&#x0145;&#x0146;&#x0146; &#x0145;&#x0146;&#x0146;</span> <span title="U+0148">&#x0147;&#x0148;&#x0148; &#x0147;&#x0148;&#x0148;</span> <span title="U+014B">&#x014A;&#x014B;&#x014B; &#x014A;&#x014B;&#x014B;</span> <span title="U+014D">&#x014C;&#x014D;&#x014D; &#x014C;&#x014D;&#x014D;</span> <span title="U+014F">&#x014E;&#x014F;&#x014F; &#x014E;&#x014F;&#x014F;</span> <span title="U+0151">&#x0150;&#x0151;&#x0151; &#x0150;&#x0151;&#x0151;</span> <span title="U+0153">&#x0152;&#x0153;&#x0153; &#x0152;&#x0153;&#x0153;</span> <span title="U+0155">&#x0154;&#x0155;&#x0155; &#x0154;&#x0155;&#x0155;</span> <span title="U+0157">&#x0156;&#x0157;&#x0157; &#x0156;&#x0157;&#x0157;</span> <span title="U+0159">&#x0158;&#x0159;&#x0159; &#x0158;&#x0159;&#x0159;</span> <span title="U+015B">&#x015A;&#x015B;&#x015B; &#x015A;&#x015B;&#x015B;</span> <span title="U+015D">&#x015C;&#x015D;&#x015D; &#x015C;&#x015D;&#x015D;</span> <span title="U+015F">&#x015E;&#x015F;&#x015F; &#x015E;&#x015F;&#x015F;</span> <span title="U+0161">&#x0160;&#x0161;&#x0161; &#x0160;&#x0161;&#x0161;</span> <span title="U+0163">&#x0162;&#x0163;&#x0163; &#x0162;&#x0163;&#x0163;</span> <span title="U+0165">&#x0164;&#x0165;&#x0165; &#x0164;&#x0165;&#x0165;</span> <span title="U+0167">&#x0166;&#x0167;&#x0167; &#x0166;&#x0167;&#x0167;</span> <span title="U+0169">&#x0168;&#x0169;&#x0169; &#x0168;&#x0169;&#x0169;</span> <span title="U+016B">&#x016A;&#x016B;&#x016B; &#x016A;&#x016B;&#x016B;</span> <span title="U+016D">&#x016C;&#x016D;&#x016D; &#x016C;&#x016D;&#x016D;</span> <span title="U+016F">&#x016E;&#x016F;&#x016F; &#x016E;&#x016F;&#x016F;</span> <span title="U+0171">&#x0170;&#x0171;&#x0171; &#x0170;&#x0171;&#x0171;</span> <span title="U+0173">&#x0172;&#x0173;&#x0173; &#x0172;&#x0173;&#x0173;</span> <span title="U+0175">&#x0174;&#x0175;&#x0175; &#x0174;&#x0175;&#x0175;</span> <span title="U+0177">&#x0176;&#x0177;&#x0177; &#x0176;&#x0177;&#x0177;</span> <span title="U+017A">&#x0179;&#x017A;&#x017A; &#x0179;&#x017A;&#x017A;</span> <span title="U+017C">&#x017B;&#x017C;&#x017C; &#x017B;&#x017C;&#x017C;</span> <span title="U+017E">&#x017D;&#x017E;&#x017E; &#x017D;&#x017E;&#x017E;</span> <span title="U+017F">&#x0053;&#x017F;&#x017F; &#x0053;&#x017F;&#x017F;</span></div>
-<!--Notes:
-Tip: To identify the characters where differences occur, in order to report problem characters,either mouse over to reveal a tooltip, or copy and paste the sequence into a tool such as <a href='http://r12a.github.io/uniview/' target='_blank'>UniView</a> or the <a href='http://r12a.github.io/apps/conversion/' target='_blank'>Unicode Conversion Tool</a>.
--->
 </body>
-</html>
\ No newline at end of file
+</html>
--- a/testing/web-platform/tests/css/css-text/text-transform/reference/text-transform-capitalize-007-ref.html
+++ b/testing/web-platform/tests/css/css-text/text-transform/reference/text-transform-capitalize-007-ref.html
@@ -1,10 +1,10 @@
 <!DOCTYPE html>
-<html  lang="en" >
+<html lang="en">
 <head>
 <meta charset="utf-8"/>
 <title>CSS3 Text, text transform: capitalize, Latin Extended-B</title>
 <link rel='author' title='Richard Ishida' href='mailto:ishida@w3.org'>
 <style type='text/css'>
 @font-face {
 	font-family: 'webfont';
 	src: url('/fonts/DoulosSIL-R.woff') format('woff');
@@ -14,13 +14,10 @@
 .test, .ref { font-size: 200%; line-height: 2.5em; font-family: 'Doulos SIL', 'Noto Sans', webfont, sans-serif; }
 .test span, .ref span { margin-right: 1em; white-space: nowrap; }
 </style>
 </head>
 <body>
 <p class="instructions">The text is arranged in pairs of three-letter 'words'. Test passes if the words in each pair match<br>
 If you are missing a font glyph for a character, ignore that pair, but report which characters were ignored.</p>
 <div class="test"><span title="U+0180">&#x0243;&#x0180;&#x0180; &#x0243;&#x0180;&#x0180;</span> <span title="U+0183">&#x0182;&#x0183;&#x0183; &#x0182;&#x0183;&#x0183;</span> <span title="U+0185">&#x0184;&#x0185;&#x0185; &#x0184;&#x0185;&#x0185;</span> <span title="U+0188">&#x0187;&#x0188;&#x0188; &#x0187;&#x0188;&#x0188;</span> <span title="U+018C">&#x018B;&#x018C;&#x018C; &#x018B;&#x018C;&#x018C;</span> <span title="U+0192">&#x0191;&#x0192;&#x0192; &#x0191;&#x0192;&#x0192;</span> <span title="U+0195">&#x01F6;&#x0195;&#x0195; &#x01F6;&#x0195;&#x0195;</span> <span title="U+0199">&#x0198;&#x0199;&#x0199; &#x0198;&#x0199;&#x0199;</span> <span title="U+019A">&#x023D;&#x019A;&#x019A; &#x023D;&#x019A;&#x019A;</span> <span title="U+019E">&#x0220;&#x019E;&#x019E; &#x0220;&#x019E;&#x019E;</span> <span title="U+01A1">&#x01A0;&#x01A1;&#x01A1; &#x01A0;&#x01A1;&#x01A1;</span> <span title="U+01A3">&#x01A2;&#x01A3;&#x01A3; &#x01A2;&#x01A3;&#x01A3;</span> <span title="U+01A5">&#x01A4;&#x01A5;&#x01A5; &#x01A4;&#x01A5;&#x01A5;</span> <span title="U+01A8">&#x01A7;&#x01A8;&#x01A8; &#x01A7;&#x01A8;&#x01A8;</span> <span title="U+01AD">&#x01AC;&#x01AD;&#x01AD; &#x01AC;&#x01AD;&#x01AD;</span> <span title="U+01B0">&#x01AF;&#x01B0;&#x01B0; &#x01AF;&#x01B0;&#x01B0;</span> <span title="U+01B4">&#x01B3;&#x01B4;&#x01B4; &#x01B3;&#x01B4;&#x01B4;</span> <span title="U+01B6">&#x01B5;&#x01B6;&#x01B6; &#x01B5;&#x01B6;&#x01B6;</span> <span title="U+01B9">&#x01B8;&#x01B9;&#x01B9; &#x01B8;&#x01B9;&#x01B9;</span> <span title="U+01BD">&#x01BC;&#x01BD;&#x01BD; &#x01BC;&#x01BD;&#x01BD;</span> <span title="U+01BF">&#x01F7;&#x01BF;&#x01BF; &#x01F7;&#x01BF;&#x01BF;</span> <span title="U+01C4">&#x01C5;&#x01C6;&#x01C6; &#x01C5;&#x01C6;&#x01C6;</span> <span title="U+01C6">&#x01C5;&#x01C6;&#x01C6; &#x01C5;&#x01C6;&#x01C6;</span> <span title="U+01C7">&#x01C8;&#x01C9;&#x01C9; &#x01C8;&#x01C9;&#x01C9;</span> <span title="U+01C9">&#x01C8;&#x01C9;&#x01C9; &#x01C8;&#x01C9;&#x01C9;</span> <span title="U+01CA">&#x01CB;&#x01CC;&#x01CC; &#x01CB;&#x01CC;&#x01CC;</span> <span title="U+01CC">&#x01CB;&#x01CC;&#x01CC; &#x01CB;&#x01CC;&#x01CC;</span> <span title="U+01CE">&#x01CD;&#x01CE;&#x01CE; &#x01CD;&#x01CE;&#x01CE;</span> <span title="U+01D0">&#x01CF;&#x01D0;&#x01D0; &#x01CF;&#x01D0;&#x01D0;</span> <span title="U+01D2">&#x01D1;&#x01D2;&#x01D2; &#x01D1;&#x01D2;&#x01D2;</span> <span title="U+01D4">&#x01D3;&#x01D4;&#x01D4; &#x01D3;&#x01D4;&#x01D4;</span> <span title="U+01D6">&#x01D5;&#x01D6;&#x01D6; &#x01D5;&#x01D6;&#x01D6;</span> <span title="U+01D8">&#x01D7;&#x01D8;&#x01D8; &#x01D7;&#x01D8;&#x01D8;</span> <span title="U+01DA">&#x01D9;&#x01DA;&#x01DA; &#x01D9;&#x01DA;&#x01DA;</span> <span title="U+01DC">&#x01DB;&#x01DC;&#x01DC; &#x01DB;&#x01DC;&#x01DC;</span> <span title="U+01DD">&#x018E;&#x01DD;&#x01DD; &#x018E;&#x01DD;&#x01DD;</span> <span title="U+01DF">&#x01DE;&#x01DF;&#x01DF; &#x01DE;&#x01DF;&#x01DF;</span> <span title="U+01E1">&#x01E0;&#x01E1;&#x01E1; &#x01E0;&#x01E1;&#x01E1;</span> <span title="U+01E3">&#x01E2;&#x01E3;&#x01E3; &#x01E2;&#x01E3;&#x01E3;</span> <span title="U+01E5">&#x01E4;&#x01E5;&#x01E5; &#x01E4;&#x01E5;&#x01E5;</span> <span title="U+01E7">&#x01E6;&#x01E7;&#x01E7; &#x01E6;&#x01E7;&#x01E7;</span> <span title="U+01E9">&#x01E8;&#x01E9;&#x01E9; &#x01E8;&#x01E9;&#x01E9;</span> <span title="U+01EB">&#x01EA;&#x01EB;&#x01EB; &#x01EA;&#x01EB;&#x01EB;</span> <span title="U+01ED">&#x01EC;&#x01ED;&#x01ED; &#x01EC;&#x01ED;&#x01ED;</span> <span title="U+01EF">&#x01EE;&#x01EF;&#x01EF; &#x01EE;&#x01EF;&#x01EF;</span> <span title="U+01F1">&#x01F2;&#x01F3;&#x01F3; &#x01F2;&#x01F3;&#x01F3;</span> <span title="U+01F3">&#x01F2;&#x01F3;&#x01F3; &#x01F2;&#x01F3;&#x01F3;</span> <span title="U+01F5">&#x01F4;&#x01F5;&#x01F5; &#x01F4;&#x01F5;&#x01F5;</span> <span title="U+01F9">&#x01F8;&#x01F9;&#x01F9; &#x01F8;&#x01F9;&#x01F9;</span> <span title="U+01FB">&#x01FA;&#x01FB;&#x01FB; &#x01FA;&#x01FB;&#x01FB;</span> <span title="U+01FD">&#x01FC;&#x01FD;&#x01FD; &#x01FC;&#x01FD;&#x01FD;</span> <span title="U+01FF">&#x01FE;&#x01FF;&#x01FF; &#x01FE;&#x01FF;&#x01FF;</span> <span title="U+0201">&#x0200;&#x0201;&#x0201; &#x0200;&#x0201;&#x0201;</span> <span title="U+0203">&#x0202;&#x0203;&#x0203; &#x0202;&#x0203;&#x0203;</span> <span title="U+0205">&#x0204;&#x0205;&#x0205; &#x0204;&#x0205;&#x0205;</span> <span title="U+0207">&#x0206;&#x0207;&#x0207; &#x0206;&#x0207;&#x0207;</span> <span title="U+0209">&#x0208;&#x0209;&#x0209; &#x0208;&#x0209;&#x0209;</span> <span title="U+020B">&#x020A;&#x020B;&#x020B; &#x020A;&#x020B;&#x020B;</span> <span title="U+020D">&#x020C;&#x020D;&#x020D; &#x020C;&#x020D;&#x020D;</span> <span title="U+020F">&#x020E;&#x020F;&#x020F; &#x020E;&#x020F;&#x020F;</span> <span title="U+0211">&#x0210;&#x0211;&#x0211; &#x0210;&#x0211;&#x0211;</span> <span title="U+0213">&#x0212;&#x0213;&#x0213; &#x0212;&#x0213;&#x0213;</span> <span title="U+0215">&#x0214;&#x0215;&#x0215; &#x0214;&#x0215;&#x0215;</span> <span title="U+0217">&#x0216;&#x0217;&#x0217; &#x0216;&#x0217;&#x0217;</span> <span title="U+0219">&#x0218;&#x0219;&#x0219; &#x0218;&#x0219;&#x0219;</span> <span title="U+021B">&#x021A;&#x021B;&#x021B; &#x021A;&#x021B;&#x021B;</span> <span title="U+021D">&#x021C;&#x021D;&#x021D; &#x021C;&#x021D;&#x021D;</span> <span title="U+021F">&#x021E;&#x021F;&#x021F; &#x021E;&#x021F;&#x021F;</span> <span title="U+0223">&#x0222;&#x0223;&#x0223; &#x0222;&#x0223;&#x0223;</span> <span title="U+0225">&#x0224;&#x0225;&#x0225; &#x0224;&#x0225;&#x0225;</span> <span title="U+0227">&#x0226;&#x0227;&#x0227; &#x0226;&#x0227;&#x0227;</span> <span title="U+0229">&#x0228;&#x0229;&#x0229; &#x0228;&#x0229;&#x0229;</span> <span title="U+022B">&#x022A;&#x022B;&#x022B; &#x022A;&#x022B;&#x022B;</span> <span title="U+022D">&#x022C;&#x022D;&#x022D; &#x022C;&#x022D;&#x022D;</span> <span title="U+022F">&#x022E;&#x022F;&#x022F; &#x022E;&#x022F;&#x022F;</span> <span title="U+0231">&#x0230;&#x0231;&#x0231; &#x0230;&#x0231;&#x0231;</span> <span title="U+0233">&#x0232;&#x0233;&#x0233; &#x0232;&#x0233;&#x0233;</span> <span title="U+023C">&#x023B;&#x023C;&#x023C; &#x023B;&#x023C;&#x023C;</span> <span title="U+023F">&#x2C7E;&#x023F;&#x023F; &#x2C7E;&#x023F;&#x023F;</span> <span title="U+0240">&#x2C7F;&#x0240;&#x0240; &#x2C7F;&#x0240;&#x0240;</span> <span title="U+0242">&#x0241;&#x0242;&#x0242; &#x0241;&#x0242;&#x0242;</span> <span title="U+0247">&#x0246;&#x0247;&#x0247; &#x0246;&#x0247;&#x0247;</span> <span title="U+0249">&#x0248;&#x0249;&#x0249; &#x0248;&#x0249;&#x0249;</span> <span title="U+024B">&#x024A;&#x024B;&#x024B; &#x024A;&#x024B;&#x024B;</span> <span title="U+024D">&#x024C;&#x024D;&#x024D; &#x024C;&#x024D;&#x024D;</span> <span title="U+024F">&#x024E;&#x024F;&#x024F; &#x024E;&#x024F;&#x024F;</span> </div>
-<!--Notes:
-Tip: To identify the characters where differences occur, in order to report problem characters, copy and paste the sequence into a tool such as <a href='http://r12a.github.io/uniview/' target='_blank'>UniView</a> or the <a href='http://r12a.github.io/apps/conversion/' target='_blank'>Unicode Conversion Tool</a>.
--->
 </body>
-</html>
\ No newline at end of file
+</html>
--- a/testing/web-platform/tests/css/css-text/text-transform/reference/text-transform-capitalize-009-ref.html
+++ b/testing/web-platform/tests/css/css-text/text-transform/reference/text-transform-capitalize-009-ref.html
@@ -1,10 +1,10 @@
 <!DOCTYPE html>
-<html  lang="en" >
+<html lang="en">
 <head>
 <meta charset="utf-8"/>
 <title>CSS3 Text, text transform: capitalize, Latin Extended-C</title>
 <link rel='author' title='Richard Ishida' href='mailto:ishida@w3.org'>
 <style type='text/css'>
 @font-face {
 	font-family: 'webfont';
 	src: url('/fonts/DoulosSIL-R.woff') format('woff');
@@ -14,13 +14,10 @@
 .test, .ref { font-size: 200%; line-height: 2.5em; font-family: 'Doulos SIL', 'Noto Sans', webfont, sans-serif; }
 .test span, .ref span { margin-right: 1em; white-space: nowrap; }
 </style>
 </head>
 <body>
 <p class="instructions">The text is arranged in pairs of three-letter 'words'. Test passes if the words in each pair match<br>
 If you are missing a font glyph for a character, ignore that pair, but report which characters were ignored.</p>
 <div class="test"><span title="U+2C61">&#x2C60;&#x2C61;&#x2C61; &#x2C60;&#x2C61;&#x2C61;</span> <span title="U+2C65">&#x023A;&#x2C65;&#x2C65; &#x023A;&#x2C65;&#x2C65;</span> <span title="U+2C66">&#x023E;&#x2C66;&#x2C66; &#x023E;&#x2C66;&#x2C66;</span> <span title="U+2C68">&#x2C67;&#x2C68;&#x2C68; &#x2C67;&#x2C68;&#x2C68;</span> <span title="U+2C6A">&#x2C69;&#x2C6A;&#x2C6A; &#x2C69;&#x2C6A;&#x2C6A;</span> <span title="U+2C6C">&#x2C6B;&#x2C6C;&#x2C6C; &#x2C6B;&#x2C6C;&#x2C6C;</span> <span title="U+2C73">&#x2C72;&#x2C73;&#x2C73; &#x2C72;&#x2C73;&#x2C73;</span> <span title="U+2C76">&#x2C75;&#x2C76;&#x2C76; &#x2C75;&#x2C76;&#x2C76;</span></div>
-<!--Notes:
-Tip: To identify the characters where differences occur, in order to report problem characters, copy and paste the sequence into a tool such as <a href='http://r12a.github.io/uniview/' target='_blank'>UniView</a> or the <a href='http://r12a.github.io/apps/conversion/' target='_blank'>Unicode Conversion Tool</a>.
--->
 </body>
-</html>
\ No newline at end of file
+</html>
--- a/testing/web-platform/tests/css/css-text/text-transform/reference/text-transform-capitalize-010-ref.html
+++ b/testing/web-platform/tests/css/css-text/text-transform/reference/text-transform-capitalize-010-ref.html
@@ -1,10 +1,10 @@
 <!DOCTYPE html>
-<html  lang="en" >
+<html lang="en">
 <head>
 <meta charset="utf-8"/>
 <title>CSS3 Text, text transform: capitalize, Latin Extended-D</title>
 <link rel='author' title='Richard Ishida' href='mailto:ishida@w3.org'>
 <style type='text/css'>
 @font-face {
 	font-family: 'webfont';
 	src: url('/fonts/DoulosSIL-R.woff') format('woff');
@@ -14,13 +14,10 @@
 .test, .ref { font-size: 200%; line-height: 2.5em; font-family: 'Doulos SIL', 'Noto Sans', webfont, sans-serif; }
 .test span, .ref span { margin-right: 1em; white-space: nowrap; }
 </style>
 </head>
 <body>
 <p class="instructions">The text is arranged in pairs of three-letter 'words'. Test passes if the words in each pair match<br>
 If you are missing a font glyph for a character, ignore that pair, but report which characters were ignored.</p>
 <div class="test"><span title="U+A723">&#xA722;&#xA723;&#xA723; &#xA722;&#xA723;&#xA723;</span> <span title="U+A725">&#xA724;&#xA725;&#xA725; &#xA724;&#xA725;&#xA725;</span> <span title="U+A727">&#xA726;&#xA727;&#xA727; &#xA726;&#xA727;&#xA727;</span> <span title="U+A729">&#xA728;&#xA729;&#xA729; &#xA728;&#xA729;&#xA729;</span> <span title="U+A72B">&#xA72A;&#xA72B;&#xA72B; &#xA72A;&#xA72B;&#xA72B;</span> <span title="U+A72D">&#xA72C;&#xA72D;&#xA72D; &#xA72C;&#xA72D;&#xA72D;</span> <span title="U+A72F">&#xA72E;&#xA72F;&#xA72F; &#xA72E;&#xA72F;&#xA72F;</span> <span title="U+A733">&#xA732;&#xA733;&#xA733; &#xA732;&#xA733;&#xA733;</span> <span title="U+A735">&#xA734;&#xA735;&#xA735; &#xA734;&#xA735;&#xA735;</span> <span title="U+A737">&#xA736;&#xA737;&#xA737; &#xA736;&#xA737;&#xA737;</span> <span title="U+A739">&#xA738;&#xA739;&#xA739; &#xA738;&#xA739;&#xA739;</span> <span title="U+A73B">&#xA73A;&#xA73B;&#xA73B; &#xA73A;&#xA73B;&#xA73B;</span> <span title="U+A73D">&#xA73C;&#xA73D;&#xA73D; &#xA73C;&#xA73D;&#xA73D;</span> <span title="U+A73F">&#xA73E;&#xA73F;&#xA73F; &#xA73E;&#xA73F;&#xA73F;</span> <span title="U+A741">&#xA740;&#xA741;&#xA741; &#xA740;&#xA741;&#xA741;</span> <span title="U+A743">&#xA742;&#xA743;&#xA743; &#xA742;&#xA743;&#xA743;</span> <span title="U+A745">&#xA744;&#xA745;&#xA745; &#xA744;&#xA745;&#xA745;</span> <span title="U+A747">&#xA746;&#xA747;&#xA747; &#xA746;&#xA747;&#xA747;</span> <span title="U+A749">&#xA748;&#xA749;&#xA749; &#xA748;&#xA749;&#xA749;</span> <span title="U+A74B">&#xA74A;&#xA74B;&#xA74B; &#xA74A;&#xA74B;&#xA74B;</span> <span title="U+A74D">&#xA74C;&#xA74D;&#xA74D; &#xA74C;&#xA74D;&#xA74D;</span> <span title="U+A74F">&#xA74E;&#xA74F;&#xA74F; &#xA74E;&#xA74F;&#xA74F;</span> <span title="U+A751">&#xA750;&#xA751;&#xA751; &#xA750;&#xA751;&#xA751;</span> <span title="U+A753">&#xA752;&#xA753;&#xA753; &#xA752;&#xA753;&#xA753;</span> <span title="U+A755">&#xA754;&#xA755;&#xA755; &#xA754;&#xA755;&#xA755;</span> <span title="U+A757">&#xA756;&#xA757;&#xA757; &#xA756;&#xA757;&#xA757;</span> <span title="U+A759">&#xA758;&#xA759;&#xA759; &#xA758;&#xA759;&#xA759;</span> <span title="U+A75B">&#xA75A;&#xA75B;&#xA75B; &#xA75A;&#xA75B;&#xA75B;</span> <span title="U+A75D">&#xA75C;&#xA75D;&#xA75D; &#xA75C;&#xA75D;&#xA75D;</span> <span title="U+A75F">&#xA75E;&#xA75F;&#xA75F; &#xA75E;&#xA75F;&#xA75F;</span> <span title="U+A761">&#xA760;&#xA761;&#xA761; &#xA760;&#xA761;&#xA761;</span> <span title="U+A763">&#xA762;&#xA763;&#xA763; &#xA762;&#xA763;&#xA763;</span> <span title="U+A765">&#xA764;&#xA765;&#xA765; &#xA764;&#xA765;&#xA765;</span> <span title="U+A767">&#xA766;&#xA767;&#xA767; &#xA766;&#xA767;&#xA767;</span> <span title="U+A769">&#xA768;&#xA769;&#xA769; &#xA768;&#xA769;&#xA769;</span> <span title="U+A76B">&#xA76A;&#xA76B;&#xA76B; &#xA76A;&#xA76B;&#xA76B;</span> <span title="U+A76D">&#xA76C;&#xA76D;&#xA76D; &#xA76C;&#xA76D;&#xA76D;</span> <span title="U+A76F">&#xA76E;&#xA76F;&#xA76F; &#xA76E;&#xA76F;&#xA76F;</span> <span title="U+A77A">&#xA779;&#xA77A;&#xA77A; &#xA779;&#xA77A;&#xA77A;</span> <span title="U+A77C">&#xA77B;&#xA77C;&#xA77C; &#xA77B;&#xA77C;&#xA77C;</span> <span title="U+A77F">&#xA77E;&#xA77F;&#xA77F; &#xA77E;&#xA77F;&#xA77F;</span> <span title="U+A781">&#xA780;&#xA781;&#xA781; &#xA780;&#xA781;&#xA781;</span> <span title="U+A783">&#xA782;&#xA783;&#xA783; &#xA782;&#xA783;&#xA783;</span> <span title="U+A785">&#xA784;&#xA785;&#xA785; &#xA784;&#xA785;&#xA785;</span> <span title="U+A787">&#xA786;&#xA787;&#xA787; &#xA786;&#xA787;&#xA787;</span> <span title="U+A78C">&#xA78B;&#xA78C;&#xA78C; &#xA78B;&#xA78C;&#xA78C;</span> <span title="U+A791">&#xA790;&#xA791;&#xA791; &#xA790;&#xA791;&#xA791;</span> <span title="U+A793">&#xA792;&#xA793;&#xA793; &#xA792;&#xA793;&#xA793;</span> <span title="U+A797">&#xA796;&#xA797;&#xA797; &#xA796;&#xA797;&#xA797;</span> <span title="U+A799">&#xA798;&#xA799;&#xA799; &#xA798;&#xA799;&#xA799;</span> <span title="U+A79B">&#xA79A;&#xA79B;&#xA79B; &#xA79A;&#xA79B;&#xA79B;</span> <span title="U+A79D">&#xA79C;&#xA79D;&#xA79D; &#xA79C;&#xA79D;&#xA79D;</span> <span title="U+A79F">&#xA79E;&#xA79F;&#xA79F; &#xA79E;&#xA79F;&#xA79F;</span> <span title="U+A7A1">&#xA7A0;&#xA7A1;&#xA7A1; &#xA7A0;&#xA7A1;&#xA7A1;</span> <span title="U+A7A3">&#xA7A2;&#xA7A3;&#xA7A3; &#xA7A2;&#xA7A3;&#xA7A3;</span> <span title="U+A7A5">&#xA7A4;&#xA7A5;&#xA7A5; &#xA7A4;&#xA7A5;&#xA7A5;</span> <span title="U+A7A7">&#xA7A6;&#xA7A7;&#xA7A7; &#xA7A6;&#xA7A7;&#xA7A7;</span> <span title="U+A7A9">&#xA7A8;&#xA7A9;&#xA7A9; &#xA7A8;&#xA7A9;&#xA7A9;</span> </div>
-<!--Notes:
-Tip: To identify the characters where differences occur, in order to report problem characters,either mouse over to reveal a tooltip, or copy and paste the sequence into a tool such as <a href='http://r12a.github.io/uniview/' target='_blank'>UniView</a> or the <a href='http://r12a.github.io/apps/conversion/' target='_blank'>Unicode Conversion Tool</a>.
--->
 </body>
-</html>
\ No newline at end of file
+</html>
--- a/testing/web-platform/tests/css/css-text/text-transform/reference/text-transform-capitalize-011-ref.html
+++ b/testing/web-platform/tests/css/css-text/text-transform/reference/text-transform-capitalize-011-ref.html
@@ -1,10 +1,10 @@
 <!DOCTYPE html>
-<html  lang="en" >
+<html lang="en">
 <head>
 <meta charset="utf-8"/>
 <title>CSS3 Text, text transform: capitalize, Full-width Latin</title>
 <link rel='author' title='Richard Ishida' href='mailto:ishida@w3.org'>
 <style type='text/css'>
 @font-face {
 	font-family: 'webfont';
 	src: url('/fonts/DoulosSIL-R.woff') format('woff');
@@ -14,13 +14,10 @@
 .test, .ref { font-size: 200%; line-height: 2.5em; font-family: 'Doulos SIL', 'Noto Sans', webfont, sans-serif; }
 .test span, .ref span { margin-right: 1em; white-space: nowrap; }
 </style>
 </head>
 <body>
 <p class="instructions">The text is arranged in pairs of three-letter 'words'. Test passes if the words in each pair match<br>
 If you are missing a font glyph for a character, ignore that pair, but report which characters were ignored.</p>
 <div class="test"><span title="U+FF41">&#xFF21;&#xFF41;&#xFF41; &#xFF21;&#xFF41;&#xFF41;</span> <span title="U+FF42">&#xFF22;&#xFF42;&#xFF42; &#xFF22;&#xFF42;&#xFF42;</span> <span title="U+FF43">&#xFF23;&#xFF43;&#xFF43; &#xFF23;&#xFF43;&#xFF43;</span> <span title="U+FF44">&#xFF24;&#xFF44;&#xFF44; &#xFF24;&#xFF44;&#xFF44;</span> <span title="U+FF45">&#xFF25;&#xFF45;&#xFF45; &#xFF25;&#xFF45;&#xFF45;</span> <span title="U+FF46">&#xFF26;&#xFF46;&#xFF46; &#xFF26;&#xFF46;&#xFF46;</span> <span title="U+FF47">&#xFF27;&#xFF47;&#xFF47; &#xFF27;&#xFF47;&#xFF47;</span> <span title="U+FF48">&#xFF28;&#xFF48;&#xFF48; &#xFF28;&#xFF48;&#xFF48;</span> <span title="U+FF49">&#xFF29;&#xFF49;&#xFF49; &#xFF29;&#xFF49;&#xFF49;</span> <span title="U+FF4A">&#xFF2A;&#xFF4A;&#xFF4A; &#xFF2A;&#xFF4A;&#xFF4A;</span> <span title="U+FF4B">&#xFF2B;&#xFF4B;&#xFF4B; &#xFF2B;&#xFF4B;&#xFF4B;</span> <span title="U+FF4C">&#xFF2C;&#xFF4C;&#xFF4C; &#xFF2C;&#xFF4C;&#xFF4C;</span> <span title="U+FF4D">&#xFF2D;&#xFF4D;&#xFF4D; &#xFF2D;&#xFF4D;&#xFF4D;</span> <span title="U+FF4E">&#xFF2E;&#xFF4E;&#xFF4E; &#xFF2E;&#xFF4E;&#xFF4E;</span> <span title="U+FF4F">&#xFF2F;&#xFF4F;&#xFF4F; &#xFF2F;&#xFF4F;&#xFF4F;</span> <span title="U+FF50">&#xFF30;&#xFF50;&#xFF50; &#xFF30;&#xFF50;&#xFF50;</span> <span title="U+FF51">&#xFF31;&#xFF51;&#xFF51; &#xFF31;&#xFF51;&#xFF51;</span> <span title="U+FF52">&#xFF32;&#xFF52;&#xFF52; &#xFF32;&#xFF52;&#xFF52;</span> <span title="U+FF53">&#xFF33;&#xFF53;&#xFF53; &#xFF33;&#xFF53;&#xFF53;</span> <span title="U+FF54">&#xFF34;&#xFF54;&#xFF54; &#xFF34;&#xFF54;&#xFF54;</span> <span title="U+FF55">&#xFF35;&#xFF55;&#xFF55; &#xFF35;&#xFF55;&#xFF55;</span> <span title="U+FF56">&#xFF36;&#xFF56;&#xFF56; &#xFF36;&#xFF56;&#xFF56;</span> <span title="U+FF57">&#xFF37;&#xFF57;&#xFF57; &#xFF37;&#xFF57;&#xFF57;</span> <span title="U+FF58">&#xFF38;&#xFF58;&#xFF58; &#xFF38;&#xFF58;&#xFF58;</span> <span title="U+FF59">&#xFF39;&#xFF59;&#xFF59; &#xFF39;&#xFF59;&#xFF59;</span> <span title="U+FF5A">&#xFF3A;&#xFF5A;&#xFF5A; &#xFF3A;&#xFF5A;&#xFF5A;</span></div>
-<!--Notes:
-Tip: To identify the characters where differences occur, in order to report problem characters,either mouse over to reveal a tooltip, or copy and paste the sequence into a tool such as <a href='http://r12a.github.io/uniview/' target='_blank'>UniView</a> or the <a href='http://r12a.github.io/apps/conversion/' target='_blank'>Unicode Conversion Tool</a>.
--->
 </body>
-</html>
\ No newline at end of file
+</html>
--- a/testing/web-platform/tests/css/css-text/text-transform/reference/text-transform-capitalize-014-ref.html
+++ b/testing/web-platform/tests/css/css-text/text-transform/reference/text-transform-capitalize-014-ref.html
@@ -1,10 +1,10 @@
 <!DOCTYPE html>
-<html  lang="en" >
+<html lang="en">
 <head>
 <meta charset="utf-8"/>
 <title>CSS3 Text, text transform: capitalize, Greek and Coptic</title>
 <link rel='author' title='Richard Ishida' href='mailto:ishida@w3.org'>
 <style type='text/css'>
 @font-face {
 	font-family: 'webfont';
 	src: url('/fonts/GentiumPlus-R.woff') format('woff');
@@ -14,13 +14,10 @@
 .test, .ref { font-size: 200%; line-height: 2.5em; font-family: 'Gentium Plus', 'Noto Serif', 'Noto Sans', webfont, sans-serif; }
 .test span, .ref span { margin-right: 1em; white-space: nowrap; }
 </style>
 </head>
 <body>
 <p class="instructions">The text is arranged in pairs of three-letter 'words'. Test passes if the words in each pair match<br>
 If you are missing a font glyph for a character, ignore that pair, but report which characters were ignored.</p>
 <div class="test"><span title="U+0371">&#x0370;&#x0371;&#x0371; &#x0370;&#x0371;&#x0371;</span> <span title="U+0373">&#x0372;&#x0373;&#x0373; &#x0372;&#x0373;&#x0373;</span> <span title="U+0377">&#x0376;&#x0377;&#x0377; &#x0376;&#x0377;&#x0377;</span> <span title="U+037B">&#x03FD;&#x037B;&#x037B; &#x03FD;&#x037B;&#x037B;</span> <span title="U+037C">&#x03FE;&#x037C;&#x037C; &#x03FE;&#x037C;&#x037C;</span> <span title="U+037D">&#x03FF;&#x037D;&#x037D; &#x03FF;&#x037D;&#x037D;</span> <span title="U+03AC">&#x0386;&#x03AC;&#x03AC; &#x0386;&#x03AC;&#x03AC;</span> <span title="U+03AD">&#x0388;&#x03AD;&#x03AD; &#x0388;&#x03AD;&#x03AD;</span> <span title="U+03AE">&#x0389;&#x03AE;&#x03AE; &#x0389;&#x03AE;&#x03AE;</span> <span title="U+03AF">&#x038A;&#x03AF;&#x03AF; &#x038A;&#x03AF;&#x03AF;</span> <span title="U+03B1">&#x0391;&#x03B1;&#x03B1; &#x0391;&#x03B1;&#x03B1;</span> <span title="U+03B2">&#x0392;&#x03B2;&#x03B2; &#x0392;&#x03B2;&#x03B2;</span> <span title="U+03B3">&#x0393;&#x03B3;&#x03B3; &#x0393;&#x03B3;&#x03B3;</span> <span title="U+03B4">&#x0394;&#x03B4;&#x03B4; &#x0394;&#x03B4;&#x03B4;</span> <span title="U+03B5">&#x0395;&#x03B5;&#x03B5; &#x0395;&#x03B5;&#x03B5;</span> <span title="U+03B6">&#x0396;&#x03B6;&#x03B6; &#x0396;&#x03B6;&#x03B6;</span> <span title="U+03B7">&#x0397;&#x03B7;&#x03B7; &#x0397;&#x03B7;&#x03B7;</span> <span title="U+03B8">&#x0398;&#x03B8;&#x03B8; &#x0398;&#x03B8;&#x03B8;</span> <span title="U+03B9">&#x0399;&#x03B9;&#x03B9; &#x0399;&#x03B9;&#x03B9;</span> <span title="U+03BA">&#x039A;&#x03BA;&#x03BA; &#x039A;&#x03BA;&#x03BA;</span> <span title="U+03BB">&#x039B;&#x03BB;&#x03BB; &#x039B;&#x03BB;&#x03BB;</span> <span title="U+03BC">&#x039C;&#x03BC;&#x03BC; &#x039C;&#x03BC;&#x03BC;</span> <span title="U+03BD">&#x039D;&#x03BD;&#x03BD; &#x039D;&#x03BD;&#x03BD;</span> <span title="U+03BE">&#x039E;&#x03BE;&#x03BE; &#x039E;&#x03BE;&#x03BE;</span> <span title="U+03BF">&#x039F;&#x03BF;&#x03BF; &#x039F;&#x03BF;&#x03BF;</span> <span title="U+03C0">&#x03A0;&#x03C0;&#x03C0; &#x03A0;&#x03C0;&#x03C0;</span> <span title="U+03C1">&#x03A1;&#x03C1;&#x03C1; &#x03A1;&#x03C1;&#x03C1;</span> <span title="U+03C2">&#x03A3;&#x03C2;&#x03C2; &#x03A3;&#x03C2;&#x03C2;</span> <span title="U+03C3">&#x03A3;&#x03C3;&#x03C3; &#x03A3;&#x03C3;&#x03C3;</span> <span title="U+03C4">&#x03A4;&#x03C4;&#x03C4; &#x03A4;&#x03C4;&#x03C4;</span> <span title="U+03C5">&#x03A5;&#x03C5;&#x03C5; &#x03A5;&#x03C5;&#x03C5;</span> <span title="U+03C6">&#x03A6;&#x03C6;&#x03C6; &#x03A6;&#x03C6;&#x03C6;</span> <span title="U+03C7">&#x03A7;&#x03C7;&#x03C7; &#x03A7;&#x03C7;&#x03C7;</span> <span title="U+03C8">&#x03A8;&#x03C8;&#x03C8; &#x03A8;&#x03C8;&#x03C8;</span> <span title="U+03C9">&#x03A9;&#x03C9;&#x03C9; &#x03A9;&#x03C9;&#x03C9;</span> <span title="U+03CA">&#x03AA;&#x03CA;&#x03CA; &#x03AA;&#x03CA;&#x03CA;</span> <span title="U+03CB">&#x03AB;&#x03CB;&#x03CB; &#x03AB;&#x03CB;&#x03CB;</span> <span title="U+03CC">&#x038C;&#x03CC;&#x03CC; &#x038C;&#x03CC;&#x03CC;</span> <span title="U+03CD">&#x038E;&#x03CD;&#x03CD; &#x038E;&#x03CD;&#x03CD;</span> <span title="U+03CE">&#x038F;&#x03CE;&#x03CE; &#x038F;&#x03CE;&#x03CE;</span> <span title="U+03D0">&#x0392;&#x03D0;&#x03D0; &#x0392;&#x03D0;&#x03D0;</span> <span title="U+03D1">&#x0398;&#x03D1;&#x03D1; &#x0398;&#x03D1;&#x03D1;</span> <span title="U+03D5">&#x03A6;&#x03D5;&#x03D5; &#x03A6;&#x03D5;&#x03D5;</span> <span title="U+03D6">&#x03A0;&#x03D6;&#x03D6; &#x03A0;&#x03D6;&#x03D6;</span> <span title="U+03D7">&#x03CF;&#x03D7;&#x03D7; &#x03CF;&#x03D7;&#x03D7;</span> <span title="U+03D9">&#x03D8;&#x03D9;&#x03D9; &#x03D8;&#x03D9;&#x03D9;</span> <span title="U+03DB">&#x03DA;&#x03DB;&#x03DB; &#x03DA;&#x03DB;&#x03DB;</span> <span title="U+03DD">&#x03DC;&#x03DD;&#x03DD; &#x03DC;&#x03DD;&#x03DD;</span> <span title="U+03DF">&#x03DE;&#x03DF;&#x03DF; &#x03DE;&#x03DF;&#x03DF;</span> <span title="U+03E1">&#x03E0;&#x03E1;&#x03E1; &#x03E0;&#x03E1;&#x03E1;</span> <span title="U+03E3">&#x03E2;&#x03E3;&#x03E3; &#x03E2;&#x03E3;&#x03E3;</span> <span title="U+03E5">&#x03E4;&#x03E5;&#x03E5; &#x03E4;&#x03E5;&#x03E5;</span> <span title="U+03E7">&#x03E6;&#x03E7;&#x03E7; &#x03E6;&#x03E7;&#x03E7;</span> <span title="U+03E9">&#x03E8;&#x03E9;&#x03E9; &#x03E8;&#x03E9;&#x03E9;</span> <span title="U+03EB">&#x03EA;&#x03EB;&#x03EB; &#x03EA;&#x03EB;&#x03EB;</span> <span title="U+03ED">&#x03EC;&#x03ED;&#x03ED; &#x03EC;&#x03ED;&#x03ED;</span> <span title="U+03EF">&#x03EE;&#x03EF;&#x03EF; &#x03EE;&#x03EF;&#x03EF;</span> <span title="U+03F0">&#x039A;&#x03F0;&#x03F0; &#x039A;&#x03F0;&#x03F0;</span> <span title="U+03F1">&#x03A1;&#x03F1;&#x03F1; &#x03A1;&#x03F1;&#x03F1;</span> <span title="U+03F2">&#x03F9;&#x03F2;&#x03F2; &#x03F9;&#x03F2;&#x03F2;</span> <span title="U+03F3">&#x037F;&#x03F3;&#x03F3; &#x037F;&#x03F3;&#x03F3;</span> <span title="U+03F5">&#x0395;&#x03F5;&#x03F5; &#x0395;&#x03F5;&#x03F5;</span> <span title="U+03F8">&#x03F7;&#x03F8;&#x03F8; &#x03F7;&#x03F8;&#x03F8;</span> <span title="U+03FB">&#x03FA;&#x03FB;&#x03FB; &#x03FA;&#x03FB;&#x03FB;</span></div>
-<!--Notes:
-Tip: To identify the characters where differences occur, in order to report problem characters,either mouse over to reveal a tooltip, or copy and paste the sequence into a tool such as <a href='http://r12a.github.io/uniview/' target='_blank'>UniView</a> or the <a href='http://r12a.github.io/apps/conversion/' target='_blank'>Unicode Conversion Tool</a>.
--->
 </body>
-</html>
\ No newline at end of file
+</html>
--- a/testing/web-platform/tests/css/css-text/text-transform/reference/text-transform-capitalize-016-ref.html
+++ b/testing/web-platform/tests/css/css-text/text-transform/reference/text-transform-capitalize-016-ref.html
@@ -1,10 +1,10 @@
 <!DOCTYPE html>
-<html  lang="en" >
+<html lang="en">
 <head>
 <meta charset="utf-8"/>
 <title>CSS3 Text, text transform: capitalize, Greek Extended</title>
 <link rel='author' title='Richard Ishida' href='mailto:ishida@w3.org'>
 <meta name='flags' content=''>
 <style type='text/css'>
 @font-face {
 	font-family: 'webfont';
@@ -15,13 +15,10 @@
 .test, .ref { font-size: 200%; line-height: 2.5em; font-family: 'Gentium Plus', 'Noto Serif', 'Noto Sans', webfont, sans-serif; }
 .test span, .ref span { margin-right: 1em; white-space: nowrap; }
 </style>
 </head>
 <body>
 <p class="instructions">The text is arranged in pairs of three-letter 'words'. Test passes if the words in each pair match<br>
 If you are missing a font glyph for a character, ignore that pair, but report which characters were ignored.</p>
 <div class="test"><span title="U+1F00">&#x1F08;&#x1F00;&#x1F00; &#x1F08;&#x1F00;&#x1F00;</span> <span title="U+1F01">&#x1F09;&#x1F01;&#x1F01; &#x1F09;&#x1F01;&#x1F01;</span> <span title="U+1F02">&#x1F0A;&#x1F02;&#x1F02; &#x1F0A;&#x1F02;&#x1F02;</span> <span title="U+1F03">&#x1F0B;&#x1F03;&#x1F03; &#x1F0B;&#x1F03;&#x1F03;</span> <span title="U+1F04">&#x1F0C;&#x1F04;&#x1F04; &#x1F0C;&#x1F04;&#x1F04;</span> <span title="U+1F05">&#x1F0D;&#x1F05;&#x1F05; &#x1F0D;&#x1F05;&#x1F05;</span> <span title="U+1F06">&#x1F0E;&#x1F06;&#x1F06; &#x1F0E;&#x1F06;&#x1F06;</span> <span title="U+1F07">&#x1F0F;&#x1F07;&#x1F07; &#x1F0F;&#x1F07;&#x1F07;</span> <span title="U+1F10">&#x1F18;&#x1F10;&#x1F10; &#x1F18;&#x1F10;&#x1F10;</span> <span title="U+1F11">&#x1F19;&#x1F11;&#x1F11; &#x1F19;&#x1F11;&#x1F11;</span> <span title="U+1F12">&#x1F1A;&#x1F12;&#x1F12; &#x1F1A;&#x1F12;&#x1F12;</span> <span title="U+1F13">&#x1F1B;&#x1F13;&#x1F13; &#x1F1B;&#x1F13;&#x1F13;</span> <span title="U+1F14">&#x1F1C;&#x1F14;&#x1F14; &#x1F1C;&#x1F14;&#x1F14;</span> <span title="U+1F15">&#x1F1D;&#x1F15;&#x1F15; &#x1F1D;&#x1F15;&#x1F15;</span> <span title="U+1F20">&#x1F28;&#x1F20;&#x1F20; &#x1F28;&#x1F20;&#x1F20;</span> <span title="U+1F21">&#x1F29;&#x1F21;&#x1F21; &#x1F29;&#x1F21;&#x1F21;</span> <span title="U+1F22">&#x1F2A;&#x1F22;&#x1F22; &#x1F2A;&#x1F22;&#x1F22;</span> <span title="U+1F23">&#x1F2B;&#x1F23;&#x1F23; &#x1F2B;&#x1F23;&#x1F23;</span> <span title="U+1F24">&#x1F2C;&#x1F24;&#x1F24; &#x1F2C;&#x1F24;&#x1F24;</span> <span title="U+1F25">&#x1F2D;&#x1F25;&#x1F25; &#x1F2D;&#x1F25;&#x1F25;</span> <span title="U+1F26">&#x1F2E;&#x1F26;&#x1F26; &#x1F2E;&#x1F26;&#x1F26;</span> <span title="U+1F27">&#x1F2F;&#x1F27;&#x1F27; &#x1F2F;&#x1F27;&#x1F27;</span> <span title="U+1F30">&#x1F38;&#x1F30;&#x1F30; &#x1F38;&#x1F30;&#x1F30;</span> <span title="U+1F31">&#x1F39;&#x1F31;&#x1F31; &#x1F39;&#x1F31;&#x1F31;</span> <span title="U+1F32">&#x1F3A;&#x1F32;&#x1F32; &#x1F3A;&#x1F32;&#x1F32;</span> <span title="U+1F33">&#x1F3B;&#x1F33;&#x1F33; &#x1F3B;&#x1F33;&#x1F33;</span> <span title="U+1F34">&#x1F3C;&#x1F34;&#x1F34; &#x1F3C;&#x1F34;&#x1F34;</span> <span title="U+1F35">&#x1F3D;&#x1F35;&#x1F35; &#x1F3D;&#x1F35;&#x1F35;</span> <span title="U+1F36">&#x1F3E;&#x1F36;&#x1F36; &#x1F3E;&#x1F36;&#x1F36;</span> <span title="U+1F37">&#x1F3F;&#x1F37;&#x1F37; &#x1F3F;&#x1F37;&#x1F37;</span> <span title="U+1F40">&#x1F48;&#x1F40;&#x1F40; &#x1F48;&#x1F40;&#x1F40;</span> <span title="U+1F41">&#x1F49;&#x1F41;&#x1F41; &#x1F49;&#x1F41;&#x1F41;</span> <span title="U+1F42">&#x1F4A;&#x1F42;&#x1F42; &#x1F4A;&#x1F42;&#x1F42;</span> <span title="U+1F43">&#x1F4B;&#x1F43;&#x1F43; &#x1F4B;&#x1F43;&#x1F43;</span> <span title="U+1F44">&#x1F4C;&#x1F44;&#x1F44; &#x1F4C;&#x1F44;&#x1F44;</span> <span title="U+1F45">&#x1F4D;&#x1F45;&#x1F45; &#x1F4D;&#x1F45;&#x1F45;</span> <span title="U+1F51">&#x1F59;&#x1F51;&#x1F51; &#x1F59;&#x1F51;&#x1F51;</span> <span title="U+1F53">&#x1F5B;&#x1F53;&#x1F53; &#x1F5B;&#x1F53;&#x1F53;</span> <span title="U+1F55">&#x1F5D;&#x1F55;&#x1F55; &#x1F5D;&#x1F55;&#x1F55;</span> <span title="U+1F57">&#x1F5F;&#x1F57;&#x1F57; &#x1F5F;&#x1F57;&#x1F57;</span> <span title="U+1F60">&#x1F68;&#x1F60;&#x1F60; &#x1F68;&#x1F60;&#x1F60;</span> <span title="U+1F61">&#x1F69;&#x1F61;&#x1F61; &#x1F69;&#x1F61;&#x1F61;</span> <span title="U+1F62">&#x1F6A;&#x1F62;&#x1F62; &#x1F6A;&#x1F62;&#x1F62;</span> <span title="U+1F63">&#x1F6B;&#x1F63;&#x1F63; &#x1F6B;&#x1F63;&#x1F63;</span> <span title="U+1F64">&#x1F6C;&#x1F64;&#x1F64; &#x1F6C;&#x1F64;&#x1F64;</span> <span title="U+1F65">&#x1F6D;&#x1F65;&#x1F65; &#x1F6D;&#x1F65;&#x1F65;</span> <span title="U+1F66">&#x1F6E;&#x1F66;&#x1F66; &#x1F6E;&#x1F66;&#x1F66;</span> <span title="U+1F67">&#x1F6F;&#x1F67;&#x1F67; &#x1F6F;&#x1F67;&#x1F67;</span> <span title="U+1F70">&#x1FBA;&#x1F70;&#x1F70; &#x1FBA;&#x1F70;&#x1F70;</span> <span title="U+1F71">&#x1FBB;&#x1F71;&#x1F71; &#x1FBB;&#x1F71;&#x1F71;</span> <span title="U+1F72">&#x1FC8;&#x1F72;&#x1F72; &#x1FC8;&#x1F72;&#x1F72;</span> <span title="U+1F73">&#x1FC9;&#x1F73;&#x1F73; &#x1FC9;&#x1F73;&#x1F73;</span> <span title="U+1F74">&#x1FCA;&#x1F74;&#x1F74; &#x1FCA;&#x1F74;&#x1F74;</span> <span title="U+1F75">&#x1FCB;&#x1F75;&#x1F75; &#x1FCB;&#x1F75;&#x1F75;</span> <span title="U+1F76">&#x1FDA;&#x1F76;&#x1F76; &#x1FDA;&#x1F76;&#x1F76;</span> <span title="U+1F77">&#x1FDB;&#x1F77;&#x1F77; &#x1FDB;&#x1F77;&#x1F77;</span> <span title="U+1F78">&#x1FF8;&#x1F78;&#x1F78; &#x1FF8;&#x1F78;&#x1F78;</span> <span title="U+1F79">&#x1FF9;&#x1F79;&#x1F79; &#x1FF9;&#x1F79;&#x1F79;</span> <span title="U+1F7A">&#x1FEA;&#x1F7A;&#x1F7A; &#x1FEA;&#x1F7A;&#x1F7A;</span> <span title="U+1F7B">&#x1FEB;&#x1F7B;&#x1F7B; &#x1FEB;&#x1F7B;&#x1F7B;</span> <span title="U+1F7C">&#x1FFA;&#x1F7C;&#x1F7C; &#x1FFA;&#x1F7C;&#x1F7C;</span> <span title="U+1F7D">&#x1FFB;&#x1F7D;&#x1F7D; &#x1FFB;&#x1F7D;&#x1F7D;</span> <span title="U+1F80">&#x1F88;&#x1F80;&#x1F80; &#x1F88;&#x1F80;&#x1F80;</span> <span title="U+1F81">&#x1F89;&#x1F81;&#x1F81; &#x1F89;&#x1F81;&#x1F81;</span> <span title="U+1F82">&#x1F8A;&#x1F82;&#x1F82; &#x1F8A;&#x1F82;&#x1F82;</span> <span title="U+1F83">&#x1F8B;&#x1F83;&#x1F83; &#x1F8B;&#x1F83;&#x1F83;</span> <span title="U+1F84">&#x1F8C;&#x1F84;&#x1F84; &#x1F8C;&#x1F84;&#x1F84;</span> <span title="U+1F85">&#x1F8D;&#x1F85;&#x1F85; &#x1F8D;&#x1F85;&#x1F85;</span> <span title="U+1F86">&#x1F8E;&#x1F86;&#x1F86; &#x1F8E;&#x1F86;&#x1F86;</span> <span title="U+1F87">&#x1F8F;&#x1F87;&#x1F87; &#x1F8F;&#x1F87;&#x1F87;</span> <span title="U+1F90">&#x1F98;&#x1F90;&#x1F90; &#x1F98;&#x1F90;&#x1F90;</span> <span title="U+1F91">&#x1F99;&#x1F91;&#x1F91; &#x1F99;&#x1F91;&#x1F91;</span> <span title="U+1F92">&#x1F9A;&#x1F92;&#x1F92; &#x1F9A;&#x1F92;&#x1F92;</span> <span title="U+1F93">&#x1F9B;&#x1F93;&#x1F93; &#x1F9B;&#x1F93;&#x1F93;</span> <span title="U+1F94">&#x1F9C;&#x1F94;&#x1F94; &#x1F9C;&#x1F94;&#x1F94;</span> <span title="U+1F95">&#x1F9D;&#x1F95;&#x1F95; &#x1F9D;&#x1F95;&#x1F95;</span> <span title="U+1F96">&#x1F9E;&#x1F96;&#x1F96; &#x1F9E;&#x1F96;&#x1F96;</span> <span title="U+1F97">&#x1F9F;&#x1F97;&#x1F97; &#x1F9F;&#x1F97;&#x1F97;</span> <span title="U+1FA0">&#x1FA8;&#x1FA0;&#x1FA0; &#x1FA8;&#x1FA0;&#x1FA0;</span> <span title="U+1FA1">&#x1FA9;&#x1FA1;&#x1FA1; &#x1FA9;&#x1FA1;&#x1FA1;</span> <span title="U+1FA2">&#x1FAA;&#x1FA2;&#x1FA2; &#x1FAA;&#x1FA2;&#x1FA2;</span> <span title="U+1FA3">&#x1FAB;&#x1FA3;&#x1FA3; &#x1FAB;&#x1FA3;&#x1FA3;</span> <span title="U+1FA4">&#x1FAC;&#x1FA4;&#x1FA4; &#x1FAC;&#x1FA4;&#x1FA4;</span> <span title="U+1FA5">&#x1FAD;&#x1FA5;&#x1FA5; &#x1FAD;&#x1FA5;&#x1FA5;</span> <span title="U+1FA6">&#x1FAE;&#x1FA6;&#x1FA6; &#x1FAE;&#x1FA6;&#x1FA6;</span> <span title="U+1FA7">&#x1FAF;&#x1FA7;&#x1FA7; &#x1FAF;&#x1FA7;&#x1FA7;</span> <span title="U+1FB0">&#x1FB8;&#x1FB0;&#x1FB0; &#x1FB8;&#x1FB0;&#x1FB0;</span> <span title="U+1FB1">&#x1FB9;&#x1FB1;&#x1FB1; &#x1FB9;&#x1FB1;&#x1FB1;</span> <span title="U+1FB3">&#x1FBC;&#x1FB3;&#x1FB3; &#x1FBC;&#x1FB3;&#x1FB3;</span> <span title="U+1FBE">&#x0399;&#x1FBE;&#x1FBE; &#x0399;&#x1FBE;&#x1FBE;</span> <span title="U+1FC3">&#x1FCC;&#x1FC3;&#x1FC3; &#x1FCC;&#x1FC3;&#x1FC3;</span> <span title="U+1FD0">&#x1FD8;&#x1FD0;&#x1FD0; &#x1FD8;&#x1FD0;&#x1FD0;</span> <span title="U+1FD1">&#x1FD9;&#x1FD1;&#x1FD1; &#x1FD9;&#x1FD1;&#x1FD1;</span> <span title="U+1FE0">&#x1FE8;&#x1FE0;&#x1FE0; &#x1FE8;&#x1FE0;&#x1FE0;</span> <span title="U+1FE1">&#x1FE9;&#x1FE1;&#x1FE1; &#x1FE9;&#x1FE1;&#x1FE1;</span> <span title="U+1FE5">&#x1FEC;&#x1FE5;&#x1FE5; &#x1FEC;&#x1FE5;&#x1FE5;</span> <span title="U+1FF3">&#x1FFC;&#x1FF3;&#x1FF3; &#x1FFC;&#x1FF3;&#x1FF3;</span></div>
-<!--Notes:
-Tip: To identify the characters where differences occur, in order to report problem characters,either mouse over to reveal a tooltip, or copy and paste the sequence into a tool such as <a href='http://r12a.github.io/uniview/' target='_blank'>UniView</a> or the <a href='http://r12a.github.io/apps/conversion/' target='_blank'>Unicode Conversion Tool</a>.
--->
 </body>
-</html>
\ No newline at end of file
+</html>
--- a/testing/web-platform/tests/css/css-text/text-transform/reference/text-transform-capitalize-018-ref.html
+++ b/testing/web-platform/tests/css/css-text/text-transform/reference/text-transform-capitalize-018-ref.html
@@ -1,10 +1,10 @@
 <!DOCTYPE html>
-<html  lang="en" >
+<html lang="en">
 <head>
 <meta charset="utf-8"/>
 <title>CSS3 Text, text transform: capitalize, Cyrillic</title>
 <link rel='author' title='Richard Ishida' href='mailto:ishida@w3.org'>
 <meta name='flags' content=''>
 <style type='text/css'>
 @font-face {
 	font-family: 'webfont';
@@ -16,13 +16,10 @@
 .test, .ref { font-size: 200%; line-height: 2.5em; font-family: 'Doulos SIL', 'Noto Serif', 'Noto Sans', webfont, sans-serif; }
 .test span, .ref span { margin-right: 1em; white-space: nowrap; }
 </style>
 </head>
 <body>
 <p class="instructions">The text is arranged in pairs of three-letter 'words'. Test passes if the words in each pair match<br>
 If you are missing a font glyph for a character, ignore that pair, but report which characters were ignored.</p>
 <div class="test"><span title="U+0430">&#x0410;&#x0430;&#x0430; &#x0410;&#x0430;&#x0430;</span> <span title="U+0431">&#x0411;&#x0431;&#x0431; &#x0411;&#x0431;&#x0431;</span> <span title="U+0432">&#x0412;&#x0432;&#x0432; &#x0412;&#x0432;&#x0432;</span> <span title="U+0433">&#x0413;&#x0433;&#x0433; &#x0413;&#x0433;&#x0433;</span> <span title="U+0434">&#x0414;&#x0434;&#x0434; &#x0414;&#x0434;&#x0434;</span> <span title="U+0435">&#x0415;&#x0435;&#x0435; &#x0415;&#x0435;&#x0435;</span> <span title="U+0436">&#x0416;&#x0436;&#x0436; &#x0416;&#x0436;&#x0436;</span> <span title="U+0437">&#x0417;&#x0437;&#x0437; &#x0417;&#x0437;&#x0437;</span> <span title="U+0438">&#x0418;&#x0438;&#x0438; &#x0418;&#x0438;&#x0438;</span> <span title="U+0439">&#x0419;&#x0439;&#x0439; &#x0419;&#x0439;&#x0439;</span> <span title="U+043A">&#x041A;&#x043A;&#x043A; &#x041A;&#x043A;&#x043A;</span> <span title="U+043B">&#x041B;&#x043B;&#x043B; &#x041B;&#x043B;&#x043B;</span> <span title="U+043C">&#x041C;&#x043C;&#x043C; &#x041C;&#x043C;&#x043C;</span> <span title="U+043D">&#x041D;&#x043D;&#x043D; &#x041D;&#x043D;&#x043D;</span> <span title="U+043E">&#x041E;&#x043E;&#x043E; &#x041E;&#x043E;&#x043E;</span> <span title="U+043F">&#x041F;&#x043F;&#x043F; &#x041F;&#x043F;&#x043F;</span> <span title="U+0440">&#x0420;&#x0440;&#x0440; &#x0420;&#x0440;&#x0440;</span> <span title="U+0441">&#x0421;&#x0441;&#x0441; &#x0421;&#x0441;&#x0441;</span> <span title="U+0442">&#x0422;&#x0442;&#x0442; &#x0422;&#x0442;&#x0442;</span> <span title="U+0443">&#x0423;&#x0443;&#x0443; &#x0423;&#x0443;&#x0443;</span> <span title="U+0444">&#x0424;&#x0444;&#x0444; &#x0424;&#x0444;&#x0444;</span> <span title="U+0445">&#x0425;&#x0445;&#x0445; &#x0425;&#x0445;&#x0445;</span> <span title="U+0446">&#x0426;&#x0446;&#x0446; &#x0426;&#x0446;&#x0446;</span> <span title="U+0447">&#x0427;&#x0447;&#x0447; &#x0427;&#x0447;&#x0447;</span> <span title="U+0448">&#x0428;&#x0448;&#x0448; &#x0428;&#x0448;&#x0448;</span> <span title="U+0449">&#x0429;&#x0449;&#x0449; &#x0429;&#x0449;&#x0449;</span> <span title="U+044A">&#x042A;&#x044A;&#x044A; &#x042A;&#x044A;&#x044A;</span> <span title="U+044B">&#x042B;&#x044B;&#x044B; &#x042B;&#x044B;&#x044B;</span> <span title="U+044C">&#x042C;&#x044C;&#x044C; &#x042C;&#x044C;&#x044C;</span> <span title="U+044D">&#x042D;&#x044D;&#x044D; &#x042D;&#x044D;&#x044D;</span> <span title="U+044E">&#x042E;&#x044E;&#x044E; &#x042E;&#x044E;&#x044E;</span> <span title="U+044F">&#x042F;&#x044F;&#x044F; &#x042F;&#x044F;&#x044F;</span> <span title="U+0450">&#x0400;&#x0450;&#x0450; &#x0400;&#x0450;&#x0450;</span> <span title="U+0451">&#x0401;&#x0451;&#x0451; &#x0401;&#x0451;&#x0451;</span> <span title="U+0452">&#x0402;&#x0452;&#x0452; &#x0402;&#x0452;&#x0452;</span> <span title="U+0453">&#x0403;&#x0453;&#x0453; &#x0403;&#x0453;&#x0453;</span> <span title="U+0454">&#x0404;&#x0454;&#x0454; &#x0404;&#x0454;&#x0454;</span> <span title="U+0455">&#x0405;&#x0455;&#x0455; &#x0405;&#x0455;&#x0455;</span> <span title="U+0456">&#x0406;&#x0456;&#x0456; &#x0406;&#x0456;&#x0456;</span> <span title="U+0457">&#x0407;&#x0457;&#x0457; &#x0407;&#x0457;&#x0457;</span> <span title="U+0458">&#x0408;&#x0458;&#x0458; &#x0408;&#x0458;&#x0458;</span> <span title="U+0459">&#x0409;&#x0459;&#x0459; &#x0409;&#x0459;&#x0459;</span> <span title="U+045A">&#x040A;&#x045A;&#x045A; &#x040A;&#x045A;&#x045A;</span> <span title="U+045B">&#x040B;&#x045B;&#x045B; &#x040B;&#x045B;&#x045B;</span> <span title="U+045C">&#x040C;&#x045C;&#x045C; &#x040C;&#x045C;&#x045C;</span> <span title="U+045D">&#x040D;&#x045D;&#x045D; &#x040D;&#x045D;&#x045D;</span> <span title="U+045E">&#x040E;&#x045E;&#x045E; &#x040E;&#x045E;&#x045E;</span> <span title="U+045F">&#x040F;&#x045F;&#x045F; &#x040F;&#x045F;&#x045F;</span> <span title="U+0461">&#x0460;&#x0461;&#x0461; &#x0460;&#x0461;&#x0461;</span> <span title="U+0463">&#x0462;&#x0463;&#x0463; &#x0462;&#x0463;&#x0463;</span> <span title="U+0465">&#x0464;&#x0465;&#x0465; &#x0464;&#x0465;&#x0465;</span> <span title="U+0467">&#x0466;&#x0467;&#x0467; &#x0466;&#x0467;&#x0467;</span> <span title="U+0469">&#x0468;&#x0469;&#x0469; &#x0468;&#x0469;&#x0469;</span> <span title="U+046B">&#x046A;&#x046B;&#x046B; &#x046A;&#x046B;&#x046B;</span> <span title="U+046D">&#x046C;&#x046D;&#x046D; &#x046C;&#x046D;&#x046D;</span> <span title="U+046F">&#x046E;&#x046F;&#x046F; &#x046E;&#x046F;&#x046F;</span> <span title="U+0471">&#x0470;&#x0471;&#x0471; &#x0470;&#x0471;&#x0471;</span> <span title="U+0473">&#x0472;&#x0473;&#x0473; &#x0472;&#x0473;&#x0473;</span> <span title="U+0475">&#x0474;&#x0475;&#x0475; &#x0474;&#x0475;&#x0475;</span> <span title="U+0477">&#x0476;&#x0477;&#x0477; &#x0476;&#x0477;&#x0477;</span> <span title="U+0479">&#x0478;&#x0479;&#x0479; &#x0478;&#x0479;&#x0479;</span> <span title="U+047B">&#x047A;&#x047B;&#x047B; &#x047A;&#x047B;&#x047B;</span> <span title="U+047D">&#x047C;&#x047D;&#x047D; &#x047C;&#x047D;&#x047D;</span> <span title="U+047F">&#x047E;&#x047F;&#x047F; &#x047E;&#x047F;&#x047F;</span> <span title="U+0481">&#x0480;&#x0481;&#x0481; &#x0480;&#x0481;&#x0481;</span> <span title="U+048B">&#x048A;&#x048B;&#x048B; &#x048A;&#x048B;&#x048B;</span> <span title="U+048D">&#x048C;&#x048D;&#x048D; &#x048C;&#x048D;&#x048D;</span> <span title="U+048F">&#x048E;&#x048F;&#x048F; &#x048E;&#x048F;&#x048F;</span> <span title="U+0491">&#x0490;&#x0491;&#x0491; &#x0490;&#x0491;&#x0491;</span> <span title="U+0493">&#x0492;&#x0493;&#x0493; &#x0492;&#x0493;&#x0493;</span> <span title="U+0495">&#x0494;&#x0495;&#x0495; &#x0494;&#x0495;&#x0495;</span> <span title="U+0497">&#x0496;&#x0497;&#x0497; &#x0496;&#x0497;&#x0497;</span> <span title="U+0499">&#x0498;&#x0499;&#x0499; &#x0498;&#x0499;&#x0499;</span> <span title="U+049B">&#x049A;&#x049B;&#x049B; &#x049A;&#x049B;&#x049B;</span> <span title="U+049D">&#x049C;&#x049D;&#x049D; &#x049C;&#x049D;&#x049D;</span> <span title="U+049F">&#x049E;&#x049F;&#x049F; &#x049E;&#x049F;&#x049F;</span> <span title="U+04A1">&#x04A0;&#x04A1;&#x04A1; &#x04A0;&#x04A1;&#x04A1;</span> <span title="U+04A3">&#x04A2;&#x04A3;&#x04A3; &#x04A2;&#x04A3;&#x04A3;</span> <span title="U+04A5">&#x04A4;&#x04A5;&#x04A5; &#x04A4;&#x04A5;&#x04A5;</span> <span title="U+04A7">&#x04A6;&#x04A7;&#x04A7; &#x04A6;&#x04A7;&#x04A7;</span> <span title="U+04A9">&#x04A8;&#x04A9;&#x04A9; &#x04A8;&#x04A9;&#x04A9;</span> <span title="U+04AB">&#x04AA;&#x04AB;&#x04AB; &#x04AA;&#x04AB;&#x04AB;</span> <span title="U+04AD">&#x04AC;&#x04AD;&#x04AD; &#x04AC;&#x04AD;&#x04AD;</span> <span title="U+04AF">&#x04AE;&#x04AF;&#x04AF; &#x04AE;&#x04AF;&#x04AF;</span> <span title="U+04B1">&#x04B0;&#x04B1;&#x04B1; &#x04B0;&#x04B1;&#x04B1;</span> <span title="U+04B3">&#x04B2;&#x04B3;&#x04B3; &#x04B2;&#x04B3;&#x04B3;</span> <span title="U+04B5">&#x04B4;&#x04B5;&#x04B5; &#x04B4;&#x04B5;&#x04B5;</span> <span title="U+04B7">&#x04B6;&#x04B7;&#x04B7; &#x04B6;&#x04B7;&#x04B7;</span> <span title="U+04B9">&#x04B8;&#x04B9;&#x04B9; &#x04B8;&#x04B9;&#x04B9;</span> <span title="U+04BB">&#x04BA;&#x04BB;&#x04BB; &#x04BA;&#x04BB;&#x04BB;</span> <span title="U+04BD">&#x04BC;&#x04BD;&#x04BD; &#x04BC;&#x04BD;&#x04BD;</span> <span title="U+04BF">&#x04BE;&#x04BF;&#x04BF; &#x04BE;&#x04BF;&#x04BF;</span> <span title="U+04C2">&#x04C1;&#x04C2;&#x04C2; &#x04C1;&#x04C2;&#x04C2;</span> <span title="U+04C4">&#x04C3;&#x04C4;&#x04C4; &#x04C3;&#x04C4;&#x04C4;</span> <span title="U+04C6">&#x04C5;&#x04C6;&#x04C6; &#x04C5;&#x04C6;&#x04C6;</span> <span title="U+04C8">&#x04C7;&#x04C8;&#x04C8; &#x04C7;&#x04C8;&#x04C8;</span> <span title="U+04CA">&#x04C9;&#x04CA;&#x04CA; &#x04C9;&#x04CA;&#x04CA;</span> <span title="U+04CC">&#x04CB;&#x04CC;&#x04CC; &#x04CB;&#x04CC;&#x04CC;</span> <span title="U+04CE">&#x04CD;&#x04CE;&#x04CE; &#x04CD;&#x04CE;&#x04CE;</span> <span title="U+04CF">&#x04C0;&#x04CF;&#x04CF; &#x04C0;&#x04CF;&#x04CF;</span> <span title="U+04D1">&#x04D0;&#x04D1;&#x04D1; &#x04D0;&#x04D1;&#x04D1;</span> <span title="U+04D3">&#x04D2;&#x04D3;&#x04D3; &#x04D2;&#x04D3;&#x04D3;</span> <span title="U+04D5">&#x04D4;&#x04D5;&#x04D5; &#x04D4;&#x04D5;&#x04D5;</span> <span title="U+04D7">&#x04D6;&#x04D7;&#x04D7; &#x04D6;&#x04D7;&#x04D7;</span> <span title="U+04D9">&#x04D8;&#x04D9;&#x04D9; &#x04D8;&#x04D9;&#x04D9;</span> <span title="U+04DB">&#x04DA;&#x04DB;&#x04DB; &#x04DA;&#x04DB;&#x04DB;</span> <span title="U+04DD">&#x04DC;&#x04DD;&#x04DD; &#x04DC;&#x04DD;&#x04DD;</span> <span title="U+04DF">&#x04DE;&#x04DF;&#x04DF; &#x04DE;&#x04DF;&#x04DF;</span> <span title="U+04E1">&#x04E0;&#x04E1;&#x04E1; &#x04E0;&#x04E1;&#x04E1;</span> <span title="U+04E3">&#x04E2;&#x04E3;&#x04E3; &#x04E2;&#x04E3;&#x04E3;</span> <span title="U+04E5">&#x04E4;&#x04E5;&#x04E5; &#x04E4;&#x04E5;&#x04E5;</span> <span title="U+04E7">&#x04E6;&#x04E7;&#x04E7; &#x04E6;&#x04E7;&#x04E7;</span> <span title="U+04E9">&#x04E8;&#x04E9;&#x04E9; &#x04E8;&#x04E9;&#x04E9;</span> <span title="U+04EB">&#x04EA;&#x04EB;&#x04EB; &#x04EA;&#x04EB;&#x04EB;</span> <span title="U+04ED">&#x04EC;&#x04ED;&#x04ED; &#x04EC;&#x04ED;&#x04ED;</span> <span title="U+04EF">&#x04EE;&#x04EF;&#x04EF; &#x04EE;&#x04EF;&#x04EF;</span> <span title="U+04F1">&#x04F0;&#x04F1;&#x04F1; &#x04F0;&#x04F1;&#x04F1;</span> <span title="U+04F3">&#x04F2;&#x04F3;&#x04F3; &#x04F2;&#x04F3;&#x04F3;</span> <span title="U+04F5">&#x04F4;&#x04F5;&#x04F5; &#x04F4;&#x04F5;&#x04F5;</span> <span title="U+04F7">&#x04F6;&#x04F7;&#x04F7; &#x04F6;&#x04F7;&#x04F7;</span> <span title="U+04F9">&#x04F8;&#x04F9;&#x04F9; &#x04F8;&#x04F9;&#x04F9;</span> <span title="U+04FB">&#x04FA;&#x04FB;&#x04FB; &#x04FA;&#x04FB;&#x04FB;</span> <span title="U+04FD">&#x04FC;&#x04FD;&#x04FD; &#x04FC;&#x04FD;&#x04FD;</span> <span title="U+04FF">&#x04FE;&#x04FF;&#x04FF; &#x04FE;&#x04FF;&#x04FF;</span></div>
-<!--Notes:
-Tip: To identify the characters where differences occur, in order to report problem characters,either mouse over to reveal a tooltip, or copy and paste the sequence into a tool such as <a href='http://r12a.github.io/uniview/' target='_blank'>UniView</a> or the <a href='http://r12a.github.io/apps/conversion/' target='_blank'>Unicode Convers