Merge autoland to mozilla-central. a=merge
authorCsoregi Natalia <ncsoregi@mozilla.com>
Tue, 15 Oct 2019 00:30:01 +0300
changeset 497444 a212b426f6651c50464a0781103000821738e43c
parent 497373 439341f3e28c1a0782ac0d59cd1f44cb9741de57 (current diff)
parent 497443 b4c5c8d78d5f9bfbbaacc34929ff6bece064cc44 (diff)
child 497472 e8606a6a0c25a3c355934caaa4afe56eb521368e
push id36689
push userncsoregi@mozilla.com
push dateMon, 14 Oct 2019 21:30:51 +0000
treeherdermozilla-central@a212b426f665 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone71.0a1
first release with
nightly linux32
a212b426f665 / 71.0a1 / 20191014213051 / files
nightly linux64
a212b426f665 / 71.0a1 / 20191014213051 / files
nightly mac
a212b426f665 / 71.0a1 / 20191014213051 / files
nightly win32
a212b426f665 / 71.0a1 / 20191014213051 / files
nightly win64
a212b426f665 / 71.0a1 / 20191014213051 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge autoland to mozilla-central. a=merge
dom/bindings/test/test_queryInterface.html
dom/imptests/README
dom/imptests/WebIDLParser.js
dom/imptests/editing/mochitest.ini
dom/imptests/failures/html/dom/nodes/mochitest.ini
dom/imptests/failures/html/dom/test_historical.html.json
dom/imptests/failures/html/html/browsers/the-window-object/test_window-named-properties.html.json
dom/imptests/failures/html/html/browsers/the-window-object/test_window-properties.html.json
dom/imptests/failures/html/html/dom/documents/dta/mochitest.ini
dom/imptests/failures/html/html/dom/documents/dta/test_nameditem-06.html.json
dom/imptests/failures/html/typedarrays/test_constructors.html.json
dom/imptests/html.txt
dom/imptests/html/dom/common.js
dom/imptests/html/dom/test_interface-objects.html
dom/imptests/html/dom/test_interfaces.html
dom/imptests/html/html/browsers/the-window-object/test_window-named-properties.html
dom/imptests/html/html/dom/documents/dta/test_nameditem-06.html
dom/imptests/html/html/dom/elements/global-attributes/reftest.list
dom/imptests/html/js/builtins/test_WeakMap.prototype-properties.html
dom/imptests/html/mochitest.ini
dom/imptests/html/webgl/test_texImage2D.html
dom/imptests/html/webgl/test_texSubImage2D.html
dom/imptests/idlharness.js
dom/imptests/importTestsuite.py
dom/imptests/moz.build
dom/imptests/parseFailures.py
dom/imptests/parseManifest.py
dom/imptests/updateTestharness.py
dom/imptests/webapps.txt
dom/imptests/webapps/XMLHttpRequest/tests/submissions/Ms2ger/test_interfaces.html
dom/imptests/webapps/mochitest.ini
dom/imptests/writeBuildFiles.py
dom/webidl/LegacyQueryInterface.webidl
dom/xbl/test/file_bug950909.html
dom/xbl/test/file_bug950909.xml
dom/xbl/test/test_bug950909.xul
dom/xul/crashtests/429085-1.xhtml
media/audioipc/disable-rt.patch
third_party/rust/cubeb-sys/libcubeb/src/cubeb_panner.cpp
third_party/rust/cubeb-sys/libcubeb/src/cubeb_panner.h
third_party/rust/cubeb-sys/src/panner.rs
third_party/rust/scoped-tls-0.1.0/.cargo-checksum.json
third_party/rust/scoped-tls-0.1.0/Cargo.toml
third_party/rust/scoped-tls-0.1.0/LICENSE-APACHE
third_party/rust/scoped-tls-0.1.0/LICENSE-MIT
third_party/rust/scoped-tls-0.1.0/README.md
third_party/rust/scoped-tls-0.1.0/appveyor.yml
third_party/rust/scoped-tls-0.1.0/src/lib.rs
third_party/rust/slab-0.3.0/.cargo-checksum.json
third_party/rust/slab-0.3.0/Cargo.toml
third_party/rust/slab-0.3.0/README.md
third_party/rust/slab-0.3.0/src/lib.rs
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -69,89 +69,89 @@ source = "registry+https://github.com/ru
 dependencies = [
  "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)",
  "termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "winapi 0.3.6 (git+https://github.com/froydnj/winapi-rs?branch=aarch64)",
 ]
 
 [[package]]
 name = "audio_thread_priority"
-version = "0.19.1"
+version = "0.20.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "dbus 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)",
  "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "mach 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "winapi 0.3.6 (git+https://github.com/froydnj/winapi-rs?branch=aarch64)",
 ]
 
 [[package]]
 name = "audioipc"
 version = "0.2.4"
 dependencies = [
+ "audio_thread_priority 0.20.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "bincode 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "bytes 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)",
  "cc 1.0.34 (registry+https://github.com/rust-lang/crates.io-index)",
- "cubeb 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cubeb 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "futures 0.1.23 (registry+https://github.com/rust-lang/crates.io-index)",
  "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "memmap 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "mio 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)",
  "mio-named-pipes 0.1.6 (git+https://github.com/alexcrichton/mio-named-pipes)",
  "mio-uds 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)",
- "scoped-tls 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "miow 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde 1.0.88 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde_derive 1.0.88 (registry+https://github.com/rust-lang/crates.io-index)",
  "tokio 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
  "tokio-io 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
  "tokio-named-pipes 0.2.0 (git+https://github.com/NikVolf/tokio-named-pipes?branch=stable)",
  "tokio-reactor 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "winapi 0.3.6 (git+https://github.com/froydnj/winapi-rs?branch=aarch64)",
 ]
 
 [[package]]
 name = "audioipc-client"
 version = "0.4.0"
 dependencies = [
- "audio_thread_priority 0.19.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "audio_thread_priority 0.20.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "audioipc 0.2.4",
- "cubeb-backend 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cubeb-backend 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "futures 0.1.23 (registry+https://github.com/rust-lang/crates.io-index)",
  "futures-cpupool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
- "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "tokio 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "audioipc-server"
 version = "0.2.3"
 dependencies = [
- "audio_thread_priority 0.19.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "audio_thread_priority 0.20.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "audioipc 0.2.4",
- "cubeb-core 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cubeb-core 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "futures 0.1.23 (registry+https://github.com/rust-lang/crates.io-index)",
  "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
- "slab 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "slab 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "tokio 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "authenticator"
 version = "0.2.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
- "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
+ "bitflags 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "boxfnonce 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "core-foundation 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "devd-rs 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)",
  "libudev 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "runloop 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -239,17 +239,17 @@ dependencies = [
  "serde 1.0.88 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "bindgen"
 version = "0.51.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
- "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
+ "bitflags 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "cexpr 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "clang-sys 0.28.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "peeking_take_while 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "proc-macro2 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "regex 1.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -275,17 +275,17 @@ source = "registry+https://github.com/ru
 
 [[package]]
 name = "bit_reverse"
 version = "0.1.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
 name = "bitflags"
-version = "1.0.4"
+version = "1.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
 name = "bitreader"
 version = "0.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
@@ -465,30 +465,30 @@ dependencies = [
 
 [[package]]
 name = "clap"
 version = "2.31.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
- "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
+ "bitflags 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "term_size 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "textwrap 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "unicode-width 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
  "vec_map 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "cloudabi"
 version = "0.0.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
- "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
+ "bitflags 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "cmake"
 version = "0.1.29"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "cc 1.0.34 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -531,17 +531,17 @@ name = "core-foundation-sys"
 version = "0.6.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
 name = "core-graphics"
 version = "0.17.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
- "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
+ "bitflags 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "core-foundation 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "foreign-types 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "core-text"
 version = "13.0.0"
@@ -744,67 +744,67 @@ version = "0.1.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "procedural-masquerade 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "cubeb"
-version = "0.5.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "cubeb-core 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)",
+version = "0.6.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "cubeb-core 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "cubeb-backend"
-version = "0.5.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "cubeb-core 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)",
+version = "0.6.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "cubeb-core 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "cubeb-core"
-version = "0.5.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
- "cubeb-sys 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)",
+version = "0.6.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "bitflags 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cubeb-sys 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "cubeb-coreaudio"
 version = "0.1.0"
 dependencies = [
  "atomic 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
- "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
+ "bitflags 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "core-foundation-sys 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "coreaudio-sys-utils 0.1.0",
- "cubeb-backend 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cubeb-backend 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)",
  "mach 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "cubeb-pulse"
-version = "0.2.0"
-dependencies = [
- "cubeb-backend 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)",
- "pulse 0.2.0",
+version = "0.3.0"
+dependencies = [
+ "cubeb-backend 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "pulse 0.3.0",
  "pulse-ffi 0.1.0",
  "ringbuf 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
- "semver 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "semver 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "cubeb-sys"
-version = "0.5.5"
+version = "0.6.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "cmake 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)",
  "pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "darling"
@@ -1094,17 +1094,17 @@ name = "fuchsia-cprng"
 version = "0.1.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
 name = "fuchsia-zircon"
 version = "0.3.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
- "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
+ "bitflags 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "fuchsia-zircon-sys"
 version = "0.3.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
@@ -1214,27 +1214,27 @@ dependencies = [
  "xpcom-gtest 0.1.0",
 ]
 
 [[package]]
 name = "gkrust-shared"
 version = "0.1.0"
 dependencies = [
  "arrayvec 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)",
- "audio_thread_priority 0.19.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "audio_thread_priority 0.20.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "audioipc-client 0.4.0",
  "audioipc-server 0.2.3",
  "authenticator 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "bitsdownload 0.1.0",
  "bookmark_sync 0.1.0",
  "cert_storage 0.0.1",
  "cose-c 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "cubeb-coreaudio 0.1.0",
- "cubeb-pulse 0.2.0",
- "cubeb-sys 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cubeb-pulse 0.3.0",
+ "cubeb-sys 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "encoding_glue 0.1.0",
  "env_logger 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "geckoservo 0.0.1",
  "gkrust_utils 0.1.0",
  "jsrust_shared 0.1.0",
  "kvstore 0.1.0",
  "lmdb-rkv-sys 0.9.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1331,17 +1331,17 @@ dependencies = [
 ]
 
 [[package]]
 name = "headers"
 version = "0.2.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
+ "bitflags 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "bytes 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)",
  "headers-core 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "headers-derive 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "http 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)",
  "mime 0.3.13 (registry+https://github.com/rust-lang/crates.io-index)",
  "sha-1 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
@@ -1617,17 +1617,17 @@ name = "linked-hash-map"
 version = "0.5.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
 name = "lmdb-rkv"
 version = "0.12.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
- "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
+ "bitflags 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)",
  "lmdb-rkv-sys 0.9.5 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "lmdb-rkv-sys"
 version = "0.9.5"
@@ -2012,17 +2012,17 @@ version = "0.1.0"
 dependencies = [
  "nsstring 0.1.0",
 ]
 
 [[package]]
 name = "nsstring"
 version = "0.1.0"
 dependencies = [
- "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
+ "bitflags 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "encoding_rs 0.8.20 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "nsstring-gtest"
 version = "0.1.0"
 dependencies = [
  "nsstring 0.1.0",
@@ -2238,17 +2238,17 @@ dependencies = [
  "xml-rs 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "png"
 version = "0.15.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
- "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
+ "bitflags 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "crc32fast 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "deflate 0.7.19 (registry+https://github.com/rust-lang/crates.io-index)",
  "inflate 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "podio"
 version = "0.1.5"
@@ -2293,19 +2293,19 @@ dependencies = [
  "object 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "rustc-demangle 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
  "thin-vec 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "uuid 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "pulse"
-version = "0.2.0"
-dependencies = [
- "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
+version = "0.3.0"
+dependencies = [
+ "bitflags 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "pulse-ffi 0.1.0",
 ]
 
 [[package]]
 name = "pulse-ffi"
 version = "0.1.0"
 dependencies = [
  "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -2517,17 +2517,17 @@ dependencies = [
 
 [[package]]
 name = "rkv"
 version = "0.10.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "arrayref 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "bincode 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
+ "bitflags 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "failure 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "lmdb-rkv 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "ordered-float 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde 1.0.88 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde_derive 1.0.88 (registry+https://github.com/rust-lang/crates.io-index)",
  "url 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -2621,21 +2621,16 @@ name = "same-file"
 version = "1.0.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "winapi 0.3.6 (git+https://github.com/froydnj/winapi-rs?branch=aarch64)",
 ]
 
 [[package]]
 name = "scoped-tls"
-version = "0.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-
-[[package]]
-name = "scoped-tls"
 version = "1.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
 name = "scopeguard"
 version = "1.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
@@ -2657,17 +2652,17 @@ dependencies = [
  "quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)",
  "syn 0.15.30 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "selectors"
 version = "0.21.0"
 dependencies = [
- "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
+ "bitflags 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "cssparser 0.25.9 (registry+https://github.com/rust-lang/crates.io-index)",
  "derive_more 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "fxhash 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "matches 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "phf 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)",
  "phf_codegen 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)",
  "precomputed-hash 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -2683,16 +2678,24 @@ name = "semver"
 version = "0.6.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "semver"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "semver"
 version = "0.9.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "semver-parser"
@@ -2826,21 +2829,16 @@ version = "0.2.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
 name = "size_of_test"
 version = "0.0.1"
 
 [[package]]
 name = "slab"
-version = "0.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-
-[[package]]
-name = "slab"
 version = "0.4.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
 name = "smallbitvec"
 version = "2.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
@@ -2906,17 +2904,17 @@ source = "registry+https://github.com/ru
 [[package]]
 name = "style"
 version = "0.0.1"
 dependencies = [
  "app_units 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "arrayvec 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)",
  "atomic_refcell 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "bindgen 0.51.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
+ "bitflags 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "cssparser 0.25.9 (registry+https://github.com/rust-lang/crates.io-index)",
  "derive_more 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "euclid 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "fallible 0.0.1",
  "fxhash 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "hashglobe 0.1.0",
  "indexmap 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -2970,17 +2968,17 @@ dependencies = [
  "synstructure 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "style_traits"
 version = "0.0.1"
 dependencies = [
  "app_units 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
+ "bitflags 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "cssparser 0.25.9 (registry+https://github.com/rust-lang/crates.io-index)",
  "euclid 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "malloc_size_of 0.0.1",
  "malloc_size_of_derive 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "selectors 0.21.0",
  "servo_arc 0.1.1",
  "to_shmem 0.0.1",
@@ -3533,17 +3531,17 @@ dependencies = [
 ]
 
 [[package]]
 name = "webrender"
 version = "0.60.0"
 dependencies = [
  "base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "bincode 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
+ "bitflags 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "core-foundation 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "core-graphics 0.17.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "core-text 13.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "cstr 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "dwrote 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "euclid 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -3572,17 +3570,17 @@ dependencies = [
  "ws 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "webrender_api"
 version = "0.60.0"
 dependencies = [
  "app_units 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
+ "bitflags 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "core-foundation 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "core-graphics 0.17.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "derive_more 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "euclid 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "malloc_size_of_derive 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "peek-poke 0.2.0",
  "serde 1.0.88 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -3808,29 +3806,29 @@ dependencies = [
 "checksum aho-corasick 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)" = "58fb5e95d83b38284460a5fda7d6470aa0b8844d283a0b614b8535e880800d2d"
 "checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
 "checksum app_units 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9dadc668390b373e73e4abbfc1f07238b09a25858f2f39c06cebc6d8e141d774"
 "checksum arrayref 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "0d382e583f07208808f6b1249e60848879ba3543f57c32277bf52d69c2f0f0ee"
 "checksum arrayvec 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)" = "b8d73f9beda665eaa98ab9e4f7442bd4e7de6652587de55b2525e52e29c1b0ba"
 "checksum atomic 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "c210c1f4db048cda477b652d170572d84c9640695835f17663595d3bd543fc28"
 "checksum atomic_refcell 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fb2dcb6e6d35f20276943cc04bb98e538b348d525a04ac79c10021561d202f21"
 "checksum atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "9a7d5b8723950951411ee34d271d99dddcc2035a16ab25310ea2c8cfd4369652"
-"checksum audio_thread_priority 0.19.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4c1e4aab7f57d8334168073cd0d0f11c7d1f7f3aabef84a1733a42629d0da80c"
+"checksum audio_thread_priority 0.20.2 (registry+https://github.com/rust-lang/crates.io-index)" = "197b2d259505d11c92d266e1784f01cc935eb764d2f54e16aedf4e5085197871"
 "checksum authenticator 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "ec149e5d5d4caa2c9ead53a8ce1ea9c4204c388c65bf3b96c2d1dc0fcf4aeb66"
 "checksum autocfg 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "b671c8fb71b457dd4ae18c4ba1e59aa81793daacc361d82fcd410cef0d491875"
 "checksum backtrace 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "89a47830402e9981c5c41223151efcced65a0510c13097c769cede7efb34782a"
 "checksum backtrace-sys 0.1.24 (registry+https://github.com/rust-lang/crates.io-index)" = "c66d56ac8dabd07f6aacdaf633f4b8262f5b3601a810a0dcddffd5c22c69daa0"
 "checksum base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0b25d992356d2eb0ed82172f5248873db5560c4721f564b13cb5193bda5e668e"
 "checksum binary-space-partition 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "88ceb0d16c4fd0e42876e298d7d3ce3780dd9ebdcbe4199816a32c77e08597ff"
 "checksum bincode 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bda13183df33055cbb84b847becce220d392df502ebe7a4a78d7021771ed94d0"
 "checksum bindgen 0.51.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ebd71393f1ec0509b553aa012b9b58e81dadbdff7130bd3b8cba576e69b32f75"
 "checksum binjs_meta 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6c9a0da2208ceb785c1626fa8b7d250d2e5546ae230294b4a998e4f818c1768e"
 "checksum bit-vec 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f59bbe95d4e52a6398ec21238d31577f2b28a9d86807f06ca59d191d8440d0bb"
 "checksum bit_reverse 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "5e97e02db5a2899c0377f3d6031d5da8296ca2b47abef6ed699de51b9e40a28c"
-"checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12"
+"checksum bitflags 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8a606a02debe2813760609f57a64a2ffd27d9fdf5b2f133eaca0b248dd92cdd2"
 "checksum bitreader 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "80b13e2ab064ff3aa0bdbf1eff533f9822dc37899821f5f98c67f263eab51707"
 "checksum blake2b_simd 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)" = "5850aeee1552f495dd0250014cf64b82b7c8879a89d83b33bbdace2cc4f63182"
 "checksum block-buffer 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b"
 "checksum block-padding 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4fc4358306e344bf9775d0197fd00d2603e5afb0771bb353538630f022068ea3"
 "checksum boxfnonce 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8380105befe91099e6f69206164072c05bc92427ff6aa8a5171388317346dd75"
 "checksum byte-tools 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "980479e6fde23246dfb54d47580d66b4e99202e7579c5eaa9fe10ecb5ebd2182"
 "checksum byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a019b10a2a7cdeb292db131fc8113e57ea2a908f6e7894b0c3c671893b65dbeb"
 "checksum bytes 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)" = "e178b8e0e239e844b083d5a0d4a156b2654e67f9f80144d48398fcd736a24fb8"
@@ -3862,20 +3860,20 @@ dependencies = [
 "checksum crossbeam-deque 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b18cd2e169ad86297e6bc0ad9aa679aee9daa4f19e8163860faf7c164e4f5a71"
 "checksum crossbeam-epoch 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "fedcd6772e37f3da2a9af9bf12ebe046c0dfe657992377b4df982a2b54cd37a9"
 "checksum crossbeam-queue 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7c979cd6cfe72335896575c6b5688da489e420d36a27a0b9eb0c73db574b4a4b"
 "checksum crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "f8306fcef4a7b563b76b7dd949ca48f52bc1141aa067d2ea09565f3e2652aa5c"
 "checksum cssparser 0.25.9 (registry+https://github.com/rust-lang/crates.io-index)" = "fbe18ca4efb9ba3716c6da66cc3d7e673bf59fa576353011f48c4cfddbdd740e"
 "checksum cssparser-macros 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "5bb1c84e87c717666564ec056105052331431803d606bd45529b28547b611eef"
 "checksum cstr 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "b6557bdb1dc9647eae1cf7f5601b14cd45fc3c7ccf2df618387416fe542da6ea"
 "checksum cstr-macros 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "cd670e5ff58768ef624207fb95709ce63b8d05573fb9a05165f0eef471ea6a3a"
-"checksum cubeb 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)" = "50f6a746cc3a80bdc96203e617d3bfc8988169c012c85c4ca4f1bad7862441bc"
-"checksum cubeb-backend 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)" = "54c14a298d865c7f454dd809b2feb1fdb361b0a143f8ed2ea0b3a9becfa0a2ea"
-"checksum cubeb-core 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)" = "20c6cde72d3505dc4f2452e0378b970b03d1fddf3ad8ac1b98dcb65fc7721907"
-"checksum cubeb-sys 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)" = "318d9b60d6d5de52f815882ab5405adedc0ac7e91414950eff27a440c2860a3b"
+"checksum cubeb 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3cbcdfde9ea319160af6eff068ffaa96aad3532e1b5c0ebc134614cfacacae24"
+"checksum cubeb-backend 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "5a1e7add4e7642a8aebb24172922318482bed52389a12cb339f728bbd4c4ed9c"
+"checksum cubeb-core 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "bfd9b2ea1cb6afed9419b0d18fc4093df552ccb2300eb57793629f8cd370b4c8"
+"checksum cubeb-sys 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "309c5839c5fa03c08363bd308566cbe4654b25a9984342d7546a33d55b80a3d6"
 "checksum darling 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3fe629a532efad5526454efb0700f86d5ad7ff001acb37e431c8bf017a432a8e"
 "checksum darling_core 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ee54512bec54b41cf2337a22ddfadb53c7d4c738494dc2a186d7b037ad683b85"
 "checksum darling_macro 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0cd3e432e52c0810b72898296a69d66b1d78d1517dff6cde7a130557a55a62c1"
 "checksum dbus 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "b9e1b39f3f6aa3d4a1522c4f0f9f1e9e9167bd93740a8690874caa7cf8ce47d7"
 "checksum deflate 0.7.19 (registry+https://github.com/rust-lang/crates.io-index)" = "8a6abb26e16e8d419b5c78662aa9f82857c2386a073da266840e474d5055ec86"
 "checksum derive_more 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3f57d78cf3bd45270dad4e70c21ec77a960b36c7a841ff9db76aaa775a8fb871"
 "checksum devd-rs 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0d009f166c0d9e9f9909dc751630b3a6411ab7f85a153d32d01deb364ffe52a7"
 "checksum digest 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "05f47366984d3ad862010e22c7ce81a7dbcaebbdfb37241a620f8b6596ee135c"
@@ -4026,38 +4024,37 @@ dependencies = [
 "checksum rust-ini 0.10.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8a654c5bda722c699be6b0fe4c0d90de218928da5b724c3e467fc48865c37263"
 "checksum rust_cascade 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "f3fe4900d38dab1ad21a515e44687dd0711e6b0ec5b214a3b1aa8857343bcf3a"
 "checksum rustc-demangle 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "76d7ba1feafada44f2d38eed812bd2489a03c0f5abb975799251518b68848649"
 "checksum rustc-hash 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7540fc8b0c49f096ee9c961cda096467dce8084bec6bdca2fc83895fd9b28cb8"
 "checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
 "checksum ryu 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "fd0568787116e13c652377b6846f5931454a363a8fdf8ae50463ee40935b278b"
 "checksum safemem 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8dca453248a96cb0749e36ccdfe2b0b4e54a61bfef89fb97ec621eb8e0a93dd9"
 "checksum same-file 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "cfb6eded0b06a0b512c8ddbcf04089138c9b4362c2f696f3c3d76039d68f3637"
-"checksum scoped-tls 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f417c22df063e9450888a7561788e9bd46d3bb3c1466435b4eccb903807f147d"
 "checksum scoped-tls 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ea6a9290e3c9cf0f18145ef7ffa62d68ee0bf5fcd651017e586dc7fd5da448c2"
 "checksum scopeguard 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b42e15e59b18a828bbf5c58ea01debb36b9b096346de35d941dcb89009f24a0d"
 "checksum scroll 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2f84d114ef17fd144153d608fba7c446b0145d038985e7a8cc5d08bb0ce20383"
 "checksum scroll_derive 0.9.5 (registry+https://github.com/rust-lang/crates.io-index)" = "8f1aa96c45e7f5a91cb7fabe7b279f02fea7126239fc40b732316e8b6a2d0fcb"
 "checksum semver 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a3186ec9e65071a2095434b1f5bb24838d4e8e130f584c790f6033c79943537"
+"checksum semver 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3fdd61b85a0fa777f7fb7c454b9189b2941b110d1385ce84d7f76efdf1606a85"
 "checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
 "checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
 "checksum serde 1.0.88 (registry+https://github.com/rust-lang/crates.io-index)" = "9f301d728f2b94c9a7691c90f07b0b4e8a4517181d9461be94c04bddeb4bd850"
 "checksum serde_bytes 0.11.2 (registry+https://github.com/rust-lang/crates.io-index)" = "45af0182ff64abaeea290235eb67da3825a576c5d53e642c4d5b652e12e6effc"
 "checksum serde_derive 1.0.88 (registry+https://github.com/rust-lang/crates.io-index)" = "beed18e6f5175aef3ba670e57c60ef3b1b74d250d962a26604bff4c80e970dd4"
 "checksum serde_json 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)" = "44dd2cfde475037451fa99b7e5df77aa3cfd1536575fa8e7a538ab36dcde49ae"
 "checksum serde_repr 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "29a734c298df0346c4cd5919595981c266dabbf12dc747c85e1a95e96077a52b"
 "checksum serde_urlencoded 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9ec5d77e2d4c73717816afac02670d5c4f534ea95ed430442cad02e7a6e32c97"
 "checksum serde_yaml 0.8.9 (registry+https://github.com/rust-lang/crates.io-index)" = "38b08a9a90e5260fe01c6480ec7c811606df6d3a660415808c3c3fa8ed95b582"
 "checksum sha-1 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "23962131a91661d643c98940b20fcaffe62d776a823247be80a48fcb8b6fce68"
 "checksum sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7b4d8bfd0e469f417657573d8451fb33d16cfe0989359b93baf3a1ffc639543d"
 "checksum shift_or_euc 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f930dea4685b9803954b9d74cdc175c6d946a22f2eafe5aa2e9a58cdcae7da8c"
 "checksum shift_or_euc_c 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c81ec08c8a68c45c48d8ef58b80ce038cc9945891c4a4996761e2ec5cba05abc"
 "checksum shlex 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7fdf1b9db47230893d76faad238fd6097fd6d6a9245cd7a4d90dbd639536bbd2"
 "checksum siphasher 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2ffc669b726f2bc9a3bcff66e5e23b56ba6bf70e22a34c3d7b6d0b3450b65b84"
-"checksum slab 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "17b4fcaed89ab08ef143da37bc52adbcc04d4a69014f4c1208d6b51f0c47bc23"
 "checksum slab 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5f9776d6b986f77b35c6cf846c11ad986ff128fe0b2b63a3628e3755e8d3102d"
 "checksum smallbitvec 2.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1764fe2b30ee783bfe3b9b37b2649d8d590b3148bb12e0079715d4d5c673562e"
 "checksum smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)" = "ab606a9c5e214920bb66c458cd7be8ef094f813f20fe77a54cc7dbfff220d4b7"
 "checksum socket2 0.3.10 (registry+https://github.com/rust-lang/crates.io-index)" = "df028e0e632c2a1823d920ad74895e7f9128e6438cbc4bc6fd1f180e644767b9"
 "checksum stable_deref_trait 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "15132e0e364248108c5e2c02e3ab539be8d6f5d52a01ca9bbf27ed657316f02b"
 "checksum string 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "00caf261d6f90f588f8450b8e1230fa0d5be49ee6140fdfbcb55335aff350970"
 "checksum strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bb4f380125926a99e52bc279241539c018323fab05ad6368b56f93d9369ff550"
 "checksum svg_fmt 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c666f0fed8e1e20e057af770af9077d72f3d5a33157b8537c1475dd8ffd6d32b"
--- a/accessible/tests/browser/events/browser_test_A11yUtils_announce.js
+++ b/accessible/tests/browser/events/browser_test_A11yUtils_announce.js
@@ -7,25 +7,51 @@
 loadScripts({ name: "role.js", dir: MOCHITESTS_DIR });
 
 // Check that the browser A11yUtils.announce() function works correctly.
 // Note that this does not use mozilla::a11y::Accessible::Announce and a11y
 // announcement events, as these aren't yet supported on desktop.
 async function runTests() {
   const alert = document.getElementById("a11y-announcement");
   let alerted = waitForEvent(EVENT_ALERT, alert);
-  A11yUtils.announce("first");
+  A11yUtils.announce({ raw: "first" });
   let event = await alerted;
   const alertAcc = event.accessible;
   is(alertAcc.role, ROLE_ALERT);
   ok(!alertAcc.name);
   is(alertAcc.childCount, 1);
   is(alertAcc.firstChild.name, "first");
 
   alerted = waitForEvent(EVENT_ALERT, alertAcc);
-  A11yUtils.announce("second");
+  A11yUtils.announce({ raw: "second" });
   event = await alerted;
   ok(!alertAcc.name);
   is(alertAcc.childCount, 1);
   is(alertAcc.firstChild.name, "second");
+
+  info("Testing Fluent message");
+  // We need a simple Fluent message here without arguments or attributes.
+  const fluentId = "search-one-offs-with-title";
+  const fluentMessage = await document.l10n.formatValue(fluentId);
+  alerted = waitForEvent(EVENT_ALERT, alertAcc);
+  A11yUtils.announce({ id: fluentId });
+  event = await alerted;
+  ok(!alertAcc.name);
+  is(alertAcc.childCount, 1);
+  is(alertAcc.firstChild.name, fluentMessage);
+
+  info("Ensuring Fluent message is cancelled if announce is re-entered");
+  alerted = waitForEvent(EVENT_ALERT, alertAcc);
+  // This call runs async.
+  let asyncAnnounce = A11yUtils.announce({ id: fluentId });
+  // Before the async call finishes, call announce again.
+  A11yUtils.announce({ raw: "third" });
+  // Wait for the async call to complete.
+  await asyncAnnounce;
+  event = await alerted;
+  ok(!alertAcc.name);
+  is(alertAcc.childCount, 1);
+  // The async call should have been cancelled. If it wasn't, we would get
+  // fluentMessage here instead of "third".
+  is(alertAcc.firstChild.name, "third");
 }
 
 addAccessibleTask(``, runTests);
--- a/accessible/tests/browser/events/browser_test_focus_urlbar.js
+++ b/accessible/tests/browser/events/browser_test_focus_urlbar.js
@@ -19,20 +19,30 @@ ChromeUtils.defineModuleGetter(
   "PlacesUtils",
   "resource://gre/modules/PlacesUtils.jsm"
 );
 
 function isEventForAutocompleteItem(event) {
   return event.accessible.role == ROLE_COMBOBOX_OPTION;
 }
 
-function isEventForOneOffButton(event) {
+function isEventForButton(event) {
   return event.accessible.role == ROLE_PUSHBUTTON;
 }
 
+function isEventForOneOffEngine(event) {
+  let parent = event.accessible.parent;
+  return (
+    event.accessible.role == ROLE_PUSHBUTTON &&
+    parent &&
+    parent.role == ROLE_GROUPING &&
+    parent.name
+  );
+}
+
 function isEventForMenuPopup(event) {
   return event.accessible.role == ROLE_MENUPOPUP;
 }
 
 function isEventForMenuItem(event) {
   return event.accessible.role == ROLE_MENUITEM;
 }
 
@@ -142,17 +152,17 @@ async function runTests() {
 
   info("Ensuring autocomplete focus on down arrow (2)");
   focused = waitForEvent(EVENT_FOCUS, isEventForAutocompleteItem);
   EventUtils.synthesizeKey("KEY_ArrowDown");
   event = await focused;
   testStates(event.accessible, STATE_FOCUSED);
 
   info("Ensuring autocomplete focus on arrow up for search settings button");
-  focused = waitForEvent(EVENT_FOCUS, isEventForOneOffButton);
+  focused = waitForEvent(EVENT_FOCUS, isEventForButton);
   EventUtils.synthesizeKey("KEY_ArrowUp");
   event = await focused;
   testStates(event.accessible, STATE_FOCUSED);
 
   info("Ensuring text box focus when text is typed");
   focused = waitForEvent(EVENT_FOCUS, textBox);
   EventUtils.sendString("z");
   await focused;
@@ -174,17 +184,17 @@ async function runTests() {
 
   info("Ensuring autocomplete focus on arrow down (4)");
   focused = waitForEvent(EVENT_FOCUS, isEventForAutocompleteItem);
   EventUtils.synthesizeKey("KEY_ArrowDown");
   event = await focused;
   testStates(event.accessible, STATE_FOCUSED);
 
   info("Ensuring one-off search button focus on arrow down");
-  focused = waitForEvent(EVENT_FOCUS, isEventForOneOffButton);
+  focused = waitForEvent(EVENT_FOCUS, isEventForOneOffEngine);
   EventUtils.synthesizeKey("KEY_ArrowDown");
   event = await focused;
   testStates(event.accessible, STATE_FOCUSED);
 
   info("Ensuring autocomplete focus on arrow up");
   focused = waitForEvent(EVENT_FOCUS, isEventForAutocompleteItem);
   EventUtils.synthesizeKey("KEY_ArrowUp");
   event = await focused;
--- a/accessible/tests/browser/shared-head.js
+++ b/accessible/tests/browser/shared-head.js
@@ -148,17 +148,17 @@ function invokeSetStyle(browser, id, sty
  * @param  {Object}  browser  current "tabbrowser" element
  * @param  {String}  id       content element id
  * @return {Promise} promise  indicating that focus is set
  */
 function invokeFocus(browser, id) {
   Logger.log(`Setting focus on a node with id: ${id}`);
   return ContentTask.spawn(browser, id, contentId => {
     let elm = content.document.getElementById(contentId);
-    if (elm.editor || elm.localName == "textbox") {
+    if (elm.editor) {
       elm.selectionStart = elm.selectionEnd = elm.value.length;
     }
     elm.focus();
   });
 }
 
 /**
  * Load a list of scripts into the test
--- a/accessible/tests/mochitest/events.js
+++ b/accessible/tests/mochitest/events.js
@@ -1459,17 +1459,17 @@ function synthOpenComboboxKey(aID, aChec
  */
 function synthFocus(aNodeOrID, aCheckerOrEventSeq) {
   var checkerOfEventSeq = aCheckerOrEventSeq
     ? aCheckerOrEventSeq
     : new focusChecker(aNodeOrID);
   this.__proto__ = new synthAction(aNodeOrID, checkerOfEventSeq);
 
   this.invoke = function synthFocus_invoke() {
-    if (this.DOMNode.editor || this.DOMNode.localName == "textbox") {
+    if (this.DOMNode.editor) {
       this.DOMNode.selectionStart = this.DOMNode.selectionEnd = this.DOMNode.value.length;
     }
     this.DOMNode.focus();
   };
 
   this.getID = function synthFocus_getID() {
     return prettyName(aNodeOrID) + " focus";
   };
@@ -1629,20 +1629,17 @@ function closeCombobox(aComboboxID) {
 
 /**
  * Select all invoker.
  */
 function synthSelectAll(aNodeOrID, aCheckerOrEventSeq) {
   this.__proto__ = new synthAction(aNodeOrID, aCheckerOrEventSeq);
 
   this.invoke = function synthSelectAll_invoke() {
-    if (
-      ChromeUtils.getClassName(this.DOMNode) === "HTMLInputElement" ||
-      this.DOMNode.localName == "textbox"
-    ) {
+    if (ChromeUtils.getClassName(this.DOMNode) === "HTMLInputElement") {
       this.DOMNode.select();
     } else {
       window.getSelection().selectAllChildren(this.DOMNode);
     }
   };
 
   this.getID = function synthSelectAll_getID() {
     return aNodeOrID + " selectall";
--- a/accessible/tests/mochitest/events/test_focus_autocomplete.xul
+++ b/accessible/tests/mochitest/events/test_focus_autocomplete.xul
@@ -274,27 +274,23 @@
 
     function getTextEntry(aID)
     {
       // For form autocompletes the autocomplete widget and text entry widget
       // is the same widget, for XUL autocompletes the text entry is a first
       // child.
       var localName = getNode(aID).localName;
 
-      // XUL autocomplete
-      if (localName == "textbox")
-        return getAccessible(aID).firstChild;
-
       // HTML form autocomplete
       if (localName == "input")
         return getAccessible(aID);
 
       // XUL searchbar
       if (localName == "searchbar")
-        return getAccessible(getNode(aID).textbox.inputField);
+        return getAccessible(getNode(aID).textbox);
 
       return null;
     }
 
     function itemObj(aID, aIdx)
     {
       this.autocompleteNode = getNode(aID);
 
--- a/browser/app/blocklist.xml
+++ b/browser/app/blocklist.xml
@@ -1,10 +1,10 @@
 <?xml version='1.0' encoding='UTF-8'?>
-<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist" lastupdate="1570701652245">
+<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist" lastupdate="1570713923845">
   <emItems>
     <emItem blockID="i334" id="{0F827075-B026-42F3-885D-98981EE7B1AE}">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="3"/>
     </emItem>
     <emItem blockID="i1211" id="flvto@hotger.com">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="1"/>
@@ -3500,16 +3500,20 @@
     <emItem blockID="ab0b6868-6077-4d47-bd58-1df3f572c04d" id="{2e106fa4-ee23-4b4a-9ed0-f93edee539b5}">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="3"/>
     </emItem>
     <emItem blockID="bb76b262-7087-4cf2-a82f-12cfbf91239a" id="{2a78ab07-91b2-4086-889d-619e43d5e5f8}">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="3"/>
     </emItem>
+    <emItem blockID="986aac02-beba-40b2-b463-d8447a778a2e" id="/^((\{2B0EC7FF-F330-4e0c-8B33-EFFEC8D39E70\})|(\{f70c89b0-bbf3-41a9-bc1f-0912dcf53f33\})|(@Classifieds)|(@Converter)|(@Coupons)|(@Directions)|(@DownloadManager)|(@Email)|(@Fitness)|(@Flights)|(@FormsApp)|(@Games)|(@Maps)|(@News)|(@Package)|(@Photo)|(@Radio)|(@Recipes)|(@search-encrypt)|(@search-incognito)|(@searchencrypt-b)|(@searchencryptblocker)|(@Speedtest)|(@Sports)|(@Transit)|(@TV)|(@Weather)|(aweapps@Email)|(classified@jetpack)|(email@searchleasier\.com)|(foo-bar@example\.com)|(games@jetpack)|(JS@Converter)|(login@easier)|(maps-webext@jetpack)|(Maps@SSA)|(web-ext@games\.com)|(web@ShoppingNewTab)|(web@SocialNewTab)|(web@WebDesignNewTab)|(webapp@LoginAssistantTab)|(webex@Converter)|(webex@Email)|(webtab@Shopping)|(webtab@Social)|(webtab@WebDesign))$/">
+      <prefs/>
+      <versionRange minVersion="0" maxVersion="*" severity="3"/>
+    </emItem>
   </emItems>
   <pluginItems>
     <pluginItem blockID="p332">
       <match name="filename" exp="libflashplayer\.so"/>
       <match name="description" exp="^Shockwave Flash 11.(0|1) r[0-9]{1,3}$"/>
       <infoURL>https://get.adobe.com/flashplayer/</infoURL>
       <versionRange severity="0" vulnerabilitystatus="1">
         <targetApplication id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -2010,16 +2010,18 @@ pref("devtools.inspector.showUserAgentSt
 // Show all native anonymous content
 pref("devtools.inspector.showAllAnonymousContent", false);
 // Show user agent shadow roots
 pref("devtools.inspector.showUserAgentShadowRoots", false);
 // Enable the new Rules View
 pref("devtools.inspector.new-rulesview.enabled", false);
 // Enable the compatibility tool in the inspector.
 pref("devtools.inspector.compatibility.enabled", false);
+// Enable the new Box Model Highlighter with renderer in parent process
+pref("devtools.inspector.use-new-box-model-highlighter", false);
 
 // Grid highlighter preferences
 pref("devtools.gridinspector.gridOutlineMaxColumns", 50);
 pref("devtools.gridinspector.gridOutlineMaxRows", 50);
 pref("devtools.gridinspector.showGridAreas", false);
 pref("devtools.gridinspector.showGridLineNumbers", false);
 pref("devtools.gridinspector.showInfiniteLines", false);
 // Max number of grid highlighters that can be displayed
--- a/browser/base/content/blockedSite.xhtml
+++ b/browser/base/content/blockedSite.xhtml
@@ -32,17 +32,17 @@
           <p id="advisoryDescText">
             <a id="advisory_provider" data-l10n-name="advisory_provider"></a>
           </p>
         </div>
 
         <!-- Action buttons -->
         <div id="buttons" class="button-container">
           <!-- Commands handled in browser.js -->
-          <button id="goBackButton" data-l10n-id="safeb-palm-accept-label"></button>
+          <button id="goBackButton" class="primary" data-l10n-id="safeb-palm-accept-label"></button>
           <button id="seeDetailsButton" data-l10n-id="safeb-palm-see-details-label"></button>
         </div>
       </div>
       <div id="errorDescriptionContainer" hidden="true">
         <!-- Error Descriptions Handled in blockedSite.js -->
         <div class="error-description" id="errorLongDesc">
           <p id="errorInnerDescription">
               <span id="error_desc_sitename" data-l10n-name="sitename"></span>
--- a/browser/base/content/browser-a11yUtils.js
+++ b/browser/base/content/browser-a11yUtils.js
@@ -16,33 +16,65 @@ var A11yUtils = {
    * user and will be noticed visually, but is not related to the focused
    * control and is not a pop-up such as a doorhanger.
    * For example, this could be used to indicate that Reader View is available
    * or that Firefox is making a recommendation via the toolbar.
    * This must be used with caution, as it can create unwanted verbosity and
    * can thus hinder rather than help users if used incorrectly.
    * Please only use this after consultation with the Mozilla accessibility
    * team.
-   * @param aMessage The message to announce.
-   * @param aSource The element with which the announcement is associated.
-   *        This should generally be something the user can interact with to
-   *        respond to the announcement.
-   *        For example, for an announcement indicating that Reader View is
-   *        available, this should be the Reader View button on the toolbar.
+   * @param {string} [options.id] The Fluent id of the message to announce. The
+   *        ftl file must already be included in browser.xhtml. This must be
+   *        specified unless a raw message is specified instead.
+   * @param {object} [options.args] Arguments for the Fluent message.
+   * @param {string} [options.raw] The raw, already localized message to
+   *        announce. You should generally prefer a Fluent id instead, but in
+   *        rare cases, this might not be feasible.
+   * @param {Element} [options.source] The element with which the announcement
+   *        is associated. This should generally be something the user can
+   *        interact with to respond to the announcement. For example, for an
+   *        announcement indicating that Reader View is available, this should
+   *        be the Reader View button on the toolbar.
    */
-  announce(aMessage, aSource = document) {
-    // For now, we don't use aSource, but it might be useful in future.
+  async announce({ id = null, args = {}, raw = null, source = document } = {}) {
+    if ((!id && !raw) || (id && raw)) {
+      throw new Error("One of raw or id must be specified.");
+    }
+
+    // Cancel a previous pending call if any.
+    if (this._cancelAnnounce) {
+      this._cancelAnnounce();
+      this._cancelAnnounce = null;
+    }
+
+    let message;
+    if (id) {
+      let cancel = false;
+      this._cancelAnnounce = () => (cancel = true);
+      message = await document.l10n.formatValue(id, args);
+      if (cancel) {
+        // announce() was called again while we were waiting for translation.
+        return;
+      }
+      // No more async operations from this point.
+      this._cancelAnnounce = null;
+    } else {
+      // We run fully synchronously if a raw message is provided.
+      message = raw;
+    }
+
+    // For now, we don't use source, but it might be useful in future.
     // For example, we might use it when we support announcement events on
     // more platforms or it could be used to have a keyboard shortcut which
     // focuses the last element to announce a message.
     let live = document.getElementById("a11y-announcement");
     // We use role="alert" because JAWS doesn't support aria-live in browser
     // chrome.
     // Gecko a11y needs an insertion to trigger an alert event. This is why
     // we can't just use aria-label on the alert.
     if (live.firstChild) {
       live.firstChild.remove();
     }
     let label = document.createElement("label");
-    label.setAttribute("aria-label", aMessage);
+    label.setAttribute("aria-label", message);
     live.appendChild(label);
   },
 };
--- a/browser/base/content/browser.css
+++ b/browser/base/content/browser.css
@@ -1342,8 +1342,29 @@ toolbarpaletteitem > toolbaritem {
   #sidebar-box[sidebarcommand$="-sidebar-action"] > #sidebar-header > #sidebar-switcher-target > #sidebar-icon {
     list-style-image: var(--webextension-menuitem-image-2x, inherit);
   }
 }
 
 toolbar[keyNav=true]:not([collapsed=true]):not([customizing=true]) toolbartabstop {
   -moz-user-focus: normal;
 }
+
+/* Frame used for rendering the DevTools inspector highlighters */
+iframe.devtools-highlighter-renderer {
+  border: none;
+  pointer-events: none;
+}
+
+/* Highlighter for the Browser Toolbox */
+:root > iframe.devtools-highlighter-renderer {
+  position: fixed;
+  top: 0;
+  left: 0;
+  width: 100%;
+  height: 100%;
+  z-index: 2;
+}
+
+/* Highlighter for web content */
+.browserStack > iframe.devtools-highlighter-renderer {
+  -moz-box-flex: 1;
+}
--- a/browser/base/content/browser.xhtml
+++ b/browser/base/content/browser.xhtml
@@ -73,16 +73,17 @@
         data-l10n-sync="true">
 
 <linkset>
   <html:link rel="localization" href="branding/brand.ftl"/>
   <html:link rel="localization" href="browser/branding/sync-brand.ftl"/>
   <html:link rel="localization" href="browser/browser.ftl"/>
   <html:link rel="localization" href="browser/menubar.ftl"/>
   <html:link rel="localization" href="browser/appmenu.ftl"/>
+  <html:link rel="localization" href="browser/readerView.ftl"/>
 </linkset>
 
 # All JS files which are needed by browser.xhtml and other top level windows to
 # support MacOS specific features *must* go into the global-scripts.inc file so
 # that they can be shared with macWindow.inc.xul.
 #include global-scripts.inc
 
 <script>
--- a/browser/components/originattributes/test/browser/browser.ini
+++ b/browser/components/originattributes/test/browser/browser.ini
@@ -71,17 +71,16 @@ skip-if = verify
 fail-if = fission
 [browser_favicon_firstParty.js]
 fail-if = fission
 [browser_favicon_userContextId.js]
 fail-if = fission
 [browser_firstPartyIsolation.js]
 skip-if = fission || debug #Bug 1345346
 [browser_firstPartyIsolation_about_newtab.js]
-fail-if = fission
 [browser_firstPartyIsolation_aboutPages.js]
 fail-if = fission
 [browser_firstPartyIsolation_blobURI.js]
 skip-if = fission # Crashes: @ mozilla::dom::BrowserBridgeParent::RecvShow(mozilla::gfx::IntSizeTyped<mozilla::ScreenPixel> const&, bool const&, nsSizeMode const&)
 [browser_firstPartyIsolation_js_uri.js]
 skip-if = fission
 [browser_firstPartyIsolation_saveAs.js]
 skip-if = fission
--- a/browser/components/originattributes/test/browser/browser_firstPartyIsolation_about_newtab.js
+++ b/browser/components/originattributes/test/browser/browser_firstPartyIsolation_about_newtab.js
@@ -17,26 +17,31 @@ add_task(async function setup() {
  * tabbrowser.addTab, if it found out the uri is about:newtab, it will use
  * the preloaded browser, however the preloaded browser is loaded before when we
  * turn on the firstPartyIsolation pref, which won't have the pref set.
  *
  * To prevent to use the preloaded browser, a simple trick is open a window
  * first.
  **/
 add_task(async function test_aboutNewTab() {
-  let win = await BrowserTestUtils.openNewBrowserWindow({ remote: false });
+  // In Fission, we cannot open a non-remote window.
+  let win = await BrowserTestUtils.openNewBrowserWindow({
+    remote: SpecialPowers.useRemoteSubframes,
+  });
   let gbrowser = win.gBrowser;
   let tab = BrowserTestUtils.addTab(gbrowser, "about:newtab");
   await BrowserTestUtils.browserLoaded(tab.linkedBrowser);
 
   let attrs = {
     firstPartyDomain: "about.ef2a7dd5-93bc-417f-a698-142c3116864f.mozilla",
   };
-  await ContentTask.spawn(tab.linkedBrowser, { attrs }, async function(args) {
-    info("principal " + content.document.nodePrincipal.origin);
+  await SpecialPowers.spawn(tab.linkedBrowser, [{ attrs }], async function(
+    args
+  ) {
+    Assert.ok(true, "principal " + content.document.nodePrincipal.origin);
     Assert.equal(
       content.document.nodePrincipal.originAttributes.firstPartyDomain,
       args.attrs.firstPartyDomain,
       "about:newtab should have firstPartyDomain set"
     );
     Assert.ok(
       content.document.nodePrincipal.isContentPrincipal,
       "The principal should be a content principal."
--- a/browser/components/preferences/in-content/tests/privacypane_tests_perwindow.js
+++ b/browser/components/preferences/in-content/tests/privacypane_tests_perwindow.js
@@ -347,20 +347,16 @@ function test_custom_retention(controlTo
     controlChanged(historymode);
 
     controlToChange = win.document.getElementById(controlToChange);
     ok(controlToChange, "the control to change should exist");
     switch (controlToChange.localName) {
       case "checkbox":
         controlToChange.checked = !controlToChange.checked;
         break;
-      case "textbox":
-        controlToChange.value =
-          parseInt(controlToChange.value) + valueIncrement;
-        break;
       case "menulist":
         controlToChange.value = valueIncrement;
         break;
     }
     controlChanged(controlToChange);
   };
 }
 
--- a/browser/components/search/content/search-one-offs.js
+++ b/browser/components/search/content/search-one-offs.js
@@ -464,17 +464,18 @@ class SearchOneOffs {
     // Finally, build the list of one-off buttons.
     while (this.buttons.firstElementChild) {
       this.buttons.firstElementChild.remove();
     }
 
     let headerText = this.header.querySelector(
       ".search-panel-one-offs-header-label"
     );
-    this.buttons.setAttribute("aria-label", headerText.value);
+    headerText.id = this.telemetryOrigin + "-one-offs-header-label";
+    this.buttons.setAttribute("aria-labelledby", headerText.id);
 
     let engines = await this.getEngines();
     let defaultEngine = PrivateBrowsingUtils.isWindowPrivate(window)
       ? await Services.search.getDefaultPrivate()
       : await Services.search.getDefault();
     let oneOffCount = engines.length;
     let hideOneOffs =
       !oneOffCount ||
new file mode 100644
--- /dev/null
+++ b/browser/locales/en-US/browser/readerView.ftl
@@ -0,0 +1,8 @@
+# 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/.
+
+## Reader View
+
+# Announced by screen readers when Reader View is available for a page.
+reader-available-announcement = Reader View available
--- a/browser/modules/ReaderParent.jsm
+++ b/browser/modules/ReaderParent.jsm
@@ -73,37 +73,32 @@ var ReaderParent = {
     let win = browser.ownerGlobal;
     if (browser != win.gBrowser.selectedBrowser) {
       return;
     }
 
     let button = win.document.getElementById("reader-mode-button");
     let menuitem = win.document.getElementById("menu_readerModeItem");
     let key = win.document.getElementById("key_toggleReaderMode");
-    // aria-reader is not a real ARIA attribute. However, this will cause
-    // Gecko accessibility to expose the "reader" object attribute. We do this
-    // so that the reader state is easy for accessibility clients to access
-    // programmatically.
     if (browser.currentURI.spec.startsWith("about:reader")) {
       let closeText = gStringBundle.GetStringFromName("readerView.close");
 
       button.setAttribute("readeractive", true);
       button.hidden = false;
       button.setAttribute("aria-label", closeText);
 
       menuitem.setAttribute("label", closeText);
       menuitem.setAttribute("hidden", false);
       menuitem.setAttribute(
         "accesskey",
         gStringBundle.GetStringFromName("readerView.close.accesskey")
       );
 
       key.setAttribute("disabled", false);
 
-      browser.setAttribute("aria-reader", "active");
       Services.obs.notifyObservers(null, "reader-mode-available");
     } else {
       let enterText = gStringBundle.GetStringFromName("readerView.enter");
 
       button.removeAttribute("readeractive");
       button.hidden = !browser.isArticle;
       button.setAttribute("aria-label", enterText);
 
@@ -112,20 +107,21 @@ var ReaderParent = {
       menuitem.setAttribute(
         "accesskey",
         gStringBundle.GetStringFromName("readerView.enter.accesskey")
       );
 
       key.setAttribute("disabled", !browser.isArticle);
 
       if (browser.isArticle) {
-        browser.setAttribute("aria-reader", "available");
+        win.A11yUtils.announce({
+          id: "reader-available-announcement",
+          source: button,
+        });
         Services.obs.notifyObservers(null, "reader-mode-available");
-      } else {
-        browser.removeAttribute("aria-reader");
       }
     }
   },
 
   forceShowReaderIcon(browser) {
     browser.isArticle = true;
     this.updateReaderButton(browser);
   },
--- a/browser/themes/shared/blockedSite.css
+++ b/browser/themes/shared/blockedSite.css
@@ -1,64 +1,38 @@
 /* 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/. */
 
 @import url("chrome://browser/skin/error-pages.css");
 
-html {
-  background-color: #A4000F;
-}
-
-body {
-  color: white;
+:root {
+  --in-content-page-background: #A4000F;
+  --in-content-page-color: white;
+  --in-content-text-color: white;
+  --in-content-selected-text: black;
+  --in-content-button-background: transparent;
+  --in-content-button-background-hover: #5a0002;
+  --in-content-button-background-active: #3e0200;
+  --in-content-primary-button-background: white;
+  --in-content-primary-button-background-hover: rgba(255, 255, 255, 0.8);
+  --in-content-primary-button-background-active: rgba(255, 255, 255, 0.7);
 }
 
 .title {
   background-image: url("chrome://global/skin/icons/blocked.svg");
 }
 
-.title-text {
-  color: white;
-}
-
 .button-container button {
-  background-color: transparent;
-  /* !important overrides the common.css button color */
-  color: white !important;
   border: 1px solid white;
   margin-inline-end: 0;
   margin-top: 1.5em;
 }
 
-.button-container button:hover {
-  background-color: #5a0002;
-}
-
-.button-container button:hover:active {
-  background-color: #3e0200;
-}
-
-#goBackButton {
-  color: black !important;
-  background-color: white;
-}
-
-#goBackButton:hover {
-  background-color: white;
-  opacity: 0.8;
-}
-
-#goBackButton:active {
-  background-color: white;
-  opacity: 0.7;
-}
-
 #advisory_provider {
-  color: white;
   text-decoration: underline;
 }
 
 #errorDescriptionContainer {
   position: absolute;
   margin: 48px auto;
 }
 
--- a/browser/themes/shared/privatebrowsing/aboutPrivateBrowsing.css
+++ b/browser/themes/shared/privatebrowsing/aboutPrivateBrowsing.css
@@ -180,16 +180,20 @@ p {
   background-position: left 0 top 50px;
   background-repeat: no-repeat;
   background-size: 32px;
   letter-spacing: -.2px;
   padding: 50px 0;
   padding-inline-start: 44px;
 }
 
+.banner-body:dir(rtl) {
+  background-position-x: right;
+}
+
 .banner-body h1 {
   font-size: 18px;
   font-weight: bold;
   color: var(--in-content-banner-text-color);
   line-height: 1em;
   margin: 7px 0;
 }
 
@@ -222,22 +226,25 @@ p {
 }
 
 .info p {
   font-size: 15px;
   line-height: 25px;
 }
 
 .search-banner-close-button {
-  float: right;
-  min-width: 20px;
-  min-height: 20px;
+  float: inline-end;
+  /* min-width and min-height override values set on button elements. */
+  min-width: 28px;
+  min-height: 28px;
+  height: 28px;
   margin: 16px;
   padding: 0;
   background-color: inherit;
+  border: 0;
 }
 
 .search-banner-close-image {
   -moz-context-properties: fill, fill-opacity;
   fill: currentColor;
   fill-opacity: 0;
   width: 28px;
 }
--- a/devtools/client/application/src/components/manifest/Manifest.js
+++ b/devtools/client/application/src/components/manifest/Manifest.js
@@ -1,15 +1,14 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
-const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
 const {
   createFactory,
   PureComponent,
 } = require("devtools/client/shared/vendor/react");
 const {
   article,
   h1,
   table,
@@ -23,30 +22,25 @@ const { l10n } = require("../../modules/
 const ManifestColorItem = createFactory(require("./ManifestColorItem"));
 const ManifestIconItem = createFactory(require("./ManifestIconItem"));
 const ManifestItem = createFactory(require("./ManifestItem"));
 const ManifestIssueList = createFactory(require("./ManifestIssueList"));
 const ManifestSection = createFactory(require("./ManifestSection"));
 const ManifestJsonLink = createFactory(require("./ManifestJsonLink"));
 
 const { MANIFEST_MEMBER_VALUE_TYPES } = require("../../constants");
+const Types = require("../../types/index");
 
 /**
  * A canonical manifest, splitted in different sections
  */
 class Manifest extends PureComponent {
   static get propTypes() {
-    // TODO: Use well-defined types
-    //       See https://bugzilla.mozilla.org/show_bug.cgi?id=1576881
     return {
-      icons: PropTypes.array.isRequired,
-      identity: PropTypes.array.isRequired,
-      presentation: PropTypes.array.isRequired,
-      validation: PropTypes.array.isRequired,
-      url: PropTypes.string.isRequired,
+      ...Types.manifest, // { identity, presentation, icons, validation, url }
     };
   }
 
   renderIssueSection() {
     const { validation } = this.props;
     const shouldRender = validation && validation.length > 0;
 
     return shouldRender
--- a/devtools/client/application/src/components/manifest/ManifestColorItem.js
+++ b/devtools/client/application/src/components/manifest/ManifestColorItem.js
@@ -1,31 +1,30 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
-const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
 const {
   createFactory,
   PureComponent,
 } = require("devtools/client/shared/vendor/react");
 const { span } = require("devtools/client/shared/vendor/react-dom-factories");
 
+const Types = require("../../types/index");
 const ManifestItem = createFactory(require("./ManifestItem"));
 
 /**
  * This component displays a Manifest member which holds a color value
  */
 class ManifestColorItem extends PureComponent {
   static get propTypes() {
     return {
-      label: PropTypes.string.isRequired,
-      value: PropTypes.string,
+      ...Types.manifestItemColor, // { label, value }
     };
   }
 
   renderColor() {
     const { value } = this.props;
     return value
       ? span(
           {
--- a/devtools/client/application/src/components/manifest/ManifestIconItem.js
+++ b/devtools/client/application/src/components/manifest/ManifestIconItem.js
@@ -1,46 +1,43 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
-const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
 const {
   createFactory,
   PureComponent,
 } = require("devtools/client/shared/vendor/react");
 const {
   br,
   code,
   img,
   span,
 } = require("devtools/client/shared/vendor/react-dom-factories");
 
 const FluentReact = require("devtools/client/shared/vendor/fluent-react");
 const Localized = createFactory(FluentReact.Localized);
 const { l10n } = require("../../modules/l10n");
 
+const Types = require("../../types/index");
 const ManifestItem = createFactory(require("./ManifestItem"));
 
 /**
  * This component displays a Manifest member which holds a color value
  */
 class ManifestIconItem extends PureComponent {
   static get propTypes() {
     return {
-      label: PropTypes.shape({
-        contentType: PropTypes.string,
-        sizes: PropTypes.string,
-      }).isRequired,
-      value: PropTypes.shape({
-        src: PropTypes.string.isRequired,
-        purpose: PropTypes.string.isRequired,
-      }).isRequired,
+      // {
+      //   label: { contentType, sizes },
+      //   value: { src, purpose }
+      // }
+      ...Types.manifestItemIcon,
     };
   }
 
   getLocalizedImgTitle() {
     const { sizes } = this.props.label;
 
     return sizes && sizes.length > 0
       ? l10n.getString("manifest-icon-img-title", { sizes })
--- a/devtools/client/application/src/components/manifest/ManifestIssue.js
+++ b/devtools/client/application/src/components/manifest/ManifestIssue.js
@@ -14,28 +14,26 @@ const {
   li,
   span,
 } = require("devtools/client/shared/vendor/react-dom-factories");
 
 const FluentReact = require("devtools/client/shared/vendor/fluent-react");
 const Localized = createFactory(FluentReact.Localized);
 
 const { MANIFEST_ISSUE_LEVELS } = require("../../constants");
+const Types = require("../../types/index");
 
 /**
  * A Manifest validation issue (warning, error)
  */
 class ManifestIssue extends PureComponent {
   static get propTypes() {
     return {
       className: PropTypes.string,
-      level: PropTypes.oneOf(Object.values(MANIFEST_ISSUE_LEVELS)).isRequired,
-      message: PropTypes.string.isRequired,
-      // NOTE: we are currently ignoring the 'type' field that platform adds to
-      //       errors
+      ...Types.manifestIssue, // { level, message }
     };
   }
 
   getIconData(level) {
     switch (level) {
       case MANIFEST_ISSUE_LEVELS.WARNING:
         return {
           src: "chrome://devtools/skin/images/alert-small.svg",
--- a/devtools/client/application/src/components/manifest/ManifestIssueList.js
+++ b/devtools/client/application/src/components/manifest/ManifestIssueList.js
@@ -1,33 +1,32 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
-const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
 const {
   createFactory,
   PureComponent,
 } = require("devtools/client/shared/vendor/react");
 const { ul } = require("devtools/client/shared/vendor/react-dom-factories");
 
 const { MANIFEST_ISSUE_LEVELS } = require("../../constants");
+const Types = require("../../types/index");
+
 const ManifestIssue = createFactory(require("./ManifestIssue"));
 
 /**
  * A collection of manifest issues (errors, warnings)
  */
 class ManifestIssueList extends PureComponent {
   static get propTypes() {
-    // TODO: Use well-defined types
-    //       See https://bugzilla.mozilla.org/show_bug.cgi?id=1576881
     return {
-      issues: PropTypes.array.isRequired,
+      issues: Types.manifestIssueArray.isRequired,
     };
   }
 
   // group the errors by level, and order by severity
   groupIssuesByLevel() {
     const { issues } = this.props;
 
     const errors = issues.filter(x => x.level === MANIFEST_ISSUE_LEVELS.ERROR);
--- a/devtools/client/application/src/components/manifest/ManifestPage.js
+++ b/devtools/client/application/src/components/manifest/ManifestPage.js
@@ -10,30 +10,29 @@ const {
   PureComponent,
 } = require("devtools/client/shared/vendor/react");
 const {
   section,
 } = require("devtools/client/shared/vendor/react-dom-factories");
 
 const { connect } = require("devtools/client/shared/vendor/react-redux");
 
+const Types = require("../../types/index");
+
 const ManifestLoader = createFactory(require("../manifest/ManifestLoader"));
-
 const Manifest = createFactory(require("./Manifest"));
 const ManifestEmpty = createFactory(require("./ManifestEmpty"));
 
 class ManifestPage extends PureComponent {
-  // TODO: Use well-defined types
-  //       See https://bugzilla.mozilla.org/show_bug.cgi?id=1576881
   static get propTypes() {
     return {
       // these props are automatically injected via connect
       hasLoadingFailed: PropTypes.bool.isRequired,
       isManifestLoading: PropTypes.bool.isRequired,
-      manifest: PropTypes.object,
+      manifest: PropTypes.shape(Types.manifest),
     };
   }
 
   get shouldShowLoader() {
     const { isManifestLoading, hasLoadingFailed } = this.props;
     const mustLoadManifest = typeof this.props.manifest === "undefined";
     return isManifestLoading || mustLoadManifest || hasLoadingFailed;
   }
--- a/devtools/client/application/src/components/routing/PageSwitcher.js
+++ b/devtools/client/application/src/components/routing/PageSwitcher.js
@@ -4,27 +4,27 @@
 
 "use strict";
 
 const {
   createFactory,
   PureComponent,
 } = require("devtools/client/shared/vendor/react");
 const { connect } = require("devtools/client/shared/vendor/react-redux");
-const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
 
 const { PAGE_TYPES } = require("../../constants");
+const Types = require("../../types/index");
 
 const ManifestPage = createFactory(require("../manifest/ManifestPage"));
 const WorkersPage = createFactory(require("../service-workers/WorkersPage"));
 
 class PageSwitcher extends PureComponent {
   static get propTypes() {
     return {
-      page: PropTypes.oneOf(Object.values(PAGE_TYPES)),
+      page: Types.page.isRequired,
     };
   }
 
   render() {
     let component = null;
 
     switch (this.props.page) {
       case PAGE_TYPES.MANIFEST:
--- a/devtools/client/application/src/components/routing/Sidebar.js
+++ b/devtools/client/application/src/components/routing/Sidebar.js
@@ -7,29 +7,29 @@
 const {
   createFactory,
   PureComponent,
 } = require("devtools/client/shared/vendor/react");
 const {
   aside,
   ul,
 } = require("devtools/client/shared/vendor/react-dom-factories");
-const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
 
 const { connect } = require("devtools/client/shared/vendor/react-redux");
 
 const SidebarItem = createFactory(require("./SidebarItem"));
 
+const Types = require("../../types/index");
 const { PAGE_TYPES } = require("../../constants");
 
 class Sidebar extends PureComponent {
   static get propTypes() {
     return {
       // this prop is automatically injected via connect
-      selectedPage: PropTypes.oneOf(Object.values(PAGE_TYPES)),
+      selectedPage: Types.page.isRequired,
     };
   }
 
   render() {
     const navItems = Object.values(PAGE_TYPES);
 
     const isSelected = page => {
       return page === this.props.selectedPage;
--- a/devtools/client/application/src/components/routing/SidebarItem.js
+++ b/devtools/client/application/src/components/routing/SidebarItem.js
@@ -18,33 +18,34 @@ const PropTypes = require("devtools/clie
 
 const Actions = require("./../../actions/index");
 const { connect } = require("devtools/client/shared/vendor/react-redux");
 
 const FluentReact = require("devtools/client/shared/vendor/fluent-react");
 const Localized = createFactory(FluentReact.Localized);
 
 const { PAGE_TYPES } = require("../../constants");
+const Types = require("../../types/index");
 
 const ICONS = {
   [PAGE_TYPES.MANIFEST]:
     "chrome://devtools/skin/images/application-manifest.svg",
   [PAGE_TYPES.SERVICE_WORKERS]:
     "chrome://devtools/skin/images/debugging-workers.svg",
 };
 
 const LOCALIZATION_IDS = {
   [PAGE_TYPES.MANIFEST]: "sidebar-item-manifest",
   [PAGE_TYPES.SERVICE_WORKERS]: "sidebar-item-service-workers",
 };
 
 class SidebarItem extends PureComponent {
   static get propTypes() {
     return {
-      page: PropTypes.oneOf(Object.values(PAGE_TYPES)),
+      page: Types.page.isRequired,
       isSelected: PropTypes.bool.isRequired,
       // this prop is automatically injected via connect
       dispatch: PropTypes.func.isRequired,
     };
   }
 
   render() {
     const { isSelected, page } = this.props;
--- a/devtools/client/application/src/components/service-workers/Worker.js
+++ b/devtools/client/application/src/components/service-workers/Worker.js
@@ -26,16 +26,18 @@ const {
 const {
   getUnicodeUrl,
   getUnicodeUrlPath,
 } = require("devtools/client/shared/unicode-url");
 
 const FluentReact = require("devtools/client/shared/vendor/fluent-react");
 const Localized = createFactory(FluentReact.Localized);
 
+const Types = require("../../types/index");
+
 const UIButton = createFactory(require("../ui/UIButton"));
 
 loader.lazyRequireGetter(
   this,
   "gDevToolsBrowser",
   "devtools/client/framework/devtools-browser",
   true
 );
@@ -45,26 +47,17 @@ loader.lazyRequireGetter(
  * the list of workers displayed in the application panel. It displays information about
  * the worker as well as action links and buttons to interact with the worker (e.g. debug,
  * unregister, update etc...).
  */
 class Worker extends PureComponent {
   static get propTypes() {
     return {
       isDebugEnabled: PropTypes.bool.isRequired,
-      worker: PropTypes.shape({
-        active: PropTypes.bool,
-        name: PropTypes.string.isRequired,
-        scope: PropTypes.string.isRequired,
-        lastUpdateTime: PropTypes.number.isRequired,
-        url: PropTypes.string.isRequired,
-        // registrationFront can be missing in e10s.
-        registrationFront: PropTypes.object,
-        workerTargetFront: PropTypes.object,
-      }).isRequired,
+      worker: PropTypes.shape(Types.worker).isRequired,
     };
   }
 
   constructor(props) {
     super(props);
 
     this.debug = this.debug.bind(this);
     this.start = this.start.bind(this);
--- a/devtools/client/application/src/components/service-workers/WorkerList.js
+++ b/devtools/client/application/src/components/service-workers/WorkerList.js
@@ -12,31 +12,33 @@ const {
 } = require("devtools/client/shared/vendor/react");
 const {
   a,
   article,
   footer,
   h1,
   ul,
 } = require("devtools/client/shared/vendor/react-dom-factories");
-const Worker = createFactory(require("./Worker"));
 
 const FluentReact = require("devtools/client/shared/vendor/fluent-react");
 const Localized = createFactory(FluentReact.Localized);
 
+const Types = require("../../types/index");
+const Worker = createFactory(require("./Worker"));
+
 /**
  * This component handles the list of service workers displayed in the application panel
  * and also displays a suggestion to use about debugging for debugging other service
  * workers.
  */
 class WorkerList extends PureComponent {
   static get propTypes() {
     return {
       canDebugWorkers: PropTypes.bool.isRequired,
-      workers: PropTypes.array.isRequired,
+      workers: Types.workerArray.isRequired,
     };
   }
 
   render() {
     const { canDebugWorkers, workers } = this.props;
 
     return [
       article(
--- a/devtools/client/application/src/components/service-workers/WorkersPage.js
+++ b/devtools/client/application/src/components/service-workers/WorkersPage.js
@@ -9,26 +9,27 @@ const {
   PureComponent,
 } = require("devtools/client/shared/vendor/react");
 const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
 const {
   section,
 } = require("devtools/client/shared/vendor/react-dom-factories");
 const { connect } = require("devtools/client/shared/vendor/react-redux");
 
+const Types = require("../../types/index");
 const WorkerList = createFactory(require("./WorkerList"));
 const WorkerListEmpty = createFactory(require("./WorkerListEmpty"));
 
 class WorkersPage extends PureComponent {
   static get propTypes() {
     return {
       // mapped from state
       canDebugWorkers: PropTypes.bool.isRequired,
       domain: PropTypes.string.isRequired,
-      workers: PropTypes.array.isRequired,
+      workers: Types.workerArray.isRequired,
     };
   }
 
   render() {
     const { canDebugWorkers, domain, workers } = this.props;
 
     // Filter out workers from other domains
     const domainWorkers = workers.filter(
--- a/devtools/client/application/src/moz.build
+++ b/devtools/client/application/src/moz.build
@@ -2,15 +2,16 @@
 # 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/.
 
 DIRS += [
     'actions',
     'components',
     'modules',
     'reducers',
+    'types',
 ]
 
 DevToolsModules(
 	'base.css',
     'constants.js',
     'create-store.js',
 )
new file mode 100644
--- /dev/null
+++ b/devtools/client/application/src/types/index.js
@@ -0,0 +1,18 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+const manifestTypes = require("./manifest");
+const routingTypes = require("./routing");
+const workersTypes = require("./service-workers");
+
+module.exports = Object.assign(
+  {},
+  {
+    ...manifestTypes,
+    ...routingTypes,
+    ...workersTypes,
+  }
+);
new file mode 100644
--- /dev/null
+++ b/devtools/client/application/src/types/manifest.js
@@ -0,0 +1,79 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
+
+const { MANIFEST_ISSUE_LEVELS } = require("../constants");
+const { MANIFEST_MEMBER_VALUE_TYPES } = require("../constants");
+
+const manifestIssue = {
+  level: PropTypes.oneOf(Object.values(MANIFEST_ISSUE_LEVELS)).isRequired,
+  message: PropTypes.string.isRequired,
+  // NOTE: we are currently ignoring the 'type' field that platform adds to errors
+};
+
+const manifestIssueArray = PropTypes.arrayOf(PropTypes.shape(manifestIssue));
+
+const manifestItemColor = {
+  label: PropTypes.string.isRequired,
+  value: PropTypes.string,
+};
+
+const manifestItemIcon = {
+  label: PropTypes.shape({
+    contentType: PropTypes.string,
+    sizes: PropTypes.string,
+  }).isRequired,
+  value: PropTypes.shape({
+    src: PropTypes.string.isRequired,
+    purpose: PropTypes.string.isRequired,
+  }).isRequired,
+};
+
+const manifestMemberColor = {
+  key: manifestItemColor.label,
+  value: manifestItemColor.value,
+  type: PropTypes.oneOf([MANIFEST_MEMBER_VALUE_TYPES.COLOR]),
+};
+
+const manifestMemberIcon = {
+  key: manifestItemIcon.label,
+  value: manifestItemIcon.value,
+  type: PropTypes.oneOf([MANIFEST_MEMBER_VALUE_TYPES.ICON]),
+};
+
+const manifestMemberString = {
+  key: PropTypes.string.isRequired,
+  value: PropTypes.string,
+  type: PropTypes.oneOf([MANIFEST_MEMBER_VALUE_TYPES.STRING]),
+};
+
+const manifest = {
+  // members
+  identity: PropTypes.arrayOf(PropTypes.shape(manifestMemberString)).isRequired,
+  presentation: PropTypes.arrayOf(
+    PropTypes.oneOfType([
+      PropTypes.shape(manifestMemberColor),
+      PropTypes.shape(manifestMemberString),
+    ])
+  ).isRequired,
+  icons: PropTypes.arrayOf(PropTypes.shape(manifestMemberIcon)).isRequired,
+  // validation issues
+  validation: manifestIssueArray.isRequired,
+  // misc
+  url: PropTypes.string.isRequired,
+};
+
+module.exports = {
+  // full manifest
+  manifest,
+  // specific manifest items
+  manifestItemColor,
+  manifestItemIcon,
+  // manifest issues
+  manifestIssue,
+  manifestIssueArray,
+};
new file mode 100644
--- /dev/null
+++ b/devtools/client/application/src/types/moz.build
@@ -0,0 +1,10 @@
+# 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/.
+
+DevToolsModules(
+    'index.js',
+    'manifest.js',
+    'routing.js',
+    'service-workers.js',
+)
new file mode 100644
--- /dev/null
+++ b/devtools/client/application/src/types/routing.js
@@ -0,0 +1,14 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
+const { PAGE_TYPES } = require("../constants");
+
+const page = PropTypes.oneOf(Object.values(PAGE_TYPES));
+
+module.exports = {
+  page,
+};
new file mode 100644
--- /dev/null
+++ b/devtools/client/application/src/types/service-workers.js
@@ -0,0 +1,25 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
+
+const worker = {
+  active: PropTypes.bool,
+  name: PropTypes.string.isRequired,
+  scope: PropTypes.string.isRequired,
+  lastUpdateTime: PropTypes.number.isRequired,
+  url: PropTypes.string.isRequired,
+  // registrationFront can be missing in e10s.
+  registrationFront: PropTypes.object,
+  workerTargetFront: PropTypes.object,
+};
+
+const workerArray = PropTypes.arrayOf(PropTypes.shape(worker));
+
+module.exports = {
+  worker,
+  workerArray,
+};
--- a/devtools/client/netmonitor/src/components/NetworkActionBar.js
+++ b/devtools/client/netmonitor/src/components/NetworkActionBar.js
@@ -63,17 +63,17 @@ class NetworkActionBar extends Component
       Tabbar(
         {
           activeTabId: selectedActionBarTabId,
           onSelect: id => selectActionBarTab(id),
         },
         showSearchPanel &&
           TabPanel(
             {
-              id: "network-action-bar-search",
+              id: PANELS.SEARCH,
               title: L10N.getStr("netmonitor.actionbar.search"),
               className: "network-action-bar-search",
             },
             SearchPanel({ connector })
           ),
         showBlockingPanel &&
           TabPanel(
             {
--- a/devtools/client/netmonitor/src/components/search/SearchPanel.js
+++ b/devtools/client/netmonitor/src/components/search/SearchPanel.js
@@ -7,16 +7,17 @@
 const {
   Component,
   createRef,
   createFactory,
 } = require("devtools/client/shared/vendor/react");
 const dom = require("devtools/client/shared/vendor/react-dom-factories");
 const { div, span } = dom;
 const Actions = require("devtools/client/netmonitor/src/actions/index");
+const { PANELS } = require("../../constants");
 const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
 const {
   connect,
 } = require("devtools/client/shared/redux/visibility-handler-connect");
 const TreeViewClass = require("devtools/client/shared/components/tree/TreeView");
 const TreeView = createFactory(TreeViewClass);
 const LabelCell = createFactory(
   require("devtools/client/shared/components/tree/LabelCell")
@@ -42,16 +43,17 @@ class SearchPanel extends Component {
       closeSearch: PropTypes.func.isRequired,
       search: PropTypes.func.isRequired,
       caseSensitive: PropTypes.bool,
       connector: PropTypes.object.isRequired,
       addSearchQuery: PropTypes.func.isRequired,
       query: PropTypes.string.isRequired,
       results: PropTypes.array,
       navigate: PropTypes.func.isRequired,
+      isDisplaying: PropTypes.bool.isRequired,
     };
   }
 
   constructor(props) {
     super(props);
 
     this.searchboxRef = createRef();
     this.renderValue = this.renderValue.bind(this);
@@ -61,16 +63,22 @@ class SearchPanel extends Component {
   }
 
   componentDidMount() {
     if (this.searchboxRef) {
       this.searchboxRef.current.focus();
     }
   }
 
+  componentDidUpdate(prevProps) {
+    if (this.props.isDisplaying && !prevProps.isDisplaying) {
+      this.searchboxRef.current.focus();
+    }
+  }
+
   onClickTreeRow(path, event, member) {
     if (member.object.parentResource) {
       this.props.navigate(member.object);
     }
   }
 
   /**
    * Custom TreeView label rendering. The search result
@@ -229,16 +237,17 @@ class SearchPanel extends Component {
 }
 
 module.exports = connect(
   state => ({
     query: state.search.query,
     caseSensitive: state.search.caseSensitive,
     results: state.search.results,
     ongoingSearch: state.search.ongoingSearch,
+    isDisplaying: state.ui.selectedActionBarTabId === PANELS.SEARCH,
     status: state.search.status,
   }),
   dispatch => ({
     closeSearch: () => dispatch(Actions.closeSearch()),
     openSearch: () => dispatch(Actions.openSearch()),
     search: () => dispatch(Actions.search()),
     clearSearchResults: () => dispatch(Actions.clearSearchResults()),
     addSearchQuery: query => dispatch(Actions.addSearchQuery(query)),
--- a/devtools/client/netmonitor/src/constants.js
+++ b/devtools/client/netmonitor/src/constants.js
@@ -203,16 +203,17 @@ const PANELS = {
   HEADERS: "headers",
   MESSAGES: "messages",
   PARAMS: "params",
   RESPONSE: "response",
   CACHE: "cache",
   SECURITY: "security",
   STACK_TRACE: "stack-trace",
   TIMINGS: "timings",
+  SEARCH: "network-action-bar-search",
   BLOCKING: "network-action-bar-blocked",
 };
 
 const RESPONSE_HEADERS = [
   "Cache-Control",
   "Connection",
   "Content-Encoding",
   "Content-Length",
--- a/devtools/server/actors/highlighters.js
+++ b/devtools/server/actors/highlighters.js
@@ -41,16 +41,22 @@ loader.lazyRequireGetter(
   true
 );
 loader.lazyRequireGetter(
   this,
   "BoxModelHighlighter",
   "devtools/server/actors/highlighters/box-model",
   true
 );
+loader.lazyRequireGetter(
+  this,
+  "BoxModelHighlighterObserver",
+  "devtools/server/actors/highlighters/box-model-observer",
+  true
+);
 
 const HIGHLIGHTER_PICKED_TIMER = 1000;
 const IS_OSX = Services.appinfo.OS === "Darwin";
 
 /**
  * The registration mechanism for highlighters provide a quick way to
  * have modular highlighters, instead of a hard coded list.
  * It allow us to split highlighers in sub modules, and add them dynamically
@@ -105,25 +111,26 @@ exports.register = register;
  * Other types of highlighter actors exist and can be accessed via the
  * InspectorActor's 'getHighlighterByType' method.
  */
 
 /**
  * The HighlighterActor class
  */
 exports.HighlighterActor = protocol.ActorClassWithSpec(highlighterSpec, {
-  initialize: function(inspector, autohide) {
+  initialize: function(inspector, autohide, useNewBoxModelHighlighter = false) {
     protocol.Actor.prototype.initialize.call(this, null);
 
     this._autohide = autohide;
     this._inspector = inspector;
     this._walker = this._inspector.walker;
     this._targetActor = this._inspector.targetActor;
     this._highlighterEnv = new HighlighterEnvironment();
     this._highlighterEnv.initFromTargetActor(this._targetActor);
+    this._useNewBoxModelHighlighter = useNewBoxModelHighlighter;
 
     this._onNavigate = this._onNavigate.bind(this);
 
     const doc = this._targetActor.window.document;
     // Only try to create the highlighter when the document is loaded,
     // otherwise, wait for the navigate event to fire.
     if (doc.documentElement && doc.readyState != "uninitialized") {
       this._createHighlighter();
@@ -142,16 +149,25 @@ exports.HighlighterActor = protocol.Acto
     return {
       actor: this.actorID,
     };
   },
 
   _createHighlighter: function() {
     this._isPreviousWindowXUL = isXUL(this._targetActor.window);
 
+    if (this._useNewBoxModelHighlighter) {
+      this._highlighter = new BoxModelHighlighterObserver(
+        this._highlighterEnv,
+        this.conn
+      );
+
+      return;
+    }
+
     if (!this._isPreviousWindowXUL) {
       this._highlighter = new BoxModelHighlighter(
         this._highlighterEnv,
         this._inspector
       );
     } else {
       this._highlighter = new SimpleOutlineHighlighter(this._highlighterEnv);
     }
--- a/devtools/server/actors/highlighters/auto-refresh.js
+++ b/devtools/server/actors/highlighters/auto-refresh.js
@@ -80,16 +80,17 @@ function AutoRefreshHighlighter(highligh
   this._winDimensions = getWindowDimensions(this.win);
   this._scroll = { x: this.win.pageXOffset, y: this.win.pageYOffset };
 
   this.update = this.update.bind(this);
 }
 
 AutoRefreshHighlighter.prototype = {
   _ignoreZoom: false,
+  _ignoreScroll: false,
 
   /**
    * Window corresponding to the current highlighterEnv. When replaying, this
    * will be the window against which the server is running, which is different
    * from the window containing the target content.
    */
   get win() {
     if (!this.highlighterEnv) {
@@ -189,17 +190,17 @@ AutoRefreshHighlighter.prototype = {
   _updateAdjustedQuads: function() {
     this.currentQuads = {};
 
     for (const region of BOX_MODEL_REGIONS) {
       this.currentQuads[region] = getAdjustedQuads(
         this.contentWindow,
         this.currentNode,
         region,
-        { ignoreZoom: this._ignoreZoom }
+        { ignoreScroll: this._ignoreScroll, ignoreZoom: this._ignoreZoom }
       );
     }
   },
 
   /**
    * Update the knowledge we have of the current node's boxquads and return true
    * if any of the points x/y or bounds have change since.
    * @return {Boolean}
new file mode 100644
--- /dev/null
+++ b/devtools/server/actors/highlighters/box-model-observer.js
@@ -0,0 +1,312 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+const { DebuggerServer } = require("devtools/server/debugger-server");
+const { AutoRefreshHighlighter } = require("./auto-refresh");
+const {
+  getBindingElementAndPseudo,
+  hasPseudoClassLock,
+  isNodeValid,
+} = require("./utils/markup");
+const { PSEUDO_CLASSES } = require("devtools/shared/css/constants");
+const { getCurrentZoom } = require("devtools/shared/layout/utils");
+const {
+  getNodeDisplayName,
+  getNodeGridFlexType,
+} = require("devtools/server/actors/inspector/utils");
+const nodeConstants = require("devtools/shared/dom-node-constants");
+const { LocalizationHelper } = require("devtools/shared/l10n");
+const STRINGS_URI = "devtools/shared/locales/highlighters.properties";
+const L10N = new LocalizationHelper(STRINGS_URI);
+const {
+  BOX_MODEL_REGIONS,
+  BoxModelHighlighterRenderer,
+} = require("devtools/server/actors/highlighters/box-model-renderer");
+
+/**
+ * The BoxModelHighlighterObserver observes the coordinates of a node and communicates
+ * with the BoxModelHighlighterRenderer which draws the box model regions on top the a
+ * node.
+ *
+ * When in the context of the content toolbox, the observer lives in
+ * the child process (aka content process) and the renderer is set up in the parent
+ * process. They communicate via messages.
+ *
+ * When in the context of the browser toolbox, both observer and renderer live in the
+ * parent process. They communicate by direct reference.
+ */
+class BoxModelHighlighterObserver extends AutoRefreshHighlighter {
+  constructor(highlighterEnv, conn) {
+    super(highlighterEnv);
+    this.conn = conn;
+    this._ignoreScroll = true;
+    this.typeName = this.constructor.name.replace("Observer", "");
+
+    if (DebuggerServer.isInChildProcess) {
+      // eslint-disable-next-line no-restricted-properties
+      this.conn.setupInParent({
+        module: "devtools/server/actors/highlighters/box-model-renderer",
+        setupParent: "setupParentProcess",
+      });
+    } else {
+      this.renderer = new BoxModelHighlighterRenderer();
+    }
+
+    /**
+     * Optionally customize each region's fill color by adding an entry to the
+     * regionFill property: `highlighter.regionFill.margin = "red";
+     */
+    this.regionFill = {};
+
+    this.onPageHide = this.onPageHide.bind(this);
+    this.onWillNavigate = this.onWillNavigate.bind(this);
+
+    this.highlighterEnv.on("will-navigate", this.onWillNavigate);
+
+    const { pageListenerTarget } = highlighterEnv;
+    pageListenerTarget.addEventListener("pagehide", this.onPageHide);
+  }
+
+  /**
+   * Destroy the nodes. Remove listeners.
+   */
+  destroy() {
+    this.highlighterEnv.off("will-navigate", this.onWillNavigate);
+
+    const { pageListenerTarget } = this.highlighterEnv;
+    if (pageListenerTarget) {
+      pageListenerTarget.removeEventListener("pagehide", this.onPageHide);
+    }
+
+    if (DebuggerServer.isInChildProcess) {
+      this.postMessage("destroy");
+    } else {
+      this.renderer.destroy();
+      this.renderer = null;
+    }
+
+    AutoRefreshHighlighter.prototype.destroy.call(this);
+  }
+
+  get messageManager() {
+    if (!DebuggerServer.isInChildProcess) {
+      throw new Error(
+        "Message manager should only be used when actor is in child process."
+      );
+    }
+
+    return this.conn.parentMessageManager;
+  }
+
+  postMessage(topic, data = {}) {
+    this._msgName = `debug:${this.conn.prefix}${this.typeName}`;
+    this.messageManager.sendAsyncMessage(this._msgName, { topic, data });
+  }
+
+  /**
+   * Tell the renderer to update the markup of the box model highlighter.
+   *
+   * @param {Object} data
+   *        Object with data about the node position, type and its attributes.
+   *        @see BoxModelHighlighterRenderer.render()
+   */
+  render(data) {
+    if (DebuggerServer.isInChildProcess) {
+      this.postMessage("render", data);
+    } else {
+      this.renderer.render(data);
+    }
+  }
+
+  /**
+   * Override the AutoRefreshHighlighter's _isNodeValid method to also return true for
+   * text nodes since these can also be highlighted.
+   * @param {DOMNode} node
+   * @return {Boolean}
+   */
+  _isNodeValid(node) {
+    return (
+      node && (isNodeValid(node) || isNodeValid(node, nodeConstants.TEXT_NODE))
+    );
+  }
+
+  /**
+   * Show the highlighter on a given node
+   */
+  _show() {
+    if (!BOX_MODEL_REGIONS.includes(this.options.region)) {
+      this.options.region = "content";
+    }
+
+    const shown = this._update();
+    this._trackMutations();
+    return shown;
+  }
+
+  /**
+   * Track the current node markup mutations so that the node info bar can be
+   * updated to reflects the node's attributes
+   */
+  _trackMutations() {
+    if (isNodeValid(this.currentNode)) {
+      const win = this.currentNode.ownerGlobal;
+      this.currentNodeObserver = new win.MutationObserver(this.update);
+      this.currentNodeObserver.observe(this.currentNode, { attributes: true });
+    }
+  }
+
+  _untrackMutations() {
+    if (isNodeValid(this.currentNode) && this.currentNodeObserver) {
+      this.currentNodeObserver.disconnect();
+      this.currentNodeObserver = null;
+    }
+  }
+
+  /**
+   * Update the highlighter on the current highlighted node (the one that was
+   * passed as an argument to show(node)).
+   * Should be called whenever node size or attributes change
+   */
+  _update() {
+    const node = this.currentNode;
+    let shown = false;
+
+    if (this._nodeNeedsHighlighting()) {
+      // Tell the renderer to update the highlighter markup and provide it
+      // with options, metadata and coordinates of the target node.
+      const data = {
+        ...this.options,
+        currentQuads: { ...this.currentQuads },
+        regionFill: { ...this.regionFill },
+        nodeData: this._getNodeData(),
+
+        showBoxModel: true,
+        showInfoBar:
+          !this.options.hideInfoBar &&
+          (node.nodeType === node.ELEMENT_NODE ||
+            node.nodeType === node.TEXT_NODE),
+      };
+      this.render(data);
+      shown = true;
+    } else {
+      // Nothing to highlight (0px rectangle like a <script> tag for instance)
+      this._hide();
+    }
+
+    return shown;
+  }
+
+  /**
+   * Hide the highlighter, the outline and the infobar.
+   */
+  _hide() {
+    this._untrackMutations();
+
+    // Tell the renderer to hide the highlighter markup.
+    this.render({
+      showBoxModel: false,
+      showInfoBar: false,
+    });
+  }
+
+  /**
+   * Can the current node be highlighted? Does it have quads.
+   * @return {Boolean}
+   */
+  _nodeNeedsHighlighting() {
+    return (
+      this.currentQuads.margin.length ||
+      this.currentQuads.border.length ||
+      this.currentQuads.padding.length ||
+      this.currentQuads.content.length
+    );
+  }
+
+  /**
+   * Get data from the highlighted node to populate the infobar tooltip with
+   * information such as the node's id, class names, grid or flex item type, etc.
+   *
+   * @return {Object|null} Information about the highlighted node
+   */
+  _getNodeData() {
+    if (!this.currentNode) {
+      return null;
+    }
+
+    const { bindingElement: node, pseudo } = getBindingElementAndPseudo(
+      this.currentNode
+    );
+
+    // Update the tag, id, classes, pseudo-classes and dimensions
+    const displayName = getNodeDisplayName(node);
+
+    const id = node.id ? "#" + node.id : "";
+
+    const classList = (node.classList || []).length
+      ? "." + [...node.classList].join(".")
+      : "";
+
+    let pseudos = this._getPseudoClasses(node).join("");
+    if (pseudo) {
+      // Display :after as ::after
+      pseudos += ":" + pseudo;
+    }
+
+    const zoom = getCurrentZoom(this.win);
+
+    const { grid: gridType, flex: flexType } = getNodeGridFlexType(node);
+    const gridLayoutTextType = this._getLayoutTextType("gridType", gridType);
+    const flexLayoutTextType = this._getLayoutTextType("flexType", flexType);
+
+    return {
+      classList,
+      displayName,
+      flexLayoutTextType,
+      gridLayoutTextType,
+      id,
+      pseudos,
+      zoom,
+    };
+  }
+
+  _getLayoutTextType(layoutTypeKey, { isContainer, isItem }) {
+    if (!isContainer && !isItem) {
+      return "";
+    }
+    if (isContainer && !isItem) {
+      return L10N.getStr(`${layoutTypeKey}.container`);
+    }
+    if (!isContainer && isItem) {
+      return L10N.getStr(`${layoutTypeKey}.item`);
+    }
+    return L10N.getStr(`${layoutTypeKey}.dual`);
+  }
+
+  _getPseudoClasses(node) {
+    if (node.nodeType !== nodeConstants.ELEMENT_NODE) {
+      // hasPseudoClassLock can only be used on Elements.
+      return [];
+    }
+
+    return PSEUDO_CLASSES.filter(pseudo => hasPseudoClassLock(node, pseudo));
+  }
+
+  onPageHide({ target }) {
+    // If a pagehide event is triggered for current window's highlighter, hide the
+    // highlighter.
+    if (target.defaultView === this.win) {
+      this.hide();
+    }
+  }
+
+  onWillNavigate({ isTopLevel }) {
+    if (isTopLevel) {
+      this.hide();
+    }
+  }
+}
+
+exports.BoxModelHighlighterObserver = BoxModelHighlighterObserver;
copy from devtools/server/actors/highlighters/box-model.js
copy to devtools/server/actors/highlighters/box-model-renderer.js
--- a/devtools/server/actors/highlighters/box-model.js
+++ b/devtools/server/actors/highlighters/box-model-renderer.js
@@ -1,74 +1,40 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
-const { AutoRefreshHighlighter } = require("./auto-refresh");
 const {
-  CanvasFrameAnonymousContentHelper,
   createNode,
   createSVGNode,
-  getBindingElementAndPseudo,
-  hasPseudoClassLock,
-  isNodeValid,
   moveInfobar,
-} = require("./utils/markup");
-const { PSEUDO_CLASSES } = require("devtools/shared/css/constants");
+} = require("devtools/server/actors/highlighters/utils/markup");
+
 const {
-  getCurrentZoom,
-  setIgnoreLayoutChanges,
-} = require("devtools/shared/layout/utils");
-const {
-  getNodeDisplayName,
-  getNodeGridFlexType,
-} = require("devtools/server/actors/inspector/utils");
-const nodeConstants = require("devtools/shared/dom-node-constants");
-const { LocalizationHelper } = require("devtools/shared/l10n");
-const STRINGS_URI = "devtools/shared/locales/highlighters.properties";
-const L10N = new LocalizationHelper(STRINGS_URI);
+  HighlighterRenderer,
+} = require("devtools/server/actors/highlighters/highlighter-renderer");
 
 // Note that the order of items in this array is important because it is used
 // for drawing the BoxModelHighlighter's path elements correctly.
 const BOX_MODEL_REGIONS = ["margin", "border", "padding", "content"];
+exports.BOX_MODEL_REGIONS = BOX_MODEL_REGIONS;
+
 const BOX_MODEL_SIDES = ["top", "right", "bottom", "left"];
-// Width of boxmodelhighlighter guides
+// Width of BoxModelHighlighter guides
 const GUIDE_STROKE_WIDTH = 1;
 
 /**
- * The BoxModelHighlighter draws the box model regions on top of a node.
+ * The BoxModelHighlighterRenderer receives node coordinates from the
+ * BoxModelHighlighterObserver and draws the box model regions on top of a node.
  * If the node is a block box, then each region will be displayed as 1 polygon.
  * If the node is an inline box though, each region may be represented by 1 or
  * more polygons, depending on how many line boxes the inline element has.
  *
- * Usage example:
- *
- * let h = new BoxModelHighlighter(env);
- * h.show(node, options);
- * h.hide();
- * h.destroy();
- *
- * @param {String} options.region
- *        Specifies the region that the guides should outline:
- *          "content" (default), "padding", "border" or "margin".
- * @param {Boolean} options.hideGuides
- *        Defaults to false
- * @param {Boolean} options.hideInfoBar
- *        Defaults to false
- * @param {String} options.showOnly
- *        If set, only this region will be highlighted. Use with onlyRegionArea
- *        to only highlight the area of the region:
- *        "content", "padding", "border" or "margin"
- * @param {Boolean} options.onlyRegionArea
- *        This can be set to true to make each region's box only highlight the
- *        area of the corresponding region rather than the area of nested
- *        regions too. This is useful when used with showOnly.
- *
  * Structure:
  * <div class="highlighter-container">
  *   <div class="box-model-root">
  *     <svg class="box-model-elements" hidden="true">
  *       <g class="box-model-regions">
  *         <path class="box-model-margin" points="..." />
  *         <path class="box-model-border" points="..." />
  *         <path class="box-model-padding" points="..." />
@@ -91,343 +57,133 @@ const GUIDE_STROKE_WIDTH = 1;
  *           <span class="box-model-infobar-flex-type">Flex Type</span>
  *         </div>
  *       </div>
  *       <div class="box-model-infobar-arrow box-model-infobar-arrow-bottom"/>
  *     </div>
  *   </div>
  * </div>
  */
-class BoxModelHighlighter extends AutoRefreshHighlighter {
-  constructor(highlighterEnv) {
-    super(highlighterEnv);
-
+class BoxModelHighlighterRenderer extends HighlighterRenderer {
+  constructor(mm, prefix) {
+    super();
+    // @override Highlighter type name.
+    this.typeName = this.constructor.name.replace("Renderer", "");
+    // String used to prefix ids and classnames of highlighter nodes.
     this.ID_CLASS_PREFIX = "box-model-";
 
-    this.markup = new CanvasFrameAnonymousContentHelper(
-      this.highlighterEnv,
-      this._buildMarkup.bind(this)
-    );
-
-    /**
-     * Optionally customize each region's fill color by adding an entry to the
-     * regionFill property: `highlighter.regionFill.margin = "red";
-     */
-    this.regionFill = {};
-
-    this.onPageHide = this.onPageHide.bind(this);
-    this.onWillNavigate = this.onWillNavigate.bind(this);
-
-    this.highlighterEnv.on("will-navigate", this.onWillNavigate);
-
-    const { pageListenerTarget } = highlighterEnv;
-    pageListenerTarget.addEventListener("pagehide", this.onPageHide);
-  }
-
-  _buildMarkup() {
-    const doc = this.win.document;
-
-    const highlighterContainer = doc.createElement("div");
-    highlighterContainer.setAttribute("role", "presentation");
-    highlighterContainer.className = "highlighter-container box-model";
-
-    // Build the root wrapper, used to adapt to the page zoom.
-    const rootWrapper = createNode(this.win, {
-      parent: highlighterContainer,
-      attributes: {
-        id: "root",
-        class: "root",
-        role: "presentation",
-      },
-      prefix: this.ID_CLASS_PREFIX,
-    });
-
-    // Building the SVG element with its polygons and lines
-
-    const svg = createSVGNode(this.win, {
-      nodeType: "svg",
-      parent: rootWrapper,
-      attributes: {
-        id: "elements",
-        width: "100%",
-        height: "100%",
-        hidden: "true",
-        role: "presentation",
-      },
-      prefix: this.ID_CLASS_PREFIX,
-    });
-
-    const regions = createSVGNode(this.win, {
-      nodeType: "g",
-      parent: svg,
-      attributes: {
-        class: "regions",
-        role: "presentation",
-      },
-      prefix: this.ID_CLASS_PREFIX,
-    });
-
-    for (const region of BOX_MODEL_REGIONS) {
-      createSVGNode(this.win, {
-        nodeType: "path",
-        parent: regions,
-        attributes: {
-          class: region,
-          id: region,
-          role: "presentation",
-        },
-        prefix: this.ID_CLASS_PREFIX,
-      });
-    }
-
-    for (const side of BOX_MODEL_SIDES) {
-      createSVGNode(this.win, {
-        nodeType: "line",
-        parent: svg,
-        attributes: {
-          class: "guide-" + side,
-          id: "guide-" + side,
-          "stroke-width": GUIDE_STROKE_WIDTH,
-          role: "presentation",
-        },
-        prefix: this.ID_CLASS_PREFIX,
-      });
+    // If there is a message manager and connection prefix, it means the observer lives
+    // in the content process so we need to setup a communication system with it.
+    // Otherwise, both renderer and observer live in the parent process and there is no
+    // need for a message-based communication system.
+    if (mm && prefix) {
+      this.setMessageManager(mm, prefix);
+      this.init(false);
+    } else {
+      this.init(true);
     }
-
-    // Building the nodeinfo bar markup
-
-    const infobarContainer = createNode(this.win, {
-      parent: rootWrapper,
-      attributes: {
-        class: "infobar-container",
-        id: "infobar-container",
-        position: "top",
-        hidden: "true",
-      },
-      prefix: this.ID_CLASS_PREFIX,
-    });
-
-    const infobar = createNode(this.win, {
-      parent: infobarContainer,
-      attributes: {
-        class: "infobar",
-      },
-      prefix: this.ID_CLASS_PREFIX,
-    });
-
-    const texthbox = createNode(this.win, {
-      parent: infobar,
-      attributes: {
-        class: "infobar-text",
-      },
-      prefix: this.ID_CLASS_PREFIX,
-    });
-    createNode(this.win, {
-      nodeType: "span",
-      parent: texthbox,
-      attributes: {
-        class: "infobar-tagname",
-        id: "infobar-tagname",
-      },
-      prefix: this.ID_CLASS_PREFIX,
-    });
-    createNode(this.win, {
-      nodeType: "span",
-      parent: texthbox,
-      attributes: {
-        class: "infobar-id",
-        id: "infobar-id",
-      },
-      prefix: this.ID_CLASS_PREFIX,
-    });
-    createNode(this.win, {
-      nodeType: "span",
-      parent: texthbox,
-      attributes: {
-        class: "infobar-classes",
-        id: "infobar-classes",
-      },
-      prefix: this.ID_CLASS_PREFIX,
-    });
-    createNode(this.win, {
-      nodeType: "span",
-      parent: texthbox,
-      attributes: {
-        class: "infobar-pseudo-classes",
-        id: "infobar-pseudo-classes",
-      },
-      prefix: this.ID_CLASS_PREFIX,
-    });
-    createNode(this.win, {
-      nodeType: "span",
-      parent: texthbox,
-      attributes: {
-        class: "infobar-dimensions",
-        id: "infobar-dimensions",
-      },
-      prefix: this.ID_CLASS_PREFIX,
-    });
-
-    createNode(this.win, {
-      nodeType: "span",
-      parent: texthbox,
-      attributes: {
-        class: "infobar-grid-type",
-        id: "infobar-grid-type",
-      },
-      prefix: this.ID_CLASS_PREFIX,
-    });
-
-    createNode(this.win, {
-      nodeType: "span",
-      parent: texthbox,
-      attributes: {
-        class: "infobar-flex-type",
-        id: "infobar-flex-type",
-      },
-      prefix: this.ID_CLASS_PREFIX,
-    });
-
-    return highlighterContainer;
   }
 
   /**
-   * Destroy the nodes. Remove listeners.
+   * Update the rendering of the box model highlighter, inforbar and guides for a node
+   * using quad coordinates, node information and options from the BoxModelHighlighterObserver.
+   *
+   * @override
+   *
+   * @param {Object} data
+   *        Information used for rendering the box model highlighter, infobar and guides.
+   *
+   * @param {Boolean} data.showBoxModel
+   *        Whether to show the box model highlighter.
+   *        Defaults to false
+   * @param {Boolean} data.showInfoBar
+   *        Whether to show the tooltip with the node's dimensions, class names, id, etc.
+   *        Defaults to false
+   * @param {Object} data.currentQuads
+   *        Collection of quad coordinates for a node's box model regions keyed by region
+   * @param {Object} data.regionFill
+   *        Optional collection of custom fill colors for box model regions keyed by
+   *        region. Ex: data.regionFill.margin = "red"
+   *
+   * @param {Object} data.nodeData
+   *        Collection of information about a node used for the infobar tooltip.
+   * @param {String} data.nodeData.classList
+   *        String with list of class names separated by a dot (.) insead of space.
+   * @param {String} data.nodeData.displayName
+   *        Node tag name.
+   * @param {String} data.nodeData.flexLayoutTextType
+   *        Flex item or flex container.
+   * @param {String} data.nodeData.gridLayoutTextType
+   *        Grid idem or flex container.
+   * @param {String} data.nodeData.id
+   *        Node id attribute.
+   * @param {String} data.nodeData.pseudos
+   *        Pseudo-element type if the node is a ::before/::after pseudo-element
+   * @param {Number} data.nodeData.zoom
+   *        Zoom level of the content page where the node exists.
+   *
+   * @param {Object} data.options
+   *        Collection of optional overrides in the highlighter rendering.
+   * @param {String} data.options.region
+   *        Specifies the region that the guides should outline:
+   *        "content" (default), "padding", "border" or "margin".
+   * @param {Boolean} data.options.hideGuides
+   *        Defaults to false
+   * @param {Boolean} data.options.hideInfoBar
+   *        Defaults to false
+   * @param {String} data.options.showOnly
+   *        If set, only this region will be highlighted. Use with onlyRegionArea
+   *        to only highlight the area of the region:
+   *        "content", "padding", "border" or "margin"
+   * @param {Boolean} data.options.onlyRegionArea
+   *        This can be set to true to make each region's box only highlight the
+   *        area of the corresponding region rather than the area of nested
+   *        regions too. This is useful when used with showOnly.
    */
-  destroy() {
-    this.highlighterEnv.off("will-navigate", this.onWillNavigate);
+  render(data = {}) {
+    this.currentQuads = data.currentQuads || {};
+    this.nodeData = data.nodeData || null;
+    this.options = data.options || {};
+    this.regionFill = data.regionFill || {};
 
-    const { pageListenerTarget } = this.highlighterEnv;
-    if (pageListenerTarget) {
-      pageListenerTarget.removeEventListener("pagehide", this.onPageHide);
+    const { showBoxModel = false, showInfoBar = false } = data;
+
+    if (!showBoxModel) {
+      this._hideBoxModel();
+      this._hideInfobar();
+      return;
     }
 
-    this.markup.destroy();
+    this._updateBoxModel();
+    this._showBoxModel();
 
-    AutoRefreshHighlighter.prototype.destroy.call(this);
+    if (!showInfoBar) {
+      this._hideInfobar();
+    } else {
+      this._updateInfobar();
+      this._showInfobar();
+    }
   }
 
   getElement(id) {
     return this.markup.getElement(this.ID_CLASS_PREFIX + id);
   }
 
   /**
-   * Override the AutoRefreshHighlighter's _isNodeValid method to also return true for
-   * text nodes since these can also be highlighted.
-   * @param {DOMNode} node
-   * @return {Boolean}
-   */
-  _isNodeValid(node) {
-    return (
-      node && (isNodeValid(node) || isNodeValid(node, nodeConstants.TEXT_NODE))
-    );
-  }
-
-  /**
-   * Show the highlighter on a given node
-   */
-  _show() {
-    if (!BOX_MODEL_REGIONS.includes(this.options.region)) {
-      this.options.region = "content";
-    }
-
-    const shown = this._update();
-    this._trackMutations();
-    return shown;
-  }
-
-  /**
-   * Track the current node markup mutations so that the node info bar can be
-   * updated to reflects the node's attributes
-   */
-  _trackMutations() {
-    if (isNodeValid(this.currentNode)) {
-      const win = this.currentNode.ownerGlobal;
-      this.currentNodeObserver = new win.MutationObserver(this.update);
-      this.currentNodeObserver.observe(this.currentNode, { attributes: true });
-    }
-  }
-
-  _untrackMutations() {
-    if (isNodeValid(this.currentNode) && this.currentNodeObserver) {
-      this.currentNodeObserver.disconnect();
-      this.currentNodeObserver = null;
-    }
-  }
-
-  /**
-   * Update the highlighter on the current highlighted node (the one that was
-   * passed as an argument to show(node)).
-   * Should be called whenever node size or attributes change
-   */
-  _update() {
-    const node = this.currentNode;
-    let shown = false;
-    setIgnoreLayoutChanges(true);
-
-    if (this._updateBoxModel()) {
-      // Show the infobar only if configured to do so and the node is an element or a text
-      // node.
-      if (
-        !this.options.hideInfoBar &&
-        (node.nodeType === node.ELEMENT_NODE ||
-          node.nodeType === node.TEXT_NODE)
-      ) {
-        this._showInfobar();
-      } else {
-        this._hideInfobar();
-      }
-      this._showBoxModel();
-      shown = true;
-    } else {
-      // Nothing to highlight (0px rectangle like a <script> tag for instance)
-      this._hide();
-    }
-
-    setIgnoreLayoutChanges(
-      false,
-      this.highlighterEnv.window.document.documentElement
-    );
-
-    return shown;
-  }
-
-  _scrollUpdate() {
-    this._moveInfobar();
-  }
-
-  /**
-   * Hide the highlighter, the outline and the infobar.
-   */
-  _hide() {
-    setIgnoreLayoutChanges(true);
-
-    this._untrackMutations();
-    this._hideBoxModel();
-    this._hideInfobar();
-
-    setIgnoreLayoutChanges(
-      false,
-      this.highlighterEnv.window.document.documentElement
-    );
-  }
-
-  /**
    * Hide the infobar
    */
   _hideInfobar() {
     this.getElement("infobar-container").setAttribute("hidden", "true");
   }
 
   /**
    * Show the infobar
    */
   _showInfobar() {
     this.getElement("infobar-container").removeAttribute("hidden");
-    this._updateInfobar();
   }
 
   /**
    * Hide the box model
    */
   _hideBoxModel() {
     this.getElement("elements").setAttribute("hidden", "true");
   }
@@ -435,16 +191,66 @@ class BoxModelHighlighter extends AutoRe
   /**
    * Show the box model
    */
   _showBoxModel() {
     this.getElement("elements").removeAttribute("hidden");
   }
 
   /**
+   * Update the box model
+   */
+  _updateBoxModel() {
+    const options = this.options;
+    options.region = options.region || "content";
+
+    for (let i = 0; i < BOX_MODEL_REGIONS.length; i++) {
+      const boxType = BOX_MODEL_REGIONS[i];
+      const nextBoxType = BOX_MODEL_REGIONS[i + 1];
+      const box = this.getElement(boxType);
+
+      if (this.regionFill[boxType]) {
+        box.setAttribute("style", "fill:" + this.regionFill[boxType]);
+      } else {
+        box.setAttribute("style", "");
+      }
+
+      // Highlight all quads for this region by setting the "d" attribute of the
+      // corresponding <path>.
+      const path = [];
+      for (let j = 0; j < this.currentQuads[boxType].length; j++) {
+        const boxQuad = this.currentQuads[boxType][j];
+        const nextBoxQuad = this.currentQuads[nextBoxType]
+          ? this.currentQuads[nextBoxType][j]
+          : null;
+        path.push(this._getBoxPathCoordinates(boxQuad, nextBoxQuad));
+      }
+
+      box.setAttribute("d", path.join(" "));
+      box.removeAttribute("faded");
+
+      // If showOnly is defined, either hide the other regions, or fade them out
+      // if onlyRegionArea is set too.
+      if (options.showOnly && options.showOnly !== boxType) {
+        if (options.onlyRegionArea) {
+          box.setAttribute("faded", "true");
+        } else {
+          box.removeAttribute("d");
+        }
+      }
+
+      if (boxType === options.region && !options.hideGuides) {
+        this._showGuides(boxType);
+      } else if (options.hideGuides) {
+        this._hideGuides();
+      }
+    }
+  }
+
+  /**
    * Calculate an outer quad based on the quads returned by getAdjustedQuads.
    * The BoxModelHighlighter may highlight more than one boxes, so in this case
    * create a new quad that "contains" all of these quads.
    * This is useful to position the guides and infobar.
    * This may happen if the BoxModelHighlighter is used to highlight an inline
    * element that spans line breaks.
    * @param {String} region The box-model region to get the outer quad for.
    * @return {Object} A quad-like object {p1,p2,p3,p4,bounds}
@@ -490,80 +296,16 @@ class BoxModelHighlighter extends AutoRe
     quad.bounds.x = quad.bounds.left;
     quad.bounds.y = quad.bounds.top;
     quad.bounds.width = quad.bounds.right - quad.bounds.left;
     quad.bounds.height = quad.bounds.bottom - quad.bounds.top;
 
     return quad;
   }
 
-  /**
-   * Update the box model as per the current node.
-   *
-   * @return {boolean}
-   *         True if the current node has a box model to be highlighted
-   */
-  _updateBoxModel() {
-    const options = this.options;
-    options.region = options.region || "content";
-
-    if (!this._nodeNeedsHighlighting()) {
-      this._hideBoxModel();
-      return false;
-    }
-
-    for (let i = 0; i < BOX_MODEL_REGIONS.length; i++) {
-      const boxType = BOX_MODEL_REGIONS[i];
-      const nextBoxType = BOX_MODEL_REGIONS[i + 1];
-      const box = this.getElement(boxType);
-
-      if (this.regionFill[boxType]) {
-        box.setAttribute("style", "fill:" + this.regionFill[boxType]);
-      } else {
-        box.setAttribute("style", "");
-      }
-
-      // Highlight all quads for this region by setting the "d" attribute of the
-      // corresponding <path>.
-      const path = [];
-      for (let j = 0; j < this.currentQuads[boxType].length; j++) {
-        const boxQuad = this.currentQuads[boxType][j];
-        const nextBoxQuad = this.currentQuads[nextBoxType]
-          ? this.currentQuads[nextBoxType][j]
-          : null;
-        path.push(this._getBoxPathCoordinates(boxQuad, nextBoxQuad));
-      }
-
-      box.setAttribute("d", path.join(" "));
-      box.removeAttribute("faded");
-
-      // If showOnly is defined, either hide the other regions, or fade them out
-      // if onlyRegionArea is set too.
-      if (options.showOnly && options.showOnly !== boxType) {
-        if (options.onlyRegionArea) {
-          box.setAttribute("faded", "true");
-        } else {
-          box.removeAttribute("d");
-        }
-      }
-
-      if (boxType === options.region && !options.hideGuides) {
-        this._showGuides(boxType);
-      } else if (options.hideGuides) {
-        this._hideGuides();
-      }
-    }
-
-    // Un-zoom the root wrapper if the page was zoomed.
-    const rootId = this.ID_CLASS_PREFIX + "elements";
-    this.markup.scaleRootElement(this.currentNode, rootId);
-
-    return true;
-  }
-
   _getBoxPathCoordinates(boxQuad, nextBoxQuad) {
     const { p1, p2, p3, p4 } = boxQuad;
 
     let path;
     if (!nextBoxQuad || !this.options.onlyRegionArea) {
       // If this is the content box (inner-most box) or if we're not being asked
       // to highlight only region areas, then draw a simple rectangle.
       path =
@@ -639,31 +381,18 @@ class BoxModelHighlighter extends AutoRe
         np1.x +
         "," +
         np1.y;
     }
 
     return path;
   }
 
-  /**
-   * Can the current node be highlighted? Does it have quads.
-   * @return {Boolean}
-   */
-  _nodeNeedsHighlighting() {
-    return (
-      this.currentQuads.margin.length ||
-      this.currentQuads.border.length ||
-      this.currentQuads.padding.length ||
-      this.currentQuads.content.length
-    );
-  }
-
   _getOuterBounds() {
-    for (const region of ["margin", "border", "padding", "content"]) {
+    for (const region of BOX_MODEL_REGIONS) {
       const quad = this._getOuterQuad(region);
 
       if (!quad) {
         // Invisible element such as a script tag.
         break;
       }
 
       const { bottom, height, left, right, top, width, x, y } = quad.bounds;
@@ -763,113 +492,261 @@ class BoxModelHighlighter extends AutoRe
     }
 
     guide.removeAttribute("hidden");
 
     return true;
   }
 
   /**
+   * Move the Infobar to the right place in the highlighter.
+   */
+  _moveInfobar() {
+    const bounds = this._getOuterBounds();
+    const container = this.getElement("infobar-container");
+
+    moveInfobar(container, bounds, this.iframe.contentWindow);
+  }
+
+  /**
    * Update node information (displayName#id.class)
    */
   _updateInfobar() {
-    if (!this.currentNode) {
+    if (!this.nodeData) {
       return;
     }
 
-    const { bindingElement: node, pseudo } = getBindingElementAndPseudo(
-      this.currentNode
-    );
-
-    // Update the tag, id, classes, pseudo-classes and dimensions
-    const displayName = getNodeDisplayName(node);
-
-    const id = node.id ? "#" + node.id : "";
-
-    const classList = (node.classList || []).length
-      ? "." + [...node.classList].join(".")
-      : "";
-
-    let pseudos = this._getPseudoClasses(node).join("");
-    if (pseudo) {
-      // Display :after as ::after
-      pseudos += ":" + pseudo;
-    }
+    const {
+      classList,
+      displayName,
+      flexLayoutTextType,
+      gridLayoutTextType,
+      id,
+      pseudos,
+      zoom,
+    } = this.nodeData;
 
     // We want to display the original `width` and `height`, instead of the ones affected
     // by any zoom. Since the infobar can be displayed also for text nodes, we can't
     // access the computed style for that, and this is why we recalculate them here.
-    const zoom = getCurrentZoom(this.win);
     const quad = this._getOuterQuad("border");
-
     if (!quad) {
       return;
     }
 
     const { width, height } = quad.bounds;
     const dim =
       parseFloat((width / zoom).toPrecision(6)) +
       " \u00D7 " +
       parseFloat((height / zoom).toPrecision(6));
 
-    const { grid: gridType, flex: flexType } = getNodeGridFlexType(node);
-    const gridLayoutTextType = this._getLayoutTextType("gridType", gridType);
-    const flexLayoutTextType = this._getLayoutTextType("flexType", flexType);
-
     this.getElement("infobar-tagname").setTextContent(displayName);
     this.getElement("infobar-id").setTextContent(id);
     this.getElement("infobar-classes").setTextContent(classList);
     this.getElement("infobar-pseudo-classes").setTextContent(pseudos);
     this.getElement("infobar-dimensions").setTextContent(dim);
     this.getElement("infobar-grid-type").setTextContent(gridLayoutTextType);
     this.getElement("infobar-flex-type").setTextContent(flexLayoutTextType);
 
     this._moveInfobar();
   }
 
-  _getLayoutTextType(layoutTypeKey, { isContainer, isItem }) {
-    if (!isContainer && !isItem) {
-      return "";
-    }
-    if (isContainer && !isItem) {
-      return L10N.getStr(`${layoutTypeKey}.container`);
-    }
-    if (!isContainer && isItem) {
-      return L10N.getStr(`${layoutTypeKey}.item`);
-    }
-    return L10N.getStr(`${layoutTypeKey}.dual`);
-  }
+  /**
+   * @override
+   */
+  _buildMarkup() {
+    const doc = this.win.document;
+
+    const highlighterContainer = doc.createElement("div");
+    highlighterContainer.setAttribute("role", "presentation");
+    highlighterContainer.className = "highlighter-container box-model";
+
+    // Build the root wrapper, used to adapt to the page zoom.
+    const rootWrapper = createNode(this.win, {
+      parent: highlighterContainer,
+      attributes: {
+        id: "root",
+        class: "root",
+        role: "presentation",
+      },
+      prefix: this.ID_CLASS_PREFIX,
+    });
+
+    // Building the nodeinfo bar markup
+
+    const infobarContainer = createNode(this.win, {
+      parent: rootWrapper,
+      attributes: {
+        class: "infobar-container",
+        id: "infobar-container",
+        position: "top",
+        hidden: "true",
+      },
+      prefix: this.ID_CLASS_PREFIX,
+    });
+
+    const infobar = createNode(this.win, {
+      parent: infobarContainer,
+      attributes: {
+        class: "infobar",
+      },
+      prefix: this.ID_CLASS_PREFIX,
+    });
 
-  _getPseudoClasses(node) {
-    if (node.nodeType !== nodeConstants.ELEMENT_NODE) {
-      // hasPseudoClassLock can only be used on Elements.
-      return [];
+    const texthbox = createNode(this.win, {
+      parent: infobar,
+      attributes: {
+        class: "infobar-text",
+      },
+      prefix: this.ID_CLASS_PREFIX,
+    });
+    createNode(this.win, {
+      nodeType: "span",
+      parent: texthbox,
+      attributes: {
+        class: "infobar-tagname",
+        id: "infobar-tagname",
+      },
+      prefix: this.ID_CLASS_PREFIX,
+    });
+    createNode(this.win, {
+      nodeType: "span",
+      parent: texthbox,
+      attributes: {
+        class: "infobar-id",
+        id: "infobar-id",
+      },
+      prefix: this.ID_CLASS_PREFIX,
+    });
+    createNode(this.win, {
+      nodeType: "span",
+      parent: texthbox,
+      attributes: {
+        class: "infobar-classes",
+        id: "infobar-classes",
+      },
+      prefix: this.ID_CLASS_PREFIX,
+    });
+    createNode(this.win, {
+      nodeType: "span",
+      parent: texthbox,
+      attributes: {
+        class: "infobar-pseudo-classes",
+        id: "infobar-pseudo-classes",
+      },
+      prefix: this.ID_CLASS_PREFIX,
+    });
+    createNode(this.win, {
+      nodeType: "span",
+      parent: texthbox,
+      attributes: {
+        class: "infobar-dimensions",
+        id: "infobar-dimensions",
+      },
+      prefix: this.ID_CLASS_PREFIX,
+    });
+
+    createNode(this.win, {
+      nodeType: "span",
+      parent: texthbox,
+      attributes: {
+        class: "infobar-grid-type",
+        id: "infobar-grid-type",
+      },
+      prefix: this.ID_CLASS_PREFIX,
+    });
+
+    createNode(this.win, {
+      nodeType: "span",
+      parent: texthbox,
+      attributes: {
+        class: "infobar-flex-type",
+        id: "infobar-flex-type",
+      },
+      prefix: this.ID_CLASS_PREFIX,
+    });
+
+    // Building the SVG element with its polygons and lines
+
+    const svg = createSVGNode(this.win, {
+      nodeType: "svg",
+      parent: rootWrapper,
+      attributes: {
+        id: "elements",
+        width: "100%",
+        height: "100%",
+        hidden: "true",
+        role: "presentation",
+      },
+      prefix: this.ID_CLASS_PREFIX,
+    });
+
+    const regions = createSVGNode(this.win, {
+      nodeType: "g",
+      parent: svg,
+      attributes: {
+        class: "regions",
+        role: "presentation",
+      },
+      prefix: this.ID_CLASS_PREFIX,
+    });
+
+    for (const region of BOX_MODEL_REGIONS) {
+      createSVGNode(this.win, {
+        nodeType: "path",
+        parent: regions,
+        attributes: {
+          class: region,
+          id: region,
+          role: "presentation",
+        },
+        prefix: this.ID_CLASS_PREFIX,
+      });
     }
 
-    return PSEUDO_CLASSES.filter(pseudo => hasPseudoClassLock(node, pseudo));
-  }
-
-  /**
-   * Move the Infobar to the right place in the highlighter.
-   */
-  _moveInfobar() {
-    const bounds = this._getOuterBounds();
-    const container = this.getElement("infobar-container");
-
-    moveInfobar(container, bounds, this.win);
-  }
+    for (const side of BOX_MODEL_SIDES) {
+      createSVGNode(this.win, {
+        nodeType: "line",
+        parent: svg,
+        attributes: {
+          class: "guide-" + side,
+          id: "guide-" + side,
+          "stroke-width": GUIDE_STROKE_WIDTH,
+          role: "presentation",
+        },
+        prefix: this.ID_CLASS_PREFIX,
+      });
+    }
 
-  onPageHide({ target }) {
-    // If a pagehide event is triggered for current window's highlighter, hide the
-    // highlighter.
-    if (target.defaultView === this.win) {
-      this.hide();
-    }
-  }
-
-  onWillNavigate({ isTopLevel }) {
-    if (isTopLevel) {
-      this.hide();
-    }
+    return highlighterContainer;
   }
 }
 
-exports.BoxModelHighlighter = BoxModelHighlighter;
+exports.BoxModelHighlighterRenderer = BoxModelHighlighterRenderer;
+
+/**
+ * Setup function that runs in parent process and sets up the rendering part of the
+ * box model highlighter and the communication channel with the observer part
+ * of the box model highlighter which lives in the content process.
+ *
+ *
+ * @param  {Object} options.mm
+ *         Message manager that corresponds to the current content tab.
+ * @param  {String} options.prefix
+ *         Unique prefix for message manager messages.
+ *         This is the debugger-server-connection prefix.
+ * @return {Object}
+ *         Defines event listeners for when client disconnects or browser gets
+ *         swapped.
+ */
+function setupParentProcess({ mm, prefix }) {
+  let renderer = new BoxModelHighlighterRenderer(mm, prefix);
+
+  return {
+    onBrowserSwap: newMM => renderer.setMessageManager(newMM, prefix),
+    onDisconnected: () => {
+      renderer.destroy();
+      renderer = null;
+    },
+  };
+}
+
+exports.setupParentProcess = setupParentProcess;
--- a/devtools/server/actors/highlighters/box-model.js
+++ b/devtools/server/actors/highlighters/box-model.js
@@ -96,17 +96,16 @@ const GUIDE_STROKE_WIDTH = 1;
  *   </div>
  * </div>
  */
 class BoxModelHighlighter extends AutoRefreshHighlighter {
   constructor(highlighterEnv) {
     super(highlighterEnv);
 
     this.ID_CLASS_PREFIX = "box-model-";
-
     this.markup = new CanvasFrameAnonymousContentHelper(
       this.highlighterEnv,
       this._buildMarkup.bind(this)
     );
 
     /**
      * Optionally customize each region's fill color by adding an entry to the
      * regionFill property: `highlighter.regionFill.margin = "red";
new file mode 100644
--- /dev/null
+++ b/devtools/server/actors/highlighters/highlighter-renderer.js
@@ -0,0 +1,202 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+const Services = require("Services");
+
+const {
+  HighlighterEnvironment,
+} = require("devtools/server/actors/highlighters");
+
+const {
+  CanvasFrameAnonymousContentHelper,
+} = require("devtools/server/actors/highlighters/utils/markup");
+
+/**
+ * HighlighterRenderer is the base class that implements the rendering surface for a
+ * highlighter in the parent process.
+ *
+ * It injects an iframe in the browser window which hosts the anonymous canvas
+ * frame where the highlighter's markup is generated and manipulated.
+ *
+ * This is the renderer part of a highlighter. It has a counterpart: the observer part of
+ * a highlighter which lives in the content process so it can observe changes to a node's
+ * position and attributes over time. The observer communicates any changes either through
+ * messages (via message manager) or directly to the renderer which updates the
+ * highlighter markup.
+ *
+ * NOTE: When the highlighter is used in the context of the browser toolbox, for example,
+ * when inspecting the browser UI, both observer and renderer live in the parent process
+ * and communication is done by direct reference, not using messages.
+ *
+ * Classes that extend HighlighterRenderer must implement:
+ * - a `typeName` string to identify the highighter type
+ * - a `_buildMarkup()` method to generate the highlighter markup;
+ * - a `render()` method to update the highlighter markup when given new information
+ *   about the observed node.
+ */
+class HighlighterRenderer {
+  constructor() {
+    // The highlighter's type name. To be implemented by sub classes.
+    this.typeName = "";
+    this.onMessage = this.onMessage.bind(this);
+  }
+
+  /**
+   * Create an HTML iframe in order to use the anonymous canvas frame within it
+   * for hosting and manipulating the highlighter's markup.
+   *
+   * @param {Boolean} isBrowserToolboxHighlighter
+   *        Whether the highlighter is used in the context of the
+   *        browser toolbox (as opposed to the content toolbox).
+   *        When set to true, this will influence where the
+   *        iframe is appended so that it overlaps the browser UI
+   *        (as opposed to overlapping just the page content).
+   */
+  init(isBrowserToolboxHighlighter) {
+    // Get a reference to the parent process window.
+    this.win = Services.wm.getMostRecentBrowserWindow();
+
+    const { gBrowser } = this.win;
+    // Get a reference to the selected <browser> element.
+    const browser = gBrowser.selectedBrowser;
+    const browserContainer = gBrowser.getBrowserContainer(browser);
+
+    // The parent node of the iframe depends on the highlighter context:
+    // - browser toolbox: top-level browser window
+    // - content toolbox: host node of the <browser> element for the selected tab
+    const parent = isBrowserToolboxHighlighter
+      ? this.win.document.documentElement
+      : browserContainer.querySelector(".browserStack");
+
+    // Grab the host iframe if it was previously created by another highlighter.
+    const iframe = parent.querySelector(
+      `:scope > .devtools-highlighter-renderer`
+    );
+
+    if (iframe) {
+      this.iframe = iframe;
+      this.setupMarkup();
+    } else {
+      this.iframe = this.win.document.createElement("iframe");
+      this.iframe.classList.add("devtools-highlighter-renderer");
+
+      if (isBrowserToolboxHighlighter) {
+        parent.append(this.iframe);
+      } else {
+        // Ensure highlighters are drawn underneath alerts and dialog boxes.
+        parent.querySelector("browser").after(this.iframe);
+      }
+
+      this.iframe.contentWindow.addEventListener(
+        "DOMContentLoaded",
+        this.setupMarkup.bind(this)
+      );
+    }
+  }
+
+  /**
+   * Generate the highlighter markup and insert it into the anoymous canvas frame.
+   */
+  setupMarkup() {
+    if (!this.iframe || !this.iframe.contentWindow) {
+      throw Error(
+        "The highlighter renderer's host iframe is missing or not yet ready"
+      );
+    }
+
+    this.highlighterEnv = new HighlighterEnvironment();
+    this.highlighterEnv.initFromWindow(this.iframe.contentWindow);
+
+    this.markup = new CanvasFrameAnonymousContentHelper(
+      this.highlighterEnv,
+      this._buildMarkup.bind(this)
+    );
+  }
+
+  /**
+   * Set up message manager listener to listen for messages
+   * coming from the from the child process.
+   *
+   * @param {Object} mm
+   *        Message manager that corresponds to the current content tab.
+   * @param {String} prefix
+   *        Cross-process connection prefix.
+   */
+  setMessageManager(mm, prefix) {
+    if (this.messageManager === mm) {
+      return;
+    }
+
+    // Message name used to distinguish between messages over the message manager.
+    this._msgName = `debug:${prefix}${this.typeName}`;
+
+    if (this.messageManager) {
+      // If the browser was swapped we need to reset the message manager.
+      const oldMM = this.messageManager;
+      oldMM.removeMessageListener(this._msgName, this.onMessage);
+    }
+
+    this.messageManager = mm;
+    if (mm) {
+      mm.addMessageListener(this._msgName, this.onMessage);
+    }
+  }
+
+  postMessage(topic, data = {}) {
+    this.messageManager.sendAsyncMessage(`${this._msgName}:event`, {
+      topic,
+      data,
+    });
+  }
+
+  /**
+   * Handler for messages coming from the content process.
+   *
+   * @param  {Object} msg
+   *         Data payload associated with the message.
+   */
+  onMessage(msg) {
+    const { topic, data } = msg.json;
+    switch (topic) {
+      case "render":
+        this.render(data);
+        break;
+
+      case "destroy":
+        this.destroy();
+        break;
+    }
+  }
+
+  render() {
+    // When called, sub classes should update the highlighter.
+    // To be implemented by sub classes.
+    throw new Error(
+      "Highlighter renderer class had to implement render method"
+    );
+  }
+
+  destroy() {
+    if (this.highlighterEnv) {
+      this.highlighterEnv.destroy();
+      this.highlighterEnv = null;
+    }
+
+    if (this.markup) {
+      this.markup.destroy();
+      this.markup = null;
+    }
+
+    if (this.iframe) {
+      this.iframe.remove();
+      this.iframe = null;
+    }
+
+    this.win = null;
+    this.setMessageManager(null);
+  }
+}
+exports.HighlighterRenderer = HighlighterRenderer;
--- a/devtools/server/actors/highlighters/moz.build
+++ b/devtools/server/actors/highlighters/moz.build
@@ -6,23 +6,26 @@
 
 DIRS += [
     'utils',
 ]
 
 DevToolsModules(
     'accessible.js',
     'auto-refresh.js',
+    'box-model-observer.js',
+    'box-model-renderer.js',
     'box-model.js',
     'css-grid.js',
     'css-transform.js',
     'eye-dropper.js',
     'flexbox.js',
     'fonts.js',
     'geometry-editor.js',
+    'highlighter-renderer.js',
     'measuring-tool.js',
     'paused-debugger.js',
     'rulers.js',
     'selector.js',
     'shapes.js',
     'simple-outline.js',
     'xul-accessible.js'
 )
--- a/devtools/server/actors/inspector/event-collector.js
+++ b/devtools/server/actors/inspector/event-collector.js
@@ -299,25 +299,32 @@ class MainEventCollector {
     return null;
   }
 
   unwrap(obj) {
     return Cu.isXrayWrapper(obj) ? obj.wrappedJSObject : obj;
   }
 
   isChromeHandler(handler) {
-    const handlerPrincipal = Cu.getObjectPrincipal(handler);
+    try {
+      const handlerPrincipal = Cu.getObjectPrincipal(handler);
 
-    // Chrome codebase may register listeners on the page from a frame script or
-    // JSM <video> tags may also report internal listeners, but they won't be
-    // coming from the system principal. Instead, they will be using an expanded
-    // principal.
-    return (
-      handlerPrincipal.isSystemPrincipal || handlerPrincipal.isExpandedPrincipal
-    );
+      // Chrome codebase may register listeners on the page from a frame script or
+      // JSM <video> tags may also report internal listeners, but they won't be
+      // coming from the system principal. Instead, they will be using an expanded
+      // principal.
+      return (
+        handlerPrincipal.isSystemPrincipal ||
+        handlerPrincipal.isExpandedPrincipal
+      );
+    } catch (e) {
+      // Anything from a dead object to a CSP error can leave us here so let's
+      // return false so that we can fail gracefully.
+      return false;
+    }
   }
 }
 
 /**
  * Get or detect DOM events. These may include DOM events created by libraries
  * that enable their custom events to work. At this point we are unable to
  * effectively filter them as they may be proxied or wrapped. Although we know
  * there is an event, we may not know the true contents until it goes
--- a/devtools/server/actors/inspector/inspector.js
+++ b/devtools/server/actors/inspector/inspector.js
@@ -191,25 +191,31 @@ exports.InspectorActor = protocol.ActorC
    * The same instance will always be returned by this method when called
    * several times.
    * The highlighter actor returned here is used to highlighter elements's
    * box-models from the markup-view, box model, console, debugger, ... as
    * well as select elements with the pointer (pick).
    *
    * @param {Boolean} autohide Optionally autohide the highlighter after an
    * element has been picked
+   * @param {Boolean} useNewBoxModelHighlighter Whether to use the new box model
+   * highlighter that has split renderer and observer parts.
    * @return {HighlighterActor}
    */
-  getHighlighter: function(autohide) {
+  getHighlighter: function(autohide, useNewBoxModelHighlighter) {
     if (this._highlighterPromise) {
       return this._highlighterPromise;
     }
 
     this._highlighterPromise = this.getWalker().then(walker => {
-      const highlighter = HighlighterActor(this, autohide);
+      const highlighter = HighlighterActor(
+        this,
+        autohide,
+        useNewBoxModelHighlighter
+      );
       this.manage(highlighter);
       return highlighter;
     });
     return this._highlighterPromise;
   },
 
   /**
    * If consumers need to display several highlighters at the same time or
--- a/devtools/shared/fronts/inspector.js
+++ b/devtools/shared/fronts/inspector.js
@@ -25,17 +25,19 @@ loader.lazyRequireGetter(
 loader.lazyRequireGetter(this, "flags", "devtools/shared/flags");
 
 const TELEMETRY_EYEDROPPER_OPENED = "DEVTOOLS_EYEDROPPER_OPENED_COUNT";
 const TELEMETRY_EYEDROPPER_OPENED_MENU =
   "DEVTOOLS_MENU_EYEDROPPER_OPENED_COUNT";
 const SHOW_ALL_ANONYMOUS_CONTENT_PREF =
   "devtools.inspector.showAllAnonymousContent";
 const SHOW_UA_SHADOW_ROOTS_PREF = "devtools.inspector.showUserAgentShadowRoots";
-const FISSION_ENABLED = "devtools.browsertoolbox.fission";
+const FISSION_ENABLED_PREF = "devtools.browsertoolbox.fission";
+const USE_NEW_BOX_MODEL_HIGHLIGHTER_PREF =
+  "devtools.inspector.use-new-box-model-highlighter";
 
 const telemetry = new Telemetry();
 
 /**
  * Client side of the DOM walker.
  */
 class WalkerFront extends FrontClassWithSpec(walkerSpec) {
   /**
@@ -526,17 +528,20 @@ class InspectorFront extends FrontClassW
     this.walker = await this.getWalker({
       showAllAnonymousContent,
       showUserAgentShadowRoots,
     });
   }
 
   async _getHighlighter() {
     const autohide = !flags.testing;
-    this.highlighter = await this.getHighlighter(autohide);
+    this.highlighter = await this.getHighlighter(
+      autohide,
+      Services.prefs.getBoolPref(USE_NEW_BOX_MODEL_HIGHLIGHTER_PREF)
+    );
   }
 
   hasHighlighter(type) {
     return this._highlighters.has(type);
   }
 
   async _getPageStyle() {
     this.pageStyle = await super.getPageStyle();
@@ -599,17 +604,17 @@ class InspectorFront extends FrontClassW
    * targets in remote frames nested within the document inspected here.
    *
    * Note that this only returns a non-empty array if the used from the Browser Toolbox
    * and with the FISSION_ENABLED pref on.
    *
    * @return {Array} The list of InspectorFront instances.
    */
   async getChildInspectors() {
-    const fissionEnabled = Services.prefs.getBoolPref(FISSION_ENABLED);
+    const fissionEnabled = Services.prefs.getBoolPref(FISSION_ENABLED_PREF);
     const childInspectors = [];
     const target = this.targetFront;
     // this line can be removed when we are ready for fission frames
     if (fissionEnabled && target.chrome && !target.isAddon) {
       const { frames } = await target.listRemoteFrames();
       // attempt to get targets and filter by targets that could connect
       for (const descriptor of frames) {
         const remoteTarget = await descriptor.getTarget();
--- a/devtools/shared/layout/utils.js
+++ b/devtools/shared/layout/utils.js
@@ -149,33 +149,40 @@ exports.getFrameOffsets = getFrameOffset
  *        The box model region to return: "content", "padding", "border" or
  *        "margin".
  * @param {Object} [options.ignoreZoom=false]
  *        Ignore zoom used in the context of e.g. canvas.
  * @return {Array}
  *        An array of objects that have the same structure as quads returned by
  *        getBoxQuads. An empty array if the node has no quads or is invalid.
  */
-function getAdjustedQuads(boundaryWindow, node, region, { ignoreZoom } = {}) {
+function getAdjustedQuads(
+  boundaryWindow,
+  node,
+  region,
+  { ignoreZoom, ignoreScroll } = {}
+) {
   if (!node || !node.getBoxQuads) {
     return [];
   }
 
   const quads = node.getBoxQuads({
     box: region,
     relativeTo: boundaryWindow.document,
     createFramesForSuppressedWhitespace: false,
   });
 
   if (!quads.length) {
     return [];
   }
 
   const scale = ignoreZoom ? 1 : getCurrentZoom(node);
-  const { scrollX, scrollY } = boundaryWindow;
+  const { scrollX, scrollY } = ignoreScroll
+    ? { scrollX: 0, scrollY: 0 }
+    : boundaryWindow;
 
   const xOffset = scrollX * scale;
   const yOffset = scrollY * scale;
 
   const adjustedQuads = [];
   for (const quad of quads) {
     const bounds = quad.getBounds();
     adjustedQuads.push({
--- a/devtools/shared/specs/inspector.js
+++ b/devtools/shared/specs/inspector.js
@@ -401,16 +401,17 @@ const inspectorSpec = generateActorSpec(
       request: {},
       response: {
         pageStyle: RetVal("pagestyle"),
       },
     },
     getHighlighter: {
       request: {
         autohide: Arg(0, "boolean"),
+        useNewBoxModelHighlighter: Arg(1, "nullable:boolean"),
       },
       response: {
         highligter: RetVal("highlighter"),
       },
     },
     getHighlighterByType: {
       request: {
         typeName: Arg(0),
--- a/dom/base/TimeoutHandler.cpp
+++ b/dom/base/TimeoutHandler.cpp
@@ -125,21 +125,21 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(CallbackTimeoutHandler)
   if (MOZ_UNLIKELY(cb.WantDebugInfo())) {
     nsAutoCString name("CallbackTimeoutHandler");
     JSObject* obj = tmp->mFunction->CallablePreserveColor();
     JSFunction* fun =
         JS_GetObjectFunction(js::UncheckedUnwrapWithoutExpose(obj));
     if (fun && JS_GetFunctionId(fun)) {
-      JSFlatString* funId = JS_ASSERT_STRING_IS_FLAT(JS_GetFunctionId(fun));
-      size_t size = 1 + JS_PutEscapedFlatString(nullptr, 0, funId, 0);
+      JSLinearString* funId = JS_ASSERT_STRING_IS_LINEAR(JS_GetFunctionId(fun));
+      size_t size = 1 + JS_PutEscapedLinearString(nullptr, 0, funId, 0);
       char* funIdName = new char[size];
       if (funIdName) {
-        JS_PutEscapedFlatString(funIdName, size, funId, 0);
+        JS_PutEscapedLinearString(funIdName, size, funId, 0);
         name.AppendLiteral(" [");
         name.Append(funIdName);
         delete[] funIdName;
         name.Append(']');
       }
     }
     cb.DescribeRefCountedNode(tmp->mRefCnt.get(), name.get());
   } else {
--- a/dom/base/nsJSUtils.h
+++ b/dom/base/nsJSUtils.h
@@ -265,22 +265,22 @@ inline bool AssignJSString(JSContext* cx
                 "Shouldn't overflow here or in SetCapacity");
   if (MOZ_UNLIKELY(!dest.SetLength(len, mozilla::fallible))) {
     JS_ReportOutOfMemory(cx);
     return false;
   }
   return js::CopyStringChars(cx, dest.BeginWriting(), s, len);
 }
 
-inline void AssignJSFlatString(nsAString& dest, JSFlatString* s) {
-  size_t len = js::GetFlatStringLength(s);
+inline void AssignJSLinearString(nsAString& dest, JSLinearString* s) {
+  size_t len = js::GetLinearStringLength(s);
   static_assert(js::MaxStringLength < (1 << 30),
                 "Shouldn't overflow here or in SetCapacity");
   dest.SetLength(len);
-  js::CopyFlatStringChars(dest.BeginWriting(), s, len);
+  js::CopyLinearStringChars(dest.BeginWriting(), s, len);
 }
 
 class nsAutoJSString : public nsAutoString {
  public:
   /**
    * nsAutoJSString should be default constructed, which leaves it empty
    * (this->IsEmpty()), and initialized with one of the init() methods below.
    */
--- a/dom/base/test/chrome/cpows_parent.xul
+++ b/dom/base/test/chrome/cpows_parent.xul
@@ -206,38 +206,26 @@
     function recvErrorReportingTest(message) {
       throw "Test Error Probe";
     }
 
     let savedElement = null;
     function recvDomTest(message) {
       savedElement = message.objects.element;
 
-      is(savedElement.QueryInterface(Ci.nsISupports), savedElement,
-         "QI to nsISupports works");
-
-      function testNoInterface(savedElement, i) {
-        try {
-          savedElement.QueryInterface(i);
-          ok(false, "should have thrown an exception");
-        } catch (e) {
-          is(e.result, Cr.NS_ERROR_NO_INTERFACE, "threw the right exception");
-        }
-      }
-
-      testNoInterface(savedElement, Ci.nsIClassInfo);
+      is(savedElement.QueryInterface, undefined,
+         "Should not have a QueryInterface");
 
       // Test to ensure that we don't pass CPOWs to C++-implemented interfaces.
       // See bug 1072980.
       if (test_state == "remote") {
-        // This doesn't work because we intercept toString and QueryInterface specially
+        // This doesn't work because we intercept toString specially
         // and don't cache the function pointer.
         // See bug 1140636.
         todo_is(savedElement.toString, savedElement.toString, "toString identity works");
-        todo_is(savedElement.QueryInterface, savedElement.QueryInterface, "toString identity works");
 
         is(Object.prototype.toString.call(savedElement), "[object HTMLDivElement]",
            "prove that this works (and doesn't leak)");
 
         is(Object.prototype.toString.call(savedElement), "[object HTMLDivElement]",
            "prove that this works twice (since we cache it and doesn't leak)");
 
         // This does work because we create a CPOW for isEqualNode that stays
--- a/dom/bindings/BindingUtils.cpp
+++ b/dom/bindings/BindingUtils.cpp
@@ -1233,68 +1233,16 @@ bool InitIds(JSContext* cx, const Native
                CompareIdsAtIndices,
                const_cast<PropertyInfo*>(nativeProperties->PropertyInfos()));
 
   return true;
 }
 
 #undef INIT_IDS_IF_DEFINED
 
-bool QueryInterface(JSContext* cx, unsigned argc, JS::Value* vp) {
-  JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
-  if (!args.thisv().isObject()) {
-    JS_ReportErrorASCII(cx, "QueryInterface called on incompatible non-object");
-    return false;
-  }
-
-  // Get the object. It might be a security wrapper, in which case we do a
-  // checked unwrap.
-  JS::Rooted<JSObject*> origObj(cx, &args.thisv().toObject());
-  JS::Rooted<JSObject*> obj(
-      cx, js::CheckedUnwrapDynamic(origObj, cx,
-                                   /* stopAtWindowProxy = */ false));
-  if (!obj) {
-    JS_ReportErrorASCII(cx, "Permission denied to access object");
-    return false;
-  }
-
-  nsCOMPtr<nsISupports> native = UnwrapDOMObjectToISupports(obj);
-  if (!native) {
-    return Throw(cx, NS_ERROR_FAILURE);
-  }
-
-  if (argc < 1) {
-    return Throw(cx, NS_ERROR_XPC_NOT_ENOUGH_ARGS);
-  }
-
-  Maybe<nsIID> iid = xpc::JSValue2ID(cx, args[0]);
-  if (!iid) {
-    return Throw(cx, NS_ERROR_XPC_BAD_CONVERT_JS);
-  }
-
-  if (iid->Equals(NS_GET_IID(nsIClassInfo))) {
-    nsresult rv;
-    nsCOMPtr<nsIClassInfo> ci = do_QueryInterface(native, &rv);
-    if (NS_FAILED(rv)) {
-      return Throw(cx, rv);
-    }
-
-    return WrapObject(cx, ci, &NS_GET_IID(nsIClassInfo), args.rval());
-  }
-
-  nsCOMPtr<nsISupports> unused;
-  nsresult rv = native->QueryInterface(*iid, getter_AddRefs(unused));
-  if (NS_FAILED(rv)) {
-    return Throw(cx, rv);
-  }
-
-  args.rval().set(args.thisv());
-  return true;
-}
-
 void GetInterfaceImpl(JSContext* aCx, nsIInterfaceRequestor* aRequestor,
                       nsWrapperCache* aCache, JS::Handle<JS::Value> aIID,
                       JS::MutableHandle<JS::Value> aRetval,
                       ErrorResult& aError) {
   Maybe<nsIID> iid = xpc::JSValue2ID(aCx, aIID);
   if (!iid) {
     aError.Throw(NS_ERROR_XPC_BAD_CONVERT_JS);
     return;
--- a/dom/bindings/BindingUtils.h
+++ b/dom/bindings/BindingUtils.h
@@ -1760,27 +1760,16 @@ static inline bool AtomizeAndPinJSString
     id = INTERNED_STRING_TO_JSID(cx, str);
     return true;
   }
   return false;
 }
 
 bool InitIds(JSContext* cx, const NativeProperties* properties);
 
-bool QueryInterface(JSContext* cx, unsigned argc, JS::Value* vp);
-
-template <class T>
-struct WantsQueryInterface {
-  static_assert(IsBaseOf<nsISupports, T>::value,
-                "QueryInterface can't work without an nsISupports.");
-  static bool Enabled(JSContext* aCx, JSObject* aGlobal) {
-    return NS_IsMainThread() && IsChromeOrXBL(aCx, aGlobal);
-  }
-};
-
 void GetInterfaceImpl(JSContext* aCx, nsIInterfaceRequestor* aRequestor,
                       nsWrapperCache* aCache, JS::Handle<JS::Value> aIID,
                       JS::MutableHandle<JS::Value> aRetval,
                       ErrorResult& aError);
 
 template <class T>
 void GetInterface(JSContext* aCx, T* aThis, JS::Handle<JS::Value> aIID,
                   JS::MutableHandle<JS::Value> aRetval, ErrorResult& aError) {
--- a/dom/bindings/CallbackInterface.cpp
+++ b/dom/bindings/CallbackInterface.cpp
@@ -15,18 +15,17 @@ namespace dom {
 
 bool CallbackInterface::GetCallableProperty(
     JSContext* cx, JS::Handle<jsid> aPropId,
     JS::MutableHandle<JS::Value> aCallable) {
   if (!JS_GetPropertyById(cx, CallbackKnownNotGray(), aPropId, aCallable)) {
     return false;
   }
   if (!aCallable.isObject() || !JS::IsCallable(&aCallable.toObject())) {
-    JS::RootedString propId(
-        cx, JS_FORGET_STRING_FLATNESS(JSID_TO_FLAT_STRING(aPropId)));
+    JS::RootedString propId(cx, JSID_TO_STRING(aPropId));
     JS::UniqueChars propName = JS_EncodeStringToUTF8(cx, propId);
     nsPrintfCString description("Property '%s'", propName.get());
     ThrowErrorMessage(cx, MSG_NOT_CALLABLE, description.get());
     return false;
   }
 
   return true;
 }
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -2339,46 +2339,16 @@ class MethodDefiner(PropertyDefiner):
                        (not crossOriginOnly or m.getExtendedAttribute("CrossOriginCallable")) and
                        not m.isIdentifierLess() and
                        not m.getExtendedAttribute("Unexposed")]
         else:
             methods = []
         self.chrome = []
         self.regular = []
         for m in methods:
-            if m.identifier.name == 'QueryInterface':
-                # QueryInterface is special, because instead of generating an
-                # impl we just call out directly to our shared one.
-                if m.isStatic():
-                    raise TypeError("Legacy QueryInterface member shouldn't be static")
-                signatures = m.signatures()
-
-                if (len(signatures) > 1 or len(signatures[0][1]) > 1 or
-                    not signatures[0][1][0].type.isAny()):
-                    raise TypeError("There should be only one QueryInterface method with 1 argument of type any")
-
-                # Make sure to not stick QueryInterface on abstract interfaces.
-                if (not self.descriptor.interface.hasInterfacePrototypeObject() or
-                    not self.descriptor.concrete):
-                    raise TypeError("QueryInterface is only supported on "
-                                    "interfaces that are concrete: " +
-                                    self.descriptor.name)
-
-                if not isChromeOnly(m):
-                    raise TypeError("QueryInterface must be ChromeOnly")
-
-                self.chrome.append({
-                    "name": 'QueryInterface',
-                    "methodInfo": False,
-                    "length": 1,
-                    "flags": "0",
-                    "condition": PropertyDefiner.getControllingCondition(m, descriptor)
-                })
-                continue
-
             method = self.methodData(m, descriptor)
 
             if m.isStatic():
                 method["nativeName"] = CppKeywords.checkMethodName(IDLToCIdentifier(m.identifier.name))
 
             if isChromeOnly(m):
                 self.chrome.append(method)
             else:
@@ -11831,27 +11801,27 @@ def missingPropUseCountersForDescriptor(
         containing interface name and prop name.
 
         Incoming props should be a sorted list.
         """
         if len(props) == 1:
             # We're down to one string: just check whether we match it.
             return fill(
                 """
-                if (JS_FlatStringEqualsLiteral(str, "${name}")) {
+                if (JS_LinearStringEqualsLiteral(str, "${name}")) {
                   counter.emplace(eUseCounter_${iface}_${name});
                 }
                 """,
                 iface=props[0][0],
                 name=props[0][1])
 
         switch = dict()
         if charIndex == 0:
             switch['precondition'] = \
-                'StringIdChars chars(nogc, js::FlatStringToLinearString(str));\n'
+                'StringIdChars chars(nogc, str);\n'
         else:
             switch['precondition'] = ""
 
         # Find the first place where we might actually have a difference.
         while all(prop[1][charIndex] == props[0][1][charIndex]
                   for prop in props):
             charIndex += 1
 
@@ -11869,33 +11839,33 @@ def missingPropUseCountersForDescriptor(
                 current_props = []
                 curChar = nextChar
             current_props.append(props[idx])
             idx += 1
         switch['cases'][curChar] = charSwitch(current_props, charIndex + 1)
         return switch
 
     lengths = set(len(prop[1]) for prop in instrumentedProps)
-    switchDesc = { 'condition': 'js::GetFlatStringLength(str)',
+    switchDesc = { 'condition': 'js::GetLinearStringLength(str)',
                    'precondition': '' }
     switchDesc['cases'] = dict()
     for length in sorted(lengths):
         switchDesc['cases'][str(length)] = charSwitch(
             list(sorted(prop for prop in instrumentedProps
                         if len(prop[1]) == length)),
             0)
 
     return fill(
         """
         if (StaticPrefs::${pref}() && JSID_IS_ATOM(id)) {
           Maybe<UseCounter> counter;
           {
             // Scope for our no-GC section, so we don't need to rely on SetUseCounter not GCing.
             JS::AutoCheckCannotGC nogc;
-            JSFlatString* str = js::AtomToFlatString(JSID_TO_ATOM(id));
+            JSLinearString* str = js::AtomToLinearString(JSID_TO_ATOM(id));
             // Don't waste time fetching the chars until we've done the length switch.
             $*{switch}
           }
           if (counter) {
             SetUseCounter(proxy, *counter);
           }
         }
 
@@ -14531,21 +14501,21 @@ class CGGlobalNames(CGGeneric):
 
         # Build the perfect hash function.
         phf = PerfectHash(entries, GLOBAL_NAMES_PHF_SIZE)
 
         # Generate code for the PHF
         phfCodegen = phf.codegen('WebIDLGlobalNameHash::sEntries',
                                  'WebIDLNameTableEntry')
         entries = phfCodegen.gen_entries(lambda e: e[1])
-        getter = phfCodegen.gen_jsflatstr_getter(
+        getter = phfCodegen.gen_jslinearstr_getter(
             name='WebIDLGlobalNameHash::GetEntry',
             return_type='const WebIDLNameTableEntry*',
             return_entry=dedent("""
-                if (JS_FlatStringEqualsAscii(aKey, sNames + entry.mNameOffset, entry.mNameLength)) {
+                if (JS_LinearStringEqualsAscii(aKey, sNames + entry.mNameOffset, entry.mNameLength)) {
                   return &entry;
                 }
                 return nullptr;
                 """))
 
         define = fill("""
             const uint32_t WebIDLGlobalNameHash::sCount = ${count};
 
--- a/dom/bindings/Configuration.py
+++ b/dom/bindings/Configuration.py
@@ -53,27 +53,22 @@ class Configuration(DescriptorProvider):
         self.maxProtoChainLength = 0
         for thing in parseData:
             if isinstance(thing, IDLIncludesStatement):
                 # Our build system doesn't support dep build involving
                 # addition/removal of "includes" statements that appear in a
                 # different .webidl file than their LHS interface.  Make sure we
                 # don't have any of those.  See similar block below for partial
                 # interfaces!
-                #
-                # But whitelist a RHS that is LegacyQueryInterface,
-                # since people shouldn't be adding any of those.
-                if (thing.interface.filename() != thing.filename() and
-                    thing.mixin.identifier.name != "LegacyQueryInterface"):
+                if (thing.interface.filename() != thing.filename()):
                     raise TypeError(
                         "The binding build system doesn't really support "
                         "'includes' statements which don't appear in the "
                         "file in which the left-hand side of the statement is "
-                        "defined.  Don't do this unless your right-hand side "
-                        "is LegacyQueryInterface.\n"
+                        "defined.\n"
                         "%s\n"
                         "%s" %
                         (thing.location, thing.interface.location))
 
             assert not thing.isType()
 
             if (not thing.isInterface() and not thing.isNamespace() and
                 not thing.isInterfaceMixin()):
--- a/dom/bindings/WebIDLGlobalNameHash.cpp
+++ b/dom/bindings/WebIDLGlobalNameHash.cpp
@@ -57,19 +57,17 @@ static JSObject* FindNamedConstructorFor
 }
 
 /* static */
 bool WebIDLGlobalNameHash::DefineIfEnabled(
     JSContext* aCx, JS::Handle<JSObject*> aObj, JS::Handle<jsid> aId,
     JS::MutableHandle<JS::PropertyDescriptor> aDesc, bool* aFound) {
   MOZ_ASSERT(JSID_IS_STRING(aId), "Check for string id before calling this!");
 
-  const WebIDLNameTableEntry* entry;
-  { entry = GetEntry(JSID_TO_FLAT_STRING(aId)); }
-
+  const WebIDLNameTableEntry* entry = GetEntry(JSID_TO_LINEAR_STRING(aId));
   if (!entry) {
     *aFound = false;
     return true;
   }
 
   *aFound = true;
 
   ConstructorEnabled checkEnabledForScope = entry->mEnabled;
@@ -170,17 +168,17 @@ bool WebIDLGlobalNameHash::DefineIfEnabl
   // doesn't matter what we pass for the "readonly" argument here.
   FillPropertyDescriptor(aDesc, aObj, JS::UndefinedValue(), false);
 
   return true;
 }
 
 /* static */
 bool WebIDLGlobalNameHash::MayResolve(jsid aId) {
-  return GetEntry(JSID_TO_FLAT_STRING(aId)) != nullptr;
+  return GetEntry(JSID_TO_LINEAR_STRING(aId)) != nullptr;
 }
 
 /* static */
 bool WebIDLGlobalNameHash::GetNames(JSContext* aCx, JS::Handle<JSObject*> aObj,
                                     NameType aNameType,
                                     JS::MutableHandleVector<jsid> aNames) {
   // aObj is always a Window here, so GetProtoAndIfaceCache on it is safe.
   ProtoAndIfaceCache* cache = GetProtoAndIfaceCache(aObj);
@@ -223,17 +221,17 @@ bool WebIDLGlobalNameHash::ResolveForSys
   }
 
   // XXX(nika): In the Window case, we unwrap our global object here to handle
   // XRays. I don't think we ever create xrays to system globals, so I believe
   // we can skip this step.
   MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(aObj), "Xrays not supported!");
 
   // Look up the corresponding entry in the name table, and resolve if enabled.
-  const WebIDLNameTableEntry* entry = GetEntry(JSID_TO_FLAT_STRING(aId));
+  const WebIDLNameTableEntry* entry = GetEntry(JSID_TO_LINEAR_STRING(aId));
   if (entry && (!entry->mEnabled || entry->mEnabled(aCx, aObj))) {
     if (NS_WARN_IF(!GetPerInterfaceObjectHandle(
             aCx, entry->mConstructorId, entry->mCreate,
             /* aDefineOnGlobal = */ true))) {
       return Throw(aCx, NS_ERROR_FAILURE);
     }
 
     *aResolvedp = true;
--- a/dom/bindings/WebIDLGlobalNameHash.h
+++ b/dom/bindings/WebIDLGlobalNameHash.h
@@ -6,17 +6,17 @@
 
 #ifndef mozilla_dom_WebIDLGlobalNameHash_h__
 #define mozilla_dom_WebIDLGlobalNameHash_h__
 
 #include "js/RootingAPI.h"
 #include "nsTArray.h"
 #include "mozilla/dom/BindingDeclarations.h"
 
-class JSFlatString;
+class JSLinearString;
 
 namespace mozilla {
 namespace dom {
 
 namespace constructors {
 namespace id {
 enum ID : uint16_t;
 }  // namespace id
@@ -72,17 +72,17 @@ class WebIDLGlobalNameHash {
       JSContext* aCx, JS::Handle<JSObject*> aObj,
       JS::MutableHandleVector<jsid> aProperties, bool aEnumerableOnly);
 
  private:
   friend struct WebIDLNameTableEntry;
 
   // Look up an entry by key name. `nullptr` if the entry was not found.
   // The impl of GetEntry is generated by Codegen.py in RegisterBindings.cpp
-  static const WebIDLNameTableEntry* GetEntry(JSFlatString* aKey);
+  static const WebIDLNameTableEntry* GetEntry(JSLinearString* aKey);
 
   // The total number of names in the hash.
   // The value of sCount is generated by Codegen.py in RegisterBindings.cpp.
   static const uint32_t sCount;
 
   // The name table entries in the hash.
   // The value of sEntries is generated by Codegen.py in RegisterBindings.cpp.
   static const WebIDLNameTableEntry sEntries[];
--- a/dom/bindings/test/mochitest.ini
+++ b/dom/bindings/test/mochitest.ini
@@ -40,17 +40,16 @@ support-files =
 [test_exceptions_from_jsimplemented.html]
 tags = webrtc
 [test_lenientThis.html]
 [test_lookupGetter.html]
 [test_namedNoIndexed.html]
 [test_named_getter_enumerability.html]
 [test_Object.prototype_props.html]
 [test_proxy_expandos.html]
-[test_queryInterface.html]
 [test_returnUnion.html]
 skip-if = debug == false
 [test_usvstring.html]
 skip-if = debug == false
 [test_sequence_wrapping.html]
 subsuite = gpu
 [test_setWithNamedGetterNoNamedSetter.html]
 [test_throwing_method_noDCE.html]
deleted file mode 100644
--- a/dom/bindings/test/test_queryInterface.html
+++ /dev/null
@@ -1,41 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-<!--
-https://bugzilla.mozilla.org/show_bug.cgi?id=827546
--->
-<head>
-  <meta charset="utf-8">
-  <title>Test for Bug 827546</title>
-  <script src="/tests/SimpleTest/SimpleTest.js"></script>
-  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
-  <script type="application/javascript">
-
-  /** Test for Bug 827546 **/
-
-  var notImage = document.createElement("div");
-  var thrown;
-  try {
-    thrown = false;
-    SpecialPowers.do_QueryInterface(notImage, "nsIImageLoadingContent");
-  } catch (e) {
-    thrown = true;
-  }
-  ok(thrown,
-     "QI to nsIImageLoadingContent on a non-image element should fail");
-
-  var image = document.createElement("img");
-  ok(SpecialPowers.do_QueryInterface(image, "nsIImageLoadingContent"),
-     "Image  element needs to support QI to nsIImageLoadingContent");
-
-  </script>
-</head>
-<body>
-<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=827546">Mozilla Bug 827546</a>
-<p id="display"></p>
-<div id="content" style="display: none">
-
-</div>
-<pre id="test">
-</pre>
-</body>
-</html>
--- a/dom/file/FileReader.cpp
+++ b/dom/file/FileReader.cpp
@@ -204,19 +204,19 @@ void FileReader::OnLoadEndArrayBuffer() 
   JS::Rooted<JSObject*> exceptionObject(cx, &exceptionValue.toObject());
   JSErrorReport* er = JS_ErrorFromException(cx, exceptionObject);
   if (!er || er->message()) {
     FreeDataAndDispatchError(NS_ERROR_OUT_OF_MEMORY);
     return;
   }
 
   nsAutoString errorName;
-  JSFlatString* name = js::GetErrorTypeName(cx, er->exnType);
+  JSLinearString* name = js::GetErrorTypeName(cx, er->exnType);
   if (name) {
-    AssignJSFlatString(errorName, name);
+    AssignJSLinearString(errorName, name);
   }
 
   nsAutoCString errorMsg(er->message().c_str());
   nsAutoCString errorNameC = NS_LossyConvertUTF16toASCII(errorName);
   // XXX Code selected arbitrarily
   mError =
       new DOMException(NS_ERROR_DOM_INVALID_STATE_ERR, errorMsg, errorNameC,
                        DOMException_Binding::INVALID_STATE_ERR);
new file mode 100644
--- /dev/null
+++ b/dom/indexedDB/test/gtest/TestKey.cpp
@@ -0,0 +1,427 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "gtest/gtest.h"
+
+#include "mozilla/dom/SimpleGlobalObject.h"
+#include "mozilla/dom/ScriptSettings.h"
+#include "mozilla/dom/indexedDB/Key.h"
+
+#include "jsapi.h"
+#include "js/ArrayBuffer.h"
+
+// TODO: This PrintTo overload is defined in dom/media/gtest/TestGroupId.cpp.
+// However, it is not used, probably because of
+// https://stackoverflow.com/a/36941270
+void PrintTo(const nsString& value, std::ostream* os);
+
+using namespace mozilla;
+using namespace mozilla::dom::indexedDB;
+
+// DOM_IndexedDB_Key_Ctor tests test the construction of a Key, and check the
+// properties of the constructed key with the const methods afterwards. The
+// tested ctors include the default ctor, which constructs an unset key, and the
+// ctors that accepts an encoded buffer, which is then decoded using the
+// Key::To* method corresponding to its type.
+//
+// So far, only some cases are tested:
+// - scalar binary
+// -- empty
+// -- with 1-byte encoded representation
+// - scalar string
+// -- empty
+// -- with 1-byte encoded representation
+//
+// TODO More test cases should be added, including
+// - empty (?)
+// - scalar binary
+// -- containing 0 byte(s)
+// -- with 2-byte encoded representation
+// - scalar string
+// -- with 2-byte and 3-byte encoded representation
+// - scalar number
+// - scalar date
+// - arrays, incl. nested arrays, with various combinations of contained types
+
+TEST(DOM_IndexedDB_Key, Ctor_Default)
+{
+  auto key = Key{};
+
+  EXPECT_TRUE(key.IsUnset());
+}
+
+// TODO does such a helper function already exist?
+template <size_t N>
+static auto BufferAsCString(const uint8_t (&aBuffer)[N]) {
+  return nsCString{reinterpret_cast<const char*>(
+                       static_cast<std::decay_t<const uint8_t[]>>(aBuffer)),
+                   N};
+}
+
+#if 0
+static void ExpectKeyIsBinary(const Key& aKey) {
+  EXPECT_FALSE(aKey.IsUnset());
+
+  EXPECT_FALSE(aKey.IsArray());
+  EXPECT_TRUE(aKey.IsBinary());
+  EXPECT_FALSE(aKey.IsDate());
+  EXPECT_FALSE(aKey.IsFloat());
+  EXPECT_FALSE(aKey.IsString());
+}
+#endif
+
+static void ExpectKeyIsString(const Key& aKey) {
+  EXPECT_FALSE(aKey.IsUnset());
+
+  EXPECT_FALSE(aKey.IsArray());
+  EXPECT_FALSE(aKey.IsBinary());
+  EXPECT_FALSE(aKey.IsDate());
+  EXPECT_FALSE(aKey.IsFloat());
+  EXPECT_TRUE(aKey.IsString());
+}
+
+#if 0
+static void ExpectKeyIsArray(const Key& aKey) {
+  EXPECT_FALSE(aKey.IsUnset());
+
+  EXPECT_TRUE(aKey.IsArray());
+  EXPECT_FALSE(aKey.IsBinary());
+  EXPECT_FALSE(aKey.IsDate());
+  EXPECT_FALSE(aKey.IsFloat());
+  EXPECT_FALSE(aKey.IsString());
+}
+#endif
+
+#if 0
+static JSObject& ExpectArrayBufferObject(const JS::Value& aValue) {
+  EXPECT_TRUE(aValue.isObject());
+  auto& object = aValue.toObject();
+  EXPECT_TRUE(JS::IsArrayBufferObject(&object));
+  return object;
+}
+
+static JSObject& ExpectArrayObject(JSContext* const aContext,
+                                   const JS::Heap<JS::Value>& aValue) {
+  EXPECT_TRUE(aValue.get().isObject());
+  auto&& value = JS::RootedValue(aContext, aValue.get());
+  bool rv;
+  EXPECT_TRUE(JS_IsArrayObject(aContext, value, &rv));
+  EXPECT_TRUE(rv);
+  return value.toObject();
+}
+
+static void CheckArrayBuffer(const nsCString& aExpected,
+                             const JS::Value& aActual) {
+  auto& obj = ExpectArrayBufferObject(aActual);
+  uint32_t length;
+  bool isSharedMemory;
+  uint8_t* data;
+  JS::GetArrayBufferLengthAndData(&obj, &length, &isSharedMemory, &data);
+
+  EXPECT_EQ(aExpected.Length(), length);
+  EXPECT_EQ(0, memcmp(aExpected.get(), data, length));
+}
+
+static void CheckString(JSContext* const aContext, const nsString& aExpected,
+                        const JS::Value& aActual) {
+  EXPECT_TRUE(aActual.isString());
+  int32_t rv;
+  EXPECT_TRUE(JS_CompareStrings(aContext,
+                                JS_NewUCStringCopyZ(aContext, aExpected.get()),
+                                aActual.toString(), &rv));
+  EXPECT_EQ(0, rv);
+}
+#endif
+
+namespace {
+// TODO Using AutoTestJSContext causes rooting hazards. Disabling for now.
+// See https://phabricator.services.mozilla.com/D38360#inline-272833
+#if 0
+// TODO cf. AutoJSContext, which doesn't work here. Should it?
+// This is modeled after dom/base/test/gtest/TestContentUtils.cpp
+struct AutoTestJSContext {
+  AutoTestJSContext()
+      : mGlobalObject{mozilla::dom::SimpleGlobalObject::Create(
+            mozilla::dom::SimpleGlobalObject::GlobalType::BindingDetail)} {
+    EXPECT_TRUE(mJsAPI.Init(mGlobalObject));
+    mContext = mJsAPI.cx();
+  }
+
+  operator JSContext*() { return mContext; }
+
+ private:
+  JSObject* mGlobalObject;
+  mozilla::dom::AutoJSAPI mJsAPI;
+  JSContext* mContext;
+};
+#endif
+
+// The following classes serve as base classes for the parametrized tests below.
+// The name of each class reflects the parameter type.
+
+class TestWithParam_CString_ArrayBuffer_Pair
+    : public ::testing::TestWithParam<std::pair<nsCString, nsLiteralCString>> {
+};
+
+class TestWithParam_CString_String_Pair
+    : public ::testing::TestWithParam<std::pair<nsCString, nsLiteralString>> {};
+
+class TestWithParam_LiteralString
+    : public ::testing::TestWithParam<nsLiteralString> {};
+
+class TestWithParam_StringArray
+    : public ::testing::TestWithParam<std::vector<nsString>> {};
+
+class TestWithParam_ArrayBufferArray
+    : public ::testing::TestWithParam<std::vector<nsCString>> {};
+
+}  // namespace
+
+#if 0
+TEST_P(TestWithParam_CString_ArrayBuffer_Pair, Ctor_EncodedBinary) {
+  const auto key = Key{GetParam().first};
+
+  ExpectKeyIsBinary(key);
+
+  AutoTestJSContext context;
+
+  JS::Heap<JS::Value> rv;
+  EXPECT_EQ(NS_OK, key.ToJSVal(context, rv));
+
+  CheckArrayBuffer(GetParam().second, rv);
+}
+
+static const uint8_t zeroLengthBinaryEncodedBuffer[] = {Key::eBinary};
+static const uint8_t nonZeroLengthBinaryEncodedBuffer[] = {Key::eBinary,
+                                                           'a' + 1, 'b' + 1};
+INSTANTIATE_TEST_CASE_P(
+    DOM_IndexedDB_Key, TestWithParam_CString_ArrayBuffer_Pair,
+    ::testing::Values(
+        std::make_pair(BufferAsCString(zeroLengthBinaryEncodedBuffer),
+                       NS_LITERAL_CSTRING("")),
+        std::make_pair(BufferAsCString(nonZeroLengthBinaryEncodedBuffer),
+                       NS_LITERAL_CSTRING("ab"))));
+#endif
+
+TEST_P(TestWithParam_CString_String_Pair, Ctor_EncodedString) {
+  const auto key = Key{GetParam().first};
+
+  ExpectKeyIsString(key);
+
+  nsString rv;
+  key.ToString(rv);
+
+  EXPECT_EQ(GetParam().second, rv);
+}
+
+static const uint8_t zeroLengthStringEncodedBuffer[] = {Key::eString};
+static const uint8_t nonZeroLengthStringEncodedBuffer[] = {Key::eString,
+                                                           'a' + 1, 'b' + 1};
+
+INSTANTIATE_TEST_CASE_P(
+    DOM_IndexedDB_Key, TestWithParam_CString_String_Pair,
+    ::testing::Values(
+        std::make_pair(BufferAsCString(zeroLengthStringEncodedBuffer),
+                       NS_LITERAL_STRING("")),
+        std::make_pair(BufferAsCString(nonZeroLengthStringEncodedBuffer),
+                       NS_LITERAL_STRING("ab"))));
+
+TEST_P(TestWithParam_LiteralString, SetFromString) {
+  auto key = Key{};
+  mozilla::ErrorResult error;
+  const auto result = key.SetFromString(GetParam(), error);
+  EXPECT_FALSE(error.Failed());
+  EXPECT_TRUE(result.Is(mozilla::dom::indexedDB::Ok, error));
+
+  ExpectKeyIsString(key);
+
+  nsString rv;
+  key.ToString(rv);
+
+  EXPECT_EQ(GetParam(), rv);
+}
+
+INSTANTIATE_TEST_CASE_P(DOM_IndexedDB_Key, TestWithParam_LiteralString,
+                        ::testing::Values(NS_LITERAL_STRING(""),
+                                          NS_LITERAL_STRING(u"abc"),
+                                          NS_LITERAL_STRING(u"\u007f"),
+                                          NS_LITERAL_STRING(u"\u0080"),
+                                          NS_LITERAL_STRING(u"\u1fff"),
+                                          NS_LITERAL_STRING(u"\u7fff"),
+                                          NS_LITERAL_STRING(u"\u8000"),
+                                          NS_LITERAL_STRING(u"\uffff")));
+
+#if 0
+static JS::RootedValue CreateArrayBufferValue(JSContext* const aContext,
+                                              const size_t aSize,
+                                              char* const aData) {
+  auto&& arrayBuffer = JS::RootedObject{
+      aContext, JS::NewArrayBufferWithContents(aContext, aSize, aData)};
+  return {aContext, JS::ObjectValue(*arrayBuffer)};
+}
+
+// This tests calling SetFromJSVal with an ArrayBuffer scalar of length 0.
+// TODO Probably there should be more test cases for SetFromJSVal with other
+// ArrayBuffer scalars, which convert this into a parametrized test as well.
+TEST(DOM_IndexedDB_Key, SetFromJSVal_ZeroLengthArrayBuffer)
+{
+  AutoTestJSContext context;
+
+  auto key = Key{};
+  auto&& arrayBuffer = CreateArrayBufferValue(context, 0, nullptr);
+  auto rv1 = mozilla::ErrorResult{};
+  const auto result = key.SetFromJSVal(context, arrayBuffer, rv1);
+  EXPECT_FALSE(rv1.Failed());
+  EXPECT_TRUE(result.Is(mozilla::dom::indexedDB::Ok, rv1));
+
+  ExpectKeyIsBinary(key);
+
+  JS::Heap<JS::Value> rv2;
+  EXPECT_EQ(NS_OK, key.ToJSVal(context, rv2));
+
+  CheckArrayBuffer(NS_LITERAL_CSTRING(""), rv2);
+}
+
+template <typename CheckElement>
+static void CheckArray(JSContext* const context,
+                       const JS::Heap<JS::Value>& arrayValue,
+                       const size_t expectedLength,
+                       const CheckElement& checkElement) {
+  auto&& actualArray =
+      JS::RootedObject(context, &ExpectArrayObject(context, arrayValue));
+
+  uint32_t actualLength;
+  EXPECT_TRUE(JS_GetArrayLength(context, actualArray, &actualLength));
+  EXPECT_EQ(expectedLength, actualLength);
+  for (size_t i = 0; i < expectedLength; ++i) {
+    JS::RootedValue element(static_cast<JSContext*>(context));
+    EXPECT_TRUE(JS_GetElement(context, actualArray, i, &element));
+
+    checkElement(i, element);
+  }
+}
+
+static JS::RootedValue CreateArrayBufferArray(
+    JSContext* const context, const std::vector<nsCString>& elements) {
+  auto* const array = JS_NewArrayObject(context, elements.size());
+  auto&& arrayObject = JS::RootedObject{context, array};
+
+  for (size_t i = 0; i < elements.size(); ++i) {
+    // TODO strdup only works if the element is actually 0-terminated
+    auto&& arrayBuffer = CreateArrayBufferValue(
+        context, elements[i].Length(),
+        elements[i].Length() ? strdup(elements[i].get()) : nullptr);
+    EXPECT_TRUE(JS_SetElement(context, arrayObject, i, arrayBuffer));
+  }
+
+  return {context, JS::ObjectValue(*array)};
+}
+
+TEST_P(TestWithParam_ArrayBufferArray, SetFromJSVal) {
+  const auto& elements = GetParam();
+
+  AutoTestJSContext context;
+  auto&& arrayValue = CreateArrayBufferArray(context, elements);
+
+  auto rv1 = mozilla::ErrorResult{};
+  auto key = Key{};
+  const auto result = key.SetFromJSVal(context, arrayValue, rv1);
+  EXPECT_FALSE(rv1.Failed());
+  EXPECT_TRUE(result.Is(mozilla::dom::indexedDB::Ok, rv1));
+
+  ExpectKeyIsArray(key);
+
+  JS::Heap<JS::Value> rv2;
+  EXPECT_EQ(NS_OK, key.ToJSVal(context, rv2));
+
+  CheckArray(context, rv2, elements.size(),
+             [&elements](const size_t i, const JS::HandleValue& element) {
+               CheckArrayBuffer(elements[i], element);
+             });
+}
+
+const uint8_t element2[] = "foo";
+INSTANTIATE_TEST_CASE_P(
+    DOM_IndexedDB_Key, TestWithParam_ArrayBufferArray,
+    testing::Values(std::vector<nsCString>{},
+                    std::vector<nsCString>{NS_LITERAL_CSTRING("")},
+                    std::vector<nsCString>{NS_LITERAL_CSTRING(""),
+                                           BufferAsCString(element2)}));
+
+static JS::RootedValue CreateStringValue(JSContext* const context,
+                                         const nsString& string) {
+  return {context, JS::StringValue(JS_NewUCStringCopyZ(context, string.get()))};
+}
+
+static JS::RootedValue CreateStringArray(
+    JSContext* const context, const std::vector<nsString>& elements) {
+  auto* const array = JS_NewArrayObject(context, elements.size());
+  auto&& arrayObject = JS::RootedObject{context, array};
+
+  for (size_t i = 0; i < elements.size(); ++i) {
+    auto&& string = CreateStringValue(context, elements[i]);
+    EXPECT_TRUE(JS_SetElement(context, arrayObject, i, string));
+  }
+
+  return {context, JS::ObjectValue(*array)};
+}
+
+TEST_P(TestWithParam_StringArray, SetFromJSVal) {
+  const auto& elements = GetParam();
+
+  AutoTestJSContext context;
+  auto&& arrayValue = CreateStringArray(context, elements);
+
+  auto rv1 = mozilla::ErrorResult{};
+  auto key = Key{};
+  const auto result = key.SetFromJSVal(context, arrayValue, rv1);
+  EXPECT_FALSE(rv1.Failed());
+  EXPECT_TRUE(result.Is(mozilla::dom::indexedDB::Ok, rv1));
+
+  ExpectKeyIsArray(key);
+
+  JS::Heap<JS::Value> rv2;
+  EXPECT_EQ(NS_OK, key.ToJSVal(context, rv2));
+
+  CheckArray(
+      context, rv2, elements.size(),
+      [&elements, &context](const size_t i, const JS::HandleValue& element) {
+        CheckString(context, elements[i], element);
+      });
+}
+
+INSTANTIATE_TEST_CASE_P(
+    DOM_IndexedDB_Key, TestWithParam_StringArray,
+    testing::Values(std::vector<nsString>{NS_LITERAL_STRING(""),
+                                          NS_LITERAL_STRING("abc\u0080\u1fff")},
+                    std::vector<nsString>{
+                        NS_LITERAL_STRING("abc\u0080\u1fff"),
+                        NS_LITERAL_STRING("abc\u0080\u1fff")}));
+
+TEST(DOM_IndexedDB_Key, CompareKeys_NonZeroLengthArrayBuffer)
+{
+  AutoTestJSContext context;
+  const char buf[] = "abc\x80";
+
+  auto first = Key{};
+  auto rv1 = mozilla::ErrorResult{};
+  auto&& arrayBuffer1 =
+      CreateArrayBufferValue(context, sizeof buf, strdup(buf));
+  const auto result1 = first.SetFromJSVal(context, arrayBuffer1, rv1);
+  EXPECT_FALSE(rv1.Failed());
+  EXPECT_TRUE(result1.Is(mozilla::dom::indexedDB::Ok, rv1));
+
+  auto second = Key{};
+  auto rv2 = mozilla::ErrorResult{};
+  auto&& arrayBuffer2 =
+      CreateArrayBufferValue(context, sizeof buf, strdup(buf));
+  const auto result2 = second.SetFromJSVal(context, arrayBuffer2, rv2);
+  EXPECT_FALSE(rv2.Failed());
+  EXPECT_TRUE(result2.Is(mozilla::dom::indexedDB::Ok, rv2));
+
+  EXPECT_EQ(0, Key::CompareKeys(first, second));
+}
+#endif
--- a/dom/indexedDB/test/gtest/moz.build
+++ b/dom/indexedDB/test/gtest/moz.build
@@ -1,15 +1,21 @@
 # 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/.
 
 UNIFIED_SOURCES = [
     'TestIDBResult.cpp',
 ]
 
+# not UNIFIED_SOURCES because TestKey.cpp has classes in an anonymous namespace
+# which result in a GCC error when used in tests, cf. gfx/tests/gtest/moz.build
+SOURCES = [
+    'TestKey.cpp',
+]
+
 include('/ipc/chromium/chromium-config.mozbuild')
 
 FINAL_LIBRARY = 'xul-gtest'
 
 LOCAL_INCLUDES += [
     '/dom/indexedDB',
 ]
--- a/dom/indexedDB/test/test_third_party.html
+++ b/dom/indexedDB/test/test_third_party.html
@@ -66,18 +66,24 @@
         };
       });
     }
 
     function messageListener(event) {
       // eslint-disable-next-line no-eval
       let message = eval(event.data);
 
-      is(message.source, "iframe", "Good source");
-      is(message.result, testData[testIndex].expectedResult, "Good result");
+      // TODO: This is an ad-hoc solution to get a useful assertion message.
+      // It would be desirable that the test framework provides the ability
+      // to capture context information and provide it on assertion failures,
+      // automatically stringified.
+      let testContext = `testData[${testIndex}] == ${JSON.stringify(testData[testIndex])}`;
+
+      is(message.source, "iframe", `Good source for ${testContext}`);
+      is(message.result, testData[testIndex].expectedResult, `Good result for ${testContext}`);
 
       openedWindow.close();
 
       if (testIndex < testData.length - 1) {
         testIndex++;
         openWindow();
         return;
       }
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -92,27 +92,29 @@
 #include "mozilla/widget/ScreenManager.h"
 #include "mozilla/widget/WidgetMessageUtils.h"
 #include "nsBaseDragService.h"
 #include "mozilla/media/MediaChild.h"
 #include "mozilla/BasePrincipal.h"
 #include "mozilla/WebBrowserPersistDocumentChild.h"
 #include "mozilla/HangDetails.h"
 #include "mozilla/LoadInfo.h"
+#include "mozilla/UnderrunHandler.h"
 #include "nsIChildProcessChannelListener.h"
 #include "mozilla/net/HttpChannelChild.h"
 #include "nsQueryObject.h"
 #include "imgLoader.h"
 #include "GMPServiceChild.h"
 #include "nsISimpleEnumerator.h"
 #include "nsIStringBundle.h"
 #include "nsIWorkerDebuggerManager.h"
 #include "nsGeolocation.h"
 #include "audio_thread_priority.h"
 #include "nsIConsoleService.h"
+#include "audio_thread_priority.h"
 
 #if !defined(XP_WIN)
 #  include "mozilla/Omnijar.h"
 #endif
 
 #ifdef MOZ_GECKO_PROFILER
 #  include "ChildProfilerController.h"
 #endif
@@ -1771,24 +1773,24 @@ mozilla::ipc::IPCResult ContentChild::Re
 #if defined(MOZ_SANDBOX)
   bool sandboxEnabled = true;
 #  if defined(XP_LINUX)
   // On Linux, we have to support systems that can't use any sandboxing.
   if (!SandboxInfo::Get().CanSandboxContent()) {
     sandboxEnabled = false;
   } else {
     // Pre-start audio before sandboxing; see bug 1443612.
-    if (!Preferences::GetBool("media.cubeb.sandbox")) {
+    if (Preferences::GetBool("media.cubeb.sandbox")) {
+      if (atp_set_real_time_limit(0, 48000)) {
+        NS_WARNING("could not set real-time limit at process startup");
+      }
+      InstallSoftRealTimeLimitHandler();
+    } else {
       Unused << CubebUtils::GetCubebContext();
     }
-#    if defined(XP_LINUX)
-    else {
-      CubebUtils::InitAudioThreads();
-    }
-#    endif
   }
 
   if (sandboxEnabled) {
     sandboxEnabled = SetContentProcessSandbox(
         ContentProcessSandboxParams::ForThisProcess(aBroker));
   }
 #  elif defined(XP_WIN)
   mozilla::SandboxTarget::Instance()->StartSandbox();
--- a/dom/media/AudioStream.cpp
+++ b/dom/media/AudioStream.cpp
@@ -14,16 +14,17 @@
 #include "mozilla/Mutex.h"
 #include "mozilla/Sprintf.h"
 #include "mozilla/Unused.h"
 #include <algorithm>
 #include "mozilla/Telemetry.h"
 #include "CubebUtils.h"
 #include "nsPrintfCString.h"
 #include "AudioConverter.h"
+#include "UnderrunHandler.h"
 #if defined(XP_WIN)
 #  include "nsXULAppAPI.h"
 #endif
 #include "Tracing.h"
 
 namespace mozilla {
 
 #undef LOG
@@ -561,16 +562,20 @@ void AudioStream::GetTimeStretched(Audio
 }
 
 long AudioStream::DataCallback(void* aBuffer, long aFrames) {
   TRACE_AUDIO_CALLBACK_BUDGET(aFrames, mAudioClock.GetInputRate());
   TRACE_AUDIO_CALLBACK();
   MonitorAutoLock mon(mMonitor);
   MOZ_ASSERT(mState != SHUTDOWN, "No data callback after shutdown");
 
+  if (SoftRealTimeLimitReached()) {
+    DemoteThreadFromRealTime();
+  }
+
   auto writer = AudioBufferWriter(
       MakeSpan<AudioDataValue>(reinterpret_cast<AudioDataValue*>(aBuffer),
                                mOutChannels * aFrames),
       mOutChannels, aFrames);
 
   if (mPrefillQuirk) {
     // Don't consume audio data until Start() is called.
     // Expected only with cubeb winmm backend.
--- a/dom/media/CubebUtils.cpp
+++ b/dom/media/CubebUtils.cpp
@@ -76,19 +76,16 @@ struct AudioIpcInitParams {
 // These functions are provided by audioipc-server crate
 extern void* audioipc_server_start(const char*, const char*);
 extern mozilla::ipc::FileDescriptor::PlatformHandleType
 audioipc_server_new_client(void*);
 extern void audioipc_server_stop(void*);
 // These functions are provided by audioipc-client crate
 extern int audioipc_client_init(cubeb**, const char*,
                                 const AudioIpcInitParams*);
-#ifdef XP_LINUX
-extern void audioipc_init_threads(const AudioIpcInitParams*);
-#endif
 }
 
 namespace mozilla {
 
 namespace {
 
 LazyLogModule gCubebLog("cubeb");
 
@@ -440,28 +437,16 @@ ipc::FileDescriptor CreateAudioIPCConnec
   close(rawFD);
 #  endif
   return fd;
 #else
   return ipc::FileDescriptor();
 #endif
 }
 
-#if defined(XP_LINUX) && !defined(MOZ_WIDGET_ANDROID)
-void InitAudioThreads() {
-  AudioIpcInitParams initParams;
-  initParams.mPoolSize = sAudioIPCPoolSize;
-  initParams.mStackSize = sAudioIPCStackSize;
-  initParams.mThreadCreateCallback = [](const char* aName) {
-    PROFILER_REGISTER_THREAD(aName);
-  };
-  audioipc_init_threads(&initParams);
-}
-#endif
-
 cubeb* GetCubebContextUnlocked() {
   sMutex.AssertCurrentThreadOwns();
   if (sCubebForceNullContext) {
     // Pref set such that we should return a null context
     MOZ_LOG(gCubebLog, LogLevel::Debug,
             ("%s: returning null context due to %s!", __func__,
              PREF_CUBEB_FORCE_NULL_CONTEXT));
     return nullptr;
--- a/dom/media/CubebUtils.h
+++ b/dom/media/CubebUtils.h
@@ -18,19 +18,16 @@ MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(cu
 namespace mozilla {
 namespace CubebUtils {
 
 typedef cubeb_devid AudioDeviceID;
 
 // Initialize Audio Library. Some Audio backends require initializing the
 // library before using it.
 void InitLibrary();
-#  ifdef XP_LINUX
-void InitAudioThreads();
-#  endif
 
 // Shutdown Audio Library. Some Audio backends require shutting down the
 // library after using it.
 void ShutdownLibrary();
 
 // Returns the maximum number of channels supported by the audio hardware.
 uint32_t MaxNumberOfChannels();
 
--- a/dom/media/GraphDriver.cpp
+++ b/dom/media/GraphDriver.cpp
@@ -893,16 +893,28 @@ long AudioCallbackDriver::DataCallback(c
   // Callback any observers for the AEC speaker data.  Note that one
   // (maybe) of these will be full-duplex, the others will get their input
   // data off separate cubeb callbacks.  Take care with how stuff is
   // removed/added to this list and TSAN issues, but input and output will
   // use separate callback methods.
   GraphImpl()->NotifyOutputData(aOutputBuffer, static_cast<size_t>(aFrames),
                                 mSampleRate, mOutputChannels);
 
+#ifdef XP_MACOSX
+  // This only happens when the output is on a macbookpro's external speaker,
+  // that are stereo, but let's just be safe.
+  if (mNeedsPanning && mOutputChannels == 2) {
+    // hard pan to the right
+    for (uint32_t i = 0; i < aFrames * 2; i += 2) {
+      aOutputBuffer[i + 1] += aOutputBuffer[i];
+      aOutputBuffer[i] = 0.0;
+    }
+  }
+#endif
+
   if (!stillProcessing) {
     // About to hand over control of the graph.  Do not start a new driver if
     // StateCallback() receives an error for this stream while the main thread
     // or another driver has control of the graph.
     mShouldFallbackIfError = false;
     RemoveMixerCallback();
     // Update the flag before handing over the graph and going to drain.
     mAudioThreadRunning = false;
@@ -1008,44 +1020,42 @@ void AudioCallbackDriver::PanOutputIfNee
     return;
   }
 
   if (!strncmp(name, "MacBookPro", 10)) {
     if (cubeb_stream_get_current_device(mAudioStream, &out) == CUBEB_OK) {
       // Check if we are currently outputing sound on external speakers.
       if (!strcmp(out->output_name, "ispk")) {
         // Pan everything to the right speaker.
-        if (aMicrophoneActive) {
-          if (cubeb_stream_set_panning(mAudioStream, 1.0) != CUBEB_OK) {
-            NS_WARNING("Could not pan audio output to the right.");
-          }
-        } else {
-          if (cubeb_stream_set_panning(mAudioStream, 0.0) != CUBEB_OK) {
-            NS_WARNING("Could not pan audio output to the center.");
-          }
-        }
+        LOG(LogLevel::Debug, ("Using the built-in speakers, with%s audio input",
+                              aMicrophoneActive ? "" : "out"));
+        mNeedsPanning = aMicrophoneActive;
       } else {
-        if (cubeb_stream_set_panning(mAudioStream, 0.0) != CUBEB_OK) {
-          NS_WARNING("Could not pan audio output to the center.");
-        }
+        LOG(LogLevel::Debug, ("Using an external output device"));
+        mNeedsPanning = false;
       }
       cubeb_stream_device_destroy(mAudioStream, out);
     }
   }
 #endif
 }
 
 void AudioCallbackDriver::DeviceChangedCallback() {
   MOZ_ASSERT(!OnGraphThread());
   // Tell the audio engine the device has changed, it might want to reset some
   // state.
   MonitorAutoLock mon(mGraphImpl->GetMonitor());
   GraphImpl()->DeviceChanged();
 #ifdef XP_MACOSX
-  PanOutputIfNeeded(mInputChannelCount);
+  RefPtr<AudioCallbackDriver> self(this);
+  bool hasInput = mInputChannelCount;
+  NS_DispatchToBackgroundThread(NS_NewRunnableFunction(
+      "PanOutputIfNeeded", [self{std::move(self)}, hasInput]() {
+        self->PanOutputIfNeeded(hasInput);
+      }));
 #endif
 }
 
 uint32_t AudioCallbackDriver::IterationDuration() {
   MOZ_ASSERT(OnGraphThread());
   // The real fix would be to have an API in cubeb to give us the number. Short
   // of that, we approximate it here. bug 1019507
   return mIterationDurationMS;
--- a/dom/media/GraphDriver.h
+++ b/dom/media/GraphDriver.h
@@ -529,16 +529,22 @@ class AudioCallbackDriver : public Graph
    * After transitioning to false on the last DataCallback(), the stream is
    * not accessed from another thread until the graph thread either signals
    * main thread cleanup or dispatches an event to switch to another
    * driver. */
   bool mShouldFallbackIfError;
   /* True if this driver was created from a driver created because of a previous
    * AudioCallbackDriver failure. */
   bool mFromFallback;
+#ifdef XP_MACOSX
+  /* When using the built-in speakers on macbook pro (13 and 15, all models),
+   * it's best to hard pan the audio on the right, to avoid feedback into the
+   * microphone that is located next to the left speaker.  */
+  Atomic<bool> mNeedsPanning;
+#endif
 };
 
 class AsyncCubebTask : public Runnable {
  public:
   AsyncCubebTask(AudioCallbackDriver* aDriver, AsyncCubebOperation aOperation);
 
   nsresult Dispatch(uint32_t aFlags = NS_DISPATCH_NORMAL) {
     return mDriver->mInitShutdownThread->Dispatch(this, aFlags);
--- a/dom/media/MediaTrackGraph.cpp
+++ b/dom/media/MediaTrackGraph.cpp
@@ -29,16 +29,17 @@
 #include "VideoFrameContainer.h"
 #include "mozilla/AbstractThread.h"
 #include "mozilla/StaticPrefs_dom.h"
 #include "mozilla/Unused.h"
 #include "mtransport/runnable_utils.h"
 #include "VideoUtils.h"
 #include "GraphRunner.h"
 #include "Tracing.h"
+#include "UnderrunHandler.h"
 
 #include "webaudio/blink/DenormalDisabler.h"
 #include "webaudio/blink/HRTFDatabaseLoader.h"
 
 using namespace mozilla::layers;
 using namespace mozilla::dom;
 using namespace mozilla::gfx;
 using namespace mozilla::media;
@@ -1292,16 +1293,20 @@ bool MediaTrackGraphImpl::OneIteration(G
   }
 
   return OneIterationImpl(aStateEnd);
 }
 
 bool MediaTrackGraphImpl::OneIterationImpl(GraphTime aStateEnd) {
   TRACE_AUDIO_CALLBACK();
 
+  if (SoftRealTimeLimitReached()) {
+    DemoteThreadFromRealTime();
+  }
+
   // Changes to LIFECYCLE_RUNNING occur before starting or reviving the graph
   // thread, and so the monitor need not be held to check mLifecycleState.
   // LIFECYCLE_THREAD_NOT_STARTED is possible when shutting down offline
   // graphs that have not started.
   MOZ_DIAGNOSTIC_ASSERT(mLifecycleState <= LIFECYCLE_RUNNING);
   MOZ_ASSERT(OnGraphThread());
 
   WebCore::DenormalDisabler disabler;
new file mode 100644
--- /dev/null
+++ b/dom/media/UnderrunHandler.h
@@ -0,0 +1,22 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_UNDERRUNHANDLER_H_
+#define MOZILLA_UNDERRUNHANDLER_H_
+
+namespace mozilla {
+// Install an handler on SIGXPCU for the calling thread, that calls
+// `UnderrunHandler` when the soft real-time limit has been reached. If a
+// handler was already in place, this does nothing. No-op if not on Linux
+// Desktop.
+void InstallSoftRealTimeLimitHandler();
+// Query whether or not the soft-real-time limit has been reached. Always
+// false when not on Linux desktop. Can be called from any thread.
+bool SoftRealTimeLimitReached();
+// Set the calling thread to a normal scheduling class.
+void DemoteThreadFromRealTime();
+}  // namespace mozilla
+
+#endif  // MOZILLA_UNDERRUNHANDLER_H_
new file mode 100644
--- /dev/null
+++ b/dom/media/UnderrunHandlerLinux.cpp
@@ -0,0 +1,77 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <csignal>
+#include <cerrno>
+#include <pthread.h>
+
+#include <mozilla/Sprintf.h>
+#include <mozilla/Atomics.h>
+#include "audio_thread_priority.h"
+
+namespace mozilla {
+
+Atomic<bool, MemoryOrdering::ReleaseAcquire> gRealtimeLimitReached;
+
+void UnderrunHandler(int signum) { gRealtimeLimitReached = true; }
+
+bool SoftRealTimeLimitReached() { return gRealtimeLimitReached; }
+
+void InstallSoftRealTimeLimitHandler() {
+  struct sigaction action, previous;
+
+  action.sa_handler = UnderrunHandler;
+  sigemptyset(&action.sa_mask);
+  action.sa_flags = 0;
+
+  int rv = sigaction(SIGXCPU, &action, &previous);
+  if (rv != 0) {
+    char buf[256];
+    SprintfLiteral(buf, "sigaction(SIGXCPU, ...): %s", strerror(errno));
+    NS_WARNING(buf);
+    return;
+  }
+
+  void* previous_handler = previous.sa_flags == SA_SIGINFO
+                               ? reinterpret_cast<void*>(previous.sa_sigaction)
+                               : reinterpret_cast<void*>(previous.sa_handler);
+
+  MOZ_ASSERT(previous_handler != UnderrunHandler,
+             "Only install the SIGXCPU handler once per process.");
+
+  if (previous_handler != SIG_DFL && previous_handler != UnderrunHandler) {
+    NS_WARNING(
+        "SIGXCPU handler was already set by something else, dropping real-time "
+        "priority.");
+    rv = sigaction(SIGXCPU, &previous, nullptr);
+    if (rv != 0) {
+      NS_WARNING("Could not restore previous handler for SIGXCPU.");
+    }
+    gRealtimeLimitReached = true;
+    return;
+  }
+
+  gRealtimeLimitReached = false;
+}
+
+void DemoteThreadFromRealTime() {
+  atp_thread_info* info = atp_get_current_thread_info();
+  if (!info) {
+    NS_WARNING("Could not get current thread info when demoting thread.");
+    return;
+  }
+  int rv = atp_demote_thread_from_real_time(info);
+  if (rv) {
+    NS_WARNING("Could not demote thread from real-time.");
+    return;
+  }
+  rv = atp_free_thread_info(info);
+  if (rv) {
+    NS_WARNING("Could not free atp_thread_info struct");
+  }
+  gRealtimeLimitReached = false;
+}
+
+}  // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/media/UnderrunHandlerNoop.cpp
@@ -0,0 +1,14 @@
+/* -*- 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/. */
+
+namespace mozilla {
+
+bool SoftRealTimeLimitReached() { return false; }
+
+void InstallSoftRealTimeLimitHandler() {}
+
+void DemoteThreadFromRealTime() {}
+
+}  // namespace mozilla
--- a/dom/media/VideoFrameContainer.cpp
+++ b/dom/media/VideoFrameContainer.cpp
@@ -6,16 +6,17 @@
 
 #include "VideoFrameContainer.h"
 
 #ifdef MOZ_WIDGET_ANDROID
 #include "GLImages.h"  // for SurfaceTextureImage
 #endif
 #include "MediaDecoderOwner.h"
 #include "mozilla/Telemetry.h"
+#include "mozilla/AbstractThread.h"
 
 using namespace mozilla::layers;
 
 namespace mozilla {
 static LazyLogModule gVideoFrameContainerLog("VideoFrameContainer");
 #define CONTAINER_LOG(type, msg) MOZ_LOG(gVideoFrameContainerLog, type, msg)
 
 #define NS_DispatchToMainThread(...) CompileError_UseAbstractMainThreadInstead
--- a/dom/media/moz.build
+++ b/dom/media/moz.build
@@ -181,16 +181,17 @@ EXPORTS += [
     'VideoUtils.h',
     'VorbisUtils.h',
     'WavDumper.h',
     'XiphExtradata.h',
 ]
 
 EXPORTS.mozilla += [
     'MediaManager.h',
+    'UnderrunHandler.h',
 ]
 
 EXPORTS.mozilla.media.webrtc += [
     'webrtc/WebrtcGlobal.h',
     'webrtc/WebrtcIPCTraits.h',
 ]
 
 if not CONFIG['MOZ_WEBRTC']:
@@ -297,16 +298,21 @@ UNIFIED_SOURCES += [
     'VideoStreamTrack.cpp',
     'VideoTrack.cpp',
     'VideoTrackList.cpp',
     'VideoUtils.cpp',
     'WebVTTListener.cpp',
     'XiphExtradata.cpp',
 ]
 
+if CONFIG['OS_TARGET'] == 'Linux':
+    UNIFIED_SOURCES += [ 'UnderrunHandlerLinux.cpp' ]
+else:
+    UNIFIED_SOURCES += [ 'UnderrunHandlerNoop.cpp']
+
 if CONFIG['OS_TARGET'] == 'WINNT':
   EXPORTS.mozilla.audio += [
       'AudioNotificationReceiver.h',
       'AudioNotificationSender.h',
   ]
   SOURCES += [
       'AudioNotificationReceiver.cpp',
       'AudioNotificationSender.cpp',
--- a/dom/plugins/base/nsNPAPIPlugin.cpp
+++ b/dom/plugins/base/nsNPAPIPlugin.cpp
@@ -746,17 +746,17 @@ NPUTF8* _utf8fromidentifier(NPIdentifier
   if (!id) return nullptr;
 
   if (!NPIdentifierIsString(id)) {
     return nullptr;
   }
 
   JSString* str = NPIdentifierToString(id);
   nsAutoString autoStr;
-  AssignJSFlatString(autoStr, JS_ASSERT_STRING_IS_FLAT(str));
+  AssignJSLinearString(autoStr, JS_ASSERT_STRING_IS_LINEAR(str));
 
   return ToNewUTF8String(autoStr);
 }
 
 int32_t _intfromidentifier(NPIdentifier id) {
   if (!NS_IsMainThread()) {
     NPN_PLUGIN_LOG(PLUGIN_LOG_ALWAYS,
                    ("NPN_intfromidentifier called from the wrong thread\n"));
deleted file mode 100644
--- a/dom/webidl/LegacyQueryInterface.webidl
+++ /dev/null
@@ -1,16 +0,0 @@
-/* -*- Mode: IDL; 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/.
- */
-
-interface nsISupports;
-
-[Exposed=Window]
-interface mixin LegacyQueryInterface {
-  // Legacy QueryInterface, only exposed to chrome code on the main thread.
-  [Exposed=Window, ChromeOnly]
-  nsISupports QueryInterface(any iid);
-};
-
-Element includes LegacyQueryInterface;
--- a/dom/webidl/moz.build
+++ b/dom/webidl/moz.build
@@ -630,17 +630,16 @@ WEBIDL_FILES = [
     'IntlUtils.webidl',
     'IterableIterator.webidl',
     'KeyAlgorithm.webidl',
     'KeyboardEvent.webidl',
     'KeyEvent.webidl',
     'KeyframeAnimationOptions.webidl',
     'KeyframeEffect.webidl',
     'KeyIdsInitData.webidl',
-    'LegacyQueryInterface.webidl',
     'LinkStyle.webidl',
     'LoadURIOptions.webidl',
     'Location.webidl',
     'MathMLElement.webidl',
     'MediaCapabilities.webidl',
     'MediaDebugInfo.webidl',
     'MediaDeviceInfo.webidl',
     'MediaDevices.webidl',
--- a/dom/xbl/test/chrome.ini
+++ b/dom/xbl/test/chrome.ini
@@ -1,15 +1,13 @@
 [DEFAULT]
 support-files =
-  file_bug950909.xml
   file_fieldScopeChain.xml
 
 [test_bug378518.xul]
 skip-if = (verify && debug && (os == 'linux' || os == 'mac'))
 [test_bug398135.xul]
 [test_bug398492.xul]
 [test_bug721452.xul]
 [test_bug723676.xul]
 [test_bug772966.xul]
-[test_bug950909.xul]
 [test_bug1086996.xhtml]
 [test_fieldScopeChain.html]
deleted file mode 100644
--- a/dom/xbl/test/file_bug950909.html
+++ /dev/null
@@ -1,8 +0,0 @@
-<!DOCTYPE html>
-<html>
-<head>
-</head>
-<body>
-<div style="-moz-binding: url(chrome://mochitests/content/chrome/dom/xbl/test/file_bug950909.xml#testBinding"></div>
-</body>
-</html>
deleted file mode 100644
--- a/dom/xbl/test/file_bug950909.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-<?xml version="1.0"?>
-<bindings id="chromeTestBindings" xmlns="http://www.mozilla.org/xbl">
-  <binding id="testBinding" bindToUntrustedContent="true">
-    <implementation implements="nsIObserver">
-      <constructor>
-      <![CDATA[
-        var win = window;
-        var SpecialPowers = win.SpecialPowers;
-        var ok = SpecialPowers.unwrap(SpecialPowers.wrap(window).parent.ok);
-
-        // Generate an XPCWrappedJS for the reflector. We need to do this
-        // explicitly with a testing helper, because we're converging on
-        // removing XPConnect from the web, which means that it won't be
-        // possible to generate an XPCWrappedJS from content (or XBL) code.
-        var scope = {};
-        var holder = SpecialPowers.Cu.generateXPCWrappedJS(this, scope);
-
-        // Now, QI |this|, which will generate an aggregated native.
-        SpecialPowers.wrap(this).QueryInterface(SpecialPowers.Ci.nsIObserver);
-
-        ok(true, "Didn't assert or crash");
-
-        SpecialPowers.wrap(window).parent.SimpleTest.finish();
-      ]]>
-      </constructor>
-      <method name="observe">
-        <parameter name="aSubject"/>
-        <parameter name="aTopic"/>
-        <parameter name="aData"/>
-        <body><![CDATA[]]></body>
-      </method>
-    </implementation>
-  </binding>
-</bindings>
--- a/dom/xbl/test/mochitest.ini
+++ b/dom/xbl/test/mochitest.ini
@@ -8,17 +8,16 @@ support-files =
   file_bug379959_data.html
   file_bug379959_xbl.xml
   file_bug397934.xhtml
   file_bug481558.xbl
   file_bug481558css.sjs
   file_bug591198_inner.html
   file_bug591198_xbl.xml
   file_bug844783.xhtml
-  file_bug950909.html
 
 [test_bug310107.html]
 [test_bug366770.html]
 [test_bug371724.xhtml]
 [test_bug372769.html]
 [test_bug378866.xhtml]
 [test_bug379959.html]
 [test_bug389322.xhtml]
deleted file mode 100644
--- a/dom/xbl/test/test_bug950909.xul
+++ /dev/null
@@ -1,36 +0,0 @@
-<?xml version="1.0"?>
-<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
-<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
-<!--
-https://bugzilla.mozilla.org/show_bug.cgi?id=950909
--->
-<window title="Mozilla Bug 950909"
-        xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
-  <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
-
-  <!-- test results are displayed in the html:body -->
-  <body xmlns="http://www.w3.org/1999/xhtml">
-  <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=950909"
-     target="_blank">Mozilla Bug 950909</a>
-  </body>
-
-  <!-- test code goes here -->
-  <script type="application/javascript">
-  <![CDATA[
-
-  /*
-   * Test for bug 950909. This has to be a chrome tests because content needs
-   * to apply a chrome binding (file_bug950909.xml), which will only be registered
-   * as a chrome:// URI during mochitest-chrome runs. And the binding has to be
-   * served from a chrome origin, because otherwise implements="nsIFoo" attributes
-   * are ignored. So this test needs 3 files, all told. Ugh.
-   */
-
-  // Just wait. When the iframe loads, it'll apply the binding, which will
-  // trigger the constructor for the binding.
-  SimpleTest.waitForExplicitFinish();
-
-  ]]>
-  </script>
-  <iframe src="http://example.com/tests/dom/xbl/test/file_bug950909.html"/>
-</window>
deleted file mode 100644
--- a/dom/xul/crashtests/429085-1.xhtml
+++ /dev/null
@@ -1,21 +0,0 @@
-<html xmlns="http://www.w3.org/1999/xhtml">
-<head>
-<script type="text/javascript">
-<![CDATA[
-
-function boom()
-{
-  var XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
-  var prefs = document.createElementNS(XUL_NS, "preferences");
-  var textbox = document.createElementNS(XUL_NS, "textbox");
-  textbox.setAttribute("onchange", "1");
-  prefs.appendChild(textbox);
-  prefs.cloneNode(true);
-}
-
-]]>
-</script>
-</head>
-
-<body onload="boom();"></body>
-</html>
--- a/dom/xul/crashtests/crashtests.list
+++ b/dom/xul/crashtests/crashtests.list
@@ -12,14 +12,13 @@ load 354611-1.html
 skip-if(!xbl) load 360078-1.xhtml
 load chrome://reftest/content/crashtests/dom/xul/crashtests/363791-1.xul
 load chrome://reftest/content/crashtests/dom/xul/crashtests/384740-1.xul
 load 384877-1.html
 load 386914-1.html
 skip-if(!xbl) asserts(1) load chrome://reftest/content/crashtests/dom/xul/crashtests/386947-1.xul
 load chrome://reftest/content/crashtests/dom/xul/crashtests/425821-1.xul
 load chrome://reftest/content/crashtests/dom/xul/crashtests/428951-1.xul
-load 429085-1.xhtml
 load 431906-1.html
 load 461917-1.xhtml
 skip-if(!xbl) load chrome://reftest/content/crashtests/dom/xul/crashtests/468211-1.xul
 skip-if(!xbl) load chrome://reftest/content/crashtests/dom/xul/crashtests/468211-2.xul
 skip-if(!xbl) load chrome://reftest/content/crashtests/dom/xul/crashtests/468211-3.xul
--- a/gfx/webrender_bindings/DCLayerTree.cpp
+++ b/gfx/webrender_bindings/DCLayerTree.cpp
@@ -37,17 +37,19 @@ UniquePtr<DCLayerTree> DCLayerTree::Crea
   if (!layerTree->Initialize(aHwnd)) {
     return nullptr;
   }
 
   return layerTree;
 }
 
 DCLayerTree::DCLayerTree(IDCompositionDevice2* aCompositionDevice)
-    : mCompositionDevice(aCompositionDevice) {}
+    : mCompositionDevice(aCompositionDevice),
+      mDebugCounter(false),
+      mDebugVisualRedrawRegions(false) {}
 
 DCLayerTree::~DCLayerTree() {}
 
 bool DCLayerTree::Initialize(HWND aHwnd) {
   HRESULT hr;
 
   RefPtr<IDCompositionDesktopDevice> desktopDevice;
   hr = mCompositionDevice->QueryInterface(
@@ -73,28 +75,16 @@ bool DCLayerTree::Initialize(HWND aHwnd)
 
   hr =
       mCompositionDevice->CreateVisual(getter_AddRefs(mDefaultSwapChainVisual));
   if (FAILED(hr)) {
     gfxCriticalNote << "Failed to create DCompositionVisual: " << gfx::hexa(hr);
     return false;
   }
 
-  if (StaticPrefs::gfx_webrender_dcomp_win_debug_counter_enabled_AtStartup()) {
-    RefPtr<IDCompositionDeviceDebug> debugDevice;
-    hr = mCompositionDevice->QueryInterface(
-        (IDCompositionDeviceDebug**)getter_AddRefs(debugDevice));
-    if (SUCCEEDED(hr)) {
-      debugDevice->EnableDebugCounters();
-    } else {
-      gfxCriticalNote << "Failed to get IDCompositionDesktopDevice: "
-                      << gfx::hexa(hr);
-    }
-  }
-
   mCompositionTarget->SetRoot(mRootVisual);
   // Set interporation mode to Linear.
   // By default, a visual inherits the interpolation mode of the parent visual.
   // If no visuals set the interpolation mode, the default for the entire visual
   // tree is nearest neighbor interpolation.
   mRootVisual->SetBitmapInterpolationMode(
       DCOMPOSITION_BITMAP_INTERPOLATION_MODE_LINEAR);
   return true;
@@ -104,11 +94,67 @@ void DCLayerTree::SetDefaultSwapChain(ID
   mRootVisual->AddVisual(mDefaultSwapChainVisual, TRUE, nullptr);
   mDefaultSwapChainVisual->SetContent(aSwapChain);
   // Default SwapChain's visual does not need linear interporation.
   mDefaultSwapChainVisual->SetBitmapInterpolationMode(
       DCOMPOSITION_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR);
   mCompositionDevice->Commit();
 }
 
+void DCLayerTree::MaybeUpdateDebug() {
+  bool updated = false;
+  updated |= MaybeUpdateDebugCounter();
+  updated |= MaybeUpdateDebugVisualRedrawRegions();
+  if (updated) {
+    mCompositionDevice->Commit();
+  }
+}
+
+bool DCLayerTree::MaybeUpdateDebugCounter() {
+  bool debugCounter = StaticPrefs::gfx_webrender_debug_dcomp_counter();
+  if (mDebugCounter == debugCounter) {
+    return false;
+  }
+
+  RefPtr<IDCompositionDeviceDebug> debugDevice;
+  HRESULT hr = mCompositionDevice->QueryInterface(
+      (IDCompositionDeviceDebug**)getter_AddRefs(debugDevice));
+  if (FAILED(hr)) {
+    return false;
+  }
+
+  if (debugCounter) {
+    debugDevice->EnableDebugCounters();
+  } else {
+    debugDevice->DisableDebugCounters();
+  }
+
+  mDebugCounter = debugCounter;
+  return true;
+}
+
+bool DCLayerTree::MaybeUpdateDebugVisualRedrawRegions() {
+  bool debugVisualRedrawRegions =
+      StaticPrefs::gfx_webrender_debug_dcomp_redraw_regions();
+  if (mDebugVisualRedrawRegions == debugVisualRedrawRegions) {
+    return false;
+  }
+
+  RefPtr<IDCompositionVisualDebug> visualDebug;
+  HRESULT hr = mRootVisual->QueryInterface(
+      (IDCompositionVisualDebug**)getter_AddRefs(visualDebug));
+  if (FAILED(hr)) {
+    return false;
+  }
+
+  if (debugVisualRedrawRegions) {
+    visualDebug->EnableRedrawRegions();
+  } else {
+    visualDebug->DisableRedrawRegions();
+  }
+
+  mDebugVisualRedrawRegions = debugVisualRedrawRegions;
+  return true;
+}
+
 #endif
 }  // namespace wr
 }  // namespace mozilla
--- a/gfx/webrender_bindings/DCLayerTree.h
+++ b/gfx/webrender_bindings/DCLayerTree.h
@@ -30,29 +30,36 @@ namespace wr {
  */
 class DCLayerTree {
  public:
   static UniquePtr<DCLayerTree> Create(HWND aHwnd);
   explicit DCLayerTree(IDCompositionDevice2* aCompositionDevice);
   ~DCLayerTree();
 
   void SetDefaultSwapChain(IDXGISwapChain1* aSwapChain);
+  void MaybeUpdateDebug();
 
  protected:
   bool Initialize(HWND aHwnd);
+  bool MaybeUpdateDebugCounter();
+  bool MaybeUpdateDebugVisualRedrawRegions();
 
   RefPtr<IDCompositionDevice2> mCompositionDevice;
   RefPtr<IDCompositionTarget> mCompositionTarget;
   RefPtr<IDCompositionVisual2> mRootVisual;
   RefPtr<IDCompositionVisual2> mDefaultSwapChainVisual;
+
+  bool mDebugCounter;
+  bool mDebugVisualRedrawRegions;
 };
 #else
 class DCLayerTree {
  public:
   static UniquePtr<DCLayerTree> Create(HWND aHwnd) { return nullptr; }
   void SetDefaultSwapChain(IDXGISwapChain1* aSwapChain) {}
+  void MaybeUpdateDebug() {}
 };
 #endif
 
 }  // namespace wr
 }  // namespace mozilla
 
 #endif
--- a/gfx/webrender_bindings/RenderCompositorANGLE.cpp
+++ b/gfx/webrender_bindings/RenderCompositorANGLE.cpp
@@ -401,16 +401,20 @@ void RenderCompositorANGLE::EndFrame() {
     // window's contents to the VR window.
     FxROutputHandler* fxrHandler = mWidget->AsWindows()->GetFxrOutputHandler();
     if (fxrHandler->TryInitialize(mSwapChain, mDevice)) {
       fxrHandler->UpdateOutput(mCtx);
     }
   }
 
   mSwapChain->Present(0, 0);
+
+  if (mDCLayerTree) {
+    mDCLayerTree->MaybeUpdateDebug();
+  }
 }
 
 bool RenderCompositorANGLE::WaitForGPU() {
   // Note: this waits on the query we inserted in the previous frame,
   // not the one we just inserted now. Example:
   //   Insert query #1
   //   Present #1
   //   (first frame, no wait)
--- a/js/ipc/WrapperOwner.cpp
+++ b/js/ipc/WrapperOwner.cpp
@@ -23,31 +23,29 @@ using namespace js;
 using namespace JS;
 using namespace mozilla;
 using namespace mozilla::jsipc;
 
 struct AuxCPOWData {
   ObjectId id;
   bool isCallable;
   bool isConstructor;
-  bool isDOMObject;
 
   // The object tag is just some auxilliary information that clients can use
   // however they see fit.
   nsCString objectTag;
 
   // The class name for WrapperOwner::className, below.
   nsCString className;
 
   AuxCPOWData(ObjectId id, bool isCallable, bool isConstructor,
-              bool isDOMObject, const nsACString& objectTag)
+              const nsACString& objectTag)
       : id(id),
         isCallable(isCallable),
         isConstructor(isConstructor),
-        isDOMObject(isDOMObject),
         objectTag(objectTag) {}
 };
 
 WrapperOwner::WrapperOwner() : inactive_(false) {}
 
 static inline AuxCPOWData* AuxCPOWDataOf(JSObject* obj) {
   MOZ_ASSERT(IsCPOW(obj));
   return static_cast<AuxCPOWData*>(GetProxyReservedSlot(obj, 1).toPrivate());
@@ -307,27 +305,16 @@ bool WrapperOwner::hasOwn(JSContext* cx,
 }
 
 bool CPOWProxyHandler::get(JSContext* cx, HandleObject proxy,
                            HandleValue receiver, HandleId id,
                            MutableHandleValue vp) const {
   FORWARD(get, (cx, proxy, receiver, id, vp), false);
 }
 
-static bool CPOWDOMQI(JSContext* cx, unsigned argc, Value* vp) {
-  CallArgs args = CallArgsFromVp(argc, vp);
-  if (!args.thisv().isObject() || !IsCPOW(&args.thisv().toObject())) {
-    JS_ReportErrorASCII(cx, "bad this object passed to special QI");
-    return false;
-  }
-
-  RootedObject proxy(cx, &args.thisv().toObject());
-  FORWARD(DOMQI, (cx, proxy, args), false);
-}
-
 static bool CPOWToString(JSContext* cx, unsigned argc, Value* vp) {
   CallArgs args = CallArgsFromVp(argc, vp);
   RootedObject callee(cx, &args.callee());
   RootedValue cpowValue(cx);
   if (!JS_GetProperty(cx, callee, "__cpow__", &cpowValue)) {
     return false;
   }
 
@@ -374,80 +361,30 @@ bool WrapperOwner::toString(JSContext* c
   if (!str) {
     return false;
   }
 
   args.rval().setString(str);
   return true;
 }
 
-bool WrapperOwner::DOMQI(JSContext* cx, JS::HandleObject proxy,
-                         JS::CallArgs& args) {
-  // Someone's calling us, handle nsISupports specially to avoid unnecessary
-  // CPOW traffic.
-  if (Maybe<nsID> id = xpc::JSValue2ID(cx, args[0])) {
-    if (id->Equals(NS_GET_IID(nsISupports))) {
-      args.rval().set(args.thisv());
-      return true;
-    }
-
-    // Webidl-implemented DOM objects never have nsIClassInfo.
-    if (id->Equals(NS_GET_IID(nsIClassInfo))) {
-      return Throw(cx, NS_ERROR_NO_INTERFACE);
-    }
-  }
-
-  // It wasn't nsISupports, call into the other process to do the QI for us
-  // (since we don't know what other interfaces our object supports). Note
-  // that we have to use JS_GetPropertyDescriptor here to avoid infinite
-  // recursion back into CPOWDOMQI via WrapperOwner::get().
-  // We could stash the actual QI function on our own function object to avoid
-  // if we're called multiple times, but since we're transient, there's no
-  // point right now.
-  JS::Rooted<PropertyDescriptor> propDesc(cx);
-  if (!JS_GetPropertyDescriptor(cx, proxy, "QueryInterface", &propDesc)) {
-    return false;
-  }
-
-  if (!propDesc.value().isObject()) {
-    MOZ_ASSERT_UNREACHABLE("We didn't get QueryInterface off a node");
-    return Throw(cx, NS_ERROR_UNEXPECTED);
-  }
-  return JS_CallFunctionValue(cx, proxy, propDesc.value(), args, args.rval());
-}
-
 bool WrapperOwner::get(JSContext* cx, HandleObject proxy, HandleValue receiver,
                        HandleId id, MutableHandleValue vp) {
   ObjectId objId = idOf(proxy);
 
   JSVariant receiverVar;
   if (!toVariant(cx, receiver, &receiverVar)) {
     return false;
   }
 
   JSIDVariant idVar;
   if (!toJSIDVariant(cx, id, &idVar)) {
     return false;
   }
 
-  AuxCPOWData* data = AuxCPOWDataOf(proxy);
-  if (data->isDOMObject && idVar.type() == JSIDVariant::TnsString &&
-      idVar.get_nsString().EqualsLiteral("QueryInterface")) {
-    // Handle QueryInterface on DOM Objects specially since we can assume
-    // certain things about their implementation.
-    RootedFunction qi(cx,
-                      JS_NewFunction(cx, CPOWDOMQI, 1, 0, "QueryInterface"));
-    if (!qi) {
-      return false;
-    }
-
-    vp.set(ObjectValue(*JS_GetFunctionObject(qi)));
-    return true;
-  }
-
   JSVariant val;
   ReturnStatus status;
   if (!SendGet(objId, receiverVar, idVar, &status, &val)) {
     return ipcfail(cx);
   }
 
   LOG_STACK();
 
@@ -1141,19 +1078,18 @@ JSObject* WrapperOwner::fromRemoteObject
       return nullptr;
     }
 
     nextCPOWNumber_ = objId.serialNumber() + 1;
 
     // Incref once we know the decref will be called.
     incref();
 
-    AuxCPOWData* aux =
-        new AuxCPOWData(objId, objVar.isCallable(), objVar.isConstructor(),
-                        objVar.isDOMObject(), objVar.objectTag());
+    AuxCPOWData* aux = new AuxCPOWData(
+        objId, objVar.isCallable(), objVar.isConstructor(), objVar.objectTag());
 
     SetProxyReservedSlot(obj, 0, PrivateValue(this));
     SetProxyReservedSlot(obj, 1, PrivateValue(aux));
   }
 
   if (!JS_WrapObject(cx, &obj)) {
     return nullptr;
   }
--- a/js/ipc/WrapperOwner.h
+++ b/js/ipc/WrapperOwner.h
@@ -62,17 +62,16 @@ class WrapperOwner : public virtual Java
   bool getPrototypeIfOrdinary(JSContext* cx, JS::HandleObject proxy,
                               bool* isOrdinary, JS::MutableHandleObject protop);
 
   js::RegExpShared* regexp_toShared(JSContext* cx, JS::HandleObject proxy);
 
   nsresult instanceOf(JSObject* obj, const nsID* id, bool* bp);
 
   bool toString(JSContext* cx, JS::HandleObject callee, JS::CallArgs& args);
-  bool DOMQI(JSContext* cx, JS::HandleObject callee, JS::CallArgs& args);
 
   /*
    * Check that |obj| is a DOM wrapper whose prototype chain contains
    * |prototypeID| at depth |depth|.
    */
   bool domInstanceOf(JSContext* cx, JSObject* obj, int prototypeID, int depth,
                      bool* bp);
 
--- a/js/public/CharacterEncoding.h
+++ b/js/public/CharacterEncoding.h
@@ -8,17 +8,17 @@
 #define js_CharacterEncoding_h
 
 #include "mozilla/Range.h"
 #include "mozilla/Span.h"
 
 #include "js/TypeDecls.h"
 #include "js/Utility.h"
 
-class JSFlatString;
+class JSLinearString;
 
 namespace JS {
 
 /*
  * By default, all C/C++ 1-byte-per-character strings passed into the JSAPI
  * are treated as ISO/IEC 8859-1, also known as Latin-1. That is, each
  * byte is treated as a 2-byte character, and there is no way to pass in a
  * string containing characters beyond U+00FF.
@@ -278,35 +278,35 @@ LossyUTF8CharsToNewTwoByteCharsZ(JSConte
 extern JS_PUBLIC_API TwoByteCharsZ
 LossyUTF8CharsToNewTwoByteCharsZ(JSContext* cx, const ConstUTF8CharsZ& utf8,
                                  size_t* outlen, arena_id_t destArenaId);
 
 /*
  * Returns the length of the char buffer required to encode |s| as UTF8.
  * Does not include the null-terminator.
  */
-JS_PUBLIC_API size_t GetDeflatedUTF8StringLength(JSFlatString* s);
+JS_PUBLIC_API size_t GetDeflatedUTF8StringLength(JSLinearString* s);
 
 /*
  * Encode whole scalar values of |src| into |dst| as UTF-8 until |src| is
  * exhausted or too little space is available in |dst| to fit the scalar
  * value. Lone surrogates are converted to REPLACEMENT CHARACTER. Return
  * the number of bytes of |dst| that were filled.
  *
  * Use |JS_EncodeStringToUTF8BufferPartial| if your string isn't already
- * flat.
+ * linear.
  *
- * Given |JSString* str = JS_FORGET_STRING_FLATNESS(src)|,
+ * Given |JSString* str = JS_FORGET_STRING_LINEARNESS(src)|,
  * if |JS_StringHasLatin1Chars(str)|, then |src| is always fully converted
  * if |dst.Length() >= JS_GetStringLength(str) * 2|. Otherwise |src| is
  * always fully converted if |dst.Length() >= JS_GetStringLength(str) * 3|.
  *
  * The exact space required is always |GetDeflatedUTF8StringLength(str)|.
  */
-JS_PUBLIC_API size_t DeflateStringToUTF8Buffer(JSFlatString* src,
+JS_PUBLIC_API size_t DeflateStringToUTF8Buffer(JSLinearString* src,
                                                mozilla::Span<char> dst);
 
 /*
  * The smallest character encoding capable of fully representing a particular
  * string.
  */
 enum class SmallestEncoding { ASCII, Latin1, UTF16 };
 
--- a/js/public/HeapAPI.h
+++ b/js/public/HeapAPI.h
@@ -258,18 +258,16 @@ class JS_FRIEND_API GCCellPtr {
       : ptr(checkedCast(nullptr, JS::TraceKind::Null)) {}
 
   // Construction from an explicit type.
   template <typename T>
   explicit GCCellPtr(T* p)
       : ptr(checkedCast(p, JS::MapTypeToTraceKind<T>::kind)) {}
   explicit GCCellPtr(JSFunction* p)
       : ptr(checkedCast(p, JS::TraceKind::Object)) {}
-  explicit GCCellPtr(JSFlatString* str)
-      : ptr(checkedCast(str, JS::TraceKind::String)) {}
   explicit GCCellPtr(const Value& v);
 
   JS::TraceKind kind() const {
     JS::TraceKind traceKind = JS::TraceKind(ptr & OutOfLineTraceKindMask);
     if (uintptr_t(traceKind) != OutOfLineTraceKindMask) {
       return traceKind;
     }
     return outOfLineKind();
--- a/js/rust/build.rs
+++ b/js/rust/build.rs
@@ -173,17 +173,17 @@ const WHITELIST_TYPES: &'static [&'stati
     "JSClass",
     "JSClassOps",
     "JSContext",
     "JSErrNum",
     "JSErrorCallback",
     "JSErrorFormatString",
     "JSErrorReport",
     "JSExnType",
-    "JSFlatString",
+    "JSLinearString",
     "JSFunction",
     "JSFunctionSpec",
     "JS::GCDescription",
     "JSGCInvocationKind",
     "JSGCMode",
     "JSGCParamKey",
     "JS::GCProgress",
     "JSGCStatus",
--- a/js/rust/src/rust.rs
+++ b/js/rust/src/rust.rs
@@ -278,17 +278,17 @@ pub trait RootKind {
     fn rootKind() -> JS::RootKind;
 }
 
 impl RootKind for *mut JSObject {
     #[inline(always)]
     fn rootKind() -> JS::RootKind { JS::RootKind::Object }
 }
 
-impl RootKind for *mut JSFlatString {
+impl RootKind for *mut JSLinearString {
     #[inline(always)]
     fn rootKind() -> JS::RootKind { JS::RootKind::String }
 }
 
 impl RootKind for *mut JSFunction {
     #[inline(always)]
     fn rootKind() -> JS::RootKind { JS::RootKind::Object }
 }
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -577,29 +577,29 @@ static const struct ParamInfo {
 static bool GCParameter(JSContext* cx, unsigned argc, Value* vp) {
   CallArgs args = CallArgsFromVp(argc, vp);
 
   JSString* str = ToString(cx, args.get(0));
   if (!str) {
     return false;
   }
 
-  JSFlatString* flatStr = JS_FlattenString(cx, str);
-  if (!flatStr) {
+  JSLinearString* linearStr = JS_EnsureLinearString(cx, str);
+  if (!linearStr) {
     return false;
   }
 
   size_t paramIndex = 0;
   for (;; paramIndex++) {
     if (paramIndex == ArrayLength(paramMap)) {
       JS_ReportErrorASCII(
           cx, "the first argument must be one of:" GC_PARAMETER_ARGS_LIST);
       return false;
     }
-    if (JS_FlatStringEqualsAscii(flatStr, paramMap[paramIndex].name)) {
+    if (JS_LinearStringEqualsAscii(linearStr, paramMap[paramIndex].name)) {
       break;
     }
   }
   const ParamInfo& info = paramMap[paramIndex];
   JSGCParamKey param = info.param;
 
   // Request mode.
   if (args.length() == 1) {
@@ -840,16 +840,18 @@ static bool WasmTextToBinary(JSContext* 
     return false;
   }
 
   if (!args[0].isString()) {
     ReportUsageErrorASCII(cx, callee, "First argument must be a String");
     return false;
   }
 
+  size_t textLen = args[0].toString()->length();
+
   AutoStableStringChars twoByteChars(cx);
   if (!twoByteChars.initTwoByte(cx, args[0].toString())) {
     return false;
   }
 
   bool withOffsets = false;
   if (args.hasDefined(1)) {
     if (!args[1].isBoolean()) {
@@ -860,18 +862,18 @@ static bool WasmTextToBinary(JSContext* 
     withOffsets = ToBoolean(args[1]);
   }
 
   uintptr_t stackLimit = GetNativeStackLimit(cx);
 
   wasm::Bytes bytes;
   UniqueChars error;
   wasm::Uint32Vector offsets;
-  if (!wasm::TextToBinary(twoByteChars.twoByteChars(), stackLimit, &bytes,
-                          &offsets, &error)) {
+  if (!wasm::TextToBinary(twoByteChars.twoByteChars(), textLen, stackLimit,
+                          &bytes, &offsets, &error)) {
     JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
                               JSMSG_WASM_TEXT_FAIL,
                               error.get() ? error.get() : "out of memory");
     return false;
   }
 
   RootedObject binary(cx, JS_NewUint8Array(cx, bytes.length()));
   if (!binary) {
@@ -1111,23 +1113,23 @@ static bool InternalConst(JSContext* cx,
     JS_ReportErrorASCII(cx, "the function takes exactly one argument");
     return false;
   }
 
   JSString* str = ToString(cx, args[0]);
   if (!str) {
     return false;
   }
-  JSFlatString* flat = JS_FlattenString(cx, str);
-  if (!flat) {
-    return false;
-  }
-
-  if (JS_FlatStringEqualsLiteral(flat,
-                                 "INCREMENTAL_MARK_STACK_BASE_CAPACITY")) {
+  JSLinearString* linear = JS_EnsureLinearString(cx, str);
+  if (!linear) {
+    return false;
+  }
+
+  if (JS_LinearStringEqualsLiteral(linear,
+                                   "INCREMENTAL_MARK_STACK_BASE_CAPACITY")) {
     args.rval().setNumber(uint32_t(js::INCREMENTAL_MARK_STACK_BASE_CAPACITY));
   } else {
     JS_ReportErrorASCII(cx, "unknown const name");
     return false;
   }
   return true;
 }
 
--- a/js/src/ctypes/CTypes.cpp
+++ b/js/src/ctypes/CTypes.cpp
@@ -989,17 +989,18 @@ static void BuildConversionPosition(JSCo
       BuildFunctionTypeSource(cx, funObj, source);
       break;
     default:
       MOZ_ASSERT(!funObj);
       break;
   }
 }
 
-static JSFlatString* GetFieldName(HandleObject structObj, unsigned fieldIndex) {
+static JSLinearString* GetFieldName(HandleObject structObj,
+                                    unsigned fieldIndex) {
   const FieldInfoHash* fields = StructType::GetFieldInfo(structObj);
   for (FieldInfoHash::Range r = fields->all(); !r.empty(); r.popFront()) {
     if (r.front().value().mIndex == fieldIndex) {
       return (&r.front())->key();
     }
   }
   return nullptr;
 }
@@ -1269,17 +1270,17 @@ static bool ArgumentTypeMismatch(JSConte
 }
 
 static bool CannotConstructError(JSContext* cx, const char* type) {
   JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
                             CTYPESMSG_CANNOT_CONSTRUCT, type);
   return false;
 }
 
-static bool DuplicateFieldError(JSContext* cx, Handle<JSFlatString*> name) {
+static bool DuplicateFieldError(JSContext* cx, Handle<JSLinearString*> name) {
   JS::UniqueChars nameStr = JS_EncodeStringToUTF8(cx, name);
   if (!nameStr) {
     return false;
   }
 
   JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
                            CTYPESMSG_DUPLICATE_FIELD, nameStr.get());
   return false;
@@ -1418,17 +1419,17 @@ static bool FieldDescriptorTypeError(JSC
   }
 
   JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
                            CTYPESMSG_FIELD_DESC_TYPE, typeStr, propStr.get());
   return false;
 }
 
 static bool FieldMissingError(JSContext* cx, JSObject* typeObj,
-                              JSFlatString* name_) {
+                              JSLinearString* name_) {
   JS::UniqueChars typeBytes;
   RootedString name(cx, name_);
   RootedValue typeVal(cx, ObjectValue(*typeObj));
   const char* typeStr = CTypesToSourceForError(cx, typeVal, typeBytes);
   if (!typeStr) {
     return false;
   }
 
@@ -3641,17 +3642,17 @@ static bool ImplicitConvert(JSContext* c
         for (size_t i = 0; i < props.length(); ++i) {
           id = props[i];
 
           if (!JSID_IS_STRING(id)) {
             return PropNameNonStringError(cx, id, val, convType, funObj,
                                           argIndex);
           }
 
-          JSFlatString* name = JSID_TO_FLAT_STRING(id);
+          JSLinearString* name = JSID_TO_LINEAR_STRING(id);
           const FieldInfo* field =
               StructType::LookupField(cx, targetType, name);
           if (!field) {
             return false;
           }
 
           RootedValue prop(cx);
           if (!JS_GetPropertyById(cx, valObj, id, &prop)) {
@@ -5700,18 +5701,18 @@ bool ArrayType::AddressOfElement(JSConte
 }
 
 /*******************************************************************************
 ** StructType implementation
 *******************************************************************************/
 
 // For a struct field descriptor 'val' of the form { name : type }, extract
 // 'name' and 'type'.
-static JSFlatString* ExtractStructField(JSContext* cx, HandleValue val,
-                                        MutableHandleObject typeObj) {
+static JSLinearString* ExtractStructField(JSContext* cx, HandleValue val,
+                                          MutableHandleObject typeObj) {
   if (val.isPrimitive()) {
     FieldDescriptorNameTypeError(cx, val);
     return nullptr;
   }
 
   RootedObject obj(cx, &val.toObject());
   Rooted<IdVector> props(cx, IdVector(cx));
   if (!JS_Enumerate(cx, obj, &props)) {
@@ -5745,25 +5746,25 @@ static JSFlatString* ExtractStructField(
   // choke on a zero-size struct, so we disallow them.)
   typeObj.set(&propVal.toObject());
   size_t size;
   if (!CType::GetSafeSize(typeObj, &size) || size == 0) {
     FieldDescriptorSizeError(cx, typeObj, nameid);
     return nullptr;
   }
 
-  return JSID_TO_FLAT_STRING(nameid);
+  return JSID_TO_LINEAR_STRING(nameid);
 }
 
 // For a struct field with 'name' and 'type', add an element of the form
 // { name : type }.
 static bool AddFieldToArray(JSContext* cx, MutableHandleValue element,
-                            JSFlatString* name_, JSObject* typeObj_) {
+                            JSLinearString* name_, JSObject* typeObj_) {
   RootedObject typeObj(cx, typeObj_);
-  Rooted<JSFlatString*> name(cx, name_);
+  Rooted<JSLinearString*> name(cx, name_);
   RootedObject fieldObj(cx, JS_NewPlainObject(cx));
   if (!fieldObj) {
     return false;
   }
 
   element.setObject(*fieldObj);
 
   AutoStableStringChars nameChars(cx);
@@ -5871,17 +5872,18 @@ bool StructType::DefineInternal(JSContex
 
     for (uint32_t i = 0; i < len; ++i) {
       RootedValue item(cx);
       if (!JS_GetElement(cx, fieldsObj, i, &item)) {
         return false;
       }
 
       RootedObject fieldType(cx, nullptr);
-      Rooted<JSFlatString*> name(cx, ExtractStructField(cx, item, &fieldType));
+      Rooted<JSLinearString*> name(cx,
+                                   ExtractStructField(cx, item, &fieldType));
       if (!name) {
         return false;
       }
 
       // Make sure each field name is unique
       FieldInfoHash::AddPtr entryPtr = fields.lookupForAdd(name);
       if (entryPtr) {
         return DuplicateFieldError(cx, name);
@@ -5895,27 +5897,27 @@ bool StructType::DefineInternal(JSContex
 
       RootedFunction getter(
           cx,
           NewFunctionWithReserved(cx, StructType::FieldGetter, 0, 0, nullptr));
       if (!getter) {
         return false;
       }
       SetFunctionNativeReserved(getter, StructType::SLOT_FIELDNAME,
-                                StringValue(JS_FORGET_STRING_FLATNESS(name)));
+                                StringValue(JS_FORGET_STRING_LINEARNESS(name)));
       RootedObject getterObj(cx, JS_GetFunctionObject(getter));
 
       RootedFunction setter(
           cx,
           NewFunctionWithReserved(cx, StructType::FieldSetter, 1, 0, nullptr));
       if (!setter) {
         return false;
       }
       SetFunctionNativeReserved(setter, StructType::SLOT_FIELDNAME,
-                                StringValue(JS_FORGET_STRING_FLATNESS(name)));
+                                StringValue(JS_FORGET_STRING_LINEARNESS(name)));
       RootedObject setterObj(cx, JS_GetFunctionObject(setter));
 
       if (!JS_DefineUCProperty(cx, prototype, nameChars.twoByteChars(),
                                name->length(), getterObj, setterObj,
                                JSPROP_ENUMERATE | JSPROP_PERMANENT |
                                    JSPROP_GETTER | JSPROP_SETTER)) {
         return false;
       }
@@ -6192,17 +6194,17 @@ const FieldInfoHash* StructType::GetFiel
 
   Value slot = JS_GetReservedSlot(obj, SLOT_FIELDINFO);
   MOZ_ASSERT(!slot.isUndefined() && slot.toPrivate());
 
   return static_cast<const FieldInfoHash*>(slot.toPrivate());
 }
 
 const FieldInfo* StructType::LookupField(JSContext* cx, JSObject* obj,
-                                         JSFlatString* name) {
+                                         JSLinearString* name) {
   MOZ_ASSERT(CType::IsCType(obj));
   MOZ_ASSERT(CType::GetTypeCode(obj) == TYPE_struct);
 
   FieldInfoHash::Ptr ptr = GetFieldInfo(obj)->lookup(name);
   if (ptr) {
     return &ptr->value();
   }
 
@@ -6296,17 +6298,18 @@ bool StructType::FieldGetter(JSContext* 
   JSObject* typeObj = CData::GetCType(obj);
   if (CType::GetTypeCode(typeObj) != TYPE_struct) {
     return IncompatibleThisType(cx, "StructType property getter",
                                 "non-StructType CData", args.thisv());
   }
 
   RootedValue nameVal(
       cx, GetFunctionNativeReserved(&args.callee(), SLOT_FIELDNAME));
-  Rooted<JSFlatString*> name(cx, JS_FlattenString(cx, nameVal.toString()));
+  Rooted<JSLinearString*> name(cx,
+                               JS_EnsureLinearString(cx, nameVal.toString()));
   if (!name) {
     return false;
   }
 
   const FieldInfo* field = LookupField(cx, typeObj, name);
   if (!field) {
     return false;
   }
@@ -6333,17 +6336,18 @@ bool StructType::FieldSetter(JSContext* 
   RootedObject typeObj(cx, CData::GetCType(obj));
   if (CType::GetTypeCode(typeObj) != TYPE_struct) {
     return IncompatibleThisType(cx, "StructType property setter",
                                 "non-StructType CData", args.thisv());
   }
 
   RootedValue nameVal(
       cx, GetFunctionNativeReserved(&args.callee(), SLOT_FIELDNAME));
-  Rooted<JSFlatString*> name(cx, JS_FlattenString(cx, nameVal.toString()));
+  Rooted<JSLinearString*> name(cx,
+                               JS_EnsureLinearString(cx, nameVal.toString()));
   if (!name) {
     return false;
   }
 
   const FieldInfo* field = LookupField(cx, typeObj, name);
   if (!field) {
     return false;
   }
@@ -6380,17 +6384,17 @@ bool StructType::AddressOfField(JSContex
                                "");
   }
 
   if (!args[0].isString()) {
     return ArgumentTypeMismatch(cx, "", "StructType.prototype.addressOfField",
                                 "a string");
   }
 
-  JSFlatString* str = JS_FlattenString(cx, args[0].toString());
+  JSLinearString* str = JS_EnsureLinearString(cx, args[0].toString());
   if (!str) {
     return false;
   }
 
   const FieldInfo* field = LookupField(cx, typeObj, str);
   if (!field) {
     return false;
   }
--- a/js/src/ctypes/CTypes.h
+++ b/js/src/ctypes/CTypes.h
@@ -320,18 +320,18 @@ struct UnbarrieredFieldInfo {
   size_t mIndex;    // index of the field in the struct (first is 0)
   size_t mOffset;   // offset of the field in the struct, in bytes
 };
 static_assert(sizeof(UnbarrieredFieldInfo) == sizeof(FieldInfo),
               "UnbarrieredFieldInfo should be the same as FieldInfo but with "
               "unbarriered mType");
 
 // Hash policy for FieldInfos.
-struct FieldHashPolicy : DefaultHasher<JSFlatString*> {
-  typedef JSFlatString* Key;
+struct FieldHashPolicy : DefaultHasher<JSLinearString*> {
+  typedef JSLinearString* Key;
   typedef Key Lookup;
 
   template <typename CharT>
   static uint32_t hash(const CharT* s, size_t n) {
     uint32_t hash = 0;
     for (; n > 0; s++, n--) {
       hash = hash * 33 + *s;
     }
@@ -352,17 +352,17 @@ struct FieldHashPolicy : DefaultHasher<J
     if (k->length() != l->length()) {
       return false;
     }
 
     return EqualChars(k, l);
   }
 };
 
-using FieldInfoHash = GCHashMap<js::HeapPtr<JSFlatString*>, FieldInfo,
+using FieldInfoHash = GCHashMap<js::HeapPtr<JSLinearString*>, FieldInfo,
                                 FieldHashPolicy, ZoneAllocPolicy>;
 
 // Descriptor of ABI, return type, argument types, and variadicity for a
 // FunctionType.
 struct FunctionInfo {
   explicit FunctionInfo(JS::Zone* zone) : mArgTypes(zone), mFFITypes(zone) {}
 
   // Initialized in NewFunctionInfo when !mIsVariadic, but only later, in
@@ -562,17 +562,18 @@ MOZ_MUST_USE bool GetSafeLength(JSObject
 UniquePtrFFIType BuildFFIType(JSContext* cx, JSObject* obj);
 }  // namespace ArrayType
 
 namespace StructType {
 MOZ_MUST_USE bool DefineInternal(JSContext* cx, JSObject* typeObj,
                                  JSObject* fieldsObj);
 
 const FieldInfoHash* GetFieldInfo(JSObject* obj);
-const FieldInfo* LookupField(JSContext* cx, JSObject* obj, JSFlatString* name);
+const FieldInfo* LookupField(JSContext* cx, JSObject* obj,
+                             JSLinearString* name);
 JSObject* BuildFieldsArray(JSContext* cx, JSObject* obj);
 UniquePtrFFIType BuildFFIType(JSContext* cx, JSObject* obj);
 }  // namespace StructType
 
 namespace FunctionType {
 JSObject* CreateInternal(JSContext* cx, HandleValue abi, HandleValue rtype,
                          const HandleValueArray& args);
 
--- a/js/src/ctypes/Library.cpp
+++ b/js/src/ctypes/Library.cpp
@@ -102,28 +102,28 @@ JSObject* Library::Create(JSContext* cx,
   }
 
   if (!path.isString()) {
     JS_ReportErrorASCII(cx, "open takes a string argument");
     return nullptr;
   }
 
   PRLibSpec libSpec;
-  RootedFlatString pathStr(cx, JS_FlattenString(cx, path.toString()));
+  RootedLinearString pathStr(cx, JS_EnsureLinearString(cx, path.toString()));
   if (!pathStr) {
     return nullptr;
   }
 #ifdef XP_WIN
   // On Windows, converting to native charset may corrupt path string.
   // So, we have to use Unicode path directly.
-  AutoStableStringChars pathStrChars(cx);
-  if (!pathStrChars.initTwoByte(cx, pathStr)) {
+  JS::UniqueTwoByteChars pathZeroTerminated(JS_CopyStringCharsZ(cx, pathStr));
+  if (!pathZeroTerminated) {
     return nullptr;
   }
-  char16ptr_t pathChars = pathStrChars.twoByteChars();
+  char16ptr_t pathChars = pathZeroTerminated.get();
   libSpec.value.pathname_u = pathChars;
   libSpec.type = PR_LibSpec_PathnameU;
 #else
   // Convert to platform native charset if the appropriate callback has been
   // provided.
   JS::UniqueChars pathBytes;
   if (callbacks && callbacks->unicodeToNative) {
     AutoStableStringChars pathStrChars(cx);
--- a/js/src/debugger/Object.cpp
+++ b/js/src/debugger/Object.cpp
@@ -1140,24 +1140,24 @@ bool DebuggerObject::CallData::executeIn
       cx, comp,
       DebuggerObject::executeInGlobal(cx, object, chars, bindings, options));
   return comp.get().buildCompletionValue(cx, object->owner(), args.rval());
 }
 
 // Copy a narrow or wide string to a vector, appending a null terminator.
 template <typename T>
 static bool CopyStringToVector(JSContext* cx, JSString* str, Vector<T>& chars) {
-  JSFlatString* flat = str->ensureFlat(cx);
-  if (!flat) {
+  JSLinearString* linear = str->ensureLinear(cx);
+  if (!linear) {
     return false;
   }
-  if (!chars.appendN(0, flat->length() + 1)) {
+  if (!chars.appendN(0, linear->length() + 1)) {
     return false;
   }
-  CopyChars(chars.begin(), *flat);
+  CopyChars(chars.begin(), *linear);
   return true;
 }
 
 bool DebuggerObject::CallData::createSource() {
   if (!args.requireAtLeast(cx, "Debugger.Object.prototype.createSource", 1)) {
     return false;
   }
 
--- a/js/src/gc/GC.h
+++ b/js/src/gc/GC.h
@@ -51,16 +51,19 @@ struct MapTypeToFinalizeKind {};
   };
 FOR_EACH_NONOBJECT_ALLOCKIND(EXPAND_MAPTYPETOFINALIZEKIND)
 #undef EXPAND_MAPTYPETOFINALIZEKIND
 
 } /* namespace gc */
 
 extern void TraceRuntime(JSTracer* trc);
 
+// Trace roots but don't evict the nursery first; used from DumpHeap.
+extern void TraceRuntimeWithoutEviction(JSTracer* trc);
+
 extern void ReleaseAllJITCode(JSFreeOp* op);
 
 extern void PrepareForDebugGC(JSRuntime* rt);
 
 /* Functions for managing cross compartment gray pointers. */
 
 extern void NotifyGCNukeWrapper(JSObject* o);
 
--- a/js/src/gc/GCInternals.h
+++ b/js/src/gc/GCInternals.h
@@ -245,28 +245,16 @@ struct MOZ_RAII AutoAssertNoNurseryAlloc
 #ifdef DEBUG
   AutoAssertNoNurseryAlloc();
   ~AutoAssertNoNurseryAlloc();
 #else
   AutoAssertNoNurseryAlloc() {}
 #endif
 };
 
-// Note that this class does not suppress buffer allocation/reallocation in the
-// nursery, only Cells themselves.
-class MOZ_RAII AutoSuppressNurseryCellAlloc {
-  JSContext* cx_;
-
- public:
-  explicit AutoSuppressNurseryCellAlloc(JSContext* cx) : cx_(cx) {
-    cx_->nurserySuppressions_++;
-  }
-  ~AutoSuppressNurseryCellAlloc() { cx_->nurserySuppressions_--; }
-};
-
 /*
  * There are a couple of classes here that serve mostly as "tokens" indicating
  * that a condition holds. Some functions force the caller to possess such a
  * token because they would misbehave if the condition were false, and it is
  * far more clear to make the condition visible at the point where it can be
  * affected rather than just crashing in an assertion down in the place where
  * it is relied upon.
  */
--- a/js/src/gc/RootMarking.cpp
+++ b/js/src/gc/RootMarking.cpp
@@ -311,16 +311,25 @@ void js::TraceRuntime(JSTracer* trc) {
 
   JSRuntime* rt = trc->runtime();
   rt->gc.evictNursery();
   AutoPrepareForTracing prep(rt->mainContextFromOwnThread());
   gcstats::AutoPhase ap(rt->gc.stats(), gcstats::PhaseKind::TRACE_HEAP);
   rt->gc.traceRuntime(trc, prep);
 }
 
+void js::TraceRuntimeWithoutEviction(JSTracer* trc) {
+  MOZ_ASSERT(!trc->isMarkingTracer());
+
+  JSRuntime* rt = trc->runtime();
+  AutoTraceSession session(rt);
+  gcstats::AutoPhase ap(rt->gc.stats(), gcstats::PhaseKind::TRACE_HEAP);
+  rt->gc.traceRuntime(trc, session);
+}
+
 void js::gc::GCRuntime::traceRuntime(JSTracer* trc, AutoTraceSession& session) {
   MOZ_ASSERT(!rt->isBeingDestroyed());
 
   gcstats::AutoPhase ap(stats(), gcstats::PhaseKind::MARK_ROOTS);
 
   traceRuntimeAtoms(trc, session);
   traceRuntimeCommon(trc, TraceRuntime);
 }
--- a/js/src/gdb/tests/test-JSString.cpp
+++ b/js/src/gdb/tests/test-JSString.cpp
@@ -1,13 +1,13 @@
 #include "gdb-tests.h"
 
 #include "vm/JSContext.h"
-// When JSGC_ANALYSIS is #defined, Rooted<JSFlatString*> needs the definition
-// of JSFlatString in order to figure out its ThingRootKind
+// When JSGC_ANALYSIS is #defined, Rooted<JSLinearString*> needs the definition
+// of JSLinearString in order to figure out its ThingRootKind
 #include "vm/StringType.h"
 
 FRAGMENT(JSString, simple) {
   AutoSuppressHazardsForTest noanalysis;
 
   JS::Rooted<JSString*> empty(cx, JS_NewStringCopyN(cx, nullptr, 0));
   JS::Rooted<JSString*> x(cx, JS_NewStringCopyN(cx, "x", 1));
   JS::Rooted<JSString*> z(cx, JS_NewStringCopyZ(cx, "z"));
@@ -48,22 +48,22 @@ FRAGMENT(JSString, null) {
 
   breakpoint();
 
   use(null);
   use(nullRaw);
 }
 
 FRAGMENT(JSString, subclasses) {
-  JS::Rooted<JSFlatString*> flat(
-      cx, JS_FlattenString(cx, JS_NewStringCopyZ(cx, "Hi!")));
+  JS::Rooted<JSLinearString*> linear(
+      cx, JS_EnsureLinearString(cx, JS_NewStringCopyZ(cx, "Hi!")));
 
   breakpoint();
 
-  use(flat);
+  use(linear);
 }
 
 FRAGMENT(JSString, atom) {
   JSAtom* molybdenum = js::Atomize(cx, "molybdenum", 10);
   breakpoint();
 
   use(molybdenum);
 }
--- a/js/src/jsapi-tests/testCompileNonSyntactic.cpp
+++ b/js/src/jsapi-tests/testCompileNonSyntactic.cpp
@@ -1,15 +1,14 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/Utf8.h"  // mozilla::Utf8Unit
 
-#include "gc/GCInternals.h"
 #include "js/CompilationAndEvaluation.h"  // JS::Compile{,ForNonSyntacticScope}{,DontInflate}
 #include "js/SourceText.h"                // JS::Source{Ownership,Text}
 #include "jsapi-tests/tests.h"
 #include "vm/HelperThreads.h"
 #include "vm/Monitor.h"
 #include "vm/MutexIDs.h"
 
 using namespace JS;
--- a/js/src/jsapi-tests/testDeflateStringToUTF8Buffer.cpp
+++ b/js/src/jsapi-tests/testDeflateStringToUTF8Buffer.cpp
@@ -3,160 +3,160 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "jsapi-tests/tests.h"
 
 using namespace JS;
 
 BEGIN_TEST(test_DeflateStringToUTF8Buffer) {
   JSString* str;
-  JSFlatString* flatStr;
+  JSLinearString* linearStr;
 
   // DeflateStringToUTF8Buffer does not write a null terminator, so the byte
   // following the last byte written to the |actual| buffer should retain
   // the value it held before the call to DeflateStringToUTF8Buffer, which is
   // initialized to 0x1.
 
   char actual[100];
   auto span = mozilla::MakeSpan(actual);
 
-  // Test with an ASCII string, which calls JSFlatString::latin1Chars
+  // Test with an ASCII string, which calls JSLinearString::latin1Chars
   // to retrieve the characters from the string and generates UTF-8 output
   // that is identical to the ASCII input.
 
   str = JS_NewStringCopyZ(cx, "Ohai");  // { 0x4F, 0x68, 0x61, 0x69 }
   MOZ_RELEASE_ASSERT(str);
-  flatStr = JS_FlattenString(cx, str);
+  linearStr = JS_EnsureLinearString(cx, str);
 
   {
     const char expected[] = {0x4F, 0x68, 0x61, 0x69, 0x1};
     memset(actual, 0x1, 100);
-    size_t dstlen = JS::DeflateStringToUTF8Buffer(flatStr, span);
+    size_t dstlen = JS::DeflateStringToUTF8Buffer(linearStr, span);
     CHECK_EQUAL(memcmp(actual, expected, sizeof(expected)), 0);
     CHECK_EQUAL(dstlen, 4u);
   }
 
   {
     const char expected[] = {0x4F, 0x68, 0x61, 0x1};
     memset(actual, 0x1, 100);
-    size_t dstlen = JS::DeflateStringToUTF8Buffer(flatStr, span.To(3));
+    size_t dstlen = JS::DeflateStringToUTF8Buffer(linearStr, span.To(3));
     CHECK_EQUAL(memcmp(actual, expected, sizeof(expected)), 0);
     CHECK_EQUAL(dstlen, 3u);
   }
 
   {
     const unsigned char expected[] = {0x1};
     memset(actual, 0x1, 100);
-    size_t dstlen = JS::DeflateStringToUTF8Buffer(flatStr, span.To(0));
+    size_t dstlen = JS::DeflateStringToUTF8Buffer(linearStr, span.To(0));
     CHECK_EQUAL(memcmp(actual, expected, sizeof(expected)), 0);
     CHECK_EQUAL(dstlen, 0u);
   }
 
-  // Test with a Latin-1 string, which calls JSFlatString::latin1Chars
+  // Test with a Latin-1 string, which calls JSLinearString::latin1Chars
   // like with the ASCII string but generates UTF-8 output that is different
   // from the ASCII input.
 
   str = JS_NewUCStringCopyZ(cx, u"\xD3\x68\xE3\xEF");  // u"Óhãï"
   MOZ_RELEASE_ASSERT(str);
-  flatStr = JS_FlattenString(cx, str);
+  linearStr = JS_EnsureLinearString(cx, str);
 
   {
     const unsigned char expected[] = {0xC3, 0x93, 0x68, 0xC3,
                                       0xA3, 0xC3, 0xAF, 0x1};
     memset(actual, 0x1, 100);
-    JS::DeflateStringToUTF8Buffer(flatStr, span);
+    JS::DeflateStringToUTF8Buffer(linearStr, span);
     CHECK_EQUAL(memcmp(actual, expected, sizeof(expected)), 0);
   }
 
   {
     const unsigned char expected[] = {0xC3, 0x93, 0x68, 0xC3,
                                       0xA3, 0xC3, 0xAF, 0x1};
     memset(actual, 0x1, 100);
-    size_t dstlen = JS::DeflateStringToUTF8Buffer(flatStr, span.To(7));
+    size_t dstlen = JS::DeflateStringToUTF8Buffer(linearStr, span.To(7));
     CHECK_EQUAL(memcmp(actual, expected, sizeof(expected)), 0);
     CHECK_EQUAL(dstlen, 7u);
   }
 
   {
     // Specify a destination buffer length of 3.  That's exactly enough
     // space to encode the first two characters, which takes three bytes.
     const unsigned char expected[] = {0xC3, 0x93, 0x68, 0x1};
     memset(actual, 0x1, 100);
-    size_t dstlen = JS::DeflateStringToUTF8Buffer(flatStr, span.To(3));
+    size_t dstlen = JS::DeflateStringToUTF8Buffer(linearStr, span.To(3));
     CHECK_EQUAL(memcmp(actual, expected, sizeof(expected)), 0);
     CHECK_EQUAL(dstlen, 3u);
   }
 
   {
     // Specify a destination buffer length of 4.  That's only enough space
     // to encode the first two characters, which takes three bytes, because
     // the third character would take another two bytes.
     const unsigned char expected[] = {0xC3, 0x93, 0x68, 0x1};
     memset(actual, 0x1, 100);
-    size_t dstlen = JS::DeflateStringToUTF8Buffer(flatStr, span.To(4));
+    size_t dstlen = JS::DeflateStringToUTF8Buffer(linearStr, span.To(4));
     CHECK_EQUAL(memcmp(actual, expected, sizeof(expected)), 0);
     CHECK_EQUAL(dstlen, 3u);
   }
 
   {
     const unsigned char expected[] = {0x1};
     memset(actual, 0x1, 100);
-    size_t dstlen = JS::DeflateStringToUTF8Buffer(flatStr, span.To(0));
+    size_t dstlen = JS::DeflateStringToUTF8Buffer(linearStr, span.To(0));
     CHECK_EQUAL(memcmp(actual, expected, sizeof(expected)), 0);
     CHECK_EQUAL(dstlen, 0u);
   }
 
-  // Test with a UTF-16 string, which calls JSFlatString::twoByteChars
+  // Test with a UTF-16 string, which calls JSLinearString::twoByteChars
   // to retrieve the characters from the string.
 
   str = JS_NewUCStringCopyZ(cx, u"\x038C\x0068\x0203\x0457");  // u"Όhȃї"
   MOZ_RELEASE_ASSERT(str);
-  flatStr = JS_FlattenString(cx, str);
+  linearStr = JS_EnsureLinearString(cx, str);
 
   {
     const unsigned char expected[] = {0xCE, 0x8C, 0x68, 0xC8,
                                       0x83, 0xD1, 0x97, 0x1};
     memset(actual, 0x1, 100);
-    JS::DeflateStringToUTF8Buffer(flatStr, span);
+    JS::DeflateStringToUTF8Buffer(linearStr, span);
     CHECK_EQUAL(memcmp(actual, expected, sizeof(expected)), 0);
   }
 
   {
     const unsigned char expected[] = {0xCE, 0x8C, 0x68, 0xC8,
                                       0x83, 0xD1, 0x97, 0x1};
     memset(actual, 0x1, 100);
-    size_t dstlen = JS::DeflateStringToUTF8Buffer(flatStr, span.To(7));
+    size_t dstlen = JS::DeflateStringToUTF8Buffer(linearStr, span.To(7));
     CHECK_EQUAL(memcmp(actual, expected, sizeof(expected)), 0);
     CHECK_EQUAL(dstlen, 7u);
   }
 
   {
     // Specify a destination buffer length of 3.  That's exactly enough
     // space to encode the first two characters, which takes three bytes.
     const unsigned char expected[] = {0xCE, 0x8C, 0x68, 0x1};
     memset(actual, 0x1, 100);
-    size_t dstlen = JS::DeflateStringToUTF8Buffer(flatStr, span.To(3));
+    size_t dstlen = JS::DeflateStringToUTF8Buffer(linearStr, span.To(3));
     CHECK_EQUAL(memcmp(actual, expected, sizeof(expected)), 0);
     CHECK_EQUAL(dstlen, 3u);
   }
 
   {
     // Specify a destination buffer length of 4.  That's only enough space
     // to encode the first two characters, which takes three bytes, because
     // the third character would take another two bytes.
     const unsigned char expected[] = {0xCE, 0x8C, 0x68, 0x1};
     memset(actual, 0x1, 100);
-    size_t dstlen = JS::DeflateStringToUTF8Buffer(flatStr, span.To(4));
+    size_t dstlen = JS::DeflateStringToUTF8Buffer(linearStr, span.To(4));
     CHECK_EQUAL(memcmp(actual, expected, sizeof(expected)), 0);
     CHECK_EQUAL(dstlen, 3u);
   }
 
   {
     const unsigned char expected[] = {0x1};
     memset(actual, 0x1, 100);
-    size_t dstlen = JS::DeflateStringToUTF8Buffer(flatStr, span.To(0));
+    size_t dstlen = JS::DeflateStringToUTF8Buffer(linearStr, span.To(0));
     CHECK_EQUAL(memcmp(actual, expected, sizeof(expected)), 0);
     CHECK_EQUAL(dstlen, 0u);
   }
 
   return true;
 }
 END_TEST(test_DeflateStringToUTF8Buffer)
--- a/js/src/jsapi-tests/testErrorInterceptor.cpp
+++ b/js/src/jsapi-tests/testErrorInterceptor.cpp
@@ -100,18 +100,18 @@ BEGIN_TEST(testErrorInterceptor) {
 
     // Check the final error.
     JS::RootedValue exn(cx);
     CHECK(JS_GetPendingException(cx, &exn));
     JS_ClearPendingException(cx);
 
     js::JSStringBuilder buffer(cx);
     CHECK(ValueToStringBuffer(cx, exn, buffer));
-    JS::Rooted<JSFlatString*> flat(cx, buffer.finishString());
-    CHECK(equalStrings(cx, flat, gLatestMessage));
+    JS::Rooted<JSLinearString*> linear(cx, buffer.finishString());
+    CHECK(equalStrings(cx, linear, gLatestMessage));
 
     // Cleanup.
     gLatestMessage = nullptr;
   }
 
   // Test again without callback.
   JS_SetErrorInterceptorCallback(cx->runtime(), nullptr);
   for (size_t i = 0; i < mozilla::ArrayLength(SAMPLES); ++i) {
@@ -125,18 +125,18 @@ BEGIN_TEST(testErrorInterceptor) {
 
     // Check the final error.
     JS::RootedValue exn(cx);
     CHECK(JS_GetPendingException(cx, &exn));
     JS_ClearPendingException(cx);
 
     js::JSStringBuilder buffer(cx);
     CHECK(ValueToStringBuffer(cx, exn, buffer));
-    JS::Rooted<JSFlatString*> flat(cx, buffer.finishString());
-    CHECK(js::StringEqualsAscii(flat, TO_STRING[i]));
+    JS::Rooted<JSLinearString*> linear(cx, buffer.finishString());
+    CHECK(js::StringEqualsAscii(linear, TO_STRING[i]));
 
     // Cleanup.
     gLatestMessage = nullptr;
   }
 
   // Cleanup
   JS_SetErrorInterceptorCallback(cx->runtime(), original);
   gLatestMessage = nullptr;
--- a/js/src/jsapi-tests/testGCAllocator.cpp
+++ b/js/src/jsapi-tests/testGCAllocator.cpp
@@ -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/. */
 
 #include <cstdlib>
 
-#include "gc/GCInternals.h"
 #include "gc/Memory.h"
 #include "jsapi-tests/tests.h"
 
 #if defined(XP_WIN)
 #  include "util/Windows.h"
 #  include <psapi.h>
 #else
 #  include <algorithm>
--- a/js/src/jsapi-tests/testGCUniqueId.cpp
+++ b/js/src/jsapi-tests/testGCUniqueId.cpp
@@ -1,16 +1,15 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  * vim: set ts=8 sts=2 et sw=2 tw=80:
  */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#include "gc/GCInternals.h"
 #include "js/GCVector.h"
 
 #include "jsapi-tests/tests.h"
 
 #include "gc/Zone-inl.h"
 
 static void MinimizeHeap(JSContext* cx) {
   // The second collection is to force us to wait for the background
--- a/js/src/jsapi-tests/testIntString.cpp
+++ b/js/src/jsapi-tests/testIntString.cpp
@@ -8,34 +8,34 @@
 #include "jsapi-tests/tests.h"
 
 BEGIN_TEST(testIntString_bug515273) {
   JS::RootedValue v(cx);
 
   EVAL("'1';", &v);
   JSString* str = v.toString();
   CHECK(JS_StringHasBeenPinned(cx, str));
-  CHECK(JS_FlatStringEqualsLiteral(JS_ASSERT_STRING_IS_FLAT(str), "1"));
+  CHECK(JS_LinearStringEqualsLiteral(JS_ASSERT_STRING_IS_LINEAR(str), "1"));
 
   EVAL("'42';", &v);
   str = v.toString();
   CHECK(JS_StringHasBeenPinned(cx, str));
-  CHECK(JS_FlatStringEqualsLiteral(JS_ASSERT_STRING_IS_FLAT(str), "42"));
+  CHECK(JS_LinearStringEqualsLiteral(JS_ASSERT_STRING_IS_LINEAR(str), "42"));
 
   EVAL("'111';", &v);
   str = v.toString();
   CHECK(JS_StringHasBeenPinned(cx, str));
-  CHECK(JS_FlatStringEqualsLiteral(JS_ASSERT_STRING_IS_FLAT(str), "111"));
+  CHECK(JS_LinearStringEqualsLiteral(JS_ASSERT_STRING_IS_LINEAR(str), "111"));
 
   /* Test other types of static strings. */
   EVAL("'a';", &v);
   str = v.toString();
   CHECK(JS_StringHasBeenPinned(cx, str));
-  CHECK(JS_FlatStringEqualsLiteral(JS_ASSERT_STRING_IS_FLAT(str), "a"));
+  CHECK(JS_LinearStringEqualsLiteral(JS_ASSERT_STRING_IS_LINEAR(str), "a"));
 
   EVAL("'bc';", &v);
   str = v.toString();
   CHECK(JS_StringHasBeenPinned(cx, str));
-  CHECK(JS_FlatStringEqualsLiteral(JS_ASSERT_STRING_IS_FLAT(str), "bc"));
+  CHECK(JS_LinearStringEqualsLiteral(JS_ASSERT_STRING_IS_LINEAR(str), "bc"));
 
   return true;
 }
 END_TEST(testIntString_bug515273)
--- a/js/src/jsapi-tests/testLookup.cpp
+++ b/js/src/jsapi-tests/testLookup.cpp
@@ -44,21 +44,21 @@ bool document_resolve(JSContext* cx, JS:
   // If id is "all", resolve document.all=true.
   JS::RootedValue v(cx);
   if (!JS_IdToValue(cx, id, &v)) {
     return false;
   }
 
   if (v.isString()) {
     JSString* str = v.toString();
-    JSFlatString* flatStr = JS_FlattenString(cx, str);
-    if (!flatStr) {
+    JSLinearString* linearStr = JS_EnsureLinearString(cx, str);
+    if (!linearStr) {
       return false;
     }
-    if (JS_FlatStringEqualsLiteral(flatStr, "all")) {
+    if (JS_LinearStringEqualsLiteral(linearStr, "all")) {
       JS::Rooted<JSObject*> docAll(cx, JS_NewObject(cx, &DocumentAllClass));
       if (!docAll) {
         return false;
       }
 
       JS::Rooted<JS::Value> allValue(cx, JS::ObjectValue(*docAll));
       if (!JS_DefinePropertyById(cx, obj, id, allValue, JSPROP_RESOLVING)) {
         return false;
--- a/js/src/jsapi-tests/testParseJSON.cpp
+++ b/js/src/jsapi-tests/testParseJSON.cpp
@@ -74,17 +74,17 @@ BEGIN_TEST(testParseJSON_success) {
   CHECK(TryParse(cx, "1.75", expected));
 
   expected.setDouble(9e9);
   CHECK(TryParse(cx, "9e9", expected));
 
   expected.setDouble(std::numeric_limits<double>::infinity());
   CHECK(TryParse(cx, "9e99999", expected));
 
-  JS::Rooted<JSFlatString*> str(cx);
+  JS::Rooted<JSLinearString*> str(cx);
 
   const char16_t emptystr[] = {'\0'};
   str = js::NewStringCopyN<CanGC>(cx, emptystr, 0);
   CHECK(str);
   expected = JS::StringValue(str);
   CHECK(TryParse(cx, "\"\"", expected));
 
   const char16_t nullstr[] = {'\0'};
@@ -147,17 +147,17 @@ BEGIN_TEST(testParseJSON_success) {
   CHECK(!isArray);
   CHECK(JS_GetProperty(cx, obj, "f", &v2));
   CHECK(v2.isInt32(17));
 
   return true;
 }
 
 template <size_t N>
-static JSFlatString* NewString(JSContext* cx, const char16_t (&chars)[N]) {
+static JSLinearString* NewString(JSContext* cx, const char16_t (&chars)[N]) {
   return js::NewStringCopyN<CanGC>(cx, chars, N);
 }
 
 template <size_t N>
 inline bool Parse(JSContext* cx, const char (&input)[N],
                   JS::MutableHandleValue vp) {
   AutoInflatedString str(cx);
   str = input;
--- a/js/src/jsapi-tests/testRegExp.cpp
+++ b/js/src/jsapi-tests/testRegExp.cpp
@@ -52,13 +52,14 @@ END_TEST(testGetRegExpFlags)
 BEGIN_TEST(testGetRegExpSource) {
   JS::RootedValue val(cx);
   JS::RootedObject obj(cx);
 
   EVAL("/foopy/", &val);
   obj = val.toObjectOrNull();
   JSString* source = JS::GetRegExpSource(cx, obj);
   CHECK(source);
-  CHECK(JS_FlatStringEqualsLiteral(JS_ASSERT_STRING_IS_FLAT(source), "foopy"));
+  CHECK(JS_LinearStringEqualsLiteral(JS_ASSERT_STRING_IS_LINEAR(source),
+                                     "foopy"));
 
   return true;
 }
 END_TEST(testGetRegExpSource)
--- a/js/src/jsapi-tests/testResolveRecursion.cpp
+++ b/js/src/jsapi-tests/testResolveRecursion.cpp
@@ -65,36 +65,36 @@ struct AutoIncrCounters {
 
 bool doResolve(JS::HandleObject obj, JS::HandleId id, bool* resolvedp) {
   CHECK_EQUAL(resolveExitCount, 0);
   AutoIncrCounters incr(this);
   CHECK(obj == obj1 || obj == obj2);
 
   CHECK(JSID_IS_STRING(id));
 
-  JSFlatString* str = JS_FlattenString(cx, JSID_TO_STRING(id));
+  JSLinearString* str = JS_EnsureLinearString(cx, JSID_TO_STRING(id));
   CHECK(str);
   JS::RootedValue v(cx);
-  if (JS_FlatStringEqualsLiteral(str, "x")) {
+  if (JS_LinearStringEqualsLiteral(str, "x")) {
     if (obj == obj1) {
       /* First resolve hook invocation. */
       CHECK_EQUAL(resolveEntryCount, 1);
       EVAL("obj2.y = true", &v);
       CHECK(v.isTrue());
       CHECK(JS_DefinePropertyById(cx, obj, id, JS::FalseHandleValue,
                                   JSPROP_RESOLVING));
       *resolvedp = true;
       return true;
     }
     if (obj == obj2) {
       CHECK_EQUAL(resolveEntryCount, 4);
       *resolvedp = false;
       return true;
     }
-  } else if (JS_FlatStringEqualsLiteral(str, "y")) {
+  } else if (JS_LinearStringEqualsLiteral(str, "y")) {
     if (obj == obj2) {
       CHECK_EQUAL(resolveEntryCount, 2);
       CHECK(JS_DefinePropertyById(cx, obj, id, JS::NullHandleValue,
                                   JSPROP_RESOLVING));
       EVAL("obj1.x", &v);
       CHECK(v.isUndefined());
       EVAL("obj1.y", &v);
       CHECK(v.isInt32(0));
--- a/js/src/jsapi-tests/testScriptSourceCompression.cpp
+++ b/js/src/jsapi-tests/testScriptSourceCompression.cpp
@@ -9,17 +9,17 @@
 #include "mozilla/Assertions.h"  // MOZ_RELEASE_ASSERT
 #include "mozilla/Utf8.h"        // mozilla::Utf8Unit
 
 #include <algorithm>  // std::all_of, std::equal, std::move, std::transform
 #include <memory>     // std::uninitialized_fill_n
 #include <stddef.h>   // size_t
 #include <stdint.h>   // uint32_t
 
-#include "jsapi.h"  // JS_FlattenString, JS_GC, JS_Get{Latin1,TwoByte}FlatStringChars, JS_GetStringLength, JS_ValueToFunction
+#include "jsapi.h"  // JS_EnsureLinearString, JS_GC, JS_Get{Latin1,TwoByte}LinearStringChars, JS_GetStringLength, JS_ValueToFunction
 
 #include "js/CompilationAndEvaluation.h"  // JS::Evaluate{,DontInflate}
 #include "js/CompileOptions.h"            // JS::CompileOptions
 #include "js/Conversions.h"               // JS::ToString
 #include "js/MemoryFunctions.h"           // JS_malloc
 #include "js/RootingAPI.h"                // JS::MutableHandle, JS::Rooted
 #include "js/SourceText.h"                // JS::SourceOwnership, JS::SourceText
 #include "js/UniquePtr.h"                 // js::UniquePtr
@@ -157,18 +157,18 @@ static void WriteFunctionOfSizeAtOffset(
 
 static JSString* DecompressSource(JSContext* cx, JS::Handle<JSFunction*> fun) {
   JS::Rooted<JS::Value> fval(cx, JS::ObjectValue(*JS_GetFunctionObject(fun)));
   return JS::ToString(cx, fval);
 }
 
 static bool IsExpectedFunctionString(JS::Handle<JSString*> str,
                                      char functionName, JSContext* cx) {
-  JSFlatString* fstr = JS_FlattenString(cx, str);
-  MOZ_RELEASE_ASSERT(fstr);
+  JSLinearString* lstr = JS_EnsureLinearString(cx, str);
+  MOZ_RELEASE_ASSERT(lstr);
 
   size_t len = JS_GetStringLength(str);
   if (len < FunctionStartLength || len < FunctionEndLength) {
     return false;
   }
 
   JS::AutoAssertNoGC nogc(cx);
 
@@ -189,20 +189,20 @@ static bool IsExpectedFunctionString(JS:
                        chars + len - FunctionEndLength,
                        [](auto c) { return c == FillerWhitespace; }) &&
            std::equal(chars + len - FunctionEndLength, chars + len,
                       FunctionEnd);
   };
 
   bool hasExpectedContents;
   if (JS_StringHasLatin1Chars(str)) {
-    const JS::Latin1Char* chars = JS_GetLatin1FlatStringChars(nogc, fstr);
+    const JS::Latin1Char* chars = JS_GetLatin1LinearStringChars(nogc, lstr);
     hasExpectedContents = CheckContents(chars);
   } else {
-    const char16_t* chars = JS_GetTwoByteFlatStringChars(nogc, fstr);
+    const char16_t* chars = JS_GetTwoByteLinearStringChars(nogc, lstr);
     hasExpectedContents = CheckContents(chars);
   }
 
   return hasExpectedContents;
 }
 
 BEGIN_TEST(testScriptSourceCompression_inOneChunk) {
   CHECK(run<char16_t>());
--- a/js/src/jsapi-tests/testSetPropertyIgnoringNamedGetter.cpp
+++ b/js/src/jsapi-tests/testSetPropertyIgnoringNamedGetter.cpp
@@ -14,17 +14,17 @@ using namespace JS;
 class CustomProxyHandler : public Wrapper {
  public:
   CustomProxyHandler() : Wrapper(0) {}
 
   bool getOwnPropertyDescriptor(
       JSContext* cx, HandleObject proxy, HandleId id,
       MutableHandle<PropertyDescriptor> desc) const override {
     if (JSID_IS_STRING(id) &&
-        JS_FlatStringEqualsLiteral(JSID_TO_FLAT_STRING(id), "phantom")) {
+        JS_LinearStringEqualsLiteral(JSID_TO_LINEAR_STRING(id), "phantom")) {
       desc.object().set(proxy);
       desc.attributesRef() = JSPROP_ENUMERATE;
       desc.value().setInt32(42);
       return true;
     }
 
     return Wrapper::getOwnPropertyDescriptor(cx, proxy, id, desc);
   }
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -3268,17 +3268,17 @@ JS_PUBLIC_API JSFunction* JS::NewFunctio
   cx->check(id);
 
 #ifdef DEBUG
   if (fs->name.isSymbol()) {
     MOZ_ASSERT(SYMBOL_TO_JSID(cx->wellKnownSymbols().get(fs->name.symbol())) ==
                id);
   } else {
     MOZ_ASSERT(JSID_IS_STRING(id) &&
-               StringEqualsAscii(JSID_TO_FLAT_STRING(id), fs->name.string()));
+               StringEqualsAscii(JSID_TO_LINEAR_STRING(id), fs->name.string()));
   }
 #endif
 
   // Delay cloning self-hosted functions until they are called. This is
   // achieved by passing DefineFunction a nullptr JSNative which produces an
   // interpreted JSFunction where !hasScript. Interpreted call paths then
   // call InitializeLazyFunctionScript if !hasScript.
   if (fs->selfHostedName) {
@@ -4305,17 +4305,17 @@ JS_PUBLIC_API JSString* JS_AtomizeAndPin
 
 JS_PUBLIC_API JSString* JS_AtomizeAndPinUCString(JSContext* cx,
                                                  const char16_t* s) {
   return JS_AtomizeAndPinUCStringN(cx, s, js_strlen(s));
 }
 
 JS_PUBLIC_API size_t JS_GetStringLength(JSString* str) { return str->length(); }
 
-JS_PUBLIC_API bool JS_StringIsFlat(JSString* str) { return str->isFlat(); }
+JS_PUBLIC_API bool JS_StringIsLinear(JSString* str) { return str->isLinear(); }
 
 JS_PUBLIC_API bool JS_StringHasLatin1Chars(JSString* str) {
   return str->hasLatin1Chars();
 }
 
 JS_PUBLIC_API const JS::Latin1Char* JS_GetLatin1StringCharsAndLength(
     JSContext* cx, const JS::AutoRequireNoGC& nogc, JSString* str,
     size_t* plength) {
@@ -4360,17 +4360,18 @@ JS_PUBLIC_API bool JS_GetStringCharAt(JS
   if (!linear) {
     return false;
   }
 
   *res = linear->latin1OrTwoByteChar(index);
   return true;
 }
 
-JS_PUBLIC_API char16_t JS_GetFlatStringCharAt(JSFlatString* str, size_t index) {
+JS_PUBLIC_API char16_t JS_GetLinearStringCharAt(JSLinearString* str,
+                                                size_t index) {
   return str->latin1OrTwoByteChar(index);
 }
 
 JS_PUBLIC_API bool JS_CopyStringChars(JSContext* cx,
                                       mozilla::Range<char16_t> dest,
                                       JSString* str) {
   AssertHeapIsIdleOrStringIsFlat(str);
   CHECK_THREAD(cx);
@@ -4381,55 +4382,58 @@ JS_PUBLIC_API bool JS_CopyStringChars(JS
     return false;
   }
 
   MOZ_ASSERT(linear->length() <= dest.length());
   CopyChars(dest.begin().get(), *linear);
   return true;
 }
 
-JS_PUBLIC_API const Latin1Char* JS_GetLatin1InternedStringChars(
-    const JS::AutoRequireNoGC& nogc, JSString* str) {
-  MOZ_ASSERT(str->isAtom());
-  JSFlatString* flat = str->ensureFlat(nullptr);
-  if (!flat) {
-    return nullptr;
-  }
-  return flat->latin1Chars(nogc);
-}
-
-JS_PUBLIC_API const char16_t* JS_GetTwoByteInternedStringChars(
-    const JS::AutoRequireNoGC& nogc, JSString* str) {
-  MOZ_ASSERT(str->isAtom());
-  JSFlatString* flat = str->ensureFlat(nullptr);
-  if (!flat) {
-    return nullptr;
-  }
-  return flat->twoByteChars(nogc);
-}
-
-extern JS_PUBLIC_API JSFlatString* JS_FlattenString(JSContext* cx,
-                                                    JSString* str) {
+extern JS_PUBLIC_API JS::UniqueTwoByteChars JS_CopyStringCharsZ(JSContext* cx,
+                                                                JSString* str) {
   AssertHeapIsIdle();
   CHECK_THREAD(cx);
   cx->check(str);
-  JSFlatString* flat = str->ensureFlat(cx);
-  if (!flat) {
+
+  JSLinearString* linear = str->ensureLinear(cx);
+  if (!linear) {
+    return nullptr;
+  }
+
+  size_t len = linear->length();
+
+  static_assert(js::MaxStringLength < UINT32_MAX,
+                "len + 1 must not overflow on 32-bit platforms");
+
+  UniqueTwoByteChars chars(cx->pod_malloc<char16_t>(len + 1));
+  if (!chars) {
     return nullptr;
   }
-  return flat;
-}
-
-extern JS_PUBLIC_API const Latin1Char* JS_GetLatin1FlatStringChars(
-    const JS::AutoRequireNoGC& nogc, JSFlatString* str) {
+
+  CopyChars(chars.get(), *linear);
+  chars[len] = '\0';
+
+  return chars;
+}
+
+extern JS_PUBLIC_API JSLinearString* JS_EnsureLinearString(JSContext* cx,
+                                                           JSString* str) {
+  AssertHeapIsIdle();
+  CHECK_THREAD(cx);
+  cx->check(str);
+  return str->ensureLinear(cx);
+}
+
+extern JS_PUBLIC_API const Latin1Char* JS_GetLatin1LinearStringChars(
+    const JS::AutoRequireNoGC& nogc, JSLinearString* str) {
   return str->latin1Chars(nogc);
 }
 
-extern JS_PUBLIC_API const char16_t* JS_GetTwoByteFlatStringChars(
-    const JS::AutoRequireNoGC& nogc, JSFlatString* str) {
+extern JS_PUBLIC_API const char16_t* JS_GetTwoByteLinearStringChars(
+    const JS::AutoRequireNoGC& nogc, JSLinearString* str) {
   return str->twoByteChars(nogc);
 }
 
 JS_PUBLIC_API bool JS_CompareStrings(JSContext* cx, JSString* str1,
                                      JSString* str2, int32_t* result) {
   AssertHeapIsIdle();
   CHECK_THREAD(cx);
 
@@ -4458,29 +4462,30 @@ JS_PUBLIC_API bool JS_StringEqualsAscii(
   JSLinearString* linearStr = str->ensureLinear(cx);
   if (!linearStr) {
     return false;
   }
   *match = StringEqualsAscii(linearStr, asciiBytes, length);
   return true;
 }
 
-JS_PUBLIC_API bool JS_FlatStringEqualsAscii(JSFlatString* str,
-                                            const char* asciiBytes) {
+JS_PUBLIC_API bool JS_LinearStringEqualsAscii(JSLinearString* str,
+                                              const char* asciiBytes) {
   return StringEqualsAscii(str, asciiBytes);
 }
 
-JS_PUBLIC_API bool JS_FlatStringEqualsAscii(JSFlatString* str,
-                                            const char* asciiBytes,
-                                            size_t length) {
+JS_PUBLIC_API bool JS_LinearStringEqualsAscii(JSLinearString* str,
+                                              const char* asciiBytes,
+                                              size_t length) {
   return StringEqualsAscii(str, asciiBytes, length);
 }
 
-JS_PUBLIC_API size_t JS_PutEscapedFlatString(char* buffer, size_t size,
-                                             JSFlatString* str, char quote) {
+JS_PUBLIC_API size_t JS_PutEscapedLinearString(char* buffer, size_t size,
+                                               JSLinearString* str,
+                                               char quote) {
   return PutEscapedString(buffer, size, str, quote);
 }
 
 JS_PUBLIC_API size_t JS_PutEscapedString(JSContext* cx, char* buffer,
                                          size_t size, JSString* str,
                                          char quote) {
   AssertHeapIsIdle();
   JSLinearString* linearStr = str->ensureLinear(cx);
@@ -4654,17 +4659,17 @@ JS_PUBLIC_API bool JS::PropertySpecNameE
       return false;
     }
     Symbol* sym = JSID_TO_SYMBOL(id);
     return sym->isWellKnownSymbol() && sym->code() == name.symbol();
   }
 
   MOZ_ASSERT(!PropertySpecNameIsDigits(name));
   return JSID_IS_ATOM(id) &&
-         JS_FlatStringEqualsAscii(JSID_TO_ATOM(id), name.string());
+         JS_LinearStringEqualsAscii(JSID_TO_ATOM(id), name.string());
 }
 
 JS_PUBLIC_API bool JS_Stringify(JSContext* cx, MutableHandleValue vp,
                                 HandleObject replacer, HandleValue space,
                                 JSONWriteCallback callback, void* data) {
   AssertHeapIsIdle();
   CHECK_THREAD(cx);
   cx->check(replacer, space);
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -2310,118 +2310,127 @@ extern JS_PUBLIC_API size_t JS_PutEscape
  *
  * While getting the length of a string is infallible, getting the chars can
  * fail. As indicated by the lack of a JSContext parameter, there are two
  * special cases where getting the chars is infallible:
  *
  * The first case is for strings that have been atomized, e.g. directly by
  * JS_AtomizeAndPinString or implicitly because it is stored in a jsid.
  *
- * The second case is "flat" strings that have been explicitly prepared in a
- * fallible context by JS_FlattenString. To catch errors, a separate opaque
- * JSFlatString type is returned by JS_FlattenString and expected by
- * JS_GetFlatStringChars. Note, though, that this is purely a syntactic
- * distinction: the input and output of JS_FlattenString are the same actual
- * GC-thing. If a JSString is known to be flat, JS_ASSERT_STRING_IS_FLAT can be
- * used to make a debug-checked cast. Example:
+ * The second case is "linear" strings that have been explicitly prepared in a
+ * fallible context by JS_EnsureLinearString. To catch errors, a separate opaque
+ * JSLinearString type is returned by JS_EnsureLinearString and expected by
+ * JS_Get{Latin1,TwoByte}StringCharsAndLength. Note, though, that this is purely
+ * a syntactic distinction: the input and output of JS_EnsureLinearString are
+ * the same actual GC-thing. If a JSString is known to be linear,
+ * JS_ASSERT_STRING_IS_LINEAR can be used to make a debug-checked cast. Example:
  *
- *   // in a fallible context
- *   JSFlatString* fstr = JS_FlattenString(cx, str);
- *   if (!fstr) {
+ *   // In a fallible context.
+ *   JSLinearString* lstr = JS_EnsureLinearString(cx, str);
+ *   if (!lstr) {
  *     return false;
  *   }
- *   MOZ_ASSERT(fstr == JS_ASSERT_STRING_IS_FLAT(str));
+ *   MOZ_ASSERT(lstr == JS_ASSERT_STRING_IS_LINEAR(str));
  *
- *   // in an infallible context, for the same 'str'
+ *   // In an infallible context, for the same 'str'.
  *   AutoCheckCannotGC nogc;
- *   const char16_t* chars = JS_GetTwoByteFlatStringChars(nogc, fstr)
+ *   const char16_t* chars = JS_GetTwoByteLinearStringChars(nogc, lstr)
  *   MOZ_ASSERT(chars);
  *
- * Flat strings and interned strings are always null-terminated, so
- * JS_FlattenString can be used to get a null-terminated string.
+ * Note: JS strings (including linear strings and atoms) are not
+ * null-terminated!
  *
  * Additionally, string characters are stored as either Latin1Char (8-bit)
  * or char16_t (16-bit). Clients can use JS_StringHasLatin1Chars and can then
  * call either the Latin1* or TwoByte* functions. Some functions like
  * JS_CopyStringChars and JS_GetStringCharAt accept both Latin1 and TwoByte
  * strings.
  */
 
 extern JS_PUBLIC_API size_t JS_GetStringLength(JSString* str);
 
-extern JS_PUBLIC_API bool JS_StringIsFlat(JSString* str);
+extern JS_PUBLIC_API bool JS_StringIsLinear(JSString* str);
 
 /** Returns true iff the string's characters are stored as Latin1. */
 extern JS_PUBLIC_API bool JS_StringHasLatin1Chars(JSString* str);
 
 extern JS_PUBLIC_API const JS::Latin1Char* JS_GetLatin1StringCharsAndLength(
     JSContext* cx, const JS::AutoRequireNoGC& nogc, JSString* str,
     size_t* length);
 
 extern JS_PUBLIC_API const char16_t* JS_GetTwoByteStringCharsAndLength(
     JSContext* cx, const JS::AutoRequireNoGC& nogc, JSString* str,
     size_t* length);
 
 extern JS_PUBLIC_API bool JS_GetStringCharAt(JSContext* cx, JSString* str,
                                              size_t index, char16_t* res);
 
-extern JS_PUBLIC_API char16_t JS_GetFlatStringCharAt(JSFlatString* str,
-                                                     size_t index);
+extern JS_PUBLIC_API char16_t JS_GetLinearStringCharAt(JSLinearString* str,
+                                                       size_t index);
 
 extern JS_PUBLIC_API const char16_t* JS_GetTwoByteExternalStringChars(
     JSString* str);
 
 extern JS_PUBLIC_API bool JS_CopyStringChars(JSContext* cx,
                                              mozilla::Range<char16_t> dest,
                                              JSString* str);
 
-extern JS_PUBLIC_API JSFlatString* JS_FlattenString(JSContext* cx,
-                                                    JSString* str);
-
-extern JS_PUBLIC_API const JS::Latin1Char* JS_GetLatin1FlatStringChars(
-    const JS::AutoRequireNoGC& nogc, JSFlatString* str);
-
-extern JS_PUBLIC_API const char16_t* JS_GetTwoByteFlatStringChars(
-    const JS::AutoRequireNoGC& nogc, JSFlatString* str);
-
-static MOZ_ALWAYS_INLINE JSFlatString* JSID_TO_FLAT_STRING(jsid id) {
+/**
+ * Copies the string's characters to a null-terminated char16_t buffer.
+ *
+ * Returns nullptr on OOM.
+ */
+extern JS_PUBLIC_API JS::UniqueTwoByteChars JS_CopyStringCharsZ(JSContext* cx,
+                                                                JSString* str);
+
+extern JS_PUBLIC_API JSLinearString* JS_EnsureLinearString(JSContext* cx,
+                                                           JSString* str);
+
+extern JS_PUBLIC_API const JS::Latin1Char* JS_GetLatin1LinearStringChars(
+    const JS::AutoRequireNoGC& nogc, JSLinearString* str);
+
+extern JS_PUBLIC_API const char16_t* JS_GetTwoByteLinearStringChars(
+    const JS::AutoRequireNoGC& nogc, JSLinearString* str);
+
+static MOZ_ALWAYS_INLINE JSLinearString* JSID_TO_LINEAR_STRING(jsid id) {
   MOZ_ASSERT(JSID_IS_STRING(id));
-  return (JSFlatString*)JSID_TO_STRING(id);
+  return reinterpret_cast<JSLinearString*>(JSID_TO_STRING(id));
 }
 
-static MOZ_ALWAYS_INLINE JSFlatString* JS_ASSERT_STRING_IS_FLAT(JSString* str) {
-  MOZ_ASSERT(JS_StringIsFlat(str));
-  return (JSFlatString*)str;
+static MOZ_ALWAYS_INLINE JSLinearString* JS_ASSERT_STRING_IS_LINEAR(
+    JSString* str) {
+  MOZ_ASSERT(JS_StringIsLinear(str));
+  return reinterpret_cast<JSLinearString*>(str);
 }
 
-static MOZ_ALWAYS_INLINE JSString* JS_FORGET_STRING_FLATNESS(
-    JSFlatString* fstr) {
-  return (JSString*)fstr;
+static MOZ_ALWAYS_INLINE JSString* JS_FORGET_STRING_LINEARNESS(
+    JSLinearString* str) {
+  return reinterpret_cast<JSString*>(str);
 }
 
 /*
- * Additional APIs that avoid fallibility when given a flat string.
+ * Additional APIs that avoid fallibility when given a linear string.
  */
 
-extern JS_PUBLIC_API bool JS_FlatStringEqualsAscii(JSFlatString* str,
-                                                   const char* asciiBytes);
-extern JS_PUBLIC_API bool JS_FlatStringEqualsAscii(JSFlatString* str,
-                                                   const char* asciiBytes,
-                                                   size_t length);
+extern JS_PUBLIC_API bool JS_LinearStringEqualsAscii(JSLinearString* str,
+                                                     const char* asciiBytes);
+extern JS_PUBLIC_API bool JS_LinearStringEqualsAscii(JSLinearString* str,
+                                                     const char* asciiBytes,
+                                                     size_t length);
 
 template <size_t N>
-bool JS_FlatStringEqualsLiteral(JSFlatString* str,
-                                const char (&asciiBytes)[N]) {
+bool JS_LinearStringEqualsLiteral(JSLinearString* str,
+                                  const char (&asciiBytes)[N]) {
   MOZ_ASSERT(asciiBytes[N - 1] == '\0');
-  return JS_FlatStringEqualsAscii(str, asciiBytes, N - 1);
+  return JS_LinearStringEqualsAscii(str, asciiBytes, N - 1);
 }
 
-extern JS_PUBLIC_API size_t JS_PutEscapedFlatString(char* buffer, size_t size,
-                                                    JSFlatString* str,
-                                                    char quote);
+extern JS_PUBLIC_API size_t JS_PutEscapedLinearString(char* buffer, size_t size,
+                                                      JSLinearString* str,
+                                                      char quote);
 
 /**
  * Create a dependent string, i.e., a string that owns no character storage,
  * but that refers to a slice of another string's chars.  Dependent strings
  * are mutable by definition, so the thread safety comments above apply.
  */
 extern JS_PUBLIC_API JSString* JS_NewDependentString(JSContext* cx,
                                                      JS::HandleString str,
--- a/js/src/jsexn.cpp
+++ b/js/src/jsexn.cpp
@@ -589,18 +589,18 @@ JSObject* ErrorObject::createConstructor
   if (!ctor) {
     return nullptr;
   }
 
   ctor->as<JSFunction>().setExtendedSlot(0, Int32Value(type));
   return ctor;
 }
 
-JS_FRIEND_API JSFlatString* js::GetErrorTypeName(JSContext* cx,
-                                                 int16_t exnType) {
+JS_FRIEND_API JSLinearString* js::GetErrorTypeName(JSContext* cx,
+                                                   int16_t exnType) {
   /*
    * JSEXN_INTERNALERR returns null to prevent that "InternalError: "
    * is prepended before "uncaught exception: "
    */
   if (exnType < 0 || exnType >= JSEXN_LIMIT || exnType == JSEXN_INTERNALERR ||
       exnType == JSEXN_WARN || exnType == JSEXN_NOTE) {
     return nullptr;
   }
--- a/js/src/jsfriendapi.cpp
+++ b/js/src/jsfriendapi.cpp
@@ -12,17 +12,17 @@
 #include "mozilla/TimeStamp.h"
 
 #include <stdint.h>
 
 #include "builtin/BigInt.h"
 #include "builtin/MapObject.h"
 #include "builtin/Promise.h"
 #include "builtin/TestingFunctions.h"
-#include "gc/GCInternals.h"
+#include "gc/GC.h"
 #include "gc/PublicIterators.h"
 #include "gc/WeakMap.h"
 #include "js/CharacterEncoding.h"
 #include "js/Printf.h"
 #include "js/Proxy.h"
 #include "js/Wrapper.h"
 #include "proxy/DeadObjectProxy.h"
 #include "vm/ArgumentsObject.h"
@@ -1109,22 +1109,17 @@ void js::DumpHeap(JSContext* cx, FILE* f
                   mozilla::MallocSizeOf mallocSizeOf) {
   if (nurseryBehaviour == js::CollectNurseryBeforeDump) {
     cx->runtime()->gc.evictNursery(JS::GCReason::API);
   }
 
   DumpHeapTracer dtrc(fp, cx, mallocSizeOf);
 
   fprintf(dtrc.output, "# Roots.\n");
-  {
-    JSRuntime* rt = cx->runtime();
-    js::gc::AutoTraceSession session(rt);
-    gcstats::AutoPhase ap(rt->gc.stats(), gcstats::PhaseKind::TRACE_HEAP);
-    rt->gc.traceRuntime(&dtrc, session);
-  }
+  TraceRuntimeWithoutEviction(&dtrc);
 
   fprintf(dtrc.output, "# Weak maps.\n");
   WeakMapBase::traceAllMappings(&dtrc);
 
   fprintf(dtrc.output, "==========\n");
 
   dtrc.prefix = "> ";
   IterateHeapUnbarriered(cx, &dtrc, DumpHeapVisitZone, DumpHeapVisitRealm,
--- a/js/src/jsfriendapi.h
+++ b/js/src/jsfriendapi.h
@@ -751,20 +751,16 @@ MOZ_ALWAYS_INLINE size_t GetAtomLength(J
 // Maximum length of a JS string. This is chosen so that the number of bytes
 // allocated for a null-terminated TwoByte string still fits in int32_t.
 static const uint32_t MaxStringLength = (1 << 30) - 2;
 
 static_assert((uint64_t(MaxStringLength) + 1) * sizeof(char16_t) <= INT32_MAX,
               "size of null-terminated JSString char buffer must fit in "
               "INT32_MAX");
 
-MOZ_ALWAYS_INLINE size_t GetFlatStringLength(JSFlatString* s) {
-  return reinterpret_cast<JS::shadow::String*>(s)->length();
-}
-
 MOZ_ALWAYS_INLINE size_t GetLinearStringLength(JSLinearString* s) {
   return reinterpret_cast<JS::shadow::String*>(s)->length();
 }
 
 MOZ_ALWAYS_INLINE bool LinearStringHasLatin1Chars(JSLinearString* s) {
   return reinterpret_cast<JS::shadow::String*>(s)->flags() &
          JS::shadow::String::LATIN1_CHARS_BIT;
 }
@@ -802,24 +798,16 @@ MOZ_ALWAYS_INLINE const char16_t* GetTwo
   }
   return s->nonInlineCharsTwoByte;
 }
 
 MOZ_ALWAYS_INLINE JSLinearString* AtomToLinearString(JSAtom* atom) {
   return reinterpret_cast<JSLinearString*>(atom);
 }
 
-MOZ_ALWAYS_INLINE JSFlatString* AtomToFlatString(JSAtom* atom) {
-  return reinterpret_cast<JSFlatString*>(atom);
-}
-
-MOZ_ALWAYS_INLINE JSLinearString* FlatStringToLinearString(JSFlatString* s) {
-  return reinterpret_cast<JSLinearString*>(s);
-}
-
 MOZ_ALWAYS_INLINE const JS::Latin1Char* GetLatin1AtomChars(
     const JS::AutoRequireNoGC& nogc, JSAtom* atom) {
   return GetLatin1LinearStringChars(nogc, AtomToLinearString(atom));
 }
 
 MOZ_ALWAYS_INLINE const char16_t* GetTwoByteAtomChars(
     const JS::AutoRequireNoGC& nogc, JSAtom* atom) {
   return GetTwoByteLinearStringChars(nogc, AtomToLinearString(atom));
@@ -897,20 +885,16 @@ inline bool CopyStringChars(JSContext* c
   if (!linear) {
     return false;
   }
 
   CopyLinearStringChars(dest, linear, len, start);
   return true;
 }
 
-inline void CopyFlatStringChars(char16_t* dest, JSFlatString* s, size_t len) {
-  CopyLinearStringChars(dest, FlatStringToLinearString(s), len);
-}
-
 /**
  * Add some or all property keys of obj to the id vector *props.
  *
  * The flags parameter controls which property keys are added. Pass a
  * combination of the following bits:
  *
  *     JSITER_OWNONLY - Don't also search the prototype chain; only consider
  *       obj's own properties.
@@ -1131,18 +1115,18 @@ extern JS_FRIEND_API const DOMCallbacks*
 extern JS_FRIEND_API JSObject* GetTestingFunctions(JSContext* cx);
 
 /* Implemented in jsexn.cpp. */
 
 /**
  * Get an error type name from a JSExnType constant.
  * Returns nullptr for invalid arguments and JSEXN_INTERNALERR
  */
-extern JS_FRIEND_API JSFlatString* GetErrorTypeName(JSContext* cx,
-                                                    int16_t exnType);
+extern JS_FRIEND_API JSLinearString* GetErrorTypeName(JSContext* cx,
+                                                      int16_t exnType);
 
 extern JS_FRIEND_API RegExpShared* RegExpToSharedNonInline(
     JSContext* cx, JS::HandleObject regexp);
 
 /* Implemented in CrossCompartmentWrapper.cpp. */
 typedef enum NukeReferencesToWindow {
   NukeWindowReferences,
   DontNukeWindowReferences
--- a/js/src/jspubtd.h
+++ b/js/src/jspubtd.h
@@ -60,17 +60,17 @@ struct JSFunctionSpec;
 struct JSPrincipals;
 struct JSPropertySpec;
 struct JSSecurityCallbacks;
 struct JSStructuredCloneCallbacks;
 struct JSStructuredCloneReader;
 struct JSStructuredCloneWriter;
 class JS_PUBLIC_API JSTracer;
 
-class JSFlatString;
+class JSLinearString;
 
 template <typename T>
 struct JSConstScalarSpec;
 typedef JSConstScalarSpec<double> JSConstDoubleSpec;
 typedef JSConstScalarSpec<int32_t> JSConstIntegerSpec;
 
 namespace js {
 
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -3353,25 +3353,25 @@ struct DisassembleOptionParser {
         lines(false),
         recursive(false),
         sourceNotes(true) {}
 
   bool parse(JSContext* cx) {
     /* Read options off early arguments */
     while (argc > 0 && argv[0].isString()) {
       JSString* str = argv[0].toString();
-      JSFlatString* flatStr = JS_FlattenString(cx, str);
-      if (!flatStr) {
-        return false;
-      }
-      if (JS_FlatStringEqualsLiteral(flatStr, "-l")) {
+      JSLinearString* linearStr = JS_EnsureLinearString(cx, str);
+      if (!linearStr) {
+        return false;
+      }
+      if (JS_LinearStringEqualsLiteral(linearStr, "-l")) {
         lines = true;
-      } else if (JS_FlatStringEqualsLiteral(flatStr, "-r")) {
+      } else if (JS_LinearStringEqualsLiteral(linearStr, "-r")) {
         recursive = true;
-      } else if (JS_FlatStringEqualsLiteral(flatStr, "-S")) {
+      } else if (JS_LinearStringEqualsLiteral(linearStr, "-S")) {
         sourceNotes = false;
       } else {
         break;
       }
       argv++;
       argc--;
     }
     return true;
@@ -4537,23 +4537,23 @@ static bool SetJitCompilerOption(JSConte
   // Disallow setting JIT options when there are worker threads, to avoid
   // races.
   if (workerThreadsLock) {
     ReportUsageErrorASCII(
         cx, callee, "Can't set JIT options when there are worker threads.");
     return false;
   }
 
-  JSFlatString* strArg = JS_FlattenString(cx, args[0].toString());
+  JSLinearString* strArg = JS_EnsureLinearString(cx, args[0].toString());
   if (!strArg) {
     return false;
   }
 
-#define JIT_COMPILER_MATCH(key, string)                      \
-  else if (JS_FlatStringEqualsLiteral(strArg, string)) opt = \
+#define JIT_COMPILER_MATCH(key, string)                        \
+  else if (JS_LinearStringEqualsLiteral(strArg, string)) opt = \
       JSJITCOMPILER_##key;
 
   JSJitCompilerOption opt = JSJITCOMPILER_NOT_AN_OPTION;
   if (false) {
   }
   JIT_COMPILER_OPTIONS(JIT_COMPILER_MATCH);
 #undef JIT_COMPILER_MATCH
 
--- a/js/src/shell/jsshell.cpp
+++ b/js/src/shell/jsshell.cpp
@@ -67,18 +67,17 @@ bool GenerateInterfaceHelp(JSContext* cx
       return false;
     }
     numEntries++;
 
     if (!buf.append("  ", 2)) {
       return false;
     }
 
-    if (!buf.append(usage.isString() ? usage.toString()
-                                     : JSID_TO_FLAT_STRING(id))) {
+    if (!buf.append(usage.isString() ? usage.toString() : JSID_TO_STRING(id))) {
       return false;
     }
   }
 
   RootedString s(cx, buf.finishString());
   if (!s || !JS_DefineProperty(cx, obj, "help", s, 0)) {
     return false;
   }
--- a/js/src/vm/BytecodeUtil.cpp
+++ b/js/src/vm/BytecodeUtil.cpp
@@ -24,18 +24,16 @@
 
 #include "jsapi.h"
 #include "jsnum.h"
 #include "jstypes.h"
 #include "jsutil.h"
 
 #include "frontend/BytecodeCompiler.h"
 #include "frontend/SourceNotes.h"
-#include "gc/FreeOp.h"
-#include "gc/GCInternals.h"
 #include "js/CharacterEncoding.h"
 #include "js/Printf.h"
 #include "js/Symbol.h"
 #include "util/StringBuffer.h"
 #include "util/Text.h"
 #include "vm/BytecodeLocation.h"
 #include "vm/CodeCoverage.h"
 #include "vm/EnvironmentObject.h"
@@ -2857,41 +2855,53 @@ JS_FRIEND_API JSString* js::GetPCCountSc
 
   if (sp.hadOutOfMemory()) {
     return nullptr;
   }
 
   return NewStringCopyZ<CanGC>(cx, sp.string());
 }
 
+struct CollectedScripts {
+  MutableHandle<ScriptVector> scripts;
+  bool ok = true;
+
+  explicit CollectedScripts(MutableHandle<ScriptVector> scripts)
+      : scripts(scripts) {}
+
+  static void consider(JSRuntime* rt, void* data, JSScript* script,
+                       const JS::AutoRequireNoGC& nogc) {
+    auto self = static_cast<CollectedScripts*>(data);
+    if (!script->filename()) {
+      return;
+    }
+    if (!self->scripts.append(script)) {
+      self->ok = false;
+    }
+  }
+};
+
 static bool GenerateLcovInfo(JSContext* cx, JS::Realm* realm,
                              GenericPrinter& out) {
-  JSRuntime* rt = cx->runtime();
-
   AutoRealmUnchecked ar(cx, realm);
 
   // Collect the list of scripts which are part of the current realm.
-  { js::gc::AutoPrepareForTracing apft(cx); }
 
   // Hold the scripts that we have already flushed, to avoid flushing them
   // twice.
   using JSScriptSet = GCHashSet<JSScript*>;
   Rooted<JSScriptSet> scriptsDone(cx, JSScriptSet(cx));
 
   Rooted<ScriptVector> queue(cx, ScriptVector(cx));
-  for (ZonesIter zone(rt, SkipAtoms); !zone.done(); zone.next()) {
-    for (auto script = zone->cellIter<JSScript>(); !script.done();
-         script.next()) {
-      if (script->realm() != realm || !script->filename()) {
-        continue;
-      }
-
-      if (!queue.append(script)) {
-        return false;
-      }
+
+  {
+    CollectedScripts result(&queue);
+    IterateScripts(cx, realm, &result, &CollectedScripts::consider);
+    if (!result.ok) {
+      return false;
     }
   }
 
   if (queue.length() == 0) {
     return true;
   }
 
   // Ensure the LCovRealm exists to collect info into.
--- a/js/src/vm/CharacterEncoding.cpp
+++ b/js/src/vm/CharacterEncoding.cpp
@@ -82,25 +82,25 @@ static size_t GetDeflatedUTF8StringLengt
     while (v) {
       v >>= 5;
       nbytes++;
     }
   }
   return nbytes;
 }
 
-JS_PUBLIC_API size_t JS::GetDeflatedUTF8StringLength(JSFlatString* s) {
+JS_PUBLIC_API size_t JS::GetDeflatedUTF8StringLength(JSLinearString* s) {
   JS::AutoCheckCannotGC nogc;
   return s->hasLatin1Chars()
              ? ::GetDeflatedUTF8StringLength(s->latin1Chars(nogc), s->length())
              : ::GetDeflatedUTF8StringLength(s->twoByteChars(nogc),
                                              s->length());
 }
 
-JS_PUBLIC_API size_t JS::DeflateStringToUTF8Buffer(JSFlatString* src,
+JS_PUBLIC_API size_t JS::DeflateStringToUTF8Buffer(JSLinearString* src,
                                                    mozilla::Span<char> dst) {
   JS::AutoCheckCannotGC nogc;
   if (src->hasLatin1Chars()) {
     auto source = AsChars(MakeSpan(src->latin1Chars(nogc), src->length()));
     size_t read;
     size_t written;
     Tie(read, written) = ConvertLatin1toUtf8Partial(source, dst);
     Unused << read;
--- a/js/src/vm/ErrorObject.cpp
+++ b/js/src/vm/ErrorObject.cpp
@@ -176,19 +176,16 @@ JSErrorReport* js::ErrorObject::getOrCre
   report.column = columnNumber();
 
   // Message. Note that |new Error()| will result in an undefined |message|
   // slot, so we need to explicitly substitute the empty string in that case.
   RootedString message(cx, getMessage());
   if (!message) {
     message = cx->runtime()->emptyString;
   }
-  if (!message->ensureFlat(cx)) {
-    return nullptr;
-  }
 
   UniqueChars utf8 = StringToNewUTF8CharsZ(cx, *message);
   if (!utf8) {
     return nullptr;
   }
   report.initOwnedMessage(utf8.release());
 
   // Cache and return.
--- a/js/src/vm/HelperThreads.cpp
+++ b/js/src/vm/HelperThreads.cpp
@@ -8,17 +8,16 @@
 
 #include "mozilla/Maybe.h"
 #include "mozilla/ScopeExit.h"
 #include "mozilla/Unused.h"
 #include "mozilla/Utf8.h"  // mozilla::Utf8Unit
 
 #include "builtin/Promise.h"
 #include "frontend/BytecodeCompilation.h"
-#include "gc/GCInternals.h"
 #include "jit/IonBuilder.h"
 #include "js/ContextOptions.h"  // JS::ContextOptions
 #include "js/SourceText.h"
 #include "js/UniquePtr.h"
 #include "js/Utility.h"
 #include "threading/CpuCount.h"
 #include "util/NativeStack.h"
 #include "vm/ErrorReporting.h"
--- a/js/src/vm/JSContext.h
+++ b/js/src/vm/JSContext.h
@@ -1331,16 +1331,28 @@ struct MOZ_RAII AutoSetThreadIsSweeping 
 
  private:
   JSContext* cx;
   bool prevState;
   JS::Zone* prevZone;
 #endif
 };
 
+// Note that this class does not suppress buffer allocation/reallocation in the
+// nursery, only Cells themselves.
+class MOZ_RAII AutoSuppressNurseryCellAlloc {
+  JSContext* cx_;
+
+ public:
+  explicit AutoSuppressNurseryCellAlloc(JSContext* cx) : cx_(cx) {
+    cx_->nurserySuppressions_++;
+  }
+  ~AutoSuppressNurseryCellAlloc() { cx_->nurserySuppressions_--; }
+};
+
 }  // namespace gc
 
 } /* namespace js */
 
 #define CHECK_THREAD(cx)                            \
   MOZ_ASSERT_IF(cx && !cx->isHelperThreadContext(), \
                 js::CurrentThreadCanAccessRuntime(cx->runtime()))
 
--- a/js/src/vm/JSScript.cpp
+++ b/js/src/vm/JSScript.cpp
@@ -1909,17 +1909,17 @@ class ScriptSource::LoadSourceMatcher {
 };
 
 /* static */
 bool ScriptSource::loadSource(JSContext* cx, ScriptSource* ss, bool* loaded) {
   return ss->data.match(LoadSourceMatcher(cx, ss, loaded));
 }
 
 /* static */
-JSFlatString* JSScript::sourceData(JSContext* cx, HandleScript script) {
+JSLinearString* JSScript::sourceData(JSContext* cx, HandleScript script) {
   MOZ_ASSERT(script->scriptSource()->hasSourceText());
   return script->scriptSource()->substring(cx, script->sourceStart(),
                                            script->sourceEnd());
 }
 
 bool JSScript::appendSourceDataForToString(JSContext* cx, StringBuffer& buf) {
   MOZ_ASSERT(scriptSource()->hasSourceText());
   return scriptSource()->appendSubstring(cx, buf, toStringStart(),
@@ -2204,18 +2204,18 @@ ScriptSource::PinnedUnits<Unit>::PinnedU
     prev_ = *stack_;
     *stack_ = this;
   }
 }
 
 template class ScriptSource::PinnedUnits<Utf8Unit>;
 template class ScriptSource::PinnedUnits<char16_t>;
 
-JSFlatString* ScriptSource::substring(JSContext* cx, size_t start,
-                                      size_t stop) {
+JSLinearString* ScriptSource::substring(JSContext* cx, size_t start,
+                                        size_t stop) {
   MOZ_ASSERT(start <= stop);
 
   size_t len = stop - start;
   if (!len) {
     return cx->emptyString();
   }
   UncompressedSourceCache::AutoHoldEntry holder;
 
@@ -2234,18 +2234,18 @@ JSFlatString* ScriptSource::substring(JS
   PinnedUnits<char16_t> units(cx, this, holder, start, len);
   if (!units.asChars()) {
     return nullptr;
   }
 
   return NewStringCopyN<CanGC>(cx, units.asChars(), len);
 }
 
-JSFlatString* ScriptSource::substringDontDeflate(JSContext* cx, size_t start,
-                                                 size_t stop) {
+JSLinearString* ScriptSource::substringDontDeflate(JSContext* cx, size_t start,
+                                                   size_t stop) {
   MOZ_ASSERT(start <= stop);
 
   size_t len = stop - start;
   if (!len) {
     return cx->emptyString();
   }
   UncompressedSourceCache::AutoHoldEntry holder;
 
@@ -2300,17 +2300,17 @@ bool ScriptSource::appendSubstring(JSCon
       return false;
     }
 
     const char16_t* units = pinned.get();
     return buf.append(units, len);
   }
 }
 
-JSFlatString* ScriptSource::functionBodyString(JSContext* cx) {
+JSLinearString* ScriptSource::functionBodyString(JSContext* cx) {
   MOZ_ASSERT(isFunctionBody());
 
   size_t start =
       parameterListEnd_ + (sizeof(FunctionConstructorMedialSigils) - 1);
   size_t stop = length() - (sizeof(FunctionConstructorFinalBrace) - 1);
   return substring(cx, start, stop);
 }
 
--- a/js/src/vm/JSScript.h
+++ b/js/src/vm/JSScript.h
@@ -1001,24 +1001,25 @@ class ScriptSource {
   };
 
  public:
   size_t length() const {
     MOZ_ASSERT(hasSourceText() || hasBinASTSource());
     return data.match(UncompressedLengthMatcher());
   }
 
-  JSFlatString* substring(JSContext* cx, size_t start, size_t stop);
-  JSFlatString* substringDontDeflate(JSContext* cx, size_t start, size_t stop);
+  JSLinearString* substring(JSContext* cx, size_t start, size_t stop);
+  JSLinearString* substringDontDeflate(JSContext* cx, size_t start,
+                                       size_t stop);
 
   MOZ_MUST_USE bool appendSubstring(JSContext* cx, js::StringBuffer& buf,
                                     size_t start, size_t stop);
 
   bool isFunctionBody() { return parameterListEnd_ != 0; }
-  JSFlatString* functionBodyString(JSContext* cx);
+  JSLinearString* functionBodyString(JSContext* cx);
 
   void addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf,
                               JS::ScriptSourceInfo* info) const;
 
  private:
   // Overwrites |data| with the uncompressed data from |source|.
   //
   // This function asserts nothing about |data|.  Users should use assertions to
@@ -2766,17 +2767,17 @@ class JSScript : public js::BaseScript {
            bodyScope()->is<js::EvalScope>();
   }
   bool isGlobalCode() const { return bodyScope()->is<js::GlobalScope>(); }
 
   // Returns true if the script may read formal arguments on the stack
   // directly, via lazy arguments or a rest parameter.
   bool mayReadFrameArgsDirectly();
 
-  static JSFlatString* sourceData(JSContext* cx, JS::HandleScript script);
+  static JSLinearString* sourceData(JSContext* cx, JS::HandleScript script);
 
   MOZ_MUST_USE bool appendSourceDataForToString(JSContext* cx,
                                                 js::StringBuffer& buf);
 
   void setDefaultClassConstructorSpan(js::ScriptSourceObject* sourceObject,
                                       uint32_t start, uint32_t end,
                                       unsigned line, unsigned column);
 
--- a/js/src/vm/Runtime.cpp
+++ b/js/src/vm/Runtime.cpp
@@ -22,17 +22,16 @@
 #  include <sys/mman.h>
 #endif
 
 #include "jsfriendapi.h"
 #include "jsmath.h"
 
 #include "builtin/Promise.h"
 #include "gc/FreeOp.h"
-#include "gc/GCInternals.h"
 #include "gc/PublicIterators.h"
 #include "jit/arm/Simulator-arm.h"
 #include "jit/arm64/vixl/Simulator-vixl.h"
 #include "jit/IonBuilder.h"
 #include "jit/JitRealm.h"
 #include "jit/mips32/Simulator-mips32.h"
 #include "jit/mips64/Simulator-mips64.h"
 #include "js/Date.h"
--- a/js/src/vm/SelfHosting.cpp
+++ b/js/src/vm/SelfHosting.cpp
@@ -2870,17 +2870,17 @@ static bool CloneProperties(JSContext* c
         !JS_DefinePropertyById(cx, clone, id, val, attrs[i])) {
       return false;
     }
   }
 
   return true;
 }
 
-static JSString* CloneString(JSContext* cx, JSFlatString* selfHostedString) {
+static JSString* CloneString(JSContext* cx, JSLinearString* selfHostedString) {
   size_t len = selfHostedString->length();
   {
     JS::AutoCheckCannotGC nogc;
     JSString* clone;
     if (selfHostedString->hasLatin1Chars()) {
       clone =
           NewStringCopyN<NoGC>(cx, selfHostedString->latin1Chars(nogc), len);
     } else {
@@ -2994,20 +2994,20 @@ static JSObject* CloneObject(JSContext* 
   } else if (selfHostedObject->is<BooleanObject>()) {
     clone = BooleanObject::create(
         cx, selfHostedObject->as<BooleanObject>().unbox());
   } else if (selfHostedObject->is<NumberObject>()) {
     clone =
         NumberObject::create(cx, selfHostedObject->as<NumberObject>().unbox());
   } else if (selfHostedObject->is<StringObject>()) {
     JSString* selfHostedString = selfHostedObject->as<StringObject>().unbox();
-    if (!selfHostedString->isFlat()) {
+    if (!selfHostedString->isLinear()) {
       MOZ_CRASH();
     }
-    RootedString str(cx, CloneString(cx, &selfHostedString->asFlat()));
+    RootedString str(cx, CloneString(cx, &selfHostedString->asLinear()));
     if (!str) {
       return nullptr;
     }
     clone = StringObject::create(cx, str);
   } else if (selfHostedObject->is<ArrayObject>()) {
     clone = NewDenseEmptyArray(cx, nullptr, TenuredObject);
   } else {
     MOZ_ASSERT(selfHostedObject->isNative());
@@ -3035,20 +3035,20 @@ static bool CloneValue(JSContext* cx, Ha
       return false;
     }
     vp.setObject(*clone);
   } else if (selfHostedValue.isBoolean() || selfHostedValue.isNumber() ||
              selfHostedValue.isNullOrUndefined()) {
     // Nothing to do here: these are represented inline in the value.
     vp.set(selfHostedValue);
   } else if (selfHostedValue.isString()) {
-    if (!selfHostedValue.toString()->isFlat()) {
+    if (!selfHostedValue.toString()->isLinear()) {
       MOZ_CRASH();
     }
-    JSFlatString* selfHostedString = &selfHostedValue.toString()->asFlat();
+    JSLinearString* selfHostedString = &selfHostedValue.toString()->asLinear();
     JSString* clone = CloneString(cx, selfHostedString);
     if (!clone) {
       return false;
     }
     vp.setString(clone);
   } else if (selfHostedValue.isSymbol()) {
     // Well-known symbols are shared.
     mozilla::DebugOnly<JS::Symbol*> sym = selfHostedValue.toSymbol();
--- a/js/src/vm/StringType.cpp
+++ b/js/src/vm/StringType.cpp
@@ -23,17 +23,16 @@
 #include "mozilla/Vector.h"
 
 #include <algorithm>    // std::{all_of,copy_n,enable_if,is_const,move}
 #include <type_traits>  // std::is_unsigned
 
 #include "jsfriendapi.h"
 
 #include "frontend/BytecodeCompiler.h"
-#include "gc/GCInternals.h"
 #include "gc/Marking.h"
 #include "gc/Nursery.h"
 #include "js/CharacterEncoding.h"
 #include "js/StableStringChars.h"
 #include "js/Symbol.h"
 #include "js/UbiNode.h"
 #include "util/StringBuffer.h"
 #include "util/Unicode.h"
--- a/js/src/vm/TypeInference.cpp
+++ b/js/src/vm/TypeInference.cpp
@@ -75,17 +75,17 @@ const char* js::TypeIdStringImpl(jsid id
     return "(new)";
   }
   if (JSID_IS_SYMBOL(id)) {
     return "(symbol)";
   }
   static char bufs[4][100];
   static unsigned which = 0;
   which = (which + 1) & 3;
-  PutEscapedString(bufs[which], 100, JSID_TO_FLAT_STRING(id), 0);
+  PutEscapedString(bufs[which], 100, JSID_TO_LINEAR_STRING(id), 0);
   return bufs[which];
 }
 
 #endif
 
 /////////////////////////////////////////////////////////////////////
 // Logging
 /////////////////////////////////////////////////////////////////////
--- a/js/src/wasm/AsmJS.cpp
+++ b/js/src/wasm/AsmJS.cpp
@@ -6899,17 +6899,17 @@ static bool HandleInstantiationFailure(J
   if (!haveSource) {
     JS_ReportErrorASCII(cx,
                         "asm.js link failure with source discarding enabled");
     return false;
   }
 
   uint32_t begin = metadata.toStringStart;
   uint32_t end = metadata.srcEndAfterCurly();
-  Rooted<JSFlatString*> src(cx, source->substringDontDeflate(cx, begin, end));
+  Rooted<JSLinearString*> src(cx, source->substringDontDeflate(cx, begin, end));
   if (!src) {
     return false;
   }
 
   RootedFunction fun(
       cx, NewScriptedFunction(cx, 0, FunctionFlags::INTERPRETED_NORMAL, name,
                               /* proto = */ nullptr, gc::AllocKind::FUNCTION,
                               TenuredObject));
@@ -7244,17 +7244,17 @@ JSString* js::AsmJSModuleToString(JSCont
     }
     if (fun->explicitName() && !out.append(fun->explicitName())) {
       return nullptr;
     }
     if (!out.append("() {\n    [native code]\n}")) {
       return nullptr;
     }
   } else {
-    Rooted<JSFlatString*> src(cx, source->substring(cx, begin, end));
+    Rooted<JSLinearString*> src(cx, source->substring(cx, begin, end));
     if (!src) {
       return nullptr;
     }
 
     if (!out.append(src)) {
       return nullptr;
     }
   }
@@ -7294,17 +7294,17 @@ JSString* js::AsmJSFunctionToString(JSCo
     MOZ_ASSERT(fun->explicitName());
     if (!out.append(fun->explicitName())) {
       return nullptr;
     }
     if (!out.append("() {\n    [native code]\n}")) {
       return nullptr;
     }
   } else {
-    Rooted<JSFlatString*> src(cx, source->substring(cx, begin, end));
+    Rooted<JSLinearString*> src(cx, source->substring(cx, begin, end));
     if (!src) {
       return nullptr;
     }
     if (!out.append(src)) {
       return nullptr;
     }
   }
 
--- a/js/src/wasm/WasmJS.cpp
+++ b/js/src/wasm/WasmJS.cpp
@@ -1013,27 +1013,28 @@ bool WasmModuleObject::customSections(JS
 
   Vector<char, 8> name(cx);
   {
     RootedString str(cx, ToString(cx, args.get(1)));
     if (!str) {
       return false;
     }
 
-    Rooted<JSFlatString*> flat(cx, str->ensureFlat(cx));
-    if (!flat) {
+    Rooted<JSLinearString*> linear(cx, str->ensureLinear(cx));
+    if (!linear) {
       return false;
     }
 
-    if (!name.initLengthUninitialized(JS::GetDeflatedUTF8StringLength(flat))) {
+    if (!name.initLengthUninitialized(
+            JS::GetDeflatedUTF8StringLength(linear))) {
       return false;
     }
 
     mozilla::Unused << JS::DeflateStringToUTF8Buffer(
-        flat, MakeSpan(name.begin(), name.length()));
+        linear, MakeSpan(name.begin(), name.length()));
   }
 
   RootedValueVector elems(cx);
   RootedArrayBufferObject buf(cx);
   for (const CustomSection& cs : module->customSections()) {
     if (name.length() != cs.name.length()) {
       continue;
     }
--- a/js/src/wasm/WasmTextToBinary.cpp
+++ b/js/src/wasm/WasmTextToBinary.cpp
@@ -616,19 +616,19 @@ class WasmTokenStream {
   WasmToken fail(const char16_t* begin) const { return WasmToken(begin); }
 
   WasmToken nan(const char16_t* begin);
   WasmToken literal(const char16_t* begin);
   WasmToken next();
   void skipSpaces();
 
  public:
-  explicit WasmTokenStream(const char16_t* text)
+  explicit WasmTokenStream(const char16_t* text, size_t textLen)
       : cur_(text),
-        end_(text + js_strlen(text)),
+        end_(text + textLen),
         lineStart_(text),
         line_(1),
         lookaheadIndex_(0),
         lookaheadDepth_(0) {}
   void generateError(WasmToken token, UniqueChars* error) {
     unsigned column = token.begin() - lineStart_ + 1;
     *error = JS_smprintf("parsing wasm text at %u:%u", line_, column);
   }
@@ -2298,19 +2298,19 @@ struct WasmParseContext {
   WasmTokenStream ts;
   LifoAlloc& lifo;
   UniqueChars* error;
   DtoaState* dtoaState;
   uintptr_t stackLimit;
   uint32_t nextSym;
   bool requiresDataCount;
 
-  WasmParseContext(const char16_t* text, uintptr_t stackLimit, LifoAlloc& lifo,
-                   UniqueChars* error)
-      : ts(text),
+  WasmParseContext(const char16_t* text, size_t textLen, uintptr_t stackLimit,
+                   LifoAlloc& lifo, UniqueChars* error)
+      : ts(text, textLen),
         lifo(lifo),
         error(error),
         dtoaState(NewDtoaState()),
         stackLimit(stackLimit),
         nextSym(0),
         requiresDataCount(false) {}
 
   ~WasmParseContext() { DestroyDtoaState(dtoaState); }
@@ -5266,20 +5266,20 @@ static AstModule* ParseBinaryModule(Wasm
   auto* data = new (c.lifo) AstDataSegment(nullptr, std::move(fragments));
   if (!data || !module->append(data)) {
     return nullptr;
   }
 
   return module;
 }
 
-static AstModule* ParseModule(const char16_t* text, uintptr_t stackLimit,
-                              LifoAlloc& lifo, UniqueChars* error,
-                              bool* binary) {
-  WasmParseContext c(text, stackLimit, lifo, error);
+static AstModule* ParseModule(const char16_t* text, size_t textLen,
+                              uintptr_t stackLimit, LifoAlloc& lifo,
+                              UniqueChars* error, bool* binary) {
+  WasmParseContext c(text, textLen, stackLimit, lifo, error);
 
   *binary = false;
 
   if (!c.ts.match(WasmToken::OpenParen, c.error)) {
     return nullptr;
   }
   if (!c.ts.match(WasmToken::Module, c.error)) {
     return nullptr;
@@ -7625,23 +7625,24 @@ static bool EncodeBinaryModule(const Ast
     }
   }
 
   return true;
 }
 
 /*****************************************************************************/
 
-bool wasm::TextToBinary(const char16_t* text, uintptr_t stackLimit,
-                        Bytes* bytes, Uint32Vector* offsets,
-                        UniqueChars* error) {
+bool wasm::TextToBinary(const char16_t* text, size_t textLen,
+                        uintptr_t stackLimit, Bytes* bytes,
+                        Uint32Vector* offsets, UniqueChars* error) {
   LifoAlloc lifo(AST_LIFO_DEFAULT_CHUNK_SIZE);
 
   bool binary = false;
-  AstModule* module = ParseModule(text, stackLimit, lifo, error, &binary);
+  AstModule* module =
+      ParseModule(text, textLen, stackLimit, lifo, error, &binary);
   if (!module) {
     return false;
   }
 
   if (binary) {
     return EncodeBinaryModule(*module, bytes);
   }
 
--- a/js/src/wasm/WasmTextToBinary.h
+++ b/js/src/wasm/WasmTextToBinary.h
@@ -20,20 +20,20 @@
 #define wasm_text_to_binary_h
 
 #include "wasm/WasmTypes.h"
 
 namespace js {
 namespace wasm {
 
 // Translate the textual representation of a wasm module (given by a
-// null-terminated char16_t array) into serialized bytes. If there is an error
+// char16_t array + length) into serialized bytes. If there is an error
 // other than out-of-memory an error message string will be stored in 'error'.
 
-extern MOZ_MUST_USE bool TextToBinary(const char16_t* text,
+extern MOZ_MUST_USE bool TextToBinary(const char16_t* text, size_t textLen,
                                       uintptr_t stackLimit, Bytes* bytes,
                                       Uint32Vector* offsets,
                                       UniqueChars* error);
 
 }  // namespace wasm
 }  // namespace js
 
 #endif  // wasm_text_to_binary_h
--- a/js/xpconnect/src/Sandbox.cpp
+++ b/js/xpconnect/src/Sandbox.cpp
@@ -811,78 +811,78 @@ bool xpc::GlobalProperties::Parse(JSCont
   for (uint32_t i = 0; i < length; i++) {
     RootedValue nameValue(cx);
     ok = JS_GetElement(cx, obj, i, &nameValue);
     NS_ENSURE_TRUE(ok, false);
     if (!nameValue.isString()) {
       JS_ReportErrorASCII(cx, "Property names must be strings");
       return false;
     }
-    JSFlatString* nameStr = JS_FlattenString(cx, nameValue.toString());
+    JSLinearString* nameStr = JS_EnsureLinearString(cx, nameValue.toString());
     if (!nameStr) {
       return false;
     }
-    if (JS_FlatStringEqualsLiteral(nameStr, "Blob")) {
+    if (JS_LinearStringEqualsLiteral(nameStr, "Blob")) {
       Blob = true;
-    } else if (JS_FlatStringEqualsLiteral(nameStr, "ChromeUtils")) {
+    } else if (JS_LinearStringEqualsLiteral(nameStr, "ChromeUtils")) {
       ChromeUtils = true;
-    } else if (JS_FlatStringEqualsLiteral(nameStr, "CSS")) {
+    } else if (JS_LinearStringEqualsLiteral(nameStr, "CSS")) {
       CSS = true;
-    } else if (JS_FlatStringEqualsLiteral(nameStr, "CSSRule")) {
+    } else if (JS_LinearStringEqualsLiteral(nameStr, "CSSRule")) {
       CSSRule = true;
-    } else if (JS_FlatStringEqualsLiteral(nameStr, "Directory")) {
+    } else if (JS_LinearStringEqualsLiteral(nameStr, "Directory")) {
       Directory = true;
-    } else if (JS_FlatStringEqualsLiteral(nameStr, "DOMParser")) {
+    } else if (JS_LinearStringEqualsLiteral(nameStr, "DOMParser")) {
       DOMParser = true;
-    } else if (JS_FlatStringEqualsLiteral(nameStr, "Element")) {
+    } else if (JS_LinearStringEqualsLiteral(nameStr, "Element")) {
       Element = true;
-    } else if (JS_FlatStringEqualsLiteral(nameStr, "Event")) {
+    } else if (JS_LinearStringEqualsLiteral(nameStr, "Event")) {
       Event = true;
-    } else if (JS_FlatStringEqualsLiteral(nameStr, "File")) {
+    } else if (JS_LinearStringEqualsLiteral(nameStr, "File")) {
       File = true;
-    } else if (JS_FlatStringEqualsLiteral(nameStr, "FileReader")) {
+    } else if (JS_LinearStringEqualsLiteral(nameStr, "FileReader")) {
       FileReader = true;
-    } else if (JS_FlatStringEqualsLiteral(nameStr, "FormData")) {
+    } else if (JS_LinearStringEqualsLiteral(nameStr, "FormData")) {
       FormData = true;
-    } else if (JS_FlatStringEqualsLiteral(nameStr, "InspectorUtils")) {
+    } else if (JS_LinearStringEqualsLiteral(nameStr, "InspectorUtils")) {
       InspectorUtils = true;
-    } else if (JS_FlatStringEqualsLiteral(nameStr, "MessageChannel")) {
+    } else if (JS_LinearStringEqualsLiteral(nameStr, "MessageChannel")) {
       MessageChannel = true;
-    } else if (JS_FlatStringEqualsLiteral(nameStr, "Node")) {
+    } else if (JS_LinearStringEqualsLiteral(nameStr, "Node")) {
       Node = true;
-    } else if (JS_FlatStringEqualsLiteral(nameStr, "NodeFilter")) {
+    } else if (JS_LinearStringEqualsLiteral(nameStr, "NodeFilter")) {
       NodeFilter = true;
-    } else if (JS_FlatStringEqualsLiteral(nameStr, "PromiseDebugging")) {
+    } else if (JS_LinearStringEqualsLiteral(nameStr, "PromiseDebugging")) {
       PromiseDebugging = true;
-    } else if (JS_FlatStringEqualsLiteral(nameStr, "TextDecoder")) {
+    } else if (JS_LinearStringEqualsLiteral(nameStr, "TextDecoder")) {
       TextDecoder = true;
-    } else if (JS_FlatStringEqualsLiteral(nameStr, "TextEncoder")) {
+    } else if (JS_LinearStringEqualsLiteral(nameStr, "TextEncoder")) {
       TextEncoder = true;
-    } else if (JS_FlatStringEqualsLiteral(nameStr, "URL")) {
+    } else if (JS_LinearStringEqualsLiteral(nameStr, "URL")) {
       URL = true;
-    } else if (JS_FlatStringEqualsLiteral(nameStr, "URLSearchParams")) {
+    } else if (JS_LinearStringEqualsLiteral(nameStr, "URLSearchParams")) {
       URLSearchParams = true;
-    } else if (JS_FlatStringEqualsLiteral(nameStr, "XMLHttpRequest")) {
+    } else if (JS_LinearStringEqualsLiteral(nameStr, "XMLHttpRequest")) {
       XMLHttpRequest = true;
-    } else if (JS_FlatStringEqualsLiteral(nameStr, "XMLSerializer")) {
+    } else if (JS_LinearStringEqualsLiteral(nameStr, "XMLSerializer")) {
       XMLSerializer = true;
-    } else if (JS_FlatStringEqualsLiteral(nameStr, "atob")) {
+    } else if (JS_LinearStringEqualsLiteral(nameStr, "atob")) {
       atob = true;
-    } else if (JS_FlatStringEqualsLiteral(nameStr, "btoa")) {
+    } else if (JS_LinearStringEqualsLiteral(nameStr, "btoa")) {
       btoa = true;
-    } else if (JS_FlatStringEqualsLiteral(nameStr, "caches")) {
+    } else if (JS_LinearStringEqualsLiteral(nameStr, "caches")) {
       caches = true;
-    } else if (JS_FlatStringEqualsLiteral(nameStr, "crypto")) {
+    } else if (JS_LinearStringEqualsLiteral(nameStr, "crypto")) {
       crypto = true;
-    } else if (JS_FlatStringEqualsLiteral(nameStr, "fetch")) {
+    } else if (JS_LinearStringEqualsLiteral(nameStr, "fetch")) {
       fetch = true;
-    } else if (JS_FlatStringEqualsLiteral(nameStr, "indexedDB")) {
+    } else if (JS_LinearStringEqualsLiteral(nameStr, "indexedDB")) {
       indexedDB = true;
 #ifdef MOZ_WEBRTC
-    } else if (JS_FlatStringEqualsLiteral(nameStr, "rtcIdentityProvider")) {
+    } else if (JS_LinearStringEqualsLiteral(nameStr, "rtcIdentityProvider")) {
       rtcIdentityProvider = true;
 #endif
     } else {
       RootedString nameStr(cx, nameValue.toString());
       JS::UniqueChars name = JS_EncodeStringToUTF8(cx, nameStr);
       if (!name) {
         return false;
       }
--- a/js/xpconnect/src/XPCConvert.cpp
+++ b/js/xpconnect/src/XPCConvert.cpp
@@ -704,26 +704,26 @@ bool XPCConvert::JSData2Native(JSContext
       }
 
       size_t length = JS_GetStringLength(str);
       if (!length) {
         rs->Truncate();
         return true;
       }
 
-      JSFlatString* flat = JS_FlattenString(cx, str);
-      if (!flat) {
+      JSLinearString* linear = JS_EnsureLinearString(cx, str);
+      if (!linear) {
         return false;
       }
 
-      size_t utf8Length = JS::GetDeflatedUTF8StringLength(flat);
+      size_t utf8Length = JS::GetDeflatedUTF8StringLength(linear);
       rs->SetLength(utf8Length);
 
       mozilla::DebugOnly<size_t> written = JS::DeflateStringToUTF8Buffer(
-          flat, mozilla::MakeSpan(rs->BeginWriting(), utf8Length));
+          linear, mozilla::MakeSpan(rs->BeginWriting(), utf8Length));
       MOZ_ASSERT(written == utf8Length);
 
       return true;
     }
 
     case nsXPTType::T_CSTRING: {
       nsACString* rs = (nsACString*)d;
       if (s.isNull() || s.isUndefined()) {
--- a/js/xpconnect/src/nsXPConnect.cpp
+++ b/js/xpconnect/src/nsXPConnect.cpp
@@ -375,20 +375,20 @@ void xpc::ErrorNote::ErrorNoteToMessageS
   }
 }
 
 /* static */
 void xpc::ErrorReport::ErrorReportToMessageString(JSErrorReport* aReport,
                                                   nsAString& aString) {
   aString.Truncate();
   if (aReport->message()) {
-    JSFlatString* name = js::GetErrorTypeName(
+    JSLinearString* name = js::GetErrorTypeName(
         CycleCollectedJSContext::Get()->Context(), aReport->exnType);
     if (name) {
-      AssignJSFlatString(aString, name);
+      AssignJSLinearString(aString, name);
       aString.AppendLiteral(": ");
     }
     aString.Append(NS_ConvertUTF8toUTF16(aReport->message().c_str()));
   }
 }
 
 /***************************************************************************/
 
@@ -465,20 +465,21 @@ JSObject* CreateGlobalObject(JSContext* 
         TraceChildren(&trc, GCCellPtr(global.get()));
         MOZ_ASSERT(trc.ok,
                    "Trace hook on global needs to call TraceXPCGlobal for "
                    "XPConnect compartments.");
       }
 #endif
 
       const char* className = clasp->name;
-      AllocateProtoAndIfaceCache(global, (strcmp(className, "Window") == 0 ||
-                                          strcmp(className, "ChromeWindow") == 0)
-                                           ? ProtoAndIfaceCache::WindowLike
-                                           : ProtoAndIfaceCache::NonWindowLike);
+      AllocateProtoAndIfaceCache(global,
+                                 (strcmp(className, "Window") == 0 ||
+                                  strcmp(className, "ChromeWindow") == 0)
+                                     ? ProtoAndIfaceCache::WindowLike
+                                     : ProtoAndIfaceCache::NonWindowLike);
     }
   }
 
   return global;
 }
 
 void InitGlobalObjectOptions(JS::RealmOptions& aOptions,
                              nsIPrincipal* aPrincipal) {
--- a/js/xpconnect/tests/chrome/test_wrappers.xul
+++ b/js/xpconnect/tests/chrome/test_wrappers.xul
@@ -67,24 +67,16 @@ https://bugzilla.mozilla.org/show_bug.cg
       "stringifying a native function." + " " + eventTargetToString + " " + nativeToString);
 
    is(win.XPathResult.NUMBER_TYPE, 1, "can access constants on constructors");
    is(typeof win.IDBKeyRange.bound, "function", "can access crazy IDBKeyRange static functions");
 
    // Test getter/setter lookup on Xray wrappers.
    ok(Object.prototype.__lookupGetter__.call(win.document, 'title'), 'found getter on document');
    ok(Object.prototype.__lookupGetter__.call(win.document, 'title'), 'found getter on document');
-
-   // Test QI on new dom bindings.
-   try {
-     var QIed = win.document.documentElement.QueryInterface(Ci.nsIClassInfo);
-     ok(false, "Should throw for new binding objects not having classinfo");
-   } catch(e) {
-     is(e.name, "NS_NOINTERFACE", "Threw while QI-ing on wrapper");
-   }
   }
 
   SimpleTest.waitForExplicitFinish();
 
   ]]></script>
   <iframe type="content"
           src="http://example.org/tests/js/xpconnect/tests/mochitest/chrome_wrappers_helper.html"
           onload="go()"
--- a/media/audioipc/README_MOZILLA
+++ b/media/audioipc/README_MOZILLA
@@ -1,8 +1,8 @@
 The source from this directory was copied from the audioipc-2
 git repository using the update.sh script.  The only changes
 made were those applied by update.sh and the addition of
 Makefile.in build files for the Mozilla build system.
 
 The audioipc-2 git repository is: https://github.com/djg/audioipc-2.git
 
-The git commit ID used was f6f56c4352ee4c1b5e483e4594c4c3f309246ceb (2019-07-30 10:52:45 +1200)
+The git commit ID used was b82970f6106ceec4a8f599eda631775eb5d5afaf (2019-10-11 08:25:07 +1300)
--- a/media/audioipc/audioipc/Cargo.toml
+++ b/media/audioipc/audioipc/Cargo.toml
@@ -6,36 +6,37 @@ authors = [
         "Dan Glastonbury <dan.glastonbury@gmail.com>"
         ]
 description = "Remote Cubeb IPC"
 edition = "2018"
 
 [dependencies]
 bincode = "1.0"
 bytes = "0.4"
-cubeb = "0.5.5"
+cubeb = "0.6.0"
 futures = "0.1.18"
 log = "0.4"
 memmap = "0.7"
-scoped-tls = "0.1"
 serde = "1.*.*"
 serde_derive = "1.*.*"
 tokio = "0.1"
 tokio-io = "0.1"
+audio_thread_priority = "0.20.2"
 
 [target.'cfg(unix)'.dependencies]
 iovec = "0.1"
 libc = "0.2"
 mio = "0.6.19"
 mio-uds = "0.6.7"
 tokio-reactor = "0.1"
 
 [target.'cfg(windows)'.dependencies]
+miow = "0.3.3"
 mio-named-pipes = { git = "https://github.com/alexcrichton/mio-named-pipes" }
 tokio-named-pipes = { git = "https://github.com/NikVolf/tokio-named-pipes", branch = "stable" }
-winapi = "0.3.6"
+winapi = { version = "0.3.6", features = ["combaseapi", "objbase"] }
 
 [dependencies.error-chain]
 version = "0.11.0"
 default-features = false
 
 [build-dependencies]
 cc = "1.0"
--- a/media/audioipc/audioipc/src/frame.rs
+++ b/media/audioipc/audioipc/src/frame.rs
@@ -90,16 +90,19 @@ where
                     return Ok(Some(frame).into());
                 }
 
                 self.is_readable = false;
             }
 
             assert!(!self.eof);
 
+            // XXX(kinetik): work around tokio_named_pipes assuming at least 1kB available.
+            self.read_buf.reserve(INITIAL_CAPACITY);
+
             // Otherwise, try to read more data and try again. Make sure we've
             // got room for at least one byte to read to ensure that we don't
             // get a spurious 0 that looks like EOF
             if try_ready!(self.io.read_buf(&mut self.read_buf)) == 0 {
                 self.eof = true;
             }
 
             self.is_readable = true;
--- a/media/audioipc/audioipc/src/handle_passing.rs
+++ b/media/audioipc/audioipc/src/handle_passing.rs
@@ -128,16 +128,19 @@ where
                     return Ok(Some(item).into());
                 }
 
                 self.is_readable = false;
             }
 
             assert!(!self.eof);
 
+            // XXX(kinetik): work around tokio_named_pipes assuming at least 1kB available.
+            self.read_buf.reserve(INITIAL_CAPACITY);
+
             // Otherwise, try to read more data and try again. Make sure we've
             // got room for at least one byte to read to ensure that we don't
             // get a spurious 0 that looks like EOF
             let n = try_ready!(self.io.read_buf(&mut self.read_buf));
 
             if n == 0 {
                 self.eof = true;
             }
--- a/media/audioipc/audioipc/src/lib.rs
+++ b/media/audioipc/audioipc/src/lib.rs
@@ -179,8 +179,25 @@ pub fn get_shm_path(dir: &str) -> PathBu
 pub mod messagestream_unix;
 #[cfg(unix)]
 pub use crate::messagestream_unix::*;
 
 #[cfg(windows)]
 pub mod messagestream_win;
 #[cfg(windows)]
 pub use messagestream_win::*;
+
+#[cfg(windows)]
+pub fn server_platform_init() {
+    use winapi::um::combaseapi;
+    use winapi::um::objbase;
+    use winapi::shared::winerror;
+
+    unsafe {
+        let r = combaseapi::CoInitializeEx(std::ptr::null_mut(),
+                                           objbase::COINIT_MULTITHREADED);
+        assert!(winerror::SUCCEEDED(r));
+    }
+}
+
+#[cfg(unix)]
+pub fn server_platform_init() {
+}
--- a/media/audioipc/audioipc/src/messages.rs
+++ b/media/audioipc/audioipc/src/messages.rs
@@ -4,16 +4,18 @@
 // accompanying file LICENSE for details
 
 use crate::PlatformHandle;
 use crate::PlatformHandleType;
 use cubeb::{self, ffi};
 use std::ffi::{CStr, CString};
 use std::os::raw::{c_char, c_int, c_uint};
 use std::ptr;
+#[cfg(target_os = "linux")]
+use audio_thread_priority::RtPriorityThreadInfo;
 
 #[derive(Debug, Serialize, Deserialize)]
 pub struct Device {
     pub output_name: Option<Vec<u8>>,
     pub input_name: Option<Vec<u8>>,
 }
 
 impl<'a> From<&'a cubeb::DeviceRef> for Device {
@@ -200,19 +202,21 @@ pub enum ServerMessage {
     StreamDestroy(usize),
 
     StreamStart(usize),
     StreamStop(usize),
     StreamResetDefaultDevice(usize),
     StreamGetPosition(usize),
     StreamGetLatency(usize),
     StreamSetVolume(usize, f32),
-    StreamSetPanning(usize, f32),
     StreamGetCurrentDevice(usize),
     StreamRegisterDeviceChangeCallback(usize, bool),
+
+    #[cfg(target_os = "linux")]
+    PromoteThreadToRealTime([u8; std::mem::size_of::<RtPriorityThreadInfo>()]),
 }
 
 // Server -> Client messages.
 // TODO: Streams need id.
 #[derive(Debug, Serialize, Deserialize)]
 pub enum ClientMessage {
     ClientConnected,
     ClientDisconnected,
@@ -229,20 +233,22 @@ pub enum ClientMessage {
     StreamDestroyed,
 
     StreamStarted,
     StreamStopped,
     StreamDefaultDeviceReset,
     StreamPosition(u64),
     StreamLatency(u32),
     StreamVolumeSet,
-    StreamPanningSet,
     StreamCurrentDevice(Device),
     StreamRegisterDeviceChangeCallback,
 
+    #[cfg(target_os = "linux")]
+    ThreadPromoted,
+
     Error(c_int),
 }
 
 #[derive(Debug, Deserialize, Serialize)]
 pub enum CallbackReq {
     Data {
         nframes: isize,
         input_frame_size: usize,
--- a/media/audioipc/audioipc/src/messagestream_win.rs
+++ b/media/audioipc/audioipc/src/messagestream_win.rs
@@ -3,43 +3,58 @@
 // This program is made available under an ISC-style license.  See the
 // accompanying file LICENSE for details
 
 use mio_named_pipes;
 use std::os::windows::io::{AsRawHandle, FromRawHandle, IntoRawHandle, RawHandle};
 use std::sync::atomic::{AtomicUsize, Ordering};
 use tokio_io::{AsyncRead, AsyncWrite};
 use tokio_named_pipes;
+use winapi::um::winbase::FILE_FLAG_OVERLAPPED;
+use std::os::windows::fs::*;
 
 #[derive(Debug)]
-pub struct MessageStream(mio_named_pipes::NamedPipe);
+pub struct MessageStream(miow::pipe::NamedPipe);
 pub struct AsyncMessageStream(tokio_named_pipes::NamedPipe);
 
 impl MessageStream {
-    fn new(stream: mio_named_pipes::NamedPipe) -> MessageStream {
+    fn new(stream: miow::pipe::NamedPipe) -> MessageStream {
         MessageStream(stream)
     }
 
     pub fn anonymous_ipc_pair(
     ) -> std::result::Result<(MessageStream, MessageStream), std::io::Error> {
-        let pipe1 = mio_named_pipes::NamedPipe::new(get_pipe_name())?;
-        let pipe2 = unsafe { mio_named_pipes::NamedPipe::from_raw_handle(pipe1.as_raw_handle()) };
+        let pipe_name = get_pipe_name();
+        let pipe1 = miow::pipe::NamedPipe::new(&pipe_name)?;
+        let pipe2 = {
+            let mut opts = std::fs::OpenOptions::new();
+            opts.read(true)
+                .write(true)
+                .custom_flags(FILE_FLAG_OVERLAPPED);
+            let file = opts.open(&pipe_name)?;
+            unsafe {
+                miow::pipe::NamedPipe::from_raw_handle(file.into_raw_handle())
+            }
+        };
         Ok((MessageStream::new(pipe1), MessageStream::new(pipe2)))
     }
 
     pub unsafe fn from_raw_fd(raw: super::PlatformHandleType) -> MessageStream {
-        MessageStream::new(mio_named_pipes::NamedPipe::from_raw_handle(raw))
+        MessageStream::new(miow::pipe::NamedPipe::from_raw_handle(raw))
     }
 
     pub fn into_tokio_ipc(
         self,
         handle: &tokio::reactor::Handle,
     ) -> std::result::Result<AsyncMessageStream, std::io::Error> {
+        let pipe = unsafe {
+            mio_named_pipes::NamedPipe::from_raw_handle(self.into_raw_handle())
+        };
         Ok(AsyncMessageStream::new(
-            tokio_named_pipes::NamedPipe::from_pipe(self.0, handle)?,
+            tokio_named_pipes::NamedPipe::from_pipe(pipe, handle)?,
         ))
     }
 }
 
 impl AsyncMessageStream {
     fn new(stream: tokio_named_pipes::NamedPipe) -> AsyncMessageStream {
         AsyncMessageStream(stream)
     }
@@ -79,18 +94,17 @@ impl AsyncWrite for AsyncMessageStream {
 impl AsRawHandle for AsyncMessageStream {
     fn as_raw_handle(&self) -> RawHandle {
         self.0.as_raw_handle()
     }
 }
 
 impl IntoRawHandle for MessageStream {
     fn into_raw_handle(self) -> RawHandle {
-        // XXX: Ideally this would call into_raw_handle.
-        self.0.as_raw_handle()
+        self.0.into_raw_handle()
     }
 }
 
 static PIPE_ID: AtomicUsize = AtomicUsize::new(0);
 
 fn get_pipe_name() -> String {
     let pid = std::process::id();
     let pipe_id = PIPE_ID.fetch_add(1, Ordering::SeqCst);
--- a/media/audioipc/client/Cargo.toml
+++ b/media/audioipc/client/Cargo.toml
@@ -4,16 +4,15 @@ version = "0.4.0"
 authors = [
         "Matthew Gregan <kinetik@flim.org>",
         "Dan Glastonbury <dan.glastonbury@gmail.com>"
         ]
 description = "Cubeb Backend for talking to remote cubeb server."
 edition = "2018"
 
 [dependencies]
-audio_thread_priority = "0.19.1"
+audio_thread_priority = "0.20.2"
 audioipc = { path="../audioipc" }
-cubeb-backend = "0.5.5"
+cubeb-backend = "0.6.0"
 futures = { version="0.1.18", default-features=false, features=["use_std"] }
 futures-cpupool = { version="0.1.8", default-features=false }
-lazy_static = "1.2.0"
 log = "0.4"
 tokio = "0.1"
--- a/media/audioipc/client/src/context.rs
+++ b/media/audioipc/client/src/context.rs
@@ -1,18 +1,20 @@
 // Copyright © 2017 Mozilla Foundation
 //
 // This program is made available under an ISC-style license.  See the
 // accompanying file LICENSE for details
 
-use crate::assert_not_in_callback;
+use crate::{assert_not_in_callback, run_in_callback};
 use crate::stream;
+use crate::{ClientStream, G_SERVER_FD, CPUPOOL_INIT_PARAMS};
+#[cfg(not(target_os = "linux"))]
+use audio_thread_priority::promote_current_thread_to_real_time;
 #[cfg(target_os = "linux")]
-use crate::G_THREAD_POOL;
-use crate::{ClientStream, CpuPoolInitParams, CPUPOOL_INIT_PARAMS, G_SERVER_FD};
+use audio_thread_priority::get_current_thread_info;
 use audioipc::codec::LengthDelimitedCodec;
 use audioipc::frame::{framed, Framed};
 use audioipc::platformhandle_passing::{framed_with_platformhandles, FramedWithPlatformHandles};
 use audioipc::{core, rpc};
 use audioipc::{
     messages, messages::DeviceCollectionReq, messages::DeviceCollectionResp, ClientMessage,
     ServerMessage,
 };
@@ -84,48 +86,56 @@ fn open_server_stream() -> io::Result<au
 
         Err(io::Error::new(
             io::ErrorKind::Other,
             "Failed to get server connection.",
         ))
     }
 }
 
+#[cfg(target_os = "linux")]
+fn promote_thread(rpc: &rpc::ClientProxy<ServerMessage, ClientMessage>)
+{
+    match get_current_thread_info() {
+        Ok(info) => {
+            let bytes = info.serialize();
+            // Don't wait for the response, this is on the callback thread, which must not block.
+            rpc.call(ServerMessage::PromoteThreadToRealTime(bytes));
+        }
+        Err(_) => {
+            warn!("Could not remotely promote thread to RT.");
+        }
+    }
+}
+
+#[cfg(not(target_os = "linux"))]
+fn promote_thread(_rpc: &rpc::ClientProxy<ServerMessage, ClientMessage>)
+{
+    match promote_current_thread_to_real_time(0, 48000) {
+        Ok(_) => {
+            info!("Audio thread promoted to real-time.");
+        }
+        Err(_) => {
+            warn!("Could not promote thread to real-time.");
+        }
+    }
+}
+
 fn register_thread(callback: Option<extern "C" fn(*const ::std::os::raw::c_char)>) {
     if let Some(func) = callback {
         let thr = thread::current();
         let name = CString::new(thr.name().unwrap()).unwrap();
         func(name.as_ptr());
     }
 }
 
-fn create_thread_pool(init_params: CpuPoolInitParams) -> CpuPool {
-    futures_cpupool::Builder::new()
-        .name_prefix("AudioIPC")
-        .after_start(move || register_thread(init_params.thread_create_callback))
-        .pool_size(init_params.pool_size)
-        .stack_size(init_params.stack_size)
-        .create()
-}
-
-#[cfg(target_os = "linux")]
-fn get_thread_pool(init_params: CpuPoolInitParams) -> CpuPool {
-    let mut guard = G_THREAD_POOL.lock().unwrap();
-    if guard.is_some() {
-        // Sandbox is on, and the thread pool was created earlier, before the lockdown.
-        guard.take().unwrap()
-    } else {
-        // Sandbox is off, let's create the pool now, promoting the threads will work.
-        create_thread_pool(init_params)
-    }
-}
-
-#[cfg(not(target_os = "linux"))]
-fn get_thread_pool(init_params: CpuPoolInitParams) -> CpuPool {
-    create_thread_pool(init_params)
+fn promote_and_register_thread(rpc: &rpc::ClientProxy<ServerMessage, ClientMessage>,
+    callback: Option<extern "C" fn(*const ::std::os::raw::c_char)>) {
+    promote_thread(rpc);
+    register_thread(callback);
 }
 
 #[derive(Default)]
 struct DeviceCollectionCallback {
     cb: ffi::cubeb_device_collection_changed_callback,
     user_ptr: usize,
 }
 
@@ -157,24 +167,27 @@ impl rpc::Server for DeviceCollectionSer
                     (dcb.cb, dcb.user_ptr)
                 };
                 let (output_cb, output_user_ptr) = {
                     let dcb = self.output_device_callback.lock().unwrap();
                     (dcb.cb, dcb.user_ptr)
                 };
 
                 self.cpu_pool.spawn_fn(move || {
-                    if devtype.contains(cubeb_backend::DeviceType::INPUT) {
-                        unsafe { input_cb.unwrap()(ptr::null_mut(), input_user_ptr as *mut c_void) }
-                    }
-                    if devtype.contains(cubeb_backend::DeviceType::OUTPUT) {
-                        unsafe {
-                            output_cb.unwrap()(ptr::null_mut(), output_user_ptr as *mut c_void)
+                    run_in_callback(|| {
+
+                        if devtype.contains(cubeb_backend::DeviceType::INPUT) {
+                            unsafe { input_cb.unwrap()(ptr::null_mut(), input_user_ptr as *mut c_void) }
                         }
-                    }
+                        if devtype.contains(cubeb_backend::DeviceType::OUTPUT) {
+                            unsafe {
+                                output_cb.unwrap()(ptr::null_mut(), output_user_ptr as *mut c_void)
+                            }
+                        }
+                    });
 
                     Ok(DeviceCollectionResp::DeviceChange)
                 })
             }
         }
     }
 }
 
@@ -205,26 +218,32 @@ impl ContextOps for ClientContext {
 
             open_server_stream()
                 .and_then(|stream| stream.into_tokio_ipc(&handle))
                 .and_then(|stream| bind_and_send_client(stream, &tx_rpc))
         })
         .map_err(|_| Error::default())?;
 
         let rpc = rx_rpc.recv().map_err(|_| Error::default())?;
+        let rpc2 = rpc.clone();
 
         // Don't let errors bubble from here.  Later calls against this context
         // will return errors the caller expects to handle.
         let _ = send_recv!(rpc, ClientConnect(std::process::id()) => ClientConnected);
 
         let backend_id = send_recv!(rpc, ContextGetBackendId => ContextBackendId())
             .unwrap_or_else(|_| "(remote error)".to_string());
         let backend_id = CString::new(backend_id).expect("backend_id query failed");
 
-        let cpu_pool = get_thread_pool(params);
+        let cpu_pool = futures_cpupool::Builder::new()
+            .name_prefix("AudioIPC")
+            .after_start(move || promote_and_register_thread(&rpc2, params.thread_create_callback))
+            .pool_size(params.pool_size)
+            .stack_size(params.stack_size)
+            .create();
 
         let ctx = Box::new(ClientContext {
             _ops: &CLIENT_OPS as *const _,
             rpc,
             core,
             cpu_pool,
             backend_id,
             device_collection_rpc: false,
--- a/media/audioipc/client/src/lib.rs
+++ b/media/audioipc/client/src/lib.rs
@@ -3,48 +3,32 @@
 // This program is made available under an ISC-style license.  See the
 // accompanying file LICENSE for details.
 #![warn(unused_extern_crates)]
 
 #[macro_use]
 extern crate cubeb_backend;
 #[macro_use]
 extern crate log;
-#[macro_use]
-extern crate lazy_static;
 
 #[macro_use]
 mod send_recv;
 mod context;
 mod stream;
 
 use crate::context::ClientContext;
 use crate::stream::ClientStream;
-use audio_thread_priority::RtPriorityHandle;
 use audioipc::{PlatformHandle, PlatformHandleType};
 use cubeb_backend::{capi, ffi};
-use futures_cpupool::CpuPool;
-#[cfg(target_os = "linux")]
-use std::ffi::CString;
 use std::os::raw::{c_char, c_int};
-use std::sync::Mutex;
-#[cfg(target_os = "linux")]
-use std::sync::{Arc, Condvar};
-#[cfg(target_os = "linux")]
-use std::thread;
 
 type InitParamsTls = std::cell::RefCell<Option<CpuPoolInitParams>>;
 
 thread_local!(static IN_CALLBACK: std::cell::RefCell<bool> = std::cell::RefCell::new(false));
 thread_local!(static CPUPOOL_INIT_PARAMS: InitParamsTls = std::cell::RefCell::new(None));
-thread_local!(static G_PRIORITY_HANDLES: std::cell::RefCell<Vec<RtPriorityHandle>> = std::cell::RefCell::new(vec![]));
-
-lazy_static! {
-    static ref G_THREAD_POOL: Mutex<Option<CpuPool>> = Mutex::new(None);
-}
 
 // This must match the definition of AudioIpcInitParams in
 // dom/media/CubebUtils.cpp in Gecko.
 #[repr(C)]
 #[derive(Clone, Copy, Debug)]
 pub struct AudioIpcInitParams {
     // Fields only need to be public for ipctest.
     pub server_connection: PlatformHandleType,
@@ -72,16 +56,29 @@ impl CpuPoolInitParams {
 
 fn set_in_callback(in_callback: bool) {
     IN_CALLBACK.with(|b| {
         assert_eq!(*b.borrow(), !in_callback);
         *b.borrow_mut() = in_callback;
     });
 }
 
+fn run_in_callback<F, R>(f: F) -> R
+where
+    F: FnOnce() -> R
+{
+    set_in_callback(true);
+
+    let r = f();
+
+    set_in_callback(false);
+
+    r
+}
+
 fn assert_not_in_callback() {
     IN_CALLBACK.with(|b| {
         assert_eq!(*b.borrow(), false);
     });
 }
 
 fn set_cpupool_init_params<P>(params: P)
 where
@@ -89,63 +86,16 @@ where
 {
     CPUPOOL_INIT_PARAMS.with(|p| {
         *p.borrow_mut() = params.into();
     });
 }
 
 static mut G_SERVER_FD: Option<PlatformHandle> = None;
 
-#[cfg(target_os = "linux")]
-#[no_mangle]
-pub unsafe extern "C" fn audioipc_init_threads(init_params: *const AudioIpcInitParams) {
-    let thread_create_callback = (*init_params).thread_create_callback;
-
-    // It is critical that this function waits until the various threads are created, promoted to
-    // real-time, and _then_ return, because the sandbox lockdown happens right after returning
-    // from here.
-    let pair = Arc::new((Mutex::new((*init_params).pool_size), Condvar::new()));
-    let pair2 = pair.clone();
-
-    let register_thread = move || {
-        if let Some(func) = thread_create_callback {
-            let thr = thread::current();
-            let name = CString::new(thr.name().unwrap()).unwrap();
-            func(name.as_ptr());
-            let &(ref lock, ref cvar) = &*pair2;
-            let mut count = lock.lock().unwrap();
-            *count -= 1;
-            cvar.notify_one();
-        }
-    };
-
-    let mut pool = G_THREAD_POOL.lock().unwrap();
-
-    *pool = Some(
-        futures_cpupool::Builder::new()
-            .name_prefix("AudioIPC")
-            .after_start(register_thread)
-            .pool_size((*init_params).pool_size)
-            .stack_size((*init_params).stack_size)
-            .create(),
-    );
-
-    let &(ref lock, ref cvar) = &*pair;
-    let mut count = lock.lock().unwrap();
-    while *count != 0 {
-        count = cvar.wait(count).unwrap();
-    }
-}
-
-#[cfg(not(target_os = "linux"))]
-#[no_mangle]
-pub unsafe extern "C" fn audioipc_init_threads(_: *const AudioIpcInitParams) {
-    unimplemented!();
-}
-
 #[no_mangle]
 /// Entry point from C code.
 pub unsafe extern "C" fn audioipc_client_init(
     c: *mut *mut ffi::cubeb,
     context_name: *const c_char,
     init_params: *const AudioIpcInitParams,
 ) -> c_int {
     if init_params.is_null() {
--- a/media/audioipc/client/src/stream.rs
+++ b/media/audioipc/client/src/stream.rs
@@ -1,15 +1,15 @@
 // Copyright © 2017 Mozilla Foundation
 //
 // This program is made available under an ISC-style license.  See the
 // accompanying file LICENSE for details
 
 use crate::ClientContext;
-use crate::{assert_not_in_callback, set_in_callback};
+use crate::{assert_not_in_callback, run_in_callback};
 use audioipc::codec::LengthDelimitedCodec;
 use audioipc::frame::{framed, Framed};
 use audioipc::messages::{self, CallbackReq, CallbackResp, ClientMessage, ServerMessage};
 use audioipc::rpc;
 use audioipc::shm::{SharedMemMutSlice, SharedMemSlice};
 use cubeb_backend::{ffi, DeviceRef, Error, Result, Stream, StreamOps};
 use futures::Future;
 use futures_cpupool::{CpuFuture, CpuPool};
@@ -103,59 +103,59 @@ impl rpc::Server for CallbackServer {
                     let output_ptr: *mut u8 = match output_shm {
                         Some(ref mut shm) => shm
                             .get_mut_slice(nframes as usize * output_frame_size)
                             .unwrap()
                             .as_mut_ptr(),
                         None => ptr::null_mut(),
                     };
 
-                    set_in_callback(true);
-                    let nframes = unsafe {
-                        cb(
-                            ptr::null_mut(),
-                            user_ptr as *mut c_void,
-                            input_ptr as *const _,
-                            output_ptr as *mut _,
-                            nframes as _,
-                        )
-                    };
-                    set_in_callback(false);
+                    run_in_callback(|| {
+                        let nframes = unsafe {
+                            cb(
+                                ptr::null_mut(),
+                                user_ptr as *mut c_void,
+                                input_ptr as *const _,
+                                output_ptr as *mut _,
+                                nframes as _,
+                            )
+                        };
 
-                    Ok(CallbackResp::Data(nframes as isize))
+                        Ok(CallbackResp::Data(nframes as isize))
+                    })
                 })
             }
             CallbackReq::State(state) => {
                 trace!("stream_thread: State Callback: {:?}", state);
                 let user_ptr = self.user_ptr;
                 let cb = self.state_cb.unwrap();
                 self.cpu_pool.spawn_fn(move || {
-                    set_in_callback(true);
-                    unsafe {
-                        cb(ptr::null_mut(), user_ptr as *mut _, state);
-                    }
-                    set_in_callback(false);
+                    run_in_callback(|| {
+                        unsafe {
+                            cb(ptr::null_mut(), user_ptr as *mut _, state);
+                        }
+                    });
 
                     Ok(CallbackResp::State)
                 })
             }
             CallbackReq::DeviceChange => {
                 let cb = self.device_change_cb.clone();
                 let user_ptr = self.user_ptr;
                 self.cpu_pool.spawn_fn(move || {
-                    set_in_callback(true);
-                    let cb = cb.lock().unwrap();
-                    if let Some(cb) = *cb {
-                        unsafe {
-                            cb(user_ptr as *mut _);
+                    run_in_callback(|| {
+                        let cb = cb.lock().unwrap();
+                        if let Some(cb) = *cb {
+                            unsafe {
+                                cb(user_ptr as *mut _);
+                            }
+                        } else {
+                            warn!("DeviceChange received with null callback");
                         }
-                    } else {
-                        warn!("DeviceChange received with null callback");
-                    }
-                    set_in_callback(false);
+                    });
 
                     Ok(CallbackResp::DeviceChange)
                 })
             }
         }
     }
 }
 
@@ -279,22 +279,16 @@ impl<'ctx> StreamOps for ClientStream<'c
     }
 
     fn set_volume(&mut self, volume: f32) -> Result<()> {
         assert_not_in_callback();
         let rpc = self.context.rpc();
         send_recv!(rpc, StreamSetVolume(self.token, volume) => StreamVolumeSet)
     }
 
-    fn set_panning(&mut self, panning: f32) -> Result<()> {
-        assert_not_in_callback();
-        let rpc = self.context.rpc();
-        send_recv!(rpc, StreamSetPanning(self.token, panning) => StreamPanningSet)
-    }
-
     fn current_device(&mut self) -> Result<&DeviceRef> {
         assert_not_in_callback();
         let rpc = self.context.rpc();
         match send_recv!(rpc, StreamGetCurrentDevice(self.token) => StreamCurrentDevice()) {
             Ok(d) => Ok(unsafe { DeviceRef::from_ptr(Box::into_raw(Box::new(d.into()))) }),
             Err(e) => Err(e),
         }
     }
deleted file mode 100644
--- a/media/audioipc/disable-rt.patch
+++ /dev/null
@@ -1,139 +0,0 @@
-diff --git a/media/audioipc/client/src/context.rs b/media/audioipc/client/src/context.rs
---- a/media/audioipc/client/src/context.rs
-+++ b/media/audioipc/client/src/context.rs
-@@ -3,17 +3,16 @@
- // This program is made available under an ISC-style license.  See the
- // accompanying file LICENSE for details
- 
- use crate::assert_not_in_callback;
- use crate::stream;
- #[cfg(target_os = "linux")]
- use crate::G_THREAD_POOL;
- use crate::{ClientStream, CpuPoolInitParams, CPUPOOL_INIT_PARAMS, G_SERVER_FD};
--use audio_thread_priority::promote_current_thread_to_real_time;
- use audioipc::codec::LengthDelimitedCodec;
- use audioipc::frame::{framed, Framed};
- use audioipc::platformhandle_passing::{framed_with_platformhandles, FramedWithPlatformHandles};
- use audioipc::{core, rpc};
- use audioipc::{
-     messages, messages::DeviceCollectionReq, messages::DeviceCollectionResp, ClientMessage,
-     ServerMessage,
- };
-@@ -86,24 +85,16 @@ fn open_server_stream() -> io::Result<au
-         Err(io::Error::new(
-             io::ErrorKind::Other,
-             "Failed to get server connection.",
-         ))
-     }
- }
- 
- fn register_thread(callback: Option<extern "C" fn(*const ::std::os::raw::c_char)>) {
--    match promote_current_thread_to_real_time(0, 48000) {
--        Ok(_) => {
--            info!("Audio thread promoted to real-time.");
--        }
--        Err(_) => {
--            warn!("Could not promote thread to real-time.");
--        }
--    }
-     if let Some(func) = callback {
-         let thr = thread::current();
-         let name = CString::new(thr.name().unwrap()).unwrap();
-         func(name.as_ptr());
-     }
- }
- 
- fn create_thread_pool(init_params: CpuPoolInitParams) -> CpuPool {
-diff --git a/media/audioipc/client/src/lib.rs b/media/audioipc/client/src/lib.rs
---- a/media/audioipc/client/src/lib.rs
-+++ b/media/audioipc/client/src/lib.rs
-@@ -13,18 +13,16 @@ extern crate lazy_static;
- 
- #[macro_use]
- mod send_recv;
- mod context;
- mod stream;
- 
- use crate::context::ClientContext;
- use crate::stream::ClientStream;
--#[cfg(target_os = "linux")]
--use audio_thread_priority::promote_current_thread_to_real_time;
- use audio_thread_priority::RtPriorityHandle;
- use audioipc::{PlatformHandle, PlatformHandleType};
- use cubeb_backend::{capi, ffi};
- use futures_cpupool::CpuPool;
- #[cfg(target_os = "linux")]
- use std::ffi::CString;
- use std::os::raw::{c_char, c_int};
- use std::sync::Mutex;
-@@ -104,26 +102,16 @@ pub unsafe extern "C" fn audioipc_init_t
-     // It is critical that this function waits until the various threads are created, promoted to
-     // real-time, and _then_ return, because the sandbox lockdown happens right after returning
-     // from here.
-     let pair = Arc::new((Mutex::new((*init_params).pool_size), Condvar::new()));
-     let pair2 = pair.clone();
- 
-     let register_thread = move || {
-         if let Some(func) = thread_create_callback {
--            match promote_current_thread_to_real_time(0, 48000) {
--                Ok(handle) => {
--                    G_PRIORITY_HANDLES.with(|handles| {
--                        (handles.borrow_mut()).push(handle);
--                    });
--                }
--                Err(_) => {
--                    warn!("Could not promote audio threads to real-time during initialization.");
--                }
--            }
-             let thr = thread::current();
-             let name = CString::new(thr.name().unwrap()).unwrap();
-             func(name.as_ptr());
-             let &(ref lock, ref cvar) = &*pair2;
-             let mut count = lock.lock().unwrap();
-             *count -= 1;
-             cvar.notify_one();
-         }
-diff --git a/media/audioipc/server/src/lib.rs b/media/audioipc/server/src/lib.rs
---- a/media/audioipc/server/src/lib.rs
-+++ b/media/audioipc/server/src/lib.rs
-@@ -6,17 +6,16 @@
- 
- #[macro_use]
- extern crate error_chain;
- #[macro_use]
- extern crate log;
- #[macro_use]
- extern crate lazy_static;
- 
--use audio_thread_priority::promote_current_thread_to_real_time;
- use audioipc::core;
- use audioipc::platformhandle_passing::framed_with_platformhandles;
- use audioipc::rpc;
- use audioipc::{MessageStream, PlatformHandle, PlatformHandleType};
- use futures::sync::oneshot;
- use futures::Future;
- use std::error::Error;
- use std::ffi::{CStr, CString};
-@@ -59,22 +58,16 @@ struct ServerWrapper {
-     core_thread: core::CoreThread,
-     callback_thread: core::CoreThread,
- }
- 
- fn run() -> Result<ServerWrapper> {
-     trace!("Starting up cubeb audio server event loop thread...");
- 
-     let callback_thread = core::spawn_thread("AudioIPC Callback RPC", || {
--        match promote_current_thread_to_real_time(0, 48000) {
--            Ok(_) => {}
--            Err(_) => {
--                debug!("Failed to promote audio callback thread to real-time.");
--            }
--        }
-         trace!("Starting up cubeb audio callback event loop thread...");
-         Ok(())
-     })
-     .or_else(|e| {
-         debug!(
-             "Failed to start cubeb audio callback event loop thread: {:?}",
-             e.description()
-         );
--- a/media/audioipc/server/Cargo.toml
+++ b/media/audioipc/server/Cargo.toml
@@ -4,20 +4,20 @@ version = "0.2.3"
 authors = [
         "Matthew Gregan <kinetik@flim.org>",
         "Dan Glastonbury <dan.glastonbury@gmail.com>"
         ]
 description = "Remote cubeb server"
 edition = "2018"
 
 [dependencies]
-audio_thread_priority = "0.19.1"
+audio_thread_priority = "0.20.2"
 audioipc = { path = "../audioipc" }
-cubeb-core = "0.5.5"
+cubeb-core = "0.6.0"
 futures = "0.1.18"
 lazy_static = "1.2.0"
 log = "0.4"
-slab = "0.3.0"
+slab = "0.4"
 tokio = "0.1"
 
 [dependencies.error-chain]
 version = "0.11.0"
 default-features = false
--- a/media/audioipc/server/src/lib.rs
+++ b/media/audioipc/server/src/lib.rs
@@ -6,16 +6,17 @@
 
 #[macro_use]
 extern crate error_chain;
 #[macro_use]
 extern crate log;
 #[macro_use]
 extern crate lazy_static;
 
+use audio_thread_priority::promote_current_thread_to_real_time;
 use audioipc::core;
 use audioipc::platformhandle_passing::framed_with_platformhandles;
 use audioipc::rpc;
 use audioipc::{MessageStream, PlatformHandle, PlatformHandleType};
 use futures::sync::oneshot;
 use futures::Future;
 use std::error::Error;
 use std::ffi::{CStr, CString};
@@ -58,16 +59,22 @@ struct ServerWrapper {
     core_thread: core::CoreThread,
     callback_thread: core::CoreThread,
 }
 
 fn run() -> Result<ServerWrapper> {
     trace!("Starting up cubeb audio server event loop thread...");
 
     let callback_thread = core::spawn_thread("AudioIPC Callback RPC", || {
+        match promote_current_thread_to_real_time(0, 48000) {
+            Ok(_) => {}
+            Err(_) => {
+                debug!("Failed to promote audio callback thread to real-time.");
+            }
+        }
         trace!("Starting up cubeb audio callback event loop thread...");
         Ok(())
     })
     .or_else(|e| {
         debug!(
             "Failed to start cubeb audio callback event loop thread: {:?}",
             e.description()
         );
--- a/media/audioipc/server/src/server.rs
+++ b/media/audioipc/server/src/server.rs
@@ -25,16 +25,18 @@ use std::cell::RefCell;
 use std::convert::From;
 use std::ffi::CStr;
 use std::mem::{size_of, ManuallyDrop};
 use std::os::raw::{c_long, c_void};
 use std::rc::Rc;
 use std::{panic, slice};
 use tokio::reactor;
 use tokio::runtime::current_thread;
+#[cfg(target_os = "linux")]
+use audio_thread_priority::{RtPriorityThreadInfo, promote_thread_to_real_time};
 
 use crate::errors::*;
 
 fn error(error: cubeb::Error) -> ClientMessage {
     ClientMessage::Error(error.raw_code())
 }
 
 struct CubebDeviceCollectionManager {
@@ -162,16 +164,17 @@ where
         if state.is_none() {
             let params = super::G_CUBEB_CONTEXT_PARAMS.lock().unwrap();
             let context_name = Some(params.context_name.as_c_str());
             let backend_name = if let Some(ref name) = params.backend_name {
                 Some(name.as_c_str())
             } else {
                 None
             };
+            audioipc::server_platform_init();
             let context = cubeb::Context::init(context_name, backend_name);
             let manager = CubebDeviceCollectionManager::new();
             *state = Some(CubebContextState { context, manager });
         }
         let state = state.as_mut().unwrap();
         f(&state.context, &mut state.manager)
     })
 }
@@ -278,17 +281,17 @@ im