Merge inbound to mozilla-central. a=merge
authorMargareta Eliza Balazs <ebalazs@mozilla.com>
Tue, 25 Sep 2018 12:42:09 +0300
changeset 438085 c5a9878baf35a354cb913b4f06542e233685ea9a
parent 438084 5d3604ac613ee73530c9303b6c399e07c6fb3357 (current diff)
parent 438007 b1fc8e9cb6010273e4d73b114d96f1f5271f3ceb (diff)
child 438086 b87feca48db458defeead27d55b4dbd670e7930b
child 438153 596c71ccedecfa91d4fa0d1325bdd4f443b77864
child 438203 d03b538b6b417ba892d0a92fd693945b741246e1
push id108220
push userebalazs@mozilla.com
push dateTue, 25 Sep 2018 09:51:30 +0000
treeherdermozilla-inbound@b87feca48db4 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone64.0a1
first release with
nightly linux32
c5a9878baf35 / 64.0a1 / 20180925100100 / files
nightly linux64
c5a9878baf35 / 64.0a1 / 20180925100100 / files
nightly mac
c5a9878baf35 / 64.0a1 / 20180925100100 / files
nightly win32
c5a9878baf35 / 64.0a1 / 20180925100100 / files
nightly win64
c5a9878baf35 / 64.0a1 / 20180925100100 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge inbound to mozilla-central. a=merge
devtools/client/inspector/markup/test/browser_markup_grid_display_badge.js
devtools/client/responsive.html/actions/reload-conditions.js
devtools/client/responsive.html/reducers/reload-conditions.js
docshell/base/nsDocShell.cpp
dom/clients/manager/ClientNavigateOpChild.cpp
layout/generic/crashtests/crashtests.list
servo/components/style/stylist.rs
testing/web-platform/meta/css/vendor-imports/mozilla/mozilla-central-reftests/values3/calc-text-indent-intrinsic-1.html.ini
third_party/rust/gdi32-sys/.cargo-checksum.json
third_party/rust/gdi32-sys/Cargo.toml
third_party/rust/gdi32-sys/README.md
third_party/rust/gdi32-sys/build.rs
third_party/rust/gdi32-sys/src/lib.rs
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -20,17 +20,17 @@ dependencies = [
  "memchr 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "ansi_term"
 version = "0.11.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
- "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "app_units"
 version = "0.7.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -75,17 +75,17 @@ dependencies = [
 
 [[package]]
 name = "atty"
 version = "0.2.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "libc 0.2.43 (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.4 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "audioipc"
 version = "0.2.4"
 dependencies = [
  "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)",
@@ -629,26 +629,24 @@ dependencies = [
 name = "dump_syms_rust_demangle"
 version = "0.1.0"
 dependencies = [
  "rustc-demangle 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "dwrote"
-version = "0.4.2"
+version = "0.5.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
- "gdi32-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "lazy_static 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde 1.0.66 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde_derive 1.0.66 (git+https://github.com/servo/serde?branch=deserialize_from_enums8)",
- "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "either"
 version = "1.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
@@ -768,17 +766,17 @@ dependencies = [
 ]
 
 [[package]]
 name = "fs2"
 version = "0.4.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)",
- "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.3.6 (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)",
@@ -813,25 +811,16 @@ dependencies = [
 ]
 
 [[package]]
 name = "gcc"
 version = "0.3.54"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
-name = "gdi32-sys"
-version = "0.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
- "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
 name = "geckodriver"
 version = "0.22.0"
 dependencies = [
  "base64 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "clap 2.31.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "hyper 0.12.7 (registry+https://github.com/rust-lang/crates.io-index)",
  "lazy_static 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1157,17 +1146,17 @@ version = "0.2.43"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
 name = "libloading"
 version = "0.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "cc 1.0.23 (registry+https://github.com/rust-lang/crates.io-index)",
- "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "libudev"
 version = "0.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1443,27 +1432,27 @@ version = "0.0.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
 name = "msdos_time"
 version = "0.1.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)",
- "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "net2"
 version = "0.2.32"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)",
- "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "netwerk_helper"
 version = "0.0.1"
 dependencies = [
  "nserror 0.1.0",
  "nsstring 0.1.0",
@@ -1594,17 +1583,17 @@ dependencies = [
 [[package]]
 name = "parking_lot_core"
 version = "0.2.14"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)",
  "rand 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "smallvec 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)",
- "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "peeking_take_while"
 version = "0.1.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
@@ -1759,17 +1748,17 @@ dependencies = [
 
 [[package]]
 name = "rand"
 version = "0.4.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)",
- "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "rayon"
 version = "1.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "either 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1914,17 +1903,17 @@ name = "safemem"
 version = "0.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
 name = "same-file"
 version = "1.0.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
- "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "scoped-tls"
 version = "0.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
@@ -2306,17 +2295,17 @@ source = "registry+https://github.com/ru
 
 [[package]]
 name = "time"
 version = "0.1.40"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)",
  "redox_syscall 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)",
- "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "tokio"
 version = "0.1.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "futures 0.1.23 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -2487,17 +2476,17 @@ dependencies = [
  "core-foundation 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "core-foundation-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "devd-rs 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "libc 0.2.43 (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.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "rand 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)",
  "runloop 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "ucd-util"
 version = "0.1.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
@@ -2583,17 +2572,17 @@ version = "1.0.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
 name = "walkdir"
 version = "2.1.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "same-file 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
- "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "want"
 version = "0.0.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "futures 0.1.23 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -2638,17 +2627,17 @@ dependencies = [
  "app_units 0.7.0 (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)",
  "byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "core-foundation 0.6.0 (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)",
- "dwrote 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dwrote 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "euclid 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "freetype 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "fxhash 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "gleam 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "lazy_static 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "plane-split 0.13.2 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -2666,33 +2655,33 @@ name = "webrender_api"
 version = "0.57.2"
 dependencies = [
  "app_units 0.7.0 (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)",
  "byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "core-foundation 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "core-graphics 0.17.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "dwrote 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dwrote 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "euclid 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde 1.0.66 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde_bytes 0.10.4 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde_derive 1.0.66 (git+https://github.com/servo/serde?branch=deserialize_from_enums8)",
  "time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "webrender_bindings"
 version = "0.1.0"
 dependencies = [
  "app_units 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "bincode 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "core-foundation 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "core-graphics 0.17.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "dwrote 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dwrote 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "euclid 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "foreign-types 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "fxhash 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "gleam 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "nsstring 0.1.0",
  "rayon 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "thread_profiler 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -2710,17 +2699,17 @@ dependencies = [
 
 [[package]]
 name = "winapi"
 version = "0.2.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
 name = "winapi"
-version = "0.3.4"
+version = "0.3.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "winapi-build"
@@ -2737,25 +2726,25 @@ name = "winapi-x86_64-pc-windows-gnu"
 version = "0.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
 name = "wincolor"
 version = "0.1.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
- "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "winreg"
 version = "0.5.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
- "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "ws2_32-sys"
 version = "0.2.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -2877,17 +2866,17 @@ dependencies = [
 "checksum darling 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2a78af487e4eb8f4421a1770687b328af6bb4494ca93435210678c6eea875c11"
 "checksum darling_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b315f49c7b6db3708bca6e6913c194581a44ec619b7a39e131d4dd63733a3698"
 "checksum darling_macro 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "eb69a38fdeaeaf3db712e1df170de67ee9dfc24fb88ca3e9d21e703ec25a4d8e"
 "checksum devd-rs 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e7c9ac481c38baf400d3b732e4a06850dfaa491d1b6379a249d9d40d14c2434c"
 "checksum diff 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "3c2b69f912779fbb121ceb775d74d51e915af17aaebc38d28a592843a2dd0a3a"
 "checksum docopt 0.8.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d8acd393692c503b168471874953a2531df0e9ab77d0b6bbc582395743300a4a"
 "checksum dtoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "09c3753c3db574d215cba4ea76018483895d7bff25a31b49ba45db21c48e50ab"
 "checksum dtoa-short 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "068d4026697c1a18f0b0bb8cfcad1b0c151b90d8edb9bf4c235ad68128920d1d"
-"checksum dwrote 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b26e30aaa6bf31ec830db15fec14ed04f0f2ecfcc486ecfce88c55d3389b237f"
+"checksum dwrote 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "30a998e9ff70cd208ccdc4f864e998688bf61d7b897dccec8e17a884d17358bf"
 "checksum either 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "18785c1ba806c258137c937e44ada9ee7e69a37e3c72077542cd2f069d78562a"
 "checksum ena 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cabe5a5078ac8c506d3e4430763b1ba9b609b1286913e7d08e581d1c2de9b7e5"
 "checksum encoding_c 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "769ecb8b33323998e482b218c0d13cd64c267609023b4b7ec3ee740714c318ee"
 "checksum encoding_rs 0.8.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2a91912d6f37c6a8fef8a2316a862542d036f13c923ad518b5aca7bcaac7544c"
 "checksum env_logger 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0561146661ae44c579e993456bc76d11ce1e0c7d745e57b2fa7146b6e49fa2ad"
 "checksum error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ff511d5dc435d703f4971bc399647c9bc38e20cb41452e3b9feb4765419ed3f3"
 "checksum euclid 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)" = "70a2ebdf55fb9d6329046e026329a55ef8fbaae5ea833f56e170beb3125a8a5f"
 "checksum failure 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7efb22686e4a466b1ec1a15c2898f91fa9cb340452496dca654032de20ff95b9"
@@ -2899,17 +2888,16 @@ dependencies = [
 "checksum freetype 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b659e75b7a7338fe75afd7f909fc2b71937845cffb6ebe54ba2e50f13d8e903d"
 "checksum fs2 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "9564fc758e15025b46aa6643b1b77d047d1a56a1aea6e01002ac0c7026876213"
 "checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82"
 "checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7"
 "checksum futures 0.1.23 (registry+https://github.com/rust-lang/crates.io-index)" = "884dbe32a6ae4cd7da5c6db9b78114449df9953b8d490c9d7e1b51720b922c62"
 "checksum futures-cpupool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "ab90cde24b3319636588d0c35fe03b1333857621051837ed769faefb4c2162e4"
 "checksum fxhash 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c"
 "checksum gcc 0.3.54 (registry+https://github.com/rust-lang/crates.io-index)" = "5e33ec290da0d127825013597dbdfc28bee4964690c7ce1166cbc2a7bd08b1bb"
-"checksum gdi32-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0912515a8ff24ba900422ecda800b52f4016a56251922d397c576bf92c690518"
 "checksum gl_generator 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a795170cbd85b5a7baa58d6d7525cae6a03e486859860c220f7ebbbdd379d0a"
 "checksum gleam 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0d41e7ac812597988fdae31c9baec3c6d35cadb8ad9ab88a9bf9c0f119ed66c2"
 "checksum glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "8be18de09a56b60ed0edf84bc9df007e30040691af7acd1c41874faac5895bfb"
 "checksum h2 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "a27e7ed946e8335bdf9a191bc1b9b14a03ba822d013d2f58437f4fabcbd7fc2c"
 "checksum http 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "dca621d0fa606a5ff2850b6e337b57ad6137ee4d67e940449643ff45af6874c6"
 "checksum httparse 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "af2f2dd97457e8fb1ae7c5a420db346af389926e36f43768b96f101546b04a07"
 "checksum humantime 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0484fda3e7007f2a4a0d9c3a703ca38c71c54c55602ce4660c419fd32e188c9e"
 "checksum hyper 0.12.7 (registry+https://github.com/rust-lang/crates.io-index)" = "c087746de95e20e4dabe86606c3a019964a8fde2d5f386152939063c116c5971"
@@ -3066,17 +3054,17 @@ dependencies = [
 "checksum vcpkg 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9e0a7d8bed3178a8fb112199d466eeca9ed09a14ba8ad67718179b4fd5487d0b"
 "checksum vec_map 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "887b5b631c2ad01628bbbaa7dd4c869f80d3186688f8d0b6f58774fbe324988c"
 "checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
 "checksum walkdir 2.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "63636bd0eb3d00ccb8b9036381b526efac53caf112b7783b730ab3f8e44da369"
 "checksum want 0.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "797464475f30ddb8830cc529aaaae648d581f99e2036a928877dfde027ddf6b3"
 "checksum webidl 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "dc14e4b71f94b5bb4c6d696e3b3be4d2e9ee6750a60870ecae09ff7138a131a7"
 "checksum which 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "4be6cfa54dab45266e98b5d7be2f8ce959ddd49abd141a05d52dce4b07f803bb"
 "checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a"
-"checksum winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "04e3bd221fcbe8a271359c04f21a76db7d0c6028862d1bb5512d85e1e2eb5bb3"
+"checksum winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "92c1eb33641e276cfa214a0522acad57be5c56b10cb348b3c5117db75f3ac4b0"
 "checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc"
 "checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
 "checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
 "checksum wincolor 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "eeb06499a3a4d44302791052df005d5232b927ed1a9658146d842165c4de7767"
 "checksum winreg 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a27a759395c1195c4cc5cda607ef6f8f6498f64e78f7900f5de0a127a424704a"
 "checksum ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e"
 "checksum xml-rs 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3c1cb601d29fe2c2ac60a2b2e5e293994d87a1f6fa9687a31a15270f909be9c2"
 "checksum yaml-rust 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "57ab38ee1a4a266ed033496cf9af1828d8d6e6c1cfa5f643a2809effcae4d628"
--- a/browser/actors/ClickHandlerChild.jsm
+++ b/browser/actors/ClickHandlerChild.jsm
@@ -75,17 +75,18 @@ class ClickHandlerChild extends ActorChi
       // Only when the owner doc has |mixedContentChannel| and the same origin
       // should we allow mixed content.
       json.allowMixedContent = false;
       let docshell = ownerDoc.defaultView.docShell;
       if (this.mm.docShell.mixedContentChannel) {
         const sm = Services.scriptSecurityManager;
         try {
           let targetURI = Services.io.newURI(href);
-          sm.checkSameOriginURI(docshell.mixedContentChannel.URI, targetURI, false);
+          let isPrivateWin = ownerDoc.nodePrincipal.originAttributes.privateBrowsingId > 0;
+          sm.checkSameOriginURI(docshell.mixedContentChannel.URI, targetURI, false, isPrivateWin);
           json.allowMixedContent = true;
         } catch (e) {}
       }
       json.originPrincipal = ownerDoc.nodePrincipal;
       json.triggeringPrincipal = ownerDoc.nodePrincipal;
 
       this.mm.sendAsyncMessage("Content:Click", json);
       return;
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -6136,17 +6136,18 @@ function handleLinkClick(event, href, li
   // a same origin check with the target URI, we can preserve the users
   // decision of disabling MCB on a page for it's child tabs.
   var persistAllowMixedContentInChildTab = false;
 
   if (where == "tab" && gBrowser.docShell.mixedContentChannel) {
     const sm = Services.scriptSecurityManager;
     try {
       var targetURI = makeURI(href);
-      sm.checkSameOriginURI(referrerURI, targetURI, false);
+      let isPrivateWin = doc.nodePrincipal.originAttributes.privateBrowsingId > 0;
+      sm.checkSameOriginURI(referrerURI, targetURI, false, isPrivateWin);
       persistAllowMixedContentInChildTab = true;
     } catch (e) { }
   }
 
   // first get document wide referrer policy, then
   // get referrer attribute from clicked link and parse it and
   // allow per element referrer to overrule the document wide referrer if enabled
   let referrerPolicy = doc.referrerPolicy;
--- a/browser/base/content/nsContextMenu.js
+++ b/browser/base/content/nsContextMenu.js
@@ -801,17 +801,18 @@ nsContextMenu.prototype = {
     // a same origin check with the target URI, we can preserve the users
     // decision of disabling MCB on a page for it's child tabs.
     let persistAllowMixedContentInChildTab = false;
 
     if (gContextMenuContentData.parentAllowsMixedContent) {
       const sm = Services.scriptSecurityManager;
       try {
         let targetURI = this.linkURI;
-        sm.checkSameOriginURI(referrerURI, targetURI, false);
+        let isPrivateWin = this.browser.contentPrincipal.originAttributes.privateBrowsingId > 0;
+        sm.checkSameOriginURI(referrerURI, targetURI, false, isPrivateWin);
         persistAllowMixedContentInChildTab = true;
       } catch (e) { }
     }
 
     let params = {
       allowMixedContent: persistAllowMixedContentInChildTab,
       userContextId: parseInt(event.target.getAttribute("data-usercontextid")),
     };
--- a/browser/components/extensions/parent/ext-devtools.js
+++ b/browser/components/extensions/parent/ext-devtools.js
@@ -30,32 +30,31 @@ function getDevToolsPrefBranchName(exten
  * @param {DevToolsExtensionPageContextParent} context
  *   A devtools extension proxy context.
  *
  * @returns {Promise<TabTarget>}
  *   The cloned devtools target associated to the context.
  */
 global.getDevToolsTargetForContext = async (context) => {
   if (context.devToolsTarget) {
-    await context.devToolsTarget.makeRemote();
+    await context.devToolsTarget.attach();
     return context.devToolsTarget;
   }
 
   if (!context.devToolsToolbox || !context.devToolsToolbox.target) {
     throw new Error("Unable to get a TabTarget for a context not associated to any toolbox");
   }
 
   if (!context.devToolsToolbox.target.isLocalTab) {
     throw new Error("Unexpected target type: only local tabs are currently supported.");
   }
 
   const tab = context.devToolsToolbox.target.tab;
-  context.devToolsTarget = DevToolsShim.createTargetForTab(tab);
-
-  await context.devToolsTarget.makeRemote();
+  context.devToolsTarget = await DevToolsShim.createTargetForTab(tab);
+  await context.devToolsTarget.attach();
 
   return context.devToolsTarget;
 };
 
 /**
  * Retrieve the devtools target for the devtools extension proxy context
  * (lazily cloned from the target of the toolbox associated to the context
  * the first time that it is accessed).
--- a/browser/components/extensions/test/browser/browser_ext_devtools_inspectedWindow.js
+++ b/browser/components/extensions/test/browser/browser_ext_devtools_inspectedWindow.js
@@ -101,17 +101,17 @@ add_task(async function test_devtools_in
       "devtools_page_iframe.js": devtools_page_iframe,
     },
   });
 
   await extension.startup();
 
   let backgroundPageCurrentTabId = await extension.awaitMessage("current-tab-id");
 
-  let target = gDevTools.getTargetForTab(tab);
+  let target = await gDevTools.getTargetForTab(tab);
 
   await gDevTools.showToolbox(target, "webconsole");
   info("developer toolbox opened");
 
   let devtoolsInspectedWindowTabId = await extension.awaitMessage("inspectedWindow-tab-id");
 
   is(devtoolsInspectedWindowTabId, backgroundPageCurrentTabId,
      "Got the expected tabId from devtool.inspectedWindow.tabId");
@@ -169,17 +169,17 @@ add_task(async function test_devtools_in
        </body>
       </html>`,
       "devtools_page.js": devtools_page,
     },
   });
 
   await extension.startup();
 
-  let target = gDevTools.getTargetForTab(tab);
+  let target = await gDevTools.getTargetForTab(tab);
 
   await gDevTools.showToolbox(target, "webconsole");
   info("developer toolbox opened");
 
   const evalTestCases = [
     // Successful evaluation results.
     {
       args: ["window.location.href"],
@@ -331,17 +331,17 @@ add_task(async function test_devtools_in
        </body>
       </html>`,
       "devtools_panel.js": devtools_panel,
     },
   });
 
   await extension.startup();
 
-  const target = gDevTools.getTargetForTab(tab);
+  const target = await gDevTools.getTargetForTab(tab);
   const toolbox = await gDevTools.showToolbox(target, "webconsole");
   info("developer toolbox opened");
 
   info("Wait for devtools_panel_created event");
   await extension.awaitMessage("devtools_panel_created");
 
   info("Switch to the extension test panel");
   await gDevTools.showToolbox(target, getAdditionalPanelId(toolbox, "test-eval"));
--- a/browser/components/extensions/test/browser/browser_ext_devtools_inspectedWindow_eval_bindings.js
+++ b/browser/components/extensions/test/browser/browser_ext_devtools_inspectedWindow_eval_bindings.js
@@ -49,17 +49,17 @@ add_task(async function test_devtools_in
        </body>
       </html>`,
       "devtools_page.js": devtools_page,
     },
   });
 
   await extension.startup();
 
-  const target = gDevTools.getTargetForTab(tab);
+  const target = await gDevTools.getTargetForTab(tab);
   // Open the toolbox on the styleeditor, so that the inspector and the
   // console panel have not been explicitly activated yet.
   const toolbox = await gDevTools.showToolbox(target, "styleeditor");
   info("Developer toolbox opened");
 
   // Test $0 binding with no selected node
   info("Test inspectedWindow.eval $0 binding with no selected node");
 
--- a/browser/components/extensions/test/browser/browser_ext_devtools_inspectedWindow_reload.js
+++ b/browser/components/extensions/test/browser/browser_ext_devtools_inspectedWindow_reload.js
@@ -32,17 +32,17 @@ async function runReloadTestCase({urlPar
        </body>
       </html>`,
       "devtools_page.js": devtoolsPage,
     },
   });
 
   await extension.startup();
 
-  let target = gDevTools.getTargetForTab(tab);
+  let target = await gDevTools.getTargetForTab(tab);
 
   await gDevTools.showToolbox(target, "webconsole");
   info("developer toolbox opened");
 
   // Wait the test extension to be ready.
   await extension.awaitMessage("devtools_inspected_window_reload.ready");
 
   info("devtools page ready");
--- a/browser/components/extensions/test/browser/browser_ext_devtools_network.js
+++ b/browser/components/extensions/test/browser/browser_ext_devtools_network.js
@@ -129,17 +129,17 @@ async function navigateToolboxTarget(ext
  */
 add_task(async function test_devtools_network_on_navigated() {
   let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, "http://mochi.test:8888/");
   let extension = ExtensionTestUtils.loadExtension(extData);
 
   await extension.startup();
   await extension.awaitMessage("ready");
 
-  let target = gDevTools.getTargetForTab(tab);
+  let target = await gDevTools.getTargetForTab(tab);
 
   await gDevTools.showToolbox(target, "webconsole");
   info("Developer toolbox opened.");
 
   extension.sendMessage("navigate");
   await extension.awaitMessage("tabUpdated");
   let eventCount = await extension.awaitMessage("onNavigatedFired");
   is(eventCount, 1, "The expected number of events were fired.");
@@ -167,17 +167,17 @@ add_task(async function test_devtools_ne
  */
 add_task(async function test_devtools_network_get_har() {
   let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, "http://mochi.test:8888/");
   let extension = ExtensionTestUtils.loadExtension(extData);
 
   await extension.startup();
   await extension.awaitMessage("ready");
 
-  let target = gDevTools.getTargetForTab(tab);
+  let target = await gDevTools.getTargetForTab(tab);
 
   // Open the Toolbox
   let toolbox = await gDevTools.showToolbox(target, "webconsole");
   info("Developer toolbox opened.");
 
   // Get HAR, it should be empty since no data collected yet.
   const getHAREmptyPromise = extension.awaitMessage("getHAR-result");
   extension.sendMessage("getHAR");
@@ -218,17 +218,17 @@ add_task(async function test_devtools_ne
  * Test for `chrome.devtools.network.onRequestFinished()` API
  */
 add_task(async function test_devtools_network_on_request_finished() {
   let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, "http://mochi.test:8888/");
   let extension = ExtensionTestUtils.loadExtension(extData);
 
   await extension.startup();
   await extension.awaitMessage("ready");
-  let target = gDevTools.getTargetForTab(tab);
+  let target = await gDevTools.getTargetForTab(tab);
 
   // Open the Toolbox
   let toolbox = await gDevTools.showToolbox(target, "webconsole");
   info("Developer toolbox opened.");
 
   // Wait the extension to subscribe the onRequestFinished listener.
   await extension.sendMessage("addOnRequestFinishedListener");
 
--- a/browser/components/extensions/test/browser/browser_ext_devtools_page.js
+++ b/browser/components/extensions/test/browser/browser_ext_devtools_page.js
@@ -114,17 +114,17 @@ add_task(async function test_devtools_pa
     },
   });
 
   await extension.startup();
 
   info("Wait the content script load");
   await extension.awaitMessage("content_script_loaded");
 
-  let target = gDevTools.getTargetForTab(tab);
+  let target = await gDevTools.getTargetForTab(tab);
 
   info("Open the developer toolbox");
   await gDevTools.showToolbox(target, "webconsole");
 
   info("Wait the devtools page load");
   await extension.awaitMessage("devtools_page_loaded");
 
   info("Wait the connection 'devtools_page -> background' to complete");
@@ -242,17 +242,17 @@ add_task(async function test_devtools_pa
     },
   });
 
   await extension.startup();
 
   info("Wait the extension tab page load");
   await extension.awaitMessage("extension_tab_loaded");
 
-  let target = gDevTools.getTargetForTab(tab);
+  let target = await gDevTools.getTargetForTab(tab);
 
   info("Open the developer toolbox");
   await gDevTools.showToolbox(target, "webconsole");
 
   info("Wait the devtools page load");
   await extension.awaitMessage("devtools_page_loaded");
 
   extension.sendMessage("extension_tab.send_message");
--- a/browser/components/extensions/test/browser/browser_ext_devtools_panel.js
+++ b/browser/components/extensions/test/browser/browser_ext_devtools_panel.js
@@ -20,24 +20,24 @@ const DEVTOOLS_THEME_PREF = "devtools.th
  * - devtools.panels.themeName returns the correct value,
  *   both from a page and a panel.
  * - devtools.panels.onThemeChanged fires for theme changes,
  *   both from a page and a panel.
  * - devtools.panels.create is able to create a devtools panel.
  */
 
 async function openToolboxForTab(tab) {
-  const target = gDevTools.getTargetForTab(tab);
+  const target = await gDevTools.getTargetForTab(tab);
   const toolbox = await gDevTools.showToolbox(target, "testBlankPanel");
   info("Developer toolbox opened");
   return {toolbox, target};
 }
 
 async function closeToolboxForTab(tab) {
-  const target = gDevTools.getTargetForTab(tab);
+  const target = await gDevTools.getTargetForTab(tab);
   await gDevTools.closeToolbox(target);
   await target.destroy();
   info("Developer toolbox closed");
 }
 
 function createPage(jsScript, bodyText = "") {
   return `<!DOCTYPE html>
     <html>
--- a/browser/components/extensions/test/browser/browser_ext_devtools_panels_elements.js
+++ b/browser/components/extensions/test/browser/browser_ext_devtools_panels_elements.js
@@ -74,17 +74,17 @@ add_task(async function test_devtools_pa
        </body>
       </html>`,
       "devtools_page.js": devtools_page,
     },
   });
 
   await extension.startup();
 
-  let target = devtools.TargetFactory.forTab(tab);
+  let target = await devtools.TargetFactory.forTab(tab);
 
   const toolbox = await gDevTools.showToolbox(target, "webconsole");
   info("developer toolbox opened");
 
   await extension.awaitMessage("devtools_page_loaded");
 
   await toolbox.selectTool("inspector");
 
--- a/browser/components/extensions/test/browser/browser_ext_devtools_panels_elements_sidebar.js
+++ b/browser/components/extensions/test/browser/browser_ext_devtools_panels_elements_sidebar.js
@@ -109,17 +109,17 @@ add_task(async function test_devtools_pa
        </body>
       </html>`,
       "devtools_page.js": devtools_page,
     },
   });
 
   await extension.startup();
 
-  let target = devtools.TargetFactory.forTab(tab);
+  let target = await devtools.TargetFactory.forTab(tab);
 
   const toolbox = await gDevTools.showToolbox(target, "webconsole");
   info("developer toolbox opened");
 
   await extension.awaitMessage("devtools_page_loaded");
 
   const waitInspector = toolbox.once("inspector-selected");
   toolbox.selectTool("inspector");
--- a/browser/components/payments/test/mochitest/payments_common.js
+++ b/browser/components/payments/test/mochitest/payments_common.js
@@ -106,16 +106,26 @@ SpecialPowers.registerConsoleListener(fu
   if (msg.isWarning || !msg.errorMessage || msg.errorMessage == "paymentRequest.xhtml:") {
     // Ignore warnings and non-errors.
     return;
   }
   if (msg.category == "CSP_CSPViolationWithURI" && msg.errorMessage.includes("at inline")) {
     // Ignore unknown CSP error.
     return;
   }
+  if (msg.message.includes("Security Error: Content at http://mochi.test:8888")) {
+    // Check for same-origin policy violations and ignore specific errors
+    if (msg.message.includes("icon-credit-card-generic.svg") ||
+        msg.message.includes("editDialog-shared.css") ||
+        msg.message.includes("editAddress.css") ||
+        msg.message.includes("editDialog.css") ||
+        msg.message.includes("editCreditCard.css")) {
+      return;
+    }
+  }
   if (msg.message == "SENTINEL") {
     filterFunction = null;
   }
   if (filterFunction && filterFunction(msg)) {
     return;
   }
   ok(false, msg.message || msg.errorMessage);
 });
--- a/browser/components/translation/test/browser.ini
+++ b/browser/components/translation/test/browser.ini
@@ -8,9 +8,9 @@ support-files =
 
 [browser_translation_bing.js]
 [browser_translation_yandex.js]
 skip-if = os == 'win' && !e10s # bug 1374446
 [browser_translation_telemetry.js]
 [browser_translation_infobar.js]
 skip-if = debug || (os == 'mac') || (os == 'linux') # Bug 1316953
 [browser_translation_exceptions.js]
-skip-if = (verify && debug && (os == 'mac')) || (os == 'linux') || (os == 'mac') # Bug 1387666
+skip-if = debug || (os == 'mac') || (os == 'linux') # Bug 1387666
--- a/browser/tools/mozscreenshots/mozscreenshots/extension/configurations/DevTools.jsm
+++ b/browser/tools/mozscreenshots/mozscreenshots/extension/configurations/DevTools.jsm
@@ -8,60 +8,64 @@ var EXPORTED_SYMBOLS = ["DevTools"];
 
 ChromeUtils.import("resource://devtools/client/framework/gDevTools.jsm");
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 ChromeUtils.import("resource://gre/modules/Timer.jsm");
 
 let { devtools } = ChromeUtils.import("resource://devtools/shared/Loader.jsm", {});
 let TargetFactory = devtools.TargetFactory;
 
-function getTargetForSelectedTab() {
+async function getTargetForSelectedTab() {
   let browserWindow = Services.wm.getMostRecentWindow("navigator:browser");
-  let target = TargetFactory.forTab(browserWindow.gBrowser.selectedTab);
+  let target = await TargetFactory.forTab(browserWindow.gBrowser.selectedTab);
   return target;
 }
 
-function selectToolbox() {
-  return gDevTools.getToolbox(getTargetForSelectedTab()).win.document.querySelector("#toolbox-container");
+function selectToolbox(toolbox) {
+  return toolbox.win.document.querySelector("#toolbox-container");
 }
 
 var DevTools = {
   init(libDir) {
     let panels = ["options", "webconsole", "jsdebugger", "styleeditor",
                   "performance", "netmonitor"];
 
     panels.forEach(panel => {
       this.configurations[panel] = {};
-      this.configurations[panel].selectors = [selectToolbox];
       this.configurations[panel].applyConfig = async function() {
         Services.prefs.setIntPref("devtools.toolbox.footer.height", 800);
-        await gDevTools.showToolbox(getTargetForSelectedTab(), panel, "bottom");
+        let target = await getTargetForSelectedTab();
+        let toolbox = await gDevTools.showToolbox(target, panel, "bottom");
+        this.selectors = [selectToolbox.bind(null, toolbox)];
         await new Promise(resolve => setTimeout(resolve, 500));
       };
     });
   },
 
   configurations: {
     bottomToolbox: {
-      selectors: [selectToolbox],
       async applyConfig() {
         Services.prefs.clearUserPref("devtools.toolbox.footer.height");
-        await gDevTools.showToolbox(getTargetForSelectedTab(), "inspector", "bottom");
+        let target = await getTargetForSelectedTab();
+        let toolbox = await gDevTools.showToolbox(target, "inspector", "bottom");
+        this.selectors = [selectToolbox.bind(null, toolbox)];
         await new Promise(resolve => setTimeout(resolve, 1000));
       },
     },
     sideToolbox: {
-      selectors: [selectToolbox],
       async applyConfig() {
-        await gDevTools.showToolbox(getTargetForSelectedTab(), "inspector", "right");
+        let target = await getTargetForSelectedTab();
+        let toolbox = await gDevTools.showToolbox(target, "inspector", "right");
+        this.selectors = [selectToolbox.bind(null, toolbox)];
         await new Promise(resolve => setTimeout(resolve, 500));
       },
     },
     undockedToolbox: {
-      selectors: [selectToolbox],
       windowType: "devtools:toolbox",
       async applyConfig() {
-        await gDevTools.showToolbox(getTargetForSelectedTab(), "inspector", "window");
+        let target = await getTargetForSelectedTab();
+        let toolbox = await gDevTools.showToolbox(target, "inspector", "window");
+        this.selectors = [selectToolbox.bind(null, toolbox)];
         await new Promise(resolve => setTimeout(resolve, 500));
       },
     },
   },
 };
--- a/caps/BasePrincipal.cpp
+++ b/caps/BasePrincipal.cpp
@@ -161,18 +161,18 @@ BasePrincipal::CheckMayLoad(nsIURI* aURI
   if (NS_SUCCEEDED(rv) && fetchableByAnyone) {
     return NS_OK;
   }
 
   if (aReport) {
     nsCOMPtr<nsIURI> prinURI;
     rv = GetURI(getter_AddRefs(prinURI));
     if (NS_SUCCEEDED(rv) && prinURI) {
-      nsScriptSecurityManager::ReportError(nullptr, "CheckSameOriginError",
-                                           prinURI, aURI);
+      nsScriptSecurityManager::ReportError("CheckSameOriginError", prinURI, aURI,
+                                           mOriginAttributes.mPrivateBrowsingId > 0);
     }
   }
 
   return NS_ERROR_DOM_BAD_URI;
 }
 
 NS_IMETHODIMP
 BasePrincipal::GetCsp(nsIContentSecurityPolicy** aCsp)
--- a/caps/nsIScriptSecurityManager.idl
+++ b/caps/nsIScriptSecurityManager.idl
@@ -177,20 +177,23 @@ interface nsIScriptSecurityManager : nsI
     [implicit_jscontext]
     nsIPrincipal createNullPrincipal(in jsval originAttributes);
 
     /**
      * Returns OK if aSourceURI and target have the same "origin"
      * (scheme, host, and port).
      * ReportError flag suppresses error reports for functions that
      * don't need reporting.
+     * FromPrivateWindow indicates whether the error occurs in a private
+     * window or not.
      */
     void checkSameOriginURI(in nsIURI aSourceURI,
                             in nsIURI aTargetURI,
-                            in boolean reportError);
+                            in boolean reportError,
+                            in boolean fromPrivateWindow);
     /**
      * Get the principal for the given channel.  This will typically be the
      * channel owner if there is one, and the codebase principal for the
      * channel's URI otherwise.  aChannel must not be null.
      */
     nsIPrincipal getChannelResultPrincipal(in nsIChannel aChannel);
 
     /**
--- a/caps/nsScriptSecurityManager.cpp
+++ b/caps/nsScriptSecurityManager.cpp
@@ -10,30 +10,32 @@
 
 #include "xpcpublic.h"
 #include "XPCWrapper.h"
 #include "nsIInputStreamChannel.h"
 #include "nsILoadContext.h"
 #include "nsIServiceManager.h"
 #include "nsIScriptObjectPrincipal.h"
 #include "nsIScriptContext.h"
+#include "nsIScriptError.h"
 #include "nsIURL.h"
 #include "nsIURIMutator.h"
 #include "nsINestedURI.h"
 #include "nspr.h"
 #include "nsJSPrincipals.h"
 #include "mozilla/BasePrincipal.h"
 #include "ExpandedPrincipal.h"
 #include "SystemPrincipal.h"
 #include "DomainPolicy.h"
 #include "nsString.h"
 #include "nsCRT.h"
 #include "nsCRTGlue.h"
 #include "nsDocShell.h"
 #include "nsError.h"
+#include "nsGlobalWindowInner.h"
 #include "nsDOMCID.h"
 #include "nsTextFormatter.h"
 #include "nsIStringBundle.h"
 #include "nsNetUtil.h"
 #include "nsIEffectiveTLDService.h"
 #include "nsIProperties.h"
 #include "nsDirectoryServiceDefs.h"
 #include "nsIFile.h"
@@ -536,23 +538,26 @@ nsScriptSecurityManager::JSPrincipalsSub
                                              JSPrincipals *second)
 {
     return nsJSPrincipals::get(first)->Subsumes(nsJSPrincipals::get(second));
 }
 
 NS_IMETHODIMP
 nsScriptSecurityManager::CheckSameOriginURI(nsIURI* aSourceURI,
                                             nsIURI* aTargetURI,
-                                            bool reportError)
+                                            bool reportError,
+                                            bool aFromPrivateWindow)
 {
+  // Please note that aFromPrivateWindow is only 100% accurate if
+  // reportError is true.
     if (!SecurityCompareURIs(aSourceURI, aTargetURI))
     {
          if (reportError) {
-            ReportError(nullptr, "CheckSameOriginError",
-                        aSourceURI, aTargetURI);
+            ReportError("CheckSameOriginError",
+                        aSourceURI, aTargetURI, aFromPrivateWindow);
          }
          return NS_ERROR_DOM_BAD_URI;
     }
     return NS_OK;
 }
 
 /*static*/ uint32_t
 nsScriptSecurityManager::HashPrincipalByOrigin(nsIPrincipal* aPrincipal)
@@ -727,17 +732,18 @@ nsScriptSecurityManager::CheckLoadURIWit
                              nsIProtocolHandler::URI_LOADABLE_BY_SUBSUMERS,
                              &hasFlags);
     NS_ENSURE_SUCCESS(rv, rv);
 
     if (hasFlags) {
         // check nothing else in the URI chain has flags that prevent
         // access:
         rv = CheckLoadURIFlags(sourceURI, aTargetURI, sourceBaseURI,
-                               targetBaseURI, aFlags);
+                               targetBaseURI, aFlags,
+                               aPrincipal->OriginAttributesRef().mPrivateBrowsingId > 0);
         NS_ENSURE_SUCCESS(rv, rv);
         // Check the principal is allowed to load the target.
         return aPrincipal->CheckMayLoad(targetBaseURI, true, false);
     }
 
     //-- get the source scheme
     nsAutoCString sourceScheme;
     rv = sourceBaseURI->GetScheme(sourceScheme);
@@ -856,17 +862,18 @@ nsScriptSecurityManager::CheckLoadURIWit
 
         // If schemes are not equal, or they're equal but the target URI
         // is different from the source URI and doesn't always allow linking
         // from the same scheme, check if the URI flags of the current target
         // URI allow the current source URI to link to it.
         // The policy is specified by the protocol flags on both URIs.
         if (!schemesMatch || (denySameSchemeLinks && !isSamePage)) {
             return CheckLoadURIFlags(currentURI, currentOtherURI,
-                                     sourceBaseURI, targetBaseURI, aFlags);
+                                     sourceBaseURI, targetBaseURI, aFlags,
+                                     aPrincipal->OriginAttributesRef().mPrivateBrowsingId > 0);
         }
         // Otherwise... check if we can nest another level:
         nsCOMPtr<nsINestedURI> nestedURI = do_QueryInterface(currentURI);
         nsCOMPtr<nsINestedURI> nestedOtherURI = do_QueryInterface(currentOtherURI);
 
         // If schemes match and neither URI is nested further, we're OK.
         if (!nestedURI && !nestedOtherURI) {
             return NS_OK;
@@ -893,34 +900,35 @@ nsScriptSecurityManager::CheckLoadURIWit
  *
  * @return if success, access is allowed. Otherwise, deny access
  */
 nsresult
 nsScriptSecurityManager::CheckLoadURIFlags(nsIURI *aSourceURI,
                                            nsIURI *aTargetURI,
                                            nsIURI *aSourceBaseURI,
                                            nsIURI *aTargetBaseURI,
-                                           uint32_t aFlags)
+                                           uint32_t aFlags,
+                                           bool aFromPrivateWindow)
 {
     // Note that the order of policy checks here is very important!
     // We start from most restrictive and work our way down.
     bool reportErrors = !(aFlags & nsIScriptSecurityManager::DONT_REPORT_ERRORS);
     const char* errorTag = "CheckLoadURIError";
 
     nsAutoCString targetScheme;
     nsresult rv = aTargetBaseURI->GetScheme(targetScheme);
     if (NS_FAILED(rv)) return rv;
 
     // Check for system target URI
     rv = DenyAccessIfURIHasFlags(aTargetURI,
                                  nsIProtocolHandler::URI_DANGEROUS_TO_LOAD);
     if (NS_FAILED(rv)) {
         // Deny access, since the origin principal is not system
         if (reportErrors) {
-            ReportError(nullptr, errorTag, aSourceURI, aTargetURI);
+            ReportError(errorTag, aSourceURI, aTargetURI, aFromPrivateWindow);
         }
         return rv;
     }
 
     // Check for chrome target URI
     bool hasFlags = false;
     rv = NS_URIChainHasFlags(aTargetURI,
                              nsIProtocolHandler::URI_IS_UI_RESOURCE,
@@ -989,17 +997,17 @@ nsScriptSecurityManager::CheckLoadURIFla
                     if (accessAllowed) {
                         return NS_OK;
                     }
                 }
             }
         }
 
         if (reportErrors) {
-            ReportError(nullptr, errorTag, aSourceURI, aTargetURI);
+            ReportError(errorTag, aSourceURI, aTargetURI, aFromPrivateWindow);
         }
         return NS_ERROR_DOM_BAD_URI;
     }
 
     // Check for target URI pointing to a file
     rv = NS_URIChainHasFlags(aTargetURI,
                              nsIProtocolHandler::URI_IS_LOCAL_FILE,
                              &hasFlags);
@@ -1016,17 +1024,17 @@ nsScriptSecurityManager::CheckLoadURIFla
         // Allow chrome://
         bool isChrome = false;
         if (NS_SUCCEEDED(aSourceBaseURI->SchemeIs("chrome", &isChrome)) && isChrome) {
             return NS_OK;
         }
 
         // Nothing else.
         if (reportErrors) {
-            ReportError(nullptr, errorTag, aSourceURI, aTargetURI);
+            ReportError(errorTag, aSourceURI, aTargetURI, aFromPrivateWindow);
         }
         return NS_ERROR_DOM_BAD_URI;
     }
 
     // OK, everyone is allowed to load this, since unflagged handlers are
     // deprecated but treated as URI_LOADABLE_BY_ANYONE.  But check whether we
     // need to warn.  At some point we'll want to make this warning into an
     // error and treat unflagged handlers as URI_DANGEROUS_TO_LOAD.
@@ -1061,18 +1069,18 @@ nsScriptSecurityManager::CheckLoadURIFla
             }
         }
     }
 
     return NS_OK;
 }
 
 nsresult
-nsScriptSecurityManager::ReportError(JSContext* cx, const char* aMessageTag,
-                                     nsIURI* aSource, nsIURI* aTarget)
+nsScriptSecurityManager::ReportError(const char* aMessageTag, nsIURI* aSource,
+                                     nsIURI* aTarget, bool aFromPrivateWindow)
 {
     nsresult rv;
     NS_ENSURE_TRUE(aSource && aTarget, NS_ERROR_NULL_POINTER);
 
     // Get the source URL spec
     nsAutoCString sourceSpec;
     rv = aSource->GetAsciiSpec(sourceSpec);
     NS_ENSURE_SUCCESS(rv, rv);
@@ -1093,31 +1101,28 @@ nsScriptSecurityManager::ReportError(JSC
     NS_ConvertASCIItoUTF16 ucsTargetSpec(targetSpec);
     const char16_t *formatStrings[] = { ucsSourceSpec.get(), ucsTargetSpec.get() };
     rv = bundle->FormatStringFromName(aMessageTag,
                                       formatStrings,
                                       ArrayLength(formatStrings),
                                       message);
     NS_ENSURE_SUCCESS(rv, rv);
 
-    // If a JS context was passed in, set a JS exception.
-    // Otherwise, print the error message directly to the JS console
-    // and to standard output
-    if (cx)
-    {
-        SetPendingException(cx, message.get());
-    }
-    else // Print directly to the console
-    {
-        nsCOMPtr<nsIConsoleService> console(
-            do_GetService("@mozilla.org/consoleservice;1"));
-        NS_ENSURE_TRUE(console, NS_ERROR_FAILURE);
+    nsCOMPtr<nsIConsoleService> console(do_GetService(NS_CONSOLESERVICE_CONTRACTID));
+    NS_ENSURE_TRUE(console, NS_ERROR_FAILURE);
+    nsCOMPtr<nsIScriptError> error(do_CreateInstance(NS_SCRIPTERROR_CONTRACTID));
+    NS_ENSURE_TRUE(error, NS_ERROR_FAILURE);
 
-        console->LogStringMessage(message.get());
-    }
+    // using category of "SOP" so we can link to MDN
+    rv = error->Init(message, EmptyString(), 
+                     EmptyString(), 0, 0,
+                     nsIScriptError::errorFlag,
+                    "SOP", aFromPrivateWindow);
+    NS_ENSURE_SUCCESS(rv, rv);
+    console->LogMessage(error);
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsScriptSecurityManager::CheckLoadURIStrWithPrincipal(nsIPrincipal* aPrincipal,
                                                       const nsACString& aTargetURIStr,
                                                       uint32_t aFlags)
 {
--- a/caps/nsScriptSecurityManager.h
+++ b/caps/nsScriptSecurityManager.h
@@ -58,18 +58,18 @@ public:
      * are equivalent if their schemes, hosts, and ports (if any) match.  This
      * method returns true if aSubjectURI and aObjectURI have the same origin,
      * false otherwise.
      */
     static bool SecurityCompareURIs(nsIURI* aSourceURI, nsIURI* aTargetURI);
     static uint32_t SecurityHashURI(nsIURI* aURI);
 
     static nsresult
-    ReportError(JSContext* cx, const char* aMessageTag,
-                nsIURI* aSource, nsIURI* aTarget);
+    ReportError(const char* aMessageTag, nsIURI* aSource,
+                nsIURI* aTarget, bool aFromPrivateWindow);
 
     static uint32_t
     HashPrincipalByOrigin(nsIPrincipal* aPrincipal);
 
     static bool
     GetStrictFileOriginPolicy()
     {
         return sStrictFileOriginPolicy;
@@ -103,17 +103,17 @@ private:
     AddSitesToFileURIWhitelist(const nsCString& aSiteList);
 
     nsresult GetChannelResultPrincipal(nsIChannel* aChannel,
                                        nsIPrincipal** aPrincipal,
                                        bool aIgnoreSandboxing);
 
     nsresult
     CheckLoadURIFlags(nsIURI* aSourceURI, nsIURI* aTargetURI, nsIURI* aSourceBaseURI,
-                      nsIURI* aTargetBaseURI, uint32_t aFlags);
+                      nsIURI* aTargetBaseURI, uint32_t aFlags, bool aFromPrivateWindow);
 
     // Returns the file URI whitelist, initializing it if it has not been
     // initialized.
     const nsTArray<nsCOMPtr<nsIURI>>& EnsureFileURIWhitelist();
 
     nsCOMPtr<nsIPrincipal> mSystemPrincipal;
     bool mPrefInitialized;
     bool mIsJavaScriptEnabled;
--- a/devtools/client/accessibility/panel.js
+++ b/devtools/client/accessibility/panel.js
@@ -56,17 +56,17 @@ AccessibilityPanel.prototype = {
 
     let resolver;
     this._opening = new Promise(resolve => {
       resolver = resolve;
     });
 
     // Local monitoring needs to make the target remote.
     if (!this.target.isRemote) {
-      await this.target.makeRemote();
+      await this.target.attach();
     }
 
     this._telemetry = new Telemetry();
     this.panelWin.gTelemetry = this._telemetry;
 
     this.target.on("navigate", this.onTabNavigated);
     this._toolbox.on("select", this.onPanelVisibilityChange);
 
--- a/devtools/client/accessibility/test/browser/head.js
+++ b/devtools/client/accessibility/test/browser/head.js
@@ -151,17 +151,17 @@ async function disableAccessibilityInspe
  * Open the Accessibility panel for the given tab.
  *
  * @param {Element} tab
  *        Optional tab element for which you want open the Accessibility panel.
  *        The default tab is taken from the global variable |tab|.
  * @return a promise that is resolved once the panel is open.
  */
 async function initAccessibilityPanel(tab = gBrowser.selectedTab) {
-  const target = TargetFactory.forTab(tab);
+  const target = await TargetFactory.forTab(tab);
   const toolbox = await gDevTools.showToolbox(target, "accessibility");
   return toolbox.getCurrentPanel();
 }
 
 /**
  * Check the state of the accessibility tree.
  * @param  {document} doc       panel documnent.
  * @param  {Array}    expected  an array that represents an expected row list.
--- a/devtools/client/application/panel.js
+++ b/devtools/client/application/panel.js
@@ -19,17 +19,17 @@ class ApplicationPanel {
    */
   constructor(panelWin, toolbox) {
     this.panelWin = panelWin;
     this.toolbox = toolbox;
   }
 
   async open() {
     if (!this.toolbox.target.isRemote) {
-      await this.toolbox.target.makeRemote();
+      await this.toolbox.target.attach();
     }
 
     await this.panelWin.Application.bootstrap({
       toolbox: this.toolbox,
       panel: this,
     });
 
     this.emit("ready");
--- a/devtools/client/application/test/head.js
+++ b/devtools/client/application/test/head.js
@@ -42,18 +42,18 @@ function getWorkerContainers(doc) {
 
 function navigate(target, url, waitForTargetEvent = "navigate") {
   executeSoon(() => target.activeTab.navigateTo(url));
   return once(target, waitForTargetEvent);
 }
 
 async function openNewTabAndApplicationPanel(url) {
   const tab = await addTab(url);
-  const target = TargetFactory.forTab(tab);
-  await target.makeRemote();
+  const target = await TargetFactory.forTab(tab);
+  await target.attach();
 
   const toolbox = await gDevTools.showToolbox(target, "application");
   const panel = toolbox.getCurrentPanel();
   return { panel, tab, target, toolbox };
 }
 
 async function unregisterAllWorkers(client) {
   info("Wait until all workers have a valid registrationActor");
--- a/devtools/client/canvasdebugger/panel.js
+++ b/devtools/client/canvasdebugger/panel.js
@@ -27,17 +27,17 @@ CanvasDebuggerPanel.prototype = {
    * @return object
    *         A promise that is resolved when the Canvas Debugger completes opening.
    */
   open: function() {
     let targetPromise;
 
     // Local debugging needs to make the target remote.
     if (!this.target.isRemote) {
-      targetPromise = this.target.makeRemote();
+      targetPromise = this.target.attach();
     } else {
       targetPromise = Promise.resolve(this.target);
     }
 
     return targetPromise
       .then(() => {
         this.panelWin.gToolbox = this._toolbox;
         this.panelWin.gTarget = this.target;
--- a/devtools/client/canvasdebugger/test/head.js
+++ b/devtools/client/canvasdebugger/test/head.js
@@ -122,53 +122,54 @@ function initServer() {
 }
 
 function initCallWatcherBackend(aUrl) {
   info("Initializing a call watcher front.");
   initServer();
 
   return (async function() {
     const tab = await addTab(aUrl);
-    const target = TargetFactory.forTab(tab);
+
     await registerActorInContentProcess("chrome://mochitests/content/browser/devtools/client/canvasdebugger/test/call-watcher-actor.js", {
       prefix: "callWatcher",
       constructor: "CallWatcherActor",
       type: { target: true }
     });
 
-    await target.makeRemote();
+    const target = await TargetFactory.forTab(tab);
+    await target.attach();
 
     const front = new CallWatcherFront(target.client, target.form);
     return { target, front };
   })();
 }
 
 function initCanvasDebuggerBackend(aUrl) {
   info("Initializing a canvas debugger front.");
   initServer();
 
   return (async function() {
     const tab = await addTab(aUrl);
-    const target = TargetFactory.forTab(tab);
+    const target = await TargetFactory.forTab(tab);
 
-    await target.makeRemote();
+    await target.attach();
 
     const front = new CanvasFront(target.client, target.form);
     return { target, front };
   })();
 }
 
 function initCanvasDebuggerFrontend(aUrl) {
   info("Initializing a canvas debugger pane.");
 
   return (async function() {
     const tab = await addTab(aUrl);
-    const target = TargetFactory.forTab(tab);
+    const target = await TargetFactory.forTab(tab);
 
-    await target.makeRemote();
+    await target.attach();
 
     Services.prefs.setBoolPref("devtools.canvasdebugger.enabled", true);
     const toolbox = await gDevTools.showToolbox(target, "canvasdebugger");
     const panel = toolbox.getCurrentPanel();
     return { target, panel };
   })();
 }
 
--- a/devtools/client/debugger/new/panel.js
+++ b/devtools/client/debugger/new/panel.js
@@ -17,17 +17,17 @@ function DebuggerPanel(iframeWindow, too
   this.panelWin = iframeWindow;
   this.panelWin.L10N = L10N;
   this.toolbox = toolbox;
 }
 
 DebuggerPanel.prototype = {
   open: async function() {
     if (!this.toolbox.target.isRemote) {
-      await this.toolbox.target.makeRemote();
+      await this.toolbox.target.attach();
     }
 
     const {
       actions,
       store,
       selectors,
       client
     } = await this.panelWin.Debugger.bootstrap({
--- a/devtools/client/debugger/new/test/mochitest/browser_dbg-chrome-debugging.js
+++ b/devtools/client/debugger/new/test/mochitest/browser_dbg-chrome-debugging.js
@@ -25,17 +25,17 @@ function initDebuggerClient() {
   DebuggerServer.registerAllActors();
   DebuggerServer.allowChromeProcess = true;
 
   let transport = DebuggerServer.connectPipe();
   return new DebuggerClient(transport);
 }
 
 async function attachThread(client, actor) {
-  let [response, tabClient] = await client.attachTab(actor);
+  let [response, tabClient] = await client.attachTarget(actor);
   let [response2, threadClient] = await tabClient.attachThread(null);
   return threadClient;
 }
 
 function onNewGlobal() {
   ok(true, "Received a new chrome global.");
   gClient.removeListener("newGlobal", onNewGlobal);
   gNewGlobal.resolve();
--- a/devtools/client/debugger/new/test/mochitest/head.js
+++ b/devtools/client/debugger/new/test/mochitest/head.js
@@ -64,17 +64,17 @@ async function takeScreenshot(dbg) {
   context.drawWindow(dbg.win, 0, 0, canvas.width, canvas.height, "white");
   await waitForTime(1000);
   dump(`[SCREENSHOT] ${canvas.toDataURL()}\n`);
 }
 
 // Attach a debugger to a tab, returning a promise that resolves with the
 // debugger's toolbox.
 async function attachDebugger(tab) {
-  let target = TargetFactory.forTab(tab);
+  let target = await TargetFactory.forTab(tab);
   let toolbox = await gDevTools.showToolbox(target, "jsdebugger");
   return toolbox;
 }
 
 // Return a promise that resolves when a breakpoint has been set.
 async function setBreakpoint(threadClient, expectedFile, lineno) {
   let {sources} = await threadClient.getSources();
   ok(sources.length == 1, "Got one source");
--- a/devtools/client/debugger/panel.js
+++ b/devtools/client/debugger/panel.js
@@ -34,17 +34,17 @@ DebuggerPanel.prototype = {
    * @return object
    *         A promise that is resolved when the Debugger completes opening.
    */
   open: function () {
     let targetPromise;
 
     // Local debugging needs to make the target remote.
     if (!this.target.isRemote) {
-      targetPromise = this.target.makeRemote();
+      targetPromise = this.target.attach();
       // Listen for tab switching events to manage focus when the content window
       // is paused and events suppressed.
       this.target.tab.addEventListener("TabSelect", this);
     } else {
       targetPromise = promise.resolve(this.target);
     }
 
     return targetPromise
--- a/devtools/client/debugger/test/mochitest/browser_dbg_WorkerTargetActor.attach.js
+++ b/devtools/client/debugger/test/mochitest/browser_dbg_WorkerTargetActor.attach.js
@@ -13,17 +13,17 @@ function test() {
     DebuggerServer.init();
     DebuggerServer.registerAllActors();
 
     let client = new DebuggerClient(DebuggerServer.connectPipe());
     yield connect(client);
 
     let tab = yield addTab(TAB1_URL);
     let { tabs } = yield listTabs(client);
-    let [, tabClient] = yield attachTab(client, findTab(tabs, TAB1_URL));
+    let [, tabClient] = yield attachTarget(client, findTab(tabs, TAB1_URL));
     yield listWorkers(tabClient);
 
     // If a page still has pending network requests, it will not be moved into
     // the bfcache. Consequently, we cannot use waitForWorkerListChanged here,
     // because the worker is not guaranteed to have finished loading when it is
     // registered. Instead, we have to wait for the promise returned by
     // createWorker in the tab to be resolved.
     yield createWorkerInTab(tab, WORKER1_URL);
--- a/devtools/client/debugger/test/mochitest/browser_dbg_WorkerTargetActor.attachThread.js
+++ b/devtools/client/debugger/test/mochitest/browser_dbg_WorkerTargetActor.attachThread.js
@@ -8,19 +8,19 @@ function test() {
 
     let client1 = new DebuggerClient(DebuggerServer.connectPipe());
     yield connect(client1);
     let client2 = new DebuggerClient(DebuggerServer.connectPipe());
     yield connect(client2);
 
     let tab = yield addTab(TAB_URL);
     let { tabs: tabs1 } = yield listTabs(client1);
-    let [, tabClient1] = yield attachTab(client1, findTab(tabs1, TAB_URL));
+    let [, tabClient1] = yield attachTarget(client1, findTab(tabs1, TAB_URL));
     let { tabs: tabs2 } = yield listTabs(client2);
-    let [, tabClient2] = yield attachTab(client2, findTab(tabs2, TAB_URL));
+    let [, tabClient2] = yield attachTarget(client2, findTab(tabs2, TAB_URL));
 
     yield listWorkers(tabClient1);
     yield listWorkers(tabClient2);
     yield createWorkerInTab(tab, WORKER_URL);
     let { workers: workers1 } = yield listWorkers(tabClient1);
     let [, workerClient1] = yield attachWorker(tabClient1,
                                                findWorker(workers1, WORKER_URL));
     let { workers: workers2 } = yield listWorkers(tabClient2);
--- a/devtools/client/debugger/test/mochitest/browser_dbg_break-unselected.js
+++ b/devtools/client/debugger/test/mochitest/browser_dbg_break-unselected.js
@@ -8,17 +8,17 @@
  * the debugger UI has been initialized.
  */
 
 const TAB_URL = EXAMPLE_URL + "doc_inline-debugger-statement.html";
 
 function test() {
   Task.spawn(function* () {
     const tab = yield getTab(TAB_URL);
-    const target = TargetFactory.forTab(tab);
+    const target = yield TargetFactory.forTab(tab);
     const toolbox = yield gDevTools.showToolbox(target, "webconsole");
 
     is(toolbox.currentToolId, "webconsole", "Console is the current panel");
 
     toolbox.target.on("thread-paused", Task.async(function* () {
       // Wait for the toolbox to handle the event and switch tools
       yield waitForTick();
 
--- a/devtools/client/debugger/test/mochitest/browser_dbg_chrome-debugging.js
+++ b/devtools/client/debugger/test/mochitest/browser_dbg_chrome-debugging.js
@@ -41,17 +41,17 @@ function test() {
   });
 }
 
 function testParentProcessTargetActor() {
   gClient.getProcess().then(aResponse => {
     gClient.addListener("newGlobal", onNewGlobal);
 
     let actor = aResponse.form.actor;
-    gClient.attachTab(actor).then(([response, tabClient]) => {
+    gClient.attachTarget(actor).then(([response, tabClient]) => {
       tabClient.attachThread(null).then(([aResponse, aThreadClient]) => {
         gThreadClient = aThreadClient;
         gThreadClient.addListener("newSource", onNewSource);
 
         if (aResponse.error) {
           ok(false, "Couldn't attach to the chrome debugger.");
           gAttached.reject();
         } else {
--- a/devtools/client/debugger/test/mochitest/browser_dbg_listworkers.js
+++ b/devtools/client/debugger/test/mochitest/browser_dbg_listworkers.js
@@ -7,17 +7,17 @@ function test() {
     DebuggerServer.init();
     DebuggerServer.registerAllActors();
 
     let client = new DebuggerClient(DebuggerServer.connectPipe());
     yield connect(client);
 
     let tab = yield addTab(TAB_URL);
     let { tabs } = yield listTabs(client);
-    let [, tabClient] = yield attachTab(client, findTab(tabs, TAB_URL));
+    let [, tabClient] = yield attachTarget(client, findTab(tabs, TAB_URL));
 
     let { workers } = yield listWorkers(tabClient);
     is(workers.length, 0);
 
     executeSoon(() => {
       evalInTab(tab, "var worker1 = new Worker('" + WORKER1_URL + "');");
     });
     yield waitForWorkerListChanged(tabClient);
--- a/devtools/client/debugger/test/mochitest/browser_dbg_promises-allocation-stack.js
+++ b/devtools/client/debugger/test/mochitest/browser_dbg_promises-allocation-stack.js
@@ -24,17 +24,17 @@ function test() {
     };
     const [ tab,, panel ] = yield initDebugger(TAB_URL, options);
 
     let client = new DebuggerClient(DebuggerServer.connectPipe());
     yield connect(client);
 
     let { tabs } = yield listTabs(client);
     let targetTab = findTab(tabs, TAB_URL);
-    yield attachTab(client, targetTab);
+    yield attachTarget(client, targetTab);
 
     yield testGetAllocationStack(client, targetTab, tab);
 
     yield close(client);
     yield closeDebuggerAndFinish(panel);
   }).catch(error => {
     ok(false, "Got an error: " + error.message + "\n" + error.stack);
   });
--- a/devtools/client/debugger/test/mochitest/browser_dbg_promises-chrome-allocation-stack.js
+++ b/devtools/client/debugger/test/mochitest/browser_dbg_promises-chrome-allocation-stack.js
@@ -25,17 +25,17 @@ function test() {
 
     DebuggerServer.init();
     DebuggerServer.registerAllActors();
     DebuggerServer.allowChromeProcess = true;
 
     let client = new DebuggerClient(DebuggerServer.connectPipe());
     yield connect(client);
     let chrome = yield client.getProcess();
-    let [, tabClient] = yield attachTab(client, chrome.form);
+    let [, tabClient] = yield attachTarget(client, chrome.form);
     yield tabClient.attachThread();
 
     yield testGetAllocationStack(client, chrome.form, () => {
       let p = new Promise(() => {});
       p.name = "p";
       let q = p.then();
       q.name = "q";
       let r = p.catch(() => {});
--- a/devtools/client/debugger/test/mochitest/browser_dbg_promises-fulfillment-stack.js
+++ b/devtools/client/debugger/test/mochitest/browser_dbg_promises-fulfillment-stack.js
@@ -42,17 +42,17 @@ function test() {
     };
     const [ tab,, panel ] = yield initDebugger(TAB_URL, options);
 
     let client = new DebuggerClient(DebuggerServer.connectPipe());
     yield connect(client);
 
     let { tabs } = yield listTabs(client);
     let targetTab = findTab(tabs, TAB_URL);
-    yield attachTab(client, targetTab);
+    yield attachTarget(client, targetTab);
 
     yield testGetFulfillmentStack(client, targetTab, tab);
 
     yield close(client);
     yield closeDebuggerAndFinish(panel);
   }).catch(error => {
     ok(false, "Got an error: " + error.message + "\n" + error.stack);
   });
--- a/devtools/client/debugger/test/mochitest/browser_dbg_promises-rejection-stack.js
+++ b/devtools/client/debugger/test/mochitest/browser_dbg_promises-rejection-stack.js
@@ -49,17 +49,17 @@ function test() {
     };
     const [ tab,, panel ] = yield initDebugger(TAB_URL, options);
 
     let client = new DebuggerClient(DebuggerServer.connectPipe());
     yield connect(client);
 
     let { tabs } = yield listTabs(client);
     let targetTab = findTab(tabs, TAB_URL);
-    yield attachTab(client, targetTab);
+    yield attachTarget(client, targetTab);
 
     yield testGetRejectionStack(client, targetTab, tab);
 
     yield close(client);
     yield closeDebuggerAndFinish(panel);
   }).catch(error => {
     ok(false, "Got an error: " + error.message + "\n" + error.stack);
   });
--- a/devtools/client/debugger/test/mochitest/browser_dbg_worker-window.js
+++ b/devtools/client/debugger/test/mochitest/browser_dbg_worker-window.js
@@ -17,17 +17,17 @@ add_task(async function() {
   DebuggerServer.init();
   DebuggerServer.registerAllActors();
 
   let client = new DebuggerClient(DebuggerServer.connectPipe());
   await connect(client);
 
   let tab = await addTab(TAB_URL);
   let { tabs } = await listTabs(client);
-  let [, tabClient] = await attachTab(client, findTab(tabs, TAB_URL));
+  let [, tabClient] = await attachTarget(client, findTab(tabs, TAB_URL));
 
   await listWorkers(tabClient);
   await createWorkerInTab(tab, WORKER_URL);
 
   let { workers } = await listWorkers(tabClient);
   let [, workerClient] = await attachWorker(tabClient,
                                              findWorker(workers, WORKER_URL));
 
--- a/devtools/client/debugger/test/mochitest/head.js
+++ b/devtools/client/debugger/test/mochitest/head.js
@@ -38,17 +38,17 @@ Services.prefs.setBoolPref("devtools.deb
 registerCleanupFunction(async function() {
   Services.prefs.clearUserPref("devtools.debugger.new-debugger-frontend");
 
   info("finish() was called, cleaning up...");
   Services.prefs.setBoolPref("devtools.debugger.log", gEnableLogging);
 
   while (gBrowser && gBrowser.tabs && gBrowser.tabs.length > 1) {
     info("Destroying toolbox.");
-    let target = TargetFactory.forTab(gBrowser.selectedTab);
+    let target = await TargetFactory.forTab(gBrowser.selectedTab);
     await gDevTools.closeToolbox(target);
 
     info("Removing tab.");
     gBrowser.removeCurrentTab();
   }
 
   // Properly shut down the server to avoid memory leaks.
   DebuggerServer.destroy();
@@ -174,17 +174,17 @@ function getAddonActorForId(aClient, aAd
     deferred.resolve(addonTargetActor);
   });
 
   return deferred.promise;
 }
 
 async function attachTargetActorForUrl(aClient, aUrl) {
   let grip = await getTargetActorForUrl(aClient, aUrl);
-  let [ response ] = await aClient.attachTab(grip.actor);
+  let [ response ] = await aClient.attachTarget(grip.actor);
   return [grip, response];
 }
 
 async function attachThreadActorForUrl(aClient, aUrl) {
   let [grip, response] = await attachTargetActorForUrl(aClient, aUrl);
   let [response2, threadClient] = await aClient.attachThread(response.threadActor);
   await threadClient.resume();
   return threadClient;
@@ -548,17 +548,17 @@ let initDebugger = Task.async(function*(
     // only once the panel is ready. That to be able to safely catch the
     // SOURCE_SHOWN event.
     tab = yield addTab("about:blank", window);
     url = urlOrTab;
   }
   info("Debugee tab added successfully: " + urlOrTab);
 
   let debuggee = tab.linkedBrowser.contentWindowAsCPOW.wrappedJSObject;
-  let target = TargetFactory.forTab(tab);
+  let target = yield TargetFactory.forTab(tab);
 
   let toolbox = yield gDevTools.showToolbox(target, "jsdebugger");
   info("Debugger panel shown successfully.");
 
   let debuggerPanel = toolbox.getCurrentPanel();
   let panelWin = debuggerPanel.panelWin;
   let { Sources } = panelWin.DebuggerView;
 
@@ -634,17 +634,17 @@ AddonDebugger.prototype = {
       chrome: true,
       isBrowsingContext: false
     };
 
     let toolboxOptions = {
       customIframe: this.frame
     };
 
-    this.target = TargetFactory.forTab(targetOptions);
+    this.target = yield TargetFactory.forRemoteTab(targetOptions);
     let toolbox = yield gDevTools.showToolbox(this.target, "jsdebugger", Toolbox.HostType.CUSTOM, toolboxOptions);
 
     info("Addon debugger panel shown successfully.");
 
     this.debuggerPanel = toolbox.getCurrentPanel();
     yield waitForSourceShown(this.debuggerPanel, "");
 
     prepareDebugger(this.debuggerPanel);
@@ -1080,19 +1080,19 @@ function findTab(tabs, url) {
   for (let tab of tabs) {
     if (tab.url === url) {
       return tab;
     }
   }
   return null;
 }
 
-function attachTab(client, tab) {
+function attachTarget(client, tab) {
   info("Attaching to tab with url '" + tab.url + "'.");
-  return client.attachTab(tab.actor);
+  return client.attachTarget(tab.actor);
 }
 
 function listWorkers(tabClient) {
   info("Listing workers.");
   return new Promise(function (resolve) {
     tabClient.listWorkers(function (response) {
       resolve(response);
     });
@@ -1292,17 +1292,17 @@ async function initWorkerDebugger(TAB_UR
   DebuggerServer.init();
   DebuggerServer.registerAllActors();
 
   let client = new DebuggerClient(DebuggerServer.connectPipe());
   await connect(client);
 
   let tab = await addTab(TAB_URL);
   let { tabs } = await listTabs(client);
-  let [, tabClient] = await attachTab(client, findTab(tabs, TAB_URL));
+  let [, tabClient] = await attachTarget(client, findTab(tabs, TAB_URL));
 
   await createWorkerInTab(tab, WORKER_URL);
 
   let { workers } = await listWorkers(tabClient);
   let [, workerClient] = await attachWorker(tabClient,
                                              findWorker(workers, WORKER_URL));
 
   let toolbox = await gDevTools.showToolbox(TargetFactory.forWorker(workerClient),
--- a/devtools/client/dom/panel.js
+++ b/devtools/client/dom/panel.js
@@ -41,17 +41,17 @@ DomPanel.prototype = {
       return this._opening;
     }
 
     const deferred = defer();
     this._opening = deferred.promise;
 
     // Local monitoring needs to make the target remote.
     if (!this.target.isRemote) {
-      await this.target.makeRemote();
+      await this.target.attach();
     }
 
     this.initialize();
 
     this.isReady = true;
     this.emit("ready");
     deferred.resolve(this);
 
--- a/devtools/client/dom/test/head.js
+++ b/devtools/client/dom/test/head.js
@@ -57,24 +57,21 @@ function addTestTab(url) {
 /**
  * Open the DOM panel for the given tab.
  *
  * @param {Element} tab
  *        Optional tab element for which you want open the DOM panel.
  *        The default tab is taken from the global variable |tab|.
  * @return a promise that is resolved once the web console is open.
  */
-function initDOMPanel(tab) {
-  return new Promise(resolve => {
-    const target = TargetFactory.forTab(tab || gBrowser.selectedTab);
-    gDevTools.showToolbox(target, "dom").then(toolbox => {
-      const panel = toolbox.getCurrentPanel();
-      resolve(panel);
-    });
-  });
+async function initDOMPanel(tab) {
+  const target = await TargetFactory.forTab(tab || gBrowser.selectedTab);
+  const toolbox = await gDevTools.showToolbox(target, "dom");
+  const panel = toolbox.getCurrentPanel();
+  return panel;
 }
 
 /**
  * Synthesize asynchronous click event (with clean stack trace).
  */
 function synthesizeMouseClickSoon(panel, element) {
   return new Promise(resolve => {
     executeSoon(() => {
--- a/devtools/client/framework/browser-menus.js
+++ b/devtools/client/framework/browser-menus.js
@@ -73,21 +73,25 @@ function createToolMenuElements(toolDefi
   const id = toolDefinition.id;
   const menuId = "menuitem_" + id;
 
   // Prevent multiple entries for the same tool.
   if (doc.getElementById(menuId)) {
     return;
   }
 
-  const oncommand = function(id, event) {
-    const window = event.target.ownerDocument.defaultView;
-    gDevToolsBrowser.selectToolCommand(window.gBrowser, id, Cu.now());
-    sendEntryPointTelemetry();
-  }.bind(null, id);
+  const oncommand = (async function(id, event) {
+    try {
+      const window = event.target.ownerDocument.defaultView;
+      await gDevToolsBrowser.selectToolCommand(window.gBrowser, id, Cu.now());
+      sendEntryPointTelemetry();
+    } catch (e) {
+      console.error(`Exception while opening ${id}: ${e}\n${e.stack}`);
+    }
+  }).bind(null, id);
 
   const menuitem = createMenuItem({
     doc,
     id: "menuitem_" + id,
     label: toolDefinition.menuLabel || toolDefinition.label,
     accesskey: toolDefinition.accesskey
   });
   // Refer to the key in order to display the key shortcut at menu ends
--- a/devtools/client/framework/devtools-browser.js
+++ b/devtools/client/framework/devtools-browser.js
@@ -53,18 +53,18 @@ var gDevToolsBrowser = exports.gDevTools
   _browserStyleSheets: new WeakMap(),
 
   /**
    * This function is for the benefit of Tools:DevToolbox in
    * browser/base/content/browser-sets.inc and should not be used outside
    * of there
    */
   // used by browser-sets.inc, command
-  toggleToolboxCommand(gBrowser, startTime) {
-    const target = TargetFactory.forTab(gBrowser.selectedTab);
+  async toggleToolboxCommand(gBrowser, startTime) {
+    const target = await TargetFactory.forTab(gBrowser.selectedTab);
     const toolbox = gDevTools.getToolbox(target);
 
     // If a toolbox exists, using toggle from the Main window :
     // - should close a docked toolbox
     // - should focus a windowed toolbox
     const isDocked = toolbox && toolbox.hostType != Toolbox.HostType.WINDOW;
     if (isDocked) {
       gDevTools.closeToolbox(target);
@@ -197,18 +197,18 @@ var gDevToolsBrowser = exports.gDevTools
    *   we select it
    * - if the toolbox is open, and the targeted tool is selected,
    *   and the host is NOT a window, we close the toolbox
    * - if the toolbox is open, and the targeted tool is selected,
    *   and the host is a window, we raise the toolbox window
    */
   // Used when: - registering a new tool
   //            - new xul window, to add menu items
-  selectToolCommand(gBrowser, toolId, startTime) {
-    const target = TargetFactory.forTab(gBrowser.selectedTab);
+  async selectToolCommand(gBrowser, toolId, startTime) {
+    const target = await TargetFactory.forTab(gBrowser.selectedTab);
     const toolbox = gDevTools.getToolbox(target);
     const toolDefinition = gDevTools.getToolDefinition(toolId);
 
     if (toolbox &&
         (toolbox.currentToolId == toolId ||
           (toolId == "webconsole" && toolbox.splitConsole))) {
       toolbox.fireCustomKey(toolId);
 
@@ -238,27 +238,27 @@ var gDevToolsBrowser = exports.gDevTools
    *         are:
    *         - `toolId` used to identify a toolbox's panel like inspector or webconsole,
    *         - `id` used to identify any other key shortcuts like scratchpad or
    *         about:debugging
    * @param {Number} startTime
    *        Optional, indicates the time at which the key event fired. This is a
    *        `Cu.now()` timing.
    */
-  onKeyShortcut(window, key, startTime) {
+  async onKeyShortcut(window, key, startTime) {
     // If this is a toolbox's panel key shortcut, delegate to selectToolCommand
     if (key.toolId) {
-      gDevToolsBrowser.selectToolCommand(window.gBrowser, key.toolId, startTime);
+      await gDevToolsBrowser.selectToolCommand(window.gBrowser, key.toolId, startTime);
       return;
     }
     // Otherwise implement all other key shortcuts individually here
     switch (key.id) {
       case "toggleToolbox":
       case "toggleToolboxF12":
-        gDevToolsBrowser.toggleToolboxCommand(window.gBrowser, startTime);
+        await gDevToolsBrowser.toggleToolboxCommand(window.gBrowser, startTime);
         break;
       case "webide":
         gDevToolsBrowser.openWebIDE();
         break;
       case "browserToolbox":
         BrowserToolboxProcess.init();
         break;
       case "browserConsole":
@@ -269,17 +269,17 @@ var gDevToolsBrowser = exports.gDevTools
         ResponsiveUIManager.toggle(window, window.gBrowser.selectedTab, {
           trigger: "shortcut"
         });
         break;
       case "scratchpad":
         ScratchpadManager.openScratchpad();
         break;
       case "inspectorMac":
-        gDevToolsBrowser.selectToolCommand(window.gBrowser, "inspector", startTime);
+        await gDevToolsBrowser.selectToolCommand(window.gBrowser, "inspector", startTime);
         break;
     }
   },
 
   /**
    * Open a tab on "about:debugging", optionally pre-select a given tab.
    */
    // Used by browser-sets.inc, command
@@ -476,18 +476,18 @@ var gDevToolsBrowser = exports.gDevTools
   /**
    * Hook the JS debugger tool to the "Debug Script" button of the slow script
    * dialog.
    */
   setSlowScriptDebugHandler() {
     const debugService = Cc["@mozilla.org/dom/slow-script-debug;1"]
                          .getService(Ci.nsISlowScriptDebug);
 
-    function slowScriptDebugHandler(tab, callback) {
-      const target = TargetFactory.forTab(tab);
+    async function slowScriptDebugHandler(tab, callback) {
+      const target = await TargetFactory.forTab(tab);
 
       gDevTools.showToolbox(target, "jsdebugger").then(toolbox => {
         const threadClient = toolbox.threadClient;
 
         // Break in place, which means resuming the debuggee thread and pausing
         // right before the next step happens.
         switch (threadClient.state) {
           case "paused":
@@ -539,17 +539,17 @@ var gDevToolsBrowser = exports.gDevTools
 
     debugService.remoteActivationHandler = function(browser, callback) {
       const chromeWindow = browser.ownerDocument.defaultView;
       const tab = chromeWindow.gBrowser.getTabForBrowser(browser);
       chromeWindow.gBrowser.selected = tab;
 
       slowScriptDebugHandler(tab, function() {
         callback.finishDebuggerStartup();
-      });
+      }).catch(console.error);
     };
   },
 
   /**
    * Unset the slow script debug handler.
    */
   unsetSlowScriptDebugHandler() {
     const debugService = Cc["@mozilla.org/dom/slow-script-debug;1"]
--- a/devtools/client/framework/devtools.js
+++ b/devtools/client/framework/devtools.js
@@ -5,17 +5,16 @@
 "use strict";
 
 const {Cu} = require("chrome");
 const Services = require("Services");
 
 const {DevToolsShim} = require("chrome://devtools-startup/content/DevToolsShim.jsm");
 
 loader.lazyRequireGetter(this, "TargetFactory", "devtools/client/framework/target", true);
-loader.lazyRequireGetter(this, "TabTarget", "devtools/client/framework/target", true);
 loader.lazyRequireGetter(this, "ToolboxHostManager", "devtools/client/framework/toolbox-host-manager", true);
 loader.lazyRequireGetter(this, "HUDService", "devtools/client/webconsole/hudservice", true);
 loader.lazyRequireGetter(this, "Telemetry", "devtools/client/shared/telemetry");
 loader.lazyImporter(this, "ScratchpadManager", "resource://devtools/client/scratchpad/scratchpad-manager.jsm");
 loader.lazyImporter(this, "BrowserToolboxProcess", "resource://devtools/client/framework/ToolboxProcess.jsm");
 
 loader.lazyRequireGetter(this, "WebExtensionInspectedWindowFront",
       "devtools/shared/fronts/addon/webextension-inspected-window", true);
@@ -615,17 +614,17 @@ DevTools.prototype = {
   /**
    * Compatibility layer for web-extensions. Used by DevToolsShim for
    * browser/components/extensions/ext-devtools.js
    *
    * web-extensions need to use dedicated instances of TabTarget and cannot reuse the
    * cached instances managed by DevTools target factory.
    */
   createTargetForTab: function(tab) {
-    return new TabTarget(tab);
+    return TargetFactory.createTargetForTab(tab);
   },
 
   /**
    * Compatibility layer for web-extensions. Used by DevToolsShim for
    * browser/components/extensions/ext-devtools-inspectedWindow.js
    */
   createWebExtensionInspectedWindowFront: function(tabTarget) {
     return new WebExtensionInspectedWindowFront(tabTarget.client, tabTarget.form);
@@ -687,17 +686,17 @@ DevTools.prototype = {
    *        ending with the deepest nested frame.
    * @param {Number} startTime
    *        Optional, indicates the time at which the user event related to this node
    *        inspection started. This is a `Cu.now()` timing.
    * @return {Promise} a promise that resolves when the node is selected in the inspector
    *         markup view.
    */
   async inspectNode(tab, nodeSelectors, startTime) {
-    const target = TargetFactory.forTab(tab);
+    const target = await TargetFactory.forTab(tab);
 
     const toolbox = await gDevTools.showToolbox(target, "inspector", null, null,
                                                 startTime, "inspect_dom");
     const inspector = toolbox.getCurrentPanel();
 
     // If the toolbox has been switched into a nested frame, we should first remove
     // selectors according to the frame depth.
     nodeSelectors.splice(0, toolbox.selectedFrameDepth);
@@ -727,17 +726,17 @@ DevTools.prototype = {
    *        and not directly in the root document.
    * @param {Number} startTime
    *        Optional, indicates the time at which the user event related to this
    *        node inspection started. This is a `Cu.now()` timing.
    * @return {Promise} a promise that resolves when the accessible object is
    *         selected in the accessibility inspector.
    */
   async inspectA11Y(tab, nodeSelectors, startTime) {
-    const target = TargetFactory.forTab(tab);
+    const target = await TargetFactory.forTab(tab);
 
     const toolbox = await gDevTools.showToolbox(
       target, "accessibility", null, null, startTime);
     const nodeFront = await this.findNodeFront(toolbox.walker, nodeSelectors);
     // Select the accessible object in the panel and wait for the event that
     // tells us it has been done.
     const a11yPanel = toolbox.getCurrentPanel();
     const onSelected = a11yPanel.once("new-accessible-front-selected");
--- a/devtools/client/framework/target.js
+++ b/devtools/client/framework/target.js
@@ -15,30 +15,92 @@ loader.lazyRequireGetter(this, "getFront
 
 const targets = new WeakMap();
 const promiseTargets = new WeakMap();
 
 /**
  * Functions for creating Targets
  */
 const TargetFactory = exports.TargetFactory = {
+
   /**
-   * Construct a Target
+   * Construct a Target. The target will be cached for each Tab so that we create only
+   * one per tab.
+   *
+   * @param {XULTab} tab
+   *        The tab to use in creating a new target.
+   *
+   * @return A target object
+   */
+  forTab: async function(tab) {
+    let target = targets.get(tab);
+    if (target) {
+      return target;
+    }
+    const promise = this.createTargetForTab(tab);
+    // Immediately set the target's promise in cache to prevent race
+    targets.set(tab, promise);
+    target = await promise;
+    // Then replace the promise with the target object
+    targets.set(tab, target);
+    return target;
+  },
+
+  /**
+   * Instantiate a target for the given tab.
+   *
+   * This will automatically:
+   * - spawn a DebuggerServer in the parent process,
+   * - create a DebuggerClient and connect it to this local DebuggerServer,
+   * - call RootActor's `getTab` request to retrieve the FrameTargetActor's form,
+   * - instantiate a TabTarget instance.
+   *
    * @param {XULTab} tab
    *        The tab to use in creating a new target.
    *
    * @return A target object
    */
-  forTab: function(tab) {
-    let target = targets.get(tab);
-    if (target == null) {
-      target = new TabTarget(tab);
-      targets.set(tab, target);
+  async createTargetForTab(tab) {
+    function createLocalServer() {
+      // Since a remote protocol connection will be made, let's start the
+      // DebuggerServer here, once and for all tools.
+      DebuggerServer.init();
+
+      // When connecting to a local tab, we only need the root actor.
+      // Then we are going to call DebuggerServer.connectToFrame and talk
+      // directly with actors living in the child process.
+      // We also need browser actors for actor registry which enabled addons
+      // to register custom actors.
+      // TODO: the comment and implementation are out of sync here. See Bug 1420134.
+      DebuggerServer.registerAllActors();
+      // Enable being able to get child process actors
+      DebuggerServer.allowChromeProcess = true;
     }
-    return target;
+
+    function createLocalClient() {
+      return new DebuggerClient(DebuggerServer.connectPipe());
+    }
+
+    createLocalServer();
+    const client = createLocalClient();
+
+    // Connect the local client to the local server
+    await client.connect();
+
+    // Fetch the FrameTargetActor form
+    const response = await client.getTab({ tab });
+
+    return new TabTarget({
+      client,
+      form: response.tab,
+      // A local TabTarget will never perform chrome debugging.
+      chrome: false,
+      isBrowsingContext: true,
+      tab,
+    });
   },
 
   /**
    * Return a promise of a Target for a remote tab.
    * @param {Object} options
    *        The options object has the following properties:
    *        {
    *          form: the remote protocol form of a tab,
@@ -48,17 +110,17 @@ const TargetFactory = exports.TargetFact
    *        }
    *
    * @return A promise of a target object
    */
   forRemoteTab: function(options) {
     let targetPromise = promiseTargets.get(options);
     if (targetPromise == null) {
       const target = new TabTarget(options);
-      targetPromise = target.makeRemote().then(() => target);
+      targetPromise = target.attach().then(() => target);
       promiseTargets.set(options, targetPromise);
     }
     return targetPromise;
   },
 
   forWorker: function(workerClient) {
     let target = targets.get(workerClient);
     if (target == null) {
@@ -106,42 +168,62 @@ const TargetFactory = exports.TargetFact
  * - visible: The target is visible (for TargetTab, tab is selected)
  *
  * Comparing Targets: 2 instances of a Target object can point at the same
  * thing, so t1 !== t2 and t1 != t2 even when they represent the same object.
  * To compare to targets use 't1.equals(t2)'.
  */
 
 /**
- * A TabTarget represents a page living in a browser tab. Generally these will
- * be web pages served over http(s), but they don't have to be.
+ * A TabTarget represents a debuggable context. It can be a browser tab, a tab on
+ * a remote device, like a tab on Firefox for Android. But it can also be an add-on,
+ * as well as firefox parent process, or just one of its content process.
+ * A TabTarget is related to a given TargetActor, for which we pass the form as
+ * argument.
+ *
+ * For now, only workers are having a distinct Target class called WorkerTarget.
+ *
+ * @param {Object} form
+ *                 The TargetActor's form to be connected to.
+ * @param {DebuggerClient} client
+ *                 The DebuggerClient instance to be used to debug this target.
+ * @param {Boolean} chrome
+ *                  True, if we allow to see privileged resources like JSM, xpcom,
+ *                  frame scripts...
+ * @param {Boolean} isBrowsingContext (optional)
+ *                  To be set to True if the Target actor inherits from BrowsingContextActor.
+ *                  This argument is considered to be True is not passed.
+ * @param {xul:tab} tab (optional)
+ *                  If the target is a local Firefox tab, a reference to the firefox
+ *                  frontend tab object.
  */
-function TabTarget(tab) {
+function TabTarget({ form, client, chrome, isBrowsingContext = true, tab = null }) {
   EventEmitter.decorate(this);
   this.destroy = this.destroy.bind(this);
   this.activeTab = this.activeConsole = null;
-  // Only real tabs need initialization here. Placeholder objects for remote
-  // targets will be initialized after a makeRemote method call.
-  if (tab && !["client", "form", "chrome"].every(tab.hasOwnProperty, tab)) {
+
+  this._form = form;
+  this._url = form.url;
+  this._title = form.title;
+
+  this._client = client;
+  this._chrome = chrome;
+
+  // When debugging local tabs, we also have a reference to the Firefox tab
+  // This is used to:
+  // * distinguish local tabs from remote (see target.isLocalTab)
+  // * being able to hookup into Firefox UI (see Hosts)
+  if (tab) {
     this._tab = tab;
     this._setupListeners();
-  } else {
-    this._form = tab.form;
-    this._url = this._form.url;
-    this._title = this._form.title;
+  }
 
-    this._client = tab.client;
-    this._chrome = tab.chrome;
-  }
   // Default isBrowsingContext to true if not explicitly specified
-  if (typeof tab.isBrowsingContext == "boolean") {
-    this._isBrowsingContext = tab.isBrowsingContext;
-  } else {
-    this._isBrowsingContext = true;
-  }
+  this._isBrowsingContext = isBrowsingContext;
+
   // Cache of already created targed-scoped fronts
   // [typeName:string => Front instance]
   this.fronts = new Map();
 }
 
 exports.TabTarget = TabTarget;
 
 TabTarget.prototype = {
@@ -375,131 +457,96 @@ TabTarget.prototype = {
   get contentPrincipal() {
     if (!this.isLocalTab) {
       return null;
     }
     return this.tab.linkedBrowser.contentPrincipal;
   },
 
   /**
-   * Adds remote protocol capabilities to the target, so that it can be used
-   * for tools that support the Remote Debugging Protocol even for local
-   * connections.
+   * Attach the target and its console actor.
+   *
+   * This method will mainly call `attach` request on the target actor as well
+   * as the console actor.
+   * For webextension, it also preliminary converts addonTargetActor to a
+   * WebExtensionTargetActor.
    */
-  makeRemote: async function() {
-    if (this._remote) {
-      return this._remote;
-    }
-
-    if (this.isLocalTab) {
-      // Since a remote protocol connection will be made, let's start the
-      // DebuggerServer here, once and for all tools.
-      DebuggerServer.init();
-
-      // When connecting to a local tab, we only need the root actor.
-      // Then we are going to call DebuggerServer.connectToFrame and talk
-      // directly with actors living in the child process.
-      // We also need browser actors for actor registry which enabled addons
-      // to register custom actors.
-      // TODO: the comment and implementation are out of sync here. See Bug 1420134.
-      DebuggerServer.registerAllActors();
-      // Enable being able to get child process actors
-      DebuggerServer.allowChromeProcess = true;
-
-      this._client = new DebuggerClient(DebuggerServer.connectPipe());
-      // A local TabTarget will never perform chrome debugging.
-      this._chrome = false;
-    } else if (this._form.isWebExtension &&
-          this.client.mainRoot.traits.webExtensionAddonConnect) {
-      // The addonTargetActor form is related to a WebExtensionActor instance,
-      // which isn't a target actor on its own, it is an actor living in the parent
-      // process with access to the addon metadata, it can control the addon (e.g.
-      // reloading it) and listen to the AddonManager events related to the lifecycle of
-      // the addon (e.g. when the addon is disabled or uninstalled).
-      // To retrieve the target actor instance, we call its "connect" method, (which
-      // fetches the target actor form from a WebExtensionTargetActor instance).
-      const {form} = await this._client.request({
-        to: this._form.actor, type: "connect",
-      });
-
-      this._form = form;
-      this._url = form.url;
-      this._title = form.title;
+  attach() {
+    if (this._attach) {
+      return this._attach;
     }
 
-    this._setupRemoteListeners();
+    // Attach the target actor
+    const attachTarget = async () => {
+      const [response, tabClient] = await this._client.attachTarget(this._form.actor);
+      this.activeTab = tabClient;
+      this.threadActor = response.threadActor;
+    };
 
-    this._remote = new Promise((resolve, reject) => {
-      const attachTab = async () => {
-        try {
-          const [response, tabClient] = await this._client.attachTab(this._form.actor);
-          this.activeTab = tabClient;
-          this.threadActor = response.threadActor;
-        } catch (e) {
-          reject("Unable to attach to the tab: " + e);
-          return;
-        }
-        attachConsole();
-      };
+    // Attach the console actor
+    const attachConsole = async () => {
+      const [, consoleClient] = await this._client.attachConsole(
+        this._form.consoleActor, []);
+      this.activeConsole = consoleClient;
 
-      const onConsoleAttached = ([response, consoleClient]) => {
-        this.activeConsole = consoleClient;
-
-        this._onInspectObject = packet => this.emit("inspect-object", packet);
-        this.activeConsole.on("inspectObject", this._onInspectObject);
-
-        resolve(null);
-      };
+      this._onInspectObject = packet => this.emit("inspect-object", packet);
+      this.activeConsole.on("inspectObject", this._onInspectObject);
+    };
 
-      const attachConsole = () => {
-        this._client.attachConsole(this._form.consoleActor, [])
-          .then(onConsoleAttached, response => {
-            reject(
-              `Unable to attach to the console [${response.error}]: ${response.message}`);
-          });
-      };
+    this._attach = (async () => {
+      if (this._form.isWebExtension &&
+          this.client.mainRoot.traits.webExtensionAddonConnect) {
+        // The addonTargetActor form is related to a WebExtensionActor instance,
+        // which isn't a target actor on its own, it is an actor living in the parent
+        // process with access to the addon metadata, it can control the addon (e.g.
+        // reloading it) and listen to the AddonManager events related to the lifecycle of
+        // the addon (e.g. when the addon is disabled or uninstalled).
+        // To retrieve the target actor instance, we call its "connect" method, (which
+        // fetches the target actor form from a WebExtensionTargetActor instance).
+        const {form} = await this._client.request({
+          to: this._form.actor, type: "connect",
+        });
 
-      if (this.isLocalTab) {
-        this._client.connect()
-          .then(() => this._client.getTab({tab: this.tab}))
-          .then(response => {
-            this._form = response.tab;
-            this._url = this._form.url;
-            this._title = this._form.title;
+        this._form = form;
+        this._url = form.url;
+        this._title = form.title;
+      }
+
+      this._setupRemoteListeners();
 
-            attachTab();
-          }, e => reject(e));
-      } else if (this.isBrowsingContext) {
-        // In the remote debugging case, the protocol connection will have been
-        // already initialized in the connection screen code.
-        attachTab();
-      } else {
-        // AddonActor and chrome debugging on RootActor doesn't inherit from
-        // BrowsingContextTargetActor and doesn't need to be attached.
-        attachConsole();
+      // AddonActor and chrome debugging on RootActor don't inherit from
+      // BrowsingContextTargetActor (i.e. this.isBrowsingContext=false) and don't need
+      // to be attached.
+      if (this.isBrowsingContext) {
+        await attachTarget();
       }
-    });
 
-    return this._remote;
+      // But all target actor have a console actor to attach
+      return attachConsole();
+    })();
+
+    return this._attach;
   },
 
   /**
    * Listen to the different events.
    */
   _setupListeners: function() {
     this.tab.addEventListener("TabClose", this);
     this.tab.ownerDocument.defaultView.addEventListener("unload", this);
     this.tab.addEventListener("TabRemotenessChange", this);
   },
 
   /**
    * Teardown event listeners.
    */
   _teardownListeners: function() {
-    this._tab.ownerDocument.defaultView.removeEventListener("unload", this);
+    if (this._tab.ownerDocument.defaultView) {
+      this._tab.ownerDocument.defaultView.removeEventListener("unload", this);
+    }
     this._tab.removeEventListener("TabClose", this);
     this._tab.removeEventListener("TabRemotenessChange", this);
   },
 
   /**
    * Setup listeners for remote debugging, updating existing ones as necessary.
    */
   _setupRemoteListeners: function() {
@@ -593,24 +640,24 @@ TabTarget.prototype = {
     // remotenesschange events. But we should ignore them as at the end
     // the content doesn't change its remoteness.
     if (this._tab.isResponsiveDesignMode) {
       return;
     }
 
     // Save a reference to the tab as it will be nullified on destroy
     const tab = this._tab;
-    const onToolboxDestroyed = target => {
+    const onToolboxDestroyed = async (target) => {
       if (target != this) {
         return;
       }
       gDevTools.off("toolbox-destroyed", target);
 
       // Recreate a fresh target instance as the current one is now destroyed
-      const newTarget = TargetFactory.forTab(tab);
+      const newTarget = await TargetFactory.forTab(tab);
       gDevTools.showToolbox(newTarget);
     };
     gDevTools.on("toolbox-destroyed", onToolboxDestroyed);
   },
 
   /**
    * Target is not alive anymore.
    */
@@ -676,17 +723,17 @@ TabTarget.prototype = {
       promiseTargets.delete(this._form);
     }
 
     this.activeTab = null;
     this.activeConsole = null;
     this._client = null;
     this._tab = null;
     this._form = null;
-    this._remote = null;
+    this._attach = null;
     this._root = null;
     this._title = null;
     this._url = null;
     this.threadActor = null;
   },
 
   toString: function() {
     const id = this._tab ? this._tab : (this._form && this._form.actor);
@@ -790,17 +837,17 @@ WorkerTarget.prototype = {
     }
     return false;
   },
 
   getTrait: function() {
     return undefined;
   },
 
-  makeRemote: function() {
+  attach: function() {
     return Promise.resolve();
   },
 
   logErrorInPage: function() {
     // No-op.  See bug 1368680.
   },
 
   logWarningInPage: function() {
--- a/devtools/client/framework/test/browser_devtools_api.js
+++ b/devtools/client/framework/test/browser_devtools_api.js
@@ -10,17 +10,17 @@
 const toolId1 = "testtool1";
 const toolId2 = "testtool2";
 
 function test() {
   addTab("about:blank").then(runTests1);
 }
 
 // Test scenario 1: the tool definition build method returns a promise.
-function runTests1(tab) {
+async function runTests1(tab) {
   const toolDefinition = {
     id: toolId1,
     isTargetSupported: () => true,
     visibilityswitch: "devtools.test-tool.enabled",
     url: "about:blank",
     label: "someLabel",
     build: function(iframeWindow, toolbox) {
       const panel = createTestPanel(iframeWindow, toolbox);
@@ -31,17 +31,17 @@ function runTests1(tab) {
   ok(gDevTools, "gDevTools exists");
   ok(!gDevTools.getToolDefinitionMap().has(toolId1),
     "The tool is not registered");
 
   gDevTools.registerTool(toolDefinition);
   ok(gDevTools.getToolDefinitionMap().has(toolId1),
     "The tool is registered");
 
-  const target = TargetFactory.forTab(gBrowser.selectedTab);
+  const target = await TargetFactory.forTab(gBrowser.selectedTab);
 
   const events = {};
 
   // Check events on the gDevTools and toolbox objects.
   gDevTools.once(toolId1 + "-init", (toolbox, iframe) => {
     ok(iframe, "iframe argument available");
 
     toolbox.once(toolId1 + "-init", innerIframe => {
@@ -71,17 +71,17 @@ function runTests1(tab) {
     // Wait for unregisterTool to select the next tool before calling runTests2,
     // otherwise we will receive the wrong select event when waiting for
     // unregisterTool to select the next tool in continueTests below.
     toolbox.once("select", runTests2);
   });
 }
 
 // Test scenario 2: the tool definition build method returns panel instance.
-function runTests2() {
+async function runTests2() {
   const toolDefinition = {
     id: toolId2,
     isTargetSupported: () => true,
     visibilityswitch: "devtools.test-tool.enabled",
     url: "about:blank",
     label: "someLabel",
     build: function(iframeWindow, toolbox) {
       return createTestPanel(iframeWindow, toolbox);
@@ -90,17 +90,17 @@ function runTests2() {
 
   ok(!gDevTools.getToolDefinitionMap().has(toolId2),
     "The tool is not registered");
 
   gDevTools.registerTool(toolDefinition);
   ok(gDevTools.getToolDefinitionMap().has(toolId2),
     "The tool is registered");
 
-  const target = TargetFactory.forTab(gBrowser.selectedTab);
+  const target = await TargetFactory.forTab(gBrowser.selectedTab);
 
   const events = {};
 
   // Check events on the gDevTools and toolbox objects.
   gDevTools.once(toolId2 + "-init", (toolbox, iframe) => {
     ok(iframe, "iframe argument available");
 
     toolbox.once(toolId2 + "-init", innerIframe => {
@@ -178,18 +178,18 @@ var continueTests = async function(toolb
   info("Unregistering tool");
   gDevTools.unregisterTool(toolId2);
 
   info("Destroying toolbox");
   destroyToolbox(toolbox);
 };
 
 function destroyToolbox(toolbox) {
-  toolbox.destroy().then(function() {
-    const target = TargetFactory.forTab(gBrowser.selectedTab);
+  toolbox.destroy().then(async function() {
+    const target = await TargetFactory.forTab(gBrowser.selectedTab);
     ok(gDevTools._toolboxes.get(target) == null, "gDevTools doesn't know about target");
     ok(toolbox.target == null, "toolbox doesn't know about target.");
     finishUp();
   });
 }
 
 function finishUp() {
   gBrowser.removeCurrentTab();
--- a/devtools/client/framework/test/browser_devtools_api_destroy.js
+++ b/devtools/client/framework/test/browser_devtools_api_destroy.js
@@ -4,17 +4,17 @@
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 // Tests devtools API
 
 function test() {
   addTab("about:blank").then(runTests);
 }
 
-function runTests(aTab) {
+async function runTests(aTab) {
   const toolDefinition = {
     id: "testTool",
     visibilityswitch: "devtools.testTool.enabled",
     isTargetSupported: () => true,
     url: "about:blank",
     label: "someLabel",
     build: function(iframeWindow, toolbox) {
       return new Promise(resolve => {
@@ -29,17 +29,17 @@ function runTests(aTab) {
       });
     },
   };
 
   gDevTools.registerTool(toolDefinition);
 
   const collectedEvents = [];
 
-  const target = TargetFactory.forTab(aTab);
+  const target = await TargetFactory.forTab(aTab);
   gDevTools.showToolbox(target, toolDefinition.id).then(function(toolbox) {
     const panel = toolbox.getPanel(toolDefinition.id);
     ok(panel, "Tool open");
 
     gDevTools.once("toolbox-destroy", (toolbox, iframe) => {
       collectedEvents.push("toolbox-destroy");
     });
 
--- a/devtools/client/framework/test/browser_ignore_toolbox_network_requests.js
+++ b/devtools/client/framework/test/browser_ignore_toolbox_network_requests.js
@@ -10,17 +10,17 @@
 
 add_task(async function() {
   // TODO: This test tries to verify the normal behavior of the netmonitor and
   // therefore needs to avoid the explicit check for tests. Bug 1167188 will
   // allow us to remove this workaround.
   await pushPref("devtools.testing", false);
 
   let tab = await addTab(URL_ROOT + "doc_viewsource.html");
-  let target = TargetFactory.forTab(tab);
+  let target = await TargetFactory.forTab(tab);
   let toolbox = await gDevTools.showToolbox(target, "styleeditor");
   let panel = toolbox.getPanel("styleeditor");
 
   is(panel.UI.editors.length, 1, "correct number of editors opened");
 
   const monitor = await toolbox.selectTool("netmonitor");
   const { store } = monitor.panelWin;
 
--- a/devtools/client/framework/test/browser_keybindings_02.js
+++ b/devtools/client/framework/test/browser_keybindings_02.js
@@ -16,17 +16,17 @@ const L10N = new LocalizationHelper("dev
 
 function getZoomValue() {
   return parseFloat(Services.prefs.getCharPref("devtools.toolbox.zoomValue"));
 }
 
 add_task(async function() {
   info("Create a test tab and open the toolbox");
   const tab = await addTab(URL);
-  const target = TargetFactory.forTab(tab);
+  const target = await TargetFactory.forTab(tab);
   const toolbox = await gDevTools.showToolbox(target, "webconsole");
 
   const {RIGHT, BOTTOM} = Toolbox.HostType;
   for (const type of [RIGHT, BOTTOM, RIGHT]) {
     info("Switch to host type " + type);
     await toolbox.switchHost(type);
 
     info("Try to use the toolbox shortcuts");
--- a/devtools/client/framework/test/browser_keybindings_03.js
+++ b/devtools/client/framework/test/browser_keybindings_03.js
@@ -13,17 +13,17 @@ const URL = "data:text/html;charset=utf8
 var {Toolbox} = require("devtools/client/framework/toolbox");
 
 const {LocalizationHelper} = require("devtools/shared/l10n");
 const L10N = new LocalizationHelper("devtools/client/locales/toolbox.properties");
 
 add_task(async function() {
   info("Create a test tab and open the toolbox");
   const tab = await addTab(URL);
-  const target = TargetFactory.forTab(tab);
+  const target = await TargetFactory.forTab(tab);
   const toolbox = await gDevTools.showToolbox(target, "webconsole");
 
   const shortcut = L10N.getStr("toolbox.toggleHost.key");
 
   const {RIGHT, BOTTOM, WINDOW} = Toolbox.HostType;
   checkHostType(toolbox, BOTTOM, RIGHT);
 
   info("Switching from bottom to right");
--- a/devtools/client/framework/test/browser_menu_api.js
+++ b/devtools/client/framework/test/browser_menu_api.js
@@ -9,17 +9,17 @@
 
 const URL = "data:text/html;charset=utf8,test page for menu api";
 const Menu = require("devtools/client/framework/menu");
 const MenuItem = require("devtools/client/framework/menu-item");
 
 add_task(async function() {
   info("Create a test tab and open the toolbox");
   const tab = await addTab(URL);
-  const target = TargetFactory.forTab(tab);
+  const target = await TargetFactory.forTab(tab);
   const toolbox = await gDevTools.showToolbox(target, "webconsole");
 
   await testMenuItems();
   await testMenuPopup(toolbox);
   await testSubmenu(toolbox);
 });
 
 function testMenuItems() {
--- a/devtools/client/framework/test/browser_new_activation_workflow.js
+++ b/devtools/client/framework/test/browser_new_activation_workflow.js
@@ -3,18 +3,18 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 // Tests devtools API
 
 var toolbox, target;
 
 function test() {
-  addTab("about:blank").then(function(aTab) {
-    target = TargetFactory.forTab(gBrowser.selectedTab);
+  addTab("about:blank").then(async function(aTab) {
+    target = await TargetFactory.forTab(gBrowser.selectedTab);
     loadWebConsole(aTab).then(function() {
       console.log("loaded");
     });
   });
 }
 
 function loadWebConsole(aTab) {
   ok(gDevTools, "gDevTools exists");
@@ -40,19 +40,19 @@ function selectAndCheckById(id) {
   return toolbox.selectTool(id).then(function() {
     const tab = toolbox.doc.getElementById("toolbox-tab-" + id);
     is(tab.classList.contains("selected"), true, "The " + id + " tab is selected");
     is(tab.getAttribute("aria-pressed"), "true", "The " + id + " tab is pressed");
   });
 }
 
 function testToggle() {
-  toolbox.once("destroyed", () => {
+  toolbox.once("destroyed", async () => {
     // Cannot reuse a target after it's destroyed.
-    target = TargetFactory.forTab(gBrowser.selectedTab);
+    target = await TargetFactory.forTab(gBrowser.selectedTab);
     gDevTools.showToolbox(target, "styleeditor").then(function(aToolbox) {
       toolbox = aToolbox;
       is(toolbox.currentToolId, "styleeditor", "The style editor is selected");
       finishUp();
     });
   });
 
   toolbox.destroy();
--- a/devtools/client/framework/test/browser_target_events.js
+++ b/devtools/client/framework/test/browser_target_events.js
@@ -1,17 +1,17 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 add_task(async function() {
   gBrowser.selectedTab = BrowserTestUtils.addTab(gBrowser);
   await BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
 
-  const target = TargetFactory.forTab(gBrowser.selectedTab);
-  await target.makeRemote();
+  const target = await TargetFactory.forTab(gBrowser.selectedTab);
+  await target.attach();
   is(target.tab, gBrowser.selectedTab, "Target linked to the right tab.");
 
   const willNavigate = once(target, "will-navigate");
   const navigate = once(target, "navigate");
   ContentTask.spawn(gBrowser.selectedBrowser, null, () => {
     content.location = "data:text/html,<meta charset='utf8'/>test navigation";
   });
   await willNavigate;
--- a/devtools/client/framework/test/browser_target_support.js
+++ b/devtools/client/framework/test/browser_target_support.js
@@ -5,17 +5,17 @@
 
 // Test support methods on Target, such as `hasActor`, `getActorDescription`,
 // `actorHasMethod` and `getTrait`.
 
 var { WebAudioFront } =
   require("devtools/shared/fronts/webaudio");
 
 async function testTarget(client, target) {
-  await target.makeRemote();
+  await target.attach();
 
   is(target.hasActor("timeline"), true, "target.hasActor() true when actor exists.");
   is(target.hasActor("webaudio"), true, "target.hasActor() true when actor exists.");
   is(target.hasActor("notreal"), false, "target.hasActor() false when actor does not exist.");
   // Create a front to ensure the actor is loaded
   new WebAudioFront(target.client, target.form);
 
   let desc = await target.getActorDescription("webaudio");
--- a/devtools/client/framework/test/browser_toolbox_custom_host.js
+++ b/devtools/client/framework/test/browser_toolbox_custom_host.js
@@ -25,17 +25,17 @@ add_task(async function() {
     }
     window.addEventListener("message", onMessage);
   });
 
   let iframe = document.createElement("iframe");
   document.documentElement.appendChild(iframe);
 
   const tab = await addTab(TEST_URL);
-  let target = TargetFactory.forTab(tab);
+  let target = await TargetFactory.forTab(tab);
   const options = { customIframe: iframe };
   let toolbox = await gDevTools.showToolbox(target, null, Toolbox.HostType.CUSTOM, options);
 
   is(toolbox.win.top, window, "Toolbox is included in browser.xul");
   is(toolbox.doc, iframe.contentDocument, "Toolbox is in the custom iframe");
 
   iframe.remove();
   await toolbox.destroy();
--- a/devtools/client/framework/test/browser_toolbox_dynamic_registration.js
+++ b/devtools/client/framework/test/browser_toolbox_dynamic_registration.js
@@ -3,18 +3,18 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 const TEST_URL = "data:text/html,test for dynamically registering and unregistering tools";
 
 var toolbox;
 
 function test() {
-  addTab(TEST_URL).then(tab => {
-    const target = TargetFactory.forTab(tab);
+  addTab(TEST_URL).then(async tab => {
+    const target = await TargetFactory.forTab(tab);
     gDevTools.showToolbox(target).then(testRegister);
   });
 }
 
 function testRegister(aToolbox) {
   toolbox = aToolbox;
   gDevTools.once("tool-registered", toolRegistered);
 
--- a/devtools/client/framework/test/browser_toolbox_getpanelwhenready.js
+++ b/devtools/client/framework/test/browser_toolbox_getpanelwhenready.js
@@ -7,17 +7,17 @@
 // resolutions regardless of whether it has opened first.
 
 var toolbox = null;
 
 const URL = "data:text/html;charset=utf8,test for getPanelWhenReady";
 
 add_task(async function() {
   const tab = await addTab(URL);
-  const target = TargetFactory.forTab(tab);
+  const target = await TargetFactory.forTab(tab);
   toolbox = await gDevTools.showToolbox(target);
 
   const debuggerPanelPromise = toolbox.getPanelWhenReady("jsdebugger");
   await toolbox.selectTool("jsdebugger");
   const debuggerPanel = await debuggerPanelPromise;
 
   is(debuggerPanel, toolbox.getPanel("jsdebugger"),
       "The debugger panel from getPanelWhenReady before loading is the actual panel");
--- a/devtools/client/framework/test/browser_toolbox_highlight.js
+++ b/devtools/client/framework/test/browser_toolbox_highlight.js
@@ -12,17 +12,17 @@ var toolbox = null;
 function test() {
   (async function() {
     const URL = "data:text/plain;charset=UTF-8,Nothing to see here, move along";
 
     const TOOL_ID_1 = "jsdebugger";
     const TOOL_ID_2 = "webconsole";
     await addTab(URL);
 
-    const target = TargetFactory.forTab(gBrowser.selectedTab);
+    const target = await TargetFactory.forTab(gBrowser.selectedTab);
     toolbox = await gDevTools.showToolbox(target, TOOL_ID_1, Toolbox.HostType.BOTTOM);
 
     // select tool 2
     await toolbox.selectTool(TOOL_ID_2);
     // and highlight the first one
     await highlightTab(TOOL_ID_1);
     // to see if it has the proper class.
     await checkHighlighted(TOOL_ID_1);
--- a/devtools/client/framework/test/browser_toolbox_hosts.js
+++ b/devtools/client/framework/test/browser_toolbox_hosts.js
@@ -9,17 +9,17 @@ var {Toolbox} = require("devtools/client
 var {LEFT, RIGHT, BOTTOM, WINDOW} = Toolbox.HostType;
 var toolbox, target;
 
 const URL = "data:text/html;charset=utf8,test for opening toolbox in different hosts";
 
 add_task(async function runTest() {
   info("Create a test tab and open the toolbox");
   const tab = await addTab(URL);
-  target = TargetFactory.forTab(tab);
+  target = await TargetFactory.forTab(tab);
   toolbox = await gDevTools.showToolbox(target, "webconsole");
 
   await testBottomHost();
   await testLeftHost();
   await testRightHost();
   await testWindowHost();
   await testToolSelect();
   await testDestroy();
@@ -90,17 +90,17 @@ async function testWindowHost() {
 
 async function testToolSelect() {
   // make sure we can load a tool after switching hosts
   await toolbox.selectTool("inspector");
 }
 
 async function testDestroy() {
   await toolbox.destroy();
-  target = TargetFactory.forTab(gBrowser.selectedTab);
+  target = await TargetFactory.forTab(gBrowser.selectedTab);
   toolbox = await gDevTools.showToolbox(target);
 }
 
 function testRememberHost() {
   // last host was the window - make sure it's the same when re-opening
   is(toolbox.hostType, WINDOW, "host remembered");
 
   const win = Services.wm.getMostRecentWindow("devtools:toolbox");
--- a/devtools/client/framework/test/browser_toolbox_hosts_size.js
+++ b/devtools/client/framework/test/browser_toolbox_hosts_size.js
@@ -14,17 +14,18 @@ add_task(async function() {
   // Set size prefs to make the hosts way too big, so that the size has
   // to be clamped to fit into the browser window.
   Services.prefs.setIntPref("devtools.toolbox.footer.height", 10000);
   Services.prefs.setIntPref("devtools.toolbox.sidebar.width", 10000);
 
   const tab = await addTab(URL);
   const nbox = gBrowser.getNotificationBox();
   const {clientHeight: nboxHeight, clientWidth: nboxWidth} = nbox;
-  const toolbox = await gDevTools.showToolbox(TargetFactory.forTab(tab));
+  const target = await TargetFactory.forTab(tab);
+  const toolbox = await gDevTools.showToolbox(target);
 
   is(nbox.clientHeight, nboxHeight, "Opening the toolbox hasn't changed the height of the nbox");
   is(nbox.clientWidth, nboxWidth, "Opening the toolbox hasn't changed the width of the nbox");
 
   let iframe = document.getAnonymousElementByAttribute(nbox, "class", "devtools-toolbox-bottom-iframe");
   is(iframe.clientHeight, nboxHeight - 25, "The iframe fits within the available space");
 
   await toolbox.switchHost(Toolbox.HostType.RIGHT);
@@ -39,17 +40,18 @@ add_task(async function() {
   // Set size prefs to something reasonable, so we can check to make sure
   // they are being set properly.
   Services.prefs.setIntPref("devtools.toolbox.footer.height", 100);
   Services.prefs.setIntPref("devtools.toolbox.sidebar.width", 100);
 
   const tab = await addTab(URL);
   const nbox = gBrowser.getNotificationBox();
   const {clientHeight: nboxHeight, clientWidth: nboxWidth} = nbox;
-  const toolbox = await gDevTools.showToolbox(TargetFactory.forTab(tab));
+  const target = await TargetFactory.forTab(tab);
+  const toolbox = await gDevTools.showToolbox(target);
 
   is(nbox.clientHeight, nboxHeight, "Opening the toolbox hasn't changed the height of the nbox");
   is(nbox.clientWidth, nboxWidth, "Opening the toolbox hasn't changed the width of the nbox");
 
   let iframe = document.getAnonymousElementByAttribute(nbox, "class", "devtools-toolbox-bottom-iframe");
   is(iframe.clientHeight, 100, "The iframe is resized properly");
 
   await toolbox.switchHost(Toolbox.HostType.RIGHT);
--- a/devtools/client/framework/test/browser_toolbox_hosts_telemetry.js
+++ b/devtools/client/framework/test/browser_toolbox_hosts_telemetry.js
@@ -10,17 +10,17 @@ const {LEFT, RIGHT, BOTTOM, WINDOW} = To
 
 const URL = "data:text/html;charset=utf8,browser_toolbox_hosts_telemetry.js";
 
 add_task(async function() {
   startTelemetry();
 
   info("Create a test tab and open the toolbox");
   const tab = await addTab(URL);
-  const target = TargetFactory.forTab(tab);
+  const target = await TargetFactory.forTab(tab);
   const toolbox = await gDevTools.showToolbox(target, "webconsole");
 
   await changeToolboxHost(toolbox);
   await checkResults();
 });
 
 async function changeToolboxHost(toolbox) {
   info("Switch toolbox host");
--- a/devtools/client/framework/test/browser_toolbox_options.js
+++ b/devtools/client/framework/test/browser_toolbox_options.js
@@ -13,17 +13,17 @@ const {LocalizationHelper} = require("de
 const L10N = new LocalizationHelper("devtools/client/locales/toolbox.properties");
 const {PrefObserver} = require("devtools/client/shared/prefs");
 
 add_task(async function() {
   const URL = "data:text/html;charset=utf8,test for dynamically registering " +
               "and unregistering tools";
   registerNewTool();
   const tab = await addTab(URL);
-  const target = TargetFactory.forTab(tab);
+  const target = await TargetFactory.forTab(tab);
   toolbox = await gDevTools.showToolbox(target);
 
   doc = toolbox.doc;
   await registerNewPerToolboxTool();
   await testSelectTool();
   await testOptionsShortcut();
   await testOptions();
   await testToggleTools();
--- a/devtools/client/framework/test/browser_toolbox_options_disable_buttons.js
+++ b/devtools/client/framework/test/browser_toolbox_options_disable_buttons.js
@@ -10,33 +10,33 @@ let TEST_URL = "data:text/html;charset=u
 
 // The frames button is only shown if the page has at least one iframe so we
 // need to add one to the test page.
 TEST_URL += "<iframe src=\"data:text/plain,iframe\"></iframe>";
 
 var doc = null, toolbox = null, panelWin = null, modifiedPrefs = [];
 
 function test() {
-  addTab(TEST_URL).then(tab => {
-    const target = TargetFactory.forTab(tab);
+  addTab(TEST_URL).then(async (tab) => {
+    const target = await TargetFactory.forTab(tab);
     gDevTools.showToolbox(target)
       .then(testSelectTool)
       .then(testToggleToolboxButtons)
       .then(testPrefsAreRespectedWhenReopeningToolbox)
       .then(cleanup, errorHandler);
   });
 }
 
-function testPrefsAreRespectedWhenReopeningToolbox() {
-  const target = TargetFactory.forTab(gBrowser.selectedTab);
+async function testPrefsAreRespectedWhenReopeningToolbox() {
+  const target = await TargetFactory.forTab(gBrowser.selectedTab);
 
   return new Promise(resolve => {
     info("Closing toolbox to test after reopening");
-    gDevTools.closeToolbox(target).then(() => {
-      const tabTarget = TargetFactory.forTab(gBrowser.selectedTab);
+    gDevTools.closeToolbox(target).then(async () => {
+      const tabTarget = await TargetFactory.forTab(gBrowser.selectedTab);
       gDevTools.showToolbox(tabTarget)
         .then(testSelectTool)
         .then(() => {
           info("Toolbox has been reopened.  Checking UI state.");
           testPreferenceAndUIStateIsConsistent();
           resolve();
         });
     });
--- a/devtools/client/framework/test/browser_toolbox_options_disable_cache-02.js
+++ b/devtools/client/framework/test/browser_toolbox_options_disable_cache-02.js
@@ -31,17 +31,17 @@ add_task(async function() {
   await setDisableCacheCheckboxChecked(tabs[0], true);
 
   // Open toolbox in tab 2 and ensure the cache is then disabled.
   tabs[2].toolbox = await gDevTools.showToolbox(tabs[2].target, "options");
   await checkCacheEnabled(tabs[2], false);
 
   // Close toolbox in tab 2 and ensure the cache is enabled again
   await tabs[2].toolbox.destroy();
-  tabs[2].target = TargetFactory.forTab(tabs[2].tab);
+  tabs[2].target = await TargetFactory.forTab(tabs[2].tab);
   await checkCacheEnabled(tabs[2], true);
 
   // Open toolbox in tab 2 and ensure the cache is then disabled.
   tabs[2].toolbox = await gDevTools.showToolbox(tabs[2].target, "options");
   await checkCacheEnabled(tabs[2], false);
 
   // Check the checkbox in tab 2 and ensure cache is enabled for all tabs.
   await setDisableCacheCheckboxChecked(tabs[2], false);
--- a/devtools/client/framework/test/browser_toolbox_options_disable_js.js
+++ b/devtools/client/framework/test/browser_toolbox_options_disable_js.js
@@ -3,18 +3,18 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 // Tests that disabling JavaScript for a tab works as it should.
 
 const TEST_URI = URL_ROOT + "browser_toolbox_options_disable_js.html";
 
 function test() {
-  addTab(TEST_URI).then(tab => {
-    const target = TargetFactory.forTab(tab);
+  addTab(TEST_URI).then(async (tab) => {
+    const target = await TargetFactory.forTab(tab);
     gDevTools.showToolbox(target).then(testSelectTool);
   });
 }
 
 function testSelectTool(toolbox) {
   toolbox.once("options-selected", () => testToggleJS(toolbox));
   toolbox.selectTool("options");
 }
--- a/devtools/client/framework/test/browser_toolbox_options_enable_serviceworkers_testing.js
+++ b/devtools/client/framework/test/browser_toolbox_options_enable_serviceworkers_testing.js
@@ -24,18 +24,18 @@ function test() {
   SpecialPowers.pushPrefEnv({"set": [
     ["dom.serviceWorkers.exemptFromPerDomainMax", true],
     ["dom.serviceWorkers.enabled", true],
     ["dom.serviceWorkers.testing.enabled", false]
   ]}, init);
 }
 
 function init() {
-  addTab(TEST_URI).then(tab => {
-    const target = TargetFactory.forTab(tab);
+  addTab(TEST_URI).then(async tab => {
+    const target = await TargetFactory.forTab(tab);
     const linkedBrowser = tab.linkedBrowser;
 
     loadFrameScriptUtils(linkedBrowser);
     linkedBrowser.messageManager.loadFrameScript(FRAME_SCRIPT_URL, false);
 
     gDevTools.showToolbox(target).then(testSelectTool);
   });
 }
--- a/devtools/client/framework/test/browser_toolbox_options_frames_button.js
+++ b/devtools/client/framework/test/browser_toolbox_options_frames_button.js
@@ -11,17 +11,17 @@ const TEST_URL = "data:text/html;charset
 const TEST_URL_FRAMES = TEST_URL + "<iframe src=\"data:text/plain,iframe\"></iframe>";
 const FRAME_BUTTON_PREF = "devtools.command-button-frames.enabled";
 
 add_task(async function() {
   // Hide the button by default.
   await pushPref(FRAME_BUTTON_PREF, false);
 
   const tab = await addTab(TEST_URL);
-  const target = TargetFactory.forTab(tab);
+  const target = await TargetFactory.forTab(tab);
 
   info("Open the toolbox on the Options panel");
   const toolbox = await gDevTools.showToolbox(target, "options");
   const doc = toolbox.doc;
 
   const optionsPanel = toolbox.getCurrentPanel();
 
   let framesButton = doc.getElementById("command-button-frames");
--- a/devtools/client/framework/test/browser_toolbox_options_multiple_tabs.js
+++ b/devtools/client/framework/test/browser_toolbox_options_multiple_tabs.js
@@ -13,17 +13,17 @@ add_task(async function() {
   tab2 = await openToolboxOptionsInNewTab();
 
   await testToggleTools();
   await cleanup();
 });
 
 async function openToolboxOptionsInNewTab() {
   const tab = await addTab(URL);
-  const target = TargetFactory.forTab(tab);
+  const target = await TargetFactory.forTab(tab);
   const toolbox = await gDevTools.showToolbox(target);
   const doc = toolbox.doc;
   const panel = await toolbox.selectTool("options");
   const { id } = panel.panelDoc.querySelector(
     "#default-tools-box input[type=checkbox]:not([data-unsupported]):not([checked])");
 
   return {
     tab,
--- a/devtools/client/framework/test/browser_toolbox_raise.js
+++ b/devtools/client/framework/test/browser_toolbox_raise.js
@@ -5,19 +5,19 @@
 
 const TEST_URL = "data:text/html,test for opening toolbox in different hosts";
 
 var {Toolbox} = require("devtools/client/framework/toolbox");
 
 var toolbox, tab1, tab2;
 
 function test() {
-  addTab(TEST_URL).then(tab => {
+  addTab(TEST_URL).then(async (tab) => {
     tab2 = BrowserTestUtils.addTab(gBrowser);
-    const target = TargetFactory.forTab(tab);
+    const target = await TargetFactory.forTab(tab);
     gDevTools.showToolbox(target)
              .then(testBottomHost, console.error)
              .catch(console.error);
   });
 }
 
 function testBottomHost(aToolbox) {
   toolbox = aToolbox;
--- a/devtools/client/framework/test/browser_toolbox_ready.js
+++ b/devtools/client/framework/test/browser_toolbox_ready.js
@@ -2,17 +2,17 @@
 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 const TEST_URL = "data:text/html,test for toolbox being ready";
 
 add_task(async function() {
   const tab = await addTab(TEST_URL);
-  const target = TargetFactory.forTab(tab);
+  const target = await TargetFactory.forTab(tab);
 
   const toolbox = await gDevTools.showToolbox(target, "webconsole");
   ok(toolbox.isReady, "toolbox isReady is set");
   ok(toolbox.threadClient, "toolbox has a thread client");
 
   const toolbox2 = await gDevTools.showToolbox(toolbox.target, toolbox.toolId);
   is(toolbox2, toolbox, "same toolbox");
 
--- a/devtools/client/framework/test/browser_toolbox_selected_tool_unavailable.js
+++ b/devtools/client/framework/test/browser_toolbox_selected_tool_unavailable.js
@@ -23,26 +23,26 @@ const testToolDefinition = {
         panelDoc: iframeWindow.document
     };
   }
 };
 
 add_task(async function() {
   gDevTools.registerTool(testToolDefinition);
   let tab = await addTab("about:blank");
-  let target = TargetFactory.forTab(tab);
+  let target = await TargetFactory.forTab(tab);
 
   let toolbox = await gDevTools.showToolbox(target, testToolDefinition.id);
   is(toolbox.currentToolId, "testTool", "test-tool was selected");
   await gDevTools.closeToolbox(target);
 
   // Make the previously selected tool unavailable.
   testToolDefinition.isTargetSupported = () => false;
 
-  target = TargetFactory.forTab(tab);
+  target = await TargetFactory.forTab(tab);
   toolbox = await gDevTools.showToolbox(target);
   is(toolbox.currentToolId, "webconsole", "web console was selected");
 
   await gDevTools.closeToolbox(target);
   gDevTools.unregisterTool(testToolDefinition.id);
   tab = toolbox = target = null;
   gBrowser.removeCurrentTab();
 });
--- a/devtools/client/framework/test/browser_toolbox_sidebar.js
+++ b/devtools/client/framework/test/browser_toolbox_sidebar.js
@@ -32,18 +32,18 @@ function test() {
           });
         });
       });
     },
   };
 
   gDevTools.registerTool(toolDefinition);
 
-  addTab("about:blank").then(function(aTab) {
-    const target = TargetFactory.forTab(aTab);
+  addTab("about:blank").then(async function(aTab) {
+    const target = await TargetFactory.forTab(aTab);
     gDevTools.showToolbox(target, toolDefinition.id).then(function(toolbox) {
       const panel = toolbox.getPanel(toolDefinition.id);
       panel.toolbox = toolbox;
       ok(true, "Tool open");
 
       const tabbox = panel.panelDoc.getElementById("sidebar");
       panel.sidebar = new ToolSidebar(tabbox, panel, "testbug865688", true);
 
--- a/devtools/client/framework/test/browser_toolbox_sidebar_events.js
+++ b/devtools/client/framework/test/browser_toolbox_sidebar_events.js
@@ -28,18 +28,18 @@ function test() {
           });
         });
       });
     },
   };
 
   gDevTools.registerTool(toolDefinition);
 
-  addTab("about:blank").then(function(aTab) {
-    const target = TargetFactory.forTab(aTab);
+  addTab("about:blank").then(async function(aTab) {
+    const target = await TargetFactory.forTab(aTab);
     gDevTools.showToolbox(target, toolDefinition.id).then(function(toolbox) {
       const panel = toolbox.getPanel(toolDefinition.id);
       ok(true, "Tool open");
 
       panel.once("sidebar-created", function() {
         collectedEvents.push("sidebar-created");
       });
 
--- a/devtools/client/framework/test/browser_toolbox_sidebar_existing_tabs.js
+++ b/devtools/client/framework/test/browser_toolbox_sidebar_existing_tabs.js
@@ -23,17 +23,17 @@ const testToolDefinition = {
       panelDoc: iframeWindow.document,
     });
   }
 };
 
 add_task(async function() {
   const tab = await addTab("about:blank");
 
-  const target = TargetFactory.forTab(tab);
+  const target = await TargetFactory.forTab(tab);
 
   gDevTools.registerTool(testToolDefinition);
   const toolbox = await gDevTools.showToolbox(target, testToolDefinition.id);
 
   const toolPanel = toolbox.getPanel(testToolDefinition.id);
   const tabbox = toolPanel.panelDoc.getElementById("sidebar");
 
   info("Creating the sidebar widget");
--- a/devtools/client/framework/test/browser_toolbox_sidebar_overflow_menu.js
+++ b/devtools/client/framework/test/browser_toolbox_sidebar_overflow_menu.js
@@ -23,17 +23,17 @@ const testToolDefinition = {
       destroy: () => {},
       panelDoc: iframeWindow.document,
     };
   }
 };
 
 add_task(async function() {
   const tab = await addTab("about:blank");
-  const target = TargetFactory.forTab(tab);
+  const target = await TargetFactory.forTab(tab);
 
   gDevTools.registerTool(testToolDefinition);
   const toolbox = await gDevTools.showToolbox(target, testToolDefinition.id);
 
   const toolPanel = toolbox.getPanel(testToolDefinition.id);
   const tabbox = toolPanel.panelDoc.getElementById("sidebar");
 
   info("Creating the sidebar widget");
--- a/devtools/client/framework/test/browser_toolbox_split_console.js
+++ b/devtools/client/framework/test/browser_toolbox_split_console.js
@@ -11,17 +11,17 @@
 
 let gToolbox = null;
 let panelWin = null;
 
 const URL = "data:text/html;charset=utf8,test split console key delegation";
 
 add_task(async function() {
   const tab = await addTab(URL);
-  const target = TargetFactory.forTab(tab);
+  const target = await TargetFactory.forTab(tab);
   gToolbox = await gDevTools.showToolbox(target, "jsdebugger");
   panelWin = gToolbox.getPanel("jsdebugger").panelWin;
 
   await gToolbox.openSplitConsole();
   await testIsSplitConsoleFocused();
   await testUseKeyWithSplitConsole();
   await testUseKeyWithSplitConsoleWrongTool();
 
--- a/devtools/client/framework/test/browser_toolbox_tabsswitch_shortcuts.js
+++ b/devtools/client/framework/test/browser_toolbox_tabsswitch_shortcuts.js
@@ -9,18 +9,18 @@ requestLongerTimeout(2);
 
 var {Toolbox} = require("devtools/client/framework/toolbox");
 
 const {LocalizationHelper} = require("devtools/shared/l10n");
 const L10N = new LocalizationHelper("devtools/client/locales/toolbox.properties");
 
 add_task(async function() {
   const tab = await addTab("about:blank");
-  const target = TargetFactory.forTab(tab);
-  await target.makeRemote();
+  const target = await TargetFactory.forTab(tab);
+  await target.attach();
 
   const toolIDs = gDevTools.getToolDefinitionArray()
                          .filter(
                            def =>
                              def.isTargetSupported(target) &&
                              def.id !== "options"
                          )
                          .map(def => def.id);
--- a/devtools/client/framework/test/browser_toolbox_telemetry_activate_splitconsole.js
+++ b/devtools/client/framework/test/browser_toolbox_telemetry_activate_splitconsole.js
@@ -54,17 +54,17 @@ add_task(async function() {
   // Let's reset the counts.
   Services.telemetry.clearEvents();
 
   // Ensure no events have been logged
   const snapshot = Services.telemetry.snapshotEvents(OPTOUT, true);
   ok(!snapshot.parent, "No events have been logged for the main process");
 
   const tab = await addTab(URL);
-  const target = TargetFactory.forTab(tab);
+  const target = await TargetFactory.forTab(tab);
   const toolbox = await gDevTools.showToolbox(target, "inspector");
 
   await toolbox.openSplitConsole();
   await toolbox.closeSplitConsole();
   await toolbox.openSplitConsole();
   await toolbox.closeSplitConsole();
 
   await checkResults();
--- a/devtools/client/framework/test/browser_toolbox_telemetry_close.js
+++ b/devtools/client/framework/test/browser_toolbox_telemetry_close.js
@@ -44,17 +44,17 @@ add_task(async function() {
   await openAndCloseToolbox("webconsole", RIGHT);
   await openAndCloseToolbox("webconsole", BOTTOM);
 
   checkResults();
 });
 
 async function openAndCloseToolbox(toolId, host) {
   const tab = await addTab(URL);
-  const target = TargetFactory.forTab(tab);
+  const target = await TargetFactory.forTab(tab);
   const toolbox = await gDevTools.showToolbox(target, toolId);
 
   await toolbox.switchHost(host);
   await toolbox.destroy();
 }
 
 function checkResults() {
   const snapshot = Services.telemetry.snapshotEvents(OPTOUT, true);
--- a/devtools/client/framework/test/browser_toolbox_telemetry_enter.js
+++ b/devtools/client/framework/test/browser_toolbox_telemetry_enter.js
@@ -91,17 +91,17 @@ add_task(async function() {
   // Let's reset the counts.
   Services.telemetry.clearEvents();
 
   // Ensure no events have been logged
   const snapshot = Services.telemetry.snapshotEvents(OPTOUT, true);
   ok(!snapshot.parent, "No events have been logged for the main process");
 
   const tab = await addTab(URL);
-  const target = TargetFactory.forTab(tab);
+  const target = await TargetFactory.forTab(tab);
 
   // Set up some cached messages for the web console.
   await ContentTask.spawn(tab.linkedBrowser, {}, () => {
     content.console.log("test 1");
     content.console.log("test 2");
     content.console.log("test 3");
     content.console.log("test 4");
     content.console.log("test 5");
--- a/devtools/client/framework/test/browser_toolbox_telemetry_exit.js
+++ b/devtools/client/framework/test/browser_toolbox_telemetry_exit.js
@@ -78,17 +78,17 @@ add_task(async function() {
   // Let's reset the counts.
   Services.telemetry.clearEvents();
 
   // Ensure no events have been logged
   const snapshot = Services.telemetry.snapshotEvents(OPTOUT, true);
   ok(!snapshot.parent, "No events have been logged for the main process");
 
   const tab = await addTab(URL);
-  const target = TargetFactory.forTab(tab);
+  const target = await TargetFactory.forTab(tab);
 
   // Open the toolbox
   await gDevTools.showToolbox(target, "inspector");
 
   // Switch between a few tools
   await gDevTools.showToolbox(target, "jsdebugger");
   await gDevTools.showToolbox(target, "styleeditor");
   await gDevTools.showToolbox(target, "netmonitor");
--- a/devtools/client/framework/test/browser_toolbox_theme_registration.js
+++ b/devtools/client/framework/test/browser_toolbox_theme_registration.js
@@ -9,17 +9,17 @@
 const CHROME_URL = "chrome://mochitests/content/browser/devtools/client/framework/test/";
 const TEST_THEME_NAME = "test-theme";
 const LIGHT_THEME_NAME = "light";
 
 var toolbox;
 
 add_task(async function themeRegistration() {
   const tab = await addTab("data:text/html,test");
-  const target = TargetFactory.forTab(tab);
+  const target = await TargetFactory.forTab(tab);
   toolbox = await gDevTools.showToolbox(target, "options");
 
   const themeId = await new Promise(resolve => {
     gDevTools.once("theme-registered", registeredThemeId => {
       resolve(registeredThemeId);
     });
 
     gDevTools.registerTheme({
--- a/devtools/client/framework/test/browser_toolbox_toggle.js
+++ b/devtools/client/framework/test/browser_toolbox_toggle.js
@@ -29,26 +29,27 @@ add_task(async function() {
 
   // Test with F12 ; no modifiers
   info("Test toggle using F12");
   await testToggle("VK_F12", {});
 });
 
 async function testToggle(key, modifiers) {
   const tab = await addTab(URL + " ; key : '" + key + "'");
-  await gDevTools.showToolbox(TargetFactory.forTab(tab));
+  const target = await TargetFactory.forTab(tab);
+  await gDevTools.showToolbox(target);
 
   await testToggleDockedToolbox(tab, key, modifiers);
   await testToggleDetachedToolbox(tab, key, modifiers);
 
   await cleanup();
 }
 
 async function testToggleDockedToolbox(tab, key, modifiers) {
-  const toolbox = getToolboxForTab(tab);
+  const toolbox = await getToolboxForTab(tab);
 
   isnot(toolbox.hostType, Toolbox.HostType.WINDOW,
     "Toolbox is docked in the main window");
 
   info("verify docked toolbox is destroyed when using toggle key");
   const onToolboxDestroyed = gDevTools.once("toolbox-destroyed");
   EventUtils.synthesizeKey(key, modifiers);
   await onToolboxDestroyed;
@@ -57,17 +58,17 @@ async function testToggleDockedToolbox(t
   info("verify new toolbox is created when using toggle key");
   const onToolboxReady = gDevTools.once("toolbox-ready");
   EventUtils.synthesizeKey(key, modifiers);
   await onToolboxReady;
   ok(true, "Toolbox is created by using when toggle key");
 }
 
 async function testToggleDetachedToolbox(tab, key, modifiers) {
-  const toolbox = getToolboxForTab(tab);
+  const toolbox = await getToolboxForTab(tab);
 
   info("change the toolbox hostType to WINDOW");
 
   await toolbox.switchHost(Toolbox.HostType.WINDOW);
   is(toolbox.hostType, Toolbox.HostType.WINDOW,
     "Toolbox opened on separate window");
 
   info("Wait for focus on the toolbox window");
@@ -92,16 +93,17 @@ async function testToggleDetachedToolbox
     "own window");
 
   const onToolboxDestroyed = gDevTools.once("toolbox-destroyed");
   EventUtils.synthesizeKey(key, modifiers, toolboxWindow);
   await onToolboxDestroyed;
   ok(true, "Toolbox destroyed");
 }
 
-function getToolboxForTab(tab) {
-  return gDevTools.getToolbox(TargetFactory.forTab(tab));
+async function getToolboxForTab(tab) {
+  const target = await TargetFactory.forTab(tab);
+  return gDevTools.getToolbox(target);
 }
 
 function cleanup() {
   Services.prefs.setCharPref("devtools.toolbox.host", Toolbox.HostType.BOTTOM);
   gBrowser.removeCurrentTab();
 }
--- a/devtools/client/framework/test/browser_toolbox_tool_ready.js
+++ b/devtools/client/framework/test/browser_toolbox_tool_ready.js
@@ -29,16 +29,16 @@ function performChecks(target) {
     await toolbox.destroy();
   })();
 }
 
 function test() {
   (async function() {
     toggleAllTools(true);
     const tab = await addTab("about:blank");
-    const target = TargetFactory.forTab(tab);
-    await target.makeRemote();
+    const target = await TargetFactory.forTab(tab);
+    await target.attach();
     await performChecks(target);
     gBrowser.removeCurrentTab();
     toggleAllTools(false);
     finish();
   })();
 }
--- a/devtools/client/framework/test/browser_toolbox_tools_per_toolbox_registration.js
+++ b/devtools/client/framework/test/browser_toolbox_tools_per_toolbox_registration.js
@@ -13,18 +13,18 @@ const TEST_URL = `data:text/html,<!DOCTY
     </body>
   </html>`;
 
 const TOOL_ID = "test-toolbox-tool";
 var toolbox;
 var target;
 
 function test() {
-  addTab(TEST_URL).then(tab => {
-    target = TargetFactory.forTab(tab);
+  addTab(TEST_URL).then(async (tab) => {
+    target = await TargetFactory.forTab(tab);
 
     gDevTools.showToolbox(target)
       .then(toolboxRegister)
       .then(testToolRegistered);
   });
 }
 
 var resolveToolInstanceBuild;
--- a/devtools/client/framework/test/browser_toolbox_window_reload_target.js
+++ b/devtools/client/framework/test/browser_toolbox_window_reload_target.js
@@ -12,20 +12,20 @@ const TEST_URL = "data:text/html;charset
 var {Toolbox} = require("devtools/client/framework/toolbox");
 
 const {LocalizationHelper} = require("devtools/shared/l10n");
 const L10N = new LocalizationHelper("devtools/client/locales/toolbox.properties");
 
 var target, toolbox, description, reloadsSent, toolIDs;
 
 function test() {
-  addTab(TEST_URL).then(() => {
-    target = TargetFactory.forTab(gBrowser.selectedTab);
+  addTab(TEST_URL).then(async () => {
+    target = await TargetFactory.forTab(gBrowser.selectedTab);
 
-    target.makeRemote().then(() => {
+    target.attach().then(() => {
       toolIDs = gDevTools.getToolDefinitionArray()
                   .filter(def => def.isTargetSupported(target))
                   .map(def => def.id);
       gDevTools.showToolbox(target, toolIDs[0], Toolbox.HostType.BOTTOM)
                .then(startReloadTest);
     });
   });
 }
--- a/devtools/client/framework/test/browser_toolbox_window_shortcuts.js
+++ b/devtools/client/framework/test/browser_toolbox_window_shortcuts.js
@@ -6,18 +6,18 @@
 "use strict";
 
 var Startup = Cc["@mozilla.org/devtools/startup-clh;1"].getService(Ci.nsISupports)
   .wrappedJSObject;
 var {Toolbox} = require("devtools/client/framework/toolbox");
 
 var toolbox, toolIDs, toolShortcuts = [], idIndex, modifiedPrefs = [];
 
-function test() {
-  addTab("about:blank").then(function() {
+async function test() {
+  addTab("about:blank").then(async function() {
     toolIDs = [];
     for (const [id, definition] of gDevTools._tools) {
       const shortcut = Startup.KeyShortcuts.filter(s => s.toolId == id)[0];
       if (!shortcut) {
         continue;
       }
       toolIDs.push(id);
       toolShortcuts.push(shortcut);
@@ -27,17 +27,17 @@ function test() {
       if (pref) {
         const prefValue = Services.prefs.getBoolPref(pref, false);
         if (!prefValue) {
           modifiedPrefs.push(pref);
           Services.prefs.setBoolPref(pref, true);
         }
       }
     }
-    const target = TargetFactory.forTab(gBrowser.selectedTab);
+    const target = await TargetFactory.forTab(gBrowser.selectedTab);
     idIndex = 0;
     gDevTools.showToolbox(target, toolIDs[0], Toolbox.HostType.WINDOW)
              .then(testShortcuts);
   });
 }
 
 function testShortcuts(aToolbox, aIndex) {
   if (aIndex === undefined) {
--- a/devtools/client/framework/test/browser_toolbox_window_title_changes.js
+++ b/devtools/client/framework/test/browser_toolbox_window_title_changes.js
@@ -16,18 +16,18 @@ function test() {
   const TOOL_ID_2 = "jsdebugger";
 
   const NAME_1 = "";
   const NAME_2 = "";
   const NAME_3 = "Toolbox test for title update";
 
   let toolbox;
 
-  addTab(URL_1).then(function() {
-    let target = TargetFactory.forTab(gBrowser.selectedTab);
+  addTab(URL_1).then(async function() {
+    let target = await TargetFactory.forTab(gBrowser.selectedTab);
     gDevTools.showToolbox(target, null, Toolbox.HostType.BOTTOM)
       .then(function(aToolbox) {
         toolbox = aToolbox;
       })
       .then(() => toolbox.selectTool(TOOL_ID_1))
 
     // undock toolbox and check title
       .then(() => {
@@ -64,19 +64,19 @@ function test() {
 
     // destroy toolbox, create new one hosted in a window (with a
     // different tool id), and check title
       .then(function() {
         // Give the tools a chance to handle the navigation event before
         // destroying the toolbox.
         executeSoon(function() {
           toolbox.destroy()
-            .then(function() {
+            .then(async function() {
               // After destroying the toolbox, a fresh target is required.
-              target = TargetFactory.forTab(gBrowser.selectedTab);
+              target = await TargetFactory.forTab(gBrowser.selectedTab);
               return gDevTools.showToolbox(target, null, Toolbox.HostType.WINDOW);
             })
             .then(function(aToolbox) {
               toolbox = aToolbox;
             })
             .then(() => {
               const onTitleChanged = waitForTitleChange(toolbox);
               toolbox.selectTool(TOOL_ID_1);
--- a/devtools/client/framework/test/browser_toolbox_window_title_frame_select.js
+++ b/devtools/client/framework/test/browser_toolbox_window_title_frame_select.js
@@ -16,17 +16,17 @@ const URL = URL_ROOT + "browser_toolbox_
 const IFRAME_URL = URL_ROOT + "browser_toolbox_window_title_changes_page.html";
 const {LocalizationHelper} = require("devtools/shared/l10n");
 const L10N = new LocalizationHelper("devtools/client/locales/toolbox.properties");
 
 add_task(async function() {
   Services.prefs.setBoolPref("devtools.command-button-frames.enabled", true);
 
   await addTab(URL);
-  const target = TargetFactory.forTab(gBrowser.selectedTab);
+  const target = await TargetFactory.forTab(gBrowser.selectedTab);
   let toolbox = await gDevTools.showToolbox(target, null,
     Toolbox.HostType.BOTTOM);
 
   await toolbox.switchHost(Toolbox.HostType.WINDOW);
   // Wait for title change event *after* switch host, in order to listen
   // for the event on the WINDOW host window, which only exists after switchHost
   await waitForTitleChange(toolbox);
 
--- a/devtools/client/framework/test/browser_toolbox_zoom.js
+++ b/devtools/client/framework/test/browser_toolbox_zoom.js
@@ -10,17 +10,17 @@ const L10N = new LocalizationHelper("dev
 add_task(async function() {
   registerCleanupFunction(function() {
     Services.prefs.clearUserPref("devtools.toolbox.zoomValue");
   });
 
   // This test assume that zoom value will be default value. i.e. x1.0.
   Services.prefs.setCharPref("devtools.toolbox.zoomValue", "1.0");
   await addTab("about:blank");
-  const target = TargetFactory.forTab(gBrowser.selectedTab);
+  const target = await TargetFactory.forTab(gBrowser.selectedTab);
   const toolbox = await gDevTools.showToolbox(target,
                                             "styleeditor",
                                             Toolbox.HostType.BOTTOM);
 
   info("testing zoom keys");
 
   testZoomLevel("In", 2, 1.2, toolbox);
   testZoomLevel("Out", 3, 0.9, toolbox);
--- a/devtools/client/framework/test/browser_toolbox_zoom_popup.js
+++ b/devtools/client/framework/test/browser_toolbox_zoom_popup.js
@@ -15,17 +15,17 @@ add_task(async function() {
   registerCleanupFunction(async function() {
     Services.prefs.clearUserPref("devtools.toolbox.zoomValue");
   });
   const zoom = 1.4;
   Services.prefs.setCharPref("devtools.toolbox.zoomValue", zoom.toString(10));
 
   info("Load iframe page for checking the frame menu with x1.4 zoom.");
   await addTab(TEST_URL);
-  const target = TargetFactory.forTab(gBrowser.selectedTab);
+  const target = await TargetFactory.forTab(gBrowser.selectedTab);
   const toolbox = await gDevTools.showToolbox(target,
                                             "inspector",
                                             Toolbox.HostType.WINDOW);
   const inspector = toolbox.getCurrentPanel();
   const hostWindow = toolbox.win.parent;
   const originWidth = hostWindow.outerWidth;
   const originHeight = hostWindow.outerHeight;
   const windowUtils = toolbox.win.windowUtils;
--- a/devtools/client/framework/test/helper_disable_cache.js
+++ b/devtools/client/framework/test/helper_disable_cache.js
@@ -32,17 +32,17 @@ var tabs = [
   {
     title: "Tab 3",
     desc: "No toolbox",
     startToolbox: false
   }];
 
 async function initTab(tabX, startToolbox) {
   tabX.tab = await addTab(TEST_URI);
-  tabX.target = TargetFactory.forTab(tabX.tab);
+  tabX.target = await TargetFactory.forTab(tabX.tab);
 
   if (startToolbox) {
     tabX.toolbox = await gDevTools.showToolbox(tabX.target, "options");
   }
 }
 
 async function checkCacheStateForAllTabs(states) {
   for (let i = 0; i < tabs.length; i++) {
--- a/devtools/client/framework/toolbox-options.js
+++ b/devtools/client/framework/toolbox-options.js
@@ -79,17 +79,17 @@ OptionsPanel.prototype = {
 
   get target() {
     return this.toolbox.target;
   },
 
   async open() {
     // For local debugging we need to make the target remote.
     if (!this.target.isRemote) {
-      await this.target.makeRemote();
+      await this.target.attach();
     }
 
     this.setupToolsList();
     this.setupToolbarButtonsList();
     this.setupThemeList();
     this.setupNightlyOptions();
     await this.populatePreferences();
     this.isReady = true;
@@ -447,17 +447,17 @@ OptionsPanel.prototype = {
       prefSelect.addEventListener("change", function(e) {
         const select = e.target;
         SetPref(select.getAttribute("data-pref"),
           select.options[select.selectedIndex].value);
       });
     }
 
     if (this.target.activeTab && !this.target.chrome) {
-      const [ response ] = await this.target.client.attachTab(this.target.activeTab._actor);
+      const [ response ] = await this.target.client.attachTarget(this.target.activeTab._actor);
       this._origJavascriptEnabled = !response.javascriptEnabled;
       this.disableJSNode.checked = this._origJavascriptEnabled;
       this.disableJSNode.addEventListener("click", this._disableJSClicked);
     } else {
       // Hide the checkbox and label
       this.disableJSNode.parentNode.style.display = "none";
     }
   },
--- a/devtools/client/framework/toolbox.js
+++ b/devtools/client/framework/toolbox.js
@@ -442,17 +442,17 @@ Toolbox.prototype = {
           resolve();
         }, this._URL);
       });
 
       // Optimization: fire up a few other things before waiting on
       // the iframe being ready (makes startup faster)
 
       // Load the toolbox-level actor fronts and utilities now
-      await this._target.makeRemote();
+      await this._target.attach();
 
       // Start tracking network activity on toolbox open for targets such as tabs.
       // (Workers and potentially others don't manage the console client in the target.)
       if (this._target.activeConsole) {
         await this._target.activeConsole.startListeners([
           "NetworkActivity",
         ]);
       }
--- a/devtools/client/inspector/animation/test/head.js
+++ b/devtools/client/inspector/animation/test/head.js
@@ -38,17 +38,17 @@ const openAnimationInspector = async fun
 };
 
 /**
  * Close the toolbox.
  *
  * @return {Promise} that resolves when the toolbox has closed.
  */
 const closeAnimationInspector = async function() {
-  const target = TargetFactory.forTab(gBrowser.selectedTab);
+  const target = await TargetFactory.forTab(gBrowser.selectedTab);
   return gDevTools.closeToolbox(target);
 };
 
 /**
  * Some animation features are not enabled by default in release/beta channels
  * yet including:
  *   * parts of the Web Animations API (Bug 1264101), and
  *   * the frames() timing function (Bug 1379582).
--- a/devtools/client/inspector/grids/test/browser_grids_grid-list-toggle-multiple-grids.js
+++ b/devtools/client/inspector/grids/test/browser_grids_grid-list-toggle-multiple-grids.js
@@ -1,14 +1,14 @@
 /* Any copyright is dedicated to the Public Domain.
  http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
-// Test toggling the multiple grid highlighters in the grid inspector panel.
+// Test toggling multiple grid highlighters in the grid inspector panel.
 
 const TEST_URI = `
   <style type='text/css'>
     .grid {
       display: grid;
     }
   </style>
   <div id="grid1" class="grid">
--- a/devtools/client/inspector/inspector.js
+++ b/devtools/client/inspector/inspector.js
@@ -167,17 +167,17 @@ Inspector.prototype = {
   /**
    * open is effectively an asynchronous constructor
    */
   async init() {
     // Localize all the nodes containing a data-localization attribute.
     localizeMarkup(this.panelDoc);
 
     this._cssProperties = await initCssProperties(this.toolbox);
-    await this.target.makeRemote();
+    await this.target.attach();
     await this._getPageStyle();
 
     // This may throw if the document is still loading and we are
     // refering to a dead about:blank document
     const defaultSelection = await this._getDefaultNodeForSelection()
       .catch(this._handleRejectionIfNotDestroyed);
 
     return this._deferredOpen(defaultSelection);
--- a/devtools/client/inspector/markup/test/browser.ini
+++ b/devtools/client/inspector/markup/test/browser.ini
@@ -128,17 +128,18 @@ skip-if = true # Bug 1177550
 [browser_markup_events_react_development_15.4.1_jsx.js]
 [browser_markup_events_react_production_15.3.1.js]
 [browser_markup_events_react_production_15.3.1_jsx.js]
 [browser_markup_events_react_production_16.2.0.js]
 [browser_markup_events_react_production_16.2.0_jsx.js]
 [browser_markup_events_source_map.js]
 [browser_markup_events-windowed-host.js]
 [browser_markup_flex_display_badge.js]
-[browser_markup_grid_display_badge.js]
+[browser_markup_grid_display_badge_01.js]
+[browser_markup_grid_display_badge_02.js]
 [browser_markup_links_01.js]
 [browser_markup_links_02.js]
 [browser_markup_links_03.js]
 [browser_markup_links_04.js]
 subsuite = clipboard
 skip-if = (os == 'linux' && bits == 32 && debug) # bug 1328915, disable linux32 debug devtools for timeouts
 [browser_markup_links_05.js]
 [browser_markup_links_06.js]
--- a/devtools/client/inspector/markup/test/browser_markup_flex_display_badge.js
+++ b/devtools/client/inspector/markup/test/browser_markup_flex_display_badge.js
@@ -23,16 +23,18 @@ add_task(async function() {
   const { inspector } = await openLayoutView();
   const { highlighters, store } = inspector;
 
   info("Check the flex display badge is shown and not active.");
   await selectNode("#flex", inspector);
   const flexContainer = await getContainerForSelector("#flex", inspector);
   const flexDisplayBadge = flexContainer.elt.querySelector(".markup-badge[data-display]");
   ok(!flexDisplayBadge.classList.contains("active"), "flex display badge is not active.");
+  ok(flexDisplayBadge.classList.contains("interactive"),
+    "flex display badge is interactive.");
 
   info("Check the initial state of the flex highlighter.");
   ok(!highlighters.highlighters[HIGHLIGHTER_TYPE],
     "No flexbox highlighter exists in the highlighters overlay.");
   ok(!highlighters.flexboxHighlighterShown, "No flexbox highlighter is shown.");
 
   info("Toggling ON the flexbox highlighter from the flex display badge.");
   const onHighlighterShown = highlighters.once("flexbox-highlighter-shown");
@@ -41,18 +43,22 @@ add_task(async function() {
   await onHighlighterShown;
   await onCheckboxChange;
 
   info("Check the flexbox highlighter is created and flex display badge state.");
   ok(highlighters.highlighters[HIGHLIGHTER_TYPE],
     "Flexbox highlighter is created in the highlighters overlay.");
   ok(highlighters.flexboxHighlighterShown, "Flexbox highlighter is shown.");
   ok(flexDisplayBadge.classList.contains("active"), "flex display badge is active.");
+  ok(flexDisplayBadge.classList.contains("interactive"),
+    "flex display badge is interactive.");
 
   info("Toggling OFF the flexbox highlighter from the flex display badge.");
   const onHighlighterHidden = highlighters.once("flexbox-highlighter-hidden");
   onCheckboxChange = waitUntilState(store, state => !state.flexbox.highlighted);
   flexDisplayBadge.click();
   await onHighlighterHidden;
   await onCheckboxChange;
 
   ok(!flexDisplayBadge.classList.contains("active"), "flex display badge is not active.");
+  ok(flexDisplayBadge.classList.contains("interactive"),
+    "flex display badge is interactive.");
 });
rename from devtools/client/inspector/markup/test/browser_markup_grid_display_badge.js
rename to devtools/client/inspector/markup/test/browser_markup_grid_display_badge_01.js
--- a/devtools/client/inspector/markup/test/browser_markup_grid_display_badge.js
+++ b/devtools/client/inspector/markup/test/browser_markup_grid_display_badge_01.js
@@ -19,16 +19,18 @@ add_task(async function() {
   const { inspector } = await openLayoutView();
   const { highlighters, store } = inspector;
 
   info("Check the grid display badge is shown and not active.");
   await selectNode("#grid", inspector);
   const gridContainer = await getContainerForSelector("#grid", inspector);
   const gridDisplayBadge = gridContainer.elt.querySelector(".markup-badge[data-display]");
   ok(!gridDisplayBadge.classList.contains("active"), "grid display badge is not active.");
+  ok(gridDisplayBadge.classList.contains("interactive"),
+    "grid display badge is interactive.");
 
   info("Check the initial state of the grid highlighter.");
   ok(!highlighters.gridHighlighters.size,
     "No CSS grid highlighter exists in the highlighters overlay.");
 
   info("Toggling ON the CSS grid highlighter from the grid display badge.");
   const onHighlighterShown = highlighters.once("grid-highlighter-shown");
   let onCheckboxChange = waitUntilState(store, state =>
@@ -37,20 +39,24 @@ add_task(async function() {
   gridDisplayBadge.click();
   await onHighlighterShown;
   await onCheckboxChange;
 
   info("Check that the CSS grid highlighter is created and the display badge state.");
   is(highlighters.gridHighlighters.size, 1,
     "CSS grid highlighter is created in the highlighters overlay.");
   ok(gridDisplayBadge.classList.contains("active"), "grid display badge is active.");
+  ok(gridDisplayBadge.classList.contains("interactive"),
+   "grid display badge is interactive.");
 
   info("Toggling OFF the CSS grid highlighter from the grid display badge.");
   const onHighlighterHidden = highlighters.once("grid-highlighter-hidden");
   onCheckboxChange = waitUntilState(store, state =>
     state.grids.length == 1 &&
     !state.grids[0].highlighted);
   gridDisplayBadge.click();
   await onHighlighterHidden;
   await onCheckboxChange;
 
   ok(!gridDisplayBadge.classList.contains("active"), "grid display badge is not active.");
+  ok(gridDisplayBadge.classList.contains("interactive"),
+    "grid display badge is interactive.");
 });
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/markup/test/browser_markup_grid_display_badge_02.js
@@ -0,0 +1,155 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Tests toggling multiple grid highlighters in the markup view with the grid display
+// badges.
+
+const TEST_URI = `
+  <style type='text/css'>
+    .grid {
+      display: grid;
+    }
+  </style>
+  <div id="grid1" class="grid">
+    <div class="cell1">cell1</div>
+    <div class="cell2">cell2</div>
+  </div>
+  <div id="grid2" class="grid">
+    <div class="cell1">cell1</div>
+    <div class="cell2">cell2</div>
+  </div>
+  <div id="grid3" class="grid">
+    <div class="cell1">cell1</div>
+    <div class="cell2">cell2</div>
+  </div>
+`;
+
+add_task(async function() {
+  await pushPref("devtools.gridinspector.maxHighlighters", 2);
+  await addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
+  const { inspector } = await openLayoutView();
+  const { highlighters } = inspector;
+
+  const grid1 = await getContainerForSelector("#grid1", inspector);
+  const grid2 = await getContainerForSelector("#grid2", inspector);
+  const grid3 = await getContainerForSelector("#grid3", inspector);
+  const gridDisplayBadge1 = grid1.elt.querySelector(".markup-badge[data-display]");
+  const gridDisplayBadge2 = grid2.elt.querySelector(".markup-badge[data-display]");
+  const gridDisplayBadge3 = grid3.elt.querySelector(".markup-badge[data-display]");
+
+  info("Check the initial state of the grid display badges and grid highlighters");
+  ok(!gridDisplayBadge1.classList.contains("active"),
+    "#grid1 display badge is not active.");
+  ok(!gridDisplayBadge2.classList.contains("active"),
+    "#grid2 display badge is not active.");
+  ok(!gridDisplayBadge3.classList.contains("active"),
+    "#grid3 display badge is not active.");
+  ok(gridDisplayBadge1.classList.contains("interactive"),
+    "#grid1 display badge is interactive");
+  ok(gridDisplayBadge2.classList.contains("interactive"),
+    "#grid2 display badge is interactive");
+  ok(gridDisplayBadge3.classList.contains("interactive"),
+    "#grid3 display badge is interactive");
+  ok(!highlighters.gridHighlighters.size,
+    "No CSS grid highlighter exists in the highlighters overlay.");
+
+  info("Toggling ON the CSS grid highlighter from the #grid1 display badge.");
+  let onHighlighterShown = highlighters.once("grid-highlighter-shown");
+  gridDisplayBadge1.click();
+  await onHighlighterShown;
+
+  ok(gridDisplayBadge1.classList.contains("active"),
+    "#grid1 display badge is active.");
+  ok(!gridDisplayBadge2.classList.contains("active"),
+    "#grid2 display badge is not active.");
+  ok(!gridDisplayBadge3.classList.contains("active"),
+    "#grid3 display badge is not active.");
+  ok(gridDisplayBadge1.classList.contains("interactive"),
+    "#grid1 display badge is interactive");
+  ok(gridDisplayBadge2.classList.contains("interactive"),
+    "#grid2 display badge is interactive");
+  ok(gridDisplayBadge3.classList.contains("interactive"),
+    "#grid3 display badge is interactive");
+  is(highlighters.gridHighlighters.size, 1,
+    "Got expected number of grid highlighters shown.");
+
+  info("Toggling ON the CSS grid highlighter from the #grid2 display badge.");
+  onHighlighterShown = highlighters.once("grid-highlighter-shown");
+  gridDisplayBadge2.click();
+  await onHighlighterShown;
+
+  ok(gridDisplayBadge1.classList.contains("active"),
+    "#grid1 display badge is active.");
+  ok(gridDisplayBadge2.classList.contains("active"),
+    "#grid2 display badge is active.");
+  ok(!gridDisplayBadge3.classList.contains("active"),
+    "#grid3 display badge is not active.");
+  ok(gridDisplayBadge1.classList.contains("interactive"),
+    "#grid1 display badge is interactive");
+  ok(gridDisplayBadge2.classList.contains("interactive"),
+    "#grid2 display badge is interactive");
+  ok(!gridDisplayBadge3.classList.contains("interactive"),
+    "#grid3 display badge is not interactive");
+  is(highlighters.gridHighlighters.size, 2,
+    "Got expected number of grid highlighters shown.");
+
+  info("Attempt to toggle ON the CSS grid highlighter from the #grid3 display badge.");
+  gridDisplayBadge3.click();
+
+  ok(gridDisplayBadge1.classList.contains("active"),
+    "#grid1 display badge is active.");
+  ok(gridDisplayBadge2.classList.contains("active"),
+    "#grid2 display badge is active.");
+  ok(!gridDisplayBadge3.classList.contains("active"),
+    "#grid3 display badge is not active.");
+  ok(gridDisplayBadge1.classList.contains("interactive"),
+    "#grid1 display badge is interactive");
+  ok(gridDisplayBadge2.classList.contains("interactive"),
+    "#grid2 display badge is interactive");
+  ok(!gridDisplayBadge3.classList.contains("interactive"),
+    "#grid3 display badge is not interactive");
+  is(highlighters.gridHighlighters.size, 2,
+    "Got expected number of grid highlighters shown.");
+
+  info("Toggling OFF the CSS grid highlighter from the #grid2 display badge.");
+  let onHighlighterHidden = highlighters.once("grid-highlighter-hidden");
+  gridDisplayBadge2.click();
+  await onHighlighterHidden;
+
+  ok(gridDisplayBadge1.classList.contains("active"),
+    "#grid1 display badge is active.");
+  ok(!gridDisplayBadge2.classList.contains("active"),
+    "#grid2 display badge is not active.");
+  ok(!gridDisplayBadge3.classList.contains("active"),
+    "#grid3 display badge is not active.");
+  ok(gridDisplayBadge1.classList.contains("interactive"),
+    "#grid1 display badge is interactive");
+  ok(gridDisplayBadge2.classList.contains("interactive"),
+    "#grid2 display badge is interactive");
+  ok(gridDisplayBadge3.classList.contains("interactive"),
+    "#grid3 display badge is interactive");
+  is(highlighters.gridHighlighters.size, 1,
+    "Got expected number of grid highlighters shown.");
+
+  info("Toggling OFF the CSS grid highlighter from the #grid1 display badge.");
+  onHighlighterHidden = highlighters.once("grid-highlighter-hidden");
+  gridDisplayBadge1.click();
+  await onHighlighterHidden;
+
+  ok(!gridDisplayBadge1.classList.contains("active"),
+    "#grid1 display badge is not active.");
+  ok(!gridDisplayBadge2.classList.contains("active"),
+    "#grid2 display badge is not active.");
+  ok(!gridDisplayBadge3.classList.contains("active"),
+    "#grid3 display badge is not active.");
+  ok(gridDisplayBadge1.classList.contains("interactive"),
+    "#grid1 display badge is interactive");
+  ok(gridDisplayBadge2.classList.contains("interactive"),
+    "#grid2 display badge is interactive");
+  ok(gridDisplayBadge3.classList.contains("interactive"),
+    "#grid3 display badge is interactive");
+  ok(!highlighters.gridHighlighters.size,
+    "No CSS grid highlighter exists in the highlighters overlay.");
+});
--- a/devtools/client/inspector/markup/views/element-container.js
+++ b/devtools/client/inspector/markup/views/element-container.js
@@ -29,81 +29,34 @@ loader.lazyRequireGetter(this, "setEvent
  *         The markup view that owns this container.
  * @param  {NodeFront} node
  *         The node to display.
  */
 function MarkupElementContainer(markupView, node) {
   MarkupContainer.prototype.initialize.call(this, markupView, node,
     "elementcontainer");
 
-  this.onFlexboxHighlighterChange = this.onFlexboxHighlighterChange.bind(this);
-  this.onGridHighlighterChange = this.onGridHighlighterChange.bind(this);
-
-  this.markup.highlighters.on("flexbox-highlighter-hidden",
-    this.onFlexboxHighlighterChange);
-  this.markup.highlighters.on("flexbox-highlighter-shown",
-    this.onFlexboxHighlighterChange);
-  this.markup.highlighters.on("grid-highlighter-hidden", this.onGridHighlighterChange);
-  this.markup.highlighters.on("grid-highlighter-shown", this.onGridHighlighterChange);
-
   if (node.nodeType === nodeConstants.ELEMENT_NODE) {
     this.editor = new ElementEditor(this, node);
   } else {
     throw new Error("Invalid node for MarkupElementContainer");
   }
 
   this.tagLine.appendChild(this.editor.elt);
 }
 
 MarkupElementContainer.prototype = extend(MarkupContainer.prototype, {
-  destroy: function() {
-    this.markup.highlighters.off("flexbox-highlighter-hidden",
-      this.onFlexboxHighlighterChange);
-    this.markup.highlighters.off("flexbox-highlighter-shown",
-      this.onFlexboxHighlighterChange);
-    this.markup.highlighters.off("grid-highlighter-hidden", this.onGridHighlighterChange);
-    this.markup.highlighters.off("grid-highlighter-shown", this.onGridHighlighterChange);
-
-    MarkupContainer.prototype.destroy.call(this);
-  },
-
   onContainerClick: function(event) {
     if (!event.target.hasAttribute("data-event")) {
       return;
     }
 
     this._buildEventTooltipContent(event.target);
   },
 
-  /**
-   * Handler for "flexbox-highlighter-hidden" and "flexbox-highlighter-shown" event
-   * emitted from the HighlightersOverlay. Toggles the active state of the display badge
-   * if it matches the highlighted flex container node.
-   */
-  onFlexboxHighlighterChange: function() {
-    if (!this.editor._displayBadge) {
-      return;
-    }
-    this.editor._displayBadge.classList.toggle("active",
-      this.markup.highlighters.flexboxHighlighterShown === this.node);
-  },
-
-  /**
-   * Handler for "grid-highlighter-hidden" and "grid-highlighter-shown" event emitted from
-   * the HighlightersOverlay. Toggles the active state of the display badge if it matches
-   * the highlighted grid node.
-   */
-  onGridHighlighterChange: function() {
-    if (!this.editor._displayBadge) {
-      return;
-    }
-    this.editor._displayBadge.classList.toggle("active",
-      this.markup.highlighters.gridHighlighters.has(this.node));
-  },
-
   async _buildEventTooltipContent(target) {
     const tooltip = this.markup.eventDetailsTooltip;
     await tooltip.hide();
 
     const listenerInfo = await this.node.getEventListenerInfo();
 
     const toolbox = this.markup.toolbox;
 
--- a/devtools/client/inspector/markup/views/element-editor.js
+++ b/devtools/client/inspector/markup/views/element-editor.js
@@ -8,23 +8,20 @@ const Services = require("Services");
 const TextEditor = require("devtools/client/inspector/markup/views/text-editor");
 const {
   getAutocompleteMaxWidth,
   flashElementOn,
   flashElementOff,
   parseAttributeValues,
 } = require("devtools/client/inspector/markup/utils");
 const { truncateString } = require("devtools/shared/inspector/utils");
-const {editableField, InplaceEditor} =
-      require("devtools/client/shared/inplace-editor");
-const {parseAttribute} =
-      require("devtools/client/shared/node-attribute-parser");
-const {getCssProperties} = require("devtools/shared/fronts/css-properties");
+const { editableField, InplaceEditor } = require("devtools/client/shared/inplace-editor");
+const { parseAttribute } = require("devtools/client/shared/node-attribute-parser");
+const { getCssProperties } = require("devtools/shared/fronts/css-properties");
 
-// Global tooltip inspector
 const {LocalizationHelper} = require("devtools/shared/l10n");
 const INSPECTOR_L10N =
   new LocalizationHelper("devtools/client/locales/inspector.properties");
 
 // Page size for pageup/pagedown
 const COLLAPSE_DATA_URL_REGEX = /^data.+base64/;
 const COLLAPSE_DATA_URL_LENGTH = 60;
 
@@ -72,16 +69,18 @@ function ElementEditor(container, node) 
   this.closeTag = null;
   this.attrList = null;
   this.newAttr = null;
   this.closeElt = null;
 
   this.onCustomBadgeClick = this.onCustomBadgeClick.bind(this);
   this.onDisplayBadgeClick = this.onDisplayBadgeClick.bind(this);
   this.onExpandBadgeClick = this.onExpandBadgeClick.bind(this);
+  this.onFlexboxHighlighterChange = this.onFlexboxHighlighterChange.bind(this);
+  this.onGridHighlighterChange = this.onGridHighlighterChange.bind(this);
   this.onTagEdit = this.onTagEdit.bind(this);
 
   // Create the main editor
   this.buildMarkup();
 
   // Make the tag name editable (unless this is a remote node or
   // a document element)
   if (!node.isDocumentElement) {
@@ -297,46 +296,61 @@ ElementEditor.prototype = {
     // Badges order is [event][display][custom], insert event badge before others.
     this.elt.insertBefore(this._eventBadge, this._displayBadge || this._customBadge);
   },
 
   /**
    * Update the markup display badge.
    */
   updateDisplayBadge: function() {
-    const showDisplayBadge = this.node.displayType in DISPLAY_TYPES;
+    const displayType = this.node.displayType;
+    const showDisplayBadge = displayType in DISPLAY_TYPES;
+
     if (this._displayBadge && !showDisplayBadge) {
+      this.stopTrackingFlexboxHighlighterEvents();
+      this.stopTrackingGridHighlighterEvents();
+
       this._displayBadge.remove();
       this._displayBadge = null;
     } else if (showDisplayBadge) {
       if (!this._displayBadge) {
         this._createDisplayBadge();
       }
+
       this._updateDisplayBadgeContent();
     }
   },
 
   _createDisplayBadge: function() {
     this._displayBadge = this.doc.createElement("div");
     this._displayBadge.classList.add("markup-badge");
     this._displayBadge.addEventListener("click", this.onDisplayBadgeClick);
     // Badges order is [event][display][custom], insert display badge before custom.
     this.elt.insertBefore(this._displayBadge, this._customBadge);
+
+    this.startTrackingFlexboxHighlighterEvents();
+    this.startTrackingGridHighlighterEvents();
   },
 
   _updateDisplayBadgeContent: function() {
-    this._displayBadge.textContent = this.node.displayType;
-    this._displayBadge.dataset.display = this.node.displayType;
-    this._displayBadge.title = DISPLAY_TYPES[this.node.displayType];
+    const displayType = this.node.displayType;
+    this._displayBadge.textContent = displayType;
+    this._displayBadge.dataset.display = displayType;
+    this._displayBadge.title = DISPLAY_TYPES[displayType];
     this._displayBadge.classList.toggle("active",
       this.highlighters.flexboxHighlighterShown === this.node ||
       this.highlighters.gridHighlighters.has(this.node));
-    this._displayBadge.classList.toggle("interactive",
-      Services.prefs.getBoolPref("devtools.inspector.flexboxHighlighter.enabled") &&
-      (this.node.displayType === "flex" || this.node.displayType === "inline-flex"));
+
+    if (displayType === "flex" || displayType === "inline-flex") {
+      this._displayBadge.classList.toggle("interactive",
+        Services.prefs.getBoolPref("devtools.inspector.flexboxHighlighter.enabled"));
+    } else if (displayType === "grid" || displayType === "inline-grid") {
+      this._displayBadge.classList.toggle("interactive",
+        this.highlighters.canGridHighlighterToggle(this.node));
+    }
   },
 
   /**
    * Update the markup custom element badge.
    */
   updateCustomBadge: function() {
     const showCustomBadge = !!this.node.customElementLocation;
     if (this._customBadge && !showCustomBadge) {
@@ -695,49 +709,113 @@ ElementEditor.prototype = {
       this.markup.emit("refocusedonedit");
     };
 
     // Start listening for mutations until we find an attributes change
     // that modifies this attribute.
     this.markup.inspector.once("markupmutation", onMutations);
   },
 
+  startTrackingFlexboxHighlighterEvents() {
+    this.highlighters.on("flexbox-highlighter-hidden", this.onFlexboxHighlighterChange);
+    this.highlighters.on("flexbox-highlighter-shown", this.onFlexboxHighlighterChange);
+  },
+
+  startTrackingGridHighlighterEvents() {
+    this.highlighters.on("grid-highlighter-hidden", this.onGridHighlighterChange);
+    this.highlighters.on("grid-highlighter-shown", this.onGridHighlighterChange);
+  },
+
+  stopTrackingFlexboxHighlighterEvents() {
+    this.highlighters.off("flexbox-highlighter-hidden", this.onFlexboxHighlighterChange);
+    this.highlighters.off("flexbox-highlighter-shown", this.onFlexboxHighlighterChange);
+  },
+
+  stopTrackingGridHighlighterEvents() {
+    this.highlighters.off("grid-highlighter-hidden", this.onGridHighlighterChange);
+    this.highlighters.off("grid-highlighter-shown", this.onGridHighlighterChange);
+  },
+
   /**
-   * Called when the display badge is clicked. Toggles on the grid highlighter for the
-   * selected node if it is a grid container.
+   * Called when the display badge is clicked. Toggles on the flex/grid highlighter for
+   * the selected node if it is a grid container.
    */
-  onDisplayBadgeClick: function(event) {
+  onDisplayBadgeClick: async function(event) {
     event.stopPropagation();
 
     const target = event.target;
 
     if (Services.prefs.getBoolPref("devtools.inspector.flexboxHighlighter.enabled") &&
         (target.dataset.display === "flex" || target.dataset.display === "inline-flex")) {
-      this._displayBadge.classList.add("active");
-      this.highlighters.toggleFlexboxHighlighter(this.inspector.selection.nodeFront,
-        "markup");
+      // Stop tracking highlighter events to avoid flickering of the active class.
+      this.stopTrackingFlexboxHighlighterEvents();
+
+      this._displayBadge.classList.toggle("active");
+      await this.highlighters.toggleFlexboxHighlighter(this.node);
+
+      this.startTrackingFlexboxHighlighterEvents();
     }
 
     if (target.dataset.display === "grid" || target.dataset.display === "inline-grid") {
-      this._displayBadge.classList.add("active");
-      this.highlighters.toggleGridHighlighter(this.inspector.selection.nodeFront,
-        "markup");
+      // Don't toggle the grid highlighter if the max number of new grid highlighters
+      // allowed has been reached.
+      if (!this.highlighters.canGridHighlighterToggle(this.node)) {
+        return;
+      }
+
+      // Stop tracking highlighter events to avoid flickering of the active class.
+      this.stopTrackingGridHighlighterEvents();
+
+      this._displayBadge.classList.toggle("active");
+      await this.highlighters.toggleGridHighlighter(this.node, "markup");
+
+      this.startTrackingGridHighlighterEvents();
     }
   },
 
   onCustomBadgeClick: function() {
     const { url, line } = this.node.customElementLocation;
     this.markup.toolbox.viewSourceInDebugger(url, line, "show_custom_element");
   },
 
   onExpandBadgeClick: function() {
     this.container.expandContainer();
   },
 
   /**
+   * Handler for "flexbox-highlighter-hidden" and "flexbox-highlighter-shown" event
+   * emitted from the HighlightersOverlay. Toggles the active state of the display badge
+   * if it matches the highlighted flex container node.
+   */
+  onFlexboxHighlighterChange: function() {
+    if (!this._displayBadge) {
+      return;
+    }
+
+    this._displayBadge.classList.toggle("active",
+      this.highlighters.flexboxHighlighterShown === this.node);
+  },
+
+  /**
+   * Handler for "grid-highlighter-hidden" and "grid-highlighter-shown" event emitted from
+   * the HighlightersOverlay. Toggles the active state of the display badge if it matches
+   * the highlighted grid node.
+   */
+  onGridHighlighterChange: function() {
+    if (!this._displayBadge) {
+      return;
+    }
+
+    this._displayBadge.classList.toggle("active",
+      this.highlighters.gridHighlighters.has(this.node));
+
+    this._updateDisplayBadgeContent();
+  },
+
+  /**
    * Called when the tag name editor has is done editing.
    */
   onTagEdit: function(newTagName, isCommit) {
     if (!isCommit ||
         newTagName.toLowerCase() === this.node.tagName.toLowerCase() ||
         !("editTagName" in this.markup.walker)) {
       return;
     }
@@ -749,17 +827,20 @@ ElementEditor.prototype = {
       // Failed to edit the tag name, cancel the reselection.
       this.markup.cancelReselectOnRemoved();
     });
   },
 
   destroy: function() {
     if (this._displayBadge) {
       this._displayBadge.removeEventListener("click", this.onDisplayBadgeClick);
+      this.stopTrackingFlexboxHighlighterEvents();
+      this.stopTrackingGridHighlighterEvents();
     }
+
     if (this._customBadge) {
       this._customBadge.removeEventListener("click", this.onCustomBadgeClick);
     }
 
     this.expandBadge.removeEventListener("click", this.onExpandBadgeClick);
 
     for (const key in this.animationTimers) {
       clearTimeout(this.animationTimers[key]);
--- a/devtools/client/inspector/rules/test/browser_rules_authored_color.js
+++ b/devtools/client/inspector/rules/test/browser_rules_authored_color.js
@@ -56,12 +56,12 @@ add_task(async function() {
     focusAndSendKey(spectrum.element.ownerDocument.defaultView, "RETURN");
     await onHidden;
     await onRuleViewChanged;
 
     is(getRuleViewPropertyValue(view, "element", "color"), color.result,
        "changing the color preserved the unit for " + color.name);
   }
 
-  const target = TargetFactory.forTab(tab);
+  const target = await TargetFactory.forTab(tab);
   await gDevTools.closeToolbox(target);
   gBrowser.removeCurrentTab();
 });
--- a/devtools/client/inspector/rules/test/browser_rules_colorUnit.js
+++ b/devtools/client/inspector/rules/test/browser_rules_colorUnit.js
@@ -27,17 +27,17 @@ add_task(async function() {
 
     const tab = await addTab("data:text/html;charset=utf-8," +
                            encodeURIComponent(TEST_URI));
     const {inspector, view} = await openRuleView();
 
     await selectNode("#testid", inspector);
     await basicTest(view, name, result);
 
-    const target = TargetFactory.forTab(tab);
+    const target = await TargetFactory.forTab(tab);
     await gDevTools.closeToolbox(target);
     gBrowser.removeCurrentTab();
   }
 });
 
 async function basicTest(view, name, result) {
   const cPicker = view.tooltips.getTooltip("colorPicker");
   const swatch = getRuleViewProperty(view, "#testid", "color").valueSpan
--- a/devtools/client/inspector/rules/test/browser_rules_flexbox-highlighter-on-reload.js
+++ b/devtools/client/inspector/rules/test/browser_rules_flexbox-highlighter-on-reload.js
@@ -17,17 +17,17 @@ const TEST_URI = `
 
 add_task(async function() {
   await addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
 
   info("Check that the flexbox highlighter can be displayed.");
   await checkFlexboxHighlighter();
 
   info("Close the toolbox before reloading the tab.");
-  const target = TargetFactory.forTab(gBrowser.selectedTab);
+  const target = await TargetFactory.forTab(gBrowser.selectedTab);
   await gDevTools.closeToolbox(target);
 
   await refreshTab();
 
   info("Check that the flexbox highlighter can be displayed after reloading the page.");
   await checkFlexboxHighlighter();
 });
 
--- a/devtools/client/inspector/rules/test/browser_rules_grid-highlighter-on-reload.js
+++ b/devtools/client/inspector/rules/test/browser_rules_grid-highlighter-on-reload.js
@@ -22,17 +22,17 @@ const TEST_URI = `
 
 add_task(async function() {
   await addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
 
   info("Check that the grid highlighter can be displayed");
   await checkGridHighlighter();
 
   info("Close the toolbox before reloading the tab");
-  const target = TargetFactory.forTab(gBrowser.selectedTab);
+  const target = await TargetFactory.forTab(gBrowser.selectedTab);
   await gDevTools.closeToolbox(target);
 
   await refreshTab();
 
   info("Check that the grid highlighter can be displayed after reloading the page");
   await checkGridHighlighter();
 });
 
--- a/devtools/client/inspector/shared/highlighters-overlay.js
+++ b/devtools/client/inspector/shared/highlighters-overlay.js
@@ -84,16 +84,34 @@ class HighlightersOverlay {
     // Add inspector events, not specific to a given view.
     this.inspector.on("markupmutation", this.onMarkupMutation);
     this.inspector.target.on("will-navigate", this.onWillNavigate);
 
     EventEmitter.decorate(this);
   }
 
   /**
+   * Returns true if the grid highlighter can be toggled on/off for the given node, and
+   * false otherwise. A grid container can be toggled on if the max grid highlighters
+   * is only 1 or less than the maximum grid highlighters that can be displayed or if
+   * the grid highlighter already highlights the given node.
+   *
+   * @param  {NodeFront} node
+   *         Grid container NodeFront.
+   * @return {Boolean}
+   */
+  canGridHighlighterToggle(node) {
+    const maxGridHighlighters =
+      Services.prefs.getIntPref("devtools.gridinspector.maxHighlighters");
+    return maxGridHighlighters === 1 ||
+           this.gridHighlighters.size < maxGridHighlighters ||
+           this.gridHighlighters.has(node);
+  }
+
+  /**
    * Returns whether `node` is somewhere inside the DOM of the rule view.
    *
    * @param {DOMNode} node
    * @return {Boolean}
    */
   isRuleView(node) {
     return !!node.closest("#ruleview-panel");
   }
--- a/devtools/client/inspector/test/browser_inspector_destroy-before-ready.js
+++ b/devtools/client/inspector/test/browser_inspector_destroy-before-ready.js
@@ -11,16 +11,16 @@ add_task(async function() {
   // At least one assertion is needed to avoid failing the test, but really,
   // what we're interested in is just having the test pass when switching to the
   // inspector.
   ok(true);
 
   await addTab("data:text/html;charset=utf-8,test inspector destroy");
 
   info("Open the toolbox on the debugger panel");
-  const target = TargetFactory.forTab(gBrowser.selectedTab);
+  const target = await TargetFactory.forTab(gBrowser.selectedTab);
   const toolbox = await gDevTools.showToolbox(target, "jsdebugger");
 
   info("Switch to the inspector panel and immediately end the test");
   const onInspectorSelected = toolbox.once("inspector-selected");
   toolbox.selectTool("inspector");
   await onInspectorSelected;
 });
--- a/devtools/client/inspector/test/browser_inspector_initialization.js
+++ b/devtools/client/inspector/test/browser_inspector_initialization.js
@@ -30,17 +30,17 @@ add_task(async function() {
   const testActor = await getTestActorWithoutToolbox(tab);
 
   await testToolboxInitialization(testActor, tab);
   await testContextMenuInitialization(testActor);
   await testContextMenuInspectorAlreadyOpen(testActor);
 });
 
 async function testToolboxInitialization(testActor, tab) {
-  const target = TargetFactory.forTab(tab);
+  const target = await TargetFactory.forTab(tab);
 
   info("Opening inspector with gDevTools.");
   const toolbox = await gDevTools.showToolbox(target, "inspector");
   const inspector = toolbox.getCurrentPanel();
 
   ok(true, "Inspector started, and notification received.");
   ok(inspector, "Inspector instance is accessible.");
   ok(inspector.isReady, "Inspector instance is ready.");
@@ -70,40 +70,44 @@ async function testContextMenuInitializa
   info("Checking inspector state.");
   await testMarkupView("#salutation");
   await testBreadcrumbs("#salutation");
 }
 
 async function testContextMenuInspectorAlreadyOpen(testActor) {
   info("Changing node by clicking on 'Inspect Element' context menu item");
 
-  const inspector = getActiveInspector();
+  const inspector = await getActiveInspector();
   ok(inspector, "Inspector is active");
 
   await clickOnInspectMenuItem(testActor, "#closing");
 
   ok(true, "Inspector was updated when 'Inspect Element' was clicked.");
   await testMarkupView("#closing", inspector);
   await testBreadcrumbs("#closing", inspector);
 }
 
 async function testMarkupView(selector, inspector) {
-  inspector = inspector || getActiveInspector();
+  if (!inspector) {
+    inspector = await getActiveInspector();
+  }
   const nodeFront = await getNodeFront(selector, inspector);
   try {
     is(inspector.selection.nodeFront, nodeFront,
        "Right node is selected in the markup view");
   } catch (ex) {
     ok(false, "Got exception while resolving selected node of markup view.");
     console.error(ex);
   }
 }
 
 async function testBreadcrumbs(selector, inspector) {
-  inspector = inspector || getActiveInspector();
+  if (!inspector) {
+    inspector = await getActiveInspector();
+  }
   const nodeFront = await getNodeFront(selector, inspector);
 
   const b = inspector.breadcrumbs;
   const expectedText = b.prettyPrintNodeAsText(nodeFront);
   const button = b.container.querySelector("button[checked=true]");
   ok(button, "A crumbs is checked=true");
   is(button.getAttribute("title"), expectedText,
      "Crumb refers to the right node");
--- a/devtools/client/inspector/test/browser_inspector_inspect_node_contextmenu.js
+++ b/devtools/client/inspector/test/browser_inspector_inspect_node_contextmenu.js
@@ -39,25 +39,25 @@ add_task(async function() {
 });
 
 async function testContextMenuWithinIframe(testActor, nodeFrontGetter) {
   info("Opening inspector via 'Inspect Element' context menu item within an iframe");
   const selector = ["iframe", "#in-frame"];
   await clickOnInspectMenuItem(testActor, selector);
 
   info("Checking inspector state.");
-  const inspector = getActiveInspector();
+  const inspector = await getActiveInspector();
   const nodeFront = await nodeFrontGetter(inspector);
 
   is(inspector.selection.nodeFront, nodeFront,
      "Right node is selected in the markup view");
 }
 
 async function changeToolboxToInnerFrame() {
-  const { toolbox } = getActiveInspector();
+  const { toolbox } = await getActiveInspector();
 
   const btn = toolbox.doc.getElementById("command-button-frames");
   const panel = toolbox.doc.getElementById("command-button-frames-panel");
   btn.click();
   ok(panel, "popup panel has created.");
   await waitUntil(() => panel.classList.contains("tooltip-visible"));
 
   info("Select the iframe in the frame list.");
--- a/devtools/client/inspector/test/browser_inspector_reload_xul.js
+++ b/devtools/client/inspector/test/browser_inspector_reload_xul.js
@@ -13,17 +13,17 @@ const TEST_URI = URL_ROOT + "doc_inspect
 add_task(async function() {
   await pushPref("dom.allow_XUL_XBL_for_file", false);
 
   const { tab, inspector, toolbox } = await openInspectorForURL(TEST_URI);
   await testToolboxInitialization(tab, inspector, toolbox);
 });
 
 async function testToolboxInitialization(tab, inspector, toolbox) {
-  const target = TargetFactory.forTab(tab);
+  const target = await TargetFactory.forTab(tab);
 
   ok(true, "Inspector started, and notification received.");
   ok(inspector, "Inspector instance is accessible.");
   ok(inspector.isReady, "Inspector instance is ready.");
   is(inspector.target.tab, tab, "Valid target.");
 
   await selectNode("#p", inspector);
   await testMarkupView("#p", inspector);
--- a/devtools/client/inspector/test/browser_inspector_switch-to-inspector-on-pick.js
+++ b/devtools/client/inspector/test/browser_inspector_switch-to-inspector-on-pick.js
@@ -67,19 +67,19 @@ add_task(async function() {
   await startPickerAndAssertSwitchToInspector(toolbox);
 
   info("Stoppping element picker.");
   await toolbox.highlighterUtils.stopPicker();
 
   checkResults();
 });
 
-function openToolbox(tab) {
+async function openToolbox(tab) {
   info("Opening webconsole.");
-  const target = TargetFactory.forTab(tab);
+  const target = await TargetFactory.forTab(tab);
   return gDevTools.showToolbox(target, "webconsole");
 }
 
 async function startPickerAndAssertSwitchToInspector(toolbox) {
   info("Clicking element picker button.");
   const pickButton = toolbox.doc.querySelector("#command-button-pick");
   pickButton.click();
 
--- a/devtools/client/inspector/test/head.js
+++ b/devtools/client/inspector/test/head.js
@@ -178,18 +178,18 @@ function clearCurrentNodeSelection(inspe
  *         with an object: { tab, toolbox, inspector }.
  */
 var openInspectorForURL = async function(url, hostType) {
   const tab = await addTab(url);
   const { inspector, toolbox, testActor } = await openInspector(hostType);
   return { tab, inspector, toolbox, testActor };
 };
 
-function getActiveInspector() {
-  const target = TargetFactory.forTab(gBrowser.selectedTab);
+async function getActiveInspector() {
+  const target = await TargetFactory.forTab(gBrowser.selectedTab);
   return gDevTools.getToolbox(target).getPanel("inspector");
 }
 
 /**
  * Right click on a node in the test page and click on the inspect menu item.
  * @param {TestActor}
  * @param {String} selector The selector for the node to click on in the page.
  * @return {Promise} Resolves to the inspector when it has opened and is updated
--- a/devtools/client/memory/test/browser/head.js
+++ b/devtools/client/memory/test/browser/head.js
@@ -20,29 +20,29 @@ var { L10N } = require("devtools/client/
 
 Services.prefs.setBoolPref("devtools.memory.enabled", true);
 
 /**
  * Open the memory panel for the given tab.
  */
 this.openMemoryPanel = async function(tab) {
   info("Opening memory panel.");
-  const target = TargetFactory.forTab(tab);
+  const target = await TargetFactory.forTab(tab);
   const toolbox = await gDevTools.showToolbox(target, "memory");
   info("Memory panel shown successfully.");
   const panel = toolbox.getCurrentPanel();
   return { tab, panel };
 };
 
 /**
  * Close the memory panel for the given tab.
  */
 this.closeMemoryPanel = async function(tab) {
   info("Closing memory panel.");
-  const target = TargetFactory.forTab(tab);
+  const target = await TargetFactory.forTab(tab);
   const toolbox = gDevTools.getToolbox(target);
   await toolbox.destroy();
   info("Closed memory panel successfully.");
 };
 
 /**
  * Return a test function that adds a tab with the given url, opens the memory
  * panel, runs the given generator, closes the memory panel, removes the tab,
--- a/devtools/client/menus.js
+++ b/devtools/client/menus.js
@@ -36,19 +36,23 @@ loader.lazyRequireGetter(this, "Responsi
 loader.lazyRequireGetter(this, "openDocLink", "devtools/client/shared/link", true);
 
 loader.lazyImporter(this, "BrowserToolboxProcess", "resource://devtools/client/framework/ToolboxProcess.jsm");
 loader.lazyImporter(this, "ScratchpadManager", "resource://devtools/client/scratchpad/scratchpad-manager.jsm");
 
 exports.menuitems = [
   { id: "menu_devToolbox",
     l10nKey: "devToolboxMenuItem",
-    oncommand(event) {
-      const window = event.target.ownerDocument.defaultView;
-      gDevToolsBrowser.toggleToolboxCommand(window.gBrowser, Cu.now());
+    async oncommand(event) {
+      try {
+        const window = event.target.ownerDocument.defaultView;
+        await gDevToolsBrowser.toggleToolboxCommand(window.gBrowser, Cu.now());
+      } catch (e) {
+        console.error(`Exception while opening the toolbox: ${e}\n${e.stack}`);
+      }
     },
     keyId: "toggleToolbox",
     checkbox: true
   },
   { id: "menu_devtools_separator",
     separator: true },
   { id: "menu_webide",
     l10nKey: "webide",
@@ -92,18 +96,18 @@ exports.menuitems = [
     },
     keyId: "responsiveDesignMode",
     checkbox: true
   },
   { id: "menu_eyedropper",
     l10nKey: "eyedropper",
     async oncommand(event) {
       const window = event.target.ownerDocument.defaultView;
-      const target = TargetFactory.forTab(window.gBrowser.selectedTab);
-      await target.makeRemote();
+      const target = await TargetFactory.forTab(window.gBrowser.selectedTab);
+      await target.attach();
       const inspectorFront = await target.getFront("inspector");
       inspectorFront.pickColorFromPage({copyOnSelect: true, fromMenu: true});
     },
     checkbox: true
   },
   { id: "menu_scratchpad",
     l10nKey: "scratchpad",
     oncommand() {
--- a/devtools/client/netmonitor/panel.js
+++ b/devtools/client/netmonitor/panel.js
@@ -7,17 +7,17 @@
 function NetMonitorPanel(iframeWindow, toolbox) {
   this.panelWin = iframeWindow;
   this.toolbox = toolbox;
 }
 
 NetMonitorPanel.prototype = {
   async open() {
     if (!this.toolbox.target.isRemote) {
-      await this.toolbox.target.makeRemote();
+      await this.toolbox.target.attach();
     }
 
     // Reuse an existing Network monitor API object if available.
     // It could have been created for WE API before Net panel opens.
     const api = await this.toolbox.getNetMonitorAPI();
     const app = this.panelWin.initialize(api);
 
     // Connect the application object to the UI.
--- a/devtools/client/netmonitor/src/api.js
+++ b/devtools/client/netmonitor/src/api.js
@@ -90,17 +90,17 @@ NetMonitorAPI.prototype = {
    * Connect to the Firefox backend by default.
    *
    * As soon as connections to different back-ends is supported
    * this function should be responsible for picking the right API.
    */
   async connectBackend(connector, connection, actions, getState) {
     // The connection might happen during Toolbox initialization
     // so make sure the target is ready.
-    await connection.tabConnection.tabTarget.makeRemote();
+    await connection.tabConnection.tabTarget.attach();
     return connector.connectFirefox(connection, actions, getState);
   },
 
   // HAR
 
   /**
    * Support for `devtools.network.getHAR` (get collected data as HAR)
    */
--- a/devtools/client/netmonitor/src/har/har-automation.js
+++ b/devtools/client/netmonitor/src/har/har-automation.js
@@ -40,17 +40,17 @@ function HarAutomation(toolbox) {
 
 HarAutomation.prototype = {
   // Initialization
 
   initialize: function(toolbox) {
     this.toolbox = toolbox;
 
     const target = toolbox.target;
-    target.makeRemote().then(() => {
+    target.attach().then(() => {
       this.startMonitoring(target.client, target.form);
     });
   },
 
   destroy: function() {
     if (this.collector) {
       this.collector.stop();
     }
--- a/devtools/client/netmonitor/test/head.js
+++ b/devtools/client/netmonitor/test/head.js
@@ -284,19 +284,19 @@ async function waitForAllNetworkUpdateEv
 
 function initNetMonitor(url, enableCache) {
   info("Initializing a network monitor pane.");
 
   return (async function() {
     const tab = await addTab(url);
     info("Net tab added successfully: " + url);
 
-    const target = TargetFactory.forTab(tab);
+    const target = await TargetFactory.forTab(tab);
 
-    await target.makeRemote();
+    await target.attach();
     info("Target remoted.");
 
     const toolbox = await gDevTools.showToolbox(target, "netmonitor");
     info("Network monitor pane shown successfully.");
 
     const monitor = toolbox.getCurrentPanel();
 
     startNetworkEventUpdateObserver(monitor.panelWin);
--- a/devtools/client/performance/test/helpers/panel-utils.js
+++ b/devtools/client/performance/test/helpers/panel-utils.js
@@ -18,18 +18,18 @@ exports.initPanelInNewTab = async functi
 };
 
 /**
  * Initializes a toolbox panel in the specified tab.
  */
 exports.initPanelInTab = async function({ tool, tab }) {
   dump(`Initializing a ${tool} panel.\n`);
 
-  const target = TargetFactory.forTab(tab);
-  await target.makeRemote();
+  const target = await TargetFactory.forTab(tab);
+  await target.attach();
 
   // Open a toolbox and wait for the connection to the performance actors
   // to be opened. This is necessary because of the WebConsole's
   // `profile` and `profileEnd` methods.
   const toolbox = await gDevTools.showToolbox(target, tool);
   await toolbox.initPerformance();
 
   const panel = toolbox.getCurrentPanel();
--- a/devtools/client/responsive.html/actions/index.js
+++ b/devtools/client/responsive.html/actions/index.js
@@ -43,31 +43,25 @@ createEnum([
   // Change the user agent of the viewport.
   "CHANGE_USER_AGENT",
 
   // The pixel ratio of the viewport has changed. This may be triggered by the user
   // when changing the device displayed in the viewport, or when a pixel ratio is
   // selected from the device pixel ratio dropdown.
   "CHANGE_PIXEL_RATIO",
 
-  // Change one of the reload conditions.
-  "CHANGE_RELOAD_CONDITION",
-
   // Indicates that the device list is being loaded.
   "LOAD_DEVICE_LIST_START",
 
   // Indicates that the device list loading action threw an error.
   "LOAD_DEVICE_LIST_ERROR",
 
   // Indicates that the device list has been loaded successfully.
   "LOAD_DEVICE_LIST_END",
 
-  // Indicates that the reload conditions have been loaded successfully.
-  "LOAD_RELOAD_CONDITIONS_END",
-
   // Remove a device.
   "REMOVE_DEVICE",
 
   // Remove the viewport's device assocation.
   "REMOVE_DEVICE_ASSOCIATION",
 
   // Resize the viewport.
   "RESIZE_VIEWPORT",
@@ -79,16 +73,22 @@ createEnum([
   "TAKE_SCREENSHOT_START",
 
   // Indicates when the screenshot action ends.
   "TAKE_SCREENSHOT_END",
 
   // Toggles the left alignment of the viewports.
   "TOGGLE_LEFT_ALIGNMENT",
 
+  // Toggles the reload on touch simulation changes.
+  "TOGGLE_RELOAD_ON_TOUCH_SIMULATION",
+
+  // Toggles the reload on user agent changes.
+  "TOGGLE_RELOAD_ON_USER_AGENT",
+
   // Toggles the touch simulation state of the viewports.
   "TOGGLE_TOUCH_SIMULATION",
 
   // Toggles the user agent input displayed in the toolbar.
   "TOGGLE_USER_AGENT_INPUT",
 
   // Update the device display state in the device selector.
   "UPDATE_DEVICE_DISPLAYED",
--- a/devtools/client/responsive.html/actions/moz.build
+++ b/devtools/client/responsive.html/actions/moz.build
@@ -2,13 +2,12 @@
 # vim: set filetype=python:
 # 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(
     'devices.js',
     'index.js',
-    'reload-conditions.js',
     'screenshot.js',
     'ui.js',
     'viewports.js',
 )
deleted file mode 100644
--- a/devtools/client/responsive.html/actions/reload-conditions.js
+++ /dev/null
@@ -1,53 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-"use strict";
-
-const Services = require("Services");
-
-const {
-  CHANGE_RELOAD_CONDITION,
-  LOAD_RELOAD_CONDITIONS_END,
-} = require("./index");
-
-const Types = require("../types");
-
-const PREF_PREFIX = "devtools.responsive.reloadConditions.";
-
-module.exports = {
-
-  changeReloadCondition(id, value) {
-    return dispatch => {
-      const pref = PREF_PREFIX + id;
-      Services.prefs.setBoolPref(pref, value);
-      dispatch({
-        type: CHANGE_RELOAD_CONDITION,
-        id,
-        value,
-      });
-    };
-  },
-
-  loadReloadConditions() {
-    return dispatch => {
-      // Loop over the conditions and load their values from prefs.
-      for (const id in Types.reloadConditions) {
-        // Skip over the loading state of the list.
-        if (id == "state") {
-          return;
-        }
-        const pref = PREF_PREFIX + id;
-        const value = Services.prefs.getBoolPref(pref, false);
-        dispatch({
-          type: CHANGE_RELOAD_CONDITION,
-          id,
-          value,
-        });
-      }
-
-      dispatch({ type: LOAD_RELOAD_CONDITIONS_END });
-    };
-  },
-
-};
--- a/devtools/client/responsive.html/actions/ui.js
+++ b/devtools/client/responsive.html/actions/ui.js
@@ -1,18 +1,20 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const {
+  CHANGE_DISPLAY_PIXEL_RATIO,
   CHANGE_USER_AGENT,
-  CHANGE_DISPLAY_PIXEL_RATIO,
   TOGGLE_LEFT_ALIGNMENT,
+  TOGGLE_RELOAD_ON_TOUCH_SIMULATION,
+  TOGGLE_RELOAD_ON_USER_AGENT,
   TOGGLE_TOUCH_SIMULATION,
   TOGGLE_USER_AGENT_INPUT,
 } = require("./index");
 
 module.exports = {
 
   /**
    * The pixel ratio of the display has changed. This may be triggered by the user
@@ -35,16 +37,30 @@ module.exports = {
 
   toggleLeftAlignment(enabled) {
     return {
       type: TOGGLE_LEFT_ALIGNMENT,
       enabled,
     };
   },
 
+  toggleReloadOnTouchSimulation(enabled) {
+    return {
+      type: TOGGLE_RELOAD_ON_TOUCH_SIMULATION,
+      enabled,
+    };
+  },
+
+  toggleReloadOnUserAgent(enabled) {
+    return {
+      type: TOGGLE_RELOAD_ON_USER_AGENT,
+      enabled,
+    };
+  },
+
   toggleTouchSimulation(enabled) {
     return {
       type: TOGGLE_TOUCH_SIMULATION,
       enabled,
     };
   },
 
   toggleUserAgentInput(enabled) {
--- a/devtools/client/responsive.html/components/App.js
+++ b/devtools/client/responsive.html/components/App.js
@@ -20,22 +20,23 @@ loader.lazyGetter(this, "DeviceModal",
 const { changeNetworkThrottling } = require("devtools/client/shared/components/throttling/actions");
 const {
   addCustomDevice,
   removeCustomDevice,
   updateDeviceDisplayed,
   updateDeviceModal,
   updatePreferredDevices,
 } = require("../actions/devices");
-const { changeReloadCondition } = require("../actions/reload-conditions");
 const { takeScreenshot } = require("../actions/screenshot");
 const {
   changeUserAgent,
+  toggleLeftAlignment,
+  toggleReloadOnTouchSimulation,
+  toggleReloadOnUserAgent,
   toggleTouchSimulation,
-  toggleLeftAlignment,
   toggleUserAgentInput,
 } = require("../actions/ui");
 const {
   changeDevice,
   changePixelRatio,
   removeDeviceAssociation,
   resizeViewport,
   rotateViewport,
@@ -44,42 +45,43 @@ const {
 const Types = require("../types");
 
 class App extends PureComponent {
   static get propTypes() {
     return {
       devices: PropTypes.shape(Types.devices).isRequired,
       dispatch: PropTypes.func.isRequired,
       networkThrottling: PropTypes.shape(Types.networkThrottling).isRequired,
-      reloadConditions: PropTypes.shape(Types.reloadConditions).isRequired,
       screenshot: PropTypes.shape(Types.screenshot).isRequired,
       viewports: PropTypes.arrayOf(PropTypes.shape(Types.viewport)).isRequired,
     };
   }
 
   constructor(props) {
     super(props);
 
     this.onAddCustomDevice = this.onAddCustomDevice.bind(this);
     this.onBrowserMounted = this.onBrowserMounted.bind(this);
     this.onChangeDevice = this.onChangeDevice.bind(this);
     this.onChangeNetworkThrottling = this.onChangeNetworkThrottling.bind(this);
     this.onChangePixelRatio = this.onChangePixelRatio.bind(this);
-    this.onChangeReloadCondition = this.onChangeReloadCondition.bind(this);
     this.onChangeTouchSimulation = this.onChangeTouchSimulation.bind(this);
     this.onChangeUserAgent = this.onChangeUserAgent.bind(this);
     this.onContentResize = this.onContentResize.bind(this);
     this.onDeviceListUpdate = this.onDeviceListUpdate.bind(this);
     this.onExit = this.onExit.bind(this);
     this.onRemoveCustomDevice = this.onRemoveCustomDevice.bind(this);
     this.onRemoveDeviceAssociation = this.onRemoveDeviceAssociation.bind(this);
     this.onResizeViewport = this.onResizeViewport.bind(this);
     this.onRotateViewport = this.onRotateViewport.bind(this);
     this.onScreenshot = this.onScreenshot.bind(this);
     this.onToggleLeftAlignment = this.onToggleLeftAlignment.bind(this);
+    this.onToggleReloadOnTouchSimulation =
+      this.onToggleReloadOnTouchSimulation.bind(this);
+    this.onToggleReloadOnUserAgent = this.onToggleReloadOnUserAgent.bind(this);
     this.onToggleUserAgentInput = this.onToggleUserAgentInput.bind(this);
     this.onUpdateDeviceDisplayed = this.onUpdateDeviceDisplayed.bind(this);
     this.onUpdateDeviceModal = this.onUpdateDeviceModal.bind(this);
   }
 
   onAddCustomDevice(device) {
     this.props.dispatch(addCustomDevice(device));
   }
@@ -114,20 +116,16 @@ class App extends PureComponent {
   onChangePixelRatio(pixelRatio) {
     window.postMessage({
       type: "change-pixel-ratio",
       pixelRatio,
     }, "*");
     this.props.dispatch(changePixelRatio(0, pixelRatio));
   }
 
-  onChangeReloadCondition(id, value) {
-    this.props.dispatch(changeReloadCondition(id, value));
-  }
-
   onChangeTouchSimulation(enabled) {
     window.postMessage({
       type: "change-touch-simulation",
       enabled,
     }, "*");
     this.props.dispatch(toggleTouchSimulation(enabled));
   }
 
@@ -187,55 +185,63 @@ class App extends PureComponent {
   onScreenshot() {
     this.props.dispatch(takeScreenshot());
   }
 
   onToggleLeftAlignment() {
     this.props.dispatch(toggleLeftAlignment());
   }
 
+  onToggleReloadOnTouchSimulation() {
+    this.props.dispatch(toggleReloadOnTouchSimulation());
+  }
+
+  onToggleReloadOnUserAgent() {
+    this.props.dispatch(toggleReloadOnUserAgent());
+  }
+
   onToggleUserAgentInput() {
     this.props.dispatch(toggleUserAgentInput());
   }
 
   onUpdateDeviceDisplayed(device, deviceType, displayed) {
     this.props.dispatch(updateDeviceDisplayed(device, deviceType, displayed));
   }
 
   onUpdateDeviceModal(isOpen, modalOpenedFromViewport) {
     this.props.dispatch(updateDeviceModal(isOpen, modalOpenedFromViewport));
   }
 
   render() {
     const {
       devices,
       networkThrottling,
-      reloadConditions,
       screenshot,
       viewports,
     } = this.props;
 
     const {
       onAddCustomDevice,
       onBrowserMounted,
       onChangeDevice,
       onChangeNetworkThrottling,
       onChangePixelRatio,
-      onChangeReloadCondition,
       onChangeTouchSimulation,
       onChangeUserAgent,
       onContentResize,
       onDeviceListUpdate,
       onExit,
       onRemoveCustomDevice,
       onRemoveDeviceAssociation,
       onResizeViewport,
       onRotateViewport,
       onScreenshot,
       onToggleLeftAlignment,
+      onToggleReloadOnTouchSimulation,
+      onToggleReloadOnUserAgent,
       onToggleUserAgentInput,
       onUpdateDeviceDisplayed,
       onUpdateDeviceModal,
     } = this;
 
     if (!viewports.length) {
       return null;
     }
@@ -248,33 +254,33 @@ class App extends PureComponent {
       deviceAdderViewportTemplate = viewports[devices.modalOpenedFromViewport];
     }
 
     return (
       dom.div({ id: "app" },
         Toolbar({
           devices,
           networkThrottling,
-          reloadConditions,
           screenshot,
           selectedDevice,
           selectedPixelRatio,
           viewport: viewports[0],
           onChangeDevice,
           onChangeNetworkThrottling,
           onChangePixelRatio,
-          onChangeReloadCondition,
           onChangeTouchSimulation,
           onChangeUserAgent,
           onExit,
           onRemoveDeviceAssociation,
           onResizeViewport,
           onRotateViewport,
           onScreenshot,
           onToggleLeftAlignment,
+          onToggleReloadOnTouchSimulation,
+          onToggleReloadOnUserAgent,
           onToggleUserAgentInput,
           onUpdateDeviceModal,
         }),
         Viewports({
           screenshot,
           viewports,
           onBrowserMounted,
           onContentResize,
--- a/devtools/client/responsive.html/components/SettingsMenu.js
+++ b/devtools/client/responsive.html/components/SettingsMenu.js
@@ -1,48 +1,51 @@
 /* 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 { connect } = require("devtools/client/shared/vendor/react-redux");
 const { PureComponent } = require("devtools/client/shared/vendor/react");
 const dom = 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 { getStr } = require("../utils/l10n");
-const Types = require("../types");
 
 loader.lazyRequireGetter(this, "showMenu", "devtools/client/shared/components/menu/utils", true);
 
 class SettingsMenu extends PureComponent {
   static get propTypes() {
     return {
       leftAlignmentEnabled: PropTypes.bool.isRequired,
-      onChangeReloadCondition: PropTypes.func.isRequired,
       onToggleLeftAlignment: PropTypes.func.isRequired,
+      onToggleReloadOnTouchSimulation: PropTypes.func.isRequired,
+      onToggleReloadOnUserAgent: PropTypes.func.isRequired,
       onToggleUserAgentInput: PropTypes.func.isRequired,
-      reloadConditions: PropTypes.shape(Types.reloadConditions).isRequired,
+      reloadOnTouchSimulation: PropTypes.bool.isRequired,
+      reloadOnUserAgent: PropTypes.bool.isRequired,
       showUserAgentInput: PropTypes.bool.isRequired,
     };
   }
 
   constructor(props) {
     super(props);
     this.onToggleSettingMenu = this.onToggleSettingMenu.bind(this);
   }
 
   onToggleSettingMenu(event) {
     const {
       leftAlignmentEnabled,
-      onChangeReloadCondition,
       onToggleLeftAlignment,
+      onToggleReloadOnTouchSimulation,
+      onToggleReloadOnUserAgent,
       onToggleUserAgentInput,
-      reloadConditions,
+      reloadOnTouchSimulation,
+      reloadOnUserAgent,
       showUserAgentInput,
     } = this.props;
 
     const menuItems = [
       {
         id: "toggleLeftAlignment",
         checked: leftAlignmentEnabled,
         label: getStr("responsive.leftAlignViewport"),
@@ -59,30 +62,30 @@ class SettingsMenu extends PureComponent
         type: "checkbox",
         click: () => {
           onToggleUserAgentInput();
         },
       },
       "-",
       {
         id: "touchSimulation",
-        checked: reloadConditions.touchSimulation,
+        checked: reloadOnTouchSimulation,
         label: getStr("responsive.reloadConditions.touchSimulation"),
         type: "checkbox",
         click: () => {
-          onChangeReloadCondition("touchSimulation", !reloadConditions.touchSimulation);
+          onToggleReloadOnTouchSimulation();
         },
       },
       {
         id: "userAgent",
-        checked: reloadConditions.userAgent,
+        checked: reloadOnUserAgent,
         label: getStr("responsive.reloadConditions.userAgent"),
         type: "checkbox",
         click: () => {
-          onChangeReloadCondition("userAgent", !reloadConditions.userAgent);
+          onToggleReloadOnUserAgent();
         },
       },
     ];
 
     showMenu(menuItems, {
       button: event.target,
       useTopLevelWindow: true,
     });
@@ -97,13 +100,15 @@ class SettingsMenu extends PureComponent
       })
     );
   }
 }
 
 const mapStateToProps = state => {
   return {
     leftAlignmentEnabled: state.ui.leftAlignmentEnabled,
+    reloadOnTouchSimulation: state.ui.reloadOnTouchSimulation,
+    reloadOnUserAgent: state.ui.reloadOnUserAgent,
     showUserAgentInput: state.ui.showUserAgentInput,
   };
 };
 
 module.exports = connect(mapStateToProps)(SettingsMenu);
--- a/devtools/client/responsive.html/components/Toolbar.js
+++ b/devtools/client/responsive.html/components/Toolbar.js
@@ -32,78 +32,75 @@ class Toolbar extends PureComponent {
     return {
       devices: PropTypes.shape(Types.devices).isRequired,
       displayPixelRatio: PropTypes.number.isRequired,
       leftAlignmentEnabled: PropTypes.bool.isRequired,
       networkThrottling: PropTypes.shape(Types.networkThrottling).isRequired,
       onChangeDevice: PropTypes.func.isRequired,
       onChangeNetworkThrottling: PropTypes.func.isRequired,
       onChangePixelRatio: PropTypes.func.isRequired,
-      onChangeReloadCondition: PropTypes.func.isRequired,
       onChangeTouchSimulation: PropTypes.func.isRequired,
       onChangeUserAgent: PropTypes.func.isRequired,
       onExit: PropTypes.func.isRequired,
       onRemoveDeviceAssociation: PropTypes.func.isRequired,
       onResizeViewport: PropTypes.func.isRequired,
       onRotateViewport: PropTypes.func.isRequired,
       onScreenshot: PropTypes.func.isRequired,
       onToggleLeftAlignment: PropTypes.func.isRequired,
+      onToggleReloadOnTouchSimulation: PropTypes.func.isRequired,
+      onToggleReloadOnUserAgent: PropTypes.func.isRequired,
       onToggleUserAgentInput: PropTypes.func.isRequired,
       onUpdateDeviceModal: PropTypes.func.isRequired,
-      reloadConditions: PropTypes.shape(Types.reloadConditions).isRequired,
       screenshot: PropTypes.shape(Types.screenshot).isRequired,
       selectedDevice: PropTypes.string.isRequired,
       selectedPixelRatio: PropTypes.number.isRequired,
       showUserAgentInput: PropTypes.bool.isRequired,
       touchSimulationEnabled: PropTypes.bool.isRequired,
-      userAgent: PropTypes.string.isRequired,
       viewport: PropTypes.shape(Types.viewport).isRequired,
     };
   }
 
   renderUserAgent() {
     const {
       onChangeUserAgent,
       showUserAgentInput,
-      userAgent,
     } = this.props;
 
     if (!showUserAgentInput) {
       return null;
     }
 
     return createElement(Fragment, null,
       UserAgentInput({
         onChangeUserAgent,
-        userAgent,
       }),
       dom.div({ className: "devtools-separator" }),
     );
   }
 
   render() {
     const {
       devices,
       displayPixelRatio,
       leftAlignmentEnabled,
       networkThrottling,
       onChangeDevice,
       onChangeNetworkThrottling,
       onChangePixelRatio,
-      onChangeReloadCondition,
       onChangeTouchSimulation,
       onExit,
       onRemoveDeviceAssociation,
       onResizeViewport,
       onRotateViewport,
       onScreenshot,
       onToggleLeftAlignment,
+      onToggleReloadOnTouchSimulation,
+      onToggleReloadOnUserAgent,
       onToggleUserAgentInput,
       onUpdateDeviceModal,
-      reloadConditions,
       screenshot,
       selectedDevice,
       selectedPixelRatio,
       touchSimulationEnabled,
       viewport,
     } = this.props;
 
     return (
@@ -167,19 +164,19 @@ class Toolbar extends PureComponent {
           dom.button({
             id: "screenshot-button",
             className: "devtools-button",
             title: getStr("responsive.screenshot"),
             onClick: onScreenshot,
             disabled: screenshot.isCapturing,
           }),
           SettingsMenu({
-            reloadConditions,
-            onChangeReloadCondition,
             onToggleLeftAlignment,
+            onToggleReloadOnTouchSimulation,
+            onToggleReloadOnUserAgent,
             onToggleUserAgentInput,
           }),
           dom.div({ className: "devtools-separator" }),
           dom.button({
             id: "exit-button",
             className: "devtools-button",
             title: getStr("responsive.exit"),
             onClick: onExit,
@@ -191,13 +188,12 @@ class Toolbar extends PureComponent {
 }
 
 const mapStateToProps = state => {
   return {
     displayPixelRatio: state.ui.displayPixelRatio,
     leftAlignmentEnabled: state.ui.leftAlignmentEnabled,
     showUserAgentInput: state.ui.showUserAgentInput,
     touchSimulationEnabled: state.ui.touchSimulationEnabled,
-    userAgent: state.ui.userAgent,
   };
 };
 
 module.exports = connect(mapStateToProps)(Toolbar);
--- a/devtools/client/responsive.html/components/UserAgentInput.js
+++ b/devtools/client/responsive.html/components/UserAgentInput.js
@@ -2,16 +2,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const { PureComponent } = require("devtools/client/shared/vendor/react");
 const dom = 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 { KeyCodes } = require("devtools/client/shared/keycodes");
 
 const { getStr } = require("../utils/l10n");
 
 class UserAgentInput extends PureComponent {
   static get propTypes() {
     return {
       onChangeUserAgent: PropTypes.func.isRequired,
@@ -81,9 +82,15 @@ class UserAgentInput extends PureCompone
           type: "text",
           value: this.state.value,
         })
       )
     );
   }
 }
 
-module.exports = UserAgentInput;
+const mapStateToProps = state => {
+  return {
+    userAgent: state.ui.userAgent,
+  };
+};
+
+module.exports = connect(mapStateToProps)(UserAgentInput);
--- a/devtools/client/responsive.html/index.js
+++ b/devtools/client/responsive.html/index.js
@@ -18,17 +18,16 @@ const { createFactory, createElement } =
   require("devtools/client/shared/vendor/react");
 const ReactDOM = require("devtools/client/shared/vendor/react-dom");
 const { Provider } = require("devtools/client/shared/vendor/react-redux");
 
 const message = require("./utils/message");
 const App = createFactory(require("./components/App"));
 const Store = require("./store");
 const { loadDevices } = require("./actions/devices");
-const { loadReloadConditions } = require("./actions/reload-conditions");
 const { addViewport, resizeViewport } = require("./actions/viewports");
 const { changeDisplayPixelRatio } = require("./actions/ui");
 
 // Exposed for use by tests
 window.require = require;
 
 const bootstrap = {
 
@@ -70,17 +69,16 @@ const bootstrap = {
 
 // manager.js sends a message to signal init
 message.wait(window, "init").then(() => bootstrap.init());
 
 // manager.js sends a message to signal init is done, which can be used for delayed
 // startup work that shouldn't block initial load
 message.wait(window, "post-init").then(() => {
   bootstrap.dispatch(loadDevices());
-  bootstrap.dispatch(loadReloadConditions());
 });
 
 window.addEventListener("unload", function() {
   bootstrap.destroy();
 }, {once: true});
 
 // Allows quick testing of actions from the console
 window.dispatch = action => bootstrap.dispatch(action);
--- a/devtools/client/responsive.html/manager.js
+++ b/devtools/client/responsive.html/manager.js
@@ -82,55 +82,69 @@ const ResponsiveUIManager = exports.Resp
    *          - `menu`:     Web Developer menu item
    *          - `shortcut`: Keyboard shortcut
    * @return Promise
    *         Resolved to the ResponsiveUI instance for this tab when opening is
    *         complete.
    */
   async openIfNeeded(window, tab, options = {}) {
     if (!tab.linkedBrowser.isRemoteBrowser) {
-      this.showRemoteOnlyNotification(window, tab, options);
+      await this.showRemoteOnlyNotification(window, tab, options);
       return promise.reject(new Error("RDM only available for remote tabs."));
     }
     if (!this.isActiveForTab(tab)) {
       this.initMenuCheckListenerFor(window);
 
-      // Track whether a toolbox was opened before RDM was opened.
-      const toolbox = gDevTools.getToolbox(TargetFactory.forTab(tab));
-      const hostType = toolbox ? toolbox.hostType : "none";
-      const hasToolbox = !!toolbox;
-      const tel = this._telemetry;
-      if (hasToolbox) {
-        tel.scalarAdd("devtools.responsive.toolbox_opened_first", 1);
-      }
-
-      tel.recordEvent("devtools.main", "activate", "responsive_design", null, {
-        "host": hostType,
-        "width": Math.ceil(window.outerWidth / 50) * 50,
-        "session_id": toolbox ? toolbox.sessionId : -1
-      });
-
-      // Track opens keyed by the UI entry point used.
-      let { trigger } = options;
-      if (!trigger) {
-        trigger = "unknown";
-      }
-      tel.keyedScalarAdd("devtools.responsive.open_trigger", trigger, 1);
-
       const ui = new ResponsiveUI(window, tab);
       this.activeTabs.set(tab, ui);
+
+      // Explicitly not await on telemetry to avoid delaying RDM opening
+      this.recordTelemetryOpen(window, tab, options);
+
       await this.setMenuCheckFor(tab, window);
       await ui.inited;
       this.emit("on", { tab });
     }
 
     return this.getResponsiveUIForTab(tab);
   },
 
   /**
+   * Record all telemetry probes related to RDM opening.
+   */
+  async recordTelemetryOpen(window, tab, options) {
+    // Track whether a toolbox was opened before RDM was opened.
+    const isKnownTab = TargetFactory.isKnownTab(tab);
+    let toolbox;
+    if (isKnownTab) {
+      const target = await TargetFactory.forTab(tab);
+      toolbox = gDevTools.getToolbox(target);
+    }
+    const hostType = toolbox ? toolbox.hostType : "none";
+    const hasToolbox = !!toolbox;
+    const tel = this._telemetry;
+    if (hasToolbox) {
+      tel.scalarAdd("devtools.responsive.toolbox_opened_first", 1);
+    }
+
+    tel.recordEvent("devtools.main", "activate", "responsive_design", null, {
+      "host": hostType,
+      "width": Math.ceil(window.outerWidth / 50) * 50,
+      "session_id": toolbox ? toolbox.sessionId : -1
+    });
+
+    // Track opens keyed by the UI entry point used.
+    let { trigger } = options;
+    if (!trigger) {
+      trigger = "unknown";
+    }
+    tel.keyedScalarAdd("devtools.responsive.open_trigger", trigger, 1);
+  },
+
+  /**
    * Closes the responsive UI, if not already closed.
    *
    * @param window
    *        The main browser chrome window.
    * @param tab
    *        The browser tab.
    * @param options
    *        Other options associated with closing.  Currently includes:
@@ -139,50 +153,53 @@ const ResponsiveUIManager = exports.Resp
    *          - `menu`:     Web Developer menu item
    *          - `shortcut`: Keyboard shortcut
    *        - `reason`: String detailing the specific cause for closing
    * @return Promise
    *         Resolved (with no value) when closing is complete.
    */
   async closeIfNeeded(window, tab, options = {}) {
     if (this.isActiveForTab(tab)) {
-      const isKnownTab = TargetFactory.isKnownTab(tab);
-      const target = TargetFactory.forTab(tab);
-      const toolbox = gDevTools.getToolbox(target);
-
-      if (!toolbox && !isKnownTab) {
-        // Destroy the tabTarget to avoid a memory leak.
-        target.destroy();
-      }
-
       const ui = this.activeTabs.get(tab);
       const destroyed = await ui.destroy(options);
       if (!destroyed) {
         // Already in the process of destroying, abort.
         return;
       }
 
-      const hostType = toolbox ? toolbox.hostType : "none";
-      const t = this._telemetry;
-      t.recordEvent("devtools.main", "deactivate", "responsive_design", null, {
-        "host": hostType,
-        "width": Math.ceil(window.outerWidth / 50) * 50,
-        "session_id": toolbox ? toolbox.sessionId : -1
-      });
-
       this.activeTabs.delete(tab);
 
       if (!this.isActiveForWindow(window)) {
         this.removeMenuCheckListenerFor(window);
       }
       this.emit("off", { tab });
       await this.setMenuCheckFor(tab, window);
+
+      // Explicitly not await on telemetry to avoid delaying RDM closing
+      this.recordTelemetryClose(window, tab);
     }
   },
 
+  async recordTelemetryClose(window, tab) {
+    const isKnownTab = TargetFactory.isKnownTab(tab);
+    let toolbox;
+    if (isKnownTab) {
+      const target = await TargetFactory.forTab(tab);
+      toolbox = gDevTools.getToolbox(target);
+    }
+
+    const hostType = toolbox ? toolbox.hostType : "none";
+    const t = this._telemetry;
+    t.recordEvent("devtools.main", "deactivate", "responsive_design", null, {
+      "host": hostType,
+      "width": Math.ceil(window.outerWidth / 50) * 50,
+      "session_id": toolbox ? toolbox.sessionId : -1
+    });
+  },
+
   /**
    * Returns true if responsive UI is active for a given tab.
    *
    * @param tab
    *        The browser tab.
    * @return boolean
    */
   isActiveForTab(tab) {
@@ -233,17 +250,17 @@ const ResponsiveUIManager = exports.Resp
 
     const menu = window.document.getElementById("menu_responsiveUI");
     if (menu) {
       menu.setAttribute("checked", this.isActiveForTab(tab));
     }
   },
 
   showRemoteOnlyNotification(window, tab, { trigger } = {}) {
-    showNotification(window, tab, {
+    return showNotification(window, tab, {
       toolboxButton: trigger == "toolbox",
       msg: l10n.getStr("responsive.remoteOnly"),
       priority: PriorityLevels.PRIORITY_CRITICAL_MEDIUM,
     });
   },
 };
 
 EventEmitter.decorate(ResponsiveUIManager);
@@ -369,17 +386,18 @@ ResponsiveUI.prototype = {
     if (this.destroying) {
       return false;
     }
     this.destroying = true;
 
     // If our tab is about to be closed, there's not enough time to exit
     // gracefully, but that shouldn't be a problem since the tab will go away.
     // So, skip any waiting when we're about to close the tab.
-    const isWindowClosing = options && options.reason === "unload";
+    const isTabDestroyed = !this.tab.linkedBrowser;
+    const isWindowClosing = (options && options.reason === "unload") || isTabDestroyed;
     const isTabContentDestroying =
       isWindowClosing || (options && (options.reason === "TabClose" ||
                                       options.reason === "BeforeTabRemotenessChange"));
 
     // Ensure init has finished before starting destroy
     if (!isTabContentDestroying) {
       await this.inited;
     }
--- a/devtools/client/responsive.html/reducers.js
+++ b/devtools/client/responsive.html/reducers.js
@@ -1,12 +1,11 @@
 /* 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";
 
 exports.devices = require("./reducers/devices");
 exports.networkThrottling = require("devtools/client/shared/components/throttling/reducer");
-exports.reloadConditions = require("./reducers/reload-conditions");
 exports.screenshot = require("./reducers/screenshot");
 exports.ui = require("./reducers/ui");
 exports.viewports = require("./reducers/viewports");
--- a/devtools/client/responsive.html/reducers/devices.js
+++ b/devtools/client/responsive.html/reducers/devices.js
@@ -22,78 +22,87 @@ const INITIAL_DEVICES = {
   listState: Types.loadableState.INITIALIZED,
   modalOpenedFromViewport: null,
   types: [],
 };
 
 const reducers = {
 
   [ADD_DEVICE](devices, { device, deviceType }) {
-    return Object.assign({}, devices, {
+    return {
+      ...devices,
       [deviceType]: [...devices[deviceType], device],
-    });
+    };
   },
 
   [ADD_DEVICE_TYPE](devices, { deviceType }) {
-    return Object.assign({}, devices, {
+    return {
+      ...devices,
       types: [...devices.types, deviceType],
       [deviceType]: [],
-    });
+    };
   },
 
   [UPDATE_DEVICE_DISPLAYED](devices, { device, deviceType, displayed }) {
     const newDevices = devices[deviceType].map(d => {
       if (d == device) {
         d.displayed = displayed;
       }
 
       return d;
     });
 
-    return Object.assign({}, devices, {
+    return {
+      ...devices,
       [deviceType]: newDevices,
-    });
+    };
   },
 
   [LOAD_DEVICE_LIST_START](devices, action) {
-    return Object.assign({}, devices, {
+    return {
+      ...devices,
       listState: Types.loadableState.LOADING,
-    });
+    };
   },
 
   [LOAD_DEVICE_LIST_ERROR](devices, action) {
-    return Object.assign({}, devices, {
+    return {
+      ...devices,
       listState: Types.loadableState.ERROR,
-    });
+    };
   },
 
   [LOAD_DEVICE_LIST_END](devices, action) {
-    return Object.assign({}, devices, {
+    return {
+      ...devices,
       listState: Types.loadableState.LOADED,
-    });
+    };
   },
 
   [REMOVE_DEVICE](devices, { device, deviceType }) {
     const index = devices[deviceType].indexOf(device);
     if (index < 0) {
       return devices;
     }
 
     const list = [...devices[deviceType]];
     list.splice(index, 1);
-    return Object.assign({}, devices, {
-      [deviceType]: list
-    });
+
+    return {
+      ...devices,
+      [deviceType]: list,
+    };
   },
 
   [UPDATE_DEVICE_MODAL](devices, { isOpen, modalOpenedFromViewport }) {
-    return Object.assign({}, devices, {
+    return {
+      ...devices,
       isModalOpen: isOpen,
       modalOpenedFromViewport,
-    });
+    };
   },
 
 };
 
 module.exports = function(devices = INITIAL_DEVICES, action) {
   const reducer = reducers[action.type];
   if (!reducer) {
     return devices;
--- a/devtools/client/responsive.html/reducers/moz.build
+++ b/devtools/client/responsive.html/reducers/moz.build
@@ -1,13 +1,12 @@
 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # 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(
     'devices.js',
-    'reload-conditions.js',
     'screenshot.js',
     'ui.js',
     'viewports.js',
 )
deleted file mode 100644
--- a/devtools/client/responsive.html/reducers/reload-conditions.js
+++ /dev/null
@@ -1,42 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-"use strict";
-
-const {
-  CHANGE_RELOAD_CONDITION,
-  LOAD_RELOAD_CONDITIONS_END,
-} = require("../actions/index");
-
-const Types = require("../types");
-
-const INITIAL_RELOAD_CONDITIONS = {
-  touchSimulation: false,
-  userAgent: false,
-  state: Types.loadableState.INITIALIZED,
-};
-
-const reducers = {
-
-  [CHANGE_RELOAD_CONDITION](conditions, { id, value }) {
-    return Object.assign({}, conditions, {
-      [id]: value,
-    });
-  },
-
-  [LOAD_RELOAD_CONDITIONS_END](conditions) {
-    return Object.assign({}, conditions, {
-      state: Types.loadableState.LOADED,
-    });
-  },
-
-};
-
-module.exports = function(conditions = INITIAL_RELOAD_CONDITIONS, action) {
-  const reducer = reducers[action.type];
-  if (!reducer) {
-    return conditions;
-  }
-  return reducer(conditions, action);
-};
--- a/devtools/client/responsive.html/reducers/screenshot.js
+++ b/devtools/client/responsive.html/reducers/screenshot.js
@@ -11,21 +11,27 @@ const {
 
 const INITIAL_SCREENSHOT = {
   isCapturing: false,
 };
 
 const reducers = {
 
   [TAKE_SCREENSHOT_END](screenshot, action) {
-    return Object.assign({}, screenshot, { isCapturing: false });
+    return {
+      ...screenshot,
+      isCapturing: false,
+    };
   },
 
   [TAKE_SCREENSHOT_START](screenshot, action) {
-    return Object.assign({}, screenshot, { isCapturing: true });
+    return {
+      ...screenshot,
+      isCapturing: true,
+    };
   },
 };
 
 module.exports = function(screenshot = INITIAL_SCREENSHOT, action) {
   const reducer = reducers[action.type];
   if (!reducer) {
     return screenshot;
   }
--- a/devtools/client/responsive.html/reducers/ui.js
+++ b/devtools/client/responsive.html/reducers/ui.js
@@ -5,76 +5,113 @@
 "use strict";
 
 const Services = require("Services");
 
 const {
   CHANGE_DISPLAY_PIXEL_RATIO,
   CHANGE_USER_AGENT,
   TOGGLE_LEFT_ALIGNMENT,
+  TOGGLE_RELOAD_ON_TOUCH_SIMULATION,
+  TOGGLE_RELOAD_ON_USER_AGENT,
   TOGGLE_TOUCH_SIMULATION,
   TOGGLE_USER_AGENT_INPUT,
 } = require("../actions/index");
 
 const LEFT_ALIGNMENT_ENABLED = "devtools.responsive.leftAlignViewport.enabled";
+const RELOAD_ON_TOUCH_SIMULATION = "devtools.responsive.reloadConditions.touchSimulation";
+const RELOAD_ON_USER_AGENT = "devtools.responsive.reloadConditions.userAgent";
 const SHOW_USER_AGENT_INPUT = "devtools.responsive.showUserAgentInput";
 
 const INITIAL_UI = {
   // The pixel ratio of the display.
   displayPixelRatio: 0,
   // Whether or not the viewports are left aligned.
   leftAlignmentEnabled: Services.prefs.getBoolPref(LEFT_ALIGNMENT_ENABLED),
+  // Whether or not to reload when touch simulation is toggled.
+  reloadOnTouchSimulation: Services.prefs.getBoolPref(RELOAD_ON_TOUCH_SIMULATION),
+  // Whether or not to reload when user agent is changed.
+  reloadOnUserAgent: Services.prefs.getBoolPref(RELOAD_ON_USER_AGENT),
   // Whether or not to show the user agent input in the toolbar.
   showUserAgentInput: Services.prefs.getBoolPref(SHOW_USER_AGENT_INPUT),
   // Whether or not touch simulation is enabled.
   touchSimulationEnabled: false,
   // The user agent of the viewport.
   userAgent: "",
 };
 
 const reducers = {
 
   [CHANGE_DISPLAY_PIXEL_RATIO](ui, { displayPixelRatio }) {
-    return Object.assign({}, ui, {
+    return {
+      ...ui,
       displayPixelRatio,
-    });
+    };
   },
 
   [CHANGE_USER_AGENT](ui, { userAgent }) {
-    return Object.assign({}, ui, {
+    return {
+      ...ui,
       userAgent,
-    });
+    };
   },
 
   [TOGGLE_LEFT_ALIGNMENT](ui, { enabled }) {
     const leftAlignmentEnabled = enabled !== undefined ?
       enabled : !ui.leftAlignmentEnabled;
 
     Services.prefs.setBoolPref(LEFT_ALIGNMENT_ENABLED, leftAlignmentEnabled);
 
-    return Object.assign({}, ui, {
+    return {
+      ...ui,
       leftAlignmentEnabled,
-    });
+    };
+  },
+
+  [TOGGLE_RELOAD_ON_TOUCH_SIMULATION](ui, { enabled }) {
+    const reloadOnTouchSimulation = enabled !== undefined ?
+      enabled : !ui.reloadOnTouchSimulation;
+
+    Services.prefs.setBoolPref(RELOAD_ON_TOUCH_SIMULATION, reloadOnTouchSimulation);
+
+    return {
+      ...ui,
+      reloadOnTouchSimulation,
+    };
+  },
+
+  [TOGGLE_RELOAD_ON_USER_AGENT](ui, { enabled }) {
+    const reloadOnUserAgent = enabled !== undefined ?
+      enabled : !ui.reloadOnUserAgent;
+
+    Services.prefs.setBoolPref(RELOAD_ON_USER_AGENT, reloadOnUserAgent);
+
+    return {
+      ...ui,
+      reloadOnUserAgent,
+    };
   },
 
   [TOGGLE_TOUCH_SIMULATION](ui, { enabled }) {
-    return Object.assign({}, ui, {
+    return {
+      ...ui,
       touchSimulationEnabled: enabled,
-    });
+    };
   },
 
   [TOGGLE_USER_AGENT_INPUT](ui, { enabled }) {
     const showUserAgentInput = enabled !== undefined ?
       enabled : !ui.showUserAgentInput;
 
     Services.prefs.setBoolPref(SHOW_USER_AGENT_INPUT, showUserAgentInput);
 
-    return Object.assign({}, ui, {
+    return {
+      ...ui,
       showUserAgentInput,
-    });
+    };
   },
 
 };
 
 module.exports = function(ui = INITIAL_UI, action) {
   const reducer = reducers[action.type];
   if (!reducer) {
     return ui;
--- a/devtools/client/responsive.html/reducers/viewports.js
+++ b/devtools/client/responsive.html/reducers/viewports.js
@@ -15,102 +15,113 @@ const {
 
 let nextViewportId = 0;
 
 const INITIAL_VIEWPORTS = [];
 const INITIAL_VIEWPORT = {
   id: nextViewportId++,
   device: "",
   deviceType: "",
+  height: 480,
   width: 320,
-  height: 480,
   pixelRatio: 0,
   userContextId: 0,
 };
 
 const reducers = {
 
   [ADD_VIEWPORT](viewports, { userContextId }) {
     // For the moment, there can be at most one viewport.
     if (viewports.length === 1) {
       return viewports;
     }
-    return [...viewports, Object.assign({}, INITIAL_VIEWPORT, {
-      userContextId,
-    })];
+
+    return [
+      ...viewports,
+      {
+        ...INITIAL_VIEWPORT,
+        userContextId,
+      },
+    ];
   },
 
   [CHANGE_DEVICE](viewports, { id, device, deviceType }) {
     return viewports.map(viewport => {
       if (viewport.id !== id) {
         return viewport;
       }
 
-      return Object.assign({}, viewport, {
+      return {
+        ...viewport,
         device,
         deviceType,
-      });
+      };
     });
   },
 
   [CHANGE_PIXEL_RATIO](viewports, { id, pixelRatio }) {
     return viewports.map(viewport => {
       if (viewport.id !== id) {
         return viewport;
       }
 
-      return Object.assign({}, viewport, {
+      return {
+        ...viewport,
         pixelRatio,
-      });
+      };
     });
   },
 
   [REMOVE_DEVICE_ASSOCIATION](viewports, { id }) {
     return viewports.map(viewport => {
       if (viewport.id !== id) {
         return viewport;
       }
 
-      return Object.assign({}, viewport, {
+      return {
+        ...viewport,
         device: "",
         deviceType: "",
-      });
+      };
     });
   },
 
   [RESIZE_VIEWPORT](viewports, { id, width, height }) {
     return viewports.map(viewport => {
       if (viewport.id !== id) {
         return viewport;
       }
 
-      if (!width) {
-        width = viewport.width;
-      }
       if (!height) {
         height = viewport.height;
       }
 
-      return Object.assign({}, viewport, {
+      if (!width) {
+        width = viewport.width;
+      }
+
+      return {
+        ...viewport,
+        height,
         width,
-        height,
-      });
+      };
     });
   },
 
   [ROTATE_VIEWPORT](viewports, { id }) {
     return viewports.map(viewport => {
       if (viewport.id !== id) {
         return viewport;
       }
 
-      return Object.assign({}, viewport, {
+      return {
+        ...viewport,
+        height: viewport.width,
         width: viewport.height,
-        height: viewport.width,
-      });
+      };
     });
   },
 
 };
 
 module.exports = function(viewports = INITIAL_VIEWPORTS, action) {
   const reducer = reducers[action.type];
   if (!reducer) {
--- a/devtools/client/responsive.html/test/browser/browser_telemetry_activate_rdm.js
+++ b/devtools/client/responsive.html/test/browser/browser_telemetry_activate_rdm.js
@@ -52,17 +52,17 @@ add_task(async function() {
   // Let's reset the counts.
   Services.telemetry.clearEvents();
 
   // Ensure no events have been logged
   const snapshot = Services.telemetry.snapshotEvents(OPTOUT, true);
   ok(!snapshot.parent, "No events have been logged for the main process");
 
   const tab = await addTab(URL);
-  const target = TargetFactory.forTab(tab);
+  const target = await TargetFactory.forTab(tab);
 
   await openCloseRDM(tab);
   await gDevTools.showToolbox(target, "inspector");
   await openCloseRDM(tab);
   await checkResults();
 });
 
 async function openCloseRDM(tab) {
--- a/devtools/client/responsive.html/test/browser/browser_toolbox_swap_browsers.js
+++ b/devtools/client/responsive.html/test/browser/browser_toolbox_swap_browsers.js
@@ -20,17 +20,17 @@ function getServerConnections(browser) {
 }
 
 const checkServerConnectionCount = async function(browser, expected, msg) {
   const conns = await getServerConnections(browser);
   is(conns.length || 0, expected, "Server connection count: " + msg);
 };
 
 const checkToolbox = async function(tab, location) {
-  const target = TargetFactory.forTab(tab);
+  const target = await TargetFactory.forTab(tab);
   ok(!!gDevTools.getToolbox(target), `Toolbox exists ${location}`);
 };
 
 add_task(async function() {
   const tab = await addTab(TEST_URL);
 
   const tabsInDifferentProcesses = E10S_MULTI_ENABLED &&
     (gBrowser.tabs[0].linkedBrowser.frameLoader.childID !=
--- a/devtools/client/responsive.html/test/browser/browser_toolbox_swap_inspector.js
+++ b/devtools/client/responsive.html/test/browser/browser_toolbox_swap_inspector.js
@@ -3,17 +3,17 @@
 
 "use strict";
 
 // Verify that inspector does not reboot when opening and closing RDM.
 
 const TEST_URL = "http://example.com/";
 
 const checkToolbox = async function(tab, location) {
-  const target = TargetFactory.forTab(tab);
+  const target = await TargetFactory.forTab(tab);
   ok(!!gDevTools.getToolbox(target), `Toolbox exists ${location}`);
 };
 
 add_task(async function() {
   const tab = await addTab(TEST_URL);
 
   info("Open toolbox outside RDM");
   {
--- a/devtools/client/responsive.html/types.js
+++ b/devtools/client/responsive.html/types.js
@@ -17,34 +17,16 @@ const { createEnum } = require("devtools
  */
 exports.loadableState = createEnum([
   "INITIALIZED",
   "LOADING",
   "LOADED",
   "ERROR",
 ]);
 
-/* GLOBAL */
-
-/**
- * Whether to reload the page automatically when certain actions occur.
- */
-exports.reloadConditions = {
-
-  // Whether to reload when touch simulation is toggled
-  touchSimulation: PropTypes.bool,
-
-  // Whether to reload when user agent is changed
-  userAgent: PropTypes.bool,
-
-  // Loaded state of these conditions
-  state: PropTypes.oneOf(Object.keys(exports.loadableState)),
-
-};
-
 /* DEVICE */
 
 /**
  * A single device that can be displayed in the viewport.
  */
 const device = {
 
   // The name of the device
@@ -121,26 +103,16 @@ exports.networkThrottling = {
   enabled: PropTypes.bool,
 
   // Name of the selected throttling profile
   profile: PropTypes.string,
 
 };
 
 /**
- * Touch simulation state for a given viewport.
- */
-exports.touchSimulation = {
-
-  // Whether or not touch simulation is enabled
-  enabled: PropTypes.bool,
-
-};
-
-/**
  * A single viewport displaying a document.
  */
 exports.viewport = {
 
   // The id of the viewport
   id: PropTypes.number,
 
   // The currently selected device applied to the viewport
--- a/devtools/client/responsive.html/utils/notification.js
+++ b/devtools/client/responsive.html/utils/notification.js
@@ -17,25 +17,25 @@ loader.lazyRequireGetter(this, "gDevTool
  *        The browser tab.
  * @param options
  *        Other options associated with opening.  Currently includes:
  *        - `toolbox`: Whether initiated via toolbox button
  *        - `msg`: String to show in the notification
  *        - `priority`: Priority level for the notification, which affects the icon and
  *                      overall appearance.
  */
-function showNotification(window, tab, { toolboxButton, msg, priority } = {}) {
+async function showNotification(window, tab, { toolboxButton, msg, priority } = {}) {
   // Default to using the browser's per-tab notification box
   let nbox = window.gBrowser.getNotificationBox(tab.linkedBrowser);
 
   // If opening was initiated by a toolbox button, check for an open
   // toolbox for the tab.  If one exists, use the toolbox's notification box so that the
   // message is placed closer to the action taken by the user.
   if (toolboxButton) {
-    const target = TargetFactory.forTab(tab);
+    const target = await TargetFactory.forTab(tab);
     const toolbox = gDevTools.getToolbox(target);
     if (toolbox) {
       nbox = toolbox.notificationBox;
     }
   }
 
   const value = "devtools-responsive";
   if (nbox.getNotificationWithValue(value)) {
--- a/devtools/client/scratchpad/scratchpad.js
+++ b/devtools/client/scratchpad/scratchpad.js
@@ -581,30 +581,30 @@ var Scratchpad = {
   /**
    * Reload the current page and execute the entire editor content when
    * the page finishes loading. Note that this operation should be available
    * only in the content context.
    *
    * @return Promise
    *         The promise for the script evaluation result.
    */
-  reloadAndRun: function SP_reloadAndRun() {
+  reloadAndRun: async function SP_reloadAndRun() {
     const deferred = defer();
 
     if (this.executionContext !== SCRATCHPAD_CONTEXT_CONTENT) {
       console.error(this.strings
                     .GetStringFromName("scratchpadContext.invalid"));
       return;
     }
 
-    const target = TargetFactory.forTab(this.gBrowser.selectedTab);
+    const target = await TargetFactory.forTab(this.gBrowser.selectedTab);
     target.once("navigate", () => {
       this.run().then(results => deferred.resolve(results));
     });
-    target.makeRemote().then(() => target.activeTab.reload());
+    target.attach().then(() => target.activeTab.reload());
 
     return deferred.promise;
   },
 
   /**
    * Execute the selected text (if any) or the entire editor content in the
    * current context. The evaluation result is inserted into the editor after
    * the selected text, or at the end of the editor content if there is no
@@ -1538,18 +1538,18 @@ var Scratchpad = {
    */
   openErrorConsole: function SP_openErrorConsole() {
     HUDService.toggleBrowserConsole();
   },
 
   /**
    * Open the Web Console.
    */
-  openWebConsole: function SP_openWebConsole() {
-    const target = TargetFactory.forTab(this.gBrowser.selectedTab);
+  openWebConsole: async function SP_openWebConsole() {
+    const target = await TargetFactory.forTab(this.gBrowser.selectedTab);
     gDevTools.showToolbox(target, "webconsole");
     this.browserWindow.focus();
   },
 
   /**
    * Set the current execution context to be the active tab content window.
    */
   setContentContext: function SP_setContentContext() {
@@ -2087,24 +2087,24 @@ ScratchpadTab.prototype = {
   /**
    * Attach to this tab.
    *
    * @param object aSubject
    *        The tab or window to obtain the connection for.
    * @return Promise
    *         The promise for the TabTarget for this tab.
    */
-  _attach: function ST__attach(aSubject) {
-    const target = TargetFactory.forTab(this._tab);
+  _attach: async function ST__attach(aSubject) {
+    const target = await TargetFactory.forTab(this._tab);
     target.once("close", () => {
       if (scratchpadTargets) {
         scratchpadTargets.delete(aSubject);
       }
     });
-    return target.makeRemote().then(() => target);
+    return target.attach().then(() => target);
   },
 };
 
 /**
  * Represents the DebuggerClient connection to a specific window as used by the
  * Scratchpad.
  */
 function ScratchpadWindow() {}
@@ -2138,17 +2138,17 @@ function ScratchpadTarget(aTarget) {
 
 ScratchpadTarget.consoleFor = ScratchpadTab.consoleFor;
 
 ScratchpadTarget.prototype = extend(ScratchpadTab.prototype, {
   _attach: function ST__attach() {
     if (this._target.isRemote) {
       return promise.resolve(this._target);
     }
-    return this._target.makeRemote().then(() => this._target);
+    return this._target.attach().then(() => this._target);
   }
 });
 
 /**
  * Encapsulates management of the sidebar containing the VariablesView for
  * object inspection.
  */
 function ScratchpadSidebar(aScratchpad) {
--- a/devtools/client/scratchpad/test/browser_scratchpad_close_toolbox.js
+++ b/devtools/client/scratchpad/test/browser_scratchpad_close_toolbox.js
@@ -18,17 +18,17 @@ function test() {
 async function runTests([win, sp]) {
   // Use the scratchpad before opening the toolbox.
   const source = "window.foobar = 7;";
   sp.setText(source);
   const [,, result] = await sp.display();
   is(result, 7, "Display produced the expected output.");
 
   // Now open the toolbox and close it again.
-  const target = TargetFactory.forTab(gBrowser.selectedTab);
+  const target = await TargetFactory.forTab(gBrowser.selectedTab);
   const toolbox = await gDevTools.showToolbox(target, "webconsole");
   ok(toolbox, "Toolbox was opened.");
   const closed = await gDevTools.closeToolbox(target);
   is(closed, true, "Toolbox was closed.");
 
   // Now see if using the scratcphad works as expected.
   sp.setText(source);
   const [,, result2] = await sp.display();
--- a/devtools/client/scratchpad/test/browser_scratchpad_menubar.js
+++ b/devtools/client/scratchpad/test/browser_scratchpad_menubar.js
@@ -23,14 +23,14 @@ add_task(async function() {
 add_task(async function() {
   // Now open the scratchpad panel after setting visibility preference.
   info("Test existence of menu bar of scratchpad panel.");
   await new Promise(resolve => {
     SpecialPowers.pushPrefEnv({"set": [["devtools.scratchpad.enabled", true]]}, resolve);
   });
 
   info("Open devtools on the Scratchpad.");
-  const target = TargetFactory.forTab(gBrowser.selectedTab);
+  const target = await TargetFactory.forTab(gBrowser.selectedTab);
   const toolbox = await gDevTools.showToolbox(target, "scratchpad");
 
   const menuToolbar = toolbox.doc.getElementById("sp-menu-toolbar");
   ok(!menuToolbar, "The scratchpad panel should not have a menu bar.");
 });
--- a/devtools/client/scratchpad/test/browser_scratchpad_wrong_window_focus.js
+++ b/devtools/client/scratchpad/test/browser_scratchpad_wrong_window_focus.js
@@ -20,18 +20,18 @@ function test() {
 
   gBrowser.selectedTab = BrowserTestUtils.addTab(gBrowser);
   BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser).then(function() {
     openScratchpad(function() {
       const sw = gScratchpadWindow;
       const {require} = ChromeUtils.import("resource://devtools/shared/Loader.jsm", {});
       const {TargetFactory} = require("devtools/client/framework/target");
 
-      openScratchpad(function() {
-        const target = TargetFactory.forTab(gBrowser.selectedTab);
+      openScratchpad(async function() {
+        const target = await TargetFactory.forTab(gBrowser.selectedTab);
         gDevTools.showToolbox(target, "webconsole").then((toolbox) => {
           const hud = toolbox.getCurrentPanel().hud;
           hud.ui.clearOutput(true);
           testFocus(sw, hud);
         });
       });
     });
   });
--- a/devtools/client/shadereditor/panel.js
+++ b/devtools/client/shadereditor/panel.js
@@ -31,17 +31,17 @@ ShaderEditorPanel.prototype = {
    * Open is effectively an asynchronous constructor.
    *
    * @return object
    *         A promise that is resolved when the Shader Editor completes opening.
    */
   async open() {
     // Local debugging needs to make the target remote.
     if (!this.target.isRemote) {
-      await this.target.makeRemote();
+      await this.target.attach();
     }
 
     this.front = new WebGLFront(this.target.client, this.target.form);
     this.shadersListView = new ShadersListView();
     this.eventsHandler = new EventsHandler();
     this.shadersEditorsView = new ShadersEditorsView();
     await this.shadersListView.initialize(this._toolbox, this.shadersEditorsView);
     await this.eventsHandler.initialize(this, this._toolbox, this.target, this.front,
--- a/devtools/client/shadereditor/test/head.js
+++ b/devtools/client/shadereditor/test/head.js
@@ -144,33 +144,33 @@ function reload(aTarget, aWaitForTargetE
 function initBackend(aUrl) {
   info("Initializing a shader editor front.");
 
   DebuggerServer.init();
   DebuggerServer.registerAllActors();
 
   return (async function() {
     const tab = await addTab(aUrl);
-    const target = TargetFactory.forTab(tab);
+    const target = await TargetFactory.forTab(tab);
 
-    await target.makeRemote();
+    await target.attach();
 
     const front = new WebGLFront(target.client, target.form);
     return { target, front };
   })();
 }
 
 function initShaderEditor(aUrl) {
   info("Initializing a shader editor pane.");
 
   return (async function() {
     const tab = await addTab(aUrl);
-    const target = TargetFactory.forTab(tab);
+    const target = await TargetFactory.forTab(tab);
 
-    await target.makeRemote();
+    await target.attach();
 
     Services.prefs.setBoolPref("devtools.shadereditor.enabled", true);
     const toolbox = await gDevTools.showToolbox(target, "shadereditor");
     const panel = toolbox.getCurrentPanel();
     return { target, panel };
   })();
 }
 
--- a/devtools/client/shared/link.js
+++ b/devtools/client/shared/link.js
@@ -57,17 +57,17 @@ exports.openDocLink = async function(url
 exports.openContentLink = async function(url, options = {}) {
   const top = _getTopWindow();
   if (!top) {
     return;
   }
   if (!options.triggeringPrincipal && top.gBrowser) {
     const tab = top.gBrowser.selectedTab;
     if (TargetFactory.isKnownTab(tab)) {
-      const target = TargetFactory.forTab(tab);
+      const target = await TargetFactory.forTab(tab);
       options.triggeringPrincipal = target.contentPrincipal;
     }
   }
   top.openWebLinkIn(url, "tab", options);
 };
 
 /**
  * Open a trusted |url| in a new tab using the SystemPrincipal.
--- a/devtools/client/shared/test/browser_telemetry_button_eyedropper.js
+++ b/devtools/client/shared/test/browser_telemetry_button_eyedropper.js
@@ -5,17 +5,17 @@
 
 const TEST_URI = "data:text/html;charset=utf-8," +
   "<p>browser_telemetry_button_eyedropper.js</p><div>test</div>";
 
 add_task(async function() {
   await addTab(TEST_URI);
   startTelemetry();
 
-  const target = TargetFactory.forTab(gBrowser.selectedTab);
+  const target = await TargetFactory.forTab(gBrowser.selectedTab);
   const toolbox = await gDevTools.showToolbox(target, "inspector");
 
   info("testing the eyedropper button");
   await testButton(toolbox);
 
   await gDevTools.closeToolbox(target);
   gBrowser.removeCurrentTab();
 });
--- a/devtools/client/shared/test/browser_telemetry_button_paintflashing.js
+++ b/devtools/client/shared/test/browser_telemetry_button_paintflashing.js
@@ -12,17 +12,17 @@ const TEST_URI = "data:text/html;charset
 const TOOL_DELAY = 200;
 
 add_task(async function() {
   await addTab(TEST_URI);
   startTelemetry();
 
   await pushPref("devtools.command-button-paintflashing.enabled", true);
 
-  const target = TargetFactory.forTab(gBrowser.selectedTab);
+  const target = await TargetFactory.forTab(gBrowser.selectedTab);
   const toolbox = await gDevTools.showToolbox(target, "inspector");
   info("inspector opened");
 
   info("testing the paintflashing button");
   await testButton(toolbox);
 
   await gDevTools.closeToolbox(target);
   gBrowser.removeCurrentTab();
--- a/devtools/client/shared/test/browser_telemetry_button_responsive.js
+++ b/devtools/client/shared/test/browser_telemetry_button_responsive.js
@@ -31,17 +31,17 @@ registerCleanupFunction(() => {
 });
 
 loader.lazyRequireGetter(this, "ResponsiveUIManager", "devtools/client/responsive.html/manager", true);
 
 add_task(async function() {
   await addTab(TEST_URI);
   startTelemetry();
 
-  const target = TargetFactory.forTab(gBrowser.selectedTab);
+  const target = await TargetFactory.forTab(gBrowser.selectedTab);
   const toolbox = await gDevTools.showToolbox(target, "inspector");
   info("inspector opened");
 
   info("testing the responsivedesign button");
   await testButton(toolbox);
 
   await gDevTools.closeToolbox(target);
   gBrowser.removeCurrentTab();
--- a/devtools/client/shared/test/browser_telemetry_button_scratchpad.js
+++ b/devtools/client/shared/test/browser_telemetry_button_scratchpad.js
@@ -12,17 +12,17 @@ const TEST_URI = "data:text/html;charset
 const TOOL_DELAY = 200;
 
 add_task(async function() {
   await addTab(TEST_URI);
   startTelemetry();
 
   await pushPref("devtools.command-button-scratchpad.enabled", true);
 
-  const target = TargetFactory.forTab(gBrowser.selectedTab);
+  const target = await TargetFactory.forTab(gBrowser.selectedTab);
   const toolbox = await gDevTools.showToolbox(target, "inspector");
   info("inspector opened");
 
   const onAllWindowsOpened = trackScratchpadWindows();
 
   info("testing the scratchpad button");
   await testButton(toolbox);
   await onAllWindowsOpened;
--- a/devtools/client/shared/test/browser_telemetry_sidebar.js
+++ b/devtools/client/shared/test/browser_telemetry_sidebar.js
@@ -108,17 +108,17 @@ add_task(async function() {
 
   // Ensure no events have been logged
   const snapshot = Services.telemetry.snapshotEvents(OPTOUT, true);
   ok(!snapshot.parent, "No events have been logged for the main process");
 
   await addTab(TEST_URI);
   startTelemetry();
 
-  const target = TargetFactory.forTab(gBrowser.selectedTab);
+  const target = await TargetFactory.forTab(gBrowser.selectedTab);
   const toolbox = await gDevTools.showToolbox(target, "inspector");
   info("inspector opened");
 
   await testSidebar(toolbox);
   checkResults();
   checkEventTelemetry();
 
   await gDevTools.closeToolbox(target);
--- a/devtools/client/shared/test/browser_theme_switching.js
+++ b/devtools/client/shared/test/browser_theme_switching.js
@@ -1,16 +1,16 @@
 /* vim: set ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 add_task(async function() {
-  const target = TargetFactory.forTab(gBrowser.selectedTab);
+  const target = await TargetFactory.forTab(gBrowser.selectedTab);
   const toolbox = await gDevTools.showToolbox(target);
   const doc = toolbox.doc;
   const root = doc.documentElement;
 
   const platform = root.getAttribute("platform");
   const expectedPlatform = getPlatform();
   is(platform, expectedPlatform, ":root[platform] is correct");
 
--- a/devtools/client/shared/test/head.js
+++ b/devtools/client/shared/test/head.js
@@ -124,17 +124,17 @@ async function(type = "bottom", src = CH
  * some amount of time in between.
  * @param {Number} nbOfTimes
  * @param {Number} usageTime in milliseconds
  * @param {String} toolId
  */
 async function openAndCloseToolbox(nbOfTimes, usageTime, toolId) {
   for (let i = 0; i < nbOfTimes; i++) {
     info("Opening toolbox " + (i + 1));
-    const target = TargetFactory.forTab(gBrowser.selectedTab);
+    const target = await TargetFactory.forTab(gBrowser.selectedTab);
     await gDevTools.showToolbox(target, toolId);
 
     // We use a timeout to check the toolbox's active time
     await new Promise(resolve => setTimeout(resolve, usageTime));
 
     info("Closing toolbox " + (i + 1));
     await gDevTools.closeToolbox(target);
   }
--- a/devtools/client/shared/test/shared-head.js
+++ b/devtools/client/shared/test/shared-head.js
@@ -403,18 +403,18 @@ function wait(ms) {
  * @param {String} toolId Optional. The ID of the tool to be selected.
  * @param {String} hostType Optional. The type of toolbox host to be used.
  * @return {Promise} Resolves with the toolbox, when it has been opened.
  */
 var openToolboxForTab = async function(tab, toolId, hostType) {
   info("Opening the toolbox");
 
   let toolbox;
-  const target = TargetFactory.forTab(tab);
-  await target.makeRemote();
+  const target = await TargetFactory.forTab(tab);
+  await target.attach();
 
   // Check if the toolbox is already loaded.
   toolbox = gDevTools.getToolbox(target);
   if (toolbox) {
     if (!toolId || (toolId && toolbox.getPanel(toolId))) {
       info("Toolbox is already opened");
       return toolbox;
     }
@@ -446,17 +446,17 @@ var openNewTabAndToolbox = async functio
 
 /**
  * Close a tab and if necessary, the toolbox that belongs to it
  * @param {Tab} tab The tab to close.
  * @return {Promise} Resolves when the toolbox and tab have been destroyed and
  * closed.
  */
 var closeTabAndToolbox = async function(tab = gBrowser.selectedTab) {
-  const target = TargetFactory.forTab(tab);
+  const target = await TargetFactory.forTab(tab);
   if (target) {
     await gDevTools.closeToolbox(target);
   }
 
   await removeTab(tab);
 };
 
 /**
@@ -613,17 +613,17 @@ function pushPref(preferenceName, value)
  * @return {?} anything that is found at the provided path in the object.
  */
 function lookupPath(obj, path) {
   const segments = path.split(".");
   return segments.reduce((prev, current) => prev[current], obj);
 }
 
 var closeToolbox = async function() {
-  const target = TargetFactory.forTab(gBrowser.selectedTab);
+  const target = await TargetFactory.forTab(gBrowser.selectedTab);
   await gDevTools.closeToolbox(target);
 };
 
 /**
  * Clean the logical clipboard content. This method only clears the OS clipboard on
  * Windows (see Bug 666254).
  */
 function emptyClipboard() {
--- a/devtools/client/sourceeditor/test/browser_css_autocompletion.js
+++ b/devtools/client/sourceeditor/test/browser_css_autocompletion.js
@@ -79,18 +79,18 @@ add_task(async function test() {
   const tab = await addTab(TEST_URI);
   browser = tab.linkedBrowser;
   await runTests();
   browser = null;
   gBrowser.removeCurrentTab();
 });
 
 async function runTests() {
-  const target = TargetFactory.forTab(gBrowser.selectedTab);
-  await target.makeRemote();
+  const target = await TargetFactory.forTab(gBrowser.selectedTab);
+  await target.attach();
   inspector = InspectorFront(target.client, target.form);
   const walker = await inspector.getWalker();
   completer = new CSSCompleter({walker: walker,
                                 cssProperties: getClientCssProperties()});
   await checkStateAndMoveOn();
   await completer.walker.release();
   inspector.destroy();
   inspector = null;
--- a/devtools/client/sourceeditor/test/browser_editor_autocomplete_events.js
+++ b/devtools/client/sourceeditor/test/browser_editor_autocomplete_events.js
@@ -9,18 +9,18 @@ const TEST_URI = "data:text/html;charset
                  "<div id='baz'></div><body></html>";
 
 add_task(async function() {
   await addTab(TEST_URI);
   await runTests();
 });
 
 async function runTests() {
-  const target = TargetFactory.forTab(gBrowser.selectedTab);
-  await target.makeRemote();
+  const target = await TargetFactory.forTab(gBrowser.selectedTab);
+  await target.attach();
   const inspector = InspectorFront(target.client, target.form);
   const walker = await inspector.getWalker();
   const {ed, win, edWin} = await setup(null, {
     autocomplete: true,
     mode: Editor.modes.css,
     autocompleteOpts: {walker: walker, cssProperties: getClientCssProperties()}
   });
   await testMouse(ed, edWin);
--- a/devtools/client/storage/panel.js
+++ b/devtools/client/storage/panel.js
@@ -34,17 +34,17 @@ class StoragePanel {
 
   /**
    * open is effectively an asynchronous constructor
    */
   open() {
     let targetPromise;
     // We always interact with the target as if it were remote
     if (!this.target.isRemote) {
-      targetPromise = this.target.makeRemote();
+      targetPromise = this.target.attach();
     } else {
       targetPromise = Promise.resolve(this.target);
     }
 
     return targetPromise.then(() => {
       this.target.on("close", this.destroy);
       this._front = new StorageFront(this.target.client, this.target.form);
 
--- a/devtools/client/storage/test/browser_storage_indexeddb_overflow.js
+++ b/devtools/client/storage/test/browser_storage_indexeddb_overflow.js
@@ -10,17 +10,17 @@ const ITEMS_PER_PAGE = 50;
 
 add_task(async function() {
   await openTabAndSetupStorage(MAIN_DOMAIN + "storage-overflow-indexeddb.html");
 
   info("Run the tests with short DevTools");
   await runTests();
 
   info("Close Toolbox");
-  const target = TargetFactory.forTab(gBrowser.selectedTab);
+  const target = await TargetFactory.forTab(gBrowser.selectedTab);
   await gDevTools.closeToolbox(target);
 
   await finishTests();
 });
 
 async function runTests() {
   gUI.tree.expandAll();
 
--- a/devtools/client/storage/test/browser_storage_overflow.js
+++ b/devtools/client/storage/test/browser_storage_overflow.js
@@ -10,17 +10,17 @@ const ITEMS_PER_PAGE = 50;
 
 add_task(async function() {
   await openTabAndSetupStorage(MAIN_DOMAIN + "storage-overflow.html");
 
   info("Run the tests with short DevTools");
   await runTests();
 
   info("Close Toolbox");
-  const target = TargetFactory.forTab(gBrowser.selectedTab);
+  const target = await TargetFactory.forTab(gBrowser.selectedTab);
   await gDevTools.closeToolbox(target);
 
   info("Set a toolbox height of 1000px");
   await pushPref("devtools.toolbox.footer.height", 1000);
 
   info("Open storage panel again");
   await openStoragePanel();
 
--- a/devtools/client/storage/test/head.js
+++ b/devtools/client/storage/test/head.js
@@ -130,17 +130,17 @@ async function openTabAndSetupStorage(ur
  *
  * @param cb {Function} Optional callback, if you don't want to use the returned
  *                      promise
  *
  * @return {Promise} a promise that resolves when the storage inspector is ready
  */
 var openStoragePanel = async function(cb) {
   info("Opening the storage inspector");
-  const target = TargetFactory.forTab(gBrowser.selectedTab);
+  const target = await TargetFactory.forTab(gBrowser.selectedTab);
 
   let storage, toolbox;
 
   // Checking if the toolbox and the storage are already loaded
   // The storage-updated event should only be waited for if the storage
   // isn't loaded yet
   toolbox = gDevTools.getToolbox(target);
   if (toolbox) {
--- a/devtools/client/styleeditor/panel.js
+++ b/devtools/client/styleeditor/panel.js
@@ -37,17 +37,17 @@ StyleEditorPanel.prototype = {
   },
 
   /**
    * open is effectively an asynchronous constructor
    */
   async open() {
     // We always interact with the target as if it were remote
     if (!this.target.isRemote) {
-      await this.target.makeRemote();
+      await this.target.attach();
     }
 
     this.target.on("close", this.destroy);
 
     this._debuggee = await this._target.getFront("stylesheets");
 
     // Initialize the CSS properties database.
     const {cssProperties} = await initCssProperties(this._toolbox);
--- a/devtools/client/styleeditor/test/browser_styleeditor_fetch-from-netmonitor.js
+++ b/devtools/client/styleeditor/test/browser_styleeditor_fetch-from-netmonitor.js
@@ -6,17 +6,17 @@
 // A test to ensure Style Editor only issues 1 request for each stylesheet (instead of 2)
 // by using the network monitor's request history (bug 1306892).
 
 const TEST_URL = TEST_BASE_HTTP + "doc_fetch_from_netmonitor.html";
 
 add_task(async function() {
   info("Opening netmonitor");
   const tab = await addTab("about:blank");
-  const target = TargetFactory.forTab(tab);
+  const target = await TargetFactory.forTab(tab);
   const toolbox = await gDevTools.showToolbox(target, "netmonitor");
   const monitor = toolbox.getPanel("netmonitor");
   const { store, windowRequire } = monitor.panelWin;
   const Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
   const {
     getSortedRequests,
   } = windowRequire("devtools/client/netmonitor/src/selectors/index");
 
--- a/devtools/client/styleeditor/test/browser_styleeditor_loading.js
+++ b/devtools/client/styleeditor/test/browser_styleeditor_loading.js
@@ -8,17 +8,17 @@
 const TESTCASE_URI = TEST_BASE_HTTP + "longload.html";
 
 add_task(async function() {
   // launch Style Editor right when the tab is created (before load)
   // this checks that the Style Editor still launches correctly when it is
   // opened *while* the page is still loading. The Style Editor should not
   // signal that it is loaded until the accompanying content page is loaded.
   const tabAdded = addTab(TESTCASE_URI);
-  const target = TargetFactory.forTab(gBrowser.selectedTab);
+  const target = await TargetFactory.forTab(gBrowser.selectedTab);
   const styleEditorLoaded = gDevTools.showToolbox(target, "styleeditor");
 
   await Promise.all([tabAdded, styleEditorLoaded]);
 
   const toolbox = gDevTools.getToolbox(target);
   const panel = toolbox.getPanel("styleeditor");
   const { panelWindow } = panel;
 
--- a/devtools/client/styleeditor/test/browser_styleeditor_sync.js
+++ b/devtools/client/styleeditor/test/browser_styleeditor_sync.js
@@ -15,17 +15,17 @@ const expectedText = `
   }
 
   #testid {
     /*! font-size: 4em; */
   }
   `;
 
 async function closeAndReopenToolbox() {
-  const target = TargetFactory.forTab(gBrowser.selectedTab);
+  const target = await TargetFactory.forTab(gBrowser.selectedTab);
   await gDevTools.closeToolbox(target);
   const { ui: newui } = await openStyleEditor();
   return newui;
 }
 
 add_task(async function() {
   await addTab(TESTCASE_URI);
   const { inspector, view } = await openRuleView();
--- a/devtools/client/styleeditor/test/browser_styleeditor_xul.js
+++ b/devtools/client/styleeditor/test/browser_styleeditor_xul.js
@@ -7,16 +7,16 @@
 "use strict";
 
 waitForExplicitFinish();
 
 const TEST_URL = TEST_BASE + "doc_xulpage.xul";
 
 add_task(async function() {
   const tab = await addTab(TEST_URL);
-  const target = TargetFactory.forTab(tab);
+  const target = await TargetFactory.forTab(tab);
 
   const toolbox = await gDevTools.showToolbox(target, "styleeditor");
   const panel = toolbox.getCurrentPanel();
 
   ok(panel,
      "The style-editor panel did initialize correctly for the XUL window");
 });
--- a/devtools/client/styleeditor/test/head.js
+++ b/devtools/client/styleeditor/test/head.js
@@ -67,17 +67,17 @@ var reloadPageAndWaitForStyleSheets = as
 
 /**
  * Open the style editor for the current tab.
  */
 var openStyleEditor = async function(tab) {
   if (!tab) {
     tab = gBrowser.selectedTab;
   }
-  const target = TargetFactory.forTab(tab);
+  const target = await TargetFactory.forTab(tab);
   const toolbox = await gDevTools.showToolbox(target, "styleeditor");
   const panel = toolbox.getPanel("styleeditor");
   const ui = panel.UI;
 
   // The stylesheet list appears with an animation. Let this animation finish.
   const animations = ui._root.getAnimations({subtree: true});
   await Promise.all(animations.map(a => a.finished));
 
--- a/devtools/client/themes/markup.css
+++ b/devtools/client/themes/markup.css
@@ -427,33 +427,33 @@ ul.children + .tag-line::before {
     font-size: 9px;
     vertical-align: 1px;
   }
 }
 
 /* Markup badges that are interactive/clickable */
 .markup-badge[data-custom],
 .markup-badge[data-display="flex"].interactive,
-.markup-badge[data-display="grid"],
-.markup-badge[data-display="inline-flex"],
-.markup-badge[data-display="inline-grid"],
+.markup-badge[data-display="grid"].interactive,
+.markup-badge[data-display="inline-flex"].interactive,
+.markup-badge[data-display="inline-grid"].interactive,
 .markup-badge[data-event] {
   background-color: var(--markup-badge-interactive-background-color);
   color: var(--markup-badge-interactive-color);
   cursor: pointer;
 }
 
 .markup-badge[data-display="flex"]:not(.active).interactive:focus,
 .markup-badge[data-display="flex"]:not(.active).interactive:hover,
-.markup-badge[data-display="grid"]:not(.active):focus,
-.markup-badge[data-display="grid"]:not(.active):hover,
+.markup-badge[data-display="grid"]:not(.active).interactive:focus,
+.markup-badge[data-display="grid"]:not(.active).interactive:hover,
 .markup-badge[data-display="inline-flex"]:not(.active).interactive:focus,
 .markup-badge[data-display="inline-flex"]:not(.active).interactive:hover,
-.markup-badge[data-display="inline-grid"]:not(.active):focus,
-.markup-badge[data-display="inline-grid"]:not(.active):hover,
+.markup-badge[data-display="inline-grid"]:not(.active).interactive:focus,
+.markup-badge[data-display="inline-grid"]:not(.active).interactive:hover,
 .markup-badge[data-event]:focus,
 .markup-badge[data-event]:hover,
 .expandable.collapsed .markup-expand-badge:focus::before,
 .expandable.collapsed .markup-expand-badge:hover::before {
   background-color: var(--markup-badge-hover-background-color);
 }
 
 .markup-badge.active,
--- a/devtools/client/webaudioeditor/panel.js
+++ b/devtools/client/webaudioeditor/panel.js
@@ -20,17 +20,17 @@ function WebAudioEditorPanel(iframeWindo
 exports.WebAudioEditorPanel = WebAudioEditorPanel;
 
 WebAudioEditorPanel.prototype = {
   open: function() {
     let targetPromise;
 
     // Local debugging needs to make the target remote.
     if (!this.target.isRemote) {
-      targetPromise = this.target.makeRemote();
+      targetPromise = this.target.attach();
     } else {
       targetPromise = Promise.resolve(this.target);
     }
 
     return targetPromise
       .then(() => {
         this.panelWin.gToolbox = this._toolbox;
         this.panelWin.gTarget = this.target;
--- a/devtools/client/webaudioeditor/test/head.js
+++ b/devtools/client/webaudioeditor/test/head.js
@@ -59,38 +59,38 @@ function navigate(aTarget, aUrl, aWaitFo
 function initBackend(aUrl) {
   info("Initializing a web audio editor front.");
 
   DebuggerServer.init();
   DebuggerServer.registerAllActors();
 
   return (async function() {
     const tab = await addTab(aUrl);
-    const target = TargetFactory.forTab(tab);
+    const target = await TargetFactory.forTab(tab);
 
-    await target.makeRemote();
+    await target.attach();
 
     const front = new WebAudioFront(target.client, target.form);
     return { target, front };
   })();
 }
 
 /**
  * Adds a new tab, and open the toolbox for that tab, selecting the audio editor
  * panel.
  * This requires calling teardown before the test ends.
  */
 function initWebAudioEditor(aUrl) {
   info("Initializing a web audio editor pane.");
 
   return (async function() {
     const tab = await addTab(aUrl);
-    const target = TargetFactory.forTab(tab);
+    const target = await TargetFactory.forTab(tab);
 
-    await target.makeRemote();
+    await target.attach();
 
     Services.prefs.setBoolPref("devtools.webaudioeditor.enabled", true);
     const toolbox = await gDevTools.showToolbox(target, "webaudioeditor");
     const panel = toolbox.getCurrentPanel();
     return { target, panel, toolbox };
   })();
 }
 
--- a/devtools/client/webconsole/panel.js
+++ b/devtools/client/webconsole/panel.js
@@ -49,17 +49,17 @@ WebConsolePanel.prototype = {
       if (!doc || doc.readyState !== "complete") {
         await new Promise(resolve => {
           iframe.addEventListener("load", resolve, {capture: true, once: true});
         });
       }
 
       // Local debugging needs to make the target remote.
       if (!this.target.isRemote) {
-        await this.target.makeRemote();
+        await this.target.attach();
       }
 
       const webConsoleUIWindow = iframe.contentWindow.wrappedJSObject;
       const chromeWindow = iframe.ownerDocument.defaultView;
 
       // Open the Web Console.
       this.hud = await HUDService.openWebConsole(
         this.target, webConsoleUIWindow, chromeWindow);
--- a/devtools/client/webconsole/test/mochitest/browser_console_webconsole_ctrlw_close_tab.js
+++ b/devtools/client/webconsole/test/mochitest/browser_console_webconsole_ctrlw_close_tab.js
@@ -16,17 +16,17 @@ add_task(async function() {
   await pushPref("toolkit.cosmeticAnimations.enabled", false);
 
   let hud = await openNewTabAndConsole(TEST_URI);
 
   const tabClosed = defer();
   const toolboxDestroyed = defer();
   const tabSelected = defer();
 
-  const target = TargetFactory.forTab(gBrowser.selectedTab);
+  const target = await TargetFactory.forTab(gBrowser.selectedTab);
   const toolbox = gDevTools.getToolbox(target);
 
   gBrowser.tabContainer.addEventListener("TabClose", function() {
     info("tab closed");
     tabClosed.resolve(null);
   }, {once: true});
 
   gBrowser.tabContainer.addEventListener("TabSelect", function() {
--- a/devtools/client/webconsole/test/mochitest/browser_jsterm_autocomplete_in_debugger_stackframe.js
+++ b/devtools/client/webconsole/test/mochitest/browser_jsterm_autocomplete_in_debugger_stackframe.js
@@ -27,17 +27,17 @@ add_task(async function() {
 });
 
 async function performTests() {
   const { jsterm } = await openNewTabAndConsole(TEST_URI);
   const {
     autocompletePopup: popup,
   } = jsterm;
 
-  const target = TargetFactory.forTab(gBrowser.selectedTab);
+  const target = await TargetFactory.forTab(gBrowser.selectedTab);
   const toolbox = gDevTools.getToolbox(target);
 
   const jstermComplete = value => setInputValueForAutocompletion(jsterm, value);
 
   // Test that document.title gives string methods. Native getters must execute.
   await jstermComplete("document.title.");
 
   const newItemsLabels = getPopupLabels(popup);
--- a/devtools/client/webconsole/test/mochitest/browser_jsterm_await_paused.js
+++ b/devtools/client/webconsole/test/mochitest/browser_jsterm_await_paused.js
@@ -27,17 +27,17 @@ async function performTests() {
   const pauseExpression = `(() => {
     var foo = "bar";
     /* Will pause the script and open the debugger panel */
     debugger;
   })()`;
   jsterm.execute(pauseExpression);
 
   // wait for the debugger to be opened and paused.
-  const target = TargetFactory.forTab(gBrowser.selectedTab);
+  const target = await TargetFactory.forTab(gBrowser.selectedTab);
   const toolbox = gDevTools.getToolbox(target);
   const dbg = await waitFor(() => toolbox.getPanel("jsdebugger"));
   await waitFor(() => dbg._selectors.isPaused(dbg._getState()));
   await toolbox.toggleSplitConsole();
 
   const onMessage = waitForMessage(hud, "res: bar");
   const awaitExpression = `await new Promise(res => {
     setTimeout(() => res("res: " + foo), 1000);
--- a/devtools/client/webconsole/test/mochitest/browser_webconsole_close_unfocused_window.js
+++ b/devtools/client/webconsole/test/mochitest/browser_webconsole_close_unfocused_window.js
@@ -33,12 +33,12 @@ add_task(async function() {
   win2.close();
 
   info("Close the test tab in the first window");
   window.gBrowser.removeTab(tab1);
 
   ok(true, "No error was triggered during the test");
 });
 
-function closeToolboxForTab(tab) {
-  const target = TargetFactory.forTab(tab);
+async function closeToolboxForTab(tab) {
+  const target = await TargetFactory.forTab(tab);
   return gDevTools.closeToolbox(target);
 }
--- a/devtools/client/webconsole/test/mochitest/browser_webconsole_location_debugger_link.js
+++ b/devtools/client/webconsole/test/mochitest/browser_webconsole_location_debugger_link.js
@@ -24,17 +24,17 @@ add_task(async function() {
 
   // On e10s, the exception thrown in test-location-debugger-link-errors.js
   // is triggered in child process and is ignored by test harness
   if (!Services.appinfo.browserTabsRemoteAutostart) {
     expectUncaughtException();
   }
 
   const hud = await openNewTabAndConsole(TEST_URI);
-  const target = TargetFactory.forTab(gBrowser.selectedTab);
+  const target = await TargetFactory.forTab(gBrowser.selectedTab);
   const toolbox = gDevTools.getToolbox(target);
 
   await testOpenInDebugger(hud, toolbox, "document.bar");
 
   info("Selecting the console again");
   await toolbox.selectTool("webconsole");
   await testOpenInDebugger(hud, toolbox, "Blah Blah");
 
--- a/devtools/client/webconsole/test/mochitest/browser_webconsole_location_scratchpad_link.js
+++ b/devtools/client/webconsole/test/mochitest/browser_webconsole_location_scratchpad_link.js
@@ -8,17 +8,17 @@
 const TEST_URI = "data:text/html;charset=utf8,<p>test Scratchpad panel linking</p>";
 
 add_task(async function() {
   await pushPref("devtools.scratchpad.enabled", true);
   await openNewTabAndToolbox(TEST_URI);
 
   info("Opening toolbox with Scratchpad panel");
 
-  const target = TargetFactory.forTab(gBrowser.selectedTab);
+  const target = await TargetFactory.forTab(gBrowser.selectedTab);
   const toolbox = await gDevTools.showToolbox(target, "scratchpad", "window");
 
   const scratchpadPanel = toolbox.getPanel("scratchpad");
   const { scratchpad } = scratchpadPanel;
   is(toolbox.getCurrentPanel(), scratchpadPanel,
     "Scratchpad is currently selected panel");
 
   info("Switching to webconsole panel");
--- a/devtools/client/webconsole/test/mochitest/browser_webconsole_location_styleeditor_link.js
+++ b/devtools/client/webconsole/test/mochitest/browser_webconsole_location_styleeditor_link.js
@@ -7,17 +7,17 @@
 
 const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" +
                  "test/mochitest/" +
                  "test-location-styleeditor-link.html";
 
 add_task(async function() {
   await pushPref("devtools.webconsole.filter.css", true);
   const hud = await openNewTabAndConsole(TEST_URI);
-  const target = TargetFactory.forTab(gBrowser.selectedTab);
+  const target = await TargetFactory.forTab(gBrowser.selectedTab);
   const toolbox = gDevTools.getToolbox(target);
 
   await testViewSource(hud, toolbox, "\u2018font-weight\u2019");
 
   info("Selecting the console again");
   await toolbox.selectTool("webconsole");
   await testViewSource(hud, toolbox, "\u2018color\u2019");
 });
--- a/devtools/client/webconsole/test/mochitest/browser_webconsole_network_attach.js
+++ b/devtools/client/webconsole/test/mochitest/browser_webconsole_network_attach.js
@@ -9,17 +9,17 @@ const TEST_PATH = "http://example.com/br
 const TEST_URI = TEST_PATH + TEST_FILE;
 
 add_task(async function task() {
   await pushPref("devtools.webconsole.filter.net", false);
   await pushPref("devtools.webconsole.filter.netxhr", true);
   await openNewTabAndToolbox(TEST_URI, "netmonitor");
 
   const currentTab = gBrowser.selectedTab;
-  const target = TargetFactory.forTab(currentTab);
+  const target = await TargetFactory.forTab(currentTab);
   const toolbox = gDevTools.getToolbox(target);
 
   const monitor = toolbox.getCurrentPanel();
   const netReady = monitor.panelWin.api.once("NetMonitor:PayloadReady");
 
   // Fire an XHR POST request.
   await ContentTask.spawn(gBrowser.selectedBrowser, null, function() {
     content.wrappedJSObject.testXhrGet();
--- a/devtools/client/webconsole/test/mochitest/browser_webconsole_network_messages_expand.js
+++ b/devtools/client/webconsole/test/mochitest/browser_webconsole_network_messages_expand.js
@@ -51,17 +51,17 @@ const tabs = [{
 }];
 
 /**
  * Main test for checking HTTP logs in the Console panel.
  */
 add_task(async function task() {
   const hud = await openNewTabAndConsole(TEST_URI);
   const currentTab = gBrowser.selectedTab;
-  const target = TargetFactory.forTab(currentTab);
+  const target = await TargetFactory.forTab(currentTab);
 
   // Execute XHR and expand it after all network
   // update events are received. Consequently,
   // check out content of all (HTTP details) tabs.
   await openRequestAfterUpdates(target, hud);
 
   // Test proper UI update when request is opened.
   // For every tab (with HTTP details):
--- a/devtools/client/webconsole/test/mochitest/browser_webconsole_network_messages_openinnet.js
+++ b/devtools/client/webconsole/test/mochitest/browser_webconsole_network_messages_openinnet.js
@@ -20,17 +20,17 @@ registerCleanupFunction(() => {
   Services.prefs.clearUserPref(NET_PREF);
   Services.prefs.clearUserPref(XHR_PREF);
 });
 
 add_task(async function task() {
   const hud = await openNewTabAndConsole(TEST_URI);
 
   const currentTab = gBrowser.selectedTab;
-  const target = TargetFactory.forTab(currentTab);
+  const target = await TargetFactory.forTab(currentTab);
   const toolbox = gDevTools.getToolbox(target);
 
   const documentUrl = TEST_PATH + TEST_FILE;
   await loadDocument(documentUrl);
   info("Document loaded.");
 
   await openMessageInNetmonitor(toolbox, hud, documentUrl);
 
--- a/devtools/client/webconsole/test/mochitest/browser_webconsole_network_messages_status_code.js
+++ b/devtools/client/webconsole/test/mochitest/browser_webconsole_network_messages_status_code.js
@@ -15,17 +15,17 @@ const LEARN_MORE_URI = "https://develope
 
 pushPref(NET_PREF, true);
 pushPref(XHR_PREF, true);
 
 add_task(async function task() {
   const hud = await openNewTabAndConsole(TEST_URI);
 
   const currentTab = gBrowser.selectedTab;
-  const target = TargetFactory.forTab(currentTab);
+  const target = await TargetFactory.forTab(currentTab);
   const toolbox = gDevTools.getToolbox(target);
   const {ui} = toolbox.getCurrentPanel().hud;
   const onNetworkMessageUpdate = ui.jsterm.hud.once("network-message-updated");
 
   // Fire an XHR POST request.
   await ContentTask.spawn(gBrowser.selectedBrowser, null, function() {
     content.wrappedJSObject.testXhrPost();
   });
--- a/devtools/client/webconsole/test/mochitest/browser_webconsole_object_inspector_while_debugging_and_inspecting.js
+++ b/devtools/client/webconsole/test/mochitest/browser_webconsole_object_inspector_while_debugging_and_inspecting.js
@@ -19,17 +19,17 @@ const TEST_URI = "https://example.com/br
 
 add_task(async function() {
   const hud = await openNewTabAndConsole(TEST_URI);
 
   info("Switch to the debugger");
   await openDebugger();
 
   info("Switch to the inspector");
-  const target = TargetFactory.forTab(gBrowser.selectedTab);
+  const target = await TargetFactory.forTab(gBrowser.selectedTab);
   await gDevTools.showToolbox(target, "inspector");
 
   info("Call firstCall() and wait for the debugger statement to be reached.");
   const toolbox = gDevTools.getToolbox(target);
   const dbg = createDebuggerContext(toolbox);
   await pauseDebugger(dbg);
 
   info("Switch back to the console");
--- a/devtools/client/webconsole/test/mochitest/browser_webconsole_shows_reqs_from_netmonitor.js
+++ b/devtools/client/webconsole/test/mochitest/browser_webconsole_shows_reqs_from_netmonitor.js
@@ -34,17 +34,17 @@ add_task(async function task() {
 
   await loadDocument(TEST_PATH);
   info("Document loaded.");
 
   await onMessageAdded;
   info("Network message found.");
 
   // Test that the request appears in the network panel.
-  const target = TargetFactory.forTab(currentTab);
+  const target = await TargetFactory.forTab(currentTab);
   const toolbox = await gDevTools.showToolbox(target, "netmonitor");
   info("Network panel is open.");
 
   await testNetmonitor(toolbox);
 });
 
 async function testNetmonitor(toolbox) {
   const monitor = toolbox.getCurrentPanel();
--- a/devtools/client/webconsole/test/mochitest/browser_webconsole_split.js
+++ b/devtools/client/webconsole/test/mochitest/browser_webconsole_split.js
@@ -242,17 +242,17 @@ async function performTests() {
        "Web console is collapsed after toggling");
     ok(!currentUIState.openedConsolePanel,
        "The console panel is not the current tool");
     is(currentUIState.menuLabel, "split",
        "The menu item indicates the console is not split");
   }
 
   async function openPanel(toolId) {
-    const target = TargetFactory.forTab(gBrowser.selectedTab);
+    const target = await TargetFactory.forTab(gBrowser.selectedTab);
     toolbox = await gDevTools.showToolbox(target, toolId);
   }
 
   async function openAndCheckPanel(toolId) {
     await openPanel(toolId);
     await checkToolboxUI(toolbox.getCurrentPanel());
   }
 
--- a/devtools/client/webconsole/test/mochitest/browser_webconsole_stacktrace_location_debugger_link.js
+++ b/devtools/client/webconsole/test/mochitest/browser_webconsole_stacktrace_location_debugger_link.js
@@ -20,17 +20,17 @@ const TEST_URI = "http://example.com/bro
 
 add_task(async function() {
   Services.prefs.setBoolPref("devtools.webconsole.filter.log", true);
   registerCleanupFunction(async function() {
     Services.prefs.clearUserPref("devtools.webconsole.filter.log");
   });
 
   const hud = await openNewTabAndConsole(TEST_URI);
-  const target = TargetFactory.forTab(gBrowser.selectedTab);
+  const target = await TargetFactory.forTab(gBrowser.selectedTab);
   const toolbox = gDevTools.getToolbox(target);
 
   await testOpenInDebugger(hud, toolbox, "console.trace()");
 });
 
 async function testOpenInDebugger(hud, toolbox, text) {
   info(`Testing message with text "${text}"`);
   const messageNode = await waitFor(() => findMessage(hud, text));
--- a/devtools/client/webconsole/test/mochitest/browser_webconsole_stacktrace_location_scratchpad_link.js
+++ b/devtools/client/webconsole/test/mochitest/browser_webconsole_stacktrace_location_scratchpad_link.js
@@ -8,17 +8,17 @@
 const TEST_URI = "data:text/html;charset=utf8,<p>test stacktrace scratchpad linking</p>";
 
 add_task(async function() {
   await pushPref("devtools.scratchpad.enabled", true);
   await openNewTabAndToolbox(TEST_URI);
 
   info("Opening toolbox with Scratchpad panel");
 
-  const target = TargetFactory.forTab(gBrowser.selectedTab);
+  const target = await TargetFactory.forTab(gBrowser.selectedTab);
   const toolbox = await gDevTools.showToolbox(target, "scratchpad", "window");
 
   const scratchpadPanel = toolbox.getPanel("scratchpad");
   const { scratchpad } = scratchpadPanel;
   is(toolbox.getCurrentPanel(), scratchpadPanel,
     "Scratchpad is currently selected panel");
 
   info("Switching to webconsole panel");
--- a/devtools/client/webconsole/test/mochitest/head.js
+++ b/devtools/client/webconsole/test/mochitest/head.js
@@ -519,17 +519,17 @@ function isJstermFocused(jsterm) {
  *         - toolbox: the Toolbox instance.
  *         - panel: the jsdebugger panel instance.
  */
 async function openDebugger(options = {}) {
   if (!options.tab) {
     options.tab = gBrowser.selectedTab;
   }
 
-  const target = TargetFactory.forTab(options.tab);
+  const target = await TargetFactory.forTab(options.tab);
   let toolbox = gDevTools.getToolbox(target);
   const dbgPanelAlreadyOpen = toolbox && toolbox.getPanel("jsdebugger");
   if (dbgPanelAlreadyOpen) {
     await toolbox.selectTool("jsdebugger");
 
     return {
       target,
       toolbox,
@@ -555,48 +555,48 @@ async function openDebugger(options = {}
   return {target, toolbox, panel};
 }
 
 async function openInspector(options = {}) {
   if (!options.tab) {
     options.tab = gBrowser.selectedTab;
   }
 
-  const target = TargetFactory.forTab(options.tab);
+  const target = await TargetFactory.forTab(options.tab);
   const toolbox = await gDevTools.showToolbox(target, "inspector");
 
   return toolbox.getCurrentPanel();
 }
 
 /**
  * Open the Web Console for the given tab, or the current one if none given.
  *
  * @param Element tab
  *        Optional tab element for which you want open the Web Console.
  *        Defaults to current selected tab.
  * @return Promise
  *         A promise that is resolved with the console hud once the web console is open.
  */
 async function openConsole(tab) {
-  const target = TargetFactory.forTab(tab || gBrowser.selectedTab);
+  const target = await TargetFactory.forTab(tab || gBrowser.selectedTab);
   const toolbox = await gDevTools.showToolbox(target, "webconsole");
   return toolbox.getCurrentPanel().hud;
 }
 
 /**
  * Close the Web Console for the given tab.
  *
  * @param Element [tab]
  *        Optional tab element for which you want close the Web Console.
  *        Defaults to current selected tab.
  * @return object
  *         A promise that is resolved once the web console is closed.
  */
 async function closeConsole(tab = gBrowser.selectedTab) {
-  const target = TargetFactory.forTab(tab);
+  const target = await TargetFactory.forTab(tab);
   const toolbox = gDevTools.getToolbox(target);
   if (toolbox) {
     await toolbox.destroy();
   }
 }
 
 /**
  * Fake clicking a link and return the URL we would have navigated to.
--- a/devtools/docs/backend/client-api.md
+++ b/devtools/docs/backend/client-api.md
@@ -80,17 +80,17 @@ Attaching to a browser tab requires enum
 ```javascript
 function attachToTab() {
   // Get the list of tabs to find the one to attach to.
   client.listTabs().then((response) => {
     // Find the active tab.
     let tab = response.tabs[response.selected];
 
     // Attach to the tab.
-    client.attachTab(tab.actor).then(([response, tabClient]) => {
+    client.attachTarget(tab.actor).then(([response, tabClient]) => {
       if (!tabClient) {
         return;
       }
 
       // Now the tabClient is ready and can be used.
     });
   });
 }
@@ -116,17 +116,17 @@ function onTab() {
 ```
 
 ## Debugging JavaScript running in a browser tab
 
 Once the application is attached to a tab, it can attach to its thread in order to interact with the JavaScript debugger:
 
 ```javascript
 // Assuming the application is already attached to the tab, and response is the first
-// argument of the attachTab callback.
+// argument of the attachTarget callback.
 
 client.attachThread(response.threadActor).then(function([response, threadClient]) {
   if (!threadClient) {
     return;
   }
 
   // Attach listeners for thread events.
   threadClient.addListener("paused", onPause);
@@ -190,17 +190,17 @@ function shutdownDebugger() {
  * Start debugging the current tab.
  */
 function debugTab() {
   // Get the list of tabs to find the one to attach to.
   client.listTabs().then(response => {
     // Find the active tab.
     let tab = response.tabs[response.selected];
     // Attach to the tab.
-    client.attachTab(tab.actor).then(([response, tabClient]) => {
+    client.attachTarget(tab.actor).then(([response, tabClient]) => {
       if (!tabClient) {
         return;
       }
 
       // Attach to the thread (context).
       client.attachThread(response.threadActor, (response, thread) => {
         if (!thread) {
           return;
--- a/devtools/docs/frontend/telemetry.md
+++ b/devtools/docs/frontend/telemetry.md
@@ -378,17 +378,17 @@ add_task(async function() {
   await openAndCloseToolbox("webconsole", SIDE);
   await openAndCloseToolbox("webconsole", BOTTOM);
 
   checkResults();
 });
 
 async function openAndCloseToolbox(toolId, host) {
   const tab = await addTab(URL);
-  const target = TargetFactory.forTab(tab);
+  const target = await TargetFactory.forTab(tab);
   const toolbox = await gDevTools.showToolbox(target, toolId);
 
   await toolbox.switchHost(host);
   await toolbox.destroy();
 }
 
 function checkResults() {
   const snapshot = Services.telemetry.snapshotEvents(OPTOUT, true);
--- a/devtools/server/actors/emulation.js
+++ b/devtools/server/actors/emulation.js
@@ -43,17 +43,17 @@ const EmulationActor = protocol.ActorCla
   },
 
   /**
    * Retrieve the console actor for this tab.  This allows us to expose network throttling
    * as part of emulation settings, even though it's internally connected to the network
    * monitor, which for historical reasons is part of the console actor.
    */
   get _consoleActor() {
-    if (this.targetActor.exited) {
+    if (this.targetActor.exited || !this.targetActor.actorID) {
       return null;
     }
     const form = this.targetActor.form();
     return this.conn._getOrCreateActor(form.consoleActor);
   },
 
   /* DPPX override */
 
--- a/devtools/server/tests/browser/browser_navigateEvents.js
+++ b/devtools/server/tests/browser/browser_navigateEvents.js
@@ -102,17 +102,17 @@ async function connectAndAttachTab() {
   // Connect to this tab
   const transport = DebuggerServer.connectPipe();
   const client = new DebuggerClient(transport);
   client.addListener("tabNavigated", function(event, packet) {
     assertEvent("tabNavigated", packet);
   });
   const form = await connectDebuggerClient(client);
   const actorID = form.actor;
-  await client.attachTab(actorID);
+  await client.attachTarget(actorID);
   return { client, actorID };
 }
 
 add_task(async function() {
   // Open a test tab
   const browser = await addTab(URL1);
 
   // Listen for alert() call being made in navigate-first during unload
--- a/devtools/server/tests/browser/browser_stylesheets_getTextEmpty.js
+++ b/devtools/server/tests/browser/browser_stylesheets_getTextEmpty.js
@@ -15,17 +15,17 @@ add_task(async function() {
   await addTab(TEST_URI);
 
   info("Initialising the debugger server and client.");
   initDebuggerServer();
   const client = new DebuggerClient(DebuggerServer.connectPipe());
   const form = await connectDebuggerClient(client);
 
   info("Attaching to the active tab.");
-  await client.attachTab(form.actor);
+  await client.attachTarget(form.actor);
 
   const front = StyleSheetsFront(client, form);
   ok(front, "The StyleSheetsFront was created.");
 
   const sheets = await front.getStyleSheets();
   ok(sheets, "getStyleSheets() succeeded");
   is(sheets.length, 1,
      "getStyleSheets() returned the correct number of sheets");
--- a/devtools/server/tests/browser/browser_stylesheets_nested-iframes.js
+++ b/devtools/server/tests/browser/browser_stylesheets_nested-iframes.js
@@ -13,17 +13,17 @@ add_task(async function() {
   await addTab(MAIN_DOMAIN + "stylesheets-nested-iframes.html");
 
   info("Initialising the debugger server and client.");
   initDebuggerServer();
   const client = new DebuggerClient(DebuggerServer.connectPipe());
   const form = await connectDebuggerClient(client);
 
   info("Attaching to the active tab.");
-  await client.attachTab(form.actor);
+  await client.attachTarget(form.actor);
 
   const front = StyleSheetsFront(client, form);
   ok(front, "The StyleSheetsFront was created.");
 
   const sheets = await front.getStyleSheets();
   ok(sheets, "getStyleSheets() succeeded even with documentless iframes.");
 
   // Bug 285395 limits the number of nested iframes to 10. There's one sheet per
--- a/devtools/server/tests/browser/browser_webextension_inspected_window.js
+++ b/devtools/server/tests/browser/browser_webextension_inspected_window.js
@@ -27,17 +27,17 @@ async function setup(pageUrl) {
   };
 
   await addTab(pageUrl);
   initDebuggerServer();
 
   const client = new DebuggerClient(DebuggerServer.connectPipe());
   const form = await connectDebuggerClient(client);
 
-  const [, tabClient] = await client.attachTab(form.actor);
+  const [, tabClient] = await client.attachTarget(form.actor);
 
   const [, consoleClient] = await client.attachConsole(form.consoleActor, []);
 
   const inspectedWindowFront = new WebExtensionInspectedWindowFront(client, form);
 
   return {
     client, form,
     tabClient, consoleClient,
--- a/devtools/server/tests/mochitest/inspector-helpers.js
+++ b/devtools/server/tests/mochitest/inspector-helpers.js
@@ -62,17 +62,17 @@ function attachURL(url, callback) {
     if (event.data === "ready") {
       client = new DebuggerClient(DebuggerServer.connectPipe());
       client.connect().then(([applicationType, traits]) => {
         client.listTabs().then(response => {
           for (const tab of response.tabs) {
             if (tab.url === url) {
               window.removeEventListener("message", loadListener);
               // eslint-disable-next-line max-nested-callbacks
-              client.attachTab(tab.actor).then(function() {
+              client.attachTarget(tab.actor).then(function() {
                 try {
                   callback(null, client, tab, win.document);
                 } catch (ex) {
                   Cu.reportError(ex);
                   dump(ex);
                 }
               });
               break;
--- a/devtools/server/tests/mochitest/test_framerate_04.html
+++ b/devtools/server/tests/mochitest/test_framerate_04.html
@@ -23,18 +23,18 @@ window.onload = function() {
   const url = document.getElementById("testContent").href;
   attachURL(url, onTab);
 
   async function onTab(_, client, form, contentDoc) {
     const contentWin = contentDoc.defaultView;
     const chromeWin = Services.wm.getMostRecentWindow("navigator:browser");
     const selectedTab = chromeWin.gBrowser.selectedTab;
 
-    const target = TargetFactory.forTab(selectedTab);
-    await target.makeRemote();
+    const target = await TargetFactory.forTab(selectedTab);
+    await target.attach();
     const front = FramerateFront(client, form);
 
     front.startRecording().then(() => {
       window.setTimeout(() => {
         front.getPendingTicks().then(firstBatch => {
           /* eslint-disable max-nested-callbacks */
           target.once("will-navigate", () => {
             window.setTimeout(() => {
--- a/devtools/server/tests/mochitest/webconsole-helpers.js
+++ b/devtools/server/tests/mochitest/webconsole-helpers.js
@@ -54,17 +54,17 @@ async function attachURL(url) {
   await client.connect();
   const {tabs} = await client.listTabs();
   const attachedTab = tabs.find(tab => tab.url === url);
 
   if (!attachedTab) {
     throw new Error(`Could not find a tab matching URL ${url}`);
   }
 
-  const [, tabClient] = await client.attachTab(attachedTab.actor);
+  const [, tabClient] = await client.attachTarget(attachedTab.actor);
   const [, consoleClient] = await client.attachConsole(attachedTab.consoleActor, []);
 
   return {
     tab: attachedTab,
     tabClient,
     consoleClient,
     cleanup,
   };
--- a/devtools/server/tests/unit/head_dbg.js
+++ b/devtools/server/tests/unit/head_dbg.js
@@ -172,19 +172,19 @@ function findTab(tabs, title) {
   for (const tab of tabs) {
     if (tab.title === title) {
       return tab;
     }
   }
   return null;
 }
 
-function attachTab(client, tab) {
+function attachTarget(client, tab) {
   dump("Attaching to tab with title '" + tab.title + "'.\n");
-  return client.attachTab(tab.actor);
+  return client.attachTarget(tab.actor);
 }
 
 function waitForNewSource(threadClient, url) {
   dump("Waiting for new source with url '" + url + "'.\n");
   return waitForEvent(threadClient, "newSource", function(packet) {
     return packet.source.url === url;
   });
 }
@@ -352,17 +352,17 @@ function getTestTab(client, title, callb
     callback(null);
   });
 }
 
 // Attach to |client|'s tab whose title is |title|; pass |callback| the
 // response packet and a TabClient instance referring to that tab.
 function attachTestTab(client, title, callback) {
   getTestTab(client, title, function(tab) {
-    client.attachTab(tab.actor).then(([response, tabClient]) => {
+    client.attachTarget(tab.actor).then(([response, tabClient]) => {
       callback(response, tabClient);
     });
   });
 }
 
 // Attach to |client|'s tab whose title is |title|, and then attach to
 // that tab's thread. Pass |callback| the thread attach response packet, a
 // TabClient referring to the tab, and a ThreadClient referring to the
--- a/devtools/server/tests/unit/test_promises_actor_attach.js
+++ b/devtools/server/tests/unit/test_promises_actor_attach.js
@@ -10,24 +10,24 @@
 
 const { PromisesFront } = require("devtools/shared/fronts/promises");
 
 add_task(async function() {
   const client = await startTestDebuggerServer("promises-actor-test");
   const parentProcessActors = await getParentProcessActors(client);
 
   // We have to attach the chrome target actor before playing with the PromiseActor
-  await attachTab(client, parentProcessActors);
+  await attachTarget(client, parentProcessActors);
   await testAttach(client, parentProcessActors);
 
   const response = await listTabs(client);
   const targetTab = findTab(response.tabs, "promises-actor-test");
   ok(targetTab, "Found our target tab.");
 
-  const [ tabResponse ] = await attachTab(client, targetTab);
+  const [ tabResponse ] = await attachTarget(client, targetTab);
 
   await testAttach(client, tabResponse);
 
   await close(client);
 });
 
 async function testAttach(client, parent) {
   const promises = PromisesFront(client, parent);
--- a/devtools/server/tests/unit/test_promises_actor_list_promises.js
+++ b/devtools/server/tests/unit/test_promises_actor_list_promises.js
@@ -11,17 +11,17 @@
 const { PromisesFront } = require("devtools/shared/fronts/promises");
 const SECRET = "MyLittleSecret";
 
 add_task(async function() {
   const client = await startTestDebuggerServer("promises-actor-test");
   const parentProcessActors = await getParentProcessActors(client);
 
   // We have to attach the chrome target actor before playing with the PromiseActor
-  await attachTab(client, parentProcessActors);
+  await attachTarget(client, parentProcessActors);
   await testListPromises(client, parentProcessActors, v =>
     new Promise(resolve => resolve(v)));
 
   const response = await listTabs(client);
   const targetTab = findTab(response.tabs, "promises-actor-test");
   ok(targetTab, "Found our target tab.");
 
   await testListPromises(client, targetTab, v => {
--- a/devtools/server/tests/unit/test_promises_actor_onnewpromise.js
+++ b/devtools/server/tests/unit/test_promises_actor_onnewpromise.js
@@ -14,17 +14,17 @@ var EventEmitter = require("devtools/sha
 
 add_task(async function() {
   const client = await startTestDebuggerServer("promises-actor-test");
   const parentProcessActors = await getParentProcessActors(client);
 
   ok(Promise.toString().includes("native code"), "Expect native DOM Promise");
 
   // We have to attach the chrome target actor before playing with the PromiseActor
-  await attachTab(client, parentProcessActors);
+  await attachTarget(client, parentProcessActors);
   await testNewPromisesEvent(client, parentProcessActors,
     v => new Promise(resolve => resolve(v)));
 
   const response = await listTabs(client);
   const targetTab = findTab(response.tabs, "promises-actor-test");
   ok(targetTab, "Found our target tab.");
 
   await testNewPromisesEvent(client, targetTab, v => {
--- a/devtools/server/tests/unit/test_promises_actor_onpromisesettled.js
+++ b/devtools/server/tests/unit/test_promises_actor_onpromisesettled.js
@@ -16,17 +16,17 @@ var EventEmitter = require("devtools/sha
 
 add_task(async function() {
   const client = await startTestDebuggerServer("promises-actor-test");
   const parentProcessActors = await getParentProcessActors(client);
 
   ok(Promise.toString().includes("native code"), "Expect native DOM Promise");
 
   // We have to attach the chrome target actor before playing with the PromiseActor
-  await attachTab(client, parentProcessActors);
+  await attachTarget(client, parentProcessActors);
   await testPromisesSettled(client, parentProcessActors,
     v => new Promise(resolve => resolve(v)),
     v => new Promise((resolve, reject) => reject(v)));
 
   const response = await listTabs(client);
   const targetTab = findTab(response.tabs, "promises-actor-test");
   ok(targetTab, "Found our target tab.");
 
--- a/devtools/server/tests/unit/test_promises_client_getdependentpromises.js
+++ b/devtools/server/tests/unit/test_promises_client_getdependentpromises.js
@@ -9,17 +9,17 @@
 
 const { PromisesFront } = require("devtools/shared/fronts/promises");
 
 var EventEmitter = require("devtools/shared/event-emitter");
 
 add_task(async function() {
   const client = await startTestDebuggerServer("test-promises-dependentpromises");
   const parentProcessActors = await getParentProcessActors(client);
-  await attachTab(client, parentProcessActors);
+  await attachTarget(client, parentProcessActors);
 
   ok(Promise.toString().includes("native code"), "Expect native DOM Promise.");
 
   await testGetDependentPromises(client, parentProcessActors, () => {
     const p = new Promise(() => {});
     p.name = "p";
     const q = p.then();
     q.name = "q";
@@ -27,17 +27,17 @@ add_task(async function() {
     r.name = "r";
 
     return p;
   });
 
   const response = await listTabs(client);
   const targetTab = findTab(response.tabs, "test-promises-dependentpromises");
   ok(targetTab, "Found our target tab.");
-  await attachTab(client, targetTab);
+  await attachTarget(client, targetTab);
 
   await testGetDependentPromises(client, targetTab, () => {
     const debuggee =
       DebuggerServer.getTestGlobal("test-promises-dependentpromises");
 
     const p = new debuggee.Promise(() => {});
     p.name = "p";
     const q = p.then();
--- a/devtools/server/tests/unit/test_promises_object_creationtimestamp.js
+++ b/devtools/server/tests/unit/test_promises_object_creationtimestamp.js
@@ -23,17 +23,17 @@ add_task(async function() {
   });
 
   const client = await startTestDebuggerServer("promises-object-test");
   const parentProcessActors = await getParentProcessActors(client);
 
   ok(Promise.toString().includes("native code"), "Expect native DOM Promise.");
 
   // We have to attach the chrome target actor before playing with the PromiseActor
-  await attachTab(client, parentProcessActors);
+  await attachTarget(client, parentProcessActors);
   await testPromiseCreationTimestamp(client, parentProcessActors, v => {
     return new Promise(resolve => resolve(v));
   });
 
   const response = await listTabs(client);
   const targetTab = findTab(response.tabs, "promises-object-test");
   ok(targetTab, "Found our target tab.");
 
--- a/devtools/server/tests/unit/test_promises_object_timetosettle-01.js
+++ b/devtools/server/tests/unit/test_promises_object_timetosettle-01.js
@@ -14,17 +14,17 @@ var EventEmitter = require("devtools/sha
 
 add_task(async function() {
   const client = await startTestDebuggerServer("test-promises-timetosettle");
   const parentProcessActors = await getParentProcessActors(client);
 
   ok(Promise.toString().includes("native code"), "Expect native DOM Promise.");
 
   // We have to attach the chrome target actor before playing with the PromiseActor
-  await attachTab(client, parentProcessActors);
+  await attachTarget(client, parentProcessActors);
   await testGetTimeToSettle(client, parentProcessActors, () => {
     const p = new Promise(() => {});
     p.name = "p";
     const q = p.then();
     q.name = "q";
 
     return p;
   });
--- a/devtools/server/tests/unit/test_promises_object_timetosettle-02.js
+++ b/devtools/server/tests/unit/test_promises_object_timetosettle-02.js
@@ -11,29 +11,29 @@
 const { PromisesFront } = require("devtools/shared/fronts/promises");
 const { setTimeout } = ChromeUtils.import("resource://gre/modules/Timer.jsm", {});
 
 var EventEmitter = require("devtools/shared/event-emitter");
 
 add_task(async function() {
   const client = await startTestDebuggerServer("test-promises-timetosettle");
   const parentProcessActors = await getParentProcessActors(client);
-  await attachTab(client, parentProcessActors);
+  await attachTarget(client, parentProcessActors);
 
   ok(Promise.toString().includes("native code"), "Expect native DOM Promise.");
 
   // We have to attach the chrome target actor before playing with the PromiseActor
-  await attachTab(client, parentProcessActors);
+  await attachTarget(client, parentProcessActors);
   await testGetTimeToSettle(client, parentProcessActors,
     v => new Promise(resolve => setTimeout(() => resolve(v), 100)));
 
   const response = await listTabs(client);
   const targetTab = findTab(response.tabs, "test-promises-timetosettle");
   ok(targetTab, "Found our target tab.");
-  await attachTab(client, targetTab);
+  await attachTarget(client, targetTab);
 
   await testGetTimeToSettle(client, targetTab, v => {
     const debuggee =
       DebuggerServer.getTestGlobal("test-promises-timetosettle");
     return new debuggee.Promise(resolve => setTimeout(() => resolve(v), 100));
   });
 
   await close(client);
--- a/devtools/server/tests/unit/test_setBreakpoint-at-the-beginning-of-a-line.js
+++ b/devtools/server/tests/unit/test_setBreakpoint-at-the-beginning-of-a-line.js
@@ -10,17 +10,17 @@ async function run_test() {
   const global = createTestGlobal("test");
   DebuggerServer.addTestGlobal(global);
 
   const client = new DebuggerClient(DebuggerServer.connectPipe());
   await connect(client);
 
   const { tabs } = await listTabs(client);
   const tab = findTab(tabs, "test");
-  const [, tabClient] = await attachTab(client, tab);
+  const [, tabClient] = await attachTarget(client, tab);
   const [, threadClient] = await attachThread(tabClient);
   await resume(threadClient);
 
   const promise = waitForNewSource(threadClient, SOURCE_URL);
   loadSubScript(SOURCE_URL, global);
   const { source } = await promise;
   const sourceClient = threadClient.source(source);
 
--- a/devtools/server/tests/unit/test_setBreakpoint-at-the-end-of-a-line.js
+++ b/devtools/server/tests/unit/test_setBreakpoint-at-the-end-of-a-line.js
@@ -12,17 +12,17 @@ async function run_test() {
   const global = createTestGlobal("test");
   DebuggerServer.addTestGlobal(global);
 
   const client = new DebuggerClient(DebuggerServer.connectPipe());
   await connect(client);
 
   const { tabs } = await listTabs(client);
   const tab = findTab(tabs, "test");
-  const [, tabClient] = await attachTab(client, tab);
+  const [, tabClient] = await attachTarget(client, tab);
   const [, threadClient] = await attachThread(tabClient);
   await resume(threadClient);
 
   const promise = waitForNewSource(threadClient, SOURCE_URL);
   loadSubScript(SOURCE_URL, global);
   const { source } = await promise;
   const sourceClient = threadClient.source(source);
 
--- a/devtools/server/tests/unit/test_setBreakpoint-on-column-in-gcd-script.js
+++ b/devtools/server/tests/unit/test_setBreakpoint-on-column-in-gcd-script.js
@@ -14,17 +14,17 @@ function run_test() {
     DebuggerServer.setRootActor(createRootActor);
     DebuggerServer.init(() => true);
     DebuggerServer.addTestGlobal(global);
     const client = new DebuggerClient(DebuggerServer.connectPipe());
     await connect(client);
 
     const { tabs } = await listTabs(client);
     const tab = findTab(tabs, "test");
-    const [, tabClient] = await attachTab(client, tab);
+    const [, tabClient] = await attachTarget(client, tab);
     const [, threadClient] = await attachThread(tabClient);
     await resume(threadClient);
 
     const { sources } = await getSources(threadClient);
     const source = findSource(sources, SOURCE_URL);
     const sourceClient = threadClient.source(source);
 
     const location = { line: 6, column: 17 };
--- a/devtools/server/tests/unit/test_setBreakpoint-on-column.js
+++ b/devtools/server/tests/unit/test_setBreakpoint-on-column.js
@@ -13,17 +13,17 @@ function run_test() {
     const global = createTestGlobal("test");
     DebuggerServer.addTestGlobal(global);
 
     const client = new DebuggerClient(DebuggerServer.connectPipe());
     await connect(client);
 
     const { tabs } = await listTabs(client);
     const tab = findTab(tabs, "test");
-    const [, tabClient] = await attachTab(client, tab);
+    const [, tabClient] = await attachTarget(client, tab);
     const [, threadClient] = await attachThread(tabClient);
     await resume(threadClient);
 
     const promise = waitForNewSource(threadClient, SOURCE_URL);
     loadSubScript(SOURCE_URL, global);
     const { source } = await promise;
     const sourceClient = threadClient.source(source);
 
--- a/devtools/server/tests/unit/test_setBreakpoint-on-line-in-gcd-script.js
+++ b/devtools/server/tests/unit/test_setBreakpoint-on-line-in-gcd-script.js
@@ -14,17 +14,17 @@ function run_test() {
     DebuggerServer.setRootActor(createRootActor);
     DebuggerServer.init(() => true);
     DebuggerServer.addTestGlobal(global);
     const client = new DebuggerClient(DebuggerServer.connectPipe());
     await connect(client);
 
     const { tabs } = await listTabs(client);
     const tab = findTab(tabs, "test");
-    const [, tabClient] = await attachTab(client, tab);
+    const [, tabClient] = await attachTarget(client, tab);
     const [, threadClient] = await attachThread(tabClient);
     await resume(threadClient);
 
     const { sources } = await getSources(threadClient);
     const source = findSource(sources, SOURCE_URL);
     const sourceClient = threadClient.source(source);
 
     const location = { line: 7 };
--- a/devtools/server/tests/unit/test_setBreakpoint-on-line-with-multiple-offsets.js
+++ b/devtools/server/tests/unit/test_setBreakpoint-on-line-with-multiple-offsets.js
@@ -13,17 +13,17 @@ function run_test() {
     const global = createTestGlobal("test");
     DebuggerServer.addTestGlobal(global);
 
     const client = new DebuggerClient(DebuggerServer.connectPipe());
     await connect(client);
 
     const { tabs } = await listTabs(client);
     const tab = findTab(tabs, "test");
-    const [, tabClient] = await attachTab(client, tab);
+    const [, tabClient] = await attachTarget(client, tab);
 
     const [, threadClient] = await attachThread(tabClient);
     await resume(threadClient);
 
     const promise = waitForNewSource(threadClient, SOURCE_URL);
     loadSubScript(SOURCE_URL, global);
     const { source } = await promise;
     const sourceClient = threadClient.source(source);
--- a/devtools/server/tests/unit/test_setBreakpoint-on-line-with-multiple-statements.js
+++ b/devtools/server/tests/unit/test_setBreakpoint-on-line-with-multiple-statements.js
@@ -13,17 +13,17 @@ function run_test() {
     const global = createTestGlobal("test");
     DebuggerServer.addTestGlobal(global);
 
     const client = new DebuggerClient(DebuggerServer.connectPipe());
     await connect(client);
 
     const { tabs } = await listTabs(client);
     const tab = findTab(tabs, "test");
-    const [, tabClient] = await attachTab(client, tab);
+    const [, tabClient] = await attachTarget(client, tab);
 
     const [, threadClient] = await attachThread(tabClient);
     await resume(threadClient);
 
     const promise = waitForNewSource(threadClient, SOURCE_URL);
     loadSubScript(SOURCE_URL, global);
     const { source } = await promise;
     const sourceClient = threadClient.source(source);
--- a/devtools/server/tests/unit/test_setBreakpoint-on-line-with-no-offsets-in-gcd-script.js
+++ b/devtools/server/tests/unit/test_setBreakpoint-on-line-with-no-offsets-in-gcd-script.js
@@ -15,17 +15,17 @@ function run_test() {
     DebuggerServer.init(() => true);
     DebuggerServer.addTestGlobal(global);
 
     const client = new DebuggerClient(DebuggerServer.connectPipe());
     await connect(client);
 
     const { tabs } = await listTabs(client);
     const tab = findTab(tabs, "test");
-    const [, tabClient] = await attachTab(client, tab);
+    const [, tabClient] = await attachTarget(client, tab);
     const [, threadClient] = await attachThread(tabClient);
     await resume(threadClient);
 
     const { sources } = await getSources(threadClient);
     const source = findSource(sources, SOURCE_URL);
     const sourceClient = threadClient.source(source);
 
     const location = { line: 7 };
--- a/devtools/server/tests/unit/test_setBreakpoint-on-line-with-no-offsets.js
+++ b/devtools/server/tests/unit/test_setBreakpoint-on-line-with-no-offsets.js
@@ -13,17 +13,17 @@ function run_test() {
     const global = createTestGlobal("test");
     DebuggerServer.addTestGlobal(global);
 
     const client = new DebuggerClient(DebuggerServer.connectPipe());
     await connect(client);
 
     const { tabs } = await listTabs(client);
     const tab = findTab(tabs, "test");
-    const [, tabClient] = await attachTab(client, tab);
+    const [, tabClient] = await attachTarget(client, tab);
     const [, threadClient] = await attachThread(tabClient);
     await resume(threadClient);
 
     const promise = waitForNewSource(threadClient, SOURCE_URL);
     loadSubScript(SOURCE_URL, global);
     const { source } = await promise;
     const sourceClient = threadClient.source(source);
 
--- a/devtools/server/tests/unit/test_setBreakpoint-on-line.js
+++ b/devtools/server/tests/unit/test_setBreakpoint-on-line.js
@@ -13,17 +13,17 @@ function run_test() {
     const global = createTestGlobal("test");
     DebuggerServer.addTestGlobal(global);
 
     const client = new DebuggerClient(DebuggerServer.connectPipe());
     await connect(client);
 
     const { tabs } = await listTabs(client);
     const tab = findTab(tabs, "test");
-    const [, tabClient] = await attachTab(client, tab);
+    const [, tabClient] = await attachTarget(client, tab);
 
     const [, threadClient] = await attachThread(tabClient);
     await resume(threadClient);
 
     const promise = waitForNewSource(threadClient, SOURCE_URL);
     loadSubScript(SOURCE_URL, global);
     const { source } = await promise;
     const sourceClient = threadClient.source(source);
--- a/devtools/server/tests/unit/test_xpcshell_debugging.js
+++ b/devtools/server/tests/unit/test_xpcshell_debugging.js
@@ -24,17 +24,17 @@ add_task(async function() {
   const deviceFront = await client.mainRoot.getFront("device");
   const desc = await deviceFront.getDescription();
   equal(desc.geckobuildid, Services.appinfo.platformBuildID, "device actor works");
 
   // Even though we have no tabs, getProcess gives us the chromeDebugger.
   const response = await client.getProcess();
 
   const actor = response.form.actor;
-  const [, tabClient] = await client.attachTab(actor);
+  const [, tabClient] = await client.attachTarget(actor);
   const [, threadClient] = await tabClient.attachThread(null);
   const onResumed = new Promise(resolve => {
     threadClient.addOneTimeListener("paused", (event, packet) => {
       equal(packet.why.type, "breakpoint",
           "yay - hit the breakpoint at the first line in our script");
       // Resume again - next stop should be our "debugger" statement.
       threadClient.addOneTimeListener("paused", (event, packet) => {
         equal(packet.why.type, "debuggerStatement",
--- a/devtools/shared/client/debugger-client.js
+++ b/devtools/shared/client/debugger-client.js
@@ -322,40 +322,43 @@ DebuggerClient.prototype = {
     return this.mainRoot.listAddons(onResponse);
   },
 
   getTab: function(filter) {
     return this.mainRoot.getTab(filter);
   },
 
   /**
-   * Attach to a tab's target actor.
+   * Attach to a target actor.
    *
    * @param string targetActor
    *        The target actor ID for the tab to attach.
    */
-  attachTab: function(targetActor) {
+  attachTarget: function(targetActor) {
     if (this._clients.has(targetActor)) {
-      const cachedTab = this._clients.get(targetActor);
+      const cachedTarget = this._clients.get(targetActor);
       const cachedResponse = {
-        cacheDisabled: cachedTab.cacheDisabled,
-        javascriptEnabled: cachedTab.javascriptEnabled,
-        traits: cachedTab.traits,
+        cacheDisabled: cachedTarget.cacheDisabled,
+        javascriptEnabled: cachedTarget.javascriptEnabled,
+        traits: cachedTarget.traits,
       };
-      return promise.resolve([cachedResponse, cachedTab]);
+      return promise.resolve([cachedResponse, cachedTarget]);
     }
 
     const packet = {
       to: targetActor,
       type: "attach"
     };
     return this.request(packet).then(response => {
-      const tabClient = new TabClient(this, response);
-      this.registerClient(tabClient);
-      return [response, tabClient];
+      // TabClient can actually represent targets other than a tab.
+      // It is planned to be renamed while being converted to a front
+      // in bug 1485660.
+      const targetClient = new TabClient(this, response);
+      this.registerClient(targetClient);
+      return [response, targetClient];
     });
   },
 
   attachWorker: function(workerTargetActor) {
     let workerClient = this._clients.get(workerTargetActor);
     if (workerClient !== undefined) {
       const response = {
         from: workerClient.actor,
--- a/devtools/shared/fronts/csscoverage.js
+++ b/devtools/shared/fronts/csscoverage.js
@@ -105,21 +105,21 @@ const CSSUsageFront = protocol.FrontClas
 });
 
 exports.CSSUsageFront = CSSUsageFront;
 
 const knownFronts = new WeakMap();
 
 /**
  * Create a CSSUsageFront only when needed (returns a promise)
- * For notes on target.makeRemote(), see
+ * For notes on target.attach(), see
  * https://bugzilla.mozilla.org/show_bug.cgi?id=1016330#c7
  */
 exports.getUsage = function(trgt) {
-  return trgt.makeRemote().then(() => {
+  return trgt.attach().then(() => {
     let front = knownFronts.get(trgt.client);
     if (front == null && trgt.form.cssUsageActor != null) {
       front = new CSSUsageFront(trgt.client, trgt.form);
       knownFronts.set(trgt.client, front);
     }
     return front;
   });
 };
--- a/devtools/shared/webconsole/test/common.js
+++ b/devtools/shared/webconsole/test/common.js
@@ -74,32 +74,32 @@ var _attachConsole = async function(
     console.error("client.connect() failed: " + response.error + " " +
                   response.message);
     callback(state, response);
     return;
   }
 
   if (!attachToTab) {
     response = await state.dbgClient.getProcess();
-    await state.dbgClient.attachTab(response.form.actor);
+    await state.dbgClient.attachTarget(response.form.actor);
     const consoleActor = response.form.consoleActor;
     state.actor = consoleActor;
     state.dbgClient.attachConsole(consoleActor, listeners)
       .then(_onAttachConsole.bind(null, state), _onAttachError.bind(null, state));
     return;
   }
   response = await state.dbgClient.listTabs();
   if (response.error) {
     console.error("listTabs failed: " + response.error + " " +
                   response.message);
     callback(state, response);
     return;
   }
   const tab = response.tabs[response.selected];
-  const [, tabClient] = await state.dbgClient.attachTab(tab.actor);
+  const [, tabClient] = await state.dbgClient.attachTarget(tab.actor);
   if (attachToWorker) {
     const workerName = "console-test-worker.js#" + new Date().getTime();
     const worker = new Worker(workerName);
     // Keep a strong reference to the Worker to avoid it being
     // GCd during the test (bug 1237492).
     // eslint-disable-next-line camelcase
     state._worker_ref = worker;
     await waitForMessage(worker);
--- a/devtools/startup/devtools-startup.js
+++ b/devtools/startup/devtools-startup.js
@@ -577,28 +577,32 @@ DevToolsStartup.prototype = {
 
     // Appending a <key> element is not always enough. The <keyset> needs
     // to be detached and reattached to make sure the <key> is taken into
     // account (see bug 832984).
     const mainKeyset = doc.getElementById("mainKeyset");
     mainKeyset.parentNode.insertBefore(keyset, mainKeyset);
   },
 
-  onKey(window, key) {
-    if (!Services.prefs.getBoolPref(DEVTOOLS_ENABLED_PREF)) {
-      const id = key.toolId || key.id;
-      this.openInstallPage("KeyShortcut", id);
-    } else {
-      // Record the timing at which this event started in order to compute later in
-      // gDevTools.showToolbox, the complete time it takes to open the toolbox.
-      // i.e. especially take `initDevTools` into account.
-      const startTime = Cu.now();
-      const require = this.initDevTools("KeyShortcut", key);
-      const { gDevToolsBrowser } = require("devtools/client/framework/devtools-browser");
-      gDevToolsBrowser.onKeyShortcut(window, key, startTime);
+  async onKey(window, key) {
+    try {
+      if (!Services.prefs.getBoolPref(DEVTOOLS_ENABLED_PREF)) {
+        const id = key.toolId || key.id;
+        this.openInstallPage("KeyShortcut", id);
+      } else {
+        // Record the timing at which this event started in order to compute later in
+        // gDevTools.showToolbox, the complete time it takes to open the toolbox.
+        // i.e. especially take `initDevTools` into account.
+        const startTime = Cu.now();
+        const require = this.initDevTools("KeyShortcut", key);
+        const { gDevToolsBrowser } = require("devtools/client/framework/devtools-browser");
+        await gDevToolsBrowser.onKeyShortcut(window, key, startTime);
+      }
+    } catch (e) {
+      console.error(`Exception while trigerring key ${key}: ${e}\n${e.stack}`);
     }
   },
 
   // Create a <xul:key> DOM Element
   createKey(doc, { id, toolId, shortcut, modifiers: mod }, oncommand) {
     const k = doc.createXULElement("key");
     k.id = "key_" + (id || toolId);
 
@@ -704,21 +708,21 @@ DevToolsStartup.prototype = {
     }
 
     if (cmdLine.state == Ci.nsICommandLine.STATE_REMOTE_AUTO) {
       cmdLine.preventDefault = true;
     }
   },
 
   // Open the toolbox on the selected tab once the browser starts up.
-  handleDevToolsFlag: function(window) {
+  handleDevToolsFlag: async function(window) {
     const require = this.initDevTools("CommandLine");
     const {gDevTools} = require("devtools/client/framework/devtools");
     const {TargetFactory} = require("devtools/client/framework/target");
-    const target = TargetFactory.forTab(window.gBrowser.selectedTab);
+    const target = await TargetFactory.forTab(window.gBrowser.selectedTab);
     gDevTools.showToolbox(target);
   },
 
   _isRemoteDebuggingEnabled() {
     let remoteDebuggingEnabled = false;
     try {
       remoteDebuggingEnabled = kDebuggerPrefs.every(pref => {
         return Services.prefs.getBoolPref(pref);
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -11607,17 +11607,19 @@ nsDocShell::AddState(JS::Handle<JS::Valu
 
       // It's very important that we check that newURI is of the same
       // origin as currentURI, not docBaseURI, because a page can
       // set docBaseURI arbitrarily to any domain.
       nsAutoCString currentUserPass, newUserPass;
       NS_ENSURE_SUCCESS(currentURI->GetUserPass(currentUserPass),
                         NS_ERROR_FAILURE);
       NS_ENSURE_SUCCESS(newURI->GetUserPass(newUserPass), NS_ERROR_FAILURE);
-      if (NS_FAILED(secMan->CheckSameOriginURI(currentURI, newURI, true)) ||
+      bool isPrivateWin =
+        document->NodePrincipal()->OriginAttributesRef().mPrivateBrowsingId > 0;
+      if (NS_FAILED(secMan->CheckSameOriginURI(currentURI, newURI, true, isPrivateWin)) ||
           !currentUserPass.Equals(newUserPass)) {
         return NS_ERROR_DOM_SECURITY_ERR;
       }
     } else {
       // It's a file:// URI
       nsCOMPtr<nsIScriptObjectPrincipal> docScriptObj =
         do_QueryInterface(document);
 
@@ -12972,20 +12974,28 @@ nsDocShell::IsOKToLoadURI(nsIURI* aURI)
   if (!mFiredUnloadEvent) {
     return true;
   }
 
   if (!mLoadingURI) {
     return false;
   }
 
+
+  bool isPrivateWin = false;
+  nsIDocument *doc = GetDocument();
+  if (doc) {
+    isPrivateWin =
+      doc->NodePrincipal()->OriginAttributesRef().mPrivateBrowsingId > 0;
+  }
+
   nsCOMPtr<nsIScriptSecurityManager> secMan =
     do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID);
   return secMan &&
-         NS_SUCCEEDED(secMan->CheckSameOriginURI(aURI, mLoadingURI, false));
+         NS_SUCCEEDED(secMan->CheckSameOriginURI(aURI, mLoadingURI, false, isPrivateWin));
 }
 
 //
 // Routines for selection and clipboard
 //
 nsresult
 nsDocShell::GetControllerForCommand(const char* aCommand,
                                     nsIController** aResult)
--- a/docshell/base/nsPingListener.cpp
+++ b/docshell/base/nsPingListener.cpp
@@ -163,18 +163,24 @@ SendPing(void* aClosure, nsIContent* aCo
   if (sm && info->referrer) {
     bool referrerIsSecure;
     uint32_t flags = nsIProtocolHandler::URI_IS_POTENTIALLY_TRUSTWORTHY;
     rv = NS_URIChainHasFlags(info->referrer, flags, &referrerIsSecure);
 
     // Default to sending less data if NS_URIChainHasFlags() fails.
     referrerIsSecure = NS_FAILED(rv) || referrerIsSecure;
 
+    bool isPrivateWin = false;
+    if (doc) {
+      isPrivateWin =
+        doc->NodePrincipal()->OriginAttributesRef().mPrivateBrowsingId > 0;
+    }
+
     bool sameOrigin =
-      NS_SUCCEEDED(sm->CheckSameOriginURI(info->referrer, aURI, false));
+      NS_SUCCEEDED(sm->CheckSameOriginURI(info->referrer, aURI, false, isPrivateWin));
 
     // If both the address of the document containing the hyperlink being
     // audited and "ping URL" have the same origin or the document containing
     // the hyperlink being audited was not retrieved over an encrypted
     // connection, send a Ping-From header.
     if (sameOrigin || !referrerIsSecure) {
       nsAutoCString pingFrom;
       if (NS_SUCCEEDED(info->referrer->GetSpec(pingFrom))) {
--- a/dom/base/nsDOMNavigationTiming.cpp
+++ b/dom/base/nsDOMNavigationTiming.cpp
@@ -329,28 +329,32 @@ nsDOMNavigationTiming::NotifyDocShellSta
   mDocShellHasBeenActiveSinceNavigationStart &=
     (aDocShellState == DocShellState::eActive);
 }
 
 mozilla::TimeStamp
 nsDOMNavigationTiming::GetUnloadEventStartTimeStamp() const
 {
   nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
-  nsresult rv = ssm->CheckSameOriginURI(mLoadedURI, mUnloadedURI, false);
+  // todo: if you intend to update CheckSameOriginURI to log the error to the
+  // console you also need to update the 'aFromPrivateWindow' argument.
+  nsresult rv = ssm->CheckSameOriginURI(mLoadedURI, mUnloadedURI, false, false);
   if (NS_SUCCEEDED(rv)) {
     return mUnloadStart;
   }
   return mozilla::TimeStamp();
 }
 
 mozilla::TimeStamp
 nsDOMNavigationTiming::GetUnloadEventEndTimeStamp() const
 {
   nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
-  nsresult rv = ssm->CheckSameOriginURI(mLoadedURI, mUnloadedURI, false);
+  // todo: if you intend to update CheckSameOriginURI to log the error to the
+  // console you also need to update the 'aFromPrivateWindow' argument.
+  nsresult rv = ssm->CheckSameOriginURI(mLoadedURI, mUnloadedURI, false, false);
   if (NS_SUCCEEDED(rv)) {
     return mUnloadEnd;
   }
   return mozilla::TimeStamp();
 }
 
 bool
 nsDOMNavigationTiming::IsTopLevelContentDocumentInContentProcess() const
--- a/dom/base/nsDataDocumentContentPolicy.cpp
+++ b/dom/base/nsDataDocumentContentPolicy.cpp
@@ -97,17 +97,18 @@ nsDataDocumentContentPolicy::ShouldLoad(
       // Report error, if we can.
       if (node) {
         nsIPrincipal* requestingPrincipal = node->NodePrincipal();
         RefPtr<nsIURI> principalURI;
         nsresult rv =
           requestingPrincipal->GetURI(getter_AddRefs(principalURI));
         if (NS_SUCCEEDED(rv) && principalURI) {
           nsScriptSecurityManager::ReportError(
-            nullptr, "ExternalDataError", principalURI, aContentLocation);
+            "ExternalDataError", principalURI, aContentLocation,
+            requestingPrincipal->OriginAttributesRef().mPrivateBrowsingId > 0);
         }
       }
     } else if ((contentType == nsIContentPolicy::TYPE_IMAGE ||
                 contentType == nsIContentPolicy::TYPE_IMAGESET) &&
                doc->GetDocumentURI()) {
       // Check for (& disallow) recursive image-loads
       bool isRecursiveLoad;
       nsresult rv = aContentLocation->EqualsExceptRef(doc->GetDocumentURI(),
--- a/dom/clients/manager/ClientNavigateOpChild.cpp
+++ b/dom/clients/manager/ClientNavigateOpChild.cpp
@@ -69,17 +69,19 @@ public:
     }
 
     nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
     MOZ_DIAGNOSTIC_ASSERT(ssm);
 
     // If the resulting window is not same origin, then resolve immediately
     // without returning any information about the new Client.  This is
     // step 6.10 in the Client.navigate(url) spec.
-    rv = ssm->CheckSameOriginURI(mBaseURL, channelURL, false);
+    // todo: if you intend to update CheckSameOriginURI to log the error to the
+    // console you also need to update the 'aFromPrivateWindow' argument.
+    rv = ssm->CheckSameOriginURI(mBaseURL, channelURL, false, false);
     if (NS_FAILED(rv)) {
       mPromise->Resolve(NS_OK, __func__);
       return NS_OK;
     }
 
     nsPIDOMWindowInner* innerWindow = mOuterWindow->GetCurrentInnerWindow();
     MOZ_DIAGNOSTIC_ASSERT(innerWindow);
 
--- a/dom/clients/manager/ClientOpenWindowUtils.cpp
+++ b/dom/clients/manager/ClientOpenWindowUtils.cpp
@@ -68,18 +68,20 @@ public:
       mPromise->Reject(NS_ERROR_FAILURE, __func__);
       mPromise = nullptr;
       return NS_OK;
     }
 
     // Check same origin.
     nsCOMPtr<nsIScriptSecurityManager> securityManager =
       nsContentUtils::GetSecurityManager();
+    bool isPrivateWin =
+      doc->NodePrincipal()->OriginAttributesRef().mPrivateBrowsingId > 0;
     nsresult rv = securityManager->CheckSameOriginURI(doc->GetOriginalURI(),
-                                                      mBaseURI, false);
+                                                      mBaseURI, false, isPrivateWin);
     if (NS_FAILED(rv)) {
       mPromise->Resolve(NS_OK, __func__);
       mPromise = nullptr;
       return NS_OK;
     }
 
     Maybe<ClientInfo> info(doc->GetClientInfo());
     Maybe<ClientState> state(doc->GetClientState());
--- a/dom/offline/nsDOMOfflineResourceList.cpp
+++ b/dom/offline/nsDOMOfflineResourceList.cpp
@@ -105,19 +105,22 @@ nsDOMOfflineResourceList::Init()
     return NS_OK;
   }
 
   if (!mManifestURI) {
     return NS_ERROR_DOM_INVALID_STATE_ERR;
   }
 
   mManifestURI->GetAsciiSpec(mManifestSpec);
+  bool isPrivateWin = mLoadingPrincipal
+    ? mLoadingPrincipal->OriginAttributesRef().mPrivateBrowsingId > 0
+    : false;
 
   nsresult rv = nsContentUtils::GetSecurityManager()->
-                   CheckSameOriginURI(mManifestURI, mDocumentURI, true);
+                   CheckSameOriginURI(mManifestURI, mDocumentURI, true, isPrivateWin);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // Dynamically-managed resources are stored as a separate ownership list
   // from the manifest.
   nsCOMPtr<nsIURI> innerURI = NS_GetInnermostURI(mDocumentURI);
   if (!innerURI)
     return NS_ERROR_FAILURE;
 
--- a/dom/payments/PaymentRequestManager.cpp
+++ b/dom/payments/PaymentRequestManager.cpp
@@ -322,19 +322,16 @@ PaymentRequestManager::NotifyRequestDone
 }
 
 void
 PaymentRequestManager::RequestIPCOver(PaymentRequest* aRequest)
 {
   // This must only be called from ActorDestroy or if we're sure we won't
   // receive any more IPC for aRequest.
   mActivePayments.Remove(aRequest);
-  if (aRequest == mShowingRequest) {
-    mShowingRequest = nullptr;
-  }
 }
 
 already_AddRefed<PaymentRequestManager>
 PaymentRequestManager::GetSingleton()
 {
   if (!gPaymentManager) {
     gPaymentManager = new PaymentRequestManager();
     ClearOnShutdown(&gPaymentManager);
@@ -456,34 +453,29 @@ PaymentRequestManager::CanMakePayment(Pa
   IPCPaymentCanMakeActionRequest action(requestId);
 
   return SendRequestPayment(aRequest, action);
 }
 
 nsresult
 PaymentRequestManager::ShowPayment(PaymentRequest* aRequest)
 {
-  if (mShowingRequest) {
-    return NS_ERROR_ABORT;
-  }
   nsresult rv = NS_OK;
   if (!aRequest->IsUpdating()) {
     nsAutoString requestId;
     aRequest->GetInternalId(requestId);
     IPCPaymentShowActionRequest action(requestId);
     rv = SendRequestPayment(aRequest, action);
   }
-  mShowingRequest = aRequest;
   return rv;
 }
 
 nsresult
 PaymentRequestManager::AbortPayment(PaymentRequest* aRequest, bool aDeferredShow)
 {
-  MOZ_ASSERT(aRequest == mShowingRequest);
   nsAutoString requestId;
   aRequest->GetInternalId(requestId);
   IPCPaymentAbortActionRequest action(requestId);
 
   // If aDeferredShow is true, then show was called with a promise that was
   // rejected. In that case, we need to remember that we called show earlier.
   return SendRequestPayment(aRequest, action, aDeferredShow);
 }
@@ -543,19 +535,16 @@ PaymentRequestManager::UpdatePayment(JSC
 
 nsresult
 PaymentRequestManager::ClosePayment(PaymentRequest* aRequest)
 {
   // for the case, the payment request is waiting for response from user.
   if (auto entry = mActivePayments.Lookup(aRequest)) {
     NotifyRequestDone(aRequest);
   }
-  if (mShowingRequest == aRequest) {
-    mShowingRequest = nullptr;
-  }
   nsAutoString requestId;
   aRequest->GetInternalId(requestId);
   IPCPaymentCloseActionRequest action(requestId);
   return SendRequestPayment(aRequest, action, false);
 }
 
 nsresult
 PaymentRequestManager::RetryPayment(JSContext* aCx,
@@ -629,36 +618,28 @@ PaymentRequestManager::RespondPayment(Pa
       }
       aRequest->RespondShowPayment(response.methodName(),
                                    response.data(),
                                    response.payerName(),
                                    response.payerEmail(),
                                    response.payerPhone(),
                                    rejectedReason);
       if (NS_FAILED(rejectedReason)) {
-        MOZ_ASSERT(mShowingRequest == aRequest);
-        mShowingRequest = nullptr;
         NotifyRequestDone(aRequest);
       }
       break;
     }
     case IPCPaymentActionResponse::TIPCPaymentAbortActionResponse: {
       const IPCPaymentAbortActionResponse& response = aResponse;
       aRequest->RespondAbortPayment(response.isSucceeded());
-      if (response.isSucceeded()) {
-        MOZ_ASSERT(mShowingRequest == aRequest);
-      }
-      mShowingRequest = nullptr;
       NotifyRequestDone(aRequest);
       break;
     }
     case IPCPaymentActionResponse::TIPCPaymentCompleteActionResponse: {
       aRequest->RespondComplete();
-      MOZ_ASSERT(mShowingRequest == aRequest);
-      mShowingRequest = nullptr;
       NotifyRequestDone(aRequest);
       break;
     }
     default: {
       return NS_ERROR_FAILURE;
     }
   }
   return NS_OK;
--- a/dom/payments/PaymentRequestManager.h
+++ b/dom/payments/PaymentRequestManager.h
@@ -90,15 +90,14 @@ private:
   nsresult SendRequestPayment(PaymentRequest* aRequest,
                               const IPCPaymentActionRequest& action,
                               bool aResponseExpected = true);
 
   void NotifyRequestDone(PaymentRequest* aRequest);
 
   // Strong pointer to requests with ongoing IPC messages to the parent.
   nsDataHashtable<nsRefPtrHashKey<PaymentRequest>, uint32_t> mActivePayments;
-  RefPtr<PaymentRequest> mShowingRequest;
 };
 
 } // end of namespace dom
 } // end of namespace mozilla
 
 #endif
--- a/dom/payments/PaymentRequestService.cpp
+++ b/dom/payments/PaymentRequestService.cpp
@@ -277,46 +277,19 @@ PaymentRequestService::RequestPayment(co
       }
       rv = RespondPayment(canMakeResponse.get());
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
       break;
     }
     case IPCPaymentActionRequest::TIPCPaymentShowActionRequest: {
-      MOZ_ASSERT(request);
-      request->SetState(payments::PaymentRequest::eInteractive);
-
-      if (mShowingRequest || !CanMakePayment(aRequestId)) {
-        uint32_t responseStatus;
-        if (mShowingRequest) {
-          responseStatus = nsIPaymentActionResponse::PAYMENT_REJECTED;
-        } else {
-          responseStatus = nsIPaymentActionResponse::PAYMENT_NOTSUPPORTED;
-        }
-        nsCOMPtr<nsIPaymentShowActionResponse> showResponse =
-          do_CreateInstance(NS_PAYMENT_SHOW_ACTION_RESPONSE_CONTRACT_ID);
-        MOZ_ASSERT(showResponse);
-        rv = showResponse->Init(aRequestId,
-                                responseStatus,
-                                EmptyString(),
-                                nullptr,
-                                EmptyString(),
-                                EmptyString(),
-                                EmptyString());
-        rv = RespondPayment(showResponse.get());
-        if (NS_WARN_IF(NS_FAILED(rv))) {
-          return rv;
-        }
-      } else {
-        mShowingRequest = request;
-        rv = LaunchUIAction(aRequestId, type);
-        if (NS_WARN_IF(NS_FAILED(rv))) {
-          return rv;
-        }
+      rv = ShowPayment(aRequestId);
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return rv;
       }
       break;
     }
     case IPCPaymentActionRequest::TIPCPaymentAbortActionRequest: {
       MOZ_ASSERT(request);
       request->SetState(payments::PaymentRequest::eInteractive);
       rv = LaunchUIAction(aRequestId, type);
       if (NS_WARN_IF(NS_FAILED(rv))) {
@@ -341,27 +314,31 @@ PaymentRequestService::RequestPayment(co
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
       MOZ_ASSERT(request);
       rv = request->UpdatePaymentDetails(details, action.shippingOption());
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
-      if (mShowingRequest) {
-        MOZ_ASSERT(mShowingRequest == request);
+
+      // mShowingRequest exists and it equals to the updated PaymentRequest
+      // Call UI::UpdatePayment
+      if (mShowingRequest && mShowingRequest == request) {
         rv = LaunchUIAction(aRequestId, type);
+        if (NS_WARN_IF(NS_FAILED(rv))) {
+          return rv;
+        }
       } else {
-        request->SetState(payments::PaymentRequest::eInteractive);
-        mShowingRequest = request;
-        rv = LaunchUIAction(aRequestId,
-                            IPCPaymentActionRequest::TIPCPaymentShowActionRequest);
-      }
-      if (NS_WARN_IF(NS_FAILED(rv))) {
-        return rv;
+        // mShowingRequest does not equal to the updated PaymentRequest, try to
+        // show the updated one.
+        rv = ShowPayment(aRequestId);
+        if (NS_WARN_IF(NS_FAILED(rv))) {
+          return rv;
+        }
       }
       break;
     }
     case IPCPaymentActionRequest::TIPCPaymentCloseActionRequest: {
       rv = LaunchUIAction(aRequestId, type);
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
@@ -455,17 +432,21 @@ PaymentRequestService::RespondPayment(ns
       request->SetState(payments::PaymentRequest::eClosed);
       nsCOMPtr<nsIPaymentShowActionResponse> response =
         do_QueryInterface(aResponse);
       MOZ_ASSERT(response);
       uint32_t acceptStatus;
       rv = response->GetAcceptStatus(&acceptStatus);
       NS_ENSURE_SUCCESS(rv, rv);
       if (acceptStatus != nsIPaymentActionResponse::PAYMENT_ACCEPTED) {
-        mShowingRequest = nullptr;
+        // Check if rejecting the showing PaymentRequest.
+        // If yes, set mShowingRequest as nullptr.
+        if (mShowingRequest == request) {
+          mShowingRequest = nullptr;
+        }
         mRequestQueue.RemoveElement(request);
       }
       break;
     }
     case nsIPaymentActionResponse::COMPLETE_ACTION: {
       mShowingRequest = nullptr;
       mRequestQueue.RemoveElement(request);
       break;
@@ -532,16 +513,60 @@ PaymentRequestService::CanMakePayment(co
 {
   /*
    *  TODO: Check third party payment app support by traversing all
    *        registered third party payment apps.
    */
   return IsBasicCardPayment(aRequestId);
 }
 
+nsresult
+PaymentRequestService::ShowPayment(const nsAString& aRequestId)
+{
+  nsresult rv;
+  RefPtr<payments::PaymentRequest> request;
+  rv = GetPaymentRequestById(aRequestId, getter_AddRefs(request));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+  MOZ_ASSERT(request);
+  request->SetState(payments::PaymentRequest::eInteractive);
+
+  if (mShowingRequest || !CanMakePayment(aRequestId)) {
+    uint32_t responseStatus;
+    if (mShowingRequest) {
+      responseStatus = nsIPaymentActionResponse::PAYMENT_REJECTED;
+    } else {
+      responseStatus = nsIPaymentActionResponse::PAYMENT_NOTSUPPORTED;
+    }
+    nsCOMPtr<nsIPaymentShowActionResponse> showResponse =
+      do_CreateInstance(NS_PAYMENT_SHOW_ACTION_RESPONSE_CONTRACT_ID);
+    MOZ_ASSERT(showResponse);
+    rv = showResponse->Init(aRequestId,
+                            responseStatus,
+                            EmptyString(),
+                            nullptr,
+                            EmptyString(),
+                            EmptyString(),
+                            EmptyString());
+    rv = RespondPayment(showResponse.get());
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+        return rv;
+    }
+  } else {
+    mShowingRequest = request;
+    rv = LaunchUIAction(aRequestId,
+                        IPCPaymentActionRequest::TIPCPaymentShowActionRequest);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+  }
+  return NS_OK;
+}
+
 bool
 PaymentRequestService::IsBasicCardPayment(const nsAString& aRequestId)
 {
   RefPtr<payments::PaymentRequest> request;
   nsresult rv = GetPaymentRequestById(aRequestId, getter_AddRefs(request));
   NS_ENSURE_SUCCESS(rv, false);
   nsCOMPtr<nsIArray> methods;
   rv = request->GetPaymentMethods(getter_AddRefs(methods));
@@ -565,30 +590,27 @@ PaymentRequestService::IsBasicCardPaymen
 }
 
 NS_IMETHODIMP
 PaymentRequestService::ChangePayerDetail(const nsAString& aRequestId,
                                          const nsAString& aPayerName,
                                          const nsAString& aPayerEmail,
                                          const nsAString& aPayerPhone)
 {
-  nsCOMPtr<nsIPaymentRequest> request;
+  RefPtr<payments::PaymentRequest> request;
   nsresult rv = GetPaymentRequestById(aRequestId, getter_AddRefs(request));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
   MOZ_ASSERT(request);
-  payments::PaymentRequest* rowRequest =
-    static_cast<payments::PaymentRequest*>(request.get());
-  if (!rowRequest->GetIPC()) {
+  if (!request->GetIPC()) {
     return NS_ERROR_FAILURE;
   }
-  rv = rowRequest->GetIPC()->ChangePayerDetail(
+  rv = request->GetIPC()->ChangePayerDetail(
     aRequestId, aPayerName, aPayerEmail, aPayerPhone);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
-
   return NS_OK;
 }
 
 } // end of namespace dom
 } // end of namespace mozilla
--- a/dom/payments/PaymentRequestService.h
+++ b/dom/payments/PaymentRequestService.h
@@ -44,16 +44,19 @@ private:
                                  payments::PaymentRequest** aRequest);
 
   nsresult
   LaunchUIAction(const nsAString& aRequestId, uint32_t aActionType);
 
   bool
   CanMakePayment(const nsAString& aRequestId);
 
+  nsresult
+  ShowPayment(const nsAString& aRequestId);
+
   bool
   IsBasicCardPayment(const nsAString& aRequestId);
 
   FallibleTArray<RefPtr<payments::PaymentRequest>> mRequestQueue;
 
   nsCOMPtr<nsIPaymentUIService> mTestingUIService;
 
   RefPtr<payments::PaymentRequest> mShowingRequest;
new file mode 100644
--- /dev/null
+++ b/dom/payments/test/Bug1478740ChromeScript.js
@@ -0,0 +1,75 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+"use strict";
+
+const { XPCOMUtils } = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
+
+const paymentSrv = Cc["@mozilla.org/dom/payments/payment-request-service;1"].getService(Ci.nsIPaymentRequestService);
+
+function emitTestFail(message) {
+  sendAsyncMessage("test-fail", message);
+}
+function emitTestPass(message) {
+  sendAsyncMessage("test-pass", message);
+}
+
+function rejectPayment(requestId) {
+  const responseData = Cc["@mozilla.org/dom/payments/general-response-data;1"].
+                          createInstance(Ci.nsIGeneralResponseData);
+  responseData.initData({});
+  const showResponse = Cc["@mozilla.org/dom/payments/payment-show-action-response;1"].
+                          createInstance(Ci.nsIPaymentShowActionResponse);
+  showResponse.init(requestId,
+                    Ci.nsIPaymentActionResponse.PAYMENT_REJECTED,
+                    "",                 // payment method
+                    responseData,       // payment method data
+                    "",                 // payer name
+                    "",                 // payer email
+                    "");                // payer phone
+  paymentSrv.respondPayment(showResponse.QueryInterface(Ci.nsIPaymentActionResponse));
+}
+
+const DummyUIService = {
+  testName: "",
+  requestId: "",
+  showPayment(requestId) {
+    this.requestId = requestId;
+    sendAsyncMessage("showing-payment", {data:"successful"});
+  },
+  abortPayment(requestId) {
+    this.requestId = requestId;
+  },
+  completePayment(requestId) {
+    this.requestId = requestId;
+  },
+  updatePayment(requestId) {
+    this.requestId = requestId;
+  },
+  closePayment(requestId) {
+    this.requestId = requestId;
+  },
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIPaymentUIService]),
+};
+
+paymentSrv.setTestingUIService(DummyUIService.QueryInterface(Ci.nsIPaymentUIService));
+
+addMessageListener("reject-payment", function() {
+  rejectPayment(DummyUIService.requestId);
+  sendAsyncMessage("reject-payment-complete");
+});
+
+addMessageListener("start-test", function(testName) {
+  DummyUIService.testName = testName;
+  sendAsyncMessage("start-test-complete");
+});
+
+addMessageListener("finish-test", function() {
+  DummyUIService.testName = "";
+  sendAsyncMessage("finish-test-complete");
+});
+
+addMessageListener("teardown", function() {
+  paymentSrv.setTestingUIService(null);
+  sendAsyncMessage('teardown-complete');
+});
new file mode 100644
--- /dev/null
+++ b/dom/payments/test/bug1478740.html
@@ -0,0 +1,44 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>Payment Request Testing</title>
+<script>
+const methods = [
+  {
+    supportedMethods: "basic-card",
+  },
+];
+const details = {
+  id: "simple details",
+  total: {
+    label: "Donation",
+    amount: { currency: "USD", value: "55.00" },
+  },
+};
+const updatedDetails = {
+  id: "simple details",
+  total: {
+    label: "Donation",
+    amount: { currency: "USD", value: "55.00" },
+  },
+  error: "",
+};
+
+window.onmessage = async ({ data: action }) => {
+  let msg = "successful";
+  switch (action) {
+    case "Show Payment":
+      try {
+        let request = new PaymentRequest(methods, details);
+        let responsePromise = await request.show();
+      } catch (err) {
+        msg = err.name;
+      }
+      window.parent.postMessage(msg, "*")
+      break;
+    default:
+      window.parent.postMessage(`fail - unknown postmessage action: ${action}`, "*");
+  }
+};
+
+window.parent.postMessage("successful", "*");
+</script>
--- a/dom/payments/test/mochitest.ini
+++ b/dom/payments/test/mochitest.ini
@@ -1,17 +1,19 @@
 [DEFAULT]
 # skip-if !e10s will be removed once non-e10s is supported
 skip-if = !e10s || !nightly_build
 scheme = https
 support-files =
   blank_page.html
+  bug1478740.html
   simple_payment_request.html
   echo_payment_request.html
   BasiccardChromeScript.js
+  Bug1478740ChromeScript.js
   Bug1490698ChromeScript.js
   ClosePaymentChromeScript.js
   ConstructorChromeScript.js
   CurrencyAmountValidationChromeScript.js
   DefaultData.js
   GeneralChromeScript.js
   PayerDetailsChromeScript.js
   PMIValidationChromeScript.js
@@ -21,16 +23,17 @@ support-files =
   ShowPaymentChromeScript.js
   UpdateErrorsChromeScript.js
 
 [test_abortPayment.html]
 run-if = nightly_build # Bug 1390018: Depends on the Nightly-only UI service
 [test_basiccard.html]
 [test_block_none10s.html]
 skip-if = e10s # Bug 1408250: Don't expose PaymentRequest Constructor in non-e10s
+[test_bug1478740.html]
 [test_bug1490698.html]
 [test_canMakePayment.html]
 run-if = nightly_build # Bug 1390737: Depends on the Nightly-only UI service
 [test_closePayment.html]
 [test_constructor.html]
 [test_currency_amount_validation.html]
 skip-if = (verify && debug)
 [test_payerDetails.html]
new file mode 100644
--- /dev/null
+++ b/dom/payments/test/test_bug1478740.html
@@ -0,0 +1,139 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1478740
+-->
+<head>
+  <meta charset="utf-8">
+  <title>Test for retry PaymentRequest</title>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="DefaultData.js"></script>
+  <script type="application/javascript">
+
+  "use strict";
+  SimpleTest.waitForExplicitFinish();
+
+  const gUrl = SimpleTest.getTestFileURL('Bug1478740ChromeScript.js');
+  const gScript = SpecialPowers.loadChromeScript(gUrl);
+
+  function testFailHandler(message) {
+    ok(false, message);
+  }
+  function testPassHandler(message) {
+    ok(true, message);
+  }
+  gScript.addMessageListener("test-fail", testFailHandler);
+  gScript.addMessageListener("test-pass", testPassHandler);
+
+  async function requestChromeAction(action, params) {
+    gScript.sendAsyncMessage(action, params);
+    await new Promise(resolve => {
+      gScript.addMessageListener(`${action}-complete`, function completeListener() {
+        gScript.removeMessageListener(`${action}-complete`, completeListener);
+        resolve();
+      });
+    });
+  }
+  function unexpectedErrMsg(testName, errName, timing) {
+    return `${testName}: Unexpected error(${errName}) when ${timing} the PaymentRequest.`;
+  }
+
+  function testMultipleShows() {
+    return new Promise(async (resolve) => {
+      const testName = "testMultipleShows";
+      await requestChromeAction("start-test", testName);
+      let expectedResults = ["successful",
+                             "successful",
+                             "successful",
+                             "AbortError",
+                             "AbortError",
+                             "AbortError"];
+      let nextStatus = ["creating first page",
+                        "creating second page",
+                        "showing first payment",
+                        "showing second payment",
+                        "showing third payment",
+                        "aborting first payment"];
+      let currStatus = nextStatus.shift();
+      let ifr1 = document.createElement('iframe');
+      let ifr2 = document.createElement('iframe');
+      let listener = async function(event) {
+        let expected = expectedResults.shift();
+        is(event.data, expected,
+           `${testName}: Expected '${expected}' when ${currStatus}, but got '${event.data}'`);
+        switch (currStatus) {
+          case "creating first page":
+            ifr2.src = "bug1478740.html";
+            document.body.appendChild(ifr2);
+            break;
+          case "creating second page":
+            ifr1.contentWindow.postMessage("Show Payment", "*");
+            break;
+          case "showing first payment":
+            ifr2.contentWindow.postMessage("Show Payment", "*");
+            break;
+          case "showing second payment":
+            ifr2.contentWindow.postMessage("Show Payment", "*");
+            break;
+          case "showing third payment":
+            await requestChromeAction("reject-payment");
+            break;
+          case "aborting first payment":
+            window.removeEventListener("message", listener);
+            gScript.removeMessageListener("showing-payment", listener);
+            document.body.removeChild(ifr1);
+            document.body.removeChild(ifr2);
+            resolve();
+            break;
+          default:
+            ok(false, `unknown status ${currStatus}`);
+        }
+        currStatus = nextStatus.shift();
+      }
+      window.addEventListener("message", listener);
+      gScript.addMessageListener("showing-payment", listener);
+      ifr1.src = "bug1478740.html";
+      document.body.appendChild(ifr1);
+      await requestChromeAction("finish-test");
+    });
+  }
+
+  function teardown() {
+    return new Promise((resolve, reject) => {
+      gScript.addMessageListener("teardown-complete", function teardownCompleteHandler() {
+        gScript.removeMessageListener("teardown-complete", teardownCompleteHandler);
+        gScript.removeMessageListener("test-fail", testFailHandler);
+        gScript.removeMessageListener("test-pass", testPassHandler);
+        gScript.destroy();
+        SimpleTest.finish();
+        resolve();
+      });
+      gScript.sendAsyncMessage("teardown");
+    });
+  }
+
+  async function runTests() {
+    try {
+      await testMultipleShows();
+      await teardown();
+    } catch(e) {
+      ok(false, "Unexpected error: " + e.name);
+      SimpleTest.finish();
+    }
+  }
+
+  window.addEventListener('load', function() {
+    SpecialPowers.pushPrefEnv({
+      'set': [
+        ['dom.payments.request.enabled', true],
+        ['dom.payments.request.user_interaction_required', false],
+      ]
+    }, runTests);
+  });
+  </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1478740">Mozilla Bug 1478740</a>
+</body>
+</html>
--- a/dom/presentation/Presentation.cpp
+++ b/dom/presentation/Presentation.cpp
@@ -163,20 +163,28 @@ Presentation::HasReceiverSupport() const
   }
 
   nsCOMPtr<nsIURI> presentationURI;
   nsresult rv = NS_NewURI(getter_AddRefs(presentationURI), presentationURL);
   if (NS_FAILED(rv)) {
     return false;
   }
 
+  bool isPrivateWin = false;
+  nsCOMPtr<nsIDocument> doc = mWindow->GetExtantDoc();
+  if (doc) {
+    isPrivateWin =
+      doc->NodePrincipal()->OriginAttributesRef().mPrivateBrowsingId > 0;
+  }
+
   nsCOMPtr<nsIURI> docURI = mWindow->GetDocumentURI();
   return NS_SUCCEEDED(securityManager->CheckSameOriginURI(presentationURI,
                                                           docURI,
-                                                          false));
+                                                          false,
+                                                          isPrivateWin));
 }
 
 bool
 Presentation::IsInPresentedContent() const
 {
   if (!mWindow) {
     return false;
   }
--- a/dom/security/FramingChecker.cpp
+++ b/dom/security/FramingChecker.cpp
@@ -102,17 +102,19 @@ FramingChecker::CheckOneFrameOptionsPoli
             ssm->IsSystemPrincipal(topDoc->NodePrincipal(), &system)) &&
           system) {
         // Found a system-principled doc: last docshell was top.
         break;
       }
 
       if (checkSameOrigin) {
         topDoc->NodePrincipal()->GetURI(getter_AddRefs(topUri));
-        rv = ssm->CheckSameOriginURI(uri, topUri, true);
+        bool isPrivateWin =
+          topDoc->NodePrincipal()->OriginAttributesRef().mPrivateBrowsingId > 0;
+        rv = ssm->CheckSameOriginURI(uri, topUri, true, isPrivateWin);
 
         // one of the ancestors is not same origin as this document
         if (NS_FAILED(rv)) {
           ReportXFOViolation(curDocShellItem, uri, eSAMEORIGIN);
           return false;
         }
       }
     } else {
@@ -146,18 +148,19 @@ FramingChecker::CheckOneFrameOptionsPoli
          aPolicy[allowFromLen] != '\t')) {
       ReportXFOViolation(curDocShellItem, uri, eALLOWFROM);
       return false;
     }
     rv = NS_NewURI(getter_AddRefs(uri), Substring(aPolicy, allowFromLen));
     if (NS_FAILED(rv)) {
       return false;
     }
-
-    rv = ssm->CheckSameOriginURI(uri, topUri, true);
+    bool isPrivateWin =
+      topDoc->NodePrincipal()->OriginAttributesRef().mPrivateBrowsingId > 0;
+    rv = ssm->CheckSameOriginURI(uri, topUri, true, isPrivateWin);
     if (NS_FAILED(rv)) {
       ReportXFOViolation(curDocShellItem, uri, eALLOWFROM);
       return false;
     }
   }
 
   return true;
 }
--- a/dom/security/test/cors/head.js
+++ b/dom/security/test/cors/head.js
@@ -18,18 +18,18 @@ let promise = require("promise");
  * @param {String} toolId Optional. The ID of the tool to be selected.
  * @param {String} hostType Optional. The type of toolbox host to be used.
  * @return {Promise} Resolves with the toolbox, when it has been opened.
  */
 var openToolboxForTab = async function(tab, toolId, hostType) {
   info("Opening the toolbox");
 
   let toolbox;
-  let target = TargetFactory.forTab(tab);
-  await target.makeRemote();
+  let target = await TargetFactory.forTab(tab);
+  await target.attach();
 
   // Check if the toolbox is already loaded.
   toolbox = gDevTools.getToolbox(target);
   if (toolbox) {
     if (!toolId || (toolId && toolbox.getPanel(toolId))) {
       info("Toolbox is already opened");
       return toolbox;
     }
--- a/dom/security/test/general/browser_test_FTP_console_warning.js
+++ b/dom/security/test/general/browser_test_FTP_console_warning.js
@@ -20,18 +20,18 @@ let promise = require("promise");
  * @param {String} toolId Optional. The ID of the tool to be selected.
  * @param {String} hostType Optional. The type of toolbox host to be used.
  * @return {Promise} Resolves with the toolbox, when it has been opened.
  */
 var openToolboxForTab = async function(tab, toolId, hostType) {
   info("Opening the toolbox");
 
   let toolbox;
-  let target = TargetFactory.forTab(tab);
-  await target.makeRemote();
+  let target = await TargetFactory.forTab(tab);
+  await target.attach();
 
   // Check if the toolbox is already loaded.
   toolbox = gDevTools.getToolbox(target);
   if (toolbox) {
     if (!toolId || (toolId && toolbox.getPanel(toolId))) {
       info("Toolbox is already opened");
       return toolbox;
     }
--- a/gfx/layers/GPUVideoImage.h
+++ b/gfx/layers/GPUVideoImage.h
@@ -52,17 +52,21 @@ public:
   gfx::IntSize GetSize() const override { return mSize; }
 
 private:
   GPUVideoTextureData* GetData() const
   {
     if (!mTextureClient) {
       return nullptr;
     }
-    return mTextureClient->GetInternalData()->AsGPUVideoTextureData();
+    TextureData* data = mTextureClient->GetInternalData();
+    if (!data) {
+      return nullptr;
+    }
+    return data->AsGPUVideoTextureData();
   }
 
 public:
   already_AddRefed<gfx::SourceSurface> GetAsSourceSurface() override
   {
     GPUVideoTextureData* data = GetData();
     if (!data) {
       return nullptr;
--- a/gfx/layers/wr/WebRenderBridgeParent.cpp
+++ b/gfx/layers/wr/WebRenderBridgeParent.cpp
@@ -146,16 +146,44 @@ record_telemetry_time(mozilla::wr::Telem
 }
 
 namespace mozilla {
 
 namespace layers {
 
 using namespace mozilla::gfx;
 
+class ScheduleObserveLayersUpdate: public wr::NotificationHandler {
+public:
+  ScheduleObserveLayersUpdate(RefPtr<CompositorBridgeParentBase> aBridge,
+                              LayersId aLayersId,
+                              LayersObserverEpoch aEpoch,
+                              bool aIsActive)
+  : mBridge(aBridge)
+  , mLayersId(aLayersId)
+  , mObserverEpoch(aEpoch)
+  , mIsActive(aIsActive)
+  {}
+
+  virtual void Notify(wr::Checkpoint) override {
+    CompositorThreadHolder::Loop()->PostTask(
+      NewRunnableMethod<LayersId, LayersObserverEpoch, int>(
+        "ObserveLayersUpdate",
+        mBridge, &CompositorBridgeParentBase::ObserveLayersUpdate,
+        mLayersId, mObserverEpoch, mIsActive
+      )
+    );
+  }
+protected:
+  RefPtr<CompositorBridgeParentBase> mBridge;
+  LayersId mLayersId;
+  LayersObserverEpoch mObserverEpoch;
+  bool mIsActive;
+};
+
 class MOZ_STACK_CLASS AutoWebRenderBridgeParentAsyncMessageSender
 {
 public:
   explicit AutoWebRenderBridgeParentAsyncMessageSender(WebRenderBridgeParent* aWebRenderBridgeParent,
                                                        InfallibleTArray<OpDestroy>* aDestroyActors = nullptr)
     : mWebRenderBridgeParent(aWebRenderBridgeParent)
     , mActorsToDestroy(aDestroyActors)
   {
@@ -802,49 +830,61 @@ WebRenderBridgeParent::RecvSetDisplayLis
   // destination.
   // Also note that this needs to happen before the display list transaction is
   // sent to WebRender, so that the UpdateHitTestingTree call is guaranteed to
   // be in the updater queue at the time that the scene swap completes.
   UpdateAPZScrollData(wrEpoch, std::move(const_cast<WebRenderScrollData&>(aScrollData)));
 
   wr::Vec<uint8_t> dlData(std::move(dl));
 
+  bool observeLayersUpdate = ShouldParentObserveEpoch();
+
   if (validTransaction) {
     if (IsRootWebRenderBridgeParent()) {
       LayoutDeviceIntSize widgetSize = mWidget->GetClientSize();
       LayoutDeviceIntRect docRect(LayoutDeviceIntPoint(), widgetSize);
       txn.SetWindowParameters(widgetSize, docRect);
     }
     gfx::Color clearColor(0.f, 0.f, 0.f, 0.f);
     txn.SetDisplayList(clearColor, wrEpoch, LayerSize(aSize.width, aSize.height),
                        mPipelineId, aContentSize,
                        dlDesc, dlData);
 
+    if (observeLayersUpdate) {
+      txn.Notify(
+        wr::Checkpoint::SceneBuilt,
+        MakeUnique<ScheduleObserveLayersUpdate>(
+          mCompositorBridge,
+          GetLayersId(),
+          mChildLayersObserverEpoch,
+          true
+        )
+      );
+    }
+
     mApi->SendTransaction(txn);
 
     // We will schedule generating a frame after the scene
     // build is done, so we don't need to do it here.
+  } else if (observeLayersUpdate) {
+    mCompositorBridge->ObserveLayersUpdate(GetLayersId(), mChildLayersObserverEpoch, true);
   }
 
   HoldPendingTransactionId(wrEpoch, aTransactionId, aContainsSVGGroup,
                            aRefreshStartTime, aTxnStartTime, aFwdTime);
 
   if (!validTransaction) {
     // Pretend we composited since someone is wating for this event,
     // though DisplayList was not pushed to webrender.
     if (CompositorBridgeParent* cbp = GetRootCompositorBridgeParent()) {
       TimeStamp now = TimeStamp::Now();
       cbp->NotifyPipelineRendered(mPipelineId, wrEpoch, now, now);
     }
   }
 
-  if (ShouldParentObserveEpoch()) {
-    mCompositorBridge->ObserveLayersUpdate(GetLayersId(), mChildLayersObserverEpoch, true);
-  }
-
   wr::IpcResourceUpdateQueue::ReleaseShmems(this, aSmallShmems);
   wr::IpcResourceUpdateQueue::ReleaseShmems(this, aLargeShmems);
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 WebRenderBridgeParent::RecvEmptyTransaction(const FocusTarget& aFocusTarget,
                                             const ScrollUpdatesMap& aUpdates,
@@ -888,16 +928,28 @@ WebRenderBridgeParent::RecvEmptyTransact
     mAsyncImageManager->SetCompositionTime(TimeStamp::Now());
     wr::TransactionBuilder txn;
     txn.SetLowPriority(!IsRootWebRenderBridgeParent());
     wr::Epoch wrEpoch = GetNextWrEpoch();
     txn.UpdateEpoch(mPipelineId, wrEpoch);
     if (!ProcessWebRenderParentCommands(aCommands, txn)) {
       return IPC_FAIL(this, "Invalid parent command found");
     }
+    if (ShouldParentObserveEpoch()) {
+      txn.Notify(
+        wr::Checkpoint::SceneBuilt,
+        MakeUnique<ScheduleObserveLayersUpdate>(
+          mCompositorBridge,
+          GetLayersId(),
+          mChildLayersObserverEpoch,
+          true
+        )
+      );
+    }
+
     mApi->SendTransaction(txn);
     scheduleComposite = true;
   }
 
   bool sendDidComposite = true;
   if (scheduleComposite || !mPendingTransactionIds.empty()) {
     // If we are going to kick off a new composite as a result of this
     // transaction, or if there are already composite-triggering pending
@@ -926,20 +978,16 @@ WebRenderBridgeParent::RecvEmptyTransact
     // we just added, and now we're going to pretend we rendered it
     MOZ_ASSERT(mPendingTransactionIds.size() == 1);
     if (CompositorBridgeParent* cbp = GetRootCompositorBridgeParent()) {
       TimeStamp now = TimeStamp::Now();
       cbp->NotifyPipelineRendered(mPipelineId, mWrEpoch, now, now);
     }
   }
 
-  if (ShouldParentObserveEpoch()) {
-    mCompositorBridge->ObserveLayersUpdate(GetLayersId(), mChildLayersObserverEpoch, true);
-  }
-
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 WebRenderBridgeParent::RecvSetFocusTarget(const FocusTarget& aFocusTarget)
 {
   UpdateAPZFocusState(aFocusTarget);
   return IPC_OK();
@@ -1262,22 +1310,31 @@ WebRenderBridgeParent::RecvSetLayersObse
 }
 
 mozilla::ipc::IPCResult
 WebRenderBridgeParent::RecvClearCachedResources()
 {
   if (mDestroyed) {
     return IPC_OK();
   }
-  mCompositorBridge->ObserveLayersUpdate(GetLayersId(), mChildLayersObserverEpoch, false);
 
   // Clear resources
   wr::TransactionBuilder txn;
   txn.SetLowPriority(true);
   txn.ClearDisplayList(GetNextWrEpoch(), mPipelineId);
+  txn.Notify(
+    wr::Checkpoint::SceneBuilt,
+    MakeUnique<ScheduleObserveLayersUpdate>(
+      mCompositorBridge,
+      GetLayersId(),
+      mChildLayersObserverEpoch,
+      false
+    )
+  );
+
   mApi->SendTransaction(txn);
   // Schedule generate frame to clean up Pipeline
   ScheduleGenerateFrame();
   // Remove animations.
   for (const auto& id : mActiveAnimations) {
     mAnimStorage->ClearById(id);
   }
   mActiveAnimations.clear();
--- a/gfx/webrender/Cargo.toml
+++ b/gfx/webrender/Cargo.toml
@@ -68,14 +68,14 @@ optional = true
 
 [dev-dependencies]
 mozangle = "0.1"
 
 [target.'cfg(any(target_os = "android", all(unix, not(target_os = "macos"))))'.dependencies]
 freetype = { version = "0.4", default-features = false }
 
 [target.'cfg(target_os = "windows")'.dependencies]
-dwrote = "0.4.1"
+dwrote = "0.5.1"
 
 [target.'cfg(target_os = "macos")'.dependencies]
 core-foundation = "0.6"
 core-graphics = "0.17.1"
 core-text = { version = "13", default-features = false }
--- a/gfx/webrender/src/internal_types.rs
+++ b/gfx/webrender/src/internal_types.rs
@@ -1,14 +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 api::{DebugCommand, DeviceUintRect, DocumentId, ExternalImageData, ExternalImageId};
-use api::ImageFormat;
+use api::{ImageFormat, NotificationRequest};
 use device::TextureFilter;
 use renderer::PipelineInfo;
 use gpu_cache::GpuCacheUpdateList;
 use fxhash::FxHasher;
 use profiler::BackendProfileCounters;
 use std::{usize, i32};
 use std::collections::{HashMap, HashSet};
 use std::f32;
@@ -155,16 +155,17 @@ pub enum ResultMsg {
     },
     PublishPipelineInfo(PipelineInfo),
     PublishDocument(
         DocumentId,
         RenderedDocument,
         TextureUpdateList,
         BackendProfileCounters,
     ),
+    AppendNotificationRequests(Vec<NotificationRequest>),
 }
 
 #[derive(Clone, Debug)]
 pub struct ResourceCacheError {
     description: String,
 }
 
 impl ResourceCacheError {
--- a/gfx/webrender/src/platform/windows/font.rs
+++ b/gfx/webrender/src/platform/windows/font.rs
@@ -1,13 +1,13 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-use api::{FontInstanceFlags, FontKey, FontRenderMode};
+use api::{FontInstanceFlags, FontKey, FontRenderMode, FontVariation};
 use api::{ColorU, GlyphDimensions};
 use dwrote;
 use gamma_lut::ColorLut;
 use glyph_rasterizer::{FontInstance, FontTransform, GlyphKey};
 use internal_types::{FastHashMap, ResourceCacheError};
 use std::collections::hash_map::Entry;
 use std::sync::Arc;
 cfg_if! {
@@ -27,17 +27,17 @@ lazy_static! {
         weight: dwrote::FontWeight::Regular,
         stretch: dwrote::FontStretch::Normal,
         style: dwrote::FontStyle::Normal,
     };
 }
 
 pub struct FontContext {
     fonts: FastHashMap<FontKey, dwrote::FontFace>,
-    simulations: FastHashMap<(FontKey, dwrote::DWRITE_FONT_SIMULATIONS), dwrote::FontFace>,
+    variations: FastHashMap<(FontKey, dwrote::DWRITE_FONT_SIMULATIONS, Vec<FontVariation>), dwrote::FontFace>,
     #[cfg(not(feature = "pathfinder"))]
     gamma_luts: FastHashMap<(u16, u16), GammaLut>,
 }
 
 // DirectWrite is safe to use on multiple threads and non-shareable resources are
 // all hidden inside their font context.
 unsafe impl Send for FontContext {}
 
@@ -95,17 +95,17 @@ fn is_bitmap_font(font: &FontInstance) -
     font.render_mode != FontRenderMode::Mono &&
         font.flags.contains(FontInstanceFlags::EMBEDDED_BITMAPS)
 }
 
 impl FontContext {
     pub fn new() -> Result<FontContext, ResourceCacheError> {
         Ok(FontContext {
             fonts: FastHashMap::default(),
-            simulations: FastHashMap::default(),
+            variations: FastHashMap::default(),
             #[cfg(not(feature = "pathfinder"))]
             gamma_luts: FastHashMap::default(),
         })
     }
 
     pub fn has_font(&self, font_key: &FontKey) -> bool {
         self.fonts.contains_key(font_key)
     }
@@ -136,17 +136,17 @@ impl FontContext {
             None => { panic!("missing descriptor {:?}", font_handle) }
         };
         let face = font.create_font_face();
         self.fonts.insert(*font_key, face);
     }
 
     pub fn delete_font(&mut self, font_key: &FontKey) {
         if let Some(_) = self.fonts.remove(font_key) {
-            self.simulations.retain(|k, _| k.0 != *font_key);
+            self.variations.retain(|k, _| k.0 != *font_key);
         }
     }
 
     // Assumes RGB format from dwrite, which is 3 bytes per pixel as dwrite
     // doesn't output an alpha value via GlyphRunAnalysis::CreateAlphaTexture
     #[allow(dead_code)]
     fn print_glyph_data(&self, data: &[u8], width: usize, height: usize) {
         // Rust doesn't have step_by support on stable :(
@@ -162,24 +162,42 @@ impl FontContext {
             println!("");
         }
     }
 
     fn get_font_face(
         &mut self,
         font: &FontInstance,
     ) -> &dwrote::FontFace {
-        if !font.flags.contains(FontInstanceFlags::SYNTHETIC_BOLD) {
+        if !font.flags.contains(FontInstanceFlags::SYNTHETIC_BOLD) &&
+           font.variations.is_empty() {
             return self.fonts.get(&font.font_key).unwrap();
         }
-        let sims = dwrote::DWRITE_FONT_SIMULATIONS_BOLD;
-        match self.simulations.entry((font.font_key, sims)) {
+        let sims = if font.flags.contains(FontInstanceFlags::SYNTHETIC_BOLD) {
+            dwrote::DWRITE_FONT_SIMULATIONS_BOLD
+        } else {
+            dwrote::DWRITE_FONT_SIMULATIONS_NONE
+        };
+        match self.variations.entry((font.font_key, sims, font.variations.clone())) {
             Entry::Occupied(entry) => entry.into_mut(),
             Entry::Vacant(entry) => {
                 let normal_face = self.fonts.get(&font.font_key).unwrap();
+                if !font.variations.is_empty() {
+                    if let Some(var_face) = normal_face.create_font_face_with_variations(
+                        sims,
+                        &font.variations.iter().map(|var| {
+                            dwrote::DWRITE_FONT_AXIS_VALUE {
+                                axisTag: var.tag,
+                                value: var.value,
+                            }
+                        }).collect::<Vec<_>>(),
+                    ) {
+                        return entry.insert(var_face);
+                    }
+                }
                 entry.insert(normal_face.create_font_face_with_simulations(sims))
             }
         }
     }
 
     fn create_glyph_analysis(
         &mut self,
         font: &FontInstance,
--- a/gfx/webrender/src/render_backend.rs
+++ b/gfx/webrender/src/render_backend.rs
@@ -1085,16 +1085,20 @@ impl RenderBackend {
         }
 
         drain_filter(
             &mut notifications,
             |n| { n.when() == Checkpoint::FrameBuilt },
             |n| { n.notify(); },
         );
 
+        if !notifications.is_empty() {
+            self.result_tx.send(ResultMsg::AppendNotificationRequests(notifications)).unwrap();
+        }
+
         // Always forward the transaction to the renderer if a frame was requested,
         // otherwise gecko can get into a state where it waits (forever) for the
         // transaction to complete before sending new work.
         if requested_frame {
             self.notifier.new_frame_ready(document_id, scroll, render_frame, frame_build_time);
         }
     }
 
--- a/gfx/webrender/src/renderer.rs
+++ b/gfx/webrender/src/renderer.rs
@@ -7,17 +7,17 @@
 //! The `webrender::renderer` module provides the interface to webrender, which
 //! is accessible through [`Renderer`][renderer]
 //!
 //! [renderer]: struct.Renderer.html
 
 use api::{BlobImageHandler, ColorF, DeviceIntPoint, DeviceIntRect, DeviceIntSize};
 use api::{DeviceUintPoint, DeviceUintRect, DeviceUintSize, DocumentId, Epoch, ExternalImageId};
 use api::{ExternalImageType, FontRenderMode, FrameMsg, ImageFormat, PipelineId};
-use api::{ImageRendering};
+use api::{ImageRendering, Checkpoint, NotificationRequest};
 use api::{MemoryReport, VoidPtrToSizeFn};
 use api::{RenderApiSender, RenderNotifier, TexelRect, TextureTarget};
 use api::{channel};
 use api::DebugCommand;
 use api::channel::PayloadReceiverHelperMethods;
 use batch::{BatchKind, BatchTextures, BrushBatchKind};
 #[cfg(any(feature = "capture", feature = "replay"))]
 use capture::{CaptureConfig, ExternalCaptureImage, PlainExternalImage};
@@ -48,16 +48,17 @@ use profiler::{BackendProfileCounters, F
 use device::query::GpuProfiler;
 use rayon::{ThreadPool, ThreadPoolBuilder};
 use record::ApiRecordingReceiver;
 use render_backend::RenderBackend;
 use scene_builder::{SceneBuilder, LowPrioritySceneBuilder};
 use shade::Shaders;
 use render_task::{RenderTask, RenderTaskKind, RenderTaskTree};
 use resource_cache::ResourceCache;
+use util::drain_filter;
 
 use std;
 use std::cmp;
 use std::collections::VecDeque;
 use std::collections::hash_map::Entry;
 use std::f32;
 use std::mem;
 use std::os::raw::c_void;
@@ -1401,16 +1402,19 @@ pub struct Renderer {
 
     pub renderer_errors: Vec<RendererError>,
 
     /// List of profile results from previous frames. Can be retrieved
     /// via get_frame_profiles().
     cpu_profiles: VecDeque<CpuProfile>,
     gpu_profiles: VecDeque<GpuProfile>,
 
+    /// Notification requests to be fulfilled after rendering.
+    notifications: Vec<NotificationRequest>,
+
     #[cfg(feature = "capture")]
     read_fbo: FBOId,
     #[cfg(feature = "replay")]
     owned_external_images: FastHashMap<(ExternalImageId, u8), ExternalTexture>,
 }
 
 #[derive(Debug)]
 pub enum RendererError {
@@ -1834,16 +1838,17 @@ impl Renderer {
             gpu_cache_overflow: false,
             texture_cache_upload_pbo,
             texture_resolver,
             renderer_errors: Vec::new(),
             #[cfg(feature = "capture")]
             read_fbo,
             #[cfg(feature = "replay")]
             owned_external_images: FastHashMap::default(),
+            notifications: Vec::new(),
         };
 
         renderer.set_debug_flags(options.debug_flags);
 
         let sender = RenderApiSender::new(api_tx, payload_tx);
         Ok((renderer, sender))
     }
 
@@ -1946,16 +1951,19 @@ impl Renderer {
                     // If we receive a `PublishDocument` message followed by this one
                     // within the same update we need to cancel the frame because we
                     // might have deleted the resources in use in the frame due to a
                     // memory pressure event.
                     if cancel_rendering {
                         self.active_documents.clear();
                     }
                 }
+                ResultMsg::AppendNotificationRequests(mut notifications) => {
+                    self.notifications.append(&mut notifications);
+                }
                 ResultMsg::RefreshShader(path) => {
                     self.pending_shader_updates.push(path);
                 }
                 ResultMsg::DebugOutput(output) => match output {
                     DebugOutput::FetchDocuments(string) |
                     DebugOutput::FetchClipScrollTree(string) => {
                         self.debug_server.send(string);
                     }
@@ -2272,17 +2280,30 @@ impl Renderer {
     /// Renders the current frame.
     ///
     /// A Frame is supplied by calling [`generate_frame()`][genframe].
     /// [genframe]: ../../webrender_api/struct.DocumentApi.html#method.generate_frame
     pub fn render(
         &mut self,
         framebuffer_size: DeviceUintSize,
     ) -> Result<RendererStats, Vec<RendererError>> {
-        self.render_impl(Some(framebuffer_size))
+        let result = self.render_impl(Some(framebuffer_size));
+
+        drain_filter(
+            &mut self.notifications,
+            |n| { n.when() == Checkpoint::FrameRendered },
+            |n| { n.notify(); },
+        );
+
+        // This is the end of the rendering pipeline. If some notifications are is still there,
+        // just clear them and they will autimatically fire the Checkpoint::TransactionDropped
+        // event. Otherwise they would just pile up in this vector forever.
+        self.notifications.clear();
+
+        result
     }
 
     // If framebuffer_size is None, don't render
     // to the main frame buffer. This is useful
     // to update texture cache render tasks but
     // avoid doing a full frame render.
     fn render_impl(
         &mut self,
--- a/gfx/webrender_api/Cargo.toml
+++ b/gfx/webrender_api/Cargo.toml
@@ -24,9 +24,9 @@ serde_derive = { version = "=1.0.66", fe
 serde_bytes = "0.10"
 time = "0.1"
 
 [target.'cfg(target_os = "macos")'.dependencies]
 core-foundation = "0.6"
 core-graphics = "0.17.1"
 
 [target.'cfg(target_os = "windows")'.dependencies]
-dwrote = "0.4.1"
+dwrote = "0.5.1"
--- a/gfx/webrender_api/src/api.rs
+++ b/gfx/webrender_api/src/api.rs
@@ -1220,16 +1220,17 @@ pub trait RenderNotifier: Send {
     fn shut_down(&self) {}
 }
 
 #[repr(u32)]
 #[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
 pub enum Checkpoint {
     SceneBuilt,
     FrameBuilt,
+    FrameRendered,
     /// NotificationRequests get notified with this if they get dropped without having been
     /// notified. This provides the guarantee that if a request is created it will get notified.
     TransactionDropped,
 }
 
 pub trait NotificationHandler : Send + Sync {
     fn notify(&self, when: Checkpoint);
 }
--- a/gfx/webrender_api/src/display_item.rs
+++ b/gfx/webrender_api/src/display_item.rs
@@ -278,19 +278,17 @@ impl NormalBorder {
     /// Normalizes a border so that we don't render disallowed stuff, like inset
     /// borders that are less than two pixels wide.
     #[inline]
     pub fn normalize(&mut self, widths: &LayoutSideOffsets) {
         #[inline]
         fn renders_small_border_solid(style: BorderStyle) -> bool {
             match style {
                 BorderStyle::Groove |
-                BorderStyle::Ridge |
-                BorderStyle::Inset |
-                BorderStyle::Outset => true,
+                BorderStyle::Ridge => true,
                 _ => false,
             }
         }
 
         let normalize_side = |side: &mut BorderSide, width: f32| {
             if renders_small_border_solid(side.style) && width < 2. {
                 side.style = BorderStyle::Solid;
             }
--- a/gfx/webrender_bindings/Cargo.toml
+++ b/gfx/webrender_bindings/Cargo.toml
@@ -18,15 +18,15 @@ fxhash = "0.2.1"
 
 [dependencies.webrender]
 path = "../webrender"
 version = "0.57.2"
 default-features = false
 features = ["capture", "serialize_program"]
 
 [target.'cfg(target_os = "windows")'.dependencies]
-dwrote = "0.4.1"
+dwrote = "0.5.1"
 
 [target.'cfg(target_os = "macos")'.dependencies]
 core-foundation = "0.6"
 core-graphics = "0.17.1"
 foreign-types = "0.3.0"
 
--- a/gfx/webrender_bindings/WebRenderAPI.cpp
+++ b/gfx/webrender_bindings/WebRenderAPI.cpp
@@ -584,16 +584,21 @@ WebRenderAPI::Capture()
 
 void
 TransactionBuilder::Clear()
 {
   wr_resource_updates_clear(mTxn);
 }
 
 void
+TransactionBuilder::Notify(wr::Checkpoint aWhen, UniquePtr<NotificationHandler> aEvent) {
+  wr_transaction_notify(mTxn, aWhen, reinterpret_cast<uintptr_t>(aEvent.release()));
+}
+
+void
 TransactionBuilder::AddImage(ImageKey key, const ImageDescriptor& aDescriptor,
                              wr::Vec<uint8_t>& aBytes)
 {
   wr_resource_updates_add_image(mTxn,
                                 key,
                                 &aDescriptor,
                                 &aBytes.inner);
 }
@@ -1354,8 +1359,20 @@ DisplayListBuilder::FixedPosScrollTarget
 Maybe<layers::FrameMetrics::ViewID>
 DisplayListBuilder::FixedPosScrollTargetTracker::GetScrollTargetForASR(const ActiveScrolledRoot* aAsr)
 {
   return aAsr == mAsr ? Some(mScrollId) : Nothing();
 }
 
 } // namespace wr
 } // namespace mozilla
+
+extern "C" {
+
+void wr_transaction_notification_notified(uintptr_t aHandler, mozilla::wr::Checkpoint aWhen) {
+  auto handler = reinterpret_cast<mozilla::wr::NotificationHandler*>(aHandler);
+  handler->Notify(aWhen);
+  // TODO: it would be better to get a callback when the object is destroyed on the
+  // rust side and delete then.
+  delete handler;
+}
+
+} // extern C
--- a/gfx/webrender_bindings/WebRenderAPI.h
+++ b/gfx/webrender_bindings/WebRenderAPI.h
@@ -45,16 +45,28 @@ class RendererEvent;
 struct Line {
   wr::LayoutRect bounds;
   float wavyLineThickness;
   wr::LineOrientation orientation;
   wr::ColorF color;
   wr::LineStyle style;
 };
 
+/// A handler that can be bundled into a transaction and notified at specific
+/// points in the rendering pipeline, such as after scene building or after
+/// frame building.
+///
+/// If for any reason the handler is dropped before reaching the requested
+/// point, it is notified with the value Checkpoint::TransactionDropped.
+/// So it is safe to assume that the handler will be notified "at some point".
+class NotificationHandler {
+public:
+  virtual void Notify(wr::Checkpoint aCheckpoint) = 0;
+  virtual ~NotificationHandler() = default;
+};
 
 class TransactionBuilder {
 public:
   explicit TransactionBuilder(bool aUseSceneBuilderThread = true);
 
   ~TransactionBuilder();
 
   void SetLowPriority(bool aIsLowPriority);
@@ -143,16 +155,18 @@ public:
                        wr::FontKey aFontKey,
                        float aGlyphSize,
                        const wr::FontInstanceOptions* aOptions,
                        const wr::FontInstancePlatformOptions* aPlatformOptions,
                        wr::Vec<uint8_t>& aVariations);
 
   void DeleteFontInstance(wr::FontInstanceKey aKey);
 
+  void Notify(wr::Checkpoint aWhen, UniquePtr<NotificationHandler> aHandler);
+
   void Clear();
 
   bool UseSceneBuilderThread() const { return mUseSceneBuilderThread; }
   Transaction* Raw() { return mTxn; }
 protected:
   bool mUseSceneBuilderThread;
   Transaction* mTxn;
 };
--- a/gfx/webrender_bindings/revision.txt
+++ b/gfx/webrender_bindings/revision.txt
@@ -1,1 +1,1 @@
-4e7d0ed1b08f5402a6971230864a7e497b2453ec
+b83ec3fd994b69f31e9c6b6ffa19426b2b98c66a
--- a/gfx/webrender_bindings/src/bindings.rs
+++ b/gfx/webrender_bindings/src/bindings.rs
@@ -499,16 +499,18 @@ unsafe impl Send for CppNotifier {}
 
 extern "C" {
     fn wr_notifier_wake_up(window_id: WrWindowId);
     fn wr_notifier_new_frame_ready(window_id: WrWindowId);
     fn wr_notifier_nop_frame_done(window_id: WrWindowId);
     fn wr_notifier_external_event(window_id: WrWindowId,
                                   raw_event: usize);
     fn wr_schedule_render(window_id: WrWindowId);
+
+    fn wr_transaction_notification_notified(handler: usize, when: Checkpoint);
 }
 
 impl RenderNotifier for CppNotifier {
     fn clone(&self) -> Box<RenderNotifier> {
         Box::new(CppNotifier {
             window_id: self.window_id,
         })
     }
@@ -1093,16 +1095,31 @@ pub extern "C" fn wr_transaction_set_low
 }
 
 #[no_mangle]
 pub extern "C" fn wr_transaction_is_empty(txn: &Transaction) -> bool {
     txn.is_empty()
 }
 
 #[no_mangle]
+pub extern "C" fn wr_transaction_notify(txn: &mut Transaction, when: Checkpoint, event: usize) {
+    struct GeckoNotification(usize);
+    impl NotificationHandler for GeckoNotification {
+        fn notify(&self, when: Checkpoint) {
+            unsafe {
+                wr_transaction_notification_notified(self.0, when);
+            }
+        }
+    }
+
+    let handler = Arc::new(GeckoNotification(event));
+    txn.notify(NotificationRequest::new(when, handler));
+}
+
+#[no_mangle]
 pub extern "C" fn wr_transaction_update_epoch(
     txn: &mut Transaction,
     pipeline_id: WrPipelineId,
     epoch: WrEpoch,
 ) {
     txn.update_epoch(pipeline_id, epoch);
 }
 
--- a/gfx/webrender_bindings/webrender_ffi_generated.h
+++ b/gfx/webrender_bindings/webrender_ffi_generated.h
@@ -38,16 +38,26 @@ enum class BorderStyle : uint32_t {
 
 enum class BoxShadowClipMode : uint32_t {
   Outset = 0,
   Inset = 1,
 
   Sentinel /* this must be last for serialization purposes. */
 };
 
+enum class Checkpoint : uint32_t {
+  SceneBuilt,
+  FrameBuilt,
+  // NotificationRequests get notified with this if they get dropped without having been
+  // notified. This provides the guarantee that if a request is created it will get notified.
+  TransactionDropped,
+
+  Sentinel /* this must be last for serialization purposes. */
+};
+
 enum class ClipMode {
   Clip,
   ClipOut,
 
   Sentinel /* this must be last for serialization purposes. */
 };
 
 enum class ExtendMode : uint32_t {
@@ -1747,16 +1757,25 @@ WR_FUNC;
 WR_INLINE
 bool wr_transaction_is_empty(const Transaction *aTxn)
 WR_FUNC;
 
 WR_INLINE
 Transaction *wr_transaction_new(bool aDoAsync)
 WR_FUNC;