Merge inbound to mozilla-central. a=merge
authorOana Pop Rus <opoprus@mozilla.com>
Thu, 09 May 2019 06:34:15 +0300
changeset 531979 34a824c75b7b5618a06ba8987c418d6363da5038
parent 531975 59314da6bb6b37f3242e8a3b68dcb3849728818c (diff)
parent 531978 465fa362c7020a91a653ba36f068487558114b89 (current diff)
child 531982 9d0b46d9bb9ac851d04c0cce35e00d247ec77a6b
child 532043 3ce2113ec6925b3eb9f204f7175549f74cac6fc4
push id11265
push userffxbld-merge
push dateMon, 13 May 2019 10:53:39 +0000
treeherdermozilla-beta@77e0fe8dbdd3 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone68.0a1
first release with
nightly linux32
34a824c75b7b / 68.0a1 / 20190509033505 / files
nightly linux64
34a824c75b7b / 68.0a1 / 20190509033505 / files
nightly mac
34a824c75b7b / 68.0a1 / 20190509033505 / files
nightly win32
34a824c75b7b / 68.0a1 / 20190509033505 / files
nightly win64
34a824c75b7b / 68.0a1 / 20190509033505 / 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
dom/quota/test/unit/localStorageArchive2upgrade_profile.zip
dom/quota/test/unit/localStorageArchive3upgrade_profile.zip
dom/quota/test/unit/test_localStorageArchive2upgrade.js
dom/quota/test/unit/test_localStorageArchive3upgrade.js
modules/libpref/init/all.js
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -303,17 +303,17 @@ dependencies = [
  "bits 0.1.0",
  "comedy 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "failure 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "failure_derive 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "guid_win 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "rand 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "regex 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
- "tempdir 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
+ "tempfile 3.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "bitsdownload"
 version = "0.1.0"
 dependencies = [
  "bits_client 0.1.0",
  "comedy 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1844,17 +1844,17 @@ dependencies = [
  "libz-sys 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)",
  "num_cpus 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "mozprofile"
 version = "0.5.0"
 dependencies = [
- "tempdir 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
+ "tempfile 3.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "mozrunner"
 version = "0.9.0"
 dependencies = [
  "dirs 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -2394,16 +2394,24 @@ source = "registry+https://github.com/ru
 name = "regex-syntax"
 version = "0.6.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "ucd-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
+name = "remove_dir_all"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "winapi 0.3.6 (git+https://github.com/froydnj/winapi-rs?branch=aarch64)",
+]
+
+[[package]]
 name = "rkv"
 version = "0.9.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "arrayref 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
  "bincode 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "failure 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -2891,21 +2899,25 @@ version = "0.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "failure 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "failure_derive 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde_json 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
-name = "tempdir"
-version = "0.3.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "rand 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)",
+name = "tempfile"
+version = "3.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)",
+ "rand 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "redox_syscall 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)",
+ "remove_dir_all 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.3.6 (git+https://github.com/froydnj/winapi-rs?branch=aarch64)",
 ]
 
 [[package]]
 name = "term"
 version = "0.4.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -3808,16 +3820,17 @@ dependencies = [
 "checksum rayon-core 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9d24ad214285a7729b174ed6d3bcfcb80177807f959d95fafd5bfc5c4f201ac8"
 "checksum redox_syscall 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)" = "ab105df655884ede59d45b7070c8a65002d921461ee813a024558ca16030eea0"
 "checksum redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76"
 "checksum redox_users 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "214a97e49be64fd2c86f568dd0cb2c757d2cc53de95b273b6ad0a1c908482f26"
 "checksum regex 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1731164734096285ec2a5ec7fea5248ae2f5485b3feeb0115af4fda2183b2d1b"
 "checksum regex 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3d8c9f33201f46669484bacc312b00e7541bed6aaf296dffe2bb4e0ac6b8ce2a"
 "checksum regex-syntax 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ad890a5eef7953f55427c50575c680c42841653abd2b028b68cd223d157f62db"
 "checksum regex-syntax 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8f1ac0f60d675cc6cf13a20ec076568254472551051ad5dd050364d70671bf6b"
+"checksum remove_dir_all 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3488ba1b9a2084d38645c4c08276a1752dcbf2c7130d74f1569681ad5d2799c5"
 "checksum rkv 0.9.4 (registry+https://github.com/rust-lang/crates.io-index)" = "238764bd8750927754d91e4a27155ac672ba88934a2bf698c992d55e5ae25e5b"
 "checksum ron 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "da06feaa07f69125ab9ddc769b11de29090122170b402547f64b86fe16ebc399"
 "checksum runloop 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5d79b4b604167921892e84afbbaad9d5ad74e091bf6c511d9dbfb0593f09fabd"
 "checksum rust-ini 0.10.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8a654c5bda722c699be6b0fe4c0d90de218928da5b724c3e467fc48865c37263"
 "checksum rust_cascade 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "f3fe4900d38dab1ad21a515e44687dd0711e6b0ec5b214a3b1aa8857343bcf3a"
 "checksum rustc-demangle 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "76d7ba1feafada44f2d38eed812bd2489a03c0f5abb975799251518b68848649"
 "checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
 "checksum ryu 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "fd0568787116e13c652377b6846f5931454a363a8fdf8ae50463ee40935b278b"
@@ -3850,17 +3863,17 @@ dependencies = [
 "checksum string_cache_shared 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b1884d1bc09741d466d9b14e6d37ac89d6909cbcac41dd9ae982d4d063bbedfc"
 "checksum strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bb4f380125926a99e52bc279241539c018323fab05ad6368b56f93d9369ff550"
 "checksum svg_fmt 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c666f0fed8e1e20e057af770af9077d72f3d5a33157b8537c1475dd8ffd6d32b"
 "checksum syn 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)" = "91b52877572087400e83d24b9178488541e3d535259e04ff17a63df1e5ceff59"
 "checksum syn 0.14.6 (registry+https://github.com/rust-lang/crates.io-index)" = "4e4b5274d4a0a3d2749d5c158dc64d3403e60554dc61194648787ada5212473d"
 "checksum syn 0.15.30 (registry+https://github.com/rust-lang/crates.io-index)" = "66c8865bf5a7cbb662d8b011950060b3c8743dca141b054bf7195b20d314d8e2"
 "checksum synstructure 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "73687139bf99285483c96ac0add482c3776528beac1d97d444f6e91f203a2015"
 "checksum target-lexicon 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1b0ab4982b8945c35cc1c46a83a9094c414f6828a099ce5dcaa8ee2b04642dcb"
-"checksum tempdir 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "87974a6f5c1dfb344d733055601650059a3363de2a6104819293baff662132d6"
+"checksum tempfile 3.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "47776f63b85777d984a50ce49d6b9e58826b6a3766a449fc95bc66cd5663c15b"
 "checksum term 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "fa63644f74ce96fbeb9b794f66aff2a52d601cbd5e80f4b97123e3899f4570f1"
 "checksum term_size 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2b6b55df3198cc93372e85dd2ed817f0e38ce8cc0f22eb32391bfad9c4bf209"
 "checksum termcolor 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "adc4587ead41bf016f11af03e55a624c06568b5a19db4e90fde573d805074f83"
 "checksum termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "689a3bdfaab439fd92bc87df5c4c78417d3cbe537487274e9b0b2dce76e92096"
 "checksum textwrap 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c0b59b6b4b44d867f1370ef1bd91bfb262bf07bf0ae65c202ea2fbc16153b693"
 "checksum thin-slice 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8eaa81235c7058867fa8c0e7314f33dcce9c215f535d1913822a2b3f5e289f3c"
 "checksum thin-vec 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "73fdf4b84c65a85168477b7fb6c498e0716bc9487fba24623389ea7f51708044"
 "checksum thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b"
--- a/accessible/html/HTMLFormControlAccessible.cpp
+++ b/accessible/html/HTMLFormControlAccessible.cpp
@@ -221,17 +221,17 @@ HTMLTextFieldAccessible::NativeAttribute
   // Expose type for text input elements as it gives some useful context,
   // especially for mobile.
   nsAutoString type;
   // In the case of this element being part of a binding, the binding's
   // parent's type should have precedence. For example an input[type=number]
   // has an embedded anonymous input[type=text] (along with spinner buttons).
   // In that case, we would want to take the input type from the parent
   // and not the anonymous content.
-  nsIContent* widgetElm = BindingParent();
+  nsIContent* widgetElm = BindingOrWidgetParent();
   if ((widgetElm && widgetElm->AsElement()->GetAttr(kNameSpaceID_None,
                                                     nsGkAtoms::type, type)) ||
       mContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::type,
                                      type)) {
     nsAccUtils::SetAccAttr(attributes, nsGkAtoms::textInputType, type);
     if (!ARIARoleMap() && type.EqualsLiteral("search")) {
       nsAccUtils::SetAccAttr(attributes, nsGkAtoms::xmlroles,
                              NS_LITERAL_STRING("searchbox"));
@@ -254,17 +254,17 @@ HTMLTextFieldAccessible::NativeAttribute
   return attributes.forget();
 }
 
 ENameValueFlag HTMLTextFieldAccessible::NativeName(nsString& aName) const {
   ENameValueFlag nameFlag = Accessible::NativeName(aName);
   if (!aName.IsEmpty()) return nameFlag;
 
   // If part of compound of XUL widget then grab a name from XUL widget element.
-  nsIContent* widgetElm = BindingParent();
+  nsIContent* widgetElm = BindingOrWidgetParent();
   if (widgetElm) XULElmName(mDoc, widgetElm, aName);
 
   if (!aName.IsEmpty()) return eNameOK;
 
   // text inputs and textareas might have useful placeholder text
   mContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::placeholder,
                                  aName);
   return eNameOK;
@@ -290,17 +290,17 @@ void HTMLTextFieldAccessible::Value(nsSt
 }
 
 void HTMLTextFieldAccessible::ApplyARIAState(uint64_t* aState) const {
   HyperTextAccessibleWrap::ApplyARIAState(aState);
   aria::MapToState(aria::eARIAAutoComplete, mContent->AsElement(), aState);
 
   // If part of compound of XUL widget then pick up ARIA stuff from XUL widget
   // element.
-  nsIContent* widgetElm = BindingParent();
+  nsIContent* widgetElm = BindingOrWidgetParent();
   if (widgetElm)
     aria::MapToState(aria::eARIAAutoComplete, widgetElm->AsElement(), aState);
 }
 
 uint64_t HTMLTextFieldAccessible::NativeState() const {
   uint64_t state = HyperTextAccessibleWrap::NativeState();
 
   // Text fields are always editable, even if they are also read only or
@@ -333,17 +333,17 @@ uint64_t HTMLTextFieldAccessible::Native
     return state;
   }
 
   // Expose autocomplete state if it has associated autocomplete list.
   if (mContent->AsElement()->HasAttr(kNameSpaceID_None, nsGkAtoms::list_))
     return state | states::SUPPORTS_AUTOCOMPLETION | states::HASPOPUP;
 
   // Ordinal XUL textboxes don't support autocomplete.
-  if (!BindingParent() && Preferences::GetBool("browser.formfill.enable")) {
+  if (!BindingOrWidgetParent() && Preferences::GetBool("browser.formfill.enable")) {
     // Check to see if autocompletion is allowed on this input. We don't expose
     // it for password fields even though the entire password can be remembered
     // for a page if the user asks it to be. However, the kind of autocomplete
     // we're talking here is based on what the user types, where a popup of
     // possible choices comes up.
     nsAutoString autocomplete;
     mContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::autocomplete,
                                    autocomplete);
--- a/accessible/html/HTMLFormControlAccessible.h
+++ b/accessible/html/HTMLFormControlAccessible.h
@@ -93,19 +93,28 @@ class HTMLTextFieldAccessible final : pu
 
  protected:
   virtual ~HTMLTextFieldAccessible() {}
 
   // Accessible
   virtual ENameValueFlag NativeName(nsString& aName) const override;
 
   /**
-   * Return a XUL widget element this input is part of.
+   * Return a widget element this input is part of, for example, XUL:textbox or
+   * HTML:input@type="number".
    */
-  nsIContent* BindingParent() const { return mContent->GetBindingParent(); }
+  nsIContent* BindingOrWidgetParent() const {
+    nsIContent * el = mContent->GetBindingParent();
+    if (el) {
+      return el;
+    }
+    // XUL textboxes custom elements implementation.
+    ErrorResult rv;
+    return Elm()->Closest(NS_LITERAL_STRING("textbox"), rv);
+  }
 };
 
 /**
  * Accessible for input@type="file" element.
  */
 class HTMLFileInputAccessible : public HyperTextAccessibleWrap {
  public:
   HTMLFileInputAccessible(nsIContent* aContent, DocAccessible* aDoc);
--- a/accessible/tests/browser/events/browser.ini
+++ b/accessible/tests/browser/events/browser.ini
@@ -7,9 +7,8 @@ support-files =
 
 [browser_test_docload.js]
 skip-if = e10s
 [browser_test_scrolling.js]
 [browser_test_textcaret.js]
 [browser_test_focus_browserui.js]
 [browser_test_focus_dialog.js]
 [browser_test_focus_urlbar.js]
-skip-if = true # Skipping temporarily, tracked in bug 1522440.
--- a/accessible/tests/browser/events/browser_test_focus_urlbar.js
+++ b/accessible/tests/browser/events/browser_test_focus_urlbar.js
@@ -147,19 +147,23 @@ async function runTests() {
   testStates(event.accessible, STATE_FOCUSED);
 
   info("Ensuring text box focus on backspace");
   focused = waitForEvent(EVENT_FOCUS, textBox);
   EventUtils.synthesizeKey("KEY_Backspace");
   await focused;
   testStates(textBox, STATE_FOCUSED);
 
-  info("Ensuring autocomplete focus on down arrow (4)");
+  info("Ensuring autocomplete focus on arrow down & up");
   focused = waitForEvent(EVENT_FOCUS, isEventForAutocompleteItem);
   EventUtils.synthesizeKey("KEY_ArrowDown");
+  // With the quantumbar enabled, we only get one result here, and arrow down
+  // selects a one-off search button. We arrow back up to re-select the
+  // autocomplete result.
+  EventUtils.synthesizeKey("KEY_ArrowUp");
   event = await focused;
   testStates(event.accessible, STATE_FOCUSED);
 
   info("Ensuring text box focus on text selection");
   focused = waitForEvent(EVENT_FOCUS, textBox);
   EventUtils.synthesizeKey("KEY_ArrowLeft", {shiftKey: true});
   await focused;
   testStates(textBox, STATE_FOCUSED);
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -180,18 +180,16 @@ pref("app.update.BITS.enabled", false);
 //  .. etc ..
 //
 pref("extensions.update.enabled", true);
 pref("extensions.update.url", "https://versioncheck.addons.mozilla.org/update/VersionCheck.php?reqVersion=%REQ_VERSION%&id=%ITEM_ID%&version=%ITEM_VERSION%&maxAppVersion=%ITEM_MAXAPPVERSION%&status=%ITEM_STATUS%&appID=%APP_ID%&appVersion=%APP_VERSION%&appOS=%APP_OS%&appABI=%APP_ABI%&locale=%APP_LOCALE%&currentAppVersion=%CURRENT_APP_VERSION%&updateType=%UPDATE_TYPE%&compatMode=%COMPATIBILITY_MODE%");
 pref("extensions.update.background.url", "https://versioncheck-bg.addons.mozilla.org/update/VersionCheck.php?reqVersion=%REQ_VERSION%&id=%ITEM_ID%&version=%ITEM_VERSION%&maxAppVersion=%ITEM_MAXAPPVERSION%&status=%ITEM_STATUS%&appID=%APP_ID%&appVersion=%APP_VERSION%&appOS=%APP_OS%&appABI=%APP_ABI%&locale=%APP_LOCALE%&currentAppVersion=%CURRENT_APP_VERSION%&updateType=%UPDATE_TYPE%&compatMode=%COMPATIBILITY_MODE%");
 pref("extensions.update.interval", 86400);  // Check for updates to Extensions and
                                             // Themes every day
 
-pref("extensions.webextensions.themes.icons.buttons", "back,forward,reload,stop,bookmark_star,bookmark_menu,downloads,home,app_menu,cut,copy,paste,new_window,new_private_window,save_page,print,history,full_screen,find,options,addons,developer,synced_tabs,open_file,sidebars,share_page,subscribe,text_encoding,email_link,forget,pocket");
-
 pref("lightweightThemes.getMoreURL", "https://addons.mozilla.org/%LOCALE%/firefox/themes");
 
 #if defined(MOZ_WIDEVINE_EME)
 pref("browser.eme.ui.enabled", true);
 #else
 pref("browser.eme.ui.enabled", false);
 #endif
 
@@ -1744,16 +1742,17 @@ pref("dom.mozBrowserFramesEnabled", true
 pref("extensions.pocket.api", "api.getpocket.com");
 pref("extensions.pocket.enabled", true);
 pref("extensions.pocket.oAuthConsumerKey", "40249-e88c401e1b1f2242d9e441c4");
 pref("extensions.pocket.site", "getpocket.com");
 
 pref("signon.schemeUpgrades", true);
 pref("signon.privateBrowsingCapture.enabled", true);
 pref("signon.showAutoCompleteFooter", true);
+pref("signon.management.page.enabled", false);
 
 // Enable the "Simplify Page" feature in Print Preview. This feature
 // is disabled by default in toolkit.
 pref("print.use_simplify_page", true);
 
 // Space separated list of URLS that are allowed to send objects (instead of
 // only strings) through webchannels. This list is duplicated in mobile/android/app/mobile.js
 pref("webchannel.allowObject.urlWhitelist", "https://content.cdn.mozilla.net https://support.mozilla.org https://install.mozilla.org");
--- a/browser/base/content/browser.css
+++ b/browser/base/content/browser.css
@@ -1416,10 +1416,8 @@ toolbarpaletteitem > toolbaritem {
   #sidebar-box[sidebarcommand$="-sidebar-action"] > #sidebar-header > #sidebar-switcher-target > #sidebar-icon {
     list-style-image: var(--webextension-menuitem-image-2x, inherit);
   }
 }
 
 toolbar[keyNav=true]:not([collapsed=true]):not([customizing=true]) toolbartabstop {
   -moz-user-focus: normal;
 }
-
-%include theme-vars.inc.css
deleted file mode 100644
--- a/browser/base/content/theme-vars.inc.css
+++ /dev/null
@@ -1,150 +0,0 @@
-%if 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/. */
-%endif
-
-:root[lwthemeicons~="--back-icon"] #back-button:-moz-lwtheme {
-  list-style-image: var(--back-icon) !important;
-}
-
-:root[lwthemeicons~="--forward-icon"] #forward-button:-moz-lwtheme {
-  list-style-image: var(--forward-icon) !important;
-}
-
-:root[lwthemeicons~="--reload-icon"] #reload-button:-moz-lwtheme {
-  list-style-image: var(--reload-icon) !important;
-}
-
-:root[lwthemeicons~="--stop-icon"] #stop-button:-moz-lwtheme {
-  list-style-image: var(--stop-icon) !important;
-}
-
-:root[lwthemeicons~="--bookmark_star-icon"] #star-button:-moz-lwtheme,
-:root[lwthemeicons~="--bookmark_star-icon"] #bookmarks-menu-button:-moz-lwtheme {
-  list-style-image: var(--bookmark_star-icon) !important;
-}
-
-:root[lwthemeicons~="--bookmark_menu-icon"] #bookmarks-menu-button:-moz-lwtheme {
-  list-style-image: var(--bookmark_menu-icon) !important;
-}
-
-:root[lwthemeicons~="--downloads-icon"] #downloads-button:-moz-lwtheme {
-  list-style-image: var(--downloads-icon) !important;
-}
-
-:root[lwthemeicons~="--home-icon"] #home-button:-moz-lwtheme {
-  list-style-image: var(--home-icon) !important;
-}
-
-:root[lwthemeicons~="--app_menu-icon"] #PanelUI-menu-button:-moz-lwtheme {
-  list-style-image: var(--app_menu-icon) !important;
-}
-
-:root[lwthemeicons~="--cut-icon"] #cut-button:-moz-lwtheme {
-  list-style-image: var(--cut-icon) !important;
-}
-
-:root[lwthemeicons~="--copy-icon"] #copy-button:-moz-lwtheme {
-  list-style-image: var(--copy-icon) !important;
-}
-
-:root[lwthemeicons~="--paste-icon"] #paste-button:-moz-lwtheme {
-  list-style-image: var(--paste-icon) !important;
-}
-
-:root[lwthemeicons~="--new_window-icon"] #new-window-button:-moz-lwtheme {
-  list-style-image: var(--new_window-icon) !important;
-}
-
-:root[lwthemeicons~="--new_private_window-icon"] #privatebrowsing-button:-moz-lwtheme {
-  list-style-image: var(--new_private_window-icon) !important;
-}
-
-:root[lwthemeicons~="--save_page-icon"] #save-page-button:-moz-lwtheme {
-  list-style-image: var(--save_page-icon) !important;
-}
-
-:root[lwthemeicons~="--print-icon"] #print-button:-moz-lwtheme {
-  list-style-image: var(--print-icon) !important;
-}
-
-:root[lwthemeicons~="--history-icon"] #history-panelmenu:-moz-lwtheme {
-  list-style-image: var(--history-icon) !important;
-}
-
-:root[lwthemeicons~="--full_screen-icon"] #fullscreen-button:-moz-lwtheme {
-  list-style-image: var(--full_screen-icon) !important;
-}
-
-:root[lwthemeicons~="--find-icon"] #find-button:-moz-lwtheme {
-  list-style-image: var(--find-icon) !important;
-}
-
-:root[lwthemeicons~="--options-icon"] #preferences-button:-moz-lwtheme {
-  list-style-image: var(--options-icon) !important;
-}
-
-:root[lwthemeicons~="--addons-icon"] #add-ons-button:-moz-lwtheme {
-  list-style-image: var(--addons-icon) !important;
-}
-
-:root[lwthemeicons~="--developer-icon"] #developer-button:-moz-lwtheme {
-  list-style-image: var(--developer-icon) !important;
-}
-
-:root[lwthemeicons~="--synced_tabs-icon"] #sync-button:-moz-lwtheme {
-  list-style-image: var(--synced_tabs-icon) !important;
-}
-
-:root[lwthemeicons~="--open_file-icon"] #open-file-button:-moz-lwtheme {
-  list-style-image: var(--open_file-icon) !important;
-}
-
-:root[lwthemeicons~="--sidebars-icon"] #sidebar-button:-moz-lwtheme {
-  list-style-image: var(--sidebars-icon) !important;
-}
-
-:root[lwthemeicons~="--text_encoding-icon"] #characterencoding-button:-moz-lwtheme {
-  list-style-image: var(--text_encoding-icon) !important;
-}
-
-:root[lwthemeicons~="--email_link-icon"] #email-link-button:-moz-lwtheme {
-  list-style-image: var(--email_link-icon) !important;
-}
-
-:root[lwthemeicons~="--forget-icon"] #panic-button:-moz-lwtheme {
-  list-style-image: var(--forget-icon) !important;
-}
-
-:root[lwthemeicons~="--bookmark_star-icon"] #star-button:-moz-lwtheme,
-:root[lwthemeicons~="--bookmark_menu-icon"] #bookmarks-menu-button:-moz-lwtheme,
-:root[lwthemeicons~="--back-icon"] #back-button:-moz-lwtheme,
-:root[lwthemeicons~="--forward-icon"] #forward-button:-moz-lwtheme,
-:root[lwthemeicons~="--reload-icon"] #reload-button:-moz-lwtheme,
-:root[lwthemeicons~="--stop-icon"] #stop-button:-moz-lwtheme,
-:root[lwthemeicons~="--bookmark_star-icon"] #bookmarks-menu-button:-moz-lwtheme,
-:root[lwthemeicons~="--downloads-icon"] #downloads-button:-moz-lwtheme,
-:root[lwthemeicons~="--home-icon"] #home-button:-moz-lwtheme,
-:root[lwthemeicons~="--app_menu-icon"] #PanelUI-menu-button:-moz-lwtheme,
-:root[lwthemeicons~="--cut-icon"] #cut-button:-moz-lwtheme,
-:root[lwthemeicons~="--copy-icon"] #copy-button:-moz-lwtheme,
-:root[lwthemeicons~="--paste-icon"] #paste-button:-moz-lwtheme,
-:root[lwthemeicons~="--new_window-icon"] #new-window-button:-moz-lwtheme,
-:root[lwthemeicons~="--new_private_window-icon"] #privatebrowsing-button:-moz-lwtheme,
-:root[lwthemeicons~="--save_page-icon"] #save-page-button:-moz-lwtheme,
-:root[lwthemeicons~="--print-icon"] #print-button:-moz-lwtheme,
-:root[lwthemeicons~="--history-icon"] #history-panelmenu:-moz-lwtheme,
-:root[lwthemeicons~="--full_screen-icon"] #fullscreen-button:-moz-lwtheme,
-:root[lwthemeicons~="--find-icon"] #find-button:-moz-lwtheme,
-:root[lwthemeicons~="--options-icon"] #preferences-button:-moz-lwtheme,
-:root[lwthemeicons~="--addons-icon"] #add-ons-button:-moz-lwtheme,
-:root[lwthemeicons~="--developer-icon"] #developer-button:-moz-lwtheme,
-:root[lwthemeicons~="--synced_tabs-icon"] #sync-button:-moz-lwtheme,
-:root[lwthemeicons~="--open_file-icon"] #open-file-button:-moz-lwtheme,
-:root[lwthemeicons~="--sidebars-icon"] #sidebar-button:-moz-lwtheme,
-:root[lwthemeicons~="--text_encoding-icon"] #characterencoding-button:-moz-lwtheme,
-:root[lwthemeicons~="--email_link-icon"] #email-link-button:-moz-lwtheme,
-:root[lwthemeicons~="--forget-icon"] #panic-button:-moz-lwtheme {
-  -moz-image-region: rect(0, 16px, 16px, 0) !important;
-}
--- a/browser/branding/aurora/branding.nsi
+++ b/browser/branding/aurora/branding.nsi
@@ -31,21 +31,23 @@
 # The dialog units for the bitmap's dimensions should match exactly with the
 # bitmap's width and height in pixels.
 !define APPNAME_BMP_WIDTH_DU 108u
 !define APPNAME_BMP_HEIGHT_DU 48u
 !define INTRO_BLURB_WIDTH_DU "232u"
 !define INTRO_BLURB_EDGE_DU "196u"
 !define INTRO_BLURB_LTR_TOP_DU "16u"
 !define INTRO_BLURB_RTL_TOP_DU "11u"
+!define INSTALL_FOOTER_TOP_DU "-48u"
 
 # UI Colors that can be customized for each channel
 !define FOOTER_CONTROL_TEXT_COLOR_NORMAL 0x000000
 !define FOOTER_CONTROL_TEXT_COLOR_FADED 0x999999
 !define FOOTER_BKGRD_COLOR 0xFFFFFF
+!define INSTALL_FOOTER_TEXT_COLOR 0xFFFFFF
 !define INTRO_BLURB_TEXT_COLOR 0xFFFFFF
 !define INSTALL_BLURB_TEXT_COLOR 0xFFFFFF
 !define INSTALL_PROGRESS_TEXT_COLOR_NORMAL 0xFFFFFF
 !define COMMON_TEXT_COLOR_NORMAL 0xFFFFFF
 !define COMMON_TEXT_COLOR_FADED 0xA1AAB3
 !define COMMON_BKGRD_COLOR 0x0F1B26
 
 # Enable DeveloperEdition-specific behavior
--- a/browser/branding/nightly/branding.nsi
+++ b/browser/branding/nightly/branding.nsi
@@ -30,19 +30,21 @@
 # The dialog units for the bitmap's dimensions should match exactly with the
 # bitmap's width and height in pixels.
 !define APPNAME_BMP_WIDTH_DU 159u
 !define APPNAME_BMP_HEIGHT_DU 28u
 !define INTRO_BLURB_WIDTH_DU "230u"
 !define INTRO_BLURB_EDGE_DU "198u"
 !define INTRO_BLURB_LTR_TOP_DU "16u"
 !define INTRO_BLURB_RTL_TOP_DU "11u"
+!define INSTALL_FOOTER_TOP_DU "-48u"
 
 # UI Colors that can be customized for each channel
 !define FOOTER_CONTROL_TEXT_COLOR_NORMAL 0x000000
 !define FOOTER_CONTROL_TEXT_COLOR_FADED 0x999999
 !define FOOTER_BKGRD_COLOR 0xFFFFFF
+!define INSTALL_FOOTER_TEXT_COLOR 0xFFFFFF
 !define INTRO_BLURB_TEXT_COLOR 0xFFFFFF
 !define INSTALL_BLURB_TEXT_COLOR 0xFFFFFF
 !define INSTALL_PROGRESS_TEXT_COLOR_NORMAL 0xFFFFFF
 !define COMMON_TEXT_COLOR_NORMAL 0xFFFFFF
 !define COMMON_TEXT_COLOR_FADED 0xA1AAB3
 !define COMMON_BKGRD_COLOR 0x0F1B26
index cd434b501bd66a05432ce30938e7e8d00b09a94e..2d68cb7f625c2691b415dd196bfa1991c1cd8a71
GIT binary patch
literal 18070
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
V!Ah4T2>rYNW8wdl|GFFu|6kp2Di#0$
index d73f0da6c30c0829062e327b38fc10de2da306c8..3333b4042291d14ad18d797bcd2ede66aaa82159
GIT binary patch
literal 50889
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%0D$Qgv*m?RvHDY(ke1RmBBC
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
K@2Y+5;r{??7A(C0
--- a/browser/branding/official/branding.nsi
+++ b/browser/branding/official/branding.nsi
@@ -35,19 +35,21 @@
 # The dialog units for the bitmap's dimensions should match exactly with the
 # bitmap's width and height in pixels.
 !define APPNAME_BMP_WIDTH_DU "134u"
 !define APPNAME_BMP_HEIGHT_DU "36u"
 !define INTRO_BLURB_WIDTH_DU "258u"
 !define INTRO_BLURB_EDGE_DU "170u"
 !define INTRO_BLURB_LTR_TOP_DU "20u"
 !define INTRO_BLURB_RTL_TOP_DU "12u"
+!define INSTALL_FOOTER_TOP_DU "-33u"
 
 # UI Colors that can be customized for each channel
 !define FOOTER_CONTROL_TEXT_COLOR_NORMAL 0x000000
 !define FOOTER_CONTROL_TEXT_COLOR_FADED 0x666666
 !define FOOTER_BKGRD_COLOR 0xFFFFFF
+!define INSTALL_FOOTER_TEXT_COLOR 0x000000
 !define INTRO_BLURB_TEXT_COLOR 0x666666
 !define INSTALL_BLURB_TEXT_COLOR 0x666666
 !define INSTALL_PROGRESS_TEXT_COLOR_NORMAL 0x666666
 !define COMMON_TEXT_COLOR_NORMAL 0x000000
 !define COMMON_TEXT_COLOR_FADED 0x666666
 !define COMMON_BKGRD_COLOR 0xF0F0F0
--- a/browser/branding/unofficial/branding.nsi
+++ b/browser/branding/unofficial/branding.nsi
@@ -30,19 +30,21 @@
 # The dialog units for the bitmap's dimensions should match exactly with the
 # bitmap's width and height in pixels.
 !define APPNAME_BMP_WIDTH_DU 159u
 !define APPNAME_BMP_HEIGHT_DU 50u
 !define INTRO_BLURB_WIDTH_DU "230u"
 !define INTRO_BLURB_EDGE_DU "198u"
 !define INTRO_BLURB_LTR_TOP_DU "16u"
 !define INTRO_BLURB_RTL_TOP_DU "11u"
+!define INSTALL_FOOTER_TOP_DU "-48u"
 
 # UI Colors that can be customized for each channel
 !define FOOTER_CONTROL_TEXT_COLOR_NORMAL 0x000000
 !define FOOTER_CONTROL_TEXT_COLOR_FADED 0x999999
 !define FOOTER_BKGRD_COLOR 0xFFFFFF
+!define INSTALL_FOOTER_TEXT_COLOR 0xFFFFFF
 !define INTRO_BLURB_TEXT_COLOR 0xFFFFFF
 !define INSTALL_BLURB_TEXT_COLOR 0xFFFFFF
 !define INSTALL_PROGRESS_TEXT_COLOR_NORMAL 0xFFFFFF
 !define COMMON_TEXT_COLOR_NORMAL 0xFFFFFF
 !define COMMON_TEXT_COLOR_FADED 0xA1AAB3
 !define COMMON_BKGRD_COLOR 0x0F1B26
--- a/browser/components/BrowserGlue.jsm
+++ b/browser/components/BrowserGlue.jsm
@@ -25,16 +25,33 @@ let ACTORS = {
       moduleURI: "resource:///actors/SubframeCrashChild.jsm",
     },
 
     allFrames: true,
   },
 };
 
 let LEGACY_ACTORS = {
+  AboutLogins: {
+    child: {
+      matches: ["about:logins"],
+      module: "resource:///actors/AboutLoginsChild.jsm",
+      events: {
+        "AboutLoginsDeleteLogin": {wantUntrusted: true},
+        "AboutLoginsInit": {wantUntrusted: true},
+      },
+      messages: [
+        "AboutLogins:AllLogins",
+        "AboutLogins:LoginAdded",
+        "AboutLogins:LoginModified",
+        "AboutLogins:LoginRemoved",
+      ],
+    },
+  },
+
   AboutReader: {
     child: {
       module: "resource:///actors/AboutReaderChild.jsm",
       group: "browsers",
       events: {
         "AboutReaderContentLoaded": {wantUntrusted: true},
         "DOMContentLoaded": {},
         "pageshow": {mozSystemGroup: true},
@@ -443,16 +460,17 @@ XPCOMUtils.defineLazyModuleGetters(this,
   UIState: "resource://services-sync/UIState.jsm",
   UITour: "resource:///modules/UITour.jsm",
   WebChannel: "resource://gre/modules/WebChannel.jsm",
   WindowsRegistry: "resource://gre/modules/WindowsRegistry.jsm",
 });
 
 // eslint-disable-next-line no-unused-vars
 XPCOMUtils.defineLazyModuleGetters(this, {
+  AboutLoginsParent: "resource:///modules/AboutLoginsParent.jsm",
   AsyncPrefs: "resource://gre/modules/AsyncPrefs.jsm",
   ContentClick: "resource:///modules/ContentClick.jsm",
   FormValidationHandler: "resource:///modules/FormValidationHandler.jsm",
   LoginManagerParent: "resource://gre/modules/LoginManagerParent.jsm",
   PictureInPicture: "resource://gre/modules/PictureInPicture.jsm",
   ReaderParent: "resource:///modules/ReaderParent.jsm",
   RemotePrompt: "resource:///modules/RemotePrompt.jsm",
 });
@@ -521,16 +539,18 @@ const listeners = {
     "AsyncPrefs:ResetPref": ["AsyncPrefs"],
     // PLEASE KEEP THIS LIST IN SYNC WITH THE LISTENERS ADDED IN AsyncPrefs.init
 
     "webrtc:UpdateGlobalIndicators": ["webrtcUI"],
     "webrtc:UpdatingIndicators": ["webrtcUI"],
   },
 
   mm: {
+    "AboutLogins:DeleteLogin": ["AboutLoginsParent"],
+    "AboutLogins:Subscribe": ["AboutLoginsParent"],
     "Content:Click": ["ContentClick"],
     "ContentSearch": ["ContentSearch"],
     "FormValidation:ShowPopup": ["FormValidationHandler"],
     "FormValidation:HidePopup": ["FormValidationHandler"],
     "PictureInPicture:Request": ["PictureInPicture"],
     "PictureInPicture:Close": ["PictureInPicture"],
     "PictureInPicture:Playing": ["PictureInPicture"],
     "PictureInPicture:Paused": ["PictureInPicture"],
--- a/browser/components/about/AboutRedirector.cpp
+++ b/browser/components/about/AboutRedirector.cpp
@@ -17,16 +17,17 @@
 #include "nsServiceManagerUtils.h"
 
 namespace mozilla {
 namespace browser {
 
 NS_IMPL_ISUPPORTS(AboutRedirector, nsIAboutModule)
 
 bool AboutRedirector::sNewTabPageEnabled = false;
+bool AboutRedirector::sAboutLoginsEnabled = false;
 
 static const uint32_t ACTIVITY_STREAM_FLAGS =
     nsIAboutModule::ALLOW_SCRIPT | nsIAboutModule::ENABLE_INDEXED_DB |
     nsIAboutModule::URI_MUST_LOAD_IN_CHILD |
     nsIAboutModule::URI_CAN_LOAD_IN_PRIVILEGED_CHILD |
     nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT;
 
 struct RedirEntry {
@@ -51,16 +52,20 @@ static const RedirEntry kRedirMap[] = {
     {"certerror", "chrome://browser/content/aboutNetError.xhtml",
      nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT |
          nsIAboutModule::URI_CAN_LOAD_IN_CHILD | nsIAboutModule::ALLOW_SCRIPT |
          nsIAboutModule::HIDE_FROM_ABOUTABOUT},
     {"config", "chrome://browser/content/aboutconfig/aboutconfig.html", 0},
     {"framecrashed", "chrome://browser/content/aboutFrameCrashed.html",
      nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT |
          nsIAboutModule::HIDE_FROM_ABOUTABOUT},
+    {"logins", "chrome://browser/content/aboutlogins/aboutLogins.html",
+     nsIAboutModule::ALLOW_SCRIPT | nsIAboutModule::URI_MUST_LOAD_IN_CHILD |
+         nsIAboutModule::URI_CAN_LOAD_IN_PRIVILEGED_CHILD |
+         nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT},
     {"tabcrashed", "chrome://browser/content/aboutTabCrashed.xhtml",
      nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT |
          nsIAboutModule::ALLOW_SCRIPT | nsIAboutModule::HIDE_FROM_ABOUTABOUT},
     {"policies", "chrome://browser/content/policies/aboutPolicies.xhtml",
      nsIAboutModule::ALLOW_SCRIPT},
     {"privatebrowsing", "chrome://browser/content/aboutPrivateBrowsing.xhtml",
      nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT |
          nsIAboutModule::URI_MUST_LOAD_IN_CHILD | nsIAboutModule::ALLOW_SCRIPT},
@@ -142,31 +147,42 @@ AboutRedirector::NewChannel(nsIURI* aURI
 
   static bool sNTPEnabledCacheInited = false;
   if (!sNTPEnabledCacheInited) {
     Preferences::AddBoolVarCache(&AboutRedirector::sNewTabPageEnabled,
                                  "browser.newtabpage.enabled");
     sNTPEnabledCacheInited = true;
   }
 
+  static bool sAboutLoginsCacheInited = false;
+  if (!sAboutLoginsCacheInited) {
+    Preferences::AddBoolVarCache(&AboutRedirector::sAboutLoginsEnabled,
+                                 "signon.management.page.enabled");
+    sAboutLoginsCacheInited = true;
+  }
+
   for (auto& redir : kRedirMap) {
     if (!strcmp(path.get(), redir.id)) {
       nsAutoCString url;
 
       // Let the aboutNewTabService decide where to redirect for about:home and
       // enabled about:newtab. Disabledx about:newtab page uses fallback.
       if (path.EqualsLiteral("home") ||
           (sNewTabPageEnabled && path.EqualsLiteral("newtab"))) {
         nsCOMPtr<nsIAboutNewTabService> aboutNewTabService =
             do_GetService("@mozilla.org/browser/aboutnewtab-service;1", &rv);
         NS_ENSURE_SUCCESS(rv, rv);
         rv = aboutNewTabService->GetDefaultURL(url);
         NS_ENSURE_SUCCESS(rv, rv);
       }
 
+      if (!sAboutLoginsEnabled && path.EqualsLiteral("logins")) {
+        return NS_ERROR_NOT_AVAILABLE;
+      }
+
       if (path.EqualsLiteral("welcome")) {
         nsCOMPtr<nsIAboutNewTabService> aboutNewTabService =
             do_GetService("@mozilla.org/browser/aboutnewtab-service;1", &rv);
         NS_ENSURE_SUCCESS(rv, rv);
         rv = aboutNewTabService->GetWelcomeURL(url);
         NS_ENSURE_SUCCESS(rv, rv);
       }
 
--- a/browser/components/about/AboutRedirector.h
+++ b/browser/components/about/AboutRedirector.h
@@ -19,15 +19,16 @@ class AboutRedirector : public nsIAboutM
   AboutRedirector() {}
 
   static nsresult Create(nsISupports* aOuter, REFNSIID aIID, void** aResult);
 
  protected:
   virtual ~AboutRedirector() {}
 
  private:
+  static bool sAboutLoginsEnabled;
   static bool sNewTabPageEnabled;
 };
 
 }  // namespace browser
 }  // namespace mozilla
 
 #endif  // AboutRedirector_h__
--- a/browser/components/about/components.conf
+++ b/browser/components/about/components.conf
@@ -6,16 +6,17 @@
 
 pages = [
     'blocked',
     'certerror',
     'downloads',
     'framecrashed',
     'home',
     'library',
+    'logins',
     'newinstall',
     'newtab',
     'pocket-saved',
     'pocket-signup',
     'policies',
     'preferences',
     'privatebrowsing',
     'reader',
new file mode 100644
--- /dev/null
+++ b/browser/components/aboutlogins/AboutLoginsChild.jsm
@@ -0,0 +1,60 @@
+/* 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";
+
+var EXPORTED_SYMBOLS = ["AboutLoginsChild"];
+
+const {ActorChild} = ChromeUtils.import("resource://gre/modules/ActorChild.jsm");
+const {LoginHelper} = ChromeUtils.import("resource://gre/modules/LoginHelper.jsm");
+
+class AboutLoginsChild extends ActorChild {
+  handleEvent(event) {
+    switch (event.type) {
+      case "AboutLoginsInit": {
+        this.mm.sendAsyncMessage("AboutLogins:Subscribe");
+
+        let waivedContent = Cu.waiveXrays(this.content);
+        let AboutLoginsUtils = {
+          doLoginsMatch(loginA, loginB) {
+            return LoginHelper.doLoginsMatch(loginA, loginB, {});
+          },
+        };
+        waivedContent.AboutLoginsUtils = Cu.cloneInto(AboutLoginsUtils, waivedContent, {
+          cloneFunctions: true,
+        });
+        break;
+      }
+      case "AboutLoginsDeleteLogin": {
+        this.mm.sendAsyncMessage("AboutLogins:DeleteLogin", {login: event.detail});
+        break;
+      }
+    }
+  }
+
+  receiveMessage(message) {
+    switch (message.name) {
+      case "AboutLogins:AllLogins":
+        this.sendToContent("AllLogins", message.data);
+        break;
+      case "AboutLogins:LoginAdded":
+        this.sendToContent("LoginAdded", message.data);
+        break;
+      case "AboutLogins:LoginModified":
+        this.sendToContent("LoginModified", message.data);
+        break;
+      case "AboutLogins:LoginRemoved":
+        this.sendToContent("LoginRemoved", message.data);
+        break;
+    }
+  }
+
+  sendToContent(messageType, detail) {
+    let message = Object.assign({messageType}, {value: detail});
+    let event = new this.content.CustomEvent("AboutLoginsChromeToContent", {
+      detail: Cu.cloneInto(message, this.content),
+    });
+    this.content.dispatchEvent(event);
+  }
+}
new file mode 100644
--- /dev/null
+++ b/browser/components/aboutlogins/AboutLoginsParent.jsm
@@ -0,0 +1,114 @@
+/* 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";
+
+var EXPORTED_SYMBOLS = ["AboutLoginsParent"];
+
+ChromeUtils.defineModuleGetter(this, "LoginHelper",
+                               "resource://gre/modules/LoginHelper.jsm");
+ChromeUtils.defineModuleGetter(this, "Services",
+                               "resource://gre/modules/Services.jsm");
+
+const ABOUT_LOGINS_ORIGIN = "about:logins";
+
+const isValidLogin = login => {
+  return !(login.hostname || "").startsWith("chrome://");
+};
+
+const convertSubjectToLogin = subject => {
+    subject.QueryInterface(Ci.nsILoginMetaInfo).QueryInterface(Ci.nsILoginInfo);
+    const login = LoginHelper.loginToVanillaObject(subject);
+    if (!isValidLogin(login)) {
+      return null;
+    }
+    return login;
+};
+
+var AboutLoginsParent = {
+  _subscribers: new WeakSet(),
+
+  // Listeners are added in BrowserGlue.jsm
+  receiveMessage(message) {
+    // Only respond to messages sent from about:logins.
+    if (message.target.contentPrincipal.originNoSuffix != ABOUT_LOGINS_ORIGIN) {
+      return;
+    }
+
+    switch (message.name) {
+      case "AboutLogins:DeleteLogin": {
+        let login = LoginHelper.vanillaObjectToLogin(message.data.login);
+        Services.logins.removeLogin(login);
+        break;
+      }
+      case "AboutLogins:Subscribe": {
+        if (!ChromeUtils.nondeterministicGetWeakSetKeys(this._subscribers).length) {
+          Services.obs.addObserver(this, "passwordmgr-storage-changed");
+        }
+        this._subscribers.add(message.target);
+
+        let messageManager = message.target.messageManager;
+        messageManager.sendAsyncMessage("AboutLogins:AllLogins", this.getAllLogins());
+        break;
+      }
+    }
+  },
+
+  observe(subject, topic, type) {
+    if (!ChromeUtils.nondeterministicGetWeakSetKeys(this._subscribers).length) {
+      Services.obs.removeObserver(this, "passwordmgr-storage-changed");
+      return;
+    }
+
+    switch (type) {
+      case "addLogin": {
+        const login = convertSubjectToLogin(subject);
+        if (!login) {
+          return;
+        }
+        this.messageSubscribers("AboutLogins:LoginAdded", login);
+        break;
+      }
+      case "modifyLogin": {
+        subject.QueryInterface(Ci.nsIArrayExtensions);
+        const login = convertSubjectToLogin(subject.GetElementAt(1));
+        if (!login) {
+          return;
+        }
+        this.messageSubscribers("AboutLogins:LoginModified", login);
+        break;
+      }
+      case "removeLogin": {
+        const login = convertSubjectToLogin(subject);
+        if (!login) {
+          return;
+        }
+        this.messageSubscribers("AboutLogins:LoginRemoved", login);
+      }
+      default: {
+        break;
+      }
+    }
+  },
+
+  messageSubscribers(name, details) {
+    let subscribers = ChromeUtils.nondeterministicGetWeakSetKeys(this._subscribers);
+    for (let subscriber of subscribers) {
+      if (subscriber.contentPrincipal.originNoSuffix != ABOUT_LOGINS_ORIGIN) {
+        this._subscribers.delete(subscriber);
+        continue;
+      }
+      try {
+        subscriber.messageManager.sendAsyncMessage(name, details);
+      } catch (ex) {}
+    }
+  },
+
+  getAllLogins() {
+    return Services.logins
+                   .getAllLogins()
+                   .filter(isValidLogin)
+                   .map(LoginHelper.loginToVanillaObject);
+  },
+};
new file mode 100644
--- /dev/null
+++ b/browser/components/aboutlogins/content/aboutLogins.ftl
@@ -0,0 +1,23 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+### This file is not in a locales directory to prevent it from
+### being translated as the feature is still in heavy development
+### and strings are likely to change often.
+
+### Fluent isn't translating elements in the shadow DOM so the translated strings
+### need to be applied to the composed node where they can be moved to the proper
+### descendant after translation.
+
+about-logins-page-title = Login Manager
+
+login-list =
+  .login-list-header = Logins
+
+login-item =
+  .login-item-hostname = Hostname
+  .login-item-password = Password
+  .login-item-username = Username
+  .login-item-time-created = Time Created
+  .login-item-delete = Delete
new file mode 100644
--- /dev/null
+++ b/browser/components/aboutlogins/content/aboutLogins.html
@@ -0,0 +1,60 @@
+<!-- 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/. -->
+
+<!DOCTYPE html>
+<html>
+  <head>
+    <meta charset="utf-8">
+    <meta http-equiv="Content-Security-Policy" content="default-src 'none'; object-src 'none'; script-src resource: chrome:; connect-src https:; img-src https: data: blob:; style-src 'unsafe-inline';"/>
+    <title data-l10n-id="about-logins-page-title"></title>
+    <link rel="localization" href="browser/aboutLogins.ftl">
+    <script defer="defer" src="chrome://browser/content/aboutlogins/components/login-item.js"></script>
+    <script defer="defer" src="chrome://browser/content/aboutlogins/components/login-list.js"></script>
+    <script defer="defer" src="chrome://browser/content/aboutlogins/components/login-list-item.js"></script>
+    <script defer="defer" src="chrome://browser/content/aboutlogins/aboutLogins.js"></script>
+  </head>
+  <body>
+    <login-list data-l10n-id="login-list"
+                data-l10n-attrs="login-list-header"></login-list>
+    <login-item data-l10n-id="login-item"
+                data-l10n-attrs="login-item-hostname, login-item-password, login-item-username, login-item-time-created, login-item-delete"></login-item>
+
+    <template id="login-list-template">
+      <h2></h2>
+      <pre>
+      </pre>
+    </template>
+
+    <template id="login-list-item-template">
+      <style>
+        :host(.selected) {
+          font-weight: bold;
+        }
+      </style>
+      <span class="login-list-item-hostname"></span>
+      <span class="login-list-item-username"></span>
+    </template>
+
+    <template id="login-item-template">
+      <h2 data-l10n-id="login-item-header"></h2>
+      <label>
+        <span class="hostname-label"></span>
+        <input name="hostname"/>
+      </label>
+      <label>
+        <span class="username-label"></span>
+        <input name="username"/>
+      </label>
+      <label>
+        <span class="password-label"></span>
+        <input type="password" name="password"/>
+      </label>
+      <p>
+        <span class="time-created-label"></span>
+        <span class="time-created"></span>
+      </p>
+      <button class="delete-button"></button>
+    </template>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/browser/components/aboutlogins/content/aboutLogins.js
@@ -0,0 +1,36 @@
+/* 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/. */
+
+let gElements = {};
+
+document.addEventListener("DOMContentLoaded", () => {
+  gElements.loginList = document.querySelector("login-list");
+  gElements.loginItem = document.querySelector("login-item");
+
+  document.dispatchEvent(new CustomEvent("AboutLoginsInit", {bubbles: true}));
+}, {once: true});
+
+window.addEventListener("AboutLoginsChromeToContent", event => {
+  switch (event.detail.messageType) {
+    case "AllLogins": {
+      gElements.loginList.setLogins(event.detail.value);
+      break;
+    }
+    case "LoginAdded": {
+      gElements.loginList.loginAdded(event.detail.value);
+      gElements.loginItem.loginAdded(event.detail.value);
+      break;
+    }
+    case "LoginModified": {
+      gElements.loginList.loginModified(event.detail.value);
+      gElements.loginItem.loginModified(event.detail.value);
+      break;
+    }
+    case "LoginRemoved": {
+      gElements.loginList.loginRemoved(event.detail.value);
+      gElements.loginItem.loginRemoved(event.detail.value);
+      break;
+    }
+  }
+});
new file mode 100644
--- /dev/null
+++ b/browser/components/aboutlogins/content/components/login-item.js
@@ -0,0 +1,133 @@
+/* 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/. */
+
+class LoginItem extends HTMLElement {
+  constructor() {
+    super();
+    this._login = {};
+  }
+
+  connectedCallback() {
+    if (this.children.length) {
+      this.render();
+      return;
+    }
+
+    let loginItemTemplate = document.querySelector("#login-item-template");
+    this.attachShadow({mode: "open"})
+        .appendChild(loginItemTemplate.content.cloneNode(true));
+
+    let deleteButton = this.shadowRoot.querySelector(".delete-button");
+    deleteButton.addEventListener("click", this);
+
+    window.addEventListener("AboutLoginsLoginSelected", this);
+
+    this.render();
+  }
+
+  static get observedAttributes() {
+    return [
+      "login-item-delete",
+      "login-item-hostname",
+      "login-item-password",
+      "login-item-time-created",
+      "login-item-username",
+    ];
+  }
+
+  /* Fluent doesn't handle localizing into Shadow DOM yet so strings
+     need to get reflected in to their targeted element. */
+  attributeChangedCallback(attr, oldValue, newValue) {
+    if (!this.shadowRoot) {
+      return;
+    }
+
+    switch (attr) {
+      case "login-item-delete":
+        this.shadowRoot.querySelector(".delete-button").textContent = newValue;
+        break;
+      case "login-item-hostname":
+        this.shadowRoot.querySelector(".hostname-label").textContent = newValue;
+        break;
+      case "login-item-password":
+        this.shadowRoot.querySelector(".password-label").textContent = newValue;
+        break;
+      case "login-item-time-created":
+        this.shadowRoot.querySelector(".time-created-label").textContent = newValue;
+        break;
+      case "login-item-username":
+        this.shadowRoot.querySelector(".username-label").textContent = newValue;
+        break;
+    }
+  }
+
+  render() {
+    this.shadowRoot.querySelector("input[name='hostname']").value = this._login.hostname || "";
+    this.shadowRoot.querySelector("input[name='username']").value = this._login.username || "";
+    this.shadowRoot.querySelector("input[name='password']").value = this._login.password || "";
+    this.shadowRoot.querySelector(".time-created").textContent = this._login.timeCreated || "";
+  }
+
+  handleEvent(event) {
+    switch (event.type) {
+      case "AboutLoginsLoginSelected": {
+        this.setLogin(event.detail);
+        break;
+      }
+      case "click": {
+        if (event.target.classList.contains("delete-button")) {
+          document.dispatchEvent(new CustomEvent("AboutLoginsDeleteLogin", {
+            bubbles: true,
+            detail: this._login,
+          }));
+        }
+        break;
+      }
+    }
+  }
+
+  setLogin(login) {
+    this._login = login;
+    this.render();
+  }
+
+  loginAdded(login) {
+    if (!this._login.guid) {
+      let tempLogin = {
+        username: this.shadowRoot.querySelector("input[name='username']").value,
+        formSubmitURL: "", // Use the wildcard since the user doesn't supply it.
+        hostname: this.shadowRoot.querySelector("input[name='hostname']").value,
+        password: this.shadowRoot.querySelector("input[name='password']").value,
+      };
+      // Need to use LoginHelper.doLoginsMatch() to see if the login
+      // that was added is the login that was being edited, so we
+      // can update time-created, etc.
+      if (window.AboutLoginsUtils.doLoginsMatch(tempLogin, login)) {
+        this._login = login;
+        this.render();
+      }
+    } else if (login.guid == this._login.guid) {
+      this._login = login;
+      this.render();
+    }
+  }
+
+  loginModified(login) {
+    if (login.guid != this._login.guid) {
+      return;
+    }
+
+    this._login = login;
+    this.render();
+  }
+
+  loginRemoved(login) {
+    if (login.guid != this._login.guid) {
+      return;
+    }
+    this._login = {};
+    this.render();
+  }
+}
+customElements.define("login-item", LoginItem);
new file mode 100644
--- /dev/null
+++ b/browser/components/aboutlogins/content/components/login-list-item.js
@@ -0,0 +1,58 @@
+/* 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/. */
+
+class LoginListItem extends HTMLElement {
+  constructor(login) {
+    super();
+    this._login = login;
+    this._selected = false;
+  }
+
+  connectedCallback() {
+    if (this.children.length) {
+      this.render();
+      return;
+    }
+
+    let loginListItemTemplate = document.querySelector("#login-list-item-template");
+    this.attachShadow({mode: "open"})
+        .appendChild(loginListItemTemplate.content.cloneNode(true));
+    this.render();
+
+    this.addEventListener("click", this);
+    window.addEventListener("AboutLoginsLoginSelected", this);
+  }
+
+  render() {
+    this.classList.toggle("selected", this._selected);
+    this.setAttribute("guid", this._login.guid);
+    this.shadowRoot.querySelector(".login-list-item-hostname").textContent = this._login.hostname;
+    this.shadowRoot.querySelector(".login-list-item-username").textContent = this._login.username;
+  }
+
+  handleEvent(event) {
+    switch (event.type) {
+      case "AboutLoginsLoginSelected": {
+        if (this._selected != (event.detail.guid == this._login.guid)) {
+          this._selected = event.detail.guid == this._login.guid;
+          this.render();
+        }
+        break;
+      }
+      case "click": {
+        this.dispatchEvent(new CustomEvent("AboutLoginsLoginSelected", {
+          bubbles: true,
+          composed: true,
+          detail: this._login,
+        }));
+      }
+    }
+  }
+
+  update(login) {
+    this._login = login;
+    this.render();
+  }
+}
+customElements.define("login-list-item", LoginListItem);
new file mode 100644
--- /dev/null
+++ b/browser/components/aboutlogins/content/components/login-list.js
@@ -0,0 +1,88 @@
+/* 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/. */
+
+/* globals LoginListItem */
+
+class LoginList extends HTMLElement {
+  constructor() {
+    super();
+    this._logins = [];
+  }
+
+  connectedCallback() {
+    if (this.children.length) {
+      return;
+    }
+    let loginListTemplate = document.querySelector("#login-list-template");
+    this.attachShadow({mode: "open"})
+        .appendChild(loginListTemplate.content.cloneNode(true));
+    this.render();
+  }
+
+  render() {
+    let pre = this.shadowRoot.querySelector("pre");
+    for (let login of this._logins) {
+      pre.append(new LoginListItem(login));
+    }
+  }
+
+  static get observedAttributes() {
+    return ["login-list-header"];
+  }
+
+  /* Fluent doesn't handle localizing into Shadow DOM yet so strings
+     need to get reflected in to their targeted element. */
+  attributeChangedCallback(attr, oldValue, newValue) {
+    if (!this.shadowRoot) {
+      return;
+    }
+
+    switch (attr) {
+      case "login-list-header":
+        this.shadowRoot.querySelector("h2").textContent = newValue;
+        break;
+    }
+  }
+
+  setLogins(logins) {
+    let pre = this.shadowRoot.querySelector("pre");
+    pre.textContent = "";
+    this._logins = logins;
+    this.render();
+  }
+
+  loginAdded(login) {
+    this._logins.push(login);
+    let pre = this.shadowRoot.querySelector("pre");
+    pre.append(new LoginListItem(login));
+  }
+
+  loginModified(login) {
+    for (let i = 0; i < this._logins.length; i++) {
+      if (this._logins[i].guid == login.guid) {
+        this._logins[i] = login;
+        break;
+      }
+    }
+    let pre = this.shadowRoot.querySelector("pre");
+    for (let loginListItem of pre.children) {
+      if (loginListItem.getAttribute("guid") == login.guid) {
+        loginListItem.update(login);
+        break;
+      }
+    }
+  }
+
+  loginRemoved(login) {
+    this._logins = this._logins.filter(l => l.guid != login.guid);
+    let pre = this.shadowRoot.querySelector("pre");
+    for (let loginListItem of pre.children) {
+      if (loginListItem.getAttribute("guid") == login.guid) {
+        loginListItem.remove();
+        break;
+      }
+    }
+  }
+}
+customElements.define("login-list", LoginList);
new file mode 100644
--- /dev/null
+++ b/browser/components/aboutlogins/jar.mn
@@ -0,0 +1,10 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+browser.jar:
+  content/browser/aboutlogins/components/login-item.js         (content/components/login-item.js)
+  content/browser/aboutlogins/components/login-list.js         (content/components/login-list.js)
+  content/browser/aboutlogins/components/login-list-item.js    (content/components/login-list-item.js)
+  content/browser/aboutlogins/aboutLogins.js    (content/aboutLogins.js)
+  content/browser/aboutlogins/aboutLogins.html  (content/aboutLogins.html)
new file mode 100644
--- /dev/null
+++ b/browser/components/aboutlogins/moz.build
@@ -0,0 +1,21 @@
+# -*- 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/.
+
+JAR_MANIFESTS += ['jar.mn']
+
+with Files('**'):
+    BUG_COMPONENT = ('Toolkit', 'Password Manager')
+
+EXTRA_JS_MODULES += [
+    'AboutLoginsParent.jsm',
+]
+
+FINAL_TARGET_FILES.actors += [
+    'AboutLoginsChild.jsm',
+]
+
+BROWSER_CHROME_MANIFESTS += ['tests/browser/browser.ini']
+MOCHITEST_MANIFESTS += ['tests/mochitest/mochitest.ini']
new file mode 100644
--- /dev/null
+++ b/browser/components/aboutlogins/tests/browser/browser.ini
@@ -0,0 +1,6 @@
+[DEFAULT]
+prefs =
+  signon.management.page.enabled=true
+
+[browser_deleteLogin.js]
+[browser_loginChanges.js]
new file mode 100644
--- /dev/null
+++ b/browser/components/aboutlogins/tests/browser/browser_deleteLogin.js
@@ -0,0 +1,62 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+let gLogins = [{
+    guid: "70a",
+    username: "jared",
+    password: "deraj",
+    hostname: "https://www.example.com",
+  }, {
+    guid: "70b",
+    username: "firefox",
+    password: "xoferif",
+    hostname: "https://www.example.com",
+  },
+];
+
+add_task(async function setup() {
+  await BrowserTestUtils.openNewForegroundTab({gBrowser, url: "about:logins"});
+  registerCleanupFunction(() => {
+    BrowserTestUtils.removeTab(gBrowser.selectedTab);
+  });
+});
+
+add_task(async function test_show_logins() {
+  let browser = gBrowser.selectedBrowser;
+  browser.messageManager.sendAsyncMessage("AboutLogins:AllLogins", gLogins);
+
+  await ContentTask.spawn(browser, gLogins, async (logins) => {
+    let loginList = Cu.waiveXrays(content.document.querySelector("login-list"));
+    let loginFound = await ContentTaskUtils.waitForCondition(() => {
+      return loginList._logins.length == 2 &&
+             loginList._logins[0].guid == logins[0].guid &&
+             loginList._logins[1].guid == logins[1].guid;
+    }, "Waiting for logins to be displayed");
+    ok(loginFound, "Newly added logins should be added to the page");
+  });
+});
+
+add_task(async function test_login_item() {
+  let browser = gBrowser.selectedBrowser;
+  let deleteLoginMessageReceived = false;
+  browser.messageManager.addMessageListener("AboutLogins:DeleteLogin", function onMsg() {
+    deleteLoginMessageReceived = true;
+    browser.messageManager.removeMessageListener("AboutLogins:DeleteLogin", onMsg);
+  });
+  await ContentTask.spawn(browser, gLogins, async (logins) => {
+    let loginList = content.document.querySelector("login-list");
+    let loginListItems = loginList.shadowRoot.querySelectorAll("login-list-item");
+    loginListItems[0].click();
+
+    let loginItem = Cu.waiveXrays(content.document.querySelector("login-item"));
+    let loginItemPopulated = await ContentTaskUtils.waitForCondition(() => {
+      return loginItem._login.guid == loginListItems[0].getAttribute("guid");
+    }, "Waiting for login item to get populated");
+    ok(loginItemPopulated, "The login item should get populated");
+
+    let deleteButton = loginItem.shadowRoot.querySelector(".delete-button");
+    deleteButton.click();
+  });
+  ok(deleteLoginMessageReceived,
+     "Clicking the delete button should send the AboutLogins:DeleteLogin messsage");
+});
new file mode 100644
--- /dev/null
+++ b/browser/components/aboutlogins/tests/browser/browser_loginChanges.js
@@ -0,0 +1,69 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+add_task(async function setup() {
+  await BrowserTestUtils.openNewForegroundTab({gBrowser, url: "about:logins"});
+  registerCleanupFunction(() => {
+    BrowserTestUtils.removeTab(gBrowser.selectedTab);
+  });
+});
+
+add_task(async function test_login_added() {
+  let login = {
+    guid: "70",
+    username: "jared",
+    password: "deraj",
+    hostname: "https://www.example.com",
+  };
+  let browser = gBrowser.selectedBrowser;
+  browser.messageManager.sendAsyncMessage("AboutLogins:LoginAdded", login);
+
+  await ContentTask.spawn(browser, login, async (addedLogin) => {
+    let loginList = Cu.waiveXrays(content.document.querySelector("login-list"));
+    let loginFound = await ContentTaskUtils.waitForCondition(() => {
+      return loginList._logins.length == 1 &&
+             loginList._logins[0].guid == addedLogin.guid;
+    }, "Waiting for login to be added");
+    ok(loginFound, "Newly added logins should be added to the page");
+  });
+});
+
+add_task(async function test_login_modified() {
+  let login = {
+    guid: "70",
+    username: "jared@example.com",
+    password: "deraj",
+    hostname: "https://www.example.com",
+  };
+  let browser = gBrowser.selectedBrowser;
+  browser.messageManager.sendAsyncMessage("AboutLogins:LoginModified", login);
+
+  await ContentTask.spawn(browser, login, async (modifiedLogin) => {
+    let loginList = Cu.waiveXrays(content.document.querySelector("login-list"));
+    let loginFound = await ContentTaskUtils.waitForCondition(() => {
+      return loginList._logins.length == 1 &&
+             loginList._logins[0].guid == modifiedLogin.guid &&
+             loginList._logins[0].username == modifiedLogin.username;
+    }, "Waiting for username to get updated");
+    ok(loginFound, "The login should get updated on the page");
+  });
+});
+
+add_task(async function test_login_removed() {
+  let login = {
+    guid: "70",
+    username: "jared@example.com",
+    password: "deraj",
+    hostname: "https://www.example.com",
+  };
+  let browser = gBrowser.selectedBrowser;
+  browser.messageManager.sendAsyncMessage("AboutLogins:LoginRemoved", login);
+
+  await ContentTask.spawn(browser, login, async (removedLogin) => {
+    let loginList = Cu.waiveXrays(content.document.querySelector("login-list"));
+    let loginRemoved = await ContentTaskUtils.waitForCondition(() => {
+      return loginList._logins.length == 0;
+    }, "Waiting for login to get removed");
+    ok(loginRemoved, "The login should be removed from the page");
+  });
+});
new file mode 100644
--- /dev/null
+++ b/browser/components/aboutlogins/tests/mochitest/aboutlogins_common.js
@@ -0,0 +1,26 @@
+"use strict";
+
+/* exported asyncElementRendered, importDependencies */
+
+/**
+ * A helper to await on while waiting for an asynchronous rendering of a Custom
+ * Element.
+ * @returns {Promise}
+ */
+function asyncElementRendered() {
+  return Promise.resolve();
+}
+
+/**
+ * Import the templates from the real page to avoid duplication in the tests.
+ * @param {HTMLIFrameElement} templateFrame - Frame to copy the resources from
+ * @param {HTMLElement} destinationEl - Where to append the copied resources
+ */
+function importDependencies(templateFrame, destinationEl) {
+  let templates = templateFrame.contentDocument.querySelectorAll("template");
+  isnot(templates, null, "Check some templates found");
+  for (let template of templates) {
+    let imported = document.importNode(template, true);
+    destinationEl.appendChild(imported);
+  }
+}
new file mode 100644
--- /dev/null
+++ b/browser/components/aboutlogins/tests/mochitest/mochitest.ini
@@ -0,0 +1,10 @@
+[DEFAULT]
+support-files =
+   ../../content/aboutLogins.html
+   ../../content/components/login-item.js
+   ../../content/components/login-list.js
+   ../../content/components/login-list-item.js
+   aboutlogins_common.js
+
+[test_login_item.html]
+[test_login_list.html]
new file mode 100644
--- /dev/null
+++ b/browser/components/aboutlogins/tests/mochitest/test_login_item.html
@@ -0,0 +1,109 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+Test the login-item component
+-->
+<head>
+  <meta charset="utf-8">
+  <title>Test the login-item component</title>
+  <script src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script src="login-item.js"></script>
+  <script src="aboutlogins_common.js"></script>
+
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+  <p id="display">
+  </p>
+<div id="content" style="display: none">
+  <iframe id="templateFrame" src="aboutLogins.html"
+          sandbox="allow-same-origin"></iframe>
+</div>
+<pre id="test">
+</pre>
+<script>
+/** Test the login-item component **/
+
+let gLoginItem;
+const TEST_LOGIN_1 = {
+  guid: "123456789",
+  hostname: "https://example.com",
+  username: "user1",
+  password: "pass1",
+  timeCreated: "1000",
+};
+
+add_task(async function setup() {
+  let templateFrame = document.getElementById("templateFrame");
+  let displayEl = document.getElementById("display");
+  importDependencies(templateFrame, displayEl);
+
+  gLoginItem = document.createElement("login-item");
+  displayEl.appendChild(gLoginItem);
+});
+
+add_task(async function test_empty_item() {
+  ok(gLoginItem, "loginItem exists");
+  is(gLoginItem.shadowRoot.querySelector("input[name='hostname']").value, "", "hostname should be blank");
+  is(gLoginItem.shadowRoot.querySelector("input[name='username']").value, "", "username should be blank");
+  is(gLoginItem.shadowRoot.querySelector("input[name='password']").value, "", "password should be blank");
+  is(gLoginItem.shadowRoot.querySelector(".time-created").textContent, "", "time-created should be blank");
+});
+
+add_task(async function test_set_login() {
+  gLoginItem.setLogin(TEST_LOGIN_1);
+  await asyncElementRendered();
+
+  is(gLoginItem.shadowRoot.querySelector("input[name='hostname']").value, TEST_LOGIN_1.hostname, "hostname should be populated");
+  is(gLoginItem.shadowRoot.querySelector("input[name='username']").value, TEST_LOGIN_1.username, "username should be populated");
+  is(gLoginItem.shadowRoot.querySelector("input[name='password']").value, TEST_LOGIN_1.password, "password should be populated");
+  is(gLoginItem.shadowRoot.querySelector(".time-created").textContent, TEST_LOGIN_1.timeCreated, "time-created should be populated");
+});
+
+add_task(async function test_different_login_modified() {
+  let otherLogin = Object.assign({}, TEST_LOGIN_1, {username: "fakeuser", guid: "fakeguid"});
+  gLoginItem.loginModified(otherLogin);
+  await asyncElementRendered();
+
+  is(gLoginItem.shadowRoot.querySelector("input[name='hostname']").value, TEST_LOGIN_1.hostname, "hostname should be unchanged");
+  is(gLoginItem.shadowRoot.querySelector("input[name='username']").value, TEST_LOGIN_1.username, "username should be unchanged");
+  is(gLoginItem.shadowRoot.querySelector("input[name='password']").value, TEST_LOGIN_1.password, "password should be unchanged");
+  is(gLoginItem.shadowRoot.querySelector(".time-created").textContent, TEST_LOGIN_1.timeCreated, "time-created should be unchanged");
+});
+
+add_task(async function test_different_login_removed() {
+  let otherLogin = Object.assign({}, TEST_LOGIN_1, {username: "fakeuser", guid: "fakeguid"});
+  gLoginItem.loginRemoved(otherLogin);
+  await asyncElementRendered();
+
+  is(gLoginItem.shadowRoot.querySelector("input[name='hostname']").value, TEST_LOGIN_1.hostname, "hostname should be unchanged");
+  is(gLoginItem.shadowRoot.querySelector("input[name='username']").value, TEST_LOGIN_1.username, "username should be unchanged");
+  is(gLoginItem.shadowRoot.querySelector("input[name='password']").value, TEST_LOGIN_1.password, "password should be unchanged");
+  is(gLoginItem.shadowRoot.querySelector(".time-created").textContent, TEST_LOGIN_1.timeCreated, "time-created should be unchanged");
+});
+
+add_task(async function test_login_modified() {
+  let modifiedLogin = Object.assign({}, TEST_LOGIN_1, {username: "updateduser"});
+  gLoginItem.loginModified(modifiedLogin);
+  await asyncElementRendered();
+
+  is(gLoginItem.shadowRoot.querySelector("input[name='hostname']").value, modifiedLogin.hostname, "hostname should be updated");
+  is(gLoginItem.shadowRoot.querySelector("input[name='username']").value, modifiedLogin.username, "username should be updated");
+  is(gLoginItem.shadowRoot.querySelector("input[name='password']").value, modifiedLogin.password, "password should be updated");
+  is(gLoginItem.shadowRoot.querySelector(".time-created").textContent, modifiedLogin.timeCreated, "time-created should be updated");
+});
+
+add_task(async function test_login_removed() {
+  gLoginItem.loginRemoved(TEST_LOGIN_1);
+  await asyncElementRendered();
+
+  is(gLoginItem.shadowRoot.querySelector("input[name='hostname']").value, "", "hostname should be cleared");
+  is(gLoginItem.shadowRoot.querySelector("input[name='username']").value, "", "username should be cleared");
+  is(gLoginItem.shadowRoot.querySelector("input[name='password']").value, "", "password should be cleared");
+  is(gLoginItem.shadowRoot.querySelector(".time-created").textContent, "", "time-created should be cleared");
+});
+
+</script>
+
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/browser/components/aboutlogins/tests/mochitest/test_login_list.html
@@ -0,0 +1,109 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+Test the login-list component
+-->
+<head>
+  <meta charset="utf-8">
+  <title>Test the login-list component</title>
+  <script src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script src="login-list-item.js"></script>
+  <script src="login-list.js"></script>
+  <script src="aboutlogins_common.js"></script>
+
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+  <p id="display">
+  </p>
+<div id="content" style="display: none">
+  <iframe id="templateFrame" src="aboutLogins.html"
+          sandbox="allow-same-origin"></iframe>
+</div>
+<pre id="test">
+</pre>
+<script>
+/** Test the login-list component **/
+
+let gLoginList;
+const TEST_LOGIN_1 = {
+  guid: "123456789",
+  hostname: "https://example.com",
+  username: "user1",
+  password: "pass1",
+};
+const TEST_LOGIN_2 = {
+  guid: "987654321",
+  hostname: "https://example.com",
+  username: "user2",
+  password: "pass2",
+};
+
+add_task(async function setup() {
+  let templateFrame = document.getElementById("templateFrame");
+  let displayEl = document.getElementById("display");
+  importDependencies(templateFrame, displayEl);
+
+  gLoginList = document.createElement("login-list");
+  displayEl.appendChild(gLoginList);
+});
+
+add_task(async function test_empty_list() {
+  ok(gLoginList, "loginList exists");
+  is(gLoginList.textContent, "", "Initially empty");
+});
+
+add_task(async function test_populated_list() {
+  gLoginList.setLogins([TEST_LOGIN_1, TEST_LOGIN_2]);
+  await asyncElementRendered();
+  let loginListItems = gLoginList.shadowRoot.querySelectorAll("login-list-item");
+  is(loginListItems.length, 2, "Both logins should be displayed");
+  is(loginListItems[0].getAttribute("guid"), TEST_LOGIN_1.guid, "login-list-item should have correct guid attribute");
+  is(loginListItems[0].shadowRoot.querySelector(".login-list-item-hostname").textContent, TEST_LOGIN_1.hostname,
+     "login-list-item hostname should match");
+  is(loginListItems[0].shadowRoot.querySelector(".login-list-item-username").textContent, TEST_LOGIN_1.username,
+     "login-list-item username should match");
+});
+
+add_task(async function test_login_modified() {
+  let modifiedLogin = Object.assign(TEST_LOGIN_1, {username: "user11"});
+  gLoginList.loginModified(modifiedLogin);
+  await asyncElementRendered();
+  let loginListItems = gLoginList.shadowRoot.querySelectorAll("login-list-item");
+  is(loginListItems.length, 2, "Both logins should be displayed");
+  is(loginListItems[0].getAttribute("guid"), TEST_LOGIN_1.guid, "login-list-item should have correct guid attribute");
+  is(loginListItems[0].shadowRoot.querySelector(".login-list-item-hostname").textContent, TEST_LOGIN_1.hostname,
+     "login-list-item hostname should match");
+  is(loginListItems[0].shadowRoot.querySelector(".login-list-item-username").textContent, modifiedLogin.username,
+     "login-list-item username should have been updated");
+  is(loginListItems[1].shadowRoot.querySelector(".login-list-item-username").textContent, TEST_LOGIN_2.username,
+     "login-list-item2 username should remain unchanged");
+});
+
+add_task(async function test_login_added() {
+  let newLogin = Object.assign({}, TEST_LOGIN_1, {username: "user22", guid: "111222"});
+  gLoginList.loginAdded(newLogin);
+  await asyncElementRendered();
+  let loginListItems = gLoginList.shadowRoot.querySelectorAll("login-list-item");
+  is(loginListItems.length, 3, "New login should be added to the list");
+  is(loginListItems[0].getAttribute("guid"), TEST_LOGIN_1.guid, "login-list-item1 should have correct guid attribute");
+  is(loginListItems[1].getAttribute("guid"), TEST_LOGIN_2.guid, "login-list-item2 should have correct guid attribute");
+  is(loginListItems[2].getAttribute("guid"), newLogin.guid, "login-list-item3 should have correct guid attribute");
+  is(loginListItems[2].shadowRoot.querySelector(".login-list-item-hostname").textContent, newLogin.hostname,
+     "login-list-item hostname should match");
+  is(loginListItems[2].shadowRoot.querySelector(".login-list-item-username").textContent, newLogin.username,
+     "login-list-item username should have been updated");
+});
+
+add_task(async function test_login_removed() {
+  gLoginList.loginRemoved({guid: "111222"});
+  await asyncElementRendered();
+  let loginListItems = gLoginList.shadowRoot.querySelectorAll("login-list-item");
+  is(loginListItems.length, 2, "New login should be removed from the list");
+  is(loginListItems[0].getAttribute("guid"), TEST_LOGIN_1.guid, "login-list-item1 should have correct guid attribute");
+  is(loginListItems[1].getAttribute("guid"), TEST_LOGIN_2.guid, "login-list-item2 should have correct guid attribute");
+});
+</script>
+
+</body>
+</html>
--- a/browser/components/contextualidentity/test/browser/browser_aboutURLs.js
+++ b/browser/components/contextualidentity/test/browser/browser_aboutURLs.js
@@ -1,15 +1,19 @@
 "use strict";
 
 // For some about: URLs, they will take more time to load and cause timeout.
 // See Bug 1270998.
 requestLongerTimeout(2);
 
 add_task(async function() {
+  await SpecialPowers.pushPrefEnv({"set": [
+    ["signon.management.page.enabled", true],
+  ]});
+
   let aboutURLs = [];
 
   // List of about: URLs that may cause problem, so we skip them in this test.
   let skipURLs = [
     // about:credits will initiate network request.
     "credits",
     // about:telemetry will fetch Telemetry asynchronously and takes longer,
     // so we skip this for now.
--- a/browser/components/extensions/ext-browser.json
+++ b/browser/components/extensions/ext-browser.json
@@ -19,16 +19,24 @@
   "browsingData": {
     "url": "chrome://browser/content/parent/ext-browsingData.js",
     "schema": "chrome://browser/content/schemas/browsing_data.json",
     "scopes": ["addon_parent"],
     "paths": [
       ["browsingData"]
     ]
   },
+  "captivePortal": {
+    "url": "chrome://extensions/content/parent/ext-captivePortal.js",
+    "schema": "chrome://extensions/content/schemas/captive_portal.json",
+    "scopes": ["addon_parent"],
+    "paths": [
+      ["captivePortal"]
+    ]
+  },
   "chrome_settings_overrides": {
     "url": "chrome://browser/content/parent/ext-chrome-settings-overrides.js",
     "scopes": [],
     "events": ["update", "uninstall"],
     "schema": "chrome://browser/content/schemas/chrome_settings_overrides.json",
     "manifest": ["chrome_settings_overrides"]
   },
   "commands": {
--- a/browser/components/extensions/test/browser/browser-common.ini
+++ b/browser/components/extensions/test/browser/browser-common.ini
@@ -237,17 +237,16 @@ skip-if = os == 'mac' # Save as PDF not 
 [browser_ext_tabs_sendMessage.js]
 [browser_ext_tabs_sharingState.js]
 [browser_ext_tabs_successors.js]
 [browser_ext_tabs_cookieStoreId.js]
 [browser_ext_tabs_update.js]
 [browser_ext_tabs_update_highlighted.js]
 [browser_ext_tabs_update_url.js]
 [browser_ext_tabs_zoom.js]
-[browser_ext_themes_icons.js]
 [browser_ext_themes_validation.js]
 [browser_ext_url_overrides_newtab.js]
 [browser_ext_user_events.js]
 [browser_ext_webRequest.js]
 [browser_ext_webNavigation_frameId0.js]
 [browser_ext_webNavigation_getFrames.js]
 [browser_ext_webNavigation_onCreatedNavigationTarget.js]
 [browser_ext_webNavigation_onCreatedNavigationTarget_contextmenu.js]
deleted file mode 100644
--- a/browser/components/extensions/test/browser/browser_ext_themes_icons.js
+++ /dev/null
@@ -1,244 +0,0 @@
-"use strict";
-
-const ENCODED_IMAGE_DATA = "PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA2NCA2NCIgZW5hYmxlLWJhY2tncm91bmQ9Im5ldyAwIDAgNjQgNjQiPjxwYXRoIGQ9Im01NS45IDMyLjFsLTIyLjctMTQuOWMwIDAgMTIuOS0xNy40IDE5LjQtMTQuOSAzLjEgMS4xIDUuNCAyNS4xIDMuMyAyOS44IiBmaWxsPSIjM2U0MzQ3Ii8+PHBhdGggZD0ibTU0LjkgMzMuOWwtOS00LjFjMCAwLTUuMy0xNCA2LjEtMjQuMSAyLjQgMiA1LjEgMjUgMi45IDI4LjIiIGZpbGw9IiNmZmYiLz48cGF0aCBkPSJtOC4xIDMyLjFsMjIuNi0xNC45YzAgMC0xMi45LTE3LjQtMTkuNC0xNC45LTMgMS4xLTUuMyAyNS4xLTMuMiAyOS44IiBmaWxsPSIjM2U0MzQ3Ii8+PHBhdGggZD0ibTkuMSAzMy45bDktNC4xYzAgMCA1LjMtMTQtNi4xLTI0LjEtMi40IDItNS4xIDI1LTIuOSAyOC4yIiBmaWxsPSIjZmZmIi8+PHBhdGggZD0iTTMyLDEzQzE4LjksMTMsMiwzMy42LDIsNDUuNEMyMC41LDQ1LjQsMTkuNyw2MiwzMiw2MnMxMS41LTE2LjYsMzAtMTYuNkM2MiwzMy42LDQ1LjEsMTMsMzIsMTN6IiBmaWxsPSIjZmY4NzM2Ii8+PGcgZmlsbD0iI2ZmZiI+PHBhdGggZD0iTTMyLDU2LjJjMCw1LjEsOS42LDQuMiw5LjUtMi45YzYuNy05LjQsMTkuOS04LjcsMTkuOS04LjdDMzkuNiwzMi40LDMyLDU2LjIsMzIsNTYuMnoiLz48cGF0aCBkPSJNMzIsNTYuMmMwLDUuMS05LjYsNC4yLTkuNS0yLjlDMTUuOCw0NCwyLjYsNDQuNywyLjYsNDQuN0MyNC40LDMyLjQsMzIsNTYuMiwzMiw1Ni4yeiIvPjwvZz48ZyBmaWxsPSIjZmY4NzM2Ij48cGF0aCBkPSJtNTMuNCAxOC41Yy00IC43LTQuOSA2LjMtNC45IDYuM2w2IDUuM2MtMi4zLTUuOS0xLjEtMTEuNi0xLjEtMTEuNiIvPjxwYXRoIGQ9Im01MS4xIDEzLjVjLTQuNCAzLjktNS4xIDguNy01LjEgOC43bDYgNS4zYy0yLjQtNS44LS45LTE0LS45LTE0Ii8+PHBhdGggZD0ibTEwLjYgMTguNWM0IC43IDQuOSA2LjMgNC45IDYuM2wtNiA1LjNjMi4zLTUuOSAxLjEtMTEuNiAxLjEtMTEuNiIvPjxwYXRoIGQ9Im0xMi45IDEzLjVjNC40IDMuOSA1LjEgOC43IDUuMSA4LjdsLTYgNS4zYzIuNC01LjguOS0xNCAuOS0xNCIvPjwvZz48cGF0aCBkPSJtNTIuOCAzMS4xYy01LjctMS44LTEwLjktMy40LTEzLjguOS0yLjQgMy43LjcgOS40LjcgOS40IDExLjIgMS4yIDEzLjEtMTAuMyAxMy4xLTEwLjMiIGZpbGw9IiMzZTQzNDciLz48ZWxsaXBzZSBjeD0iNDMiIGN5PSIzNi4zIiByeD0iNC4yIiByeT0iNC4xIiBmaWxsPSIjZDVmZjgzIi8+PGcgZmlsbD0iIzNlNDM0NyI+PGVsbGlwc2UgY3g9IjQzIiBjeT0iMzYuMyIgcng9IjIuNyIgcnk9IjIuNyIvPjxwYXRoIGQ9Im0xMS4yIDMxLjFjNS43LTEuOCAxMC45LTMuNCAxMy43LjkgMi40IDMuNy0uNyA5LjQtLjcgOS40LTExLjEgMS4yLTEzLTEwLjMtMTMtMTAuMyIvPjwvZz48ZWxsaXBzZSBjeD0iMjEiIGN5PSIzNi4zIiByeD0iNC4yIiByeT0iNC4xIiBmaWxsPSIjZDVmZjgzIi8+PGcgZmlsbD0iIzNlNDM0NyI+PGVsbGlwc2UgY3g9IjIxIiBjeT0iMzYuMyIgcng9IjIuNyIgcnk9IjIuNyIvPjxwYXRoIGQ9Im00MS4yIDQ3LjljLS43LTIuMy0xLjgtNC40LTMtNi41IDEuMSAyLjEgMiA0LjMgMi41IDYuNi41IDIuMy43IDQuNyAwIDYuOC0uNCAxLTEgMi0xLjggMi42LS44LjYtMS44IDEtMi43IDEtLjkgMC0xLjktLjMtMi41LTEtLjYtLjctLjktMS42LS44LTIuNmwtLjkuMmgtLjljMCAxLS4yIDEuOS0uOCAyLjYtLjYuNy0xLjUgMS0yLjUgMS0uOSAwLTEuOS0uNC0yLjctMS0uOC0uNi0xLjQtMS42LTEuOC0yLjYtLjgtMi4xLS42LTQuNiAwLTYuOC41LTIuMyAxLjUtNC41IDIuNS02LjYtMS4yIDItMi4zIDQuMS0zIDYuNS0uNyAyLjMtMS4xIDQuOC0uNCA3LjMuMyAxLjIgMSAyLjQgMS45IDMuMy45LjkgMi4xIDEuNCAzLjQgMS41IDEuMi4xIDIuNi0uMiAzLjctMS4yLjMtLjIuNS0uNS43LS44LjIuMy40LjYuNy44IDEgMSAyLjQgMS4zIDMuNyAxLjIgMS4zLS4xIDIuNC0uNyAzLjQtMS41LjktLjkgMS42LTIgMS45LTMuMy41LTIuNi4xLTUuMi0uNi03LjUiLz48cGF0aCBkPSJtMzcuNiA1MC4zYy0xLjEtMS4xLTQuNS0xLjItNS42LTEuMi0xIDAtNC41LjEtNS42IDEuMi0uOC44LS4yIDIuOCAxLjkgNC41IDEuMyAxLjEgMi42IDEuNCAzLjYgMS40IDEgMCAyLjMtLjMgMy42LTEuNCAyLjMtMS43IDIuOS0zLjcgMi4xLTQuNSIvPjwvZz48L3N2Zz4=";
-
-/**
- * Verifies that the button uses the expected icon.
- *
- * @param {string} selector The CSS selector used to find the button
- *   within the DOM.
- * @param {boolean} shouldHaveCustomStyling True if the button should
- *   have custom styling, False otherwise.
- * @param {string} message The message that is printed to the console
- *   by the verifyFn.
- */
-function verifyButtonProperties(selector, shouldHaveCustomStyling, message) {
-  try {
-    let element = document.querySelector(selector);
-    let listStyleImage = getComputedStyle(element).listStyleImage;
-    info(`listStyleImage for fox.svg is ${listStyleImage}`);
-    is(listStyleImage.includes("moz-extension:"), shouldHaveCustomStyling, message);
-  } catch (ex) {
-    ok(false, `Unable to verify ${selector}: ${ex}`);
-  }
-}
-
-/**
- * Verifies that the button uses default styling.
- *
- * @param {string} selector The CSS selector used to find the button
- *   within the DOM.
- * @param {string} message The message that is printed to the console
- *   by the verifyFn.
- */
-function verifyButtonWithoutCustomStyling(selector, message) {
-  verifyButtonProperties(selector, false, message);
-}
-
-/**
- * Verifies that the button uses non-default styling.
- *
- * @param {string} selector The CSS selector used to find the button
- *   within the DOM.
- * @param {string} message The message that is printed to the console
- *   by the verifyFn.
- */
-function verifyButtonWithCustomStyling(selector, message) {
-  verifyButtonProperties(selector, true, message);
-}
-
-/**
- * Loops through all of the buttons to confirm that they are styled
- * as expected (either with or without custom styling).
- *
- * @param {object} icons Array of an array that specifies which buttons should
- *   have custom icons.
- * @param {object} iconInfo An array of arrays that maps API names to
- *   CSS selectors.
- * @param {string} area The name of the area that the button resides in.
- */
-function checkButtons(icons, iconInfo, area) {
-  for (let button of iconInfo) {
-    let iconInfo = icons.find(arr => arr[0] == button[0]);
-    if (iconInfo[1]) {
-      verifyButtonWithCustomStyling(
-        button[1],
-        `The ${button[1]} should have its icon customized in the ${area}`);
-    } else {
-      verifyButtonWithoutCustomStyling(
-        button[1],
-        `The ${button[1]} should not have its icon customized in the ${area}`);
-    }
-  }
-}
-
-async function runTestWithIcons(icons) {
-  const FRAME_COLOR = [71, 105, 91];
-  const TAB_BACKGROUND_TEXT_COLOR = [207, 221, 192, .9];
-  let manifest = {
-    "theme": {
-      "images": {
-        "theme_frame": "fox.svg",
-      },
-      "colors": {
-        "frame": FRAME_COLOR,
-        "tab_background_text": TAB_BACKGROUND_TEXT_COLOR,
-      },
-      "icons": {},
-    },
-  };
-  let files = {
-    "fox.svg": imageBufferFromDataURI(ENCODED_IMAGE_DATA),
-  };
-
-  // Each item in this array has the following setup:
-  // At position 0: The name that is used in the theme manifest.
-  // At position 1: The CSS selector for the button in the DOM.
-  // At position 2: The CustomizableUI name for the widget, only defined
-  //                if customizable.
-  const ICON_INFO = [
-    ["back", "#back-button"],
-    ["forward", "#forward-button"],
-    ["reload", "#reload-button"],
-    ["stop", "#stop-button"],
-    ["downloads", "#downloads-button", "downloads-button"],
-    ["home", "#home-button", "home-button"],
-    ["app_menu", "#PanelUI-menu-button"],
-    ["cut", "#cut-button", "edit-controls"],
-    ["copy", "#copy-button"],
-    ["paste", "#paste-button"],
-    ["new_window", "#new-window-button", "new-window-button"],
-    ["new_private_window", "#privatebrowsing-button", "privatebrowsing-button"],
-    ["save_page", "#save-page-button", "save-page-button"],
-    ["print", "#print-button", "print-button"],
-    ["history", "#history-panelmenu", "history-panelmenu"],
-    ["full_screen", "#fullscreen-button", "fullscreen-button"],
-    ["find", "#find-button", "find-button"],
-    ["options", "#preferences-button", "preferences-button"],
-    ["addons", "#add-ons-button", "add-ons-button"],
-    ["developer", "#developer-button", "developer-button"],
-    ["synced_tabs", "#sync-button", "sync-button"],
-    ["open_file", "#open-file-button", "open-file-button"],
-    ["sidebars", "#sidebar-button", "sidebar-button"],
-    ["text_encoding", "#characterencoding-button", "characterencoding-button"],
-    ["email_link", "#email-link-button", "email-link-button"],
-    ["forget", "#panic-button", "panic-button"],
-  ];
-  // We add these at the beginning because adding them at the end can end up
-  // putting them in the overflow panel, where they aren't displayed the same way.
-  ICON_INFO.unshift(["bookmark_star", "#star-button"]);
-  ICON_INFO.unshift(["bookmark_menu", "#bookmarks-menu-button", "bookmarks-menu-button"]);
-
-  window.maximize();
-
-  for (let button of ICON_INFO) {
-    if (button[2]) {
-      CustomizableUI.addWidgetToArea(button[2], CustomizableUI.AREA_NAVBAR);
-    }
-
-    verifyButtonWithoutCustomStyling(
-      button[1],
-      `The ${button[1]} should not have its icon customized when the test starts`);
-
-    let iconInfo = icons.find(arr => arr[0] == button[0]);
-    manifest.theme.icons[button[0]] = iconInfo[1];
-  }
-
-  let extension = ExtensionTestUtils.loadExtension({manifest, files});
-
-  await extension.startup();
-
-  checkButtons(icons, ICON_INFO, "toolbar");
-
-  await extension.unload();
-
-  for (let button of ICON_INFO) {
-    verifyButtonWithoutCustomStyling(
-      button[1],
-      `The ${button[1]} should not have its icon customized when the theme is unloaded`);
-  }
-}
-
-add_task(async function setup() {
-  await SpecialPowers.pushPrefEnv({
-    set: [["extensions.webextensions.themes.icons.enabled", true]],
-  });
-});
-
-add_task(async function test_all_icons() {
-  let icons = [
-    ["back", "fox.svg"],
-    ["forward", "fox.svg"],
-    ["reload", "fox.svg"],
-    ["stop", "fox.svg"],
-    ["bookmark_star", "fox.svg"],
-    ["bookmark_menu", "fox.svg"],
-    ["downloads", "fox.svg"],
-    ["home", "fox.svg"],
-    ["app_menu", "fox.svg"],
-    ["cut", "fox.svg"],
-    ["copy", "fox.svg"],
-    ["paste", "fox.svg"],
-    ["new_window", "fox.svg"],
-    ["new_private_window", "fox.svg"],
-    ["save_page", "fox.svg"],
-    ["print", "fox.svg"],
-    ["history", "fox.svg"],
-    ["full_screen", "fox.svg"],
-    ["find", "fox.svg"],
-    ["options", "fox.svg"],
-    ["addons", "fox.svg"],
-    ["developer", "fox.svg"],
-    ["synced_tabs", "fox.svg"],
-    ["open_file", "fox.svg"],
-    ["sidebars", "fox.svg"],
-    ["text_encoding", "fox.svg"],
-    ["email_link", "fox.svg"],
-    ["forget", "fox.svg"],
-  ];
-  await runTestWithIcons(icons);
-});
-
-add_task(async function teardown() {
-  CustomizableUI.reset();
-  window.restore();
-});
-
-add_task(async function test_some_icons() {
-  let icons = [
-    ["back", ""],
-    ["forward", ""],
-    ["reload", "fox.svg"],
-    ["stop", ""],
-    ["bookmark_star", ""],
-    ["bookmark_menu", ""],
-    ["downloads", ""],
-    ["home", "fox.svg"],
-    ["app_menu", "fox.svg"],
-    ["cut", ""],
-    ["copy", ""],
-    ["paste", ""],
-    ["new_window", ""],
-    ["new_private_window", ""],
-    ["save_page", ""],
-    ["print", ""],
-    ["history", ""],
-    ["full_screen", ""],
-    ["find", ""],
-    ["options", ""],
-    ["addons", ""],
-    ["developer", ""],
-    ["synced_tabs", ""],
-    ["open_file", ""],
-    ["sidebars", ""],
-    ["text_encoding", ""],
-    ["email_link", ""],
-    ["forget", ""],
-  ];
-  await runTestWithIcons(icons);
-});
-
-add_task(async function teardown() {
-  CustomizableUI.reset();
-  window.restore();
-});
--- a/browser/components/moz.build
+++ b/browser/components/moz.build
@@ -26,16 +26,17 @@ with Files("safebrowsing/**"):
     BUG_COMPONENT = ("Toolkit", "Safe Browsing")
 
 with Files('controlcenter/**'):
     BUG_COMPONENT = ('Firefox', 'General')
 
 
 DIRS += [
     'about',
+    'aboutlogins',
     'attribution',
     'contextualidentity',
     'customizableui',
     'dirprovider',
     'downloads',
     'enterprisepolicies',
     'extensions',
     'library',
--- a/browser/components/newtab/lib/BookmarkPanelHub.jsm
+++ b/browser/components/newtab/lib/BookmarkPanelHub.jsm
@@ -4,16 +4,18 @@
 "use strict";
 
 ChromeUtils.defineModuleGetter(this, "DOMLocalization",
   "resource://gre/modules/DOMLocalization.jsm");
 ChromeUtils.defineModuleGetter(this, "FxAccounts",
   "resource://gre/modules/FxAccounts.jsm");
 ChromeUtils.defineModuleGetter(this, "Services",
   "resource://gre/modules/Services.jsm");
+ChromeUtils.defineModuleGetter(this, "PrivateBrowsingUtils",
+  "resource://gre/modules/PrivateBrowsingUtils.jsm");
 
 class _BookmarkPanelHub {
   constructor() {
     this._id = "BookmarkPanelHub";
     this._trigger = {id: "bookmark-panel"};
     this._handleMessageRequest = null;
     this._addImpression = null;
     this._dispatch = null;
@@ -57,17 +59,17 @@ class _BookmarkPanelHub {
    * to ASRouter. Caches only 1 request, unique identifier is `request.url`.
    * Caching ensures we don't duplicate requests and telemetry pings.
    * Return value is important for the caller to know if a message will be
    * shown.
    *
    * @returns {obj|null} response object or null if no messages matched
    */
   async messageRequest(target, win) {
-    if (this._response && this._response.url === target.url && this._response.content) {
+    if (this._response && this._response.win === win && this._response.url === target.url && this._response.content) {
       this.showMessage(this._response.content, target, win);
       return true;
     }
 
     const response = await this._handleMessageRequest(this._trigger);
 
     return this.onResponse(response, target, win);
   }
@@ -83,17 +85,17 @@ class _BookmarkPanelHub {
       target,
       win,
       url: target.url,
     };
 
     if (response && response.content) {
       this.showMessage(response.content, target, win);
       this.sendImpression();
-      this.sendUserEventTelemetry("IMPRESSION");
+      this.sendUserEventTelemetry("IMPRESSION", win);
     } else {
       this.hideMessage(target);
     }
 
     target.infoButton.disabled = !response;
 
     return !!response;
   }
@@ -112,26 +114,26 @@ class _BookmarkPanelHub {
       recommendation.addEventListener("click", async e => {
         target.hidePopup();
         const url = await FxAccounts.config.promiseEmailFirstURI("bookmark");
         win.ownerGlobal.openLinkIn(url, "tabshifted", {
           private: false,
           triggeringPrincipal: Services.scriptSecurityManager.createNullPrincipal({}),
           csp: null,
         });
-        this.sendUserEventTelemetry("CLICK");
+        this.sendUserEventTelemetry("CLICK", win);
       });
       recommendation.style.color = message.color;
       recommendation.style.background = `-moz-linear-gradient(-45deg, ${message.background_color_1} 0%, ${message.background_color_2} 70%)`;
       const close = createElement("a");
       close.setAttribute("id", "cfrClose");
       close.setAttribute("aria-label", "close");
       this._l10n.setAttributes(close, message.close_button.tooltiptext);
       close.addEventListener("click", e => {
-        this.sendUserEventTelemetry("DISMISS");
+        this.sendUserEventTelemetry("DISMISS", win);
         this.collapseMessage();
         target.close(e);
       });
       const title = createElement("h1");
       title.setAttribute("id", "editBookmarkPanelRecommendationTitle");
       this._l10n.setAttributes(title, message.title);
       const content = createElement("p");
       content.setAttribute("id", "editBookmarkPanelRecommendationContent");
@@ -185,18 +187,21 @@ class _BookmarkPanelHub {
     this.toggleRecommendation(true);
     this.showMessage(message.content, this._response.target, this._response.win);
   }
 
   sendImpression() {
     this._addImpression(this._response);
   }
 
-  sendUserEventTelemetry(event) {
-    this._sendTelemetry({message_id: this._response.id, bucket_id: this._response.id, event});
+  sendUserEventTelemetry(event, win) {
+    // Only send pings for non private browsing windows
+    if (!PrivateBrowsingUtils.isBrowserPrivate(win.ownerGlobal.gBrowser.selectedBrowser)) {
+      this._sendTelemetry({message_id: this._response.id, bucket_id: this._response.id, event});
+    }
   }
 
   _sendTelemetry(ping) {
     this._dispatch({
       type: "DOORHANGER_TELEMETRY",
       data: {action: "cfr_user_event", source: "CFR", ...ping},
     });
   }
--- a/browser/components/newtab/test/unit/lib/BookmarkPanelHub.test.js
+++ b/browser/components/newtab/test/unit/lib/BookmarkPanelHub.test.js
@@ -7,23 +7,27 @@ describe("BookmarkPanelHub", () => {
   let instance;
   let fakeAddImpression;
   let fakeHandleMessageRequest;
   let fakeL10n;
   let fakeMessage;
   let fakeTarget;
   let fakeContainer;
   let fakeDispatch;
+  let fakeWindow;
+  let isBrowserPrivateStub;
   beforeEach(() => {
     sandbox = sinon.createSandbox();
     globals = new GlobalOverrider();
 
     fakeL10n = {setAttributes: sandbox.stub(), translateElements: sandbox.stub()};
     globals.set("DOMLocalization", function() { return fakeL10n; }); // eslint-disable-line prefer-arrow-callback
     globals.set("FxAccounts", {config: {promiseEmailFirstURI: sandbox.stub()}});
+    isBrowserPrivateStub = sandbox.stub().returns(false);
+    globals.set("PrivateBrowsingUtils", {isBrowserPrivate: isBrowserPrivateStub});
 
     instance = new _BookmarkPanelHub();
     fakeAddImpression = sandbox.stub();
     fakeHandleMessageRequest = sandbox.stub();
     fakeMessage = {
       text: "text",
       title: "title",
       link: {
@@ -52,16 +56,17 @@ describe("BookmarkPanelHub", () => {
         querySelector: sandbox.stub(),
         appendChild: sandbox.stub(),
       },
       hidePopup: sandbox.stub(),
       infoButton: {},
       close: sandbox.stub(),
     };
     fakeDispatch = sandbox.stub();
+    fakeWindow = {ownerGlobal: {openLinkIn: sandbox.stub(), gBrowser: {selectedBrowser: "browser"}}};
   });
   afterEach(() => {
     instance.uninit();
     sandbox.restore();
     globals.restore();
   });
   it("should create an instance", () => {
     assert.ok(instance);
@@ -121,36 +126,46 @@ describe("BookmarkPanelHub", () => {
     beforeEach(() => {
       instance.init(fakeHandleMessageRequest, fakeAddImpression, fakeDispatch);
       sandbox.stub(instance, "showMessage");
       sandbox.stub(instance, "sendImpression");
       sandbox.stub(instance, "hideMessage");
       fakeTarget = {infoButton: {disabled: true}};
     });
     it("should show a message when called with a response", () => {
-      instance.onResponse({content: "content"}, fakeTarget, {});
+      instance.onResponse({content: "content"}, fakeTarget, fakeWindow);
 
       assert.calledOnce(instance.showMessage);
-      assert.calledWithExactly(instance.showMessage, "content", fakeTarget, {});
+      assert.calledWithExactly(instance.showMessage, "content", fakeTarget, fakeWindow);
       assert.calledOnce(instance.sendImpression);
     });
     it("should dispatch a user impression", () => {
       sandbox.spy(instance, "sendUserEventTelemetry");
 
-      instance.onResponse({content: "content"}, fakeTarget, {});
+      instance.onResponse({content: "content"}, fakeTarget, fakeWindow);
 
       assert.calledOnce(instance.sendUserEventTelemetry);
-      assert.calledWithExactly(instance.sendUserEventTelemetry, "IMPRESSION");
+      assert.calledWithExactly(instance.sendUserEventTelemetry, "IMPRESSION", fakeWindow);
       assert.calledOnce(fakeDispatch);
 
       const [ping] = fakeDispatch.firstCall.args;
 
       assert.equal(ping.type, "DOORHANGER_TELEMETRY");
       assert.equal(ping.data.event, "IMPRESSION");
     });
+    it("should not dispatch a user impression if the window is private", () => {
+      isBrowserPrivateStub.returns(true);
+      sandbox.spy(instance, "sendUserEventTelemetry");
+
+      instance.onResponse({content: "content"}, fakeTarget, fakeWindow);
+
+      assert.calledOnce(instance.sendUserEventTelemetry);
+      assert.calledWithExactly(instance.sendUserEventTelemetry, "IMPRESSION", fakeWindow);
+      assert.notCalled(fakeDispatch);
+    });
     it("should hide existing messages if no response is provided", () => {
       instance.onResponse(null, fakeTarget);
 
       assert.calledOnce(instance.hideMessage);
       assert.calledWithExactly(instance.hideMessage, fakeTarget);
     });
   });
   describe("#showMessage.collapsed=false", () => {
@@ -170,67 +185,76 @@ describe("BookmarkPanelHub", () => {
     it("should reuse the container", () => {
       fakeTarget.container.querySelector.returns(true);
 
       instance.showMessage(fakeMessage, fakeTarget);
 
       assert.notCalled(fakeTarget.container.appendChild);
     });
     it("should open a tab with FxA signup", async () => {
-      const windowStub = {ownerGlobal: {openLinkIn: sandbox.stub()}};
       fakeTarget.container.querySelector.returns(false);
 
-      instance.showMessage(fakeMessage, fakeTarget, windowStub);
+      instance.showMessage(fakeMessage, fakeTarget, fakeWindow);
       // Call the event listener cb
       await fakeContainer.addEventListener.firstCall.args[1]();
 
-      assert.calledOnce(windowStub.ownerGlobal.openLinkIn);
+      assert.calledOnce(fakeWindow.ownerGlobal.openLinkIn);
     });
     it("should send a click event", async () => {
-      const windowStub = {ownerGlobal: {openLinkIn: sandbox.stub()}};
       sandbox.stub(instance, "sendUserEventTelemetry");
       fakeTarget.container.querySelector.returns(false);
 
-      instance.showMessage(fakeMessage, fakeTarget, windowStub);
+      instance.showMessage(fakeMessage, fakeTarget, fakeWindow);
       // Call the event listener cb
       await fakeContainer.addEventListener.firstCall.args[1]();
 
       assert.calledOnce(instance.sendUserEventTelemetry);
-      assert.calledWithExactly(instance.sendUserEventTelemetry, "CLICK");
+      assert.calledWithExactly(instance.sendUserEventTelemetry, "CLICK", fakeWindow);
+    });
+    it("should send a click event", async () => {
+      sandbox.stub(instance, "sendUserEventTelemetry");
+      fakeTarget.container.querySelector.returns(false);
+
+      instance.showMessage(fakeMessage, fakeTarget, fakeWindow);
+      // Call the event listener cb
+      await fakeContainer.addEventListener.firstCall.args[1]();
+
+      assert.calledOnce(instance.sendUserEventTelemetry);
+      assert.calledWithExactly(instance.sendUserEventTelemetry, "CLICK", fakeWindow);
     });
     it("should collapse the message", () => {
       fakeTarget.container.querySelector.returns(false);
       sandbox.spy(instance, "collapseMessage");
       instance._response.collapsed = false;
 
-      instance.showMessage(fakeMessage, fakeTarget);
+      instance.showMessage(fakeMessage, fakeTarget, fakeWindow);
       // Show message calls it once so we need to reset
       instance.toggleRecommendation.reset();
       // Call the event listener cb
       fakeContainer.addEventListener.secondCall.args[1]();
 
       assert.calledOnce(instance.collapseMessage);
       assert.calledOnce(fakeTarget.close);
       assert.isTrue(instance._response.collapsed);
       assert.calledOnce(instance.toggleRecommendation);
     });
     it("should send a dismiss event", () => {
       sandbox.stub(instance, "sendUserEventTelemetry");
       sandbox.spy(instance, "collapseMessage");
       instance._response.collapsed = false;
 
-      instance.showMessage(fakeMessage, fakeTarget);
+      instance.showMessage(fakeMessage, fakeTarget, fakeWindow);
       // Call the event listener cb
       fakeContainer.addEventListener.secondCall.args[1]();
 
       assert.calledOnce(instance.sendUserEventTelemetry);
-      assert.calledWithExactly(instance.sendUserEventTelemetry, "DISMISS");
+      assert.calledWithExactly(instance.sendUserEventTelemetry, "DISMISS", fakeWindow);
     });
     it("should call toggleRecommendation `true`", () => {
-      instance.showMessage(fakeMessage, fakeTarget);
+      instance.showMessage(fakeMessage, fakeTarget, fakeWindow);
 
       assert.calledOnce(instance.toggleRecommendation);
       assert.calledWithExactly(instance.toggleRecommendation, true);
     });
   });
   describe("#showMessage.collapsed=true", () => {
     beforeEach(() => {
       sandbox.stub(instance, "_response").value({collapsed: true, target: fakeTarget});
--- a/browser/components/originattributes/test/browser/browser_firstPartyIsolation_aboutPages.js
+++ b/browser/components/originattributes/test/browser/browser_firstPartyIsolation_aboutPages.js
@@ -1,13 +1,15 @@
 add_task(async function setup() {
   Services.prefs.setBoolPref("privacy.firstparty.isolate", true);
+  Services.prefs.setBoolPref("signon.management.page.enabled", true);
 
   registerCleanupFunction(function() {
     Services.prefs.clearUserPref("privacy.firstparty.isolate");
+    Services.prefs.clearUserPref("signon.management.page.enabled");
   });
 });
 
 /**
  * For loading the initial about:blank in e10s mode, it will be loaded with
  * NullPrincipal, and we also test if the firstPartyDomain of the origin
  * attributes is got from the origin itself.
  */
--- a/browser/components/preferences/connection.js
+++ b/browser/components/preferences/connection.js
@@ -401,16 +401,25 @@ var gConnectionsDialog = {
     if (!menu.disabled && isCustom) {
       customContainer.hidden = false;
       customInput.disabled = false;
       customContainer.scrollIntoView();
     } else {
       customContainer.hidden = true;
       customInput.disabled = true;
     }
+
+    // The height has likely changed, find our SubDialog and tell it to resize.
+    requestAnimationFrame(() => {
+      let dialogs = window.opener.gSubDialog._dialogs;
+      let dialog = dialogs.find(d => d._frame.contentDocument == document);
+      if (dialog) {
+        dialog.resizeVertically();
+      }
+    });
   },
 
   getDnsOverHttpsControls() {
     return [
       document.getElementById("networkDnsOverHttpsResolverChoices"),
       document.getElementById("networkCustomDnsOverHttpsInput"),
       document.getElementById("networkDnsOverHttpsResolverChoicesLabel"),
       document.getElementById("networkCustomDnsOverHttpsInputLabel"),
--- a/browser/components/preferences/in-content/subdialogs.js
+++ b/browser/components/preferences/in-content/subdialogs.js
@@ -281,29 +281,19 @@ SubDialog.prototype = {
 
     await this.resizeDialog();
   },
 
   async resizeDialog() {
     // Do this on load to wait for the CSS to load and apply before calculating the size.
     let docEl = this._frame.contentDocument.documentElement;
 
-    let titleBarHeight = this._titleBar.clientHeight +
-                         parseFloat(getComputedStyle(this._titleBar).borderBottomWidth);
-
     // These are deduced from styles which we don't change, so it's safe to get them now:
     let boxHorizontalBorder = 2 * parseFloat(getComputedStyle(this._box).borderLeftWidth);
-    let boxVerticalBorder = 2 * parseFloat(getComputedStyle(this._box).borderTopWidth);
     let frameHorizontalMargin = 2 * parseFloat(getComputedStyle(this._frame).marginLeft);
-    let frameVerticalMargin = 2 * parseFloat(getComputedStyle(this._frame).marginTop);
-
-    // The difference between the frame and box shouldn't change, either:
-    let boxRect = this._box.getBoundingClientRect();
-    let frameRect = this._frame.getBoundingClientRect();
-    let frameSizeDifference = (frameRect.top - boxRect.top) + (boxRect.bottom - frameRect.bottom);
 
     // Then determine and set a bunch of width stuff:
     let frameMinWidth = docEl.style.width;
     if (!frameMinWidth) {
       if (docEl.ownerDocument.body) {
         // HTML documents have a body but XUL documents don't
         frameMinWidth = docEl.ownerDocument.body.scrollWidth;
       } else {
@@ -313,16 +303,61 @@ SubDialog.prototype = {
     }
     let frameWidth = docEl.getAttribute("width") ? docEl.getAttribute("width") + "px" :
                      frameMinWidth;
     this._frame.style.width = frameWidth;
     this._box.style.minWidth = "calc(" +
                                (boxHorizontalBorder + frameHorizontalMargin) +
                                "px + " + frameMinWidth + ")";
 
+    this.resizeVertically();
+
+    this._overlay.dispatchEvent(new CustomEvent("dialogopen", {
+      bubbles: true,
+      detail: { dialog: this },
+    }));
+    this._overlay.style.visibility = "visible";
+    this._overlay.style.opacity = ""; // XXX: focus hack continued from _onContentLoaded
+
+    if (this._box.getAttribute("resizable") == "true") {
+      this._onResize = this._onResize.bind(this);
+      this._resizeObserver = new MutationObserver(this._onResize);
+      this._resizeObserver.observe(this._box, {attributes: true});
+    }
+
+    this._trapFocus();
+
+    // Search within main document and highlight matched keyword.
+    gSearchResultsPane.searchWithinNode(this._titleElement, gSearchResultsPane.query);
+
+    // Search within sub-dialog document and highlight matched keyword.
+    gSearchResultsPane.searchWithinNode(this._frame.contentDocument.firstElementChild,
+      gSearchResultsPane.query);
+
+    // Creating tooltips for all the instances found
+    for (let node of gSearchResultsPane.listSearchTooltips) {
+      if (!node.tooltipNode) {
+        gSearchResultsPane.createSearchTooltip(node, gSearchResultsPane.query);
+      }
+    }
+  },
+
+  resizeVertically() {
+    let docEl = this._frame.contentDocument.documentElement;
+
+    let titleBarHeight = this._titleBar.clientHeight +
+                         parseFloat(getComputedStyle(this._titleBar).borderBottomWidth);
+    let boxVerticalBorder = 2 * parseFloat(getComputedStyle(this._box).borderTopWidth);
+    let frameVerticalMargin = 2 * parseFloat(getComputedStyle(this._frame).marginTop);
+
+    // The difference between the frame and box shouldn't change, either:
+    let boxRect = this._box.getBoundingClientRect();
+    let frameRect = this._frame.getBoundingClientRect();
+    let frameSizeDifference = (frameRect.top - boxRect.top) + (boxRect.bottom - frameRect.bottom);
+
     // Now do the same but for the height. We need to do this afterwards because otherwise
     // XUL assumes we'll optimize for height and gives us "wrong" values which then are no
     // longer correct after we set the width:
     let frameMinHeight = docEl.style.height || docEl.scrollHeight + "px";
     let frameHeight = docEl.getAttribute("height") ? docEl.getAttribute("height") + "px" :
                                                      frameMinHeight;
 
     // Now check if the frame height we calculated is possible at this window size,
@@ -355,45 +390,16 @@ SubDialog.prototype = {
           this._frame.contentDocument.documentElement;
       contentPane.classList.add("doScroll");
     }
 
     this._frame.style.height = frameHeight;
     this._box.style.minHeight = "calc(" +
                                 (boxVerticalBorder + titleBarHeight + frameVerticalMargin) +
                                 "px + " + frameMinHeight + ")";
-
-    this._overlay.dispatchEvent(new CustomEvent("dialogopen", {
-      bubbles: true,
-      detail: { dialog: this },
-    }));
-    this._overlay.style.visibility = "visible";
-    this._overlay.style.opacity = ""; // XXX: focus hack continued from _onContentLoaded
-
-    if (this._box.getAttribute("resizable") == "true") {
-      this._onResize = this._onResize.bind(this);
-      this._resizeObserver = new MutationObserver(this._onResize);
-      this._resizeObserver.observe(this._box, {attributes: true});
-    }
-
-    this._trapFocus();
-
-    // Search within main document and highlight matched keyword.
-    gSearchResultsPane.searchWithinNode(this._titleElement, gSearchResultsPane.query);
-
-    // Search within sub-dialog document and highlight matched keyword.
-    gSearchResultsPane.searchWithinNode(this._frame.contentDocument.firstElementChild,
-      gSearchResultsPane.query);
-
-    // Creating tooltips for all the instances found
-    for (let node of gSearchResultsPane.listSearchTooltips) {
-      if (!node.tooltipNode) {
-        gSearchResultsPane.createSearchTooltip(node, gSearchResultsPane.query);
-      }
-    }
   },
 
   _onResize(mutations) {
     let frame = this._frame;
     // The width and height styles are needed for the initial
     // layout of the frame, but afterward they need to be removed
     // or their presence will restrict the contents of the <browser>
     // from resizing to a smaller size.
--- a/browser/components/preferences/in-content/tests/browser_password_management.js
+++ b/browser/components/preferences/in-content/tests/browser_password_management.js
@@ -67,16 +67,20 @@ add_task(async function subdialog_cleanu
     if (savePasswordCheckBox.checked) {
       savePasswordCheckBox.click();
     }
   });
   gBrowser.removeCurrentTab();
 });
 
 add_task(async function test_openPasswordManagement_overrideURI() {
+  await SpecialPowers.pushPrefEnv({"set": [
+    ["signon.management.page.enabled", true],
+  ]});
+
   Services.prefs.setStringPref(PREF_MANAGEMENT_URI, "about:logins?filter=%DOMAIN%");
   await openPreferencesViaOpenPreferencesAPI("privacy", {leaveOpen: true});
 
   let tabOpenPromise = BrowserTestUtils.waitForNewTab(gBrowser, "about:logins?filter=");
 
   await ContentTask.spawn(gBrowser.selectedBrowser, null, function() {
     let doc = content.document;
     let showPasswordsButton = doc.getElementById("showPasswords");
--- a/browser/components/urlbar/UrlbarController.jsm
+++ b/browser/components/urlbar/UrlbarController.jsm
@@ -8,17 +8,16 @@ var EXPORTED_SYMBOLS = [
   "UrlbarController",
 ];
 
 const {XPCOMUtils} = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
 XPCOMUtils.defineLazyModuleGetters(this, {
   AppConstants: "resource://gre/modules/AppConstants.jsm",
   BrowserUsageTelemetry: "resource:///modules/BrowserUsageTelemetry.jsm",
-  ExtensionSearchHandler: "resource://gre/modules/ExtensionSearchHandler.jsm",
   PlacesUtils: "resource://gre/modules/PlacesUtils.jsm",
   UrlbarPrefs: "resource:///modules/UrlbarPrefs.jsm",
   UrlbarProvidersManager: "resource:///modules/UrlbarProvidersManager.jsm",
   UrlbarUtils: "resource:///modules/UrlbarUtils.jsm",
   URLBAR_SELECTED_RESULT_TYPES: "resource:///modules/BrowserUsageTelemetry.jsm",
 });
 
 const TELEMETRY_1ST_RESULT = "PLACES_AUTOCOMPLETE_1ST_RESULT_TIME_MS";
@@ -90,52 +89,63 @@ class UrlbarController {
    * Takes a query context and starts the query based on the user input.
    *
    * @param {UrlbarQueryContext} queryContext The query details.
    */
   async startQuery(queryContext) {
     // Cancel any running query.
     this.cancelQuery();
 
-    this._lastQueryContext = queryContext;
+    // Wrap the external queryContext, to track a unique object, in case
+    // the external consumer reuses the same context multiple times.
+    // This also allows to add properties without polluting the context.
+    // Note this can't be null-ed or deleted once a query is done, because it's
+    // used by handleDeleteEntry and handleKeyNavigation, that can run after
+    // a query is cancelled or finished.
+    let contextWrapper = this._lastQueryContextWrapper = {queryContext};
 
     queryContext.lastResultCount = 0;
     TelemetryStopwatch.start(TELEMETRY_1ST_RESULT, queryContext);
     TelemetryStopwatch.start(TELEMETRY_6_FIRST_RESULTS, queryContext);
 
+    // For proper functionality we must ensure this notification is fired
+    // synchronously, as soon as startQuery is invoked, but after any
+    // notifications related to the previous query.
     this._notify("onQueryStarted", queryContext);
     await this.manager.startQuery(queryContext, this);
-    this._notify("onQueryFinished", queryContext);
+    // If the query has been cancelled, onQueryFinished was notified already.
+    // Note this._lastQueryContextWrapper may have changed in the meanwhile.
+    if (contextWrapper === this._lastQueryContextWrapper &&
+        !contextWrapper.done) {
+      contextWrapper.done = true;
+      // TODO (Bug 1549936) this is necessary to avoid leaks in PB tests.
+      this.manager.cancelQuery(queryContext);
+      this._notify("onQueryFinished", queryContext);
+    }
     return queryContext;
   }
 
   /**
    * Cancels an in-progress query. Note, queries may continue running if they
    * can't be cancelled.
-   *
-   * @param {UrlbarUtils.CANCEL_REASON} [reason]
-   *   The reason the query was cancelled.
    */
-  cancelQuery(reason) {
-    if (!this._lastQueryContext ||
-        this._lastQueryContext._cancelled) {
+  cancelQuery() {
+    // If the query finished already, don't handle cancel.
+    if (!this._lastQueryContextWrapper || this._lastQueryContextWrapper.done) {
       return;
     }
 
-    TelemetryStopwatch.cancel(TELEMETRY_1ST_RESULT, this._lastQueryContext);
-    TelemetryStopwatch.cancel(TELEMETRY_6_FIRST_RESULTS, this._lastQueryContext);
+    this._lastQueryContextWrapper.done = true;
 
-    this.manager.cancelQuery(this._lastQueryContext);
-    this._lastQueryContext._cancelled = true;
-    this._notify("onQueryCancelled", this._lastQueryContext);
-
-    if (reason == UrlbarUtils.CANCEL_REASON.BLUR &&
-        ExtensionSearchHandler.hasActiveInputSession()) {
-      ExtensionSearchHandler.handleInputCancelled();
-    }
+    let {queryContext} = this._lastQueryContextWrapper;
+    TelemetryStopwatch.cancel(TELEMETRY_1ST_RESULT, queryContext);
+    TelemetryStopwatch.cancel(TELEMETRY_6_FIRST_RESULTS, queryContext);
+    this.manager.cancelQuery(queryContext);
+    this._notify("onQueryCancelled", queryContext);
+    this._notify("onQueryFinished", queryContext);
   }
 
   /**
    * Receives results from a query.
    *
    * @param {UrlbarQueryContext} queryContext The query details.
    */
   receiveResults(queryContext) {
@@ -250,27 +260,25 @@ class UrlbarController {
         (event.key == "n" || event.key == "p")) {
       if (executeAction) {
         this.view.selectBy(1, { reverse: event.key == "p" });
       }
       event.preventDefault();
       return;
     }
 
-    if (this.view.isOpen && executeAction) {
-      let queryContext = this._lastQueryContext;
-      if (queryContext) {
-        let handled = this.view.oneOffSearchButtons.handleKeyPress(
-          event,
-          queryContext.results.length,
-          this.view.allowEmptySelection,
-          queryContext.searchString);
-        if (handled) {
-          return;
-        }
+    if (this.view.isOpen && executeAction && this._lastQueryContextWrapper) {
+      let {queryContext} = this._lastQueryContextWrapper;
+      let handled = this.view.oneOffSearchButtons.handleKeyPress(
+        event,
+        queryContext.results.length,
+        this.view.allowEmptySelection,
+        queryContext.searchString);
+      if (handled) {
+        return;
       }
     }
 
     switch (event.keyCode) {
       case KeyEvent.DOM_VK_ESCAPE:
         if (executeAction) {
           this.input.handleRevert();
         }
@@ -284,18 +292,18 @@ class UrlbarController {
         }
         if (executeAction) {
           this.input.handleCommand(event);
         }
         break;
       case KeyEvent.DOM_VK_TAB:
         if (this.view.isOpen) {
           if (executeAction) {
+            this.userSelectionBehavior = "tab";
             this.view.selectBy(1, { reverse: event.shiftKey });
-            this.userSelectionBehavior = "tab";
           }
           event.preventDefault();
         }
         break;
       case KeyEvent.DOM_VK_DOWN:
       case KeyEvent.DOM_VK_UP:
       case KeyEvent.DOM_VK_PAGE_DOWN:
       case KeyEvent.DOM_VK_PAGE_UP:
@@ -334,17 +342,17 @@ class UrlbarController {
         if (!this.view.isOpen) {
           break;
         }
         if (event.shiftKey) {
           if (!executeAction || this._handleDeleteEntry()) {
             event.preventDefault();
           }
         } else if (executeAction) {
-          this.view.removeAccessibleFocus();
+          this.userSelectionBehavior = "none";
         }
         break;
     }
   }
 
   /**
    * Tries to initialize a speculative connection on a result.
    * Speculative connections are only supported for a subset of all the results.
@@ -491,34 +499,35 @@ class UrlbarController {
 
   /**
    * Internal function handling deletion of entries. We only support removing
    * of history entries - other result sources will be ignored.
    *
    * @returns {boolean} Returns true if the deletion was acted upon.
    */
   _handleDeleteEntry() {
-    if (!this._lastQueryContext) {
+    if (!this._lastQueryContextWrapper) {
       Cu.reportError("Cannot delete - the latest query is not present");
       return false;
     }
 
     const selectedResult = this.input.view.selectedResult;
     if (!selectedResult ||
         selectedResult.source != UrlbarUtils.RESULT_SOURCE.HISTORY) {
       return false;
     }
 
-    let index = this._lastQueryContext.results.indexOf(selectedResult);
+    let {queryContext} = this._lastQueryContextWrapper;
+    let index = queryContext.results.indexOf(selectedResult);
     if (!index) {
       Cu.reportError("Failed to find the selected result in the results");
       return false;
     }
 
-    this._lastQueryContext.results.splice(index, 1);
+    queryContext.results.splice(index, 1);
     this._notify("onQueryResultRemoved", index);
 
     PlacesUtils.history.remove(selectedResult.payload.url).catch(Cu.reportError);
     return true;
   }
 
   /**
    * Internal function to notify listeners of results.
--- a/browser/components/urlbar/UrlbarInput.jsm
+++ b/browser/components/urlbar/UrlbarInput.jsm
@@ -1265,20 +1265,27 @@ class UrlbarInput {
   // Event handlers below.
 
   _on_blur(event) {
     // In certain cases, like holding an override key and confirming an entry,
     // we don't key a keyup event for the override key, thus we make this
     // additional cleanup on blur.
     this._clearActionOverride();
     this.formatValue();
+
+    // The extension input sessions depends more on blur than on the fact we
+    // actually cancel a running query, so we do it here.
+    if (ExtensionSearchHandler.hasActiveInputSession()) {
+      ExtensionSearchHandler.handleInputCancelled();
+    }
+
     // Respect the autohide preference for easier inspecting/debugging via
     // the browser toolbox.
     if (!UrlbarPrefs.get("ui.popup.disable_autohide")) {
-      this.view.close(UrlbarUtils.CANCEL_REASON.BLUR);
+      this.view.close();
     }
     // We may have hidden popup notifications, show them again if necessary.
     if (this.getAttribute("pageproxystate") != "valid") {
       this.window.UpdatePopupNotificationsVisibility();
     }
     this._resetSearchState();
   }
 
--- a/browser/components/urlbar/UrlbarUtils.jsm
+++ b/browser/components/urlbar/UrlbarUtils.jsm
@@ -116,22 +116,16 @@ var UrlbarUtils = {
   // IME composition states.
   COMPOSITION: {
     NONE: 1,
     COMPOSING: 2,
     COMMIT: 3,
     CANCELED: 4,
   },
 
-  // This defines possible reasons for canceling a query.
-  CANCEL_REASON: {
-    // 1 is intentionally left in case we want a none/undefined/other later.
-    BLUR: 2,
-  },
-
   // Limit the length of titles and URLs we display so layout doesn't spend too
   // much time building text runs.
   MAX_TEXT_LENGTH: 255,
 
   /**
    * Adds a url to history as long as it isn't in a private browsing window,
    * and it is valid.
    *
--- a/browser/components/urlbar/UrlbarView.jsm
+++ b/browser/components/urlbar/UrlbarView.jsm
@@ -88,17 +88,18 @@ class UrlbarView {
       throw new Error("UrlbarView: Cannot select an item if the view isn't open.");
     }
 
     if (val < 0) {
       this._selectItem(null);
       return val;
     }
 
-    let items = this._rows.children;
+    let items = Array.from(this._rows.children)
+                     .filter(r => r.style.display != "none");
     if (val >= items.length) {
       throw new Error(`UrlbarView: Index ${val} is out of bounds.`);
     }
     this._selectItem(items[val]);
     return val;
   }
 
   /**
@@ -139,74 +140,84 @@ class UrlbarView {
       throw new Error("UrlbarView: Cannot select an item if the view isn't open.");
     }
 
     // Freeze results as the user is interacting with them.
     this.controller.cancelQuery();
 
     let row = this._selected;
 
+    // Results over maxResults may be hidden and should not be selectable.
+    let lastElementChild = this._rows.lastElementChild;
+    while (lastElementChild && lastElementChild.style.display == "none") {
+      lastElementChild = lastElementChild.previousElementSibling;
+    }
+
     if (!row) {
-      this._selectItem(reverse ? this._rows.lastElementChild :
+      this._selectItem(reverse ? lastElementChild :
                                  this._rows.firstElementChild);
       return;
     }
 
     let endReached = reverse ?
       (row == this._rows.firstElementChild) :
-      (row == this._rows.lastElementChild);
+      (row == lastElementChild);
     if (endReached) {
       if (this.allowEmptySelection) {
         row = null;
       } else {
-        row = reverse ? this._rows.lastElementChild :
+        row = reverse ? lastElementChild :
                         this._rows.firstElementChild;
       }
       this._selectItem(row);
       return;
     }
 
     while (amount-- > 0) {
       let next = reverse ? row.previousElementSibling : row.nextElementSibling;
       if (!next) {
         break;
       }
+      if (next.style.display == "none") {
+        continue;
+      }
       row = next;
     }
     this._selectItem(row);
   }
 
   removeAccessibleFocus() {
     this._setAccessibleFocus(null);
   }
 
   /**
    * Closes the autocomplete popup, cancelling the query if necessary.
-   *
-   * @param {UrlbarUtils.CANCEL_REASON} [cancelReason]
-   *   Indicates if this close is being triggered as a result of a user action
-   *   which would cancel a query, e.g. on blur.
    */
-  close(cancelReason) {
-    this.controller.cancelQuery(cancelReason);
+  close() {
+    this.controller.cancelQuery();
     this.panel.hidePopup();
   }
 
   // UrlbarController listener methods.
   onQueryStarted(queryContext) {
+    this._queryWasCancelled = false;
     this._startRemoveStaleRowsTimer();
   }
 
   onQueryCancelled(queryContext) {
+    this._queryWasCancelled = true;
     this._cancelRemoveStaleRowsTimer();
   }
 
   onQueryFinished(queryContext) {
     this._cancelRemoveStaleRowsTimer();
-    this._removeStaleRows();
+    // If the query has not been canceled, remove stale rows immediately.
+    if (!this._queryWasCancelled) {
+      this._removeStaleRows();
+    }
   }
 
   onQueryResults(queryContext) {
     this._queryContext = queryContext;
 
     this._updateResults(queryContext);
 
     let isFirstPreselectedResult = false;
@@ -381,30 +392,108 @@ class UrlbarView {
       verticalOffset++;
     }
     this.panel.style.marginInlineStart = px(horizontalOffset);
     this.panel.style.marginTop = px(verticalOffset);
 
     this.panel.openPopup(this.input.textbox, "after_start");
   }
 
+  /**
+   * Whether a result is a search suggestion.
+   * @param {UrlbarResult} result The result to examine.
+   * @returns {boolean} Whether the result is a search suggestion.
+   */
+  _resultIsSearchSuggestion(result) {
+    return Boolean(result &&
+                   result.type == UrlbarUtils.RESULT_TYPE.SEARCH &&
+                   result.payload.suggestion);
+  }
+
+  /**
+   * Checks whether the given row index can be update to the result we want
+   * to apply. This is used in _updateResults to avoid flickering of results, by
+   * reusing existing rows.
+   * @param {number} rowIndex Index of the row to examine.
+   * @param {UrlbarResult} result The result we'd like to apply.
+   * @param {number} firstSearchSuggestionIndex Index of the first search suggestion.
+   * @param {number} lastSearchSuggestionIndex Index of the last search suggestion.
+   * @returns {boolean} Whether the row can be updated to this result.
+   */
+  _rowCanUpdateToResult(rowIndex, result, firstSearchSuggestionIndex,
+                        lastSearchSuggestionIndex) {
+    // The heuristic result must always be current, thus it's always compatible.
+    if (result.heuristic) {
+      return true;
+    }
+    let row = this._rows.children[rowIndex];
+    let resultIsSearchSuggestion = this._resultIsSearchSuggestion(result);
+    // If the row is same type, just update it.
+    if (resultIsSearchSuggestion == this._resultIsSearchSuggestion(row.result)) {
+      return true;
+    }
+    // If the row has a different type, update it if we are in a compatible
+    // index range.
+    // In practice we don't want to overwrite a search suggestion with a non
+    // search suggestion, but we allow the opposite.
+    return resultIsSearchSuggestion && rowIndex >= firstSearchSuggestionIndex;
+  }
+
   _updateResults(queryContext) {
+    // TODO: For now this just compares search suggestions to the rest, in the
+    // future we should make it support any type of result. Or, even better,
+    // results should be grouped, thus we can directly update groups.
+
+    // Find where are existing search suggestions.
+    let firstSearchSuggestionIndex = -1;
+    let lastSearchSuggestionIndex = -1;
+    for (let i = 0; i < this._rows.children.length; ++i) {
+      let row = this._rows.children[i];
+      // Mark every row as stale, _updateRow will unmark them later.
+      row.setAttribute("stale", "true");
+      // Skip any row that isn't a search suggestion, or is non-visible because
+      // over maxResults.
+      if (row.result.heuristic ||
+          i >= queryContext.maxResults ||
+          !this._resultIsSearchSuggestion(row.result)) {
+        continue;
+      }
+      if (firstSearchSuggestionIndex == -1) {
+        firstSearchSuggestionIndex = i;
+      }
+      lastSearchSuggestionIndex = i;
+    }
+
+    // Walk rows and find an insertion index for results. To avoid flicker, we
+    // skip rows until we find one compatible with the result we want to apply.
+    // If we couldn't find a compatible range, we'll just update.
     let results = queryContext.results;
-    let i = 0;
-    for (let row of this._rows.children) {
-      if (i < results.length) {
-        this._updateRow(row, results[i]);
-      } else {
-        row.setAttribute("stale", "true");
+    let resultIndex = 0;
+    // We can have more rows than the visible ones.
+    for (let rowIndex = 0;
+         rowIndex < this._rows.children.length && resultIndex < results.length;
+         ++rowIndex) {
+      let row = this._rows.children[rowIndex];
+      let result = results[resultIndex];
+      if (this._rowCanUpdateToResult(rowIndex, result,
+                                     firstSearchSuggestionIndex,
+                                     lastSearchSuggestionIndex)) {
+        this._updateRow(row, result);
+        resultIndex++;
       }
-      i++;
     }
-    for (; i < results.length; i++) {
+    // Add remaining results, if we have fewer rows than results.
+    for (; resultIndex < results.length; ++resultIndex) {
       let row = this._createRow();
-      this._updateRow(row, results[i]);
+      this._updateRow(row, results[resultIndex]);
+      // Due to stale rows, we may have more rows than maxResults, thus we must
+      // hide them, and we'll revert this when stale rows are removed.
+      if (this._rows.children.length >= queryContext.maxResults) {
+        row.style.display = "none";
+      }
       this._rows.appendChild(row);
     }
   }
 
   _createRow() {
     let item = this._createElement("div");
     item.className = "urlbarView-row";
     item.setAttribute("role", "option");
@@ -546,16 +635,18 @@ class UrlbarView {
   }
 
   _removeStaleRows() {
     let row = this._rows.lastElementChild;
     while (row) {
       let next = row.previousElementSibling;
       if (row.hasAttribute("stale")) {
         row.remove();
+      } else {
+        row.style.display = "";
       }
       row = next;
     }
   }
 
   _startRemoveStaleRowsTimer() {
     this._removeStaleRowsTimer = this.window.setTimeout(() => {
       this._removeStaleRowsTimer = null;
@@ -720,25 +811,27 @@ class UrlbarView {
     let row = event.target;
     while (!row.classList.contains("urlbarView-row")) {
       row = row.parentNode;
     }
     this.input.pickResult(event, parseInt(row.getAttribute("resultIndex")));
   }
 
   _on_overflow(event) {
-    if (event.target.classList.contains("urlbarView-url") ||
-        event.target.classList.contains("urlbarView-title")) {
+    if (event.detail == 1 &&
+        (event.target.classList.contains("urlbarView-url") ||
+         event.target.classList.contains("urlbarView-title"))) {
       event.target.toggleAttribute("overflow", true);
     }
   }
 
   _on_underflow(event) {
-    if (event.target.classList.contains("urlbarView-url") ||
-        event.target.classList.contains("urlbarView-title")) {
+    if (event.detail == 1 &&
+        (event.target.classList.contains("urlbarView-url") ||
+         event.target.classList.contains("urlbarView-title"))) {
       event.target.toggleAttribute("overflow", false);
     }
   }
 
   _on_popupshowing() {
     this.window.addEventListener("resize", this);
   }
 
@@ -746,16 +839,17 @@ class UrlbarView {
     this.input.inputField.setAttribute("aria-expanded", "true");
   }
 
   _on_popuphiding() {
     this.controller.cancelQuery();
     this.window.removeEventListener("resize", this);
     this.removeAccessibleFocus();
     this.input.inputField.setAttribute("aria-expanded", "false");
+    this._rows.textContent = "";
   }
 
   _on_resize() {
     // Close the popup as it would be wrongly sized. This can
     // happen when using special OS resize functions like Win+Arrow.
     this.close();
   }
 }
--- a/browser/components/urlbar/tests/UrlbarTestUtils.jsm
+++ b/browser/components/urlbar/tests/UrlbarTestUtils.jsm
@@ -451,31 +451,26 @@ class UrlbarAbstraction {
         return UrlbarUtils.RESULT_TYPE.REMOTE_TAB;
       } else if (action && action.type == "switchtab") {
         return UrlbarUtils.RESULT_TYPE.TAB_SWITCH;
       }
       return UrlbarUtils.RESULT_TYPE.URL;
     }
     let details = {};
     if (this.quantumbar) {
-      let context = await this.urlbar.lastQueryContextPromise;
-      if (index >= context.results.length) {
-        throw new Error("Requested index not found in results");
-      }
-      let {url, postData} = UrlbarUtils.getUrlFromResult(context.results[index]);
+      let result = element.result;
+      let {url, postData} = UrlbarUtils.getUrlFromResult(result);
       details.url = url;
       details.postData = postData;
-      details.type = context.results[index].type;
-      details.heuristic = context.results[index].heuristic;
-      details.autofill = !!context.results[index].autofill;
+      details.type = result.type;
+      details.heuristic = result.heuristic;
+      details.autofill = !!result.autofill;
       details.image = element.getElementsByClassName("urlbarView-favicon")[0].src;
-      details.title = context.results[index].title;
-      details.tags = "tags" in context.results[index].payload ?
-        context.results[index].payload.tags :
-        [];
+      details.title = result.title;
+      details.tags = "tags" in result.payload ? result.payload.tags : [];
       let actions = element.getElementsByClassName("urlbarView-action");
       let urls = element.getElementsByClassName("urlbarView-url");
       let typeIcon = element.querySelector(".urlbarView-type-icon");
       let typeIconStyle = this.window.getComputedStyle(typeIcon);
       details.displayed = {
         title: element.getElementsByClassName("urlbarView-title")[0].textContent,
         action: actions.length > 0 ? actions[0].textContent : null,
         url: urls.length > 0 ? urls[0].textContent : null,
@@ -485,23 +480,23 @@ class UrlbarAbstraction {
         action: element.getElementsByClassName("urlbarView-action")[0],
         row: element,
         separator: element.getElementsByClassName("urlbarView-title-separator")[0],
         title: element.getElementsByClassName("urlbarView-title")[0],
         url: element.getElementsByClassName("urlbarView-url")[0],
       };
       if (details.type == UrlbarUtils.RESULT_TYPE.SEARCH) {
         details.searchParams = {
-          engine: context.results[index].payload.engine,
-          keyword: context.results[index].payload.keyword,
-          query: context.results[index].payload.query,
-          suggestion: context.results[index].payload.suggestion,
+          engine: result.payload.engine,
+          keyword: result.payload.keyword,
+          query: result.payload.query,
+          suggestion: result.payload.suggestion,
         };
       } else if (details.type == UrlbarUtils.RESULT_TYPE.KEYWORD) {
-        details.keyword = context.results[index].payload.keyword;
+        details.keyword = result.payload.keyword;
       }
     } else {
       details.url = this.urlbar.controller.getFinalCompleteValueAt(index);
 
       let style = this.urlbar.controller.getStyleAt(index);
       let action = PlacesUtils.parseActionUrl(this.urlbar.controller.getValueAt(index));
       details.type = getType(style, action);
       details.heuristic = style.includes("heuristic");
--- a/browser/components/urlbar/tests/browser/browser_urlbarOneOffs.js
+++ b/browser/components/urlbar/tests/browser/browser_urlbarOneOffs.js
@@ -193,17 +193,17 @@ add_task(async function searchWith() {
 // Clicks a one-off.
 add_task(async function oneOffClick() {
   gBrowser.selectedTab = BrowserTestUtils.addTab(gBrowser);
 
   // We are explicitly using something that looks like a url, to make the test
   // stricter. Even if it looks like a url, we should search.
   let typedValue = "foo.bar";
   await promiseAutocompleteResultPopup(typedValue);
-  await waitForAutocompleteResultAt(1);
+  await UrlbarTestUtils.getDetailsOfResultAt(window, 0);
   assertState(0, -1, typedValue);
 
   let oneOffs = oneOffSearchButtons.getSelectableButtons(true);
   let resultsPromise =
     BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser, false,
                                    "http://mochi.test:8888/?terms=foo.bar");
   EventUtils.synthesizeMouseAtCenter(oneOffs[0], {});
   await resultsPromise;
@@ -214,17 +214,17 @@ add_task(async function oneOffClick() {
 // Presses the Return key when a one-off is selected.
 add_task(async function oneOffReturn() {
   gBrowser.selectedTab = BrowserTestUtils.addTab(gBrowser);
 
   // We are explicitly using something that looks like a url, to make the test
   // stricter. Even if it looks like a url, we should search.
   let typedValue = "foo.bar";
   await promiseAutocompleteResultPopup(typedValue, window, true);
-  await waitForAutocompleteResultAt(1);
+  await UrlbarTestUtils.getDetailsOfResultAt(window, 0);
   assertState(0, -1, typedValue);
 
   // Alt+Down to select the first one-off.
   EventUtils.synthesizeKey("KEY_ArrowDown", { altKey: true });
   assertState(0, 0, typedValue);
 
   let resultsPromise =
     BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser, false,
@@ -241,45 +241,43 @@ add_task(async function collapsedOneOffs
   let defaultEngine = await Services.search.getDefault();
   let engines = (await Services.search.getVisibleEngines()).filter(e => e.name != defaultEngine.name);
   await SpecialPowers.pushPrefEnv({"set": [
     [ "browser.search.hiddenOneOffs", engines.map(e => e.name).join(",") ],
   ]});
 
   let typedValue = "foo";
   await promiseAutocompleteResultPopup(typedValue, window, true);
-  await waitForAutocompleteResultAt(0);
+  await UrlbarTestUtils.getDetailsOfResultAt(window, 0);
   assertState(0, -1);
   Assert.ok(oneOffSearchButtons.buttons.collapsed,
     "The one-off buttons should be collapsed");
   EventUtils.synthesizeKey("KEY_ArrowUp");
   assertState(1, -1);
   await hidePopup();
 });
 
-
 // The one-offs should be hidden when searching with an "@engine" search engine
 // alias.
 add_task(async function hiddenWhenUsingSearchAlias() {
   let typedValue = "@example";
   await promiseAutocompleteResultPopup(typedValue, window, true);
-  await waitForAutocompleteResultAt(0);
+  await UrlbarTestUtils.getDetailsOfResultAt(window, 0);
   Assert.equal(UrlbarTestUtils.getOneOffSearchButtonsVisible(window), false,
     "Should not be showing the one-off buttons");
   await hidePopup();
 
   typedValue = "not an engine alias";
   await promiseAutocompleteResultPopup(typedValue, window, true);
-  await waitForAutocompleteResultAt(0);
+  await UrlbarTestUtils.getDetailsOfResultAt(window, 0);
   Assert.equal(UrlbarTestUtils.getOneOffSearchButtonsVisible(window), true,
     "Should be showing the one-off buttons");
   await hidePopup();
 });
 
-
 function assertState(result, oneOff, textValue = undefined) {
   Assert.equal(UrlbarTestUtils.getSelectedIndex(window), result,
     "Expected result should be selected");
   Assert.equal(oneOffSearchButtons.selectedButtonIndex,
     oneOff, "Expected one-off should be selected");
   if (textValue !== undefined) {
     Assert.equal(gURLBar.textValue, textValue, "Expected textValue");
   }
--- a/browser/components/urlbar/tests/unit/test_UrlbarController_integration.js
+++ b/browser/components/urlbar/tests/unit/test_UrlbarController_integration.js
@@ -63,17 +63,17 @@ add_task(async function test_basic_searc
 add_task(async function test_cancel_search() {
   let providerCanceledDeferred = PromiseUtils.defer();
   let providerName = registerBasicTestProvider([match], providerCanceledDeferred.resolve);
   const context = createContext(TEST_URL, {providers: [providerName]});
 
   let startedPromise = promiseControllerNotification(controller, "onQueryStarted");
   let cancelPromise = promiseControllerNotification(controller, "onQueryCancelled");
 
-  await controller.startQuery(context);
+  controller.startQuery(context);
 
   let params = await startedPromise;
 
   controller.cancelQuery(context);
 
   Assert.equal(params[0], context);
 
   info("Should tell the provider the query is canceled");
--- a/browser/components/urlbar/tests/unit/test_UrlbarController_telemetry.js
+++ b/browser/components/urlbar/tests/unit/test_UrlbarController_telemetry.js
@@ -34,26 +34,35 @@ class DelayedProvider extends UrlbarProv
     return UrlbarUtils.PROVIDER_TYPE.PROFILE;
   }
   get sources() {
     return [UrlbarUtils.RESULT_SOURCE.TABS];
   }
   async startQuery(context, add) {
     Assert.ok(context, "context is passed-in");
     Assert.equal(typeof add, "function", "add is a callback");
-    this._context = context;
     this._add = add;
+    await new Promise(resolve => {
+      this._resultsAdded = resolve;
+    });
   }
   cancelQuery(context) {
-    Assert.ok(false, "cancelQuery should not be called");
+    // Nothing.
   }
-  addResults(matches) {
+  async addResults(matches, finish = true) {
+    // startQuery may have not been invoked yet, so wait for it
+    await TestUtils.waitForCondition(() => !!this._add,
+                                     "Waiting for the _add callback");
     for (const match of matches) {
       this._add(this, match);
     }
+    if (finish) {
+      this._add = null;
+      this._resultsAdded();
+    }
   }
 }
 
 /**
  * Returns the number of reports sent recorded within the histogram results.
  *
  * @param {object} results a snapshot of histogram results to check.
  * @returns {number} The count of reports recorded in the histogram.
@@ -88,17 +97,17 @@ add_task(async function test_n_autocompl
   UrlbarProvidersManager.registerProvider(provider);
   const context = createContext(TEST_URL, {providers: [provider.name]});
 
   Assert.ok(!TelemetryStopwatch.running(TELEMETRY_1ST_RESULT, context),
     "Should not have started first result stopwatch");
   Assert.ok(!TelemetryStopwatch.running(TELEMETRY_6_FIRST_RESULTS, context),
     "Should not have started first 6 results stopwatch");
 
-  await controller.startQuery(context);
+  controller.startQuery(context);
 
   Assert.ok(TelemetryStopwatch.running(TELEMETRY_1ST_RESULT, context),
     "Should have started first result stopwatch");
   Assert.ok(TelemetryStopwatch.running(TELEMETRY_6_FIRST_RESULTS, context),
     "Should have started first 6 results stopwatch");
 
   controller.cancelQuery(context);
 
@@ -125,24 +134,24 @@ add_task(async function test_n_autocompl
 
   let resultsPromise = promiseControllerNotification(controller, "onQueryResults");
 
   Assert.ok(!TelemetryStopwatch.running(TELEMETRY_1ST_RESULT, context),
     "Should not have started first result stopwatch");
   Assert.ok(!TelemetryStopwatch.running(TELEMETRY_6_FIRST_RESULTS, context),
     "Should not have started first 6 results stopwatch");
 
-  await controller.startQuery(context);
+  controller.startQuery(context);
 
   Assert.ok(TelemetryStopwatch.running(TELEMETRY_1ST_RESULT, context),
     "Should have started first result stopwatch");
   Assert.ok(TelemetryStopwatch.running(TELEMETRY_6_FIRST_RESULTS, context),
     "Should have started first 6 results stopwatch");
 
-  provider.addResults([MATCH]);
+  await provider.addResults([MATCH], false);
   await resultsPromise;
 
   Assert.ok(!TelemetryStopwatch.running(TELEMETRY_1ST_RESULT, context),
     "Should have stopped the first stopwatch");
   Assert.ok(TelemetryStopwatch.running(TELEMETRY_6_FIRST_RESULTS, context),
     "Should have kept the first 6 results stopwatch running");
 
   let firstResults = firstHistogram.snapshot();
@@ -150,21 +159,21 @@ add_task(async function test_n_autocompl
   Assert.equal(getHistogramReportsCount(firstResults), 1,
     "Should have recorded one time for the first result");
   Assert.equal(getHistogramReportsCount(first6Results), 0,
     "Should not have recorded any times (first 6 results)");
 
   // Now add 5 more results, so that the first 6 results is triggered.
   for (let i = 0; i < 5; i++) {
     resultsPromise = promiseControllerNotification(controller, "onQueryResults");
-    provider.addResults([
+    await provider.addResults([
       new UrlbarResult(UrlbarUtils.RESULT_TYPE.TAB_SWITCH,
                        UrlbarUtils.RESULT_SOURCE.TABS,
                        { url: TEST_URL + "/i" }),
-    ]);
+    ], false);
     await resultsPromise;
   }
 
   Assert.ok(!TelemetryStopwatch.running(TELEMETRY_1ST_RESULT, context),
     "Should have stopped the first stopwatch");
   Assert.ok(!TelemetryStopwatch.running(TELEMETRY_6_FIRST_RESULTS, context),
     "Should have stopped the first 6 results stopwatch");
 
@@ -172,17 +181,17 @@ add_task(async function test_n_autocompl
   let updated6Results = sixthHistogram.snapshot();
   Assert.deepEqual(updatedResults, firstResults,
     "Should not have changed the histogram for the first result");
   Assert.equal(getHistogramReportsCount(updated6Results), 1,
     "Should have recorded one time for the first 6 results");
 
   // Add one more, to check neither are updated.
   resultsPromise = promiseControllerNotification(controller, "onQueryResults");
-  provider.addResults([
+  await provider.addResults([
     new UrlbarResult(UrlbarUtils.RESULT_TYPE.TAB_SWITCH,
                      UrlbarUtils.RESULT_SOURCE.TABS,
                      { url: TEST_URL + "/6" }),
   ]);
   await resultsPromise;
 
   let secondUpdateResults = firstHistogram.snapshot();
   let secondUpdate6Results = sixthHistogram.snapshot();
--- a/browser/components/urlbar/tests/unit/test_UrlbarController_unit.js
+++ b/browser/components/urlbar/tests/unit/test_UrlbarController_unit.js
@@ -144,21 +144,21 @@ add_task(function test_handle_query_star
   Assert.equal(generalListener.onQueryStarted.callCount, 1,
     "Should have called onQueryStarted for the listener");
   Assert.deepEqual(generalListener.onQueryStarted.args[0], [context],
     "Should have called onQueryStarted with the context");
 
   sandbox.resetHistory();
 });
 
-add_task(function test_handle_query_starts_search_sets_allowAutofill() {
+add_task(async function test_handle_query_starts_search_sets_allowAutofill() {
   let originalValue = Services.prefs.getBoolPref("browser.urlbar.autoFill");
   Services.prefs.setBoolPref("browser.urlbar.autoFill", !originalValue);
 
-  controller.startQuery(createContext());
+  await controller.startQuery(createContext());
 
   Assert.equal(fPM.startQuery.callCount, 1,
     "Should have called startQuery once");
   Assert.equal(fPM.startQuery.args[0].length, 2,
     "Should have called startQuery with two arguments");
 
   assertContextMatches(fPM.startQuery.args[0][0], {
     allowAutofill: !originalValue,
@@ -167,19 +167,16 @@ add_task(function test_handle_query_star
     "Should have passed the controller as the second argument");
 
   sandbox.resetHistory();
 
   Services.prefs.clearUserPref("browser.urlbar.autoFill");
 });
 
 add_task(function test_cancel_query() {
-  // Ensure the controller doesn't have any previous queries.
-  delete controller._lastQueryContext;
-
   const context = createContext();
   controller.startQuery(context);
 
   controller.cancelQuery();
 
   Assert.equal(fPM.cancelQuery.callCount, 1,
     "Should have called cancelQuery once");
   Assert.equal(fPM.cancelQuery.args[0].length, 1,
@@ -200,8 +197,49 @@ add_task(function test_receiveResults() 
 
   Assert.equal(generalListener.onQueryResults.callCount, 1,
     "Should have called onQueryResults for the listener");
   Assert.deepEqual(generalListener.onQueryResults.args[0], [context],
     "Should have called onQueryResults with the context");
 
   sandbox.resetHistory();
 });
+
+add_task(async function test_notifications_order() {
+  // Clear any pending notifications.
+  const context = createContext();
+  await controller.startQuery(context);
+
+  // Check that when multiple queries are executed, the notifications arrive
+  // in the proper order.
+  let collectingListener = new Proxy({}, {
+    _notifications: [],
+    get(target, name) {
+      if (name == "notifications") {
+        return this._notifications;
+      }
+      return () => {
+        this._notifications.push(name);
+      };
+    },
+  });
+  controller.addQueryListener(collectingListener);
+  controller.startQuery(context);
+  Assert.deepEqual(["onQueryStarted"], collectingListener.notifications,
+                   "Check onQueryStarted is fired synchronously");
+  controller.startQuery(context);
+  Assert.deepEqual(["onQueryStarted", "onQueryCancelled", "onQueryFinished",
+                    "onQueryStarted"],
+                   collectingListener.notifications,
+                   "Check order of notifications");
+  controller.cancelQuery();
+  Assert.deepEqual(["onQueryStarted", "onQueryCancelled", "onQueryFinished",
+                    "onQueryStarted", "onQueryCancelled", "onQueryFinished"],
+                   collectingListener.notifications,
+                   "Check order of notifications");
+  await controller.startQuery(context);
+  controller.cancelQuery();
+  Assert.deepEqual(["onQueryStarted", "onQueryCancelled", "onQueryFinished",
+                    "onQueryStarted", "onQueryCancelled", "onQueryFinished",
+                    "onQueryStarted", "onQueryFinished"],
+                   collectingListener.notifications,
+                   "Check order of notifications");
+});
--- a/browser/config/mozconfigs/linux32/common-opt
+++ b/browser/config/mozconfigs/linux32/common-opt
@@ -7,12 +7,10 @@ ac_add_options --with-google-location-se
 ac_add_options --with-google-safebrowsing-api-keyfile=/builds/sb-gapi.data
 ac_add_options --with-mozilla-api-keyfile=/builds/mozilla-desktop-geoloc-api.key
 
 . $topsrcdir/build/unix/mozconfig.linux32
 
 # Needed to enable breakpad in application.ini
 export MOZILLA_OFFICIAL=1
 
-export MOZ_TELEMETRY_REPORTING=1
-
 # Package js shell.
 export MOZ_PACKAGE_JSSHELL=1
--- a/browser/config/mozconfigs/linux32/debug
+++ b/browser/config/mozconfigs/linux32/debug
@@ -4,19 +4,16 @@ ac_add_options --enable-debug
 
 MOZ_AUTOMATION_L10N_CHECK=0
 
 . $topsrcdir/build/unix/mozconfig.linux32
 
 # Needed to enable breakpad in application.ini
 export MOZILLA_OFFICIAL=1
 
-# Enable Telemetry
-export MOZ_TELEMETRY_REPORTING=1
-
 #Use ccache
 
 # Package js shell.
 export MOZ_PACKAGE_JSSHELL=1
 
 ac_add_options --with-branding=browser/branding/nightly
 
 . "$topsrcdir/build/mozconfig.common.override"
--- a/browser/config/mozconfigs/linux32/debug-asan
+++ b/browser/config/mozconfigs/linux32/debug-asan
@@ -6,18 +6,15 @@ ac_add_options --enable-optimize="-O1"
 . $topsrcdir/build/mozconfig.stylo
 
 # ASan specific options on Linux
 ac_add_options --enable-valgrind
 
 . $topsrcdir/build/unix/mozconfig.asan
 ac_add_options --disable-elf-hack
 
-# Enable Telemetry
-export MOZ_TELEMETRY_REPORTING=1
-
 # Package js shell.
 export MOZ_PACKAGE_JSSHELL=1
 
 # Need this to prevent name conflicts with the normal nightly build packages
 export MOZ_PKG_SPECIAL=asan
 
 . "$topsrcdir/build/mozconfig.common.override"
--- a/browser/config/mozconfigs/linux32/l10n-mozconfig
+++ b/browser/config/mozconfigs/linux32/l10n-mozconfig
@@ -7,16 +7,13 @@ ac_add_options --disable-nodejs
 
 if test `uname -m` = "x86_64"; then
   ac_add_options --target=i686-pc-linux
   ac_add_options --host=i686-pc-linux
 fi
 
 export MOZILLA_OFFICIAL=1
 
-# Enable Telemetry
-export MOZ_TELEMETRY_REPORTING=1
-
 # Don't autoclobber l10n, as this can lead to missing binaries and broken builds
 # Bug 1283438
 mk_add_options AUTOCLOBBER=
 
 . "$topsrcdir/build/mozconfig.common.override"
--- a/browser/config/mozconfigs/linux32/l10n-mozconfig-devedition
+++ b/browser/config/mozconfigs/linux32/l10n-mozconfig-devedition
@@ -7,18 +7,15 @@ ac_add_options --disable-nodejs
 
 if test `uname -m` = "x86_64"; then
   ac_add_options --target=i686-pc-linux
   ac_add_options --host=i686-pc-linux
 fi
 
 export MOZILLA_OFFICIAL=1
 
-# Enable Telemetry
-export MOZ_TELEMETRY_REPORTING=1
-
 # Don't autoclobber l10n, as this can lead to missing binaries and broken builds
 # Bug 1283438
 mk_add_options AUTOCLOBBER=
 # Enable MOZ_ALLOW_LEGACY_EXTENSIONS
 ac_add_options "MOZ_ALLOW_LEGACY_EXTENSIONS=1"
 
 . "$topsrcdir/build/mozconfig.common.override"
--- a/browser/config/mozconfigs/linux32/nightly-asan
+++ b/browser/config/mozconfigs/linux32/nightly-asan
@@ -8,15 +8,12 @@ ac_add_options --enable-optimize="-O2 -g
 ac_add_options --enable-valgrind
 
 . $topsrcdir/build/unix/mozconfig.asan
 ac_add_options --disable-elf-hack
 
 # Package js shell.
 export MOZ_PACKAGE_JSSHELL=1
 
-# Enable Telemetry
-export MOZ_TELEMETRY_REPORTING=1
-
 # Need this to prevent name conflicts with the normal nightly build packages
 export MOZ_PKG_SPECIAL=asan
 
 . "$topsrcdir/build/mozconfig.common.override"
--- a/browser/config/mozconfigs/linux64-aarch64/common-opt
+++ b/browser/config/mozconfigs/linux64-aarch64/common-opt
@@ -10,14 +10,12 @@ ac_add_options --with-mozilla-api-keyfil
 . $topsrcdir/build/unix/mozconfig.linux
 
 unset NASM
 ac_add_options --target=aarch64
 
 # Needed to enable breakpad in application.ini
 export MOZILLA_OFFICIAL=1
 
-export MOZ_TELEMETRY_REPORTING=1
-
 # Package js shell.
 export MOZ_PACKAGE_JSSHELL=1
 
 unset MOZ_STDCXX_COMPAT
--- a/browser/config/mozconfigs/linux64/common-opt
+++ b/browser/config/mozconfigs/linux64/common-opt
@@ -7,12 +7,10 @@ ac_add_options --with-google-location-se
 ac_add_options --with-google-safebrowsing-api-keyfile=/builds/sb-gapi.data
 ac_add_options --with-mozilla-api-keyfile=/builds/mozilla-desktop-geoloc-api.key
 
 . $topsrcdir/build/unix/mozconfig.linux
 
 # Needed to enable breakpad in application.ini
 export MOZILLA_OFFICIAL=1
 
-export MOZ_TELEMETRY_REPORTING=1
-
 # Package js shell.
 export MOZ_PACKAGE_JSSHELL=1
--- a/browser/config/mozconfigs/linux64/debug
+++ b/browser/config/mozconfigs/linux64/debug
@@ -4,17 +4,14 @@ ac_add_options --enable-debug
 
 MOZ_AUTOMATION_L10N_CHECK=0
 
 . $topsrcdir/build/unix/mozconfig.linux
 
 # Needed to enable breakpad in application.ini
 export MOZILLA_OFFICIAL=1
 
-# Enable Telemetry
-export MOZ_TELEMETRY_REPORTING=1
-
 # Package js shell.
 export MOZ_PACKAGE_JSSHELL=1
 
 ac_add_options --with-branding=browser/branding/nightly
 
 . "$topsrcdir/build/mozconfig.common.override"
--- a/browser/config/mozconfigs/linux64/debug-asan
+++ b/browser/config/mozconfigs/linux64/debug-asan
@@ -6,18 +6,15 @@ ac_add_options --enable-optimize="-O1"
 . $topsrcdir/build/mozconfig.stylo
 
 # ASan specific options on Linux
 ac_add_options --enable-valgrind
 
 . $topsrcdir/build/unix/mozconfig.asan
 ac_add_options --disable-elf-hack
 
-# Enable Telemetry
-export MOZ_TELEMETRY_REPORTING=1
-
 # Package js shell.
 export MOZ_PACKAGE_JSSHELL=1
 
 # Need this to prevent name conflicts with the normal nightly build packages
 export MOZ_PKG_SPECIAL=asan
 
 . "$topsrcdir/build/mozconfig.common.override"
--- a/browser/config/mozconfigs/linux64/l10n-mozconfig
+++ b/browser/config/mozconfigs/linux64/l10n-mozconfig
@@ -2,16 +2,13 @@ ac_add_options --with-l10n-base=../../l1
 ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL}
 ac_add_options --with-branding=browser/branding/nightly
 
 . "$topsrcdir/build/mozconfig.no-compile"
 ac_add_options --disable-nodejs
 
 export MOZILLA_OFFICIAL=1
 
-# Enable Telemetry
-export MOZ_TELEMETRY_REPORTING=1
-
 # Don't autoclobber l10n, as this can lead to missing binaries and broken builds
 # Bug 1283438
 mk_add_options AUTOCLOBBER=
 
 . "$topsrcdir/build/mozconfig.common.override"
--- a/browser/config/mozconfigs/linux64/l10n-mozconfig-devedition
+++ b/browser/config/mozconfigs/linux64/l10n-mozconfig-devedition
@@ -2,19 +2,16 @@ ac_add_options --with-l10n-base=../../l1
 ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL}
 ac_add_options --with-branding=browser/branding/aurora
 
 . "$topsrcdir/build/mozconfig.no-compile"
 ac_add_options --disable-nodejs
 
 export MOZILLA_OFFICIAL=1
 
-# Enable Telemetry
-export MOZ_TELEMETRY_REPORTING=1
-
 # Don't autoclobber l10n, as this can lead to missing binaries and broken builds
 # Bug 1283438
 mk_add_options AUTOCLOBBER=
 
 # Enable MOZ_ALLOW_LEGACY_EXTENSIONS
 ac_add_options "MOZ_ALLOW_LEGACY_EXTENSIONS=1"
 
 . "$topsrcdir/build/mozconfig.common.override"
--- a/browser/config/mozconfigs/linux64/nightly-asan
+++ b/browser/config/mozconfigs/linux64/nightly-asan
@@ -15,9 +15,12 @@ ac_add_options --disable-elf-hack
 ac_add_options --enable-undefined-sanitizer
 
 # Package js shell.
 export MOZ_PACKAGE_JSSHELL=1
 
 # Need this to prevent name conflicts with the normal nightly build packages
 export MOZ_PKG_SPECIAL=asan
 
+# Disable telemetry
+ac_add_options MOZ_TELEMETRY_REPORTING=
+
 . "$topsrcdir/build/mozconfig.common.override"
--- a/browser/config/mozconfigs/linux64/nightly-asan-reporter
+++ b/browser/config/mozconfigs/linux64/nightly-asan-reporter
@@ -17,15 +17,9 @@ ac_add_options --disable-elf-hack
 ac_add_options --enable-address-sanitizer-reporter
 
 # Need this to prevent name conflicts with the normal nightly build packages
 export MOZ_PKG_SPECIAL=asan-reporter
 
 # Need this to add source information into platform.ini
 export MOZILLA_OFFICIAL=1
 
-# Enable Telemetry
-# The channel reported by Telemetry here will be "nightly-asan" as specified
-# in the respective override pref (toolkit.telemetry.overrideUpdateChannel),
-# while the build otherwise identifies as "nightly" to receive its updates.
-export MOZ_TELEMETRY_REPORTING=1
-
 . "$topsrcdir/build/mozconfig.common.override"
--- a/browser/config/mozconfigs/macosx64/common-opt
+++ b/browser/config/mozconfigs/macosx64/common-opt
@@ -5,12 +5,10 @@
 ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL}
 ac_add_options --with-google-location-service-api-keyfile=/builds/gls-gapi.data
 ac_add_options --with-google-safebrowsing-api-keyfile=/builds/sb-gapi.data
 ac_add_options --with-mozilla-api-keyfile=/builds/mozilla-desktop-geoloc-api.key
 
 # Needed to enable breakpad in application.ini
 export MOZILLA_OFFICIAL=1
 
-export MOZ_TELEMETRY_REPORTING=1
-
 # Package js shell.
 export MOZ_PACKAGE_JSSHELL=1
--- a/browser/config/mozconfigs/macosx64/debug
+++ b/browser/config/mozconfigs/macosx64/debug
@@ -1,16 +1,13 @@
 . $topsrcdir/build/macosx/mozconfig.common
 
 ac_add_options --enable-debug
 
 # Needed to enable breakpad in application.ini
 export MOZILLA_OFFICIAL=1
 
-# Enable Telemetry
-export MOZ_TELEMETRY_REPORTING=1
-
 # Package js shell.
 export MOZ_PACKAGE_JSSHELL=1
 
 ac_add_options --with-branding=browser/branding/nightly
 
 . "$topsrcdir/build/mozconfig.common.override"
--- a/browser/config/mozconfigs/macosx64/debug-asan
+++ b/browser/config/mozconfigs/macosx64/debug-asan
@@ -1,19 +1,16 @@
 # Use at least -O1 for optimization to avoid stack space
 # exhaustions caused by Clang function inlining.
 ac_add_options --enable-application=browser
 ac_add_options --enable-debug
 ac_add_options --enable-optimize="-O1"
 
 . $topsrcdir/build/unix/mozconfig.asan
 
-# Enable Telemetry
-export MOZ_TELEMETRY_REPORTING=1
-
 # Package js shell.
 export MOZ_PACKAGE_JSSHELL=1
 
 # Need this to prevent name conflicts with the normal nightly build packages
 # Before mozconfig.common so we can test for asan builds there
 export MOZ_PKG_SPECIAL=asan
 
 . "$topsrcdir/build/macosx/mozconfig.common"
--- a/browser/config/mozconfigs/macosx64/l10n-mozconfig
+++ b/browser/config/mozconfigs/macosx64/l10n-mozconfig
@@ -13,16 +13,13 @@ ac_add_options --with-l10n-base=../../l1
 ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL}
 ac_add_options --with-branding=browser/branding/nightly
 
 ac_add_options --disable-nodejs
 unset NODEJS
 
 export MOZILLA_OFFICIAL=1
 
-# Enable Telemetry
-export MOZ_TELEMETRY_REPORTING=1
-
 # Don't autoclobber l10n, as this can lead to missing binaries and broken builds
 # Bug 1283438
 mk_add_options AUTOCLOBBER=
 
 . "$topsrcdir/build/mozconfig.common.override"
--- a/browser/config/mozconfigs/macosx64/l10n-mozconfig-devedition
+++ b/browser/config/mozconfigs/macosx64/l10n-mozconfig-devedition
@@ -17,19 +17,16 @@ ac_add_options --disable-nodejs
 unset NODEJS
 
 if test "${MOZ_UPDATE_CHANNEL}" = "nightly"; then
 ac_add_options --with-macbundlename-prefix=Firefox
 fi
 
 export MOZILLA_OFFICIAL=1
 
-# Enable Telemetry
-export MOZ_TELEMETRY_REPORTING=1
-
 # Don't autoclobber l10n, as this can lead to missing binaries and broken builds
 # Bug 1283438
 mk_add_options AUTOCLOBBER=
 
 # Enable MOZ_ALLOW_LEGACY_EXTENSIONS
 ac_add_options "MOZ_ALLOW_LEGACY_EXTENSIONS=1"
 
 . "$topsrcdir/build/mozconfig.common.override"
--- a/browser/config/mozconfigs/macosx64/nightly-asan
+++ b/browser/config/mozconfigs/macosx64/nightly-asan
@@ -1,18 +1,15 @@
 ac_add_options --enable-application=browser
 # We still need to build with debug symbols
 ac_add_options --disable-debug
 ac_add_options --enable-optimize="-O2"
 
 . $topsrcdir/build/unix/mozconfig.asan
 
-# Enable Telemetry
-export MOZ_TELEMETRY_REPORTING=1
-
 # Package js shell.
 export MOZ_PACKAGE_JSSHELL=1
 
 # Need this to prevent name conflicts with the normal nightly build packages
 # Before mozconfig.common so we can test for asan builds there
 export MOZ_PKG_SPECIAL=asan
 
 . "$topsrcdir/build/macosx/mozconfig.common"
--- a/browser/config/mozconfigs/whitelist
+++ b/browser/config/mozconfigs/whitelist
@@ -30,20 +30,18 @@ for platform in all_platforms:
         'ac_add_options --enable-official-branding',
         'export BUILDING_RELEASE=1',
     ]
 whitelist['release']['win32'] += ['export MOZ_PGO=1']
 whitelist['release']['win64'] += ['export MOZ_PGO=1']
 
 whitelist['release']['linux32'] += [
     'export MOZILLA_OFFICIAL=1',
-    'export MOZ_TELEMETRY_REPORTING=1',
     'export MOZ_PGO=1',
 ]
 whitelist['release']['linux64'] += [
     'export MOZILLA_OFFICIAL=1',
-    'export MOZ_TELEMETRY_REPORTING=1',
     '. "$topsrcdir/build/mozconfig.pgo"',
 ]
 
 if __name__ == '__main__':
     import pprint
     pprint.pprint(whitelist)
--- a/browser/config/mozconfigs/win32/common-opt
+++ b/browser/config/mozconfigs/win32/common-opt
@@ -9,14 +9,12 @@ ac_add_options --enable-update-channel=$
 ac_add_options --with-google-location-service-api-keyfile=${WORKSPACE}/gls-gapi.data
 ac_add_options --with-google-safebrowsing-api-keyfile=${WORKSPACE}/sb-gapi.data
 
 ac_add_options --with-mozilla-api-keyfile=${WORKSPACE}/mozilla-desktop-geoloc-api.key
 
 # Needed to enable breakpad in application.ini
 export MOZILLA_OFFICIAL=1
 
-export MOZ_TELEMETRY_REPORTING=1
-
 . $topsrcdir/build/win32/mozconfig.vs-latest
 
 # Package js shell.
 export MOZ_PACKAGE_JSSHELL=1
--- a/browser/config/mozconfigs/win32/debug
+++ b/browser/config/mozconfigs/win32/debug
@@ -4,19 +4,16 @@ MOZ_AUTOMATION_L10N_CHECK=0
 
 . "$topsrcdir/build/mozconfig.stylo"
 
 ac_add_options --enable-debug
 
 # Needed to enable breakpad in application.ini
 export MOZILLA_OFFICIAL=1
 
-# Enable Telemetry
-export MOZ_TELEMETRY_REPORTING=1
-
 . $topsrcdir/build/win32/mozconfig.vs-latest
 
 # Package js shell.
 export MOZ_PACKAGE_JSSHELL=1
 
 ac_add_options --with-branding=browser/branding/nightly
 
 . "$topsrcdir/build/mozconfig.common.override"
--- a/browser/config/mozconfigs/win32/l10n-mozconfig
+++ b/browser/config/mozconfigs/win32/l10n-mozconfig
@@ -7,16 +7,13 @@ ac_add_options --enable-update-channel=$
 ac_add_options --with-l10n-base=../../l10n
 ac_add_options --with-branding=browser/branding/nightly
 
 ac_add_options --disable-nodejs
 unset NODEJS
 
 export MOZILLA_OFFICIAL=1
 
-# Enable Telemetry
-export MOZ_TELEMETRY_REPORTING=1
-
 # Don't autoclobber l10n, as this can lead to missing binaries and broken builds
 # Bug 1283438
 mk_add_options AUTOCLOBBER=
 
 . "$topsrcdir/build/mozconfig.common.override"
--- a/browser/config/mozconfigs/win32/l10n-mozconfig-devedition
+++ b/browser/config/mozconfigs/win32/l10n-mozconfig-devedition
@@ -7,19 +7,16 @@ ac_add_options --enable-update-channel=$
 ac_add_options --with-l10n-base=../../l10n
 ac_add_options --with-branding=browser/branding/aurora
 
 ac_add_options --disable-nodejs
 unset NODEJS
 
 export MOZILLA_OFFICIAL=1
 
-# Enable Telemetry
-export MOZ_TELEMETRY_REPORTING=1
-
 # Don't autoclobber l10n, as this can lead to missing binaries and broken builds
 # Bug 1283438
 mk_add_options AUTOCLOBBER=
 
 # Enable MOZ_ALLOW_LEGACY_EXTENSIONS
 ac_add_options "MOZ_ALLOW_LEGACY_EXTENSIONS=1"
 
 . "$topsrcdir/build/mozconfig.common.override"
--- a/browser/config/mozconfigs/win64-aarch64/common-opt
+++ b/browser/config/mozconfigs/win64-aarch64/common-opt
@@ -9,18 +9,16 @@ ac_add_options --enable-update-channel=$
 ac_add_options --with-google-location-service-api-keyfile=${WORKSPACE}/gls-gapi.data
 ac_add_options --with-google-safebrowsing-api-keyfile=${WORKSPACE}/sb-gapi.data
 
 ac_add_options --with-mozilla-api-keyfile=${WORKSPACE}/mozilla-desktop-geoloc-api.key
 
 # Needed to enable breakpad in application.ini
 export MOZILLA_OFFICIAL=1
 
-export MOZ_TELEMETRY_REPORTING=1
-
 . $topsrcdir/build/win64-aarch64/mozconfig.vs-latest
 
 # Package js shell.
 export MOZ_PACKAGE_JSSHELL=1
 
 if test -n "$MOZ_ARTIFACT_TASK_WIN32_OPT"; then
   ac_add_options --enable-eme=widevine
 fi
--- a/browser/config/mozconfigs/win64-aarch64/debug
+++ b/browser/config/mozconfigs/win64-aarch64/debug
@@ -6,19 +6,16 @@ MOZ_AUTOMATION_L10N_CHECK=0
 
 . "$topsrcdir/build/mozconfig.stylo"
 
 ac_add_options --enable-debug
 
 # Needed to enable breakpad in application.ini
 export MOZILLA_OFFICIAL=1
 
-# Enable Telemetry
-export MOZ_TELEMETRY_REPORTING=1
-
 # Package js shell.
 export MOZ_PACKAGE_JSSHELL=1
 
 ac_add_options --with-branding=browser/branding/nightly
 
 . $topsrcdir/build/win64-aarch64/mozconfig.vs-latest
 
 unset ENABLE_CLANG_PLUGIN
--- a/browser/config/mozconfigs/win64-aarch64/l10n-mozconfig
+++ b/browser/config/mozconfigs/win64-aarch64/l10n-mozconfig
@@ -6,16 +6,13 @@ ac_add_options --enable-update-channel="
 ac_add_options --with-l10n-base=../../l10n
 ac_add_options --with-branding=browser/branding/nightly
 
 ac_add_options --disable-nodejs
 unset NODEJS
 
 export MOZILLA_OFFICIAL=1
 
-# Enable Telemetry
-export MOZ_TELEMETRY_REPORTING=1
-
 # Don't autoclobber l10n, as this can lead to missing binaries and broken builds
 # Bug 1283438
 mk_add_options AUTOCLOBBER=
 
 . "$topsrcdir/build/mozconfig.common.override"
--- a/browser/config/mozconfigs/win64-aarch64/l10n-mozconfig-devedition
+++ b/browser/config/mozconfigs/win64-aarch64/l10n-mozconfig-devedition
@@ -6,19 +6,16 @@ ac_add_options --enable-update-channel="
 ac_add_options --with-l10n-base=../../l10n
 ac_add_options --with-branding=browser/branding/aurora
 
 ac_add_options --disable-nodejs
 unset NODEJS
 
 export MOZILLA_OFFICIAL=1
 
-# Enable Telemetry
-export MOZ_TELEMETRY_REPORTING=1
-
 # Don't autoclobber l10n, as this can lead to missing binaries and broken builds
 # Bug 1283438
 mk_add_options AUTOCLOBBER=
 
 # Enable MOZ_ALLOW_LEGACY_EXTENSIONS
 ac_add_options "MOZ_ALLOW_LEGACY_EXTENSIONS=1"
 
 . "$topsrcdir/build/mozconfig.common.override"
--- a/browser/config/mozconfigs/win64/code-coverage
+++ b/browser/config/mozconfigs/win64/code-coverage
@@ -8,19 +8,16 @@ ac_add_options --enable-debug
 ac_add_options --disable-sandbox
 ac_add_options --disable-warnings-as-errors
 ac_add_options --enable-coverage
 ac_add_options --enable-rust-tests
 
 # Needed to enable breakpad in application.ini
 export MOZILLA_OFFICIAL=1
 
-# Enable Telemetry
-export MOZ_TELEMETRY_REPORTING=1
-
 . $topsrcdir/build/win64/mozconfig.vs-latest
 
 # Package js shell.
 export MOZ_PACKAGE_JSSHELL=1
 
 if [ -d "$topsrcdir/clang" ]; then
     CLANG_LIB_DIR="$(cd $topsrcdir/clang/lib/clang/* && cd lib/windows && pwd)"
 
--- a/browser/config/mozconfigs/win64/common-opt
+++ b/browser/config/mozconfigs/win64/common-opt
@@ -9,18 +9,16 @@ ac_add_options --enable-update-channel=$
 ac_add_options --with-google-location-service-api-keyfile=${WORKSPACE}/gls-gapi.data
 ac_add_options --with-google-safebrowsing-api-keyfile=${WORKSPACE}/sb-gapi.data
 
 ac_add_options --with-mozilla-api-keyfile=${WORKSPACE}/mozilla-desktop-geoloc-api.key
 
 # Needed to enable breakpad in application.ini
 export MOZILLA_OFFICIAL=1
 
-export MOZ_TELEMETRY_REPORTING=1
-
 . $topsrcdir/build/win64/mozconfig.vs-latest
 
 # Package js shell.
 export MOZ_PACKAGE_JSSHELL=1
 
 if [ -n "$MOZ_PGO" ]; then
     RUSTFLAGS="-Clinker-plugin-lto"
 fi
--- a/browser/config/mozconfigs/win64/debug
+++ b/browser/config/mozconfigs/win64/debug
@@ -5,19 +5,16 @@ MOZ_AUTOMATION_L10N_CHECK=0
 
 . "$topsrcdir/build/mozconfig.stylo"
 
 ac_add_options --enable-debug
 
 # Needed to enable breakpad in application.ini
 export MOZILLA_OFFICIAL=1
 
-# Enable Telemetry
-export MOZ_TELEMETRY_REPORTING=1
-
 # Package js shell.
 export MOZ_PACKAGE_JSSHELL=1
 
 ac_add_options --with-branding=browser/branding/nightly
 
 . $topsrcdir/build/win64/mozconfig.vs-latest
 
 . "$topsrcdir/build/mozconfig.common.override"
--- a/browser/config/mozconfigs/win64/debug-asan
+++ b/browser/config/mozconfigs/win64/debug-asan
@@ -8,9 +8,12 @@ ac_add_options --enable-optimize="-O1"
 
 . "$topsrcdir/build/win64/mozconfig.vs-latest"
 
 . "$topsrcdir/build/win64/mozconfig.asan"
 
 export MOZ_PACKAGE_JSSHELL=1
 export MOZ_PKG_SPECIAL=asan
 
+# Disable telemetry
+ac_add_options MOZ_TELEMETRY_REPORTING=
+
 . "$topsrcdir/build/mozconfig.common.override"
--- a/browser/config/mozconfigs/win64/debug-fuzzing
+++ b/browser/config/mozconfigs/win64/debug-fuzzing
@@ -1,11 +1,11 @@
 . "$topsrcdir/browser/config/mozconfigs/win64/debug"
 
 # Disable telemetry. All network activity is undesirable in fuzzing.
-unset MOZ_TELEMETRY_REPORTING
+ac_add_options MOZ_TELEMETRY_REPORTING=
 
 ac_add_options --enable-fuzzing
 
 # Need this to prevent name conflicts with the normal nightly build packages
 export MOZ_PKG_SPECIAL=fuzzing
 
 . "$topsrcdir/build/mozconfig.common.override"
--- a/browser/config/mozconfigs/win64/l10n-mozconfig
+++ b/browser/config/mozconfigs/win64/l10n-mozconfig
@@ -6,16 +6,13 @@ ac_add_options --enable-update-channel=$
 ac_add_options --with-l10n-base=../../l10n
 ac_add_options --with-branding=browser/branding/nightly
 
 ac_add_options --disable-nodejs
 unset NODEJS
 
 export MOZILLA_OFFICIAL=1
 
-# Enable Telemetry
-export MOZ_TELEMETRY_REPORTING=1
-
 # Don't autoclobber l10n, as this can lead to missing binaries and broken builds
 # Bug 1283438
 mk_add_options AUTOCLOBBER=
 
 . "$topsrcdir/build/mozconfig.common.override"
--- a/browser/config/mozconfigs/win64/l10n-mozconfig-devedition
+++ b/browser/config/mozconfigs/win64/l10n-mozconfig-devedition
@@ -6,19 +6,16 @@ ac_add_options --enable-update-channel=$
 ac_add_options --with-l10n-base=../../l10n
 ac_add_options --with-branding=browser/branding/aurora
 
 ac_add_options --disable-nodejs
 unset NODEJS
 
 export MOZILLA_OFFICIAL=1
 
-# Enable Telemetry
-export MOZ_TELEMETRY_REPORTING=1
-
 # Don't autoclobber l10n, as this can lead to missing binaries and broken builds
 # Bug 1283438
 mk_add_options AUTOCLOBBER=
 
 # Enable MOZ_ALLOW_LEGACY_EXTENSIONS
 ac_add_options "MOZ_ALLOW_LEGACY_EXTENSIONS=1"
 
 . "$topsrcdir/build/mozconfig.common.override"
--- a/browser/config/mozconfigs/win64/nightly-asan
+++ b/browser/config/mozconfigs/win64/nightly-asan
@@ -8,9 +8,12 @@ ac_add_options --enable-optimize="-O2 -g
 
 . "$topsrcdir/build/win64/mozconfig.vs-latest"
 
 . "$topsrcdir/build/win64/mozconfig.asan"
 
 export MOZ_PACKAGE_JSSHELL=1
 export MOZ_PKG_SPECIAL=asan
 
+# Disable telemetry
+ac_add_options MOZ_TELEMETRY_REPORTING=
+
 . "$topsrcdir/build/mozconfig.common.override"
--- a/browser/config/mozconfigs/win64/nightly-asan-reporter
+++ b/browser/config/mozconfigs/win64/nightly-asan-reporter
@@ -19,15 +19,9 @@ ac_add_options --enable-address-sanitize
 export MOZ_PKG_SPECIAL=asan-reporter
 
 # Need this to add source information into platform.ini
 export MOZILLA_OFFICIAL=1
 
 # Sandboxing is currently not compatible with the way the ASan reporter works
 ac_add_options --disable-sandbox
 
-# Enable Telemetry
-# The channel reported by Telemetry here will be "nightly-asan" as specified
-# in the respective override pref (toolkit.telemetry.overrideUpdateChannel),
-# while the build otherwise identifies as "nightly" to receive its updates.
-export MOZ_TELEMETRY_REPORTING=1
-
 . "$topsrcdir/build/mozconfig.common.override"
--- a/browser/config/mozconfigs/win64/nightly-fuzzing-asan
+++ b/browser/config/mozconfigs/win64/nightly-fuzzing-asan
@@ -1,12 +1,12 @@
 . "$topsrcdir/browser/config/mozconfigs/win64/nightly-asan"
 
 # Disable telemetry
-unset MOZ_TELEMETRY_REPORTING
+ac_add_options MOZ_TELEMETRY_REPORTING=
 
 ac_add_options --disable-crashreporter
 
 ac_add_options --enable-fuzzing
 
 # Need this to prevent name conflicts with the normal nightly build packages
 export MOZ_PKG_SPECIAL=asan-fuzzing
 
--- a/browser/docs/AddressBar.rst
+++ b/browser/docs/AddressBar.rst
@@ -149,96 +149,96 @@ implementation details may vary deeply a
 .. note::
 
   Internal providers can access the Places database through the
   *PlacesUtils.promiseLargeCacheDBConnection* utility.
 
 .. highlight:: JavaScript
 .. code::
 
-class UrlbarProvider {
-  /**
-   * Unique name for the provider, used by the context to filter on providers.
-   * Not using a unique name will cause the newest registration to win.
-   * @abstract
-   */
-  get name() {
-    return "UrlbarProviderBase";
-  }
-  /**
-   * The type of the provider, must be one of UrlbarUtils.PROVIDER_TYPE.
-   * @abstract
-   */
-  get type() {
-    throw new Error("Trying to access the base class, must be overridden");
+  class UrlbarProvider {
+    /**
+    * Unique name for the provider, used by the context to filter on providers.
+    * Not using a unique name will cause the newest registration to win.
+    * @abstract
+    */
+    get name() {
+      return "UrlbarProviderBase";
+    }
+    /**
+    * The type of the provider, must be one of UrlbarUtils.PROVIDER_TYPE.
+    * @abstract
+    */
+    get type() {
+      throw new Error("Trying to access the base class, must be overridden");
+    }
+    /**
+    * List of UrlbarUtils.RESULT_SOURCE, representing the data sources used by
+    * the provider.
+    * @abstract
+    */
+    get sources() {
+      throw new Error("Trying to access the base class, must be overridden");
+    }
+    /**
+    * Starts querying.
+    * @param {UrlbarQueryContext} queryContext The query context object
+    * @param {function} addCallback Callback invoked by the provider to add a new
+    *        result. A UrlbarResult should be passed to it.
+    * @note Extended classes should return a Promise resolved when the provider
+    *       is done searching AND returning results.
+    * @abstract
+    */
+    startQuery(queryContext, addCallback) {
+      throw new Error("Trying to access the base class, must be overridden");
+    }
+    /**
+    * Cancels a running query,
+    * @param {UrlbarQueryContext} queryContext The query context object to cancel
+    *        query for.
+    * @abstract
+    */
+    cancelQuery(queryContext) {
+      throw new Error("Trying to access the base class, must be overridden");
+    }
   }
-  /**
-   * List of UrlbarUtils.RESULT_SOURCE, representing the data sources used by
-   * the provider.
-   * @abstract
-   */
-  get sources() {
-    throw new Error("Trying to access the base class, must be overridden");
-  }
-  /**
-   * Starts querying.
-   * @param {UrlbarQueryContext} queryContext The query context object
-   * @param {function} addCallback Callback invoked by the provider to add a new
-   *        result. A UrlbarResult should be passed to it.
-   * @note Extended classes should return a Promise resolved when the provider
-   *       is done searching AND returning results.
-   * @abstract
-   */
-  startQuery(queryContext, addCallback) {
-    throw new Error("Trying to access the base class, must be overridden");
-  }
-  /**
-   * Cancels a running query,
-   * @param {UrlbarQueryContext} queryContext The query context object to cancel
-   *        query for.
-   * @abstract
-   */
-  cancelQuery(queryContext) {
-    throw new Error("Trying to access the base class, must be overridden");
-  }
-}
 
 UrlbarMuxer
 -----------
 
 The *Muxer* is responsible for sorting results based on their importance and
 additional rules that depend on the UrlbarQueryContext. The muxer to use is
 indicated by the UrlbarQueryContext.muxer property.
 
 .. caution::
 
   The Muxer is a replaceable component, as such what is described here is a
   reference for the default View, but may not be valid for other implementations.
 
 .. highlight:: JavaScript
 .. code::
 
-class UrlbarMuxer {
-  /**
-   * Unique name for the muxer, used by the context to sort results.
-   * Not using a unique name will cause the newest registration to win.
-   * @abstract
-   */
-  get name() {
-    return "UrlbarMuxerBase";
+  class UrlbarMuxer {
+    /**
+    * Unique name for the muxer, used by the context to sort results.
+    * Not using a unique name will cause the newest registration to win.
+    * @abstract
+    */
+    get name() {
+      return "UrlbarMuxerBase";
+    }
+    /**
+    * Sorts UrlbarQueryContext results in-place.
+    * @param {UrlbarQueryContext} queryContext the context to sort results for.
+    * @abstract
+    */
+    sort(queryContext) {
+      throw new Error("Trying to access the base class, must be overridden");
+    }
   }
-  /**
-   * Sorts UrlbarQueryContext results in-place.
-   * @param {UrlbarQueryContext} queryContext the context to sort results for.
-   * @abstract
-   */
-  sort(queryContext) {
-    throw new Error("Trying to access the base class, must be overridden");
-  }
-}
 
 
 The Controller
 ==============
 
 `UrlbarController <https://dxr.mozilla.org/mozilla-central/source/browser/components/urlbar/UrlbarController.jsm>`_
 is the component responsible for reacting to user's input, by communicating
 proper course of action to the Model (e.g. starting/stopping a query) and the
@@ -340,17 +340,18 @@ Represents the base *View* implementatio
     open();
     close();
     // Invoked when the query starts.
     onQueryStarted(queryContext);
     // Invoked when new results are available.
     onQueryResults(queryContext);
     // Invoked when the query has been canceled.
     onQueryCancelled(queryContext);
-    // Invoked when the query is done.
+    // Invoked when the query is done. This is invoked in any case, even if the
+    // query was canceled earlier.
     onQueryFinished(queryContext);
     // Invoked when the view context changed, so that cached information about
     // the latest search is no more relevant and can be dropped.
     onViewContextChanged();
   }
 
 
 UrlbarResult
--- a/browser/installer/windows/nsis/defines.nsi.in
+++ b/browser/installer/windows/nsis/defines.nsi.in
@@ -147,17 +147,16 @@ VIAddVersionKey "ProductVersion"  "${App
 # least 15 MB additional for working room.
 !define APPROXIMATE_REQUIRED_SPACE_MB "145"
 
 # Control positions in Dialog Units so they are placed correctly with
 # non-default DPI settings
 !define PROFILE_CLEANUP_LABEL_TOP_DU 39u
 !define NOW_INSTALLING_TOP_DU 70u
 !define INSTALL_BLURB_TOP_DU 137u
-!define INSTALL_FOOTER_TOP_DU -48u
 !define INSTALL_FOOTER_WIDTH_DU 250u
 !define PROGRESS_BAR_TOP_DU 112u
 !define APPNAME_BMP_EDGE_DU 19u
 !define APPNAME_BMP_TOP_DU 12u
 
 # Constants for parts of the telemetry submission URL
 !define TELEMETRY_BASE_URL https://incoming.telemetry.mozilla.org/submit
 !define TELEMETRY_NAMESPACE firefox-installer
--- a/browser/installer/windows/nsis/stub.nsi
+++ b/browser/installer/windows/nsis/stub.nsi
@@ -741,17 +741,17 @@ Function createProfileCleanup
        "$R2u" "$(STUB_BLURB_FOOTER2)"
   !else
     nsDialogs::CreateControl STATIC ${DEFAULT_STYLES}|${SS_NOTIFY}|${SS_RIGHT} \
       ${WS_EX_TRANSPARENT} 175u ${INSTALL_FOOTER_TOP_DU} ${INSTALL_FOOTER_WIDTH_DU} \
       "$R2u" "$(STUB_BLURB_FOOTER2)"
   !endif
   Pop $0
   SendMessage $0 ${WM_SETFONT} $FontFooter 0
-  SetCtlColors $0 ${INSTALL_BLURB_TEXT_COLOR} transparent
+  SetCtlColors $0 ${INSTALL_FOOTER_TEXT_COLOR} transparent
 
   Call DrawBackgroundImage
 
   LockWindow off
   nsDialogs::Show
 
   ${NSD_FreeImage} $BgBitmapImage
 FunctionEnd
@@ -813,17 +813,17 @@ Function createInstall
       "$(STUB_BLURB_FOOTER2)"
   !else
     nsDialogs::CreateControl STATIC ${DEFAULT_STYLES}|${SS_NOTIFY}|${SS_RIGHT} \
       ${WS_EX_TRANSPARENT} 175u ${INSTALL_FOOTER_TOP_DU} ${INSTALL_FOOTER_WIDTH_DU} "$R2u" \
       "$(STUB_BLURB_FOOTER2)"
   !endif
   Pop $0
   SendMessage $0 ${WM_SETFONT} $FontFooter 0
-  SetCtlColors $0 ${INSTALL_BLURB_TEXT_COLOR} transparent
+  SetCtlColors $0 ${INSTALL_FOOTER_TEXT_COLOR} transparent
 
   ${NSD_CreateProgressBar} 20% ${PROGRESS_BAR_TOP_DU} 60% 12u ""
   Pop $Progressbar
   ${NSD_AddStyle} $Progressbar ${PBS_MARQUEE}
   SendMessage $Progressbar ${PBM_SETMARQUEE} 1 \
               $ProgressbarMarqueeIntervalMS ; start=1|stop=0 interval(ms)=+N
 
   Call DrawBackgroundImage
--- a/browser/locales/jar.mn
+++ b/browser/locales/jar.mn
@@ -3,16 +3,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/.
 
 # Note: This file should only contain locale entries. All
 # override and resource entries should go to browser/base/jar.mn to avoid
 # having to create the same entry for each locale.
 
 [localization] @AB_CD@.jar:
+  browser/aboutLogins.ftl                          (../components/aboutlogins/content/aboutLogins.ftl)
   browser                                          (%browser/**/*.ftl)
 
 @AB_CD@.jar:
 % locale browser @AB_CD@ %locale/browser/
 # bookmarks.html is produced by LOCALIZED_GENERATED_FILES.
     locale/browser/bookmarks.html                  (bookmarks.html)
 
     locale/browser/accounts.properties             (%chrome/browser/accounts.properties)
--- a/browser/themes/shared/urlbar-searchbar.inc.css
+++ b/browser/themes/shared/urlbar-searchbar.inc.css
@@ -100,16 +100,22 @@
   /* Remove excess space between the address bar and the menu button in popups. */
   margin-inline-end: 0;
 }
 
 :root[customizing] .urlbar-input-box {
   visibility: hidden;
 }
 
+.urlbar-input {
+  /* Remove the 1px padding-left (and padding-right) so that the titles in the
+     urlbar popup align with the text in the input. */
+  padding: 0;
+}
+
 #urlbar-container {
   -moz-box-align: center;
 }
 
 #urlbar-search-splitter {
   /* The splitter width should equal the location and search bars' combined
      neighboring margin and border width. */
   min-width: 12px;
--- a/build/moz.configure/init.configure
+++ b/build/moz.configure/init.configure
@@ -1224,16 +1224,24 @@ set_config('MOZILLA_SYMBOLVERSION', mile
 # JS configure still wants to look at these.
 add_old_configure_assignment('MOZILLA_VERSION', milestone.version)
 add_old_configure_assignment('MOZILLA_SYMBOLVERSION', milestone.symbolversion)
 
 set_config('MOZ_APP_VERSION', milestone.app_version)
 set_config('MOZ_APP_VERSION_DISPLAY', milestone.app_version_display)
 add_old_configure_assignment('MOZ_APP_VERSION', milestone.app_version)
 
+
+# Dummy function for availability in toolkit/moz.configure. Overridden in
+# mobile/android/moz.configure.
+@depends(milestone.is_nightly)
+def fennec_nightly(is_nightly):
+    return is_nightly
+
+
 # The app update channel is 'default' when not supplied. The value is used in
 # the application's confvars.sh (and is made available to a project specific
 # moz.configure).
 option('--enable-update-channel',
        nargs=1,
        help='Select application update channel',
        default='default')
 
--- a/devtools/client/aboutdebugging-new/src/base.css
+++ b/devtools/client/aboutdebugging-new/src/base.css
@@ -417,15 +417,17 @@ Form controls
 
 /*
  * Card UI, from Photon
  */
 .card {
   background-color: var(--white-100); /* from common.inc.css */
   border-radius: var(--card-shadow-blur-radius); /* from common.inc.css */
   box-shadow: 0 1px 4px var(--grey-90-a10); /* from common.inc.css */
+  box-sizing: border-box;
+  min-width: min-content;
   padding-block: calc(var(--base-unit) * 5);
 }
 
 .card__heading {
   font-size: var(--title-20-font-size); /* Note: this is from Photon Title 20 */
   font-weight: var(--title-20-font-weight); /* Note: this is from Photon Title 20 */
 }
--- a/devtools/client/aboutdebugging-new/src/components/App.css
+++ b/devtools/client/aboutdebugging-new/src/components/App.css
@@ -15,17 +15,18 @@
  * about:preferences, which uses the shared common.css
  */
 
 .app {
   /* from common */
   --sidebar-width: 280px;
   --app-top-padding: 70px;
   --app-bottom-padding: 40px;
-  --app-left-padding: 34px;
+  --app-left-padding: 32px;
+  --app-right-padding: 32px;
 
   box-sizing: border-box;
   width: 100vw;
   height: 100vh;
   overflow: hidden; /* we don't want the sidebar to scroll, only the main content */
 
   display: grid;
   grid-column-gap: 40px;
@@ -52,11 +53,13 @@
   /* padding will give space for card shadow to appear and
      margin will correct the alignment */
   margin-inline-start: calc(var(--card-shadow-blur-radius) * -1);
   padding-inline: var(--card-shadow-blur-radius);
 }
 
 .page {
   max-width: var(--page-width);
+  min-width: min-content;
   font-size: var(--body-20-font-size);
   font-weight: var(--body-20-font-weight);
+  padding-inline-end: var(--app-right-padding);
 }
--- a/devtools/client/aboutdebugging-new/src/components/debugtarget/FieldPair.css
+++ b/devtools/client/aboutdebugging-new/src/components/debugtarget/FieldPair.css
@@ -1,14 +1,15 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 .fieldpair {
-  display: flex;
+  display: grid;
+  grid-template-columns: auto auto;
   border-top: 1px solid var(--grey-20);
   padding-block: calc(var(--base-unit) * 2);
   padding-inline: calc(var(--base-unit) * 4) calc(var(--base-unit) * 2);
 }
 
 .fieldpair:last-child {
   padding-block-end: 0;
 }
--- a/devtools/client/aboutdebugging-new/test/browser/browser_aboutdebugging_devtoolstoolbox_contextmenu_markupview.js
+++ b/devtools/client/aboutdebugging-new/test/browser/browser_aboutdebugging_devtoolstoolbox_contextmenu_markupview.js
@@ -10,28 +10,28 @@ Services.scriptloader.loadSubScript(CHRO
  * Test context menu of markup view on about:devtools-toolbox page.
  */
 add_task(async function() {
   info("Force all debug target panes to be expanded");
   prepareCollapsibilitiesTest();
 
   const { document, tab, window } = await openAboutDebugging();
   await selectThisFirefoxPage(document, window.AboutDebugging.store);
-  const { devtoolsDocument, devtoolsTab, devtoolsWindow } =
+  const { devtoolsTab, devtoolsWindow } =
     await openAboutDevtoolsToolbox(document, tab, window);
 
   info("Select inspector tool");
   const toolbox = getToolbox(devtoolsWindow);
   await toolbox.selectTool("inspector");
 
   info("Show context menu of markup view");
   const markupDocument = toolbox.getPanel("inspector").markup.doc;
   EventUtils.synthesizeMouseAtCenter(markupDocument.body,
                                      { type: "contextmenu" },
                                      markupDocument.ownerGlobal);
 
   info("Check whether proper context menu of markup view will be shown");
-  await waitUntil(() => devtoolsDocument.querySelector("#node-menu-edithtml"));
+  await waitUntil(() => toolbox.topDoc.querySelector("#node-menu-edithtml"));
   ok(true, "Context menu of markup view should be shown");
 
   await closeAboutDevtoolsToolbox(document, devtoolsTab, window);
   await removeTab(tab);
 });
--- a/devtools/client/accessibility/components/AccessibilityRow.js
+++ b/devtools/client/accessibility/components/AccessibilityRow.js
@@ -198,17 +198,17 @@ class AccessibilityRow extends Component
     if (supports.snapshot) {
       menu.append(new MenuItem({
         id: "menu-printtojson",
         label: L10N.getStr("accessibility.tree.menu.printToJSON"),
         click: () => this.printToJSON(),
       }));
     }
 
-    menu.popup(e.screenX, e.screenY, gToolbox);
+    menu.popup(e.screenX, e.screenY, gToolbox.doc);
 
     if (gTelemetry) {
       gTelemetry.scalarAdd(TELEMETRY_ACCESSIBLE_CONTEXT_MENU_OPENED, 1);
     }
   }
 
   /**
    * Render accessible row component.
--- a/devtools/client/accessibility/test/mochitest/test_accessible_row_context_menu.html
+++ b/devtools/client/accessibility/test/mochitest/test_accessible_row_context_menu.html
@@ -105,21 +105,23 @@ window.onload = async function() {
       ui: { supports: {}},
       ...auditState,
     };
 
     info("Check contextmenu default behaviour.");
     renderAccessibilityRow(defaultProps, defaultState);
     let row = document.getElementById(ROW_ID);
 
+    info("Get topmost document where the context meny will be rendered");
+    const menuDoc = document.defaultView.windowRoot.ownerGlobal.document;
     await withMockEnv(async function() {
       Simulate.contextMenu(row);
 
-      const menu = document.getElementById("accessibility-row-contextmenu");
-      const printtojsonMenuItem = document.getElementById("menu-printtojson");
+      const menu = menuDoc.getElementById("accessibility-row-contextmenu");
+      const printtojsonMenuItem = menuDoc.getElementById("menu-printtojson");
 
       ok(menu, "Accessibility row context menu is open");
       ok(printtojsonMenuItem, "Print to JSON menu item is visible");
 
       const browserWindow = Services.wm.getMostRecentWindow(gDevTools.chromeWindowType);
       const defaultOpenWebLinkIn = browserWindow.openWebLinkIn;
 
       let openWebLinkInCalled;
@@ -144,25 +146,25 @@ window.onload = async function() {
     });
 
     info("Check accessibility row when context menu is not supported.");
     renderAccessibilityRow(defaultProps, mockState);
     row = document.getElementById(ROW_ID);
 
     info("Check contextmenu listener is not called when context menu is not supported.");
     Simulate.contextMenu(row);
-    let menu = document.getElementById("accessibility-row-contextmenu");
+    let menu = menuDoc.getElementById("accessibility-row-contextmenu");
     ok(!menu, "contextmenu event handler was never called.");
 
     info("Check accessibility row when no context menu is available.");
     renderAccessibilityRow(mockProps, defaultState);
     row = document.getElementById(ROW_ID);
 
     Simulate.contextMenu(row);
-    menu = document.getElementById("accessibility-row-contextmenu");
+    menu = menuDoc.getElementById("accessibility-row-contextmenu");
     ok(!menu, "contextmenu event handler was never called.");
   } catch (e) {
     ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e));
   } finally {
     SimpleTest.finish();
   }
 };
 </script>
--- a/devtools/client/debugger/bin/module-manifest.json
+++ b/devtools/client/debugger/bin/module-manifest.json
@@ -10,17 +10,17 @@
           "0": 0,
           "1": 1
         }
       },
       "chunks": {
         "byName": {},
         "byBlocks": {},
         "usedIds": {
-          "0": 0
+          "1": 1
         }
       }
     }
   ],
   "extract-text-webpack-plugin ../../extract-text-webpack-plugin/dist ../../css-loader/index.js??ref--3-1!../../postcss-loader/lib/index.js!../../react-aria-components/src/tabs/tab.css": [
     {
       "modules": {
         "byIdentifier": {
@@ -31,17 +31,17 @@
           "0": 0,
           "1": 1
         }
       },
       "chunks": {
         "byName": {},
         "byBlocks": {},
         "usedIds": {
-          "0": 0
+          "1": 1
         }
       }
     }
   ],
   "extract-text-webpack-plugin ../../extract-text-webpack-plugin/dist ../../css-loader/index.js??ref--3-1!../../postcss-loader/lib/index.js!../../react-aria-components/src/tabs/tab-list.css": [
     {
       "modules": {
         "byIdentifier": {
@@ -52,17 +52,17 @@
           "0": 0,
           "1": 1
         }
       },
       "chunks": {
         "byName": {},
         "byBlocks": {},
         "usedIds": {
-          "0": 0
+          "1": 1
         }
       }
     }
   ],
   "extract-text-webpack-plugin ../../extract-text-webpack-plugin/dist ../../css-loader/index.js??ref--3-1!../../postcss-loader/lib/index.js!../../devtools-contextmenu/menu.css": [
     {
       "modules": {
         "byIdentifier": {
@@ -73,17 +73,17 @@
           "0": 0,
           "1": 1
         }
       },
       "chunks": {
         "byName": {},
         "byBlocks": {},
         "usedIds": {
-          "0": 0
+          "1": 1
         }
       }
     }
   ],
   "extract-text-webpack-plugin ../../extract-text-webpack-plugin/dist ../../css-loader/index.js??ref--3-1!../../postcss-loader/lib/index.js!../../../packages/devtools-components/src/tree.css": [
     {
       "modules": {
         "byIdentifier": {
@@ -94,17 +94,17 @@
           "0": 0,
           "1": 1
         }
       },
       "chunks": {
         "byName": {},
         "byBlocks": {},
         "usedIds": {
-          "0": 0
+          "1": 1
         }
       }
     }
   ],
   "extract-text-webpack-plugin ../../extract-text-webpack-plugin/dist ../../css-loader/index.js??ref--3-1!../../postcss-loader/lib/index.js!../../../packages/devtools-reps/src/object-inspector/components/ObjectInspector.css": [
     {
       "modules": {
         "byIdentifier": {
@@ -115,17 +115,17 @@
           "0": 0,
           "1": 1
         }
       },
       "chunks": {
         "byName": {},
         "byBlocks": {},
         "usedIds": {
-          "0": 0
+          "1": 1
         }
       }
     }
   ],
   "extract-text-webpack-plugin ../../extract-text-webpack-plugin/dist ../../css-loader/index.js??ref--3-1!../../postcss-loader/lib/index.js!../../../packages/devtools-reps/src/reps/reps.css": [
     {
       "modules": {
         "byIdentifier": {
@@ -136,17 +136,17 @@
           "0": 0,
           "1": 1
         }
       },
       "chunks": {
         "byName": {},
         "byBlocks": {},
         "usedIds": {
-          "0": 0
+          "1": 1
         }
       }
     }
   ],
   "modules": {
     "byIdentifier": {
       "external \"devtools/client/shared/vendor/react-prop-types\"": 0,
       "external \"devtools/client/shared/vendor/react-dom-factories\"": 1,
--- a/devtools/client/debugger/dist/vendors.js
+++ b/devtools/client/debugger/dist/vendors.js
@@ -2605,22 +2605,22 @@ function showMenu(evt, items) {
   let menu = new Menu();
   items.filter(item => item.visible === undefined || item.visible === true).forEach(item => {
     let menuItem = new MenuItem(item);
     menuItem.submenu = createSubMenu(item.submenu);
     menu.append(menuItem);
   });
 
   if (inToolbox()) {
-    menu.popup(evt.screenX, evt.screenY, { doc: window.parent.document });
+    menu.popup(evt.screenX, evt.screenY, window.parent.document);
     return;
   }
 
   menu.on("open", (_, popup) => onShown(menu, popup));
-  menu.popup(evt.clientX, evt.clientY, { doc: document });
+  menu.popup(evt.clientX, evt.clientY, document);
 }
 
 function createSubMenu(subItems) {
   if (subItems) {
     let subMenu = new Menu();
     subItems.forEach(subItem => {
       subMenu.append(new MenuItem(subItem));
     });
@@ -2707,16 +2707,21 @@ function inToolbox() {
   try {
     return window.parent.document.documentURI == "about:devtools-toolbox";
   } catch (e) {
     // If `window` is not available, it's very likely that we are in the toolbox.
     return true;
   }
 }
 
+// Copied from m-c DevToolsUtils.
+function getTopWindow(win) {
+  return win.windowRoot ? win.windowRoot.ownerGlobal : win.top;
+}
+
 /**
  * A partial implementation of the Menu API provided by electron:
  * https://github.com/electron/electron/blob/master/docs/api/menu.md.
  *
  * Extra features:
  *  - Emits an 'open' and 'close' event when the menu is opened/closed
 
  * @param String id (non standard)
@@ -2758,21 +2763,26 @@ Menu.prototype.insert = function (pos, m
  * Show the Menu at a specified location on the screen
  *
  * Missing features:
  *   - browserWindow - BrowserWindow (optional) - Default is null.
  *   - positioningItem Number - (optional) OS X
  *
  * @param {int} screenX
  * @param {int} screenY
- * @param Toolbox toolbox (non standard)
- *        Needed so we in which window to inject XUL
+ * @param {Document} doc
+ *        The document that should own the context menu.
  */
-Menu.prototype.popup = function (screenX, screenY, toolbox) {
-  let doc = toolbox.doc;
+Menu.prototype.popup = function (screenX, screenY, doc) {
+  // The context-menu will be created in the topmost window to preserve keyboard
+  // navigation. See Bug 1543940. Keep a reference on the window owning the menu to hide
+  // the popup on unload.
+  const win = doc.defaultView;
+  doc = getTopWindow(doc.defaultView).document;
+
   let popupset = doc.querySelector("popupset");
   if (!popupset) {
     popupset = doc.createXULElement("popupset");
     doc.documentElement.appendChild(popupset);
   }
   // See bug 1285229, on Windows, opening the same popup multiple times in a
   // row ends up duplicating the popup. The newly inserted popup doesn't
   // dismiss the old one. So remove any previously displayed popup before
@@ -2785,19 +2795,25 @@ Menu.prototype.popup = function (screenX
   popup = this.createPopup(doc);
   popup.setAttribute("menu-api", "true");
 
   if (this.id) {
     popup.id = this.id;
   }
   this._createMenuItems(popup);
 
+  // The context menu will be created in the topmost chrome window. Hide it manually when
+  // the owner document is unloaded.
+  const onWindowUnload = () => popup.hidePopup();
+  win.addEventListener("unload", onWindowUnload);
+
   // Remove the menu from the DOM once it's hidden.
   popup.addEventListener("popuphidden", e => {
     if (e.target === popup) {
+      win.removeEventListener("unload", onWindowUnload);
       popup.remove();
       this.emit("close", popup);
     }
   });
 
   popup.addEventListener("popupshown", e => {
     if (e.target === popup) {
       this.emit("open", popup);
--- a/devtools/client/debugger/images/moz.build
+++ b/devtools/client/debugger/images/moz.build
@@ -41,10 +41,11 @@ DevToolsModules(
     'resume.svg',
     'rewind.svg',
     'search.svg',
     'stepIn.svg',
     'stepOut.svg',
     'stepOver.svg',
     'tab.svg',
     'whole-word-match.svg',
+    'window.svg',
     'worker.svg',
 )
new file mode 100644
--- /dev/null
+++ b/devtools/client/debugger/images/window.svg
@@ -0,0 +1,4 @@
+<!-- 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/. -->
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path fill="context-fill" d="M13 1H3a3 3 0 0 0-3 3v8a3 3 0 0 0 3 3h11a2 2 0 0 0 2-2V4a3 3 0 0 0-3-3zm1 11a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V6h12zm0-7H2V4a1 1 0 0 1 1-1h10a1 1 0 0 1 1 1z"></path></svg>
\ No newline at end of file
--- a/devtools/client/debugger/package.json
+++ b/devtools/client/debugger/package.json
@@ -48,17 +48,17 @@
   "dependencies": {
     "@babel/core": "^7.0.0-beta.55",
     "@babel/parser": "^7.0.0-beta.55",
     "@babel/template": "^7.0.0-beta.55",
     "@babel/types": "^7.0.0-beta.55",
     "babel-plugin-transform-imports": "^1.5.0",
     "codemirror": "^5.28.0",
     "devtools-environment": "^0.0.6",
-    "devtools-launchpad": "^0.0.151",
+    "devtools-launchpad": "^0.0.152",
     "devtools-linters": "^0.0.4",
     "devtools-reps": "0.23.0",
     "devtools-source-map": "0.16.0",
     "devtools-splitter": "^0.0.8",
     "devtools-utils": "0.0.14",
     "fuzzaldrin-plus": "^0.6.0",
     "immutable": "^3.8.2",
     "lodash": "^4.17.4",
--- a/devtools/client/debugger/packages/devtools-reps/package.json
+++ b/devtools/client/debugger/packages/devtools-reps/package.json
@@ -33,17 +33,17 @@
     "redux": "^3.7.2"
   },
   "devDependencies": {
     "@sucrase/webpack-object-rest-spread-plugin": "^1.0.0",
     "babel-plugin-syntax-object-rest-spread": "^6.13.0",
     "babel-plugin-transform-es2015-modules-commonjs": "^6.26.0",
     "babel-preset-react": "^6.24.1",
     "devtools-config": "^0.0.16",
-    "devtools-launchpad": "^0.0.150",
+    "devtools-launchpad": "^0.0.152",
     "devtools-license-check": "^0.7.0",
     "devtools-modules": "~1.1.0",
     "devtools-services": "^0.0.1",
     "enzyme": "^3.3.0",
     "enzyme-adapter-react-16": "^1.1.1",
     "enzyme-to-json": "^3.3.1",
     "eslint": "^5.0.0",
     "eslint-plugin-mozilla": "1.1.3",
--- a/devtools/client/debugger/packages/devtools-source-map/src/index.js
+++ b/devtools/client/debugger/packages/devtools-source-map/src/index.js
@@ -11,17 +11,17 @@ const {
 import type {
   OriginalFrame,
   Range,
   SourceLocation,
   Source,
   SourceId
 } from "../../../src/types";
 import type { SourceMapConsumer } from "source-map";
-import type { locationOptions } from "./source-map";
+import type { LocationOptions } from "./source-map";
 
 export const dispatcher = new WorkerDispatcher();
 
 const _getGeneratedRanges = dispatcher.task("getGeneratedRanges", {
   queue: true
 });
 
 const _getGeneratedLocation = dispatcher.task("getGeneratedLocation", {
@@ -75,22 +75,23 @@ export const getGeneratedLocation = asyn
 export const getAllGeneratedLocations = async (
   location: SourceLocation,
   originalSource: Source
 ): Promise<Array<SourceLocation>> =>
   _getAllGeneratedLocations(location, originalSource);
 
 export const getOriginalLocation = async (
   location: SourceLocation,
-  options: locationOptions = {}
+  options: LocationOptions = {}
 ): Promise<SourceLocation> => _getOriginalLocation(location, options);
 
 export const getOriginalLocations = async (
+  sourceId: SourceId,
   locations: SourceLocation[],
-  options: locationOptions = {}
+  options: LocationOptions = {}
 ): Promise<SourceLocation[]> =>
   dispatcher.invoke("getOriginalLocations", locations, options);
 
 export const getGeneratedRangesForOriginal = async (
   sourceId: SourceId,
   url: string,
   mergeUnmappedRegions?: boolean
 ): Promise<Range[]> =>
--- a/devtools/client/debugger/packages/devtools-source-map/src/source-map.js
+++ b/devtools/client/debugger/packages/devtools-source-map/src/source-map.js
@@ -41,17 +41,17 @@ type Range = {
     column: number
   },
   end: {
     line: number,
     column: number
   }
 };
 
-export type locationOptions = {
+export type LocationOptions = {
   search?: "LEAST_UPPER_BOUND" | "GREATEST_LOWER_BOUND"
 };
 
 async function getOriginalURLs(
   generatedSource: Source
 ): Promise<SourceMapConsumer> {
   const map = await fetchSourceMap(generatedSource);
   return map && map.sources;
@@ -256,38 +256,40 @@ async function getAllGeneratedLocations(
 
   return positions.map(({ line, column }) => ({
     sourceId: generatedSourceId,
     line,
     column
   }));
 }
 
-function getOriginalLocations(
+async function getOriginalLocations(
+  sourceId: string,
   locations: SourceLocation[],
-  options: locationOptions = {}
-) {
-  return Promise.all(
-    locations.map(location => getOriginalLocation(location, options))
+  options: LocationOptions = {}
+): Promise<SourceLocation[]> {
+  if (locations.some(location => location.sourceId != sourceId)) {
+    throw new Error("Generated locations must belong to the same source");
+  }
+
+  const map = await getSourceMap(sourceId);
+  if (!map) {
+    return locations;
+  }
+
+  return locations.map(location =>
+    getOriginalLocationSync(map, location, options)
   );
 }
 
-async function getOriginalLocation(
+function getOriginalLocationSync(
+  map,
   location: SourceLocation,
-  { search }: locationOptions = {}
-): Promise<SourceLocation> {
-  if (!isGeneratedId(location.sourceId)) {
-    return location;
-  }
-
-  const map = await getSourceMap(location.sourceId);
-  if (!map) {
-    return location;
-  }
-
+  { search }: LocationOptions = {}
+): SourceLocation {
   // First check for an exact match
   let match = map.originalPositionFor({
     line: location.line,
     column: location.column == null ? 0 : location.column
   });
 
   // If there is not an exact match, look for a match with a bias at the
   // current location and then on subsequent lines
@@ -316,16 +318,32 @@ async function getOriginalLocation(
   return {
     sourceId: generatedToOriginalId(location.sourceId, sourceUrl),
     sourceUrl,
     line,
     column
   };
 }
 
+async function getOriginalLocation(
+  location: SourceLocation,
+  options: LocationOptions = {}
+): Promise<SourceLocation> {
+  if (!isGeneratedId(location.sourceId)) {
+    return location;
+  }
+
+  const map = await getSourceMap(location.sourceId);
+  if (!map) {
+    return location;
+  }
+
+  return getOriginalLocationSync(map, location, options);
+}
+
 async function getOriginalSourceText(
   originalSource: Source
 ): Promise<?{
   text: string,
   contentType: string
 }> {
   assert(isOriginalId(originalSource.id), "Source is not an original source");
 
--- a/devtools/client/debugger/src/actions/breakpoints/breakpointPositions.js
+++ b/devtools/client/debugger/src/actions/breakpoints/breakpointPositions.js
@@ -34,17 +34,23 @@ import {
   type MemoizedAction
 } from "../../utils/memoizableAction";
 import type { ThunkArgs } from "../../actions/types";
 
 async function mapLocations(
   generatedLocations: SourceLocation[],
   { sourceMaps }: ThunkArgs
 ) {
+  if (generatedLocations.length == 0) {
+    return [];
+  }
+
+  const { sourceId } = generatedLocations[0];
   const originalLocations = await sourceMaps.getOriginalLocations(
+    sourceId,
     generatedLocations
   );
 
   return zip(originalLocations, generatedLocations).map(
     ([location, generatedLocation]) => ({ location, generatedLocation })
   );
 }
 
--- a/devtools/client/debugger/src/actions/navigation.js
+++ b/devtools/client/debugger/src/actions/navigation.js
@@ -53,17 +53,17 @@ export function willNavigate(event: Obje
 }
 
 export function connect(url: string, actor: string, canRewind: boolean) {
   return async function({ dispatch }: ThunkArgs) {
     await dispatch(updateWorkers());
     dispatch(
       ({
         type: "CONNECT",
-        mainThread: { url, actor, type: -1 },
+        mainThread: { url, actor, type: -1, name: "" },
         canRewind
       }: Action)
     );
   };
 }
 
 /**
  * @memberof actions/navigation
--- a/devtools/client/debugger/src/actions/preview.js
+++ b/devtools/client/debugger/src/actions/preview.js
@@ -113,16 +113,22 @@ export function setPreview(
         // Error case occurs for a token that follows an errored evaluation
         // https://github.com/firefox-devtools/debugger/pull/8056
         // Accommodating for null allows us to show preview for falsy values
         // line "", false, null, Nan, and more
         if (result === null) {
           return;
         }
 
+        // Handle cases where the result is invisible to the debugger
+        // and not possible to preview. Bug 1548256
+        if (result.class && result.class.includes("InvisibleToDebugger")) {
+          return;
+        }
+
         const root = {
           name: expression,
           path: expression,
           contents: { value: result }
         };
         const properties = await client.loadObjectProperties(root);
 
         return {
--- a/devtools/client/debugger/src/actions/source-tree.js
+++ b/devtools/client/debugger/src/actions/source-tree.js
@@ -1,29 +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/>. */
 // @flow
 
-import type { Action, FocusItem, ThunkArgs } from "./types";
-import type { Context } from "../types";
+import type { TreeNode } from "../utils/sources-tree/types";
 
-export function setExpandedState(thread: string, expanded: Set<string>) {
-  return ({ dispatch, getState }: ThunkArgs) => {
-    dispatch(
-      ({
-        type: "SET_EXPANDED_STATE",
-        thread,
-        expanded
-      }: Action)
-    );
-  };
+export function setExpandedState(expanded: Set<string>) {
+  return { type: "SET_EXPANDED_STATE", expanded };
 }
 
-export function focusItem(cx: Context, item: FocusItem) {
-  return ({ dispatch, getState }: ThunkArgs) => {
-    dispatch({
-      type: "SET_FOCUSED_SOURCE_ITEM",
-      cx,
-      item
-    });
-  };
+export function focusItem(item: TreeNode) {
+  return { type: "SET_FOCUSED_SOURCE_ITEM", item };
 }
--- a/devtools/client/debugger/src/actions/sources/tests/blackbox.spec.js
+++ b/devtools/client/debugger/src/actions/sources/tests/blackbox.spec.js
@@ -25,21 +25,15 @@ describe("blackbox", () => {
     await dispatch(actions.toggleBlackBox(cx, foo1Source));
 
     const fooSource = selectors.getSource(getState(), "foo1");
 
     if (!fooSource) {
       throw new Error("foo should exist");
     }
 
-    const { thread } = selectors.getSourceActorsForSource(
-      getState(),
-      foo1Source.id
-    )[0];
-    const displayedSources = selectors.getDisplayedSourcesForThread(
-      getState(),
-      thread
+    const displayedSources = selectors.getDisplayedSources(getState());
+    expect(displayedSources.FakeThread[fooSource.id].isBlackBoxed).toEqual(
+      true
     );
-
-    expect(displayedSources[fooSource.id].isBlackBoxed).toEqual(true);
     expect(fooSource.isBlackBoxed).toEqual(true);
   });
 });
--- a/devtools/client/debugger/src/actions/tests/setProjectDirectoryRoot.spec.js
+++ b/devtools/client/debugger/src/actions/tests/setProjectDirectoryRoot.spec.js
@@ -6,18 +6,16 @@
 
 import {
   createStore,
   selectors,
   actions,
   makeSource
 } from "../../utils/test-head";
 
-import type { Source } from "../../types";
-
 const { getProjectDirectoryRoot, getDisplayedSources } = selectors;
 
 describe("setProjectDirectoryRoot", () => {
   it("should set domain directory as root", async () => {
     const { dispatch, getState, cx } = createStore();
     dispatch(actions.setProjectDirectoryRoot(cx, "example.com"));
     expect(getProjectDirectoryRoot(getState())).toBe("example.com");
   });
@@ -49,24 +47,25 @@ describe("setProjectDirectoryRoot", () =
     });
     const { dispatch, getState, cx } = store;
     await dispatch(actions.newGeneratedSource(makeSource("js/scopes.js")));
     await dispatch(actions.newGeneratedSource(makeSource("lib/vendor.js")));
 
     dispatch(actions.setProjectDirectoryRoot(cx, "localhost:8000/examples/js"));
 
     const filteredSourcesByThread = getDisplayedSources(getState());
-    const filteredSources = Object.values(filteredSourcesByThread)[0];
-    const firstSource: Source = (Object.values(filteredSources)[0]: any);
+    const filteredSources = (Object.values(
+      filteredSourcesByThread.FakeThread
+    ): any)[0];
 
-    expect(firstSource.url).toEqual(
+    expect(filteredSources.url).toEqual(
       "http://localhost:8000/examples/js/scopes.js"
     );
 
-    expect(firstSource.relativeUrl).toEqual("scopes.js");
+    expect(filteredSources.relativeUrl).toEqual("scopes.js");
   });
 
   it("should update the child directory ", () => {
     const { dispatch, getState, cx } = createStore({
       getBreakableLines: async () => []
     });
     dispatch(actions.setProjectDirectoryRoot(cx, "example.com"));
     dispatch(actions.setProjectDirectoryRoot(cx, "example.com/foo/bar"));
--- a/devtools/client/debugger/src/actions/tests/source-tree.spec.js
+++ b/devtools/client/debugger/src/actions/tests/source-tree.spec.js
@@ -7,13 +7,13 @@
 import { actions, selectors, createStore } from "../../utils/test-head";
 const { getExpandedState } = selectors;
 
 describe("source tree", () => {
   it("should set the expanded state", () => {
     const { dispatch, getState } = createStore();
     const expandedState = new Set(["foo", "bar"]);
 
-    expect(getExpandedState(getState(), "FakeThread")).toEqual(undefined);
-    dispatch(actions.setExpandedState("FakeThread", expandedState));
-    expect(getExpandedState(getState(), "FakeThread")).toEqual(expandedState);
+    expect(getExpandedState(getState())).toEqual(new Set([]));
+    dispatch(actions.setExpandedState(expandedState));
+    expect(getExpandedState(getState())).toEqual(expandedState);
   });
 });
--- a/devtools/client/debugger/src/actions/types/index.js
+++ b/devtools/client/debugger/src/actions/types/index.js
@@ -66,20 +66,17 @@ type UpdateTabAction = {|
   +isOriginal?: boolean,
   +sourceId?: string
 |};
 
 type NavigateAction =
   | {| +type: "CONNECT", +mainThread: MainThread, +canRewind: boolean |}
   | {| +type: "NAVIGATE", +mainThread: MainThread |};
 
-export type FocusItem = {
-  thread: string,
-  item: TreeNode
-};
+export type FocusItem = TreeNode;
 
 export type SourceTreeAction =
   | {| +type: "SET_EXPANDED_STATE", +thread: string, +expanded: any |}
   | {| +type: "SET_FOCUSED_SOURCE_ITEM", +cx: Context, item: FocusItem |};
 
 export type ProjectTextSearchAction =
   | {| +type: "ADD_QUERY", +cx: Context, +query: string |}
   | {|
--- a/devtools/client/debugger/src/client/firefox/commands.js
+++ b/devtools/client/debugger/src/client/firefox/commands.js
@@ -72,17 +72,20 @@ function createObjectClient(grip: Grip) 
 }
 
 async function loadObjectProperties(root: Node) {
   const utils = Reps.objectInspector.utils;
   const properties = await utils.loadProperties.loadItemProperties(
     root,
     createObjectClient
   );
-  return utils.node.makeNodesForProperties(properties, root);
+  return utils.node.getChildren({
+    item: root,
+    loadedProperties: new Map([[root.path, properties]])
+  });
 }
 
 function releaseActor(actor: String) {
   if (!actor) {
     return;
   }
 
   return debuggerClient.release(actor);
@@ -256,17 +259,17 @@ async function evaluateExpressions(scrip
   return Promise.all(scripts.map(script => evaluate(script, options)));
 }
 
 type EvaluateParam = { thread: string, frameId: ?FrameId };
 
 function evaluate(
   script: ?Script,
   { thread, frameId }: EvaluateParam = {}
-): Promise<{ result: ?Object }> {
+): Promise<{ result: Grip | null }> {
   const params = { thread, frameActor: frameId };
   if (!tabTarget || !script) {
     return Promise.resolve({ result: null });
   }
 
   const console = thread
     ? lookupConsoleClient(thread)
     : tabTarget.activeConsole;
--- a/devtools/client/debugger/src/client/firefox/create.js
+++ b/devtools/client/debugger/src/client/firefox/create.js
@@ -72,11 +72,12 @@ export function createPause(
   };
 }
 
 export function createWorker(actor: string, url: string) {
   return {
     actor,
     url,
     // Ci.nsIWorkerDebugger.TYPE_DEDICATED
-    type: 0
+    type: 0,
+    name: ""
   };
 }
--- a/devtools/client/debugger/src/client/firefox/tests/commands.spec.js
+++ b/devtools/client/debugger/src/client/firefox/tests/commands.spec.js
@@ -19,71 +19,92 @@ function makeThreadCLient(resp) {
 function makeDependencies() {
   return {
     debuggerClient: (null: any),
     supportsWasm: true,
     tabTarget: (null: any)
   };
 }
 
+function makeGrip(actor = "") {
+  return {
+    actor,
+    class: "",
+    displayClass: "",
+    name: "",
+    extensible: true,
+    location: {
+      url: "",
+      line: 2,
+      column: 34
+    },
+    frozen: false,
+    ownPropertyLength: 1,
+    preview: {},
+    sealed: false,
+    optimizedOut: false,
+    type: ""
+  };
+}
+
 describe("firefox commands", () => {
   describe("getProperties", () => {
     it("empty response", async () => {
       const { getProperties } = clientCommands;
       const threadClient = makeThreadCLient({
         ownProperties: {},
         safeGetterValues: {}
       });
 
       setupCommands({ ...makeDependencies(), threadClient });
-      const props = await getProperties("", { actor: "" });
+      const props = await getProperties("", makeGrip());
       expect(props).toMatchSnapshot();
     });
 
     it("simple properties", async () => {
       const { getProperties } = clientCommands;
       const threadClient = makeThreadCLient({
         ownProperties: {
           obj: { value: "obj" },
           foo: { value: "foo" }
         },
         safeGetterValues: {}
       });
 
       setupCommands({ ...makeDependencies(), threadClient });
-      const props = await getProperties("", { actor: "" });
+      const props = await getProperties("", makeGrip());
       expect(props).toMatchSnapshot();
     });
 
     it("getter values", async () => {
       const { getProperties } = clientCommands;
       const threadClient = makeThreadCLient({
         ownProperties: {
           obj: { value: "obj" },
           foo: { value: "foo" }
         },
         safeGetterValues: {
           obj: { getterValue: "getter", enumerable: true, writable: false }
         }
       });
 
       setupCommands({ ...makeDependencies(), threadClient });
-      const props = await getProperties("", { actor: "" });
+      const props = await getProperties("", makeGrip());
       expect(props).toMatchSnapshot();
     });
 
     it("new getter values", async () => {
       const { getProperties } = clientCommands;
       const threadClient = makeThreadCLient({
         ownProperties: {
           foo: { value: "foo" }
         },
         safeGetterValues: {
           obj: { getterValue: "getter", enumerable: true, writable: false }
         }
       });
 
       setupCommands({ ...makeDependencies(), threadClient });
-      const props = await getProperties("", { actor: "" });
+      const props = await getProperties("", makeGrip());
       expect(props).toMatchSnapshot();
     });
   });
 });
--- a/devtools/client/debugger/src/client/firefox/types.js
+++ b/devtools/client/debugger/src/client/firefox/types.js
@@ -204,17 +204,17 @@ export type TabTarget = {
       script: Script,
       func: Function,
       params?: { frameActor: ?FrameId }
     ) => void,
     evaluateJSAsync: (
       script: Script,
       func: Function,
       params?: { frameActor: ?FrameId }
-    ) => Promise<{ result: ?Object }>,
+    ) => Promise<{ result: Grip | null }>,
     autocomplete: (
       input: string,
       cursor: number,
       func: Function,
       frameId: ?string
     ) => void,
     emit: (string, any) => void
   },
@@ -272,19 +272,36 @@ export type TabClient = {
  * value's current state to the client. They also act as references to the
  * original value, by including an actor to which the client can send messages
  * to modify the value in the debuggee.
  *
  * @see https://wiki.mozilla.org/Remote_Debugging_Protocol#Grips
  * @memberof firefox
  * @static
  */
-// FIXME: need Grip definition
 export type Grip = {
-  actor: string
+  actor: string,
+  class: string,
+  displayClass: string,
+  displayName?: string,
+  parameterNames?: string[],
+  userDisplayName?: string,
+  name: string,
+  extensible: boolean,
+  location: {
+    url: string,
+    line: number,
+    column: number
+  },
+  frozen: boolean,
+  ownPropertyLength: number,
+  preview: Object,
+  sealed: boolean,
+  optimizedOut: boolean,
+  type: string
 };
 
 export type FunctionGrip = {|
   class: "Function",
   name: string,
   parameterNames: string[],
   displayName: string,
   userDisplayName: string,
--- a/devtools/client/debugger/src/components/PrimaryPanes/SourcesTree.js
+++ b/devtools/client/debugger/src/components/PrimaryPanes/SourcesTree.js
@@ -11,119 +11,125 @@ import { connect } from "../../utils/con
 
 // Selectors
 import {
   getShownSource,
   getSelectedSource,
   getDebuggeeUrl,
   getExpandedState,
   getProjectDirectoryRoot,
-  getDisplayedSourcesForThread,
+  getDisplayedSources,
   getFocusedSourceItem,
-  getWorkerByThread,
-  getWorkerCount,
   getContext
 } from "../../selectors";
 
 import { getGeneratedSourceByURL } from "../../reducers/sources";
 
 // Actions
 import actions from "../../actions";
 
 // Components
-import AccessibleImage from "../shared/AccessibleImage";
 import SourcesTreeItem from "./SourcesTreeItem";
 import ManagedTree from "../shared/ManagedTree";
 
 // Utils
 import {
   createTree,
   getDirectories,
   isDirectory,
   getSourceFromNode,
   nodeHasChildren,
   updateTree
 } from "../../utils/sources-tree";
+import { parse } from "../../utils/url";
 import { getRawSourceURL } from "../../utils/source";
 
-import { getDisplayName } from "../../utils/workers";
-import { features } from "../../utils/prefs";
-
 import type {
   TreeNode,
   TreeDirectory,
   ParentMap
 } from "../../utils/sources-tree/types";
-import type { Worker, Source, Context } from "../../types";
-import type { SourcesMap, State as AppState } from "../../reducers/types";
+import type { Source, Context, Thread } from "../../types";
+import type {
+  SourcesMapByThread,
+  State as AppState
+} from "../../reducers/types";
 import type { Item } from "../shared/ManagedTree";
 
 type Props = {
   cx: Context,
-  thread: string,
-  worker: Worker,
-  sources: SourcesMap,
+  threads: Thread[],
+  sources: SourcesMapByThread,
   sourceCount: number,
   shownSource?: Source,
   selectedSource?: Source,
   debuggeeUrl: string,
   projectRoot: string,
   expanded: Set<string>,
   selectSource: typeof actions.selectSource,
   setExpandedState: typeof actions.setExpandedState,
   focusItem: typeof actions.focusItem,
-  focused: TreeNode,
-  workerCount: number
+  focused: TreeNode
 };
 
 type State = {
   parentMap: ParentMap,
   sourceTree: TreeDirectory,
   uncollapsedTree: TreeDirectory,
   listItems?: any,
   highlightItems?: any
 };
 
 type SetExpanded = (item: TreeNode, expanded: boolean, altKey: boolean) => void;
 
+function shouldAutoExpand(depth, item, debuggeeUrl) {
+  if (depth !== 1) {
+    return false;
+  }
+
+  const { host } = parse(debuggeeUrl);
+  return item.name === host;
+}
+
 class SourcesTree extends Component<Props, State> {
-  mounted: boolean;
-
   constructor(props: Props) {
     super(props);
-    const { debuggeeUrl, sources, projectRoot } = this.props;
+    const { debuggeeUrl, sources, threads } = this.props;
 
     this.state = createTree({
-      projectRoot,
       debuggeeUrl,
-      sources
+      sources,
+      threads
     });
   }
 
   componentWillReceiveProps(nextProps: Props) {
     const {
       projectRoot,
       debuggeeUrl,
       sources,
       shownSource,
-      selectedSource
+      selectedSource,
+      threads
     } = this.props;
     const { uncollapsedTree, sourceTree } = this.state;
 
     if (
       projectRoot != nextProps.projectRoot ||
       debuggeeUrl != nextProps.debuggeeUrl ||
+      threads != nextProps.threads ||
       nextProps.sourceCount === 0
     ) {
       // early recreate tree because of changes
-      // to project root, debugee url or lack of sources
+      // to project root, debuggee url or lack of sources
       return this.setState(
         createTree({
           sources: nextProps.sources,
-          debuggeeUrl: nextProps.debuggeeUrl
+          debuggeeUrl: nextProps.debuggeeUrl,
+          threads: nextProps.threads
         })
       );
     }
 
     if (nextProps.shownSource && nextProps.shownSource != shownSource) {
       const listItems = getDirectories(nextProps.shownSource, sourceTree);
       return this.setState({ listItems });
     }
@@ -140,67 +146,64 @@ class SourcesTree extends Component<Prop
     }
 
     // NOTE: do not run this every time a source is clicked,
     // only when a new source is added
     if (nextProps.sources != this.props.sources) {
       this.setState(
         updateTree({
           newSources: nextProps.sources,
+          threads: nextProps.threads,
           prevSources: sources,
           debuggeeUrl,
           uncollapsedTree,
           sourceTree
         })
       );
     }
   }
 
   selectItem = (item: TreeNode) => {
     if (item.type == "source" && !Array.isArray(item.contents)) {
       this.props.selectSource(this.props.cx, item.contents.id);
     }
   };
 
   onFocus = (item: TreeNode) => {
-    this.props.focusItem(this.props.cx, { thread: this.props.thread, item });
+    this.props.focusItem(item);
   };
 
   onActivate = (item: TreeNode) => {
     this.selectItem(item);
   };
 
   // NOTE: we get the source from sources because item.contents is cached
   getSource(item: TreeNode): ?Source {
-    const source = getSourceFromNode(item);
-    if (source) {
-      return this.props.sources[source.id];
-    }
-
-    return null;
+    return getSourceFromNode(item);
   }
 
   getPath = (item: TreeNode): string => {
-    const path = `${item.path}/${item.name}`;
+    const { path } = item;
     const source = this.getSource(item);
 
     if (!source || isDirectory(item)) {
       return path;
     }
 
     const blackBoxedPart = source.isBlackBoxed ? ":blackboxed" : "";
+
     return `${path}/${source.id}/${blackBoxedPart}`;
   };
 
   onExpand = (item: Item, expandedState: Set<string>) => {
-    this.props.setExpandedState(this.props.thread, expandedState);
+    this.props.setExpandedState(expandedState);
   };
 
   onCollapse = (item: Item, expandedState: Set<string>) => {
-    this.props.setExpandedState(this.props.thread, expandedState);
+    this.props.setExpandedState(expandedState);
   };
 
   isEmpty() {
     const { sourceTree } = this.state;
     return sourceTree.contents.length === 0;
   }
 
   renderEmptyElement(message) {
@@ -225,53 +228,59 @@ class SourcesTree extends Component<Prop
         return sourceContents.contents[0].contents;
       }
       return sourceContents.contents;
     }
 
     return sourceTree.contents;
   };
 
+  getChildren = (item: $Shape<TreeDirectory>) => {
+    return nodeHasChildren(item) ? item.contents : [];
+  };
+
   renderItem = (
     item: TreeNode,
     depth: number,
     focused: boolean,
     _,
     expanded: boolean,
     { setExpanded }: { setExpanded: SetExpanded }
   ) => {
-    const { debuggeeUrl, projectRoot } = this.props;
+    const { debuggeeUrl, projectRoot, threads } = this.props;
 
     return (
       <SourcesTreeItem
         item={item}
+        threads={threads}
         depth={depth}
         focused={focused}
+        autoExpand={shouldAutoExpand(depth, item, debuggeeUrl)}
         expanded={expanded}
         focusItem={this.onFocus}
         selectItem={this.selectItem}
         source={this.getSource(item)}
         debuggeeUrl={debuggeeUrl}
         projectRoot={projectRoot}
         setExpanded={setExpanded}
       />
     );
   };
 
   renderTree() {
     const { expanded, focused } = this.props;
+
     const { highlightItems, listItems, parentMap } = this.state;
 
     const treeProps = {
       autoExpandAll: false,
-      autoExpandDepth: expanded ? 0 : 1,
+      autoExpandDepth: 1,
       expanded,
       focused,
-      getChildren: (item: $Shape<TreeDirectory>) =>
-        nodeHasChildren(item) ? item.contents : [],
+      getChildren: this.getChildren,
       getParent: (item: $Shape<TreeNode>) => parentMap.get(item),
       getPath: this.getPath,
       getRoots: this.getRoots,
       highlightItems,
       itemHeight: 21,
       key: this.isEmpty() ? "empty" : "full",
       listItems,
       onCollapse: this.onCollapse,
@@ -281,117 +290,75 @@ class SourcesTree extends Component<Prop
       renderItem: this.renderItem,
       preventBlur: true
     };
 
     return <ManagedTree {...treeProps} />;
   }
 
   renderPane(...children) {
-    const { projectRoot, thread } = this.props;
+    const { projectRoot } = this.props;
 
     return (
       <div
         key="pane"
         className={classnames("sources-pane", {
-          "sources-list-custom-root": projectRoot,
-          thread
+          "sources-list-custom-root": projectRoot
         })}
       >
         {children}
       </div>
     );
   }
 
-  renderThreadHeader() {
-    const { worker, workerCount } = this.props;
-
-    if (!features.windowlessWorkers || workerCount == 0) {
-      return null;
-    }
-
-    if (worker) {
-      return (
-        <div className="node thread-header" key="thread-header">
-          <AccessibleImage className="worker" />
-          <span className="label">{getDisplayName(worker)}</span>
-        </div>
-      );
-    }
-
-    return (
-      <div className="node thread-header" key="thread-header">
-        <AccessibleImage className={"file"} />
-        <span className="label">{L10N.getStr("mainThread")}</span>
-      </div>
-    );
-  }
-
   render() {
-    const { worker } = this.props;
-
-    if (!features.windowlessWorkers && worker) {
-      return null;
-    }
-
     return this.renderPane(
-      this.renderThreadHeader(),
       this.isEmpty() ? (
         this.renderEmptyElement(L10N.getStr("noSourcesText"))
       ) : (
         <div key="tree" className="sources-list">
           {this.renderTree()}
         </div>
       )
     );
   }
 }
 
 function getSourceForTree(
   state: AppState,
-  displayedSources: SourcesMap,
-  source: ?Source,
-  thread
+  displayedSources: SourcesMapByThread,
+  source: ?Source
 ): ?Source {
   if (!source) {
     return null;
   }
 
-  source = displayedSources[source.id];
   if (!source || !source.isPrettyPrinted) {
     return source;
   }
 
   return getGeneratedSourceByURL(state, getRawSourceURL(source.url));
 }
 
 const mapStateToProps = (state, props) => {
   const selectedSource = getSelectedSource(state);
   const shownSource = getShownSource(state);
-  const focused = getFocusedSourceItem(state);
-  const thread = props.thread;
-  const displayedSources = getDisplayedSourcesForThread(state, thread);
+  const displayedSources = getDisplayedSources(state);
 
   return {
+    threads: props.threads,
     cx: getContext(state),
-    shownSource: getSourceForTree(state, displayedSources, shownSource, thread),
-    selectedSource: getSourceForTree(
-      state,
-      displayedSources,
-      selectedSource,
-      thread
-    ),
+    shownSource: getSourceForTree(state, displayedSources, shownSource),
+    selectedSource: getSourceForTree(state, displayedSources, selectedSource),
     debuggeeUrl: getDebuggeeUrl(state),
-    expanded: getExpandedState(state, props.thread),
-    focused: focused && focused.thread == props.thread ? focused.item : null,
+    expanded: getExpandedState(state),
+    focused: getFocusedSourceItem(state),
     projectRoot: getProjectDirectoryRoot(state),
     sources: displayedSources,
-    sourceCount: Object.values(displayedSources).length,
-    worker: getWorkerByThread(state, thread),
-    workerCount: getWorkerCount(state)
+    sourceCount: Object.values(displayedSources).length
   };
 };
 
 export default connect(
   mapStateToProps,
   {
     selectSource: actions.selectSource,
     setExpandedState: actions.setExpandedState,
--- a/devtools/client/debugger/src/components/PrimaryPanes/SourcesTreeItem.js
+++ b/devtools/client/debugger/src/components/PrimaryPanes/SourcesTreeItem.js
@@ -6,47 +6,52 @@
 
 import React, { Component } from "react";
 import { connect } from "../../utils/connect";
 import classnames from "classnames";
 import { showMenu } from "devtools-contextmenu";
 
 import SourceIcon from "../shared/SourceIcon";
 import AccessibleImage from "../shared/AccessibleImage";
+import { getDisplayName, isWorker } from "../../utils/workers";
 
 import {
   getGeneratedSourceByURL,
   getHasSiblingOfSameName,
   hasPrettySource as checkHasPrettySource,
-  getContext
+  getContext,
+  getMainThread
 } from "../../selectors";
 import actions from "../../actions";
 
 import {
   isOriginal as isOriginalSource,
   getSourceQueryString,
   isUrlExtension,
   shouldBlackbox
 } from "../../utils/source";
 import { isDirectory } from "../../utils/sources-tree";
 import { copyToTheClipboard } from "../../utils/clipboard";
 import { features } from "../../utils/prefs";
 
 import type { TreeNode } from "../../utils/sources-tree/types";
-import type { Source, Context } from "../../types";
+import type { Source, Context, MainThread, Thread } from "../../types";
 
 type Props = {
+  autoExpand: ?boolean,
   cx: Context,
   debuggeeUrl: string,
   projectRoot: string,
   source: ?Source,
   item: TreeNode,
   depth: number,
   focused: boolean,
   expanded: boolean,
+  threads: Thread[],
+  mainThread: MainThread,
   hasMatchingGeneratedSource: boolean,
   hasSiblingOfSameName: boolean,
   hasPrettySource: boolean,
   focusItem: TreeNode => void,
   selectItem: TreeNode => void,
   setExpanded: (TreeNode, boolean, boolean) => void,
   clearProjectDirectoryRoot: typeof actions.clearProjectDirectoryRoot,
   setProjectDirectoryRoot: typeof actions.setProjectDirectoryRoot,
@@ -60,57 +65,27 @@ type MenuOption = {
   label: string,
   disabled: boolean,
   click: () => any
 };
 
 type ContextMenu = Array<MenuOption>;
 
 class SourceTreeItem extends Component<Props, State> {
-  getIcon(item: TreeNode, depth: number) {
-    const { debuggeeUrl, projectRoot, source, hasPrettySource } = this.props;
-
-    if (item.path === "webpack://") {
-      return <AccessibleImage className="webpack" />;
-    } else if (item.path === "ng://") {
-      return <AccessibleImage className="angular" />;
-    } else if (isUrlExtension(item.path) && depth === 0) {
-      return <AccessibleImage className="extension" />;
+  componentDidMount() {
+    const { autoExpand, item } = this.props;
+    if (autoExpand) {
+      this.props.setExpanded(item, true, false);
     }
-
-    if (depth === 0 && projectRoot === "") {
-      return (
-        <AccessibleImage
-          className={classnames("globe-small", {
-            debuggee: debuggeeUrl && debuggeeUrl.includes(item.name)
-          })}
-        />
-      );
-    }
-
-    if (isDirectory(item)) {
-      return <AccessibleImage className="folder" />;
-    }
-
-    if (hasPrettySource) {
-      return <AccessibleImage className="prettyPrint" />;
-    }
-
-    if (source) {
-      return <SourceIcon source={source} />;
-    }
-
-    return null;
   }
 
   onClick = (e: MouseEvent) => {
     const { item, focusItem, selectItem } = this.props;
 
     focusItem(item);
-
     if (!isDirectory(item)) {
       selectItem(item);
     }
   };
 
   onContextMenu = (event: Event, item: TreeNode) => {
     const copySourceUri2Label = L10N.getStr("copySourceUri2");
     const copySourceUri2Key = L10N.getStr("copySourceUri2.accesskey");
@@ -203,18 +178,79 @@ class SourceTreeItem extends Component<P
     const { item, expanded } = this.props;
     return isDirectory(item) ? (
       <AccessibleImage className={classnames("arrow", { expanded })} />
     ) : (
       <span className="img no-arrow" />
     );
   }
 
-  renderItemName() {
-    const { item } = this.props;
+  renderIcon(item: TreeNode, depth: number) {
+    const {
+      debuggeeUrl,
+      projectRoot,
+      source,
+      hasPrettySource,
+      threads
+    } = this.props;
+
+    if (item.name === "webpack://") {
+      return <AccessibleImage className="webpack" />;
+    } else if (item.name === "ng://") {
+      return <AccessibleImage className="angular" />;
+    } else if (isUrlExtension(item.path) && depth === 1) {
+      return <AccessibleImage className="extension" />;
+    }
+
+    // Threads level
+    if (depth === 0 && projectRoot === "") {
+      const thread = threads.find(thrd => thrd.actor == item.name);
+
+      if (thread) {
+        const icon = thread === this.props.mainThread ? "window" : "worker";
+        return (
+          <AccessibleImage
+            className={classnames(icon, {
+              debuggee: debuggeeUrl && debuggeeUrl.includes(item.name)
+            })}
+          />
+        );
+      }
+    }
+
+    if (isDirectory(item)) {
+      // Domain level
+      if (depth === 1) {
+        return <AccessibleImage className="globe-small" />;
+      }
+      return <AccessibleImage className="folder" />;
+    }
+
+    if (hasPrettySource) {
+      return <AccessibleImage className="prettyPrint" />;
+    }
+
+    if (source) {
+      return <SourceIcon source={source} />;
+    }
+
+    return null;
+  }
+
+  renderItemName(depth) {
+    const { item, threads } = this.props;
+
+    if (depth === 0) {
+      const thread = threads.find(({ actor }) => actor == item.name);
+      if (thread) {
+        return isWorker(thread)
+          ? getDisplayName((thread: any))
+          : L10N.getStr("mainThread");
+      }
+    }
 
     switch (item.name) {
       case "ng://":
         return "Angular";
       case "webpack://":
         return "Webpack";
       default:
         return `${unescape(item.name)}`;
@@ -248,20 +284,19 @@ class SourceTreeItem extends Component<P
     return (
       <div
         className={classnames("node", { focused })}
         key={item.path}
         onClick={this.onClick}
         onContextMenu={e => this.onContextMenu(e, item)}
       >
         {this.renderItemArrow()}
-        {this.getIcon(item, depth)}
+        {this.renderIcon(item, depth)}
         <span className="label">
-          {" "}
-          {this.renderItemName()}
+          {this.renderItemName(depth)}
           {query} {suffix}
         </span>
       </div>
     );
   }
 }
 
 function getHasMatchingGeneratedSource(state, source: ?Source) {
@@ -271,16 +306,17 @@ function getHasMatchingGeneratedSource(s
 
   return !!getGeneratedSourceByURL(state, source.url);
 }
 
 const mapStateToProps = (state, props) => {
   const { source } = props;
   return {
     cx: getContext(state),
+    mainThread: getMainThread(state),
     hasMatchingGeneratedSource: getHasMatchingGeneratedSource(state, source),
     hasSiblingOfSameName: getHasSiblingOfSameName(state, source),
     hasPrettySource: source ? checkHasPrettySource(state, source.id) : false
   };
 };
 
 export default connect(
   mapStateToProps,
--- a/devtools/client/debugger/src/components/PrimaryPanes/index.js
+++ b/devtools/client/debugger/src/components/PrimaryPanes/index.js
@@ -122,19 +122,17 @@ class PrimaryPanes extends Component<Pro
           <AccessibleImage className="breadcrumb" />
           <span className="sources-clear-root-label">{rootLabel}</span>
         </button>
       </div>
     );
   }
 
   renderThreadSources() {
-    return this.props.threads.map(({ actor }) => (
-      <SourcesTree thread={actor} key={actor} />
-    ));
+    return <SourcesTree threads={this.props.threads} />;
   }
 
   render() {
     const { selectedTab, projectRoot } = this.props;
     const activeIndex = selectedTab === "sources" ? 0 : 1;
 
     return (
       <Tabs
--- a/devtools/client/debugger/src/components/PrimaryPanes/tests/SourcesTree.spec.js
+++ b/devtools/client/debugger/src/components/PrimaryPanes/tests/SourcesTree.spec.js
@@ -78,34 +78,33 @@ describe("SourcesTree", () => {
         );
       });
 
       it("updates tree with a new item", async () => {
         const { component, props } = render();
         const newSource = createMockSource(
           "server1.conn13.child1/43",
           "http://mdn.com/four.js",
-          true
+          true,
+          ""
         );
+
         const newThreadSources = {
           ...props.sources.FakeThread,
           "server1.conn13.child1/43": newSource
         };
 
         await component.setProps({
           ...props,
-          sources: {
-            ...props.sources,
-            ...newThreadSources
-          }
+          sources: newThreadSources
         });
 
         expect(
-          component.state("uncollapsedTree").contents[0].contents
-        ).toHaveLength(6);
+          component.state("uncollapsedTree").contents[0].contents[0].contents
+        ).toHaveLength(5);
       });
 
       it("updates sources if sources are emptied", async () => {
         const { component, props, defaultState } = render();
 
         expect(defaultState.uncollapsedTree.contents).toHaveLength(1);
 
         await component.setProps({
@@ -115,71 +114,126 @@ describe("SourcesTree", () => {
         });
 
         expect(component.state("uncollapsedTree").contents).toHaveLength(0);
       });
 
       it("recreates tree if projectRoot is changed", async () => {
         const { component, props, defaultState } = render();
         const sources = {
-          "server1.conn13.child1/41": createMockSource(
-            "server1.conn13.child1/41",
-            "http://mozilla.com/three.js"
-          )
+          FakeThread: {
+            "server1.conn13.child1/41": createMockSource(
+              "server1.conn13.child1/41",
+              "http://mozilla.com/three.js"
+            )
+          }
         };
 
-        expect(defaultState.uncollapsedTree.contents[0].contents).toHaveLength(
-          5
-        );
+        expect(
+          defaultState.uncollapsedTree.contents[0].contents[0].contents
+        ).toHaveLength(5);
 
         await component.setProps({
           ...props,
           sources,
           projectRoot: "mozilla"
         });
 
         expect(
           component.state("uncollapsedTree").contents[0].contents
         ).toHaveLength(1);
       });
 
-      it("recreates tree if debugeeUrl is changed", async () => {
+      it("recreates tree if debuggeeUrl is changed", async () => {
         const { component, props, defaultState } = render();
         const sources = {
-          "server1.conn13.child1/41": createMockSource(
-            "server1.conn13.child1/41",
-            "http://mdn.com/three.js"
-          )
+          FakeThread: {
+            "server1.conn13.child1/41": createMockSource(
+              "server1.conn13.child1/41",
+              "http://mdn.com/three.js",
+              true,
+              ""
+            )
+          }
         };
 
-        expect(defaultState.uncollapsedTree.contents[0].contents).toHaveLength(
-          5
-        );
+        expect(
+          defaultState.uncollapsedTree.contents[0].contents[0].contents
+        ).toHaveLength(5);
 
         await component.setProps({
           ...props,
           debuggeeUrl: "mozilla",
           sources
         });
 
         expect(
           component.state("uncollapsedTree").contents[0].contents
         ).toHaveLength(1);
       });
     });
 
+    describe("updates threads", () => {
+      it("adds sources to the correct thread", async () => {
+        const { component, props } = render();
+        const newSource = createMockSource(
+          "server1.conn13.child1/43",
+          "http://mdn.com/four.js",
+          true,
+          ""
+        );
+
+        const newThreadSources = {
+          FakeThread1: {
+            "server1.conn13.child1/43": newSource
+          }
+        };
+
+        expect(component.state("uncollapsedTree").contents[0].name).toEqual(
+          "FakeThread"
+        );
+        expect(
+          component.state("uncollapsedTree").contents[0].contents[0].contents
+        ).toHaveLength(5);
+        expect(component.state("uncollapsedTree").contents[1]).toEqual(
+          undefined
+        );
+
+        await component.setProps({
+          ...props,
+          sources: {
+            ...props.sources,
+            ...newThreadSources
+          }
+        });
+
+        expect(component.state("uncollapsedTree").contents[0].name).toEqual(
+          "FakeThread"
+        );
+        expect(
+          component.state("uncollapsedTree").contents[0].contents[0].contents
+        ).toHaveLength(5);
+
+        expect(component.state("uncollapsedTree").contents[1].name).toEqual(
+          "FakeThread1"
+        );
+        expect(
+          component.state("uncollapsedTree").contents[1].contents[0].contents
+        ).toHaveLength(1);
+      });
+    });
+
     describe("updates highlighted items", () => {
       it("updates highlightItems if selectedSource changes", async () => {
         const { component, props } = render();
         const mockSource = createMockSource(
           "server1.conn13.child1/41",
           "http://mdn.com/three.js",
           false,
-          null,
-          "FakeThread"
+          null
         );
         await component.setProps({
           ...props,
           selectedSource: mockSource
         });
         expect(component).toMatchSnapshot();
       });
     });
@@ -242,162 +296,146 @@ describe("SourcesTree", () => {
 
     it("onExpand", async () => {
       const { component, props } = render();
       const expandedState = ["x", "y"];
       await component
         .find("ManagedTree")
         .props()
         .onExpand({}, expandedState);
-      expect(props.setExpandedState).toHaveBeenCalledWith(
-        "FakeThread",
-        expandedState
-      );
+      expect(props.setExpandedState).toHaveBeenCalledWith(expandedState);
     });
 
     it("onCollapse", async () => {
       const { component, props } = render();
       const expandedState = ["y", "z"];
       await component
         .find("ManagedTree")
         .props()
         .onCollapse({}, expandedState);
-      expect(props.setExpandedState).toHaveBeenCalledWith(
-        "FakeThread",
-        expandedState
-      );
+      expect(props.setExpandedState).toHaveBeenCalledWith(expandedState);
     });
 
     it("getParent", async () => {
       const { component } = render();
       const item = component.state("sourceTree").contents[0].contents[0];
       const parent = component
         .find("ManagedTree")
         .props()
         .getParent(item);
 
-      expect(parent.path).toEqual("mdn.com");
-      expect(parent.contents).toHaveLength(5);
+      expect(parent.path).toEqual("FakeThread");
+      expect(parent.contents[0].contents).toHaveLength(5);
     });
   });
 
   describe("getPath", () => {
     it("should return path for item", async () => {
       const { instance } = render();
       const path = instance.getPath(createMockItem());
-      expect(path).toEqual(
-        "http://mdn.com/one.js/one.js/server1.conn13.child1/39/"
-      );
-    });
-
-    it("should return path for blackboxedboxed item", async () => {
-      const item = createMockItem(
-        "http://mdn.com/blackboxed.js",
-        "blackboxed.js",
-        { id: "server1.conn13.child1/59" }
-      );
-
-      const sources = {
-        "server1.conn13.child1/59": createMockSource(
-          "server1.conn13.child1/59",
-          "http://mdn.com/blackboxed.js",
-          true
-        )
-      };
-
-      const { instance } = render({ sources });
-      const path = instance.getPath(item);
-      expect(path).toEqual(
-        "http://mdn.com/blackboxed.js/blackboxed.js/server1.conn13.child1/59/:blackboxed"
-      );
+      expect(path).toEqual("http://mdn.com/one.js/server1.conn13.child1/39/");
     });
 
     it("should return path for generated item", async () => {
       const { instance } = render();
       const pathOriginal = instance.getPath(
         createMockItem("http://mdn.com/four.js", "four.js", {
           id: "server1.conn13.child1/42/originalSource-sha"
         })
       );
       expect(pathOriginal).toEqual(
-        "http://mdn.com/four.js/four.js/server1.conn13.child1/42/originalSource-sha/"
+        "http://mdn.com/four.js/server1.conn13.child1/42/originalSource-sha/"
       );
 
       const pathGenerated = instance.getPath(
         createMockItem("http://mdn.com/four.js", "four.js", {
           id: "server1.conn13.child1/42"
         })
       );
       expect(pathGenerated).toEqual(
-        "http://mdn.com/four.js/four.js/server1.conn13.child1/42/"
+        "http://mdn.com/four.js/server1.conn13.child1/42/"
       );
     });
   });
 });
 
 function generateDefaults(overrides: Object) {
   const defaultSources = {
-    "server1.conn13.child1/39": createMockSource(
-      "server1.conn13.child1/39",
-      "http://mdn.com/one.js"
-    ),
-    "server1.conn13.child1/40": createMockSource(
-      "server1.conn13.child1/40",
-      "http://mdn.com/two.js"
-    ),
-    "server1.conn13.child1/41": createMockSource(
-      "server1.conn13.child1/41",
-      "http://mdn.com/three.js"
-    ),
-    "server1.conn13.child1/42/originalSource-sha": createMockSource(
-      "server1.conn13.child1/42/originalSource-sha",
-      "http://mdn.com/four.js"
-    ),
-    "server1.conn13.child1/42": createMockSource(
-      "server1.conn13.child1/42",
-      "http://mdn.com/four.js",
-      false,
-      "data:application/json?charset=utf?dsffewrsf"
-    )
+    FakeThread: {
+      "server1.conn13.child1/39": createMockSource(
+        "server1.conn13.child1/39",
+        "http://mdn.com/one.js",
+        false,
+        null
+      ),
+      "server1.conn13.child1/40": createMockSource(
+        "server1.conn13.child1/40",
+        "http://mdn.com/two.js",
+        false,
+        null
+      ),
+      "server1.conn13.child1/41": createMockSource(
+        "server1.conn13.child1/41",
+        "http://mdn.com/three.js",
+        false,
+        null
+      ),
+      "server1.conn13.child1/42/originalSource-sha": createMockSource(
+        "server1.conn13.child1/42/originalSource-sha",
+        "http://mdn.com/four.js",
+        false,
+        null
+      ),
+      "server1.conn13.child1/42": createMockSource(
+        "server1.conn13.child1/42",
+        "http://mdn.com/four.js",
+        false,
+        "data:application/json?charset=utf?dsffewrsf"
+      )
+    }
   };
+
   return {
     cx: mockcx,
-    thread: "FakeThread",
     autoExpandAll: true,
     selectSource: jest.fn(),
     setExpandedState: jest.fn(),
     sources: defaultSources,
     debuggeeUrl: "http://mdn.com",
     clearProjectDirectoryRoot: jest.fn(),
     setProjectDirectoryRoot: jest.fn(),
     focusItem: jest.fn(),
     projectRoot: "",
+    threads: [
+      {
+        name: "FakeThread",
+        actor: "FakeThread"
+      },
+      {
+        name: "FakeThread1",
+        actor: "FakeThread1"
+      }
+    ],
     ...overrides
   };
 }
 
 function render(overrides = {}) {
   const props = generateDefaults(overrides);
   // $FlowIgnore
   const component = shallow(<SourcesTree.WrappedComponent {...props} />);
   const defaultState = component.state();
   const instance = component.instance();
 
   instance.shouldComponentUpdate = () => true;
 
   return { component, props, defaultState, instance };
 }
 
-function createMockSource(
-  id,
-  url,
-  isBlackBoxed = false,
-  sourceMapURL = null,
-  thread = ""
-) {
+function createMockSource(id, url, isBlackBoxed = false, sourceMapURL = null) {
   return {
     ...makeMockSource(url, id),
     isBlackBoxed,
     sourceMapURL
   };
 }
 
 function createMockDirectory(path = "folder/", name = "folder", contents = []) {
--- a/devtools/client/debugger/src/components/PrimaryPanes/tests/SourcesTreeItem.spec.js
+++ b/devtools/client/debugger/src/components/PrimaryPanes/tests/SourcesTreeItem.spec.js
@@ -370,31 +370,36 @@ function generateDefaults(overrides) {
     debuggeeUrl: "http://mdn.com",
     projectRoot: "",
     clearProjectDirectoryRoot: jest.fn(),
     setProjectDirectoryRoot: jest.fn(),
     toggleBlackBox: jest.fn(),
     selectItem: jest.fn(),
     focusItem: jest.fn(),
     setExpanded: jest.fn(),
+    threads: [{ name: "Main Thread" }],
     ...overrides
   };
 }
 
 function render(overrides = {}) {
   const props = generateDefaults(overrides);
   // $FlowIgnore
   const component = shallow(<SourcesTreeItem.WrappedComponent {...props} />);
   const defaultState = component.state();
   const instance = component.instance();
 
   return { component, props, defaultState, instance };
 }
 
-function createMockDirectory(path = "folder/", name = "folder", contents = []) {
+function createMockDirectory(
+  path = "domain/subfolder",
+  name = "folder",
+  contents = []
+) {
   return {
     type: "directory",
     name,
     path,
     contents
   };
 }
 
--- a/devtools/client/debugger/src/components/PrimaryPanes/tests/__snapshots__/PrimaryPanes.spec.js.snap
+++ b/devtools/client/debugger/src/components/PrimaryPanes/tests/__snapshots__/PrimaryPanes.spec.js.snap
@@ -52,16 +52,19 @@ exports[`PrimaryPanes with custom root r
           />
           <span
             className="sources-clear-root-label"
           >
             mdn.com
           </span>
         </button>
       </div>
+      <Connect(SourcesTree)
+        threads={Array []}
+      />
     </div>
     <Connect(Outline)
       alphabetizeOutline={false}
       onAlphabetizeClick={[Function]}
     />
   </TabPanels>
 </Tabs>
 `;
@@ -118,16 +121,19 @@ exports[`PrimaryPanes with custom root r
           />
           <span
             className="sources-clear-root-label"
           >
             custom
           </span>
         </button>
       </div>
+      <Connect(SourcesTree)
+        threads={Array []}
+      />
     </div>
     <Connect(Outline)
       alphabetizeOutline={false}
       onAlphabetizeClick={[Function]}
     />
   </TabPanels>
 </Tabs>
 `;
--- a/devtools/client/debugger/src/components/PrimaryPanes/tests/__snapshots__/SourcesTree.spec.js.snap
+++ b/devtools/client/debugger/src/components/PrimaryPanes/tests/__snapshots__/SourcesTree.spec.js.snap
@@ -1,35 +1,22 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
 exports[`SourcesTree After changing expanded nodes Shows the tree with four.js, five.js and six.js expanded 1`] = `
 <div
-  className="sources-pane thread"
+  className="sources-pane"
   key="pane"
 >
   <div
-    className="node thread-header"
-    key="thread-header"
-  >
-    <AccessibleImage
-      className="file"
-    />
-    <span
-      className="label"
-    >
-      Main Thread
-    </span>
-  </div>
-  <div
     className="sources-list"
     key="tree"
   >
     <ManagedTree
       autoExpandAll={false}
-      autoExpandDepth={0}
+      autoExpandDepth={1}
       expanded={
         Array [
           "four.js",
           "five.js",
           "six.js",
         ]
       }
       getChildren={[Function]}
@@ -46,60 +33,34 @@ exports[`SourcesTree After changing expa
       renderItem={[Function]}
     />
   </div>
 </div>
 `;
 
 exports[`SourcesTree Should show a 'No Sources' message if there are no sources 1`] = `
 <div
-  className="sources-pane thread"
+  className="sources-pane"
   key="pane"
 >
   <div
-    className="node thread-header"
-    key="thread-header"
-  >
-    <AccessibleImage
-      className="file"
-    />
-    <span
-      className="label"
-    >
-      Main Thread
-    </span>
-  </div>
-  <div
     className="no-sources-message"
     key="empty"
   >
     This page has no sources.
   </div>
 </div>
 `;
 
 exports[`SourcesTree Should show the tree with nothing expanded 1`] = `
 <div
-  className="sources-pane thread"
+  className="sources-pane"
   key="pane"
 >
   <div
-    className="node thread-header"
-    key="thread-header"
-  >
-    <AccessibleImage
-      className="file"
-    />
-    <span
-      className="label"
-    >
-      Main Thread
-    </span>
-  </div>
-  <div
     className="sources-list"
     key="tree"
   >
     <ManagedTree
       autoExpandAll={false}
       autoExpandDepth={1}
       getChildren={[Function]}
       getParent={[Function]}
@@ -115,39 +76,26 @@ exports[`SourcesTree Should show the tre
       renderItem={[Function]}
     />
   </div>
 </div>
 `;
 
 exports[`SourcesTree When loading initial source Shows the tree with one.js, two.js and three.js expanded 1`] = `
 <div
-  className="sources-pane thread"
+  className="sources-pane"
   key="pane"
 >
   <div
-    className="node thread-header"
-    key="thread-header"
-  >
-    <AccessibleImage
-      className="file"
-    />
-    <span
-      className="label"
-    >
-      Main Thread
-    </span>
-  </div>
-  <div
     className="sources-list"
     key="tree"
   >
     <ManagedTree
       autoExpandAll={false}
-      autoExpandDepth={0}
+      autoExpandDepth={1}
       expanded={
         Array [
           "one.js",
           "two.js",
           "three.js",
         ]
       }
       getChildren={[Function]}
@@ -164,33 +112,20 @@ exports[`SourcesTree When loading initia
       renderItem={[Function]}
     />
   </div>
 </div>
 `;
 
 exports[`SourcesTree on receiving new props updates highlighted items updates highlightItems if selectedSource changes 1`] = `
 <div
-  className="sources-pane thread"
+  className="sources-pane"
   key="pane"
 >
   <div
-    className="node thread-header"
-    key="thread-header"
-  >
-    <AccessibleImage
-      className="file"
-    />
-    <span
-      className="label"
-    >
-      Main Thread
-    </span>
-  </div>
-  <div
     className="sources-list"
     key="tree"
   >
     <ManagedTree
       autoExpandAll={false}
       autoExpandDepth={1}
       getChildren={[Function]}
       getParent={[Function]}
@@ -207,17 +142,17 @@ exports[`SourcesTree on receiving new pr
               "isExtension": false,
               "isPrettyPrinted": false,
               "isWasm": false,
               "relativeUrl": "http://mdn.com/three.js",
               "sourceMapURL": null,
               "url": "http://mdn.com/three.js",
             },
             "name": "three.js",
-            "path": "mdn.com/three.js",
+            "path": "FakeThread/mdn.com/three.js",
             "type": "source",
           },
           Object {
             "contents": Array [
               Object {
                 "contents": Object {
                   "id": "server1.conn13.child1/42",
                   "introductionType": undefined,
@@ -226,90 +161,189 @@ exports[`SourcesTree on receiving new pr
                   "isExtension": false,
                   "isPrettyPrinted": false,
                   "isWasm": false,
                   "relativeUrl": "http://mdn.com/four.js",
                   "sourceMapURL": "data:application/json?charset=utf?dsffewrsf",
                   "url": "http://mdn.com/four.js",
                 },
                 "name": "four.js",
-                "path": "mdn.com/four.js",
+                "path": "FakeThread/mdn.com/four.js",
                 "type": "source",
               },
               Object {
                 "contents": Object {
                   "id": "server1.conn13.child1/42/originalSource-sha",
                   "introductionType": undefined,
                   "introductionUrl": null,
                   "isBlackBoxed": false,
                   "isExtension": false,
                   "isPrettyPrinted": false,
                   "isWasm": false,
                   "relativeUrl": "http://mdn.com/four.js",
                   "sourceMapURL": null,
                   "url": "http://mdn.com/four.js",
                 },
                 "name": "four.js",
-                "path": "mdn.com/four.js",
+                "path": "FakeThread/mdn.com/four.js",
                 "type": "source",
               },
               Object {
                 "contents": Object {
                   "id": "server1.conn13.child1/39",
                   "introductionType": undefined,
                   "introductionUrl": null,
                   "isBlackBoxed": false,
                   "isExtension": false,
                   "isPrettyPrinted": false,
                   "isWasm": false,
                   "relativeUrl": "http://mdn.com/one.js",
                   "sourceMapURL": null,
                   "url": "http://mdn.com/one.js",
                 },
                 "name": "one.js",
-                "path": "mdn.com/one.js",
+                "path": "FakeThread/mdn.com/one.js",
                 "type": "source",
               },
               Object {
                 "contents": Object {
                   "id": "server1.conn13.child1/41",
                   "introductionType": undefined,
                   "introductionUrl": null,
                   "isBlackBoxed": false,
                   "isExtension": false,
                   "isPrettyPrinted": false,
                   "isWasm": false,
                   "relativeUrl": "http://mdn.com/three.js",
                   "sourceMapURL": null,
                   "url": "http://mdn.com/three.js",
                 },
                 "name": "three.js",
-                "path": "mdn.com/three.js",
+                "path": "FakeThread/mdn.com/three.js",
                 "type": "source",
               },
               Object {
                 "contents": Object {
                   "id": "server1.conn13.child1/40",
                   "introductionType": undefined,
                   "introductionUrl": null,
                   "isBlackBoxed": false,
                   "isExtension": false,
                   "isPrettyPrinted": false,
                   "isWasm": false,
                   "relativeUrl": "http://mdn.com/two.js",
                   "sourceMapURL": null,
                   "url": "http://mdn.com/two.js",
                 },
                 "name": "two.js",
-                "path": "mdn.com/two.js",
+                "path": "FakeThread/mdn.com/two.js",
                 "type": "source",
               },
             ],
             "name": "mdn.com",
-            "path": "mdn.com",
+            "path": "FakeThread/mdn.com",
+            "type": "directory",
+          },
+          Object {
+            "contents": Array [
+              Object {
+                "contents": Array [
+                  Object {
+                    "contents": Object {
+                      "id": "server1.conn13.child1/42",
+                      "introductionType": undefined,
+                      "introductionUrl": null,
+                      "isBlackBoxed": false,
+                      "isExtension": false,
+                      "isPrettyPrinted": false,
+                      "isWasm": false,
+                      "relativeUrl": "http://mdn.com/four.js",
+                      "sourceMapURL": "data:application/json?charset=utf?dsffewrsf",
+                      "url": "http://mdn.com/four.js",
+                    },
+                    "name": "four.js",
+                    "path": "FakeThread/mdn.com/four.js",
+                    "type": "source",
+                  },
+                  Object {
+                    "contents": Object {
+                      "id": "server1.conn13.child1/42/originalSource-sha",
+                      "introductionType": undefined,
+                      "introductionUrl": null,
+                      "isBlackBoxed": false,
+                      "isExtension": false,
+                      "isPrettyPrinted": false,
+                      "isWasm": false,
+                      "relativeUrl": "http://mdn.com/four.js",
+                      "sourceMapURL": null,
+                      "url": "http://mdn.com/four.js",
+                    },
+                    "name": "four.js",
+                    "path": "FakeThread/mdn.com/four.js",
+                    "type": "source",
+                  },
+                  Object {
+                    "contents": Object {
+                      "id": "server1.conn13.child1/39",
+                      "introductionType": undefined,
+                      "introductionUrl": null,
+                      "isBlackBoxed": false,
+                      "isExtension": false,
+                      "isPrettyPrinted": false,
+                      "isWasm": false,
+                      "relativeUrl": "http://mdn.com/one.js",
+                      "sourceMapURL": null,
+                      "url": "http://mdn.com/one.js",
+                    },
+                    "name": "one.js",
+                    "path": "FakeThread/mdn.com/one.js",
+                    "type": "source",
+                  },
+                  Object {
+                    "contents": Object {
+                      "id": "server1.conn13.child1/41",
+                      "introductionType": undefined,
+                      "introductionUrl": null,
+                      "isBlackBoxed": false,
+                      "isExtension": false,
+                      "isPrettyPrinted": false,
+                      "isWasm": false,
+                      "relativeUrl": "http://mdn.com/three.js",
+                      "sourceMapURL": null,
+                      "url": "http://mdn.com/three.js",
+                    },
+                    "name": "three.js",
+                    "path": "FakeThread/mdn.com/three.js",
+                    "type": "source",
+                  },
+                  Object {
+                    "contents": Object {
+                      "id": "server1.conn13.child1/40",
+                      "introductionType": undefined,
+                      "introductionUrl": null,
+                      "isBlackBoxed": false,
+                      "isExtension": false,
+                      "isPrettyPrinted": false,
+                      "isWasm": false,
+                      "relativeUrl": "http://mdn.com/two.js",
+                      "sourceMapURL": null,
+                      "url": "http://mdn.com/two.js",
+                    },
+                    "name": "two.js",
+                    "path": "FakeThread/mdn.com/two.js",
+                    "type": "source",
+                  },
+                ],
+                "name": "mdn.com",
+                "path": "FakeThread/mdn.com",
+                "type": "directory",
+              },
+            ],
+            "name": "FakeThread",
+            "path": "FakeThread",
             "type": "directory",
           },
         ]
       }
       itemHeight={21}
       key="full"
       onActivate={[Function]}
       onCollapse={[Function]}
--- a/devtools/client/debugger/src/components/PrimaryPanes/tests/__snapshots__/SourcesTreeItem.spec.js.snap
+++ b/devtools/client/debugger/src/components/PrimaryPanes/tests/__snapshots__/SourcesTreeItem.spec.js.snap
@@ -70,16 +70,21 @@ Object {
         "introductionUrl": null,
         "isBlackBoxed": false,
         "isExtension": false,
         "isPrettyPrinted": false,
         "isWasm": false,
         "relativeUrl": "http://mdn.com/one.js",
         "url": "http://mdn.com/one.js",
       },
+      "threads": Array [
+        Object {
+          "name": "Main Thread",
+        },
+      ],
       "toggleBlackBox": [MockFunction],
     },
     "refs": Object {},
     "state": null,
     "updater": Updater {
       "_callbacks": Array [],
       "_renderer": ReactShallowRenderer {
         "_context": Object {},
@@ -117,16 +122,23 @@ Object {
               "isBlackBoxed": false,
               "isExtension": false,
               "isPrettyPrinted": false,
               "isWasm": false,
               "relativeUrl": "http://mdn.com/one.js",
               "url": "http://mdn.com/one.js",
             }
           }
+          threads={
+            Array [
+              Object {
+                "name": "Main Thread",
+              },
+            ]
+          }
           toggleBlackBox={[MockFunction]}
         />,
         "_forcedUpdate": false,
         "_instance": [Circular],
         "_newState": null,
         "_rendered": <div
           className="node"
           onClick={[Function]}
@@ -193,16 +205,21 @@ Object {
       "introductionUrl": null,
       "isBlackBoxed": false,
       "isExtension": false,
       "isPrettyPrinted": false,
       "isWasm": false,
       "relativeUrl": "http://mdn.com/one.js",
       "url": "http://mdn.com/one.js",
     },
+    "threads": Array [
+      Object {
+        "name": "Main Thread",
+      },
+    ],
     "toggleBlackBox": [MockFunction],
   },
 }
 `;
 
 exports[`SourceTreeItem renderItem should show (mapped) for duplicate source items 1`] = `
 Object {
   "component": <div
@@ -280,16 +297,21 @@ Object {
         "introductionUrl": null,
         "isBlackBoxed": false,
         "isExtension": false,
         "isPrettyPrinted": false,
         "isWasm": false,
         "relativeUrl": "http://mdn.com/one.js",
         "url": "http://mdn.com/one.js",
       },
+      "threads": Array [
+        Object {
+          "name": "Main Thread",
+        },
+      ],
       "toggleBlackBox": [MockFunction],
     },
     "refs": Object {},
     "state": null,
     "updater": Updater {
       "_callbacks": Array [],
       "_renderer": ReactShallowRenderer {
         "_context": Object {},
@@ -329,16 +351,23 @@ Object {
               "isBlackBoxed": false,
               "isExtension": false,
               "isPrettyPrinted": false,
               "isWasm": false,
               "relativeUrl": "http://mdn.com/one.js",
               "url": "http://mdn.com/one.js",
             }
           }
+          threads={
+            Array [
+              Object {
+                "name": "Main Thread",
+              },
+            ]
+          }
           toggleBlackBox={[MockFunction]}
         />,
         "_forcedUpdate": false,
         "_instance": [Circular],
         "_newState": null,
         "_rendered": <div
           className="node"
           onClick={[Function]}
@@ -412,34 +441,51 @@ Object {
       "introductionUrl": null,
       "isBlackBoxed": false,
       "isExtension": false,
       "isPrettyPrinted": false,
       "isWasm": false,
       "relativeUrl": "http://mdn.com/one.js",
       "url": "http://mdn.com/one.js",
     },
+    "threads": Array [
+      Object {
+        "name": "Main Thread",
+      },
+    ],
     "toggleBlackBox": [MockFunction],
   },
 }
 `;
 
 exports[`SourceTreeItem renderItem should show domain item 1`] = `
 Object {
   "component": <div
     className="node"
     key="root"
     onClick={[Function]}
     onContextMenu={[Function]}
   >
     <span
       className="img no-arrow"
     />
-    <AccessibleImage
-      className="globe-small"
+    <Connect(SourceIcon)
+      source={
+        Object {
+          "id": "server1.conn13.child1/39",
+          "introductionType": undefined,
+          "introductionUrl": null,
+          "isBlackBoxed": false,
+          "isExtension": false,
+          "isPrettyPrinted": false,
+          "isWasm": false,
+          "relativeUrl": "http://mdn.com/one.js",
+          "url": "http://mdn.com/one.js",
+        }
+      }
     />
     <span
       className="label"
     >
        
       root
        
     </span>
@@ -482,16 +528,21 @@ Object {
         "introductionUrl": null,
         "isBlackBoxed": false,
         "isExtension": false,
         "isPrettyPrinted": false,
         "isWasm": false,
         "relativeUrl": "http://mdn.com/one.js",
         "url": "http://mdn.com/one.js",
       },
+      "threads": Array [
+        Object {
+          "name": "Main Thread",
+        },
+      ],
       "toggleBlackBox": [MockFunction],
     },
     "refs": Object {},
     "state": null,
     "updater": Updater {
       "_callbacks": Array [],
       "_renderer": ReactShallowRenderer {
         "_context": Object {},
@@ -531,31 +582,50 @@ Object {
               "isBlackBoxed": false,
               "isExtension": false,
               "isPrettyPrinted": false,
               "isWasm": false,
               "relativeUrl": "http://mdn.com/one.js",
               "url": "http://mdn.com/one.js",
             }
           }
+          threads={
+            Array [
+              Object {
+                "name": "Main Thread",
+              },
+            ]
+          }
           toggleBlackBox={[MockFunction]}
         />,
         "_forcedUpdate": false,
         "_instance": [Circular],
         "_newState": null,
         "_rendered": <div
           className="node"
           onClick={[Function]}
           onContextMenu={[Function]}
         >
           <span
             className="img no-arrow"
           />
-          <AccessibleImage
-            className="globe-small"
+          <Connect(SourceIcon)
+            source={
+              Object {
+                "id": "server1.conn13.child1/39",
+                "introductionType": undefined,
+                "introductionUrl": null,
+                "isBlackBoxed": false,
+                "isExtension": false,
+                "isPrettyPrinted": false,
+                "isWasm": false,
+                "relativeUrl": "http://mdn.com/one.js",
+                "url": "http://mdn.com/one.js",
+              }
+            }
           />
           <span
             className="label"
           >
              
             root
              
           </span>
@@ -597,16 +667,21 @@ Object {
       "introductionUrl": null,
       "isBlackBoxed": false,
       "isExtension": false,
       "isPrettyPrinted": false,
       "isWasm": false,
       "relativeUrl": "http://mdn.com/one.js",
       "url": "http://mdn.com/one.js",
     },
+    "threads": Array [
+      Object {
+        "name": "Main Thread",
+      },
+    ],
     "toggleBlackBox": [MockFunction],
   },
 }
 `;
 
 exports[`SourceTreeItem renderItem should show domain item as debuggee 1`] = `
 Object {
   "component": <div
@@ -614,17 +689,17 @@ Object {
     key="root"
     onClick={[Function]}
     onContextMenu={[Function]}
   >
     <AccessibleImage
       className="arrow"
     />
     <AccessibleImage
-      className="globe-small debuggee"
+      className="folder"
     />
     <span
       className="label"
     >
        
       http://mdn.com
        
     </span>
@@ -657,16 +732,21 @@ Object {
         "introductionUrl": null,
         "isBlackBoxed": false,
         "isExtension": false,
         "isPrettyPrinted": false,
         "isWasm": false,
         "relativeUrl": "http://mdn.com/one.js",
         "url": "http://mdn.com/one.js",
       },
+      "threads": Array [
+        Object {
+          "name": "Main Thread",
+        },
+      ],
       "toggleBlackBox": [MockFunction],
     },
     "refs": Object {},
     "state": null,
     "updater": Updater {
       "_callbacks": Array [],
       "_renderer": ReactShallowRenderer {
         "_context": Object {},
@@ -696,31 +776,38 @@ Object {
               "isBlackBoxed": false,
               "isExtension": false,
               "isPrettyPrinted": false,
               "isWasm": false,
               "relativeUrl": "http://mdn.com/one.js",
               "url": "http://mdn.com/one.js",
             }
           }
+          threads={
+            Array [
+              Object {
+                "name": "Main Thread",
+              },
+            ]
+          }
           toggleBlackBox={[MockFunction]}
         />,
         "_forcedUpdate": false,
         "_instance": [Circular],
         "_newState": null,
         "_rendered": <div
           className="node"
           onClick={[Function]}
           onContextMenu={[Function]}
         >
           <AccessibleImage
             className="arrow"
           />
           <AccessibleImage
-            className="globe-small debuggee"
+            className="folder"
           />
           <span
             className="label"
           >
              
             http://mdn.com
              
           </span>
@@ -752,16 +839,21 @@ Object {
       "introductionUrl": null,
       "isBlackBoxed": false,
       "isExtension": false,
       "isPrettyPrinted": false,
       "isWasm": false,
       "relativeUrl": "http://mdn.com/one.js",
       "url": "http://mdn.com/one.js",
     },
+    "threads": Array [
+      Object {
+        "name": "Main Thread",
+      },
+    ],
     "toggleBlackBox": [MockFunction],
   },
 }
 `;
 
 exports[`SourceTreeItem renderItem should show domain item as debuggee with focus and arrow 1`] = `
 Object {
   "component": <div
@@ -769,17 +861,17 @@ Object {
     key="root"
     onClick={[Function]}
     onContextMenu={[Function]}
   >
     <AccessibleImage
       className="arrow"
     />
     <AccessibleImage
-      className="globe-small debuggee"
+      className="folder"
     />
     <span
       className="label"
     >
        
       http://mdn.com
        
     </span>
@@ -813,16 +905,21 @@ Object {
         "introductionUrl": null,
         "isBlackBoxed": false,
         "isExtension": false,
         "isPrettyPrinted": false,
         "isWasm": false,
         "relativeUrl": "http://mdn.com/one.js",
         "url": "http://mdn.com/one.js",
       },
+      "threads": Array [
+        Object {
+          "name": "Main Thread",
+        },
+      ],
       "toggleBlackBox": [MockFunction],
     },
     "refs": Object {},
     "state": null,
     "updater": Updater {
       "_callbacks": Array [],
       "_renderer": ReactShallowRenderer {
         "_context": Object {},
@@ -853,31 +950,38 @@ Object {
               "isBlackBoxed": false,
               "isExtension": false,
               "isPrettyPrinted": false,
               "isWasm": false,
               "relativeUrl": "http://mdn.com/one.js",
               "url": "http://mdn.com/one.js",
             }
           }
+          threads={
+            Array [
+              Object {
+                "name": "Main Thread",
+              },
+            ]
+          }
           toggleBlackBox={[MockFunction]}
         />,
         "_forcedUpdate": false,
         "_instance": [Circular],
         "_newState": null,
         "_rendered": <div
           className="node focused"
           onClick={[Function]}
           onContextMenu={[Function]}
         >
           <AccessibleImage
             className="arrow"
           />
           <AccessibleImage
-            className="globe-small debuggee"
+            className="folder"
           />
           <span
             className="label"
           >
              
             http://mdn.com
              
           </span>
@@ -910,34 +1014,39 @@ Object {
       "introductionUrl": null,
       "isBlackBoxed": false,
       "isExtension": false,
       "isPrettyPrinted": false,
       "isWasm": false,
       "relativeUrl": "http://mdn.com/one.js",
       "url": "http://mdn.com/one.js",
     },
+    "threads": Array [
+      Object {
+        "name": "Main Thread",
+      },
+    ],
     "toggleBlackBox": [MockFunction],
   },
 }
 `;
 
 exports[`SourceTreeItem renderItem should show focused item for folder with expanded arrow 1`] = `
 Object {
   "component": <div
     className="node focused"
-    key="folder/"
+    key="domain/subfolder"
     onClick={[Function]}
     onContextMenu={[Function]}
   >
     <AccessibleImage
       className="arrow expanded"
     />
     <AccessibleImage
-      className="folder"
+      className="globe-small"
     />
     <span
       className="label"
     >
        
       folder
        
     </span>
@@ -953,24 +1062,29 @@ Object {
       "debuggeeUrl": "http://mdn.com",
       "depth": 1,
       "expanded": true,
       "focusItem": [MockFunction],
       "focused": true,
       "item": Object {
         "contents": Array [],
         "name": "folder",
-        "path": "folder/",
+        "path": "domain/subfolder",
         "type": "directory",
       },
       "projectRoot": "",
       "selectItem": [MockFunction],
       "setExpanded": [MockFunction],
       "setProjectDirectoryRoot": [MockFunction],
       "source": null,
+      "threads": Array [
+        Object {
+          "name": "Main Thread",
+        },
+      ],
       "toggleBlackBox": [MockFunction],
     },
     "refs": Object {},
     "state": null,
     "updater": Updater {
       "_callbacks": Array [],
       "_renderer": ReactShallowRenderer {
         "_context": Object {},
@@ -980,40 +1094,47 @@ Object {
           depth={1}
           expanded={true}
           focusItem={[MockFunction]}
           focused={true}
           item={
             Object {
               "contents": Array [],
               "name": "folder",
-              "path": "folder/",
+              "path": "domain/subfolder",
               "type": "directory",
             }
           }
           projectRoot=""
           selectItem={[MockFunction]}
           setExpanded={[MockFunction]}
           setProjectDirectoryRoot={[MockFunction]}
           source={null}
+          threads={
+            Array [
+              Object {
+                "name": "Main Thread",
+              },
+            ]
+          }
           toggleBlackBox={[MockFunction]}
         />,
         "_forcedUpdate": false,
         "_instance": [Circular],
         "_newState": null,
         "_rendered": <div
           className="node focused"
           onClick={[Function]}
           onContextMenu={[Function]}
         >
           <AccessibleImage
             className="arrow expanded"
           />
           <AccessibleImage
-            className="folder"
+            className="globe-small"
           />
           <span
             className="label"
           >
              
             folder
              
           </span>
@@ -1028,24 +1149,29 @@ Object {
     "debuggeeUrl": "http://mdn.com",
     "depth": 1,
     "expanded": true,
     "focusItem": [MockFunction],
     "focused": true,
     "item": Object {
       "contents": Array [],
       "name": "folder",
-      "path": "folder/",
+      "path": "domain/subfolder",
       "type": "directory",
     },
     "projectRoot": "",
     "selectItem": [MockFunction],
     "setExpanded": [MockFunction],
     "setProjectDirectoryRoot": [MockFunction],
     "source": null,
+    "threads": Array [
+      Object {
+        "name": "Main Thread",
+      },
+    ],
     "toggleBlackBox": [MockFunction],
   },
 }
 `;
 
 exports[`SourceTreeItem renderItem should show icon for angular item 1`] = `
 Object {
   "component": <div
@@ -1095,16 +1221,21 @@ Object {
         "introductionUrl": null,
         "isBlackBoxed": false,
         "isExtension": false,
         "isPrettyPrinted": false,
         "isWasm": false,
         "relativeUrl": "http://mdn.com/one.js",
         "url": "http://mdn.com/one.js",
       },
+      "threads": Array [
+        Object {
+          "name": "Main Thread",
+        },
+      ],
       "toggleBlackBox": [MockFunction],
     },
     "refs": Object {},
     "state": null,
     "updater": Updater {
       "_callbacks": Array [],
       "_renderer": ReactShallowRenderer {
         "_context": Object {},
@@ -1133,16 +1264,23 @@ Object {
               "isBlackBoxed": false,
               "isExtension": false,
               "isPrettyPrinted": false,
               "isWasm": false,
               "relativeUrl": "http://mdn.com/one.js",
               "url": "http://mdn.com/one.js",
             }
           }
+          threads={
+            Array [
+              Object {
+                "name": "Main Thread",
+              },
+            ]
+          }
           toggleBlackBox={[MockFunction]}
         />,
         "_forcedUpdate": false,
         "_instance": [Circular],
         "_newState": null,
         "_rendered": <div
           className="node"
           onClick={[Function]}
@@ -1188,26 +1326,31 @@ Object {
       "introductionUrl": null,
       "isBlackBoxed": false,
       "isExtension": false,
       "isPrettyPrinted": false,
       "isWasm": false,
       "relativeUrl": "http://mdn.com/one.js",
       "url": "http://mdn.com/one.js",
     },
+    "threads": Array [
+      Object {
+        "name": "Main Thread",
+      },
+    ],
     "toggleBlackBox": [MockFunction],
   },
 }
 `;
 
 exports[`SourceTreeItem renderItem should show icon for folder with arrow 1`] = `
 Object {
   "component": <div
     className="node"
-    key="folder/"
+    key="domain/subfolder"
     onClick={[Function]}
     onContextMenu={[Function]}
   >
     <AccessibleImage
       className="arrow"
     />
     <AccessibleImage
       className="folder"
@@ -1229,24 +1372,29 @@ Object {
     "props": Object {
       "clearProjectDirectoryRoot": [MockFunction],
       "debuggeeUrl": "http://mdn.com",
       "expanded": false,
       "focusItem": [MockFunction],
       "item": Object {
         "contents": Array [],
         "name": "folder",
-        "path": "folder/",
+        "path": "domain/subfolder",
         "type": "directory",
       },
       "projectRoot": "",
       "selectItem": [MockFunction],
       "setExpanded": [MockFunction],
       "setProjectDirectoryRoot": [MockFunction],
       "source": null,
+      "threads": Array [
+        Object {
+          "name": "Main Thread",
+        },
+      ],
       "toggleBlackBox": [MockFunction],
     },
     "refs": Object {},
     "state": null,
     "updater": Updater {
       "_callbacks": Array [],
       "_renderer": ReactShallowRenderer {
         "_context": Object {},
@@ -1254,25 +1402,32 @@ Object {
           clearProjectDirectoryRoot={[MockFunction]}
           debuggeeUrl="http://mdn.com"
           expanded={false}
           focusItem={[MockFunction]}
           item={
             Object {
               "contents": Array [],
               "name": "folder",
-              "path": "folder/",
+              "path": "domain/subfolder",
               "type": "directory",
             }
           }
           projectRoot=""
           selectItem={[MockFunction]}
           setExpanded={[MockFunction]}
           setProjectDirectoryRoot={[MockFunction]}
           source={null}
+          threads={
+            Array [
+              Object {
+                "name": "Main Thread",
+              },
+            ]
+          }
           toggleBlackBox={[MockFunction]}
         />,
         "_forcedUpdate": false,
         "_instance": [Circular],
         "_newState": null,
         "_rendered": <div
           className="node"
           onClick={[Function]}
@@ -1300,42 +1455,47 @@ Object {
   "props": Object {
     "clearProjectDirectoryRoot": [MockFunction],
     "debuggeeUrl": "http://mdn.com",
     "expanded": false,
     "focusItem": [MockFunction],
     "item": Object {
       "contents": Array [],
       "name": "folder",
-      "path": "folder/",
+      "path": "domain/subfolder",
       "type": "directory",
     },
     "projectRoot": "",
     "selectItem": [MockFunction],
     "setExpanded": [MockFunction],
     "setProjectDirectoryRoot": [MockFunction],
     "source": null,
+    "threads": Array [
+      Object {
+        "name": "Main Thread",
+      },
+    ],
     "toggleBlackBox": [MockFunction],
   },
 }
 `;
 
 exports[`SourceTreeItem renderItem should show icon for folder with expanded arrow 1`] = `
 Object {
   "component": <div
     className="node"
-    key="folder/"
+    key="domain/subfolder"
     onClick={[Function]}
     onContextMenu={[Function]}
   >
     <AccessibleImage
       className="arrow expanded"
     />
     <AccessibleImage
-      className="folder"
+      className="globe-small"
     />
     <span
       className="label"
     >
        
       folder
        
     </span>
@@ -1351,24 +1511,29 @@ Object {
       "debuggeeUrl": "http://mdn.com",
       "depth": 1,
       "expanded": true,
       "focusItem": [MockFunction],
       "focused": false,
       "item": Object {
         "contents": Array [],
         "name": "folder",
-        "path": "folder/",
+        "path": "domain/subfolder",
         "type": "directory",
       },
       "projectRoot": "",
       "selectItem": [MockFunction],
       "setExpanded": [MockFunction],
       "setProjectDirectoryRoot": [MockFunction],
       "source": null,
+      "threads": Array [
+        Object {
+          "name": "Main Thread",
+        },
+      ],
       "toggleBlackBox": [MockFunction],
     },
     "refs": Object {},
     "state": null,
     "updater": Updater {
       "_callbacks": Array [],
       "_renderer": ReactShallowRenderer {
         "_context": Object {},
@@ -1378,40 +1543,47 @@ Object {
           depth={1}
           expanded={true}
           focusItem={[MockFunction]}
           focused={false}
           item={
             Object {
               "contents": Array [],
               "name": "folder",
-              "path": "folder/",
+              "path": "domain/subfolder",
               "type": "directory",
             }
           }
           projectRoot=""
           selectItem={[MockFunction]}
           setExpanded={[MockFunction]}
           setProjectDirectoryRoot={[MockFunction]}
           source={null}
+          threads={
+            Array [
+              Object {
+                "name": "Main Thread",
+              },
+            ]
+          }
           toggleBlackBox={[MockFunction]}
         />,
         "_forcedUpdate": false,
         "_instance": [Circular],
         "_newState": null,
         "_rendered": <div
           className="node"
           onClick={[Function]}
           onContextMenu={[Function]}
         >
           <AccessibleImage
             className="arrow expanded"
           />
           <AccessibleImage
-            className="folder"
+            className="globe-small"
           />
           <span
             className="label"
           >
              
             folder
              
           </span>
@@ -1426,24 +1598,29 @@ Object {
     "debuggeeUrl": "http://mdn.com",
     "depth": 1,
     "expanded": true,
     "focusItem": [MockFunction],
     "focused": false,
     "item": Object {
       "contents": Array [],
       "name": "folder",
-      "path": "folder/",
+      "path": "domain/subfolder",
       "type": "directory",
     },
     "projectRoot": "",
     "selectItem": [MockFunction],
     "setExpanded": [MockFunction],
     "setProjectDirectoryRoot": [MockFunction],
     "source": null,
+    "threads": Array [
+      Object {
+        "name": "Main Thread",
+      },
+    ],
     "toggleBlackBox": [MockFunction],
   },
 }
 `;
 
 exports[`SourceTreeItem renderItem should show icon for moz-extension item 1`] = `
 Object {
   "component": <div
@@ -1451,17 +1628,17 @@ Object {
     key="moz-extension://e37c3c08-beac-a04b-8032-c4f699a1a856"
     onClick={[Function]}
     onContextMenu={[Function]}
   >
     <AccessibleImage
       className="arrow"
     />
     <AccessibleImage
-      className="extension"
+      className="folder"
     />
     <span
       className="label"
     >
        
       moz-extension://e37c3c08-beac-a04b-8032-c4f699a1a856
        
     </span>
@@ -1494,16 +1671,21 @@ Object {
         "introductionUrl": null,
         "isBlackBoxed": false,
         "isExtension": false,
         "isPrettyPrinted": false,
         "isWasm": false,
         "relativeUrl": "http://mdn.com/one.js",
         "url": "http://mdn.com/one.js",
       },
+      "threads": Array [
+        Object {
+          "name": "Main Thread",
+        },
+      ],
       "toggleBlackBox": [MockFunction],
     },
     "refs": Object {},
     "state": null,
     "updater": Updater {
       "_callbacks": Array [],
       "_renderer": ReactShallowRenderer {
         "_context": Object {},
@@ -1533,31 +1715,38 @@ Object {
               "isBlackBoxed": false,
               "isExtension": false,
               "isPrettyPrinted": false,
               "isWasm": false,
               "relativeUrl": "http://mdn.com/one.js",
               "url": "http://mdn.com/one.js",
             }
           }
+          threads={
+            Array [
+              Object {
+                "name": "Main Thread",
+              },
+            ]
+          }
           toggleBlackBox={[MockFunction]}
         />,
         "_forcedUpdate": false,
         "_instance": [Circular],
         "_newState": null,
         "_rendered": <div
           className="node"
           onClick={[Function]}
           onContextMenu={[Function]}
         >
           <AccessibleImage
             className="arrow"
           />
           <AccessibleImage
-            className="extension"
+            className="folder"
           />
           <span
             className="label"
           >
              
             moz-extension://e37c3c08-beac-a04b-8032-c4f699a1a856
              
           </span>
@@ -1589,16 +1778,21 @@ Object {
       "introductionUrl": null,
       "isBlackBoxed": false,
       "isExtension": false,
       "isPrettyPrinted": false,
       "isWasm": false,
       "relativeUrl": "http://mdn.com/one.js",
       "url": "http://mdn.com/one.js",
     },
+    "threads": Array [
+      Object {
+        "name": "Main Thread",
+      },
+    ],
     "toggleBlackBox": [MockFunction],
   },
 }
 `;
 
 exports[`SourceTreeItem renderItem should show icon for webpack item 1`] = `
 Object {
   "component": <div
@@ -1648,16 +1842,21 @@ Object {
         "introductionUrl": null,
         "isBlackBoxed": false,
         "isExtension": false,
         "isPrettyPrinted": false,
         "isWasm": false,
         "relativeUrl": "http://mdn.com/one.js",
         "url": "http://mdn.com/one.js",
       },
+      "threads": Array [
+        Object {
+          "name": "Main Thread",
+        },
+      ],
       "toggleBlackBox": [MockFunction],
     },
     "refs": Object {},
     "state": null,
     "updater": Updater {
       "_callbacks": Array [],
       "_renderer": ReactShallowRenderer {
         "_context": Object {},
@@ -1686,16 +1885,23 @@ Object {
               "isBlackBoxed": false,
               "isExtension": false,
               "isPrettyPrinted": false,
               "isWasm": false,
               "relativeUrl": "http://mdn.com/one.js",
               "url": "http://mdn.com/one.js",
             }
           }
+          threads={
+            Array [
+              Object {
+                "name": "Main Thread",
+              },
+            ]
+          }
           toggleBlackBox={[MockFunction]}
         />,
         "_forcedUpdate": false,
         "_instance": [Circular],
         "_newState": null,
         "_rendered": <div
           className="node"
           onClick={[Function]}
@@ -1741,16 +1947,21 @@ Object {
       "introductionUrl": null,
       "isBlackBoxed": false,
       "isExtension": false,
       "isPrettyPrinted": false,
       "isWasm": false,
       "relativeUrl": "http://mdn.com/one.js",
       "url": "http://mdn.com/one.js",
     },
+    "threads": Array [
+      Object {
+        "name": "Main Thread",
+      },
+    ],
     "toggleBlackBox": [MockFunction],
   },
 }
 `;
 
 exports[`SourceTreeItem renderItem should show source item with source icon 1`] = `
 Object {
   "component": <div
@@ -1822,16 +2033,21 @@ Object {
         "introductionUrl": null,
         "isBlackBoxed": false,
         "isExtension": false,
         "isPrettyPrinted": false,
         "isWasm": false,
         "relativeUrl": "http://mdn.com/one.js",
         "url": "http://mdn.com/one.js",
       },
+      "threads": Array [
+        Object {
+          "name": "Main Thread",
+        },
+      ],
       "toggleBlackBox": [MockFunction],
     },
     "refs": Object {},
     "state": null,
     "updater": Updater {
       "_callbacks": Array [],
       "_renderer": ReactShallowRenderer {
         "_context": Object {},
@@ -1870,16 +2086,23 @@ Object {
               "isBlackBoxed": false,
               "isExtension": false,
               "isPrettyPrinted": false,
               "isWasm": false,
               "relativeUrl": "http://mdn.com/one.js",
               "url": "http://mdn.com/one.js",
             }
           }
+          threads={
+            Array [
+              Object {
+                "name": "Main Thread",
+              },
+            ]
+          }
           toggleBlackBox={[MockFunction]}
         />,
         "_forcedUpdate": false,
         "_instance": [Circular],
         "_newState": null,
         "_rendered": <div
           className="node"
           onClick={[Function]}
@@ -1947,16 +2170,21 @@ Object {
       "introductionUrl": null,
       "isBlackBoxed": false,
       "isExtension": false,
       "isPrettyPrinted": false,
       "isWasm": false,
       "relativeUrl": "http://mdn.com/one.js",
       "url": "http://mdn.com/one.js",
     },
+    "threads": Array [
+      Object {
+        "name": "Main Thread",
+      },
+    ],
     "toggleBlackBox": [MockFunction],
   },
 }
 `;
 
 exports[`SourceTreeItem renderItem should show source item with source icon with focus 1`] = `
 Object {
   "component": <div
@@ -2029,16 +2257,21 @@ Object {
         "introductionUrl": null,
         "isBlackBoxed": false,
         "isExtension": false,
         "isPrettyPrinted": false,
         "isWasm": false,
         "relativeUrl": "http://mdn.com/one.js",
         "url": "http://mdn.com/one.js",
       },
+      "threads": Array [
+        Object {
+          "name": "Main Thread",
+        },
+      ],
       "toggleBlackBox": [MockFunction],
     },
     "refs": Object {},
     "state": null,
     "updater": Updater {
       "_callbacks": Array [],
       "_renderer": ReactShallowRenderer {
         "_context": Object {},
@@ -2078,16 +2311,23 @@ Object {
               "isBlackBoxed": false,
               "isExtension": false,
               "isPrettyPrinted": false,
               "isWasm": false,
               "relativeUrl": "http://mdn.com/one.js",
               "url": "http://mdn.com/one.js",
             }
           }
+          threads={
+            Array [
+              Object {
+                "name": "Main Thread",
+              },
+            ]
+          }
           toggleBlackBox={[MockFunction]}
         />,
         "_forcedUpdate": false,
         "_instance": [Circular],
         "_newState": null,
         "_rendered": <div
           className="node focused"
           onClick={[Function]}
@@ -2156,16 +2396,21 @@ Object {
       "introductionUrl": null,
       "isBlackBoxed": false,
       "isExtension": false,
       "isPrettyPrinted": false,
       "isWasm": false,
       "relativeUrl": "http://mdn.com/one.js",
       "url": "http://mdn.com/one.js",
     },
+    "threads": Array [
+      Object {
+        "name": "Main Thread",
+      },
+    ],
     "toggleBlackBox": [MockFunction],
   },
 }
 `;
 
 exports[`SourceTreeItem renderItem should unescape escaped source URLs 1`] = `
 Object {
   "component": <div
@@ -2237,16 +2482,21 @@ Object {
         "introductionUrl": null,
         "isBlackBoxed": false,
         "isExtension": false,
         "isPrettyPrinted": false,
         "isWasm": false,
         "relativeUrl": "http://mdn.com/one.js",
         "url": "http://mdn.com/one.js",
       },
+      "threads": Array [
+        Object {
+          "name": "Main Thread",
+        },
+      ],
       "toggleBlackBox": [MockFunction],
     },
     "refs": Object {},
     "state": null,
     "updater": Updater {
       "_callbacks": Array [],
       "_renderer": ReactShallowRenderer {
         "_context": Object {},
@@ -2285,16 +2535,23 @@ Object {
               "isBlackBoxed": false,
               "isExtension": false,
               "isPrettyPrinted": false,
               "isWasm": false,
               "relativeUrl": "http://mdn.com/one.js",
               "url": "http://mdn.com/one.js",
             }
           }
+          threads={
+            Array [
+              Object {
+                "name": "Main Thread",
+              },
+            ]
+          }
           toggleBlackBox={[MockFunction]}
         />,
         "_forcedUpdate": false,
         "_instance": [Circular],
         "_newState": null,
         "_rendered": <div
           className="node"
           onClick={[Function]}
@@ -2362,12 +2619,17 @@ Object {
       "introductionUrl": null,
       "isBlackBoxed": false,
       "isExtension": false,
       "isPrettyPrinted": false,
       "isWasm": false,
       "relativeUrl": "http://mdn.com/one.js",
       "url": "http://mdn.com/one.js",
     },
+    "threads": Array [
+      Object {
+        "name": "Main Thread",
+      },
+    ],
     "toggleBlackBox": [MockFunction],
   },
 }
 `;
--- a/devtools/client/debugger/src/components/QuickOpenModal.js
+++ b/devtools/client/debugger/src/components/QuickOpenModal.js
@@ -425,21 +425,22 @@ export class QuickOpenModal extends Comp
       </Modal>
     );
   }
 }
 
 /* istanbul ignore next: ignoring testing of redux connection stuff */
 function mapStateToProps(state) {
   const selectedSource = getSelectedSource(state);
+  const displayedSources = getDisplayedSourcesList(state);
 
   return {
     cx: getContext(state),
     enabled: getQuickOpenEnabled(state),
-    sources: formatSources(getDisplayedSourcesList(state), getTabs(state)),
+    sources: formatSources(displayedSources, getTabs(state)),
     selectedSource,
     selectedContentLoaded: selectedSource
       ? !!getSourceContent(state, selectedSource.id)
       : undefined,
     symbols: formatSymbols(getSymbols(state, selectedSource)),
     symbolsLoading: isSymbolsLoading(state, selectedSource),
     query: getQuickOpenQuery(state),
     searchType: getQuickOpenType(state),
--- a/devtools/client/debugger/src/components/SecondaryPanes/Breakpoints/Breakpoint.js
+++ b/devtools/client/debugger/src/components/SecondaryPanes/Breakpoints/Breakpoint.js
@@ -73,16 +73,18 @@ class Breakpoint extends PureComponent<P
     const { breakpoint, selectedSource } = this.props;
     return getSelectedLocation(breakpoint, selectedSource);
   }
 
   onDoubleClick = () => {
     const { breakpoint, openConditionalPanel } = this.props;
     if (breakpoint.options.condition) {
       openConditionalPanel(this.selectedLocation);
+    } else if (breakpoint.options.logValue) {
+      openConditionalPanel(this.selectedLocation, true);
     }
   };
 
   selectBreakpoint = event => {
     event.preventDefault();
     const { cx, selectSpecificLocation } = this.props;
     selectSpecificLocation(cx, this.selectedLocation);
   };
--- a/devtools/client/debugger/src/components/SecondaryPanes/Worker.js
+++ b/devtools/client/debugger/src/components/SecondaryPanes/Worker.js
@@ -39,17 +39,17 @@ export class Worker extends Component<Pr
       <div
         className={classnames("worker", {
           selected: thread.actor == currentThread
         })}
         key={thread.actor}
         onClick={this.onSelectThread}
       >
         <div className="icon">
-          <AccessibleImage className={worker ? "worker" : "file"} />
+          <AccessibleImage className={worker ? "worker" : "window"} />
         </div>
         <div className="label">{label}</div>
         {isPaused ? (
           <div className="pause-badge">
             <AccessibleImage className="pause" />
           </div>
         ) : null}
       </div>
--- a/devtools/client/debugger/src/components/shared/AccessibleImage.css
+++ b/devtools/client/debugger/src/components/shared/AccessibleImage.css
@@ -75,16 +75,21 @@ html[dir="rtl"] .img.arrow {
   mask-image: url(resource://devtools/client/debugger/images/globe.svg);
 }
 
 .img.globe-small {
   mask-image: url(resource://devtools/client/debugger/images/globe-small.svg);
   mask-size: 12px 12px;
 }
 
+.img.window {
+  mask-image: url(resource://devtools/client/debugger/images/window.svg);
+  mask-size: 12px 12px;
+}
+
 .img.file {
   mask-image: url(resource://devtools/client/debugger/images/file-small.svg);
   mask-size: 12px 12px;
 }
 
 .img.folder {
   mask-image: url(resource://devtools/client/debugger/images/folder.svg);
 }
--- a/devtools/client/debugger/src/components/shared/BracketArrow.css
+++ b/devtools/client/debugger/src/components/shared/BracketArrow.css
@@ -1,14 +1,15 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
 
 .bracket-arrow {
   position: absolute;
+  pointer-events: none;
 }
 
 .bracket-arrow::before,
 .bracket-arrow::after {
   content: "";
   height: 0;
   width: 0;
   position: absolute;
--- a/devtools/client/debugger/src/reducers/debuggee.js
+++ b/devtools/client/debugger/src/reducers/debuggee.js
@@ -5,74 +5,91 @@
 // @flow
 
 /**
  * Debuggee reducer
  * @module reducers/debuggee
  */
 
 import { sortBy } from "lodash";
+import { createSelector } from "reselect";
+
 import { getDisplayName } from "../utils/workers";
 
-import type { MainThread, WorkerList } from "../types";
+import type { Selector } from "./types";
+import type { MainThread, WorkerList, Thread } from "../types";
 import type { Action } from "../actions/types";
 
 export type DebuggeeState = {
   workers: WorkerList,
   mainThread: MainThread
 };
 
 export function initialDebuggeeState(): DebuggeeState {
-  return { workers: [], mainThread: { actor: "", url: "", type: -1 } };
+  return {
+    workers: [],
+    mainThread: { actor: "", url: "", type: -1, name: "" }
+  };
 }
 
 export default function debuggee(
   state: DebuggeeState = initialDebuggeeState(),
   action: Action
 ): DebuggeeState {
   switch (action.type) {
     case "CONNECT":
       return {
         ...state,
-        mainThread: action.mainThread
+        mainThread: { ...action.mainThread, name: L10N.getStr("mainThread") }
       };
     case "INSERT_WORKERS":
-      return {
-        ...state,
-        workers: [...state.workers, ...action.workers]
-      };
+      return insertWorkers(state, action.workers);
     case "REMOVE_WORKERS":
       const { workers } = action;
       return {
         ...state,
         workers: state.workers.filter(w => !workers.includes(w.actor))
       };
     case "NAVIGATE":
       return {
         ...initialDebuggeeState(),
         mainThread: action.mainThread
       };
     default:
       return state;
   }
 }
 
+function insertWorkers(state, workers) {
+  const formatedWorkers = workers.map(worker => ({
+    ...worker,
+    name: getDisplayName(worker)
+  }));
+
+  return {
+    ...state,
+    workers: [...state.workers, ...formatedWorkers]
+  };
+}
+
 export const getWorkers = (state: OuterState) => state.debuggee.workers;
 
 export const getWorkerCount = (state: OuterState) => getWorkers(state).length;
 
 export function getWorkerByThread(state: OuterState, thread: string) {
   return getWorkers(state).find(worker => worker.actor == thread);
 }
 
 export function getMainThread(state: OuterState): MainThread {
   return state.debuggee.mainThread;
 }
 
 export function getDebuggeeUrl(state: OuterState): string {
   return getMainThread(state).url;
 }
 
-export function getThreads(state: OuterState) {
-  return [getMainThread(state), ...sortBy(getWorkers(state), getDisplayName)];
-}
+export const getThreads: Selector<Thread[]> = createSelector(
+  getMainThread,
+  getWorkers,
+  (mainThread, workers) => [mainThread, ...sortBy(workers, getDisplayName)]
+);
 
 type OuterState = { debuggee: DebuggeeState };
--- a/devtools/client/debugger/src/reducers/source-tree.js
+++ b/devtools/client/debugger/src/reducers/source-tree.js
@@ -4,46 +4,55 @@
 
 // @flow
 
 /**
  * Source tree reducer
  * @module reducers/source-tree
  */
 
-import type { SourceTreeAction } from "../actions/types";
+import type { SourceTreeAction, FocusItem } from "../actions/types";
 
 export type SourceTreeState = {
-  expanded: { [string]: Set<string> }
+  expanded: Set<string>,
+  focusedItem: ?FocusItem
 };
 
 export function InitialState(): SourceTreeState {
   return {
-    expanded: {}
+    expanded: new Set(),
+    focusedItem: null
   };
 }
 
 export default function update(
   state: SourceTreeState = InitialState(),
   action: SourceTreeAction
 ): SourceTreeState {
   switch (action.type) {
     case "SET_EXPANDED_STATE":
       return updateExpanded(state, action);
+
+    case "SET_FOCUSED_SOURCE_ITEM":
+      return { ...state, focusedItem: action.item };
   }
 
   return state;
 }
 
 function updateExpanded(state, action) {
   return {
     ...state,
-    expanded: { ...state.expanded, [action.thread]: new Set(action.expanded) }
+    expanded: new Set(action.expanded)
   };
 }
 
 type OuterState = {
   sourceTree: SourceTreeState
 };
 
-export function getExpandedState(state: OuterState, thread: string) {
-  return state.sourceTree.expanded[thread];
+export function getExpandedState(state: OuterState) {
+  return state.sourceTree.expanded;
 }
+
+export function getFocusedSourceItem(state: OuterState): ?FocusItem {
+  return state.sourceTree.focusedItem;
+}
--- a/devtools/client/debugger/src/reducers/sources.js
+++ b/devtools/client/debugger/src/reducers/sources.js
@@ -63,18 +63,19 @@ import type { PendingSelectedLocation, S
 import type { Action, DonePromiseAction, FocusItem } from "../actions/types";
 import type { LoadSourceAction } from "../actions/types/SourceAction";
 import { uniq } from "lodash";
 
 export type SourcesMap = { [SourceId]: Source };
 type SourcesContentMap = {
   [SourceId]: AsyncValue<SourceContent> | null
 };
+export type SourcesMapByThread = { [ThreadId]: SourcesMap };
+
 export type BreakpointPositionsMap = { [SourceId]: BreakpointPositions };
-export type SourcesMapByThread = { [ThreadId]: SourcesMap };
 type SourceActorMap = { [SourceId]: Array<SourceActorId> };
 
 type UrlsMap = { [string]: SourceId[] };
 type PlainUrlsMap = { [string]: string[] };
 
 type SourceResource = Resource<{
   ...Source
 }>;
@@ -847,19 +848,20 @@ const getDisplayedSourceIDs: GetDisplaye
           sourceIDsByThread[thread] = new Set();
         }
         sourceIDsByThread[thread].add(sourceId);
       }
     }
     return sourceIDsByThread;
   }
 );
+
 type GetDisplayedSourcesSelector = (
   OuterState & SourceActorOuterState
-) => { [ThreadId]: { [SourceId]: Source } };
+) => SourcesMapByThread;
 export const getDisplayedSources: GetDisplayedSourcesSelector = createSelector(
   state => state.sources.sources,
   getDisplayedSourceIDs,
   (sources, idsByThread) => {
     const result = {};
 
     for (const thread of Object.keys(idsByThread)) {
       for (const id of idsByThread[thread]) {
@@ -869,27 +871,16 @@ export const getDisplayedSources: GetDis
         result[thread][id] = getResource(sources, id);
       }
     }
 
     return result;
   }
 );
 
-export function getDisplayedSourcesForThread(
-  state: OuterState & SourceActorOuterState,
-  thread: string
-): SourcesMap {
-  return getDisplayedSources(state)[thread] || {};
-}
-
-export function getFocusedSourceItem(state: OuterState): ?FocusItem {
-  return state.sources.focusedItem;
-}
-
 export function getSourceActorsForSource(
   state: OuterState & SourceActorOuterState,
   id: SourceId
 ): Array<SourceActor> {
   const actors = state.sources.actors[id];
   if (!actors) {
     return [];
   }
--- a/devtools/client/debugger/src/reducers/tests/sources.spec.js
+++ b/devtools/client/debugger/src/reducers/tests/sources.spec.js
@@ -76,53 +76,49 @@ describe("sources reducer", () => {
 describe("sources selectors", () => {
   it("should return all extensions when chrome preference enabled", () => {
     prefs.chromeAndExtenstionsEnabled = true;
     let state = initialSourcesState();
     state = {
       sources: update(state, {
         type: "ADD_SOURCES",
         cx: mockcx,
-        // coercing to a Source for the purpose of this test
         sources: ((mockedSources: any): Source[])
       }),
       sourceActors: undefined
     };
     const insertAction = {
       type: "INSERT_SOURCE_ACTORS",
       items: mockSourceActors
     };
     state = {
       sources: update(state.sources, insertAction),
       sourceActors: updateSourceActors(state.sourceActors, insertAction)
     };
-    const selectedDisplayedSources = getDisplayedSources(state);
-    const threadSources = selectedDisplayedSources.foo;
-    expect(Object.values(threadSources)).toHaveLength(3);
+    const threadSources = getDisplayedSources(state);
+    expect(Object.values(threadSources.foo)).toHaveLength(3);
   });
 
   it("should omit all extensions when chrome preference enabled", () => {
     prefs.chromeAndExtenstionsEnabled = false;
     let state = initialSourcesState();
     state = {
       sources: update(state, {
         type: "ADD_SOURCES",
         cx: mockcx,
-        // coercing to a Source for the purpose of this test
         sources: ((mockedSources: any): Source[])
       }),
       sourceActors: undefined
     };
 
     const insertAction = {
       type: "INSERT_SOURCE_ACTORS",
       items: mockSourceActors
     };
 
     state = {
       sources: update(state.sources, insertAction),
       sourceActors: updateSourceActors(state.sourceActors, insertAction)
     };
-    const selectedDisplayedSources = getDisplayedSources(state);
-    const threadSources = selectedDisplayedSources.foo;
-    expect(Object.values(threadSources)).toHaveLength(1);
+    const threadSources = getDisplayedSources(state);
+    expect(Object.values(threadSources.foo)).toHaveLength(1);
   });
 });
--- a/devtools/client/debugger/src/types.js
+++ b/devtools/client/debugger/src/types.js
@@ -442,23 +442,25 @@ export type Scope = {|
     parameterNames: string[]
   },
   type: string
 |};
 
 export type MainThread = {
   +actor: ThreadId,
   +url: string,
-  +type: number
+  +type: number,
+  +name: string
 };
 
 export type Worker = {
   +actor: ThreadId,
   +url: string,
-  +type: number
+  +type: number,
+  +name: string
 };
 
 export type Thread = MainThread & Worker;
 export type ThreadList = Array<Thread>;
 export type WorkerList = Array<Worker>;
 
 export type Cancellable = {
   cancel: () => void
--- a/devtools/client/debugger/src/utils/source.js
+++ b/devtools/client/debugger/src/utils/source.js
@@ -486,15 +486,15 @@ export function getSourceQueryString(sou
   if (!source) {
     return;
   }
 
   return parseURL(getRawSourceURL(source.url)).search;
 }
 
 export function isUrlExtension(url: string) {
-  return /^(chrome|moz)-extension:\//.test(url);
+  return /\/?(chrome|moz)-extension:\//.test(url);
 }
 
 export function getPlainUrl(url: string): string {
   const queryStart = url.indexOf("?");
   return queryStart !== -1 ? url.slice(0, queryStart) : url;
 }
--- a/devtools/client/debugger/src/utils/sources-tree/addToTree.js
+++ b/devtools/client/debugger/src/utils/sources-tree/addToTree.js
@@ -85,25 +85,35 @@ function findOrCreateNode(
 /*
  * walk the source tree to the final node for a given url,
  * adding new nodes along the way
  */
 function traverseTree(
   url: ParsedURL,
   tree: TreeDirectory,
   debuggeeHost: ?string,
-  source: Source
+  source: Source,
+  thread: string
 ): TreeNode {
   const parts = url.path.split("/").filter(p => p !== "");
   parts.unshift(url.group);
+  if (thread) {
+    parts.unshift(thread);
+  }
 
   let path = "";
   return parts.reduce((subTree, part, index) => {
-    path = path ? `${path}/${part}` : part;
-    const debuggeeHostIfRoot = index === 0 ? debuggeeHost : null;
+    if (index == 0 && thread) {
+      path = thread;
+    } else {
+      path = `${path}/${part}`;
+    }
+
+    const debuggeeHostIfRoot = index === 1 ? debuggeeHost : null;
+
     return findOrCreateNode(
       parts,
       subTree,
       path,
       part,
       index,
       url,
       debuggeeHostIfRoot,
@@ -160,21 +170,22 @@ function addSourceToNode(
 
 /**
  * @memberof utils/sources-tree
  * @static
  */
 export function addToTree(
   tree: TreeDirectory,
   source: Source,
-  debuggeeHost: ?string
+  debuggeeHost: ?string,
+  thread: string
 ) {
   const url = getURL(source, debuggeeHost);
 
   if (isInvalidUrl(url, source)) {
     return;
   }
 
-  const finalNode = traverseTree(url, tree, debuggeeHost, source);
+  const finalNode = traverseTree(url, tree, debuggeeHost, source, thread);
 
   // $FlowIgnore
   finalNode.contents = addSourceToNode(finalNode, url, source);
 }
--- a/devtools/client/debugger/src/utils/sources-tree/collapseTree.js
+++ b/devtools/client/debugger/src/utils/sources-tree/collapseTree.js
@@ -13,18 +13,19 @@ import type { TreeDirectory, TreeNode } 
  */
 function _collapseTree(node: TreeNode, depth: number): TreeNode {
   // Node is a folder.
   if (node.type === "directory") {
     if (!Array.isArray(node.contents)) {
       console.log(`Expected array at: ${node.path}`);
     }
 
-    // Node is not a root/domain node, and only contains 1 item.
-    if (depth > 1 && node.contents.length === 1) {
+    // Node is not a (1) thread and (2) root/domain node,
+    // and only contains 1 item.
+    if (depth > 2 && node.contents.length === 1) {
       const next = node.contents[0];
       // Do not collapse if the next node is a leaf node.
       if (next.type === "directory") {
         if (!Array.isArray(next.contents)) {
           console.log(
             `Expected array at: ${next.name} -- ${
               node.name
             } -- ${JSON.stringify(next.contents)}`
deleted file mode 100644
--- a/devtools/client/debugger/src/utils/sources-tree/createTree.js
+++ /dev/null
@@ -1,36 +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/>. */
-
-// @flow
-
-import { addToTree } from "./addToTree";
-import { collapseTree } from "./collapseTree";
-import { createDirectoryNode, createParentMap } from "./utils";
-import { getDomain } from "./treeOrder";
-
-import type { SourcesMap } from "../../reducers/types";
-import type { TreeDirectory } from "./types";
-
-type Params = {
-  sources: SourcesMap,
-  debuggeeUrl: string
-};
-
-export function createTree({ sources, debuggeeUrl }: Params) {
-  const uncollapsedTree = createDirectoryNode("root", "", []);
-  const debuggeeHost = getDomain(debuggeeUrl);
-
-  for (const sourceId in sources) {
-    const source = sources[sourceId];
-    addToTree(uncollapsedTree, source, debuggeeHost);
-  }
-
-  const sourceTree = collapseTree((uncollapsedTree: TreeDirectory));
-
-  return {
-    uncollapsedTree,
-    sourceTree,
-    parentMap: createParentMap(sourceTree)
-  };
-}
--- a/devtools/client/debugger/src/utils/sources-tree/index.js
+++ b/devtools/client/debugger/src/utils/sources-tree/index.js
@@ -6,16 +6,15 @@
 
 /**
  * Utils for Sources Tree Component
  * @module utils/sources-tree
  */
 
 export { addToTree } from "./addToTree";
 export { collapseTree } from "./collapseTree";
-export { createTree } from "./createTree";
 export { formatTree } from "./formatTree";
 export { getDirectories } from "./getDirectories";
 export { getFilenameFromPath, getURL } from "./getURL";
-export { sortEntireTree, sortTree } from "./sortTree";
-export { updateTree } from "./updateTree";
+export { sortTree } from "./sortTree";
+export { createTree, updateTree } from "./updateTree";
 
 export * from "./utils";
--- a/devtools/client/debugger/src/utils/sources-tree/moz.build
+++ b/devtools/client/debugger/src/utils/sources-tree/moz.build
@@ -5,17 +5,16 @@
 
 DIRS += [
 
 ]
 
 CompiledModules(
     'addToTree.js',
     'collapseTree.js',
-    'createTree.js',
     'formatTree.js',
     'getDirectories.js',
     'getURL.js',
     'index.js',
     'sortTree.js',
     'treeOrder.js',
     'updateTree.js',
     'utils.js',
--- a/devtools/client/debugger/src/utils/sources-tree/sortTree.js
+++ b/devtools/client/debugger/src/utils/sources-tree/sortTree.js
@@ -9,35 +9,16 @@ import { nodeHasChildren, isExactUrlMatc
 import type { TreeDirectory } from "./types";
 
 /**
  * Look at the nodes in the source tree, and determine the index of where to
  * insert a new node. The ordering is index -> folder -> file.
  * @memberof utils/sources-tree
  * @static
  */
-export function sortEntireTree(
-  tree: TreeDirectory,
-  debuggeeUrl: string = ""
-): TreeDirectory {
-  if (nodeHasChildren(tree)) {
-    const contents = sortTree(tree, debuggeeUrl).map((subtree: any) =>
-      sortEntireTree(subtree)
-    );
-    return { ...tree, contents };
-  }
-  return tree;
-}
-
-/**
- * Look at the nodes in the source tree, and determine the index of where to
- * insert a new node. The ordering is index -> folder -> file.
- * @memberof utils/sources-tree
- * @static
- */
 export function sortTree(tree: TreeDirectory, debuggeeUrl: string = "") {
   return (tree.contents: any).sort((previousNode, currentNode) => {
     const currentNodeIsDir = nodeHasChildren(currentNode);
     const previousNodeIsDir = nodeHasChildren(previousNode);
     if (currentNode.name === "(index)") {
       return 1;
     } else if (previousNode.name === "(index)") {
       return -1;
--- a/devtools/client/debugger/src/utils/sources-tree/tests/__snapshots__/addToTree.spec.js.snap
+++ b/devtools/client/debugger/src/utils/sources-tree/tests/__snapshots__/addToTree.spec.js.snap
@@ -1,83 +1,96 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
 exports[`sources-tree addToTree can add a file to an intermediate directory 1`] = `
 " - root path= 
-  - unpkg.com path=unpkg.com 
-    - codemirror@5.1 path=unpkg.com/codemirror@5.1 
-      - mode path=unpkg.com/codemirror@5.1/mode 
-        - xml path=unpkg.com/codemirror@5.1/mode/xml 
-          - xml.js path=unpkg.com/codemirror@5.1/mode/xml/xml.js source_id=server1.conn13.child1/39 
-    - codemirror@5.1 path=unpkg.com/codemirror@5.1 source_id=server1.conn13.child1/37 
+  - FakeThread path=FakeThread 
+    - unpkg.com path=FakeThread/unpkg.com 
+      - codemirror@5.1 path=FakeThread/unpkg.com/codemirror@5.1 
+        - mode path=FakeThread/unpkg.com/codemirror@5.1/mode 
+          - xml path=FakeThread/unpkg.com/codemirror@5.1/mode/xml 
+            - xml.js path=FakeThread/unpkg.com/codemirror@5.1/mode/xml/xml.js source_id=server1.conn13.child1/39 
+      - codemirror@5.1 path=FakeThread/unpkg.com/codemirror@5.1 source_id=server1.conn13.child1/37 
 "
 `;
 
 exports[`sources-tree addToTree correctly parses file sources 1`] = `
 " - root path= 
-  - file:// path=file:// 
-    - a path=file:///a 
-      - b.js path=file:///a/b.js source_id=actor1 
+  - FakeThread path=FakeThread 
+    - file:// path=FakeThread/file:// 
+      - a path=FakeThread/file:///a 
+        - b.js path=FakeThread/file:///a/b.js source_id=actor1 
 "
 `;
 
 exports[`sources-tree addToTree does not attempt to add two of the same directory 1`] = `
 " - root path= 
-  - davidwalsh.name path=davidwalsh.name 
-    - (index) path=https://davidwalsh.name/ source_id=server1.conn13.child1/37 
-    - wp-content path=davidwalsh.name/wp-content 
-      - prism.js path=davidwalsh.name/wp-content/prism.js source_id=server1.conn13.child1/39 
+  - FakeThread path=FakeThread 
+    - davidwalsh.name path=FakeThread/davidwalsh.name 
+      - (index) path=https://davidwalsh.name/ source_id=server1.conn13.child1/37 
+      - wp-content path=FakeThread/davidwalsh.name/wp-content 
+        - prism.js path=FakeThread/davidwalsh.name/wp-content/prism.js source_id=server1.conn13.child1/39 
 "
 `;
 
 exports[`sources-tree addToTree does not attempt to add two of the same file 1`] = `
 " - root path= 
-  - davidwalsh.name path=davidwalsh.name 
-    - (index) path=https://davidwalsh.name/ source_id=server1.conn13.child1/37 
+  - FakeThread path=FakeThread 
+    - davidwalsh.name path=FakeThread/davidwalsh.name 
+      - (index) path=https://davidwalsh.name/ source_id=server1.conn13.child1/39 
+      - util.js path=FakeThread/davidwalsh.name/util.js source_id=server1.conn13.child1/37 
+  - FakeThread2 path=FakeThread2 
+    - davidwalsh.name path=FakeThread2/davidwalsh.name 
+      - util.js path=FakeThread2/davidwalsh.name/util.js source_id=server1.conn13.child1/37 
 "
 `;
 
 exports[`sources-tree addToTree does not mangle encoded URLs 1`] = `
 " - root path= 
-  - example.com path=example.com 
-    - foo path=example.com/foo 
-      - B9724220.131821496;dc_ver=42.111;sz=468x60;u_sd=2;dc_adk=2020465299;ord=a53rpc;dc_rfl=1,https%3A%2F%2Fdavidwalsh.name%2F$0;xdt=1 path=example.com/foo/B9724220.131821496;dc_ver=42.111;sz=468x60;u_sd=2;dc_adk=2020465299;ord=a53rpc;dc_rfl=1,https%3A%2F%2Fdavidwalsh.name%2F$0;xdt=1 source_id=actor1 
+  - FakeThread path=FakeThread 
+    - example.com path=FakeThread/example.com 
+      - foo path=FakeThread/example.com/foo 
+        - B9724220.131821496;dc_ver=42.111;sz=468x60;u_sd=2;dc_adk=2020465299;ord=a53rpc;dc_rfl=1,https%3A%2F%2Fdavidwalsh.name%2F$0;xdt=1 path=FakeThread/example.com/foo/B9724220.131821496;dc_ver=42.111;sz=468x60;u_sd=2;dc_adk=2020465299;ord=a53rpc;dc_rfl=1,https%3A%2F%2Fdavidwalsh.name%2F$0;xdt=1 source_id=actor1 
 "
 `;
 
 exports[`sources-tree addToTree excludes javascript: URLs from the tree 1`] = `
 " - root path= 
-  - example.com path=example.com 
-    - source1.js path=example.com/source1.js source_id=actor2 
+  - FakeThread path=FakeThread 
+    - example.com path=FakeThread/example.com 
+      - source1.js path=FakeThread/example.com/source1.js source_id=actor2 
 "
 `;
 
 exports[`sources-tree addToTree name does not include query params 1`] = `
 " - root path= 
-  - example.com path=example.com 
-    - foo path=example.com/foo 
-      - name.js path=example.com/foo/name.js source_id=actor1 
+  - FakeThread path=FakeThread 
+    - example.com path=FakeThread/example.com 
+      - foo path=FakeThread/example.com/foo 
+        - name.js path=FakeThread/example.com/foo/name.js source_id=actor1 
 "
 `;
 
 exports[`sources-tree addToTree replaces a file with a directory 1`] = `
 " - root path= 
-  - unpkg.com path=unpkg.com 
-    - codemirror@5.1 path=unpkg.com/codemirror@5.1 
-      - mode path=unpkg.com/codemirror@5.1/mode 
-        - xml path=unpkg.com/codemirror@5.1/mode/xml 
-          - xml.js path=unpkg.com/codemirror@5.1/mode/xml/xml.js source_id=server1.conn13.child1/39 
-    - codemirror@5.1 path=unpkg.com/codemirror@5.1 source_id=server1.conn13.child1/37 
+  - FakeThread path=FakeThread 
+    - unpkg.com path=FakeThread/unpkg.com 
+      - codemirror@5.1 path=FakeThread/unpkg.com/codemirror@5.1 
+        - mode path=FakeThread/unpkg.com/codemirror@5.1/mode 
+          - xml path=FakeThread/unpkg.com/codemirror@5.1/mode/xml 
+            - xml.js path=FakeThread/unpkg.com/codemirror@5.1/mode/xml/xml.js source_id=server1.conn13.child1/39 
+      - codemirror@5.1 path=FakeThread/unpkg.com/codemirror@5.1 source_id=server1.conn13.child1/37 
 "
 `;
 
 exports[`sources-tree addToTree supports data URLs 1`] = `
 " - root path= 
-  - (no domain) path=(no domain) 
-    - data:text/html,<script>console.log(123)</script> path=data:text/html,<script>console.log(123)</script> source_id=server1.conn13.child1/39 
+  - FakeThread path=FakeThread 
+    - (no domain) path=FakeThread/(no domain) 
+      - data:text/html,<script>console.log(123)</script> path=data:text/html,<script>console.log(123)</script> source_id=server1.conn13.child1/39 
 "
 `;
 
 exports[`sources-tree addToTree uses debuggeeUrl as default 1`] = `
 " - root path= 
   - localhost:4242 path=localhost:4242 
     - components path=localhost:4242/components 
       - Header.js path=localhost:4242/components/Header.js source_id=undefined 
--- a/devtools/client/debugger/src/utils/sources-tree/tests/__snapshots__/collapseTree.spec.js.snap
+++ b/devtools/client/debugger/src/utils/sources-tree/tests/__snapshots__/collapseTree.spec.js.snap
@@ -1,38 +1,42 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
 exports[`sources tree collapseTree can collapse a single source 1`] = `
 " - root path= 
-  - example.com path=example.com 
-    - a/b path=example.com/a/b 
-      - c.js path=example.com/a/b/c.js source_id=actor1 
+  - Main Thread path=Main Thread 
+    - example.com path=Main Thread/example.com 
+      - a/b path=Main Thread/example.com/a/b 
+        - c.js path=Main Thread/example.com/a/b/c.js source_id=actor1 
 "
 `;
 
 exports[`sources tree collapseTree correctly merges in a collapsed source with a deeper level 1`] = `
 " - root path= 
-  - example.com path=example.com 
-    - a/b path=example.com/a/b 
-      - c/d path=example.com/a/b/c/d 
-        - e.js path=example.com/a/b/c/d/e.js source_id=actor2 
-      - c.js path=example.com/a/b/c.js source_id=actor1 
+  - Main Thread path=Main Thread 
+    - example.com path=Main Thread/example.com 
+      - a/b path=Main Thread/example.com/a/b 
+        - c/d path=Main Thread/example.com/a/b/c/d 
+          - e.js path=Main Thread/example.com/a/b/c/d/e.js source_id=actor2 
+        - c.js path=Main Thread/example.com/a/b/c.js source_id=actor1 
 "
 `;
 
 exports[`sources tree collapseTree correctly merges in a collapsed source with a shallower level 1`] = `
 " - root path= 
-  - example.com path=example.com 
-    - a/b path=example.com/a/b 
-      - c.js path=example.com/a/b/c.js source_id=actor1 
-      - x.js path=example.com/a/b/x.js source_id=actor3 
+  - Main Thread path=Main Thread 
+    - example.com path=Main Thread/example.com 
+      - a/b path=Main Thread/example.com/a/b 
+        - c.js path=Main Thread/example.com/a/b/c.js source_id=actor1 
+        - x.js path=Main Thread/example.com/a/b/x.js source_id=actor3 
 "
 `;
 
 exports[`sources tree collapseTree correctly merges in a collapsed source with the same level 1`] = `
 " - root path= 
-  - example.com path=example.com 
-    - a/b path=example.com/a/b 
-      - c/d path=example.com/a/b/c/d 
-        - e.js path=example.com/a/b/c/d/e.js source_id=actor2 
-      - c.js path=example.com/a/b/c.js source_id=actor1 
+  - Main Thread path=Main Thread 
+    - example.com path=Main Thread/example.com 
+      - a/b path=Main Thread/example.com/a/b 
+        - c/d path=Main Thread/example.com/a/b/c/d 
+          - e.js path=Main Thread/example.com/a/b/c/d/e.js source_id=actor2 
+        - c.js path=Main Thread/example.com/a/b/c.js source_id=actor1 
 "
 `;
deleted file mode 100644
--- a/devtools/client/debugger/src/utils/sources-tree/tests/__snapshots__/sortTree.spec.js.snap
+++ /dev/null
@@ -1,73 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`sources-tree sortEntireTree alphabetically sorts children 1`] = `
-" - root path= 
-  - example.com path=example.com 
-    - foo path=example.com/foo 
-      - a_source3.js path=example.com/foo/a_source3.js source_id=actor3 
-      - b_source2.js path=example.com/foo/b_source2.js source_id=actor2 
-    - source1.js path=example.com/source1.js source_id=actor1 
-"
-`;
-
-exports[`sources-tree sortEntireTree puts folder at the top of the sort 1`] = `
-" - root path= 
-  - example.com path=example.com 
-    - folder path=example.com/folder 
-      - b path=example.com/folder/b 
-        - b.js path=example.com/folder/b/b.js source_id=actor2 
-      - c path=example.com/folder/c 
-        - (index) path=http://example.com/folder/c/ source_id=actor3 
-      - a.js path=example.com/folder/a.js source_id=actor1 
-"
-`;
-
-exports[`sources-tree sortEntireTree puts root debugee url at the top of the sort 1`] = `
-" - root path= 
-  - example.com path=example.com 
-    - b.js path=example.com/b.js source_id=actor2 
-  - api.example.com path=api.example.com 
-    - a.js path=api.example.com/a.js source_id=actor1 
-  - demo.com path=demo.com 
-    - c.js path=demo.com/c.js source_id=actor3 
-"
-`;
-
-exports[`sources-tree sortEntireTree puts root debugee url at the top of the sort 2`] = `
-" - root path= 
-  - demo.com path=demo.com 
-    - c.js path=demo.com/c.js source_id=actor3 
-  - api.example.com path=api.example.com 
-    - a.js path=api.example.com/a.js source_id=actor1 
-  - example.com path=example.com 
-    - b.js path=example.com/b.js source_id=actor2 
-"
-`;
-
-exports[`sources-tree sortEntireTree sorts folders first 1`] = `
-" - root path= 
-  - example.com path=example.com 
-    - (index) path=http://example.com source_id=actor4 
-    - b.js path=example.com/b.js 
-      - b_source.js path=example.com/b.js/b_source.js source_id=actor2 
-    - d path=example.com/d 
-      - d_source.js path=example.com/d/d_source.js source_id=actor5 
-    - a.js path=example.com/a.js source_id=actor1 
-    - b2 path=example.com/b2 source_id=actor6 
-    - c.js path=example.com/c.js source_id=actor3 
-"
-`;
-
-exports[`sources-tree sortEntireTree sorts folders first 2`] = `
-" - root path= 
-  - example.com path=example.com 
-    - (index) path=http://example.com source_id=actor4 
-    - b.js path=example.com/b.js 
-      - b_source.js path=example.com/b.js/b_source.js source_id=actor2 
-    - d path=example.com/d 
-      - d_source.js path=example.com/d/d_source.js source_id=actor5 
-    - a.js path=example.com/a.js source_id=actor1 
-    - b2 path=example.com/b2 source_id=actor6 
-    - c.js path=example.com/c.js source_id=actor3 
-"
-`;
--- a/devtools/client/debugger/src/utils/sources-tree/tests/__snapshots__/updateTree.spec.js.snap
+++ b/devtools/client/debugger/src/utils/sources-tree/tests/__snapshots__/updateTree.spec.js.snap
@@ -3,155 +3,161 @@
 exports[`calls updateTree.js adds one source 1`] = `
 "{
   \\"type\\": \\"directory\\",
   \\"name\\": \\"root\\",
   \\"path\\": \\"\\",
   \\"contents\\": [
     {
       \\"type\\": \\"directory\\",
-      \\"name\\": \\"davidwalsh.name\\",
-      \\"path\\": \\"davidwalsh.name\\",
+      \\"name\\": \\"FakeThread\\",
+      \\"path\\": \\"FakeThread\\",
       \\"contents\\": [
         {
-          \\"type\\": \\"source\\",
-          \\"name\\": \\"(index)\\",
-          \\"path\\": \\"https://davidwalsh.name/\\",
-          \\"contents\\": {
-            \\"id\\": \\"server1.conn13.child1/39\\",
-            \\"url\\": \\"https://davidwalsh.name/\\",
-            \\"isBlackBoxed\\": false,
-            \\"isPrettyPrinted\\": false,
-            \\"relativeUrl\\": \\"https://davidwalsh.name/\\",
-            \\"introductionUrl\\": null,
-            \\"isWasm\\": false,
-            \\"isExtension\\": false
-          }
-        },
-        {
-          \\"type\\": \\"source\\",
-          \\"name\\": \\"source1.js\\",
-          \\"path\\": \\"davidwalsh.name/source1.js\\",
-          \\"contents\\": {
-            \\"id\\": \\"server1.conn13.child1/37\\",
-            \\"url\\": \\"https://davidwalsh.name/source1.js\\",
-            \\"isBlackBoxed\\": false,
-            \\"isPrettyPrinted\\": false,
-            \\"relativeUrl\\": \\"https://davidwalsh.name/source1.js\\",
-            \\"introductionUrl\\": null,
-            \\"isWasm\\": false,
-            \\"isExtension\\": false
-          }
+          \\"type\\": \\"directory\\",
+          \\"name\\": \\"davidwalsh.name\\",
+          \\"path\\": \\"FakeThread/davidwalsh.name\\",
+          \\"contents\\": [
+            {
+              \\"type\\": \\"source\\",
+              \\"name\\": \\"(index)\\",
+              \\"path\\": \\"https://davidwalsh.name/\\",
+              \\"contents\\": {
+                \\"id\\": \\"server1.conn13.child1/39\\",
+                \\"url\\": \\"https://davidwalsh.name/\\",
+                \\"isBlackBoxed\\": false,
+                \\"isPrettyPrinted\\": false,
+                \\"relativeUrl\\": \\"https://davidwalsh.name/\\",
+                \\"introductionUrl\\": null,
+                \\"isWasm\\": false,
+                \\"isExtension\\": false
+              }
+            },
+            {
+              \\"type\\": \\"source\\",
+              \\"name\\": \\"source1.js\\",
+              \\"path\\": \\"FakeThread/davidwalsh.name/source1.js\\",
+              \\"contents\\": {
+                \\"id\\": \\"server1.conn13.child1/37\\",
+                \\"url\\": \\"https://davidwalsh.name/source1.js\\",
+                \\"isBlackBoxed\\": false,
+                \\"isPrettyPrinted\\": false,
+                \\"relativeUrl\\": \\"https://davidwalsh.name/source1.js\\",
+                \\"introductionUrl\\": null,
+                \\"isWasm\\": false,
+                \\"isExtension\\": false
+              }
+            }
+          ]
<