Merge mozilla-central to mozilla-inbound
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Tue, 27 Oct 2015 10:57:40 +0100
changeset 304862 66d59599465c708d4dc985c774c99ccc26aca69d
parent 304861 f1f6a7ae585eb6ed1eb6caebdf4e491fbfa87fd4 (current diff)
parent 304842 9a8f2342fb3116d23989087e026448d38a3768c5 (diff)
child 304863 f0fe043c7b4cd4f7a8c747cadcf6523a188840ea
push id1001
push userraliiev@mozilla.com
push dateMon, 18 Jan 2016 19:06:03 +0000
treeherdermozilla-release@8b89261f3ac4 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone44.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge mozilla-central to mozilla-inbound
browser/locales/en-US/chrome/browser/devtools/app-manager.dtd
devtools/client/app-manager/builtin-adb-store.js
devtools/client/app-manager/connection-store.js
devtools/client/app-manager/content/connection-footer.js
devtools/client/app-manager/content/connection-footer.xhtml
devtools/client/app-manager/content/device.js
devtools/client/app-manager/content/device.xhtml
devtools/client/app-manager/content/help.xhtml
devtools/client/app-manager/content/index.js
devtools/client/app-manager/content/index.xul
devtools/client/app-manager/content/manifest-editor.js
devtools/client/app-manager/content/projects.js
devtools/client/app-manager/content/projects.xhtml
devtools/client/app-manager/content/template.js
devtools/client/app-manager/content/utils.js
devtools/client/app-manager/device-store.js
devtools/client/app-manager/simulators-store.js
devtools/client/app-manager/test/browser.ini
devtools/client/app-manager/test/browser_manifest_editor.js
devtools/client/app-manager/test/head.js
devtools/client/app-manager/test/hosted_app.manifest
devtools/client/app-manager/test/manifest.webapp
devtools/client/app-manager/test/test_connection_store.html
devtools/client/app-manager/test/test_device_store.html
devtools/client/app-manager/test/test_projects_store.html
devtools/client/app-manager/test/test_remain_connected.html
devtools/client/app-manager/test/test_template.html
devtools/client/app-manager/webapps-store.js
devtools/client/themes/app-manager/connection-footer.css
devtools/client/themes/app-manager/device.css
devtools/client/themes/app-manager/help.css
devtools/client/themes/app-manager/images/add.svg
devtools/client/themes/app-manager/images/error.svg
devtools/client/themes/app-manager/images/index-icons.svg
devtools/client/themes/app-manager/images/plus.svg
devtools/client/themes/app-manager/images/remove.svg
devtools/client/themes/app-manager/images/warning.svg
devtools/client/themes/app-manager/index.css
devtools/client/themes/app-manager/manifest-editor.inc.css
devtools/client/themes/app-manager/projects.css
mobile/android/base/LayoutInterceptor.java
mobile/android/base/menu/MenuItemActionView.java
mobile/android/base/resources/layout/menu_item_action_view.xml
mobile/android/base/sync/TabReceivedBroadcastReceiver.java
--- a/b2g/chrome/content/settings.js
+++ b/b2g/chrome/content/settings.js
@@ -116,17 +116,25 @@ SettingsListener.observe('language.curre
   if (!((new RegExp('^' + value + '[^a-z-_] *[,;]?', 'i')).test(intl))) {
     value = value + ', ' + intl;
   } else {
     value = intl;
   }
   Services.prefs.setCharPref(prefName, value);
 
   if (shell.hasStarted() == false) {
-    shell.bootstrap();
+    // On b2gdroid at first run we need to synchronize our wallpaper with
+    // Android one's before bootstrapping.
+    if (AppConstants.MOZ_B2GDROID) {
+      Cc["@mozilla.org/b2g/b2gdroid-setup;1"]
+        .getService().wrappedJSObject.setWallpaper()
+        .then(() => { shell.bootstrap(); });
+    } else {
+      shell.bootstrap();
+    }
   }
 });
 
 // =================== RIL ====================
 (function RILSettingsToPrefs() {
   // DSDS default service IDs
   ['mms', 'sms', 'telephony', 'voicemail'].forEach(function(key) {
     SettingsListener.observe('ril.' + key + '.defaultServiceId', 0,
--- a/b2g/config/aries/sources.xml
+++ b/b2g/config/aries/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="8d83715f08b7849f16a0dfc88f78d5c3a89c0a54">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="b564b21e08ffc3e4962f08850843c7482932ee7b"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="b6ede3d0fdec5fc922e9ca3401e60db461bf705c"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="fake-qemu-kernel" path="prebuilts/qemu-kernel" remote="b2g" revision="939b377d55a2f081d94029a30a75d05e5a20daf3"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="956700d9754349b630a34551750ae6353614b6aa"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="3b9a47b517d345b8d98bc7f787b9a6c2f51ca75d"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="5f931350fbc87c3df9db8b0ceb37734b8b471593"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="48d8c7c950745f1b166b42125e6f0d3293d71636"/>
--- a/b2g/config/dolphin/sources.xml
+++ b/b2g/config/dolphin/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="8d83715f08b7849f16a0dfc88f78d5c3a89c0a54">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="b564b21e08ffc3e4962f08850843c7482932ee7b"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="b6ede3d0fdec5fc922e9ca3401e60db461bf705c"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="fake-qemu-kernel" path="prebuilts/qemu-kernel" remote="b2g" revision="939b377d55a2f081d94029a30a75d05e5a20daf3"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="956700d9754349b630a34551750ae6353614b6aa"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="3b9a47b517d345b8d98bc7f787b9a6c2f51ca75d"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="5f931350fbc87c3df9db8b0ceb37734b8b471593"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="48d8c7c950745f1b166b42125e6f0d3293d71636"/>
--- a/b2g/config/emulator-ics/sources.xml
+++ b/b2g/config/emulator-ics/sources.xml
@@ -14,17 +14,17 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="refs/tags/android-4.0.4_r2.1" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="1b0db93fb6b870b03467aff50d6419771ba0d88c">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="b564b21e08ffc3e4962f08850843c7482932ee7b"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="b6ede3d0fdec5fc922e9ca3401e60db461bf705c"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="956700d9754349b630a34551750ae6353614b6aa"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="4ace9aaee0e048dfda11bb787646c59982a3dc80"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="c72c9278ddc2f442d193474993d36e7f2cfb08c4"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="3b9a47b517d345b8d98bc7f787b9a6c2f51ca75d"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="34ea6163f9f0e0122fb0bb03607eccdca31ced7a"/>
   <project name="platform_hardware_libhardware_moz" path="hardware/libhardware_moz" remote="b2g" revision="fdf3a143dc777e5f9d33a88373af7ea161d3b440"/>
   <!-- Stock Android things -->
--- a/b2g/config/emulator-jb/sources.xml
+++ b/b2g/config/emulator-jb/sources.xml
@@ -12,17 +12,17 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="660169a3d7e034a892359e39135e8c2785a6ad6f">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="b564b21e08ffc3e4962f08850843c7482932ee7b"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="b6ede3d0fdec5fc922e9ca3401e60db461bf705c"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="956700d9754349b630a34551750ae6353614b6aa"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="3b9a47b517d345b8d98bc7f787b9a6c2f51ca75d"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="0c28789b9957913be975eb002a22323f93585d4c"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="5f931350fbc87c3df9db8b0ceb37734b8b471593"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="48d8c7c950745f1b166b42125e6f0d3293d71636"/>
   <project name="platform_hardware_libhardware_moz" path="hardware/libhardware_moz" remote="b2g" revision="fdf3a143dc777e5f9d33a88373af7ea161d3b440"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.1" path="prebuilts/clang/linux-x86/3.1" revision="5c45f43419d5582949284eee9cef0c43d866e03b"/>
--- a/b2g/config/emulator-kk/sources.xml
+++ b/b2g/config/emulator-kk/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="8d83715f08b7849f16a0dfc88f78d5c3a89c0a54">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="b564b21e08ffc3e4962f08850843c7482932ee7b"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="b6ede3d0fdec5fc922e9ca3401e60db461bf705c"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="956700d9754349b630a34551750ae6353614b6aa"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="3b9a47b517d345b8d98bc7f787b9a6c2f51ca75d"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="5f931350fbc87c3df9db8b0ceb37734b8b471593"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="48d8c7c950745f1b166b42125e6f0d3293d71636"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="0c28789b9957913be975eb002a22323f93585d4c"/>
--- a/b2g/config/emulator-l/sources.xml
+++ b/b2g/config/emulator-l/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="c9d4fe680662ee44a4bdea42ae00366f5df399cf">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="b564b21e08ffc3e4962f08850843c7482932ee7b"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="b6ede3d0fdec5fc922e9ca3401e60db461bf705c"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="956700d9754349b630a34551750ae6353614b6aa"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="3b9a47b517d345b8d98bc7f787b9a6c2f51ca75d"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="5f931350fbc87c3df9db8b0ceb37734b8b471593"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="48d8c7c950745f1b166b42125e6f0d3293d71636"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="0c28789b9957913be975eb002a22323f93585d4c"/>
--- a/b2g/config/emulator/sources.xml
+++ b/b2g/config/emulator/sources.xml
@@ -14,17 +14,17 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="refs/tags/android-4.0.4_r2.1" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="1b0db93fb6b870b03467aff50d6419771ba0d88c">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="b564b21e08ffc3e4962f08850843c7482932ee7b"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="b6ede3d0fdec5fc922e9ca3401e60db461bf705c"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="956700d9754349b630a34551750ae6353614b6aa"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="4ace9aaee0e048dfda11bb787646c59982a3dc80"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="c72c9278ddc2f442d193474993d36e7f2cfb08c4"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="3b9a47b517d345b8d98bc7f787b9a6c2f51ca75d"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="34ea6163f9f0e0122fb0bb03607eccdca31ced7a"/>
   <project name="platform_hardware_libhardware_moz" path="hardware/libhardware_moz" remote="b2g" revision="fdf3a143dc777e5f9d33a88373af7ea161d3b440"/>
   <!-- Stock Android things -->
--- a/b2g/config/flame-kk/sources.xml
+++ b/b2g/config/flame-kk/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="8d83715f08b7849f16a0dfc88f78d5c3a89c0a54">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="b564b21e08ffc3e4962f08850843c7482932ee7b"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="b6ede3d0fdec5fc922e9ca3401e60db461bf705c"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="fake-qemu-kernel" path="prebuilts/qemu-kernel" remote="b2g" revision="939b377d55a2f081d94029a30a75d05e5a20daf3"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="956700d9754349b630a34551750ae6353614b6aa"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="3b9a47b517d345b8d98bc7f787b9a6c2f51ca75d"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="5f931350fbc87c3df9db8b0ceb37734b8b471593"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="48d8c7c950745f1b166b42125e6f0d3293d71636"/>
--- a/b2g/config/gaia.json
+++ b/b2g/config/gaia.json
@@ -1,9 +1,9 @@
 {
     "git": {
-        "git_revision": "b564b21e08ffc3e4962f08850843c7482932ee7b", 
+        "git_revision": "b6ede3d0fdec5fc922e9ca3401e60db461bf705c", 
         "remote": "https://git.mozilla.org/releases/gaia.git", 
         "branch": ""
     }, 
-    "revision": "bc907981bd62560e8d0079244bdc32b1dfe98e36", 
+    "revision": "220b45ec153f267a2efc58275a30a665a4ec9e57", 
     "repo_path": "integration/gaia-central"
 }
--- a/b2g/config/nexus-4-kk/sources.xml
+++ b/b2g/config/nexus-4-kk/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="8d83715f08b7849f16a0dfc88f78d5c3a89c0a54">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="b564b21e08ffc3e4962f08850843c7482932ee7b"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="b6ede3d0fdec5fc922e9ca3401e60db461bf705c"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="fake-qemu-kernel" path="prebuilts/qemu-kernel" remote="b2g" revision="939b377d55a2f081d94029a30a75d05e5a20daf3"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="956700d9754349b630a34551750ae6353614b6aa"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="3b9a47b517d345b8d98bc7f787b9a6c2f51ca75d"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="5f931350fbc87c3df9db8b0ceb37734b8b471593"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="48d8c7c950745f1b166b42125e6f0d3293d71636"/>
--- a/b2g/config/nexus-4/sources.xml
+++ b/b2g/config/nexus-4/sources.xml
@@ -13,17 +13,17 @@
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="660169a3d7e034a892359e39135e8c2785a6ad6f">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="fake-qemu-kernel" path="prebuilts/qemu-kernel" remote="b2g" revision="939b377d55a2f081d94029a30a75d05e5a20daf3"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="b564b21e08ffc3e4962f08850843c7482932ee7b"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="b6ede3d0fdec5fc922e9ca3401e60db461bf705c"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="956700d9754349b630a34551750ae6353614b6aa"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="3b9a47b517d345b8d98bc7f787b9a6c2f51ca75d"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="0c28789b9957913be975eb002a22323f93585d4c"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="5f931350fbc87c3df9db8b0ceb37734b8b471593"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="48d8c7c950745f1b166b42125e6f0d3293d71636"/>
   <project name="platform_hardware_libhardware_moz" path="hardware/libhardware_moz" remote="b2g" revision="fdf3a143dc777e5f9d33a88373af7ea161d3b440"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.1" path="prebuilts/clang/linux-x86/3.1" revision="5c45f43419d5582949284eee9cef0c43d866e03b"/>
--- a/b2g/config/nexus-5-l/sources.xml
+++ b/b2g/config/nexus-5-l/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="c9d4fe680662ee44a4bdea42ae00366f5df399cf">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="b564b21e08ffc3e4962f08850843c7482932ee7b"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="b6ede3d0fdec5fc922e9ca3401e60db461bf705c"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="fake-qemu-kernel" path="prebuilts/qemu-kernel" remote="b2g" revision="939b377d55a2f081d94029a30a75d05e5a20daf3"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="956700d9754349b630a34551750ae6353614b6aa"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="3b9a47b517d345b8d98bc7f787b9a6c2f51ca75d"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="5f931350fbc87c3df9db8b0ceb37734b8b471593"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="48d8c7c950745f1b166b42125e6f0d3293d71636"/>
--- a/b2g/simulator/bootstrap.js
+++ b/b2g/simulator/bootstrap.js
@@ -1,66 +1,4 @@
-var Ci = Components.interfaces;
-var Cu = Components.utils;
-
-Cu.import("resource://gre/modules/Services.jsm");
-
-// Useful piece of code from :bent
-// http://mxr.mozilla.org/mozilla-central/source/dom/workers/test/extensions/bootstrap/bootstrap.js
-function registerAddonResourceHandler(data) {
-  let file = data.installPath;
-  let fileuri = file.isDirectory() ?
-                Services.io.newFileURI(file) :
-                Services.io.newURI("jar:" + file.path + "!/", null, null);
-  let resourceName = encodeURIComponent(data.id.replace("@", "at"));
-
-  Services.io.getProtocolHandler("resource").
-              QueryInterface(Ci.nsIResProtocolHandler).
-              setSubstitution(resourceName, fileuri);
-
-  return "resource://" + resourceName + "/";
-}
-
-var mainModule;
-
+function startup(data, reason) {}
+function shutdown(data, reason) {}
 function install(data, reason) {}
 function uninstall(data, reason) {}
-
-function startup(data, reason) {
-  let uri = registerAddonResourceHandler(data);
-
-  let loaderModule =
-    Cu.import('resource://gre/modules/commonjs/toolkit/loader.js').Loader;
-  let { Loader, Require, Main } = loaderModule;
-
-  const { ConsoleAPI } = Cu.import("resource://gre/modules/Console.jsm");
-
-  let loader = Loader({
-    paths: {
-      "./": uri + "lib/",
-      "": "resource://gre/modules/commonjs/"
-    },
-    globals: {
-      console: new ConsoleAPI({
-        prefix: data.id
-      })
-    },
-    modules: {
-      "toolkit/loader": loaderModule,
-      addon: {
-        id: data.id,
-        version: data.version,
-        uri: uri
-      }
-    }
-  });
-
-  let require_ = Require(loader, { id: "./addon" });
-  mainModule = require_("./main");
-}
-
-function shutdown(data, reason) {
-  if (mainModule && mainModule.shutdown) {
-    mainModule.shutdown();
-  }
-}
-
-function uninstall(data, reason) {}
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -446,16 +446,18 @@ pref("browser.tabs.drawInTitlebar", true
 // browser.link.open_newwindow being set to 3 and target="_blank" etc are
 // closed:
 // true   return to the tab that opened this tab (its owner)
 // false  return to the adjacent tab (old default)
 pref("browser.tabs.selectOwnerOnClose", true);
 
 pref("browser.tabs.showAudioPlayingIcon", true);
 
+pref("browser.tabs.dontfocusfordialogs", true);
+
 pref("browser.ctrlTab.previews", false);
 
 // By default, do not export HTML at shutdown.
 // If true, at shutdown the bookmarks in your menu and toolbar will
 // be exported as HTML to the bookmarks.html file.
 pref("browser.bookmarks.autoExportHTML",          false);
 
 // The maximum number of daily bookmark backups to
--- a/browser/base/content/browser-menubar.inc
+++ b/browser/base/content/browser-menubar.inc
@@ -538,19 +538,16 @@
                 <menupopup id="menuWebDeveloperPopup">
                   <menuitem id="menu_devToolbox"
                             observes="devtoolsMenuBroadcaster_DevToolbox"
                             accesskey="&devToolboxMenuItem.accesskey;"/>
                   <menuseparator id="menu_devtools_separator"/>
                   <menuitem id="menu_devToolbar"
                             observes="devtoolsMenuBroadcaster_DevToolbar"
                             accesskey="&devToolbarMenu.accesskey;"/>
-                  <menuitem id="menu_devAppMgr"
-                            observes="devtoolsMenuBroadcaster_DevAppMgr"
-                            accesskey="&devAppMgrMenu.accesskey;"/>
                   <menuitem id="menu_webide"
                             observes="devtoolsMenuBroadcaster_webide"
                             accesskey="&webide.accesskey;"/>
                   <menuitem id="menu_browserToolbox"
                             observes="devtoolsMenuBroadcaster_BrowserToolbox"
                             accesskey="&browserToolboxMenu.accesskey;"/>
                   <menuitem id="menu_browserContentToolbox"
                             observes="devtoolsMenuBroadcaster_BrowserContentToolbox"
--- a/browser/base/content/browser-sets.inc
+++ b/browser/base/content/browser-sets.inc
@@ -94,17 +94,16 @@
     <command id="Browser:RestoreLastSession" oncommand="restoreLastSession();" disabled="true"/>
     <command id="Browser:NewUserContextTab" oncommand="openNewUserContextTab(event.sourceEvent);" reserved="true"/>
 
     <command id="Tools:Search" oncommand="BrowserSearch.webSearch();"/>
     <command id="Tools:Downloads" oncommand="BrowserDownloadsUI();"/>
     <command id="Tools:DevToolbox" oncommand="gDevToolsBrowser.toggleToolboxCommand(gBrowser);"/>
     <command id="Tools:DevToolbar" oncommand="DeveloperToolbar.toggle();" disabled="true" hidden="true"/>
     <command id="Tools:DevToolbarFocus" oncommand="DeveloperToolbar.focusToggle();" disabled="true"/>
-    <command id="Tools:DevAppMgr" oncommand="gDevToolsBrowser.openAppManager(gBrowser);" disabled="true" hidden="true"/>
     <command id="Tools:WebIDE" oncommand="gDevToolsBrowser.openWebIDE();" disabled="true" hidden="true"/>
     <command id="Tools:BrowserToolbox" oncommand="BrowserToolboxProcess.init();" disabled="true" hidden="true"/>
     <command id="Tools:BrowserContentToolbox" oncommand="gDevToolsBrowser.openContentProcessToolbox();" disabled="true" hidden="true"/>
     <command id="Tools:BrowserConsole" oncommand="HUDService.openBrowserConsoleOrFocus();"/>
     <command id="Tools:Scratchpad" oncommand="Scratchpad.openScratchpad();"/>
     <command id="Tools:ResponsiveUI" oncommand="ResponsiveUI.toggle();"/>
     <command id="Tools:Eyedropper" oncommand="openEyedropper();"/>
     <command id="Tools:Addons" oncommand="BrowserOpenAddonsMgr();"/>
@@ -194,19 +193,16 @@
                  type="checkbox" autocheck="false"
                  command="Tools:DevToolbox"
                  key="key_devToolboxMenuItem"/>
     <broadcaster id="devtoolsMenuBroadcaster_DevToolbar"
                  label="&devToolbarMenu.label;"
                  type="checkbox" autocheck="false"
                  command="Tools:DevToolbar"
                  key="key_devToolbar"/>
-    <broadcaster id="devtoolsMenuBroadcaster_DevAppMgr"
-                 label="&devAppMgrMenu.label;"
-                 command="Tools:DevAppMgr"/>
     <broadcaster id="devtoolsMenuBroadcaster_webide"
                  label="&webide.label;"
                  command="Tools:WebIDE"
                  key="key_webide"/>
     <broadcaster id="devtoolsMenuBroadcaster_BrowserToolbox"
                  label="&browserToolboxMenu.label;"
                  key="key_browserToolbox"
                  command="Tools:BrowserToolbox"/>
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -7080,17 +7080,17 @@ var gIdentityHandler = {
 
     try {
       this._uri.host;
       this._uriHasHost = true;
     } catch (ex) {
       this._uriHasHost = false;
     }
 
-    let whitelist = /^(?:accounts|addons|app-manager|cache|config|crashes|customizing|downloads|healthreport|home|license|newaddon|permissions|preferences|privatebrowsing|rights|sessionrestore|support|welcomeback)(?:[?#]|$)/i;
+    let whitelist = /^(?:accounts|addons|cache|config|crashes|customizing|downloads|healthreport|home|license|newaddon|permissions|preferences|privatebrowsing|rights|sessionrestore|support|welcomeback)(?:[?#]|$)/i;
     this._isSecureInternalUI = uri.schemeIs("about") && whitelist.test(uri.path);
 
     this._sslStatus = gBrowser.securityUI
                               .QueryInterface(Ci.nsISSLStatusProvider)
                               .SSLStatus;
     if (this._sslStatus) {
       this._sslStatus.QueryInterface(Ci.nsISSLStatus);
     }
@@ -8170,8 +8170,88 @@ var AboutPrivateBrowsingListener = {
     window.messageManager.addMessageListener(
       "AboutPrivateBrowsing:ToggleTrackingProtection",
       msg => {
         const PREF = "privacy.trackingprotection.pbmode.enabled";
         Services.prefs.setBoolPref(PREF, !Services.prefs.getBoolPref(PREF));
     });
   }
 };
+
+function TabModalPromptBox(browser) {
+  this._weakBrowserRef = Cu.getWeakReference(browser);
+}
+
+TabModalPromptBox.prototype = {
+  _promptCloseCallback(onCloseCallback, principalToAllowFocusFor, allowFocusCheckbox, ...args) {
+    if (principalToAllowFocusFor && allowFocusCheckbox.checked) {
+      Services.perms.addFromPrincipal(principalToAllowFocusFor, "focus-tab-by-prompt",
+                                      Services.perms.ALLOW_ACTION);
+    }
+    onCloseCallback.apply(this, args);
+  },
+
+  appendPrompt(args, onCloseCallback) {
+    const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
+    let newPrompt = document.createElementNS(XUL_NS, "tabmodalprompt");
+    let browser = this.browser;
+    browser.parentNode.appendChild(newPrompt);
+    browser.setAttribute("tabmodalPromptShowing", true);
+
+    newPrompt.clientTop; // style flush to assure binding is attached
+
+    let principalToAllowFocusFor = this._allowTabFocusByPromptPrincipal;
+    delete this._allowTabFocusByPromptPrincipal;
+
+    let allowFocusCheckbox; // Define outside the if block so we can bind it into the callback.
+    if (principalToAllowFocusFor) {
+      let allowFocusRow = document.createElementNS(XUL_NS, "row");
+      allowFocusCheckbox = document.createElementNS(XUL_NS, "checkbox");
+      let spacer = document.createElementNS(XUL_NS, "spacer");
+      allowFocusRow.appendChild(spacer);
+      let label = gBrowser.mStringBundle.getFormattedString("tabs.allowTabFocusByPromptForSite",
+                                                            [principalToAllowFocusFor.URI.host]);
+      allowFocusCheckbox.setAttribute("label", label);
+      allowFocusRow.appendChild(allowFocusCheckbox);
+      newPrompt.appendChild(allowFocusRow);
+    }
+
+    let tab = gBrowser.getTabForBrowser(browser);
+    let closeCB = this._promptCloseCallback.bind(null, onCloseCallback, principalToAllowFocusFor,
+                                                 allowFocusCheckbox);
+    newPrompt.init(args, tab, closeCB);
+    return newPrompt;
+  },
+
+  removePrompt(aPrompt) {
+    let browser = this.browser;
+    browser.parentNode.removeChild(aPrompt);
+
+    let prompts = this.listPrompts();
+    if (prompts.length) {
+      let prompt = prompts[prompts.length - 1];
+      prompt.Dialog.setDefaultFocus();
+    } else {
+      browser.removeAttribute("tabmodalPromptShowing");
+      browser.focus();
+    }
+  },
+
+  listPrompts(aPrompt) {
+    // Get the nodelist, then return as an array
+    const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
+    let els = this.browser.parentNode.getElementsByTagNameNS(XUL_NS, "tabmodalprompt");
+    return Array.from(els);
+  },
+
+  onNextPromptShowAllowFocusCheckboxFor(principal) {
+    this._allowTabFocusByPromptPrincipal = principal;
+  },
+
+  get browser() {
+    let browser = this._weakBrowserRef.get();
+    if (!browser) {
+      throw "Stale promptbox! The associated browser is gone.";
+    }
+    return browser;
+  },
+};
+
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -471,56 +471,21 @@
           ]]>
         </body>
       </method>
 
       <method name="getTabModalPromptBox">
         <parameter name="aBrowser"/>
         <body>
           <![CDATA[
-            const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
             let browser = (aBrowser || this.mCurrentBrowser);
-            let stack = browser.parentNode;
-            let self = this;
-
-            let promptBox = {
-              appendPrompt : function(args, onCloseCallback) {
-                let newPrompt = document.createElementNS(XUL_NS, "tabmodalprompt");
-                stack.appendChild(newPrompt);
-                browser.setAttribute("tabmodalPromptShowing", true);
-
-                newPrompt.clientTop; // style flush to assure binding is attached
-
-                let tab = self.getTabForBrowser(browser);
-                newPrompt.init(args, tab, onCloseCallback);
-                return newPrompt;
-              },
-
-              removePrompt : function(aPrompt) {
-                stack.removeChild(aPrompt);
-
-                let prompts = this.listPrompts();
-                if (prompts.length) {
-                  let prompt = prompts[prompts.length - 1];
-                  prompt.Dialog.setDefaultFocus();
-                } else {
-                  browser.removeAttribute("tabmodalPromptShowing");
-                  browser.focus();
-                }
-              },
-
-              listPrompts : function(aPrompt) {
-                let els = stack.getElementsByTagNameNS(XUL_NS, "tabmodalprompt");
-                // NodeList --> real JS array
-                let prompts = Array.slice(els);
-                return prompts;
-              },
-            };
-
-            return promptBox;
+            if (!browser.tabModalPromptBox) {
+              browser.tabModalPromptBox = new TabModalPromptBox(browser);
+            }
+            return browser.tabModalPromptBox;
           ]]>
         </body>
       </method>
 
       <method name="_callProgressListeners">
         <parameter name="aBrowser"/>
         <parameter name="aMethod"/>
         <parameter name="aArguments"/>
@@ -1140,16 +1105,17 @@
               if (oldFindBar &&
                   oldFindBar.findMode == oldFindBar.FIND_NORMAL &&
                   !oldFindBar.hidden)
                 this._lastFindValue = oldFindBar._findField.value;
 
               this.updateTitlebar();
 
               this.mCurrentTab.removeAttribute("titlechanged");
+              this.mCurrentTab.removeAttribute("attention");
             }
 
             // If the new tab is busy, and our current state is not busy, then
             // we need to fire a start to all progress listeners.
             const nsIWebProgressListener = Components.interfaces.nsIWebProgressListener;
             if (this.mCurrentTab.hasAttribute("busy") && !this.mIsBusy) {
               this.mIsBusy = true;
               this._callProgressListeners(null, "onStateChange",
@@ -4360,25 +4326,66 @@
           }
         ]]>
       </handler>
       <handler event="DOMWillOpenModalDialog" phase="capturing">
         <![CDATA[
           if (!event.isTrusted)
             return;
 
-          // We're about to open a modal dialog, make sure the opening
-          // tab is brought to the front.
+          let targetIsWindow = event.target instanceof Window;
+
+          // We're about to open a modal dialog, so figure out for which tab:
           // If this is a same-process modal dialog, then we're given its DOM
           // window as the event's target. For remote dialogs, we're given the
-          // browser, but that's in the originalTarget.
-          // XXX Why originalTarget for the browser?
-          this.selectedTab = (event.target instanceof Window) ?
-                               this._getTabForContentWindow(event.target.top) :
-                               this.getTabForBrowser(event.originalTarget);
+          // browser, but that's in the originalTarget and not the target,
+          // because it's across the tabbrowser's XBL boundary.
+          let tabForEvent = targetIsWindow ?
+                            this._getTabForContentWindow(event.target.top) :
+                            this.getTabForBrowser(event.originalTarget);
+
+          // Don't need to act if the tab is already selected:
+          if (tabForEvent.selected)
+            return;
+
+          // If this is a tabprompt, and we're not in tabview/panorama with
+          // the prompt being a beforeunload one, we won't switch tabs
+          // (unless this behaviour has been disabled entirely using the pref).
+          if (event.detail && event.detail.tabPrompt &&
+              !(event.detail.inPermitUnload && ("TabView" in window) && TabView.isVisible()) &&
+              Services.prefs.getBoolPref("browser.tabs.dontfocusfordialogs")) {
+            let docPrincipal = targetIsWindow ? event.target.document.nodePrincipal : null;
+            // At least one of these should/will be non-null:
+            let promptPrincipal = event.detail.promptPrincipal || docPrincipal ||
+                                  tabForEvent.linkedBrowser.contentPrincipal;
+            // For null principals, we bail immediately and don't show the checkbox:
+            if (!promptPrincipal || promptPrincipal.isNullPrincipal) {
+              tabForEvent.setAttribute("attention", "true");
+              return;
+            }
+
+            // For non-system/expanded principals, we bail and show the checkbox
+            if (promptPrincipal.URI &&
+                !Services.scriptSecurityManager.isSystemPrincipal(promptPrincipal)) {
+              let permission = Services.perms.testPermissionFromPrincipal(promptPrincipal,
+                                                                          "focus-tab-by-prompt");
+              if (permission != Services.perms.ALLOW_ACTION) {
+                // Tell the prompt box we want to show the user a checkbox:
+                let tabPrompt = this.getTabModalPromptBox(tabForEvent.linkedBrowser);
+                tabPrompt.onNextPromptShowAllowFocusCheckboxFor(promptPrincipal);
+                tabForEvent.setAttribute("attention", "true");
+                return;
+              }
+            }
+            // ... so system and expanded principals, as well as permitted "normal"
+            // URI-based principals, always get to steal focus for the tab when prompting.
+          }
+
+          // if prefs/permissions/origins so dictate, bring tab to the front:
+          this.selectedTab = tabForEvent;
         ]]>
       </handler>
       <handler event="DOMTitleChanged">
         <![CDATA[
           if (!event.isTrusted)
             return;
 
           var contentWin = event.target.defaultView;
@@ -5861,43 +5868,43 @@
   <binding id="tabbrowser-tab" display="xul:hbox"
            extends="chrome://global/content/bindings/tabbox.xml#tab">
     <resources>
       <stylesheet src="chrome://browser/content/tabbrowser.css"/>
     </resources>
 
     <content context="tabContextMenu">
       <xul:stack class="tab-stack" flex="1">
-        <xul:hbox xbl:inherits="pinned,selected,visuallyselected,titlechanged,fadein"
+        <xul:hbox xbl:inherits="pinned,selected,visuallyselected,fadein"
                   class="tab-background">
-          <xul:hbox xbl:inherits="pinned,selected,visuallyselected,titlechanged"
+          <xul:hbox xbl:inherits="pinned,selected,visuallyselected"
                     class="tab-background-start"/>
-          <xul:hbox xbl:inherits="pinned,selected,visuallyselected,titlechanged"
+          <xul:hbox xbl:inherits="pinned,selected,visuallyselected"
                     class="tab-background-middle"/>
-          <xul:hbox xbl:inherits="pinned,selected,visuallyselected,titlechanged"
+          <xul:hbox xbl:inherits="pinned,selected,visuallyselected"
                     class="tab-background-end"/>
         </xul:hbox>
-        <xul:hbox xbl:inherits="pinned,selected,visuallyselected,titlechanged"
+        <xul:hbox xbl:inherits="pinned,selected,visuallyselected,titlechanged,attention"
                   class="tab-content" align="center">
           <xul:image xbl:inherits="fadein,pinned,busy,progress,selected,visuallyselected"
                      class="tab-throbber"
                      role="presentation"
                      layer="true" />
           <xul:image xbl:inherits="src=image,fadein,pinned,selected,visuallyselected,busy,crashed"
                      anonid="tab-icon-image"
                      class="tab-icon-image"
                      validate="never"
                      role="presentation"/>
           <xul:image xbl:inherits="crashed,busy,soundplaying,pinned,muted"
                      anonid="overlay-icon"
                      class="tab-icon-overlay"
                      role="presentation"/>
           <xul:label flex="1"
                      anonid="tab-label"
-                     xbl:inherits="value=visibleLabel,crop,accesskey,fadein,pinned,selected,visuallyselected"
+                     xbl:inherits="value=visibleLabel,crop,accesskey,fadein,pinned,selected,visuallyselected,attention"
                      class="tab-text tab-label"
                      role="presentation"/>
           <xul:image xbl:inherits="soundplaying,pinned,muted,visuallyselected"
                      anonid="soundplaying-icon"
                      class="tab-icon-sound"
                      role="presentation"/>
           <xul:toolbarbutton anonid="close-button"
                              xbl:inherits="fadein,pinned,selected,visuallyselected"
@@ -6475,16 +6482,18 @@
         </setter>
       </property>
     </implementation>
   </binding>
 
   <binding id="tabbrowser-browser"
            extends="chrome://global/content/bindings/browser.xml#browser">
     <implementation>
+      <field name="tabModalPromptBox">null</field>
+
       <!-- throws exception for unknown schemes -->
       <method name="loadURIWithFlags">
         <parameter name="aURI"/>
         <parameter name="aFlags"/>
         <parameter name="aReferrerURI"/>
         <parameter name="aCharset"/>
         <parameter name="aPostData"/>
         <body>
@@ -6503,16 +6512,18 @@
         </body>
       </method>
     </implementation>
   </binding>
 
   <binding id="tabbrowser-remote-browser"
            extends="chrome://global/content/bindings/remote-browser.xml#remote-browser">
     <implementation>
+      <field name="tabModalPromptBox">null</field>
+
       <!-- throws exception for unknown schemes -->
       <method name="loadURIWithFlags">
         <parameter name="aURI"/>
         <parameter name="aFlags"/>
         <parameter name="aReferrerURI"/>
         <parameter name="aCharset"/>
         <parameter name="aPostData"/>
         <body>
--- a/browser/base/content/test/general/browser.ini
+++ b/browser/base/content/test/general/browser.ini
@@ -335,16 +335,18 @@ skip-if = os == "linux" # Linux: Intermi
 [browser_menuButtonFitts.js]
 skip-if = os != "win" # The Fitts Law menu button is only supported on Windows (bug 969376)
 [browser_middleMouse_noJSPaste.js]
 [browser_minimize.js]
 [browser_mixedcontent_securityflags.js]
 tags = mcb
 [browser_offlineQuotaNotification.js]
 skip-if = buildapp == 'mulet' || e10s # Bug 1093603 - test breaks with PopupNotifications.panel.firstElementChild is null
+[browser_openPromptInBackgroundTab.js]
+support-files = openPromptOffTimeout.html
 [browser_overflowScroll.js]
 [browser_pageInfo.js]
 skip-if = buildapp == 'mulet'
 [browser_page_style_menu.js]
 [browser_page_style_menu_update.js]
 [browser_parsable_css.js]
 [browser_parsable_script.js]
 skip-if = asan || (os == 'linux' && !debug && (bits == 32)) # disabled on asan because of timeouts, and bug 1172468 for the linux 32-bit pgo issue.
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/general/browser_openPromptInBackgroundTab.js
@@ -0,0 +1,63 @@
+"use strict";
+
+const ROOT = getRootDirectory(gTestPath).replace("chrome://mochitests/content/", "http://example.com/");
+let pageWithAlert = ROOT + "openPromptOffTimeout.html";
+
+registerCleanupFunction(function() {
+  Services.perms.removeAll(makeURI(pageWithAlert));
+});
+
+/*
+ * This test opens a tab that alerts when it is hidden. We then switch away
+ * from the tab, and check that by default the tab is not automatically
+ * re-selected. We also check that a checkbox appears in the alert that allows
+ * the user to enable this automatically re-selecting. We then check that
+ * checking the checkbox does actually enable that behaviour.
+ */
+add_task(function*() {
+  yield pushPrefs(["browser.tabs.dontfocusfordialogs", true]);
+  let firstTab = gBrowser.selectedTab;
+  // load page that opens prompt when page is hidden
+  let openedTab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, pageWithAlert, true);
+  let openedTabGotAttentionPromise = BrowserTestUtils.waitForAttribute("attention", openedTab, "true");
+  // switch away from that tab again - this triggers the alert.
+  yield BrowserTestUtils.switchTab(gBrowser, firstTab);
+  // ... but that's async on e10s...
+  yield openedTabGotAttentionPromise;
+  // check for attention attribute
+  is(openedTab.getAttribute("attention"), "true", "Tab with alert should have 'attention' attribute.");
+  ok(!openedTab.selected, "Tab with alert should not be selected");
+
+  // switch tab back, and check the checkbox is displayed:
+  yield BrowserTestUtils.switchTab(gBrowser, openedTab);
+  // check the prompt is there, and the extra row is present
+  let prompts = openedTab.linkedBrowser.parentNode.querySelectorAll("tabmodalprompt");
+  is(prompts.length, 1, "There should be 1 prompt");
+  let ourPrompt = prompts[0];
+  let row = ourPrompt.querySelector("row");
+  ok(row, "Should have found the row with our checkbox");
+  let checkbox = row.querySelector("checkbox[label*='example.com']");
+  ok(checkbox, "The checkbox should be there");
+  ok(!checkbox.checked, "Checkbox shouldn't be checked");
+  // tick box and accept dialog
+  checkbox.checked = true;
+  ourPrompt.onButtonClick(0);
+  // check permission is set
+  let ps = Services.perms;
+  is(ps.ALLOW_ACTION, ps.testPermission(makeURI(pageWithAlert), "focus-tab-by-prompt"),
+     "Tab switching should now be allowed");
+
+  let openedTabSelectedPromise = BrowserTestUtils.waitForAttribute("selected", openedTab, "true");
+  // switch to other tab again
+  yield BrowserTestUtils.switchTab(gBrowser, firstTab);
+
+  // This is sync in non-e10s, but in e10s we need to wait for this, so yield anyway.
+  // Note that the switchTab promise doesn't actually guarantee anything about *which*
+  // tab ends up as selected when its event fires, so using that here wouldn't work.
+  yield openedTabSelectedPromise;
+  // should be switched back
+  ok(openedTab.selected, "Ta-dah, the other tab should now be selected again!");
+
+  yield BrowserTestUtils.removeTab(openedTab);
+});
+
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/general/openPromptOffTimeout.html
@@ -0,0 +1,10 @@
+<body>
+This page opens an alert box when the page is hidden.
+<script>
+document.addEventListener("visibilitychange", () => {
+  if (document.hidden) {
+    alert("You hid my page!");
+  }
+}, false);
+</script>
+</body>
--- a/browser/components/about/AboutRedirector.cpp
+++ b/browser/components/about/AboutRedirector.cpp
@@ -100,18 +100,16 @@ static RedirEntry kRedirMap[] = {
   { "downloads", "chrome://browser/content/downloads/contentAreaDownloadsView.xul",
     nsIAboutModule::ALLOW_SCRIPT },
 #ifdef MOZ_SERVICES_HEALTHREPORT
   { "healthreport", "chrome://browser/content/abouthealthreport/abouthealth.xhtml",
     nsIAboutModule::ALLOW_SCRIPT },
 #endif
   { "accounts", "chrome://browser/content/aboutaccounts/aboutaccounts.xhtml",
     nsIAboutModule::ALLOW_SCRIPT },
-  { "app-manager", "chrome://devtools/content/app-manager/content/index.xul",
-    nsIAboutModule::ALLOW_SCRIPT },
   { "customizing", "chrome://browser/content/customizableui/aboutCustomizing.xul",
     nsIAboutModule::ALLOW_SCRIPT },
   {
     "debugging", "chrome://devtools/content/aboutdebugging/aboutdebugging.xhtml",
     nsIAboutModule::ALLOW_SCRIPT },
   { "loopconversation", "chrome://browser/content/loop/conversation.html",
     nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT |
     nsIAboutModule::ALLOW_SCRIPT |
--- a/browser/components/build/nsModule.cpp
+++ b/browser/components/build/nsModule.cpp
@@ -110,17 +110,16 @@ static const mozilla::Module::ContractID
     { NS_ABOUT_MODULE_CONTRACTID_PREFIX "remote-newtab", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
     { NS_ABOUT_MODULE_CONTRACTID_PREFIX "permissions", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
     { NS_ABOUT_MODULE_CONTRACTID_PREFIX "preferences", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
     { NS_ABOUT_MODULE_CONTRACTID_PREFIX "downloads", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
     { NS_ABOUT_MODULE_CONTRACTID_PREFIX "accounts", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
 #ifdef MOZ_SERVICES_HEALTHREPORT
     { NS_ABOUT_MODULE_CONTRACTID_PREFIX "healthreport", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
 #endif
-    { NS_ABOUT_MODULE_CONTRACTID_PREFIX "app-manager", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
     { NS_ABOUT_MODULE_CONTRACTID_PREFIX "customizing", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
     { NS_ABOUT_MODULE_CONTRACTID_PREFIX "debugging", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
     { NS_ABOUT_MODULE_CONTRACTID_PREFIX "looppanel", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
     { NS_ABOUT_MODULE_CONTRACTID_PREFIX "loopconversation", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
     { NS_ABOUT_MODULE_CONTRACTID_PREFIX "reader", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
     { NS_ABOUT_MODULE_CONTRACTID_PREFIX "pocket-saved", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
     { NS_ABOUT_MODULE_CONTRACTID_PREFIX "pocket-signup", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
 #if defined(XP_WIN)
--- a/browser/components/loop/content/shared/css/conversation.css
+++ b/browser/components/loop/content/shared/css/conversation.css
@@ -640,17 +640,17 @@ body[platform="win"] .share-service-drop
 .media-wrapper > .focus-stream {
   display: flex;
   /* We want this to be the width, minus 200px which is for the right-side text
      chat and video displays. */
   width: calc(100% - 200px);
   /* 100% height to fill up media-layout, thus forcing other elements into the
      second column that's 200px wide */
   height: 100%;
-  background-color: #4E4E4E;
+  background-color: #D8D8D8;
 }
 
 .media-wrapper > .local {
   flex: 0 1 auto;
   width: 200px;
   height: 150px;
 }
 
--- a/browser/components/loop/content/shared/img/svg/glyph-help-16x16.svg
+++ b/browser/components/loop/content/shared/img/svg/glyph-help-16x16.svg
@@ -1,8 +1,1 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- This Source Code Form is subject to the terms of the Mozilla Public
-   - License, v. 2.0. If a copy of the MPL was not distributed with this
-   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
-<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
-  <circle fill="#5a5a5a" cx="8" cy="8" r="8"/>
-  <path fill="#fff" d="M10.716,5.643c0,1.943-2.158,1.812-2.158,3.154v0.3H6.831V8.726c0-2.075,1.907-1.932,1.907-2.915 c0-0.432-0.312-0.684-0.84-0.684c-0.491,0-0.972,0.24-1.403,0.731L5.284,4.923C5.967,4.121,6.855,3.64,8.09,3.64 C9.841,3.64,10.716,4.576,10.716,5.643z M8.797,11.268c0,0.6-0.479,1.092-1.079,1.092s-1.079-0.492-1.079-1.092 c0-0.588,0.479-1.079,1.079-1.079S8.797,10.68,8.797,11.268z"/>
-</svg>
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><circle fill="#fff" cx="8" cy="8" r="8"/><path fill="#5a5a5a" d="M10.716 5.643c0 1.943-2.158 1.812-2.158 3.154v.3H6.83v-.37C6.83 6.65 8.74 6.793 8.74 5.81c0-.43-.312-.683-.84-.683-.49 0-.972.24-1.403.73l-1.21-.934C5.966 4.12 6.854 3.64 8.09 3.64c1.75 0 2.626.936 2.626 2.003zm-1.92 5.625c0 .6-.478 1.092-1.078 1.092s-1.08-.492-1.08-1.092c0-.588.48-1.08 1.08-1.08s1.08.492 1.08 1.08z"/></svg>
\ No newline at end of file
--- a/browser/components/loop/standalone/content/css/webapp.css
+++ b/browser/components/loop/standalone/content/css/webapp.css
@@ -53,20 +53,21 @@ body,
   top: 0;
   bottom: 0;
   right: 0;
   margin: 1.2rem;
   pointer-events: none;
 }
 
 .standalone-overlay-wrapper > .hello-logo {
-  width: 128px;
-  height: 21px;
+  width: 120px;
+  height: 19px;
   background-image: url("../shared/img/hello_logo.svg");
   background-size: contain;
+  background-repeat: no-repeat;
   position: absolute;
   left: 0;
   top: 0;
 }
 
 html[dir="rtl"] .standalone-overlay-wrapper > .hello-logo {
   right: 0;
   left: auto;
@@ -235,17 +236,17 @@ html[dir="rtl"] .standalone-overlay-wrap
 
 .prompt-media-message.opera {
   background-image: url("../img/gum-opera.svg");
 }
 
 /* Room Info area layout */
 
 .room-inner-info-area {
-  color: #fff;
+  color: #4a4a4a;
   margin: auto;
   padding: 0 5px;
 }
 
 .room-inner-info-area > button {
   border-radius: 3px;
   font-size: 1.2em;
   padding: .2em 1.2em;
@@ -263,38 +264,42 @@ html[dir="rtl"] .standalone-overlay-wrap
   font-size: 1.2em;
   font-weight: bold;
 }
 
 .room-inner-info-area > .room-waiting-area {
   display: flex;
   justify-content: space-between;
   margin: 3em auto 1em;
+  /* This should match the width set in the room-waiting-tile to ensure the edges
+     of the content aligns within the tile width. */
+  width: 290px;
 }
 
 .room-inner-info-area > .room-waiting-area > a >.room-waiting-help {
   background: transparent url("../shared/img/svg/glyph-help-16x16.svg") no-repeat;
   display: inline-block;
   height: 16px;
   margin-left: 5px;
   vertical-align: middle;
   width: 16px;
 }
 
 .room-inner-info-area > .room-waiting-tile {
   border: 0;
   border-radius: 5px;
   /* These sizes are the size of the tile image and title */
   height: 204px;
-  /* Override the default iframe 300px width with the inherited width */
-  width: 100%;
+  /* The tiles we are served are 290px wide. This width should also match that
+     for the .room-waiting-area. */
+  width: 290px;
 }
 
 /* Terms of Service */
 
 .room-inner-info-area > .terms-service {
   margin-top: 20px;
 }
 
 .room-inner-info-area > .terms-service,
 .room-inner-info-area > .terms-service > a {
   color: #999;
-}
\ No newline at end of file
+}
--- a/browser/components/loop/test/shared/activeRoomStore_test.js
+++ b/browser/components/loop/test/shared/activeRoomStore_test.js
@@ -816,18 +816,21 @@ describe("loop.store.ActiveRoomStore", f
 
       var state = store.getStoreState();
       expect(state.socialShareProviders)
         .eql(fakeSocialShareInfo.socialShareProviders);
     });
   });
 
   describe("#joinRoom", function() {
+    var hasDevicesStub;
+
     beforeEach(function() {
       store.setStoreState({ roomState: ROOM_STATES.READY });
+      hasDevicesStub = sandbox.stub(loop.shared.utils, "hasAudioOrVideoDevices");
     });
 
     it("should reset failureReason", function() {
       store.setStoreState({ failureReason: "Test" });
 
       store.joinRoom();
 
       expect(store.getStoreState().failureReason).eql(undefined);
@@ -840,33 +843,33 @@ describe("loop.store.ActiveRoomStore", f
         sinon.assert.calledOnce(dispatcher.dispatch);
         sinon.assert.calledWithExactly(dispatcher.dispatch,
           new sharedActions.MetricsLogJoinRoom({
             userAgentHandledRoom: false
           }));
       });
 
       it("should set the state to MEDIA_WAIT if media devices are present", function() {
-        sandbox.stub(loop.shared.utils, "hasAudioOrVideoDevices").callsArgWith(0, true);
+        hasDevicesStub.callsArgWith(0, true);
 
         store.joinRoom();
 
         expect(store.getStoreState().roomState).eql(ROOM_STATES.MEDIA_WAIT);
       });
 
       it("should not set the state to MEDIA_WAIT if no media devices are present", function() {
-        sandbox.stub(loop.shared.utils, "hasAudioOrVideoDevices").callsArgWith(0, false);
+        hasDevicesStub.callsArgWith(0, false);
 
         store.joinRoom();
 
         expect(store.getStoreState().roomState).eql(ROOM_STATES.READY);
       });
 
       it("should dispatch `ConnectionFailure` if no media devices are present", function() {
-        sandbox.stub(loop.shared.utils, "hasAudioOrVideoDevices").callsArgWith(0, false);
+        hasDevicesStub.callsArgWith(0, false);
 
         store.joinRoom();
 
         sinon.assert.called(dispatcher.dispatch);
         sinon.assert.calledWithExactly(dispatcher.dispatch,
           new sharedActions.ConnectionFailure({
             reason: FAILURE_DETAILS.NO_MEDIA
           }));
--- a/browser/locales/en-US/chrome/browser/browser.dtd
+++ b/browser/locales/en-US/chrome/browser/browser.dtd
@@ -313,18 +313,16 @@ These should match what Safari and other
   -  application menu item that opens the browser content toolbox UI in the Tools menu.
   -  This toolbox allows to debug the chrome of the content process in multiprocess builds.  -->
 <!ENTITY browserContentToolboxMenu.label     "Browser Content Toolbox">
 <!ENTITY browserContentToolboxMenu.accesskey "x">
 
 <!ENTITY devToolbarCloseButton.tooltiptext "Close Developer Toolbar">
 <!ENTITY devToolbarMenu.label              "Developer Toolbar">
 <!ENTITY devToolbarMenu.accesskey          "v">
-<!ENTITY devAppMgrMenu.label               "App Manager">
-<!ENTITY devAppMgrMenu.accesskey           "A">
 <!ENTITY webide.label                      "WebIDE">
 <!ENTITY webide.accesskey                  "W">
 <!ENTITY webide.keycode                    "VK_F8">
 <!ENTITY webide.keytext                    "F8">
 <!ENTITY devToolbar.keycode                "VK_F2">
 <!ENTITY devToolbar.keytext                "F2">
 <!ENTITY devToolboxMenuItem.label          "Toggle Tools">
 <!ENTITY devToolboxMenuItem.accesskey      "T">
deleted file mode 100644
--- a/browser/locales/en-US/chrome/browser/devtools/app-manager.dtd
+++ /dev/null
@@ -1,103 +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/. -->
-
-<!ENTITY index.title "App Manager">
-<!ENTITY index.projects2 "Apps">
-<!ENTITY index.device2 "Device">
-<!ENTITY index.help "Help">
-
-<!ENTITY device.debugMainProcess "Debug main process">
-<!ENTITY device.debugMainProcessTooltip "Open the Developer Tools connected to the main process on the device">
-<!ENTITY device.screenshot "Screenshot">
-<!ENTITY device.screenshotTooltip "Open a screenshot of the current state of the device in a new tab">
-<!ENTITY device.title "Device Control Center">
-<!ENTITY device.notConnected "Not connected. Please connect your device below.">
-<!ENTITY device.startApp "Start">
-<!ENTITY device.startAppTooltip "Start this app on the device">
-<!ENTITY device.stopApp "Stop">
-<!ENTITY device.stopAppTooltip "Stop this app on the device">
-<!ENTITY device.debugApp "Debug">
-<!ENTITY device.debugAppTooltip "Open the Developer Tools connected to this app on the device">
-<!ENTITY device.name "Name">
-<!ENTITY device.plain "Plain (default)">
-<!ENTITY device.privileged "Privileged">
-<!ENTITY device.certified "Certified">
-<!ENTITY device.allow "Allow">
-<!ENTITY device.allowTooltip "This permission is allowed for apps of this type">
-<!ENTITY device.prompt "Prompt">
-<!ENTITY device.promptTooltip "This permission requires a user prompt for apps of this type">
-<!ENTITY device.deny "Deny">
-<!ENTITY device.denyTooltip "This permission is denied for apps of this type">
-<!ENTITY device.installedApps "Installed Apps">
-<!ENTITY device.installedAppsTooltip "View a list of apps installed on the device. Some apps, such as certified apps, may be excluded from this view.">
-<!ENTITY device.permissions "Permissions">
-<!ENTITY device.permissionsTooltip "View a table of the permissions accessible to the different types of apps">
-<!ENTITY device.permissionsHelpLink "https://developer.mozilla.org/docs/Web/Apps/App_permissions">
-<!ENTITY device.browserTabs "Browser Tabs">
-<!ENTITY device.browserTabsTooltip "View a list of tabs in the browser of the connected device">
-<!ENTITY device.debugBrowserTab "Debug">
-<!ENTITY device.debugBrowserTabTooltip "Open the Developer Tools connected to this browser tab on the device">
-<!ENTITY device.help "Help">
-
-<!ENTITY connection.connectTooltip "Connect to the device">
-<!ENTITY connection.disconnect "Disconnect">
-<!ENTITY connection.disconnectTooltip "Disconnect from the current device or simulator">
-<!ENTITY connection.notConnected2 "Not Connected.">
-<!ENTITY connection.connectTo "Connect to:">
-<!ENTITY connection.noDeviceFound "No device found. Plug a device">
-<!ENTITY connection.changeHostAndPort "Change">
-<!ENTITY connection.changeHostAndPortTooltip "Change the host and port used to connect to the device (defaults to localhost:6000)">
-<!ENTITY connection.startSimulator "Start Simulator">
-<!ENTITY connection.startSimulatorTooltip "Start an instance of the Simulator and connect to it">
-<!ENTITY connection.saveConnectionInfo "Save">
-<!ENTITY connection.saveConnectionInfoTooltip "Save the host and port">
-<!ENTITY connection.connecting "Connecting…">
-<!ENTITY connection.disconnecting "Disconnecting…">
-<!ENTITY connection.cancel "Cancel">
-<!ENTITY connection.cancelConnectTooltip "Cancel the connection in progress">
-<!ENTITY connection.cancelShowSimulatorTooltip "Exit the Simulator connection mode and return to the initial prompt">
-<!ENTITY connection.or "or">
-<!ENTITY connection.noSimulatorInstalled "No simulator installed.">
-<!ENTITY connection.installOneSimulator "Install Simulator">
-<!ENTITY connection.installOneSimulatorTooltip "Install a version of the Simulator by downloading the relevant add-on">
-<!ENTITY connection.installAnotherSimulator "Add">
-<!ENTITY connection.installAnotherSimulatorTooltip "Install an additional version of the Simulator by downloading the relevant add-on">
-<!ENTITY connection.startRegisteredSimulator "Start:">
-
-<!ENTITY projects.localApps "Local Apps">
-<!ENTITY projects.addApp "Add">
-<!ENTITY projects.addPackaged "Add Packaged App">
-<!ENTITY projects.addPackagedTooltip "Add a new packaged app (a directory) from your computer">
-<!ENTITY projects.addHosted "Add Hosted App">
-<!ENTITY projects.addHostedTooltip "Add a new hosted app (link to a manifest.webapp file) from a remote website">
-<!ENTITY projects.title "Local Apps">
-<!ENTITY projects.appDetails "App Details">
-<!ENTITY projects.removeAppFromList "Remove this app from the list of apps you are working on. This will not remove it from a device or a simulator.">
-<!ENTITY projects.updateApp "Update">
-<!ENTITY projects.updateAppTooltip "Execute validation checks and update the app to the connected device">
-<!ENTITY projects.debugApp "Debug">
-<!ENTITY projects.debugAppTooltip "Open Developer Tools connected to this app">
-<!ENTITY projects.saveManifest "Save">
-<!ENTITY projects.saveManifestTooltip "Save the contents of the Manifest Editor below">
-<!ENTITY projects.hostedManifestPlaceHolder2 "http://example.com/app/manifest.webapp">
-<!ENTITY projects.noProjects "No projects. Add a new packaged app below (local directory) or a hosted app (link to a manifest file).">
-<!ENTITY projects.manifestEditor "Manifest Editor">
-<!ENTITY projects.manifestEditorTooltip "Edit your app's manifest in the panel below. The Update button will save your changes and update the app.">
-<!ENTITY projects.manifestViewer "Manifest Viewer">
-<!ENTITY projects.manifestViewerTooltip "Examine your app's manifest in the panel below">
-<!ENTITY projects.valid "Valid">
-<!ENTITY projects.error "Error">
-<!ENTITY projects.warning "Warning">
-<!ENTITY projects.hosted "Hosted">
-<!ENTITY projects.packaged "Packaged">
-
-<!ENTITY help.title "App Manager">
-<!ENTITY help.close "Close">
-<!ENTITY help.intro "This tool will help you build and install web apps on compatible devices (i.e. Firefox OS). The <strong>Apps</strong> tab will assist you in the validation and installation process of your app. The <strong>Device</strong> tab will give you information about the connected device. Use the bottom toolbar to connect to a device or start the simulator.">
-<!ENTITY help.usefullLinks "Useful links:">
-<!ENTITY help.appMgrDoc "Documentation: Using the App Manager">
-<!ENTITY help.configuringDevice "How to setup your Firefox OS device">
-<!ENTITY help.troubleShooting "Troubleshooting">
-<!ENTITY help.simulatorAddon "Install Simulator Add-on">
-<!ENTITY help.adbHelperAddon "Install Adb Helper Add-on">
--- a/browser/locales/en-US/chrome/browser/devtools/app-manager.properties
+++ b/browser/locales/en-US/chrome/browser/devtools/app-manager.properties
@@ -1,26 +1,12 @@
 # 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/.
 
-index.deprecationNotice=The App Manager will be removed in a future release.  Your projects have been migrated to WebIDE.
-index.launchWebIDE=Launch WebIDE
-index.readMoreAboutWebIDE=Read More
-# LOCALIZATION NOTE (device.deviceSize): %1$S is the device's width, %2$S is
-# the device's height, %3$S is the device's pixel density.
-# Example: 800x480 (86 DPI).
-device.deviceSize=Device size: %1$Sx%2$S (%3$S DPI)
-# LOCALIZATION NOTE (connection.connectedToDevice, connection.connectTo):
-# %1$S is the host name, %2$S is the port number.
-connection.connectedToDevice=Connected to %1$S
-connection.connectTo=Connect to %1$S:%2$S
-project.filePickerTitle=Select a webapp folder
-project.installing=Installing…
-project.installed=Installed!
 validator.nonExistingFolder=The project folder doesn't exists
 validator.expectProjectFolder=The project folder ends up being a file
 validator.noManifestFile=A manifest file is required at project root folder, named either 'manifest.webapp' for packaged apps or 'manifest.json' for add-ons.
 validator.invalidManifestURL=Invalid manifest URL '%S'
 # LOCALIZATION NOTE (validator.invalidManifestJSON, validator.noAccessManifestURL):
 # %1$S is the error message, %2$S is the URI of the manifest.
 validator.invalidManifestJSON=The webapp manifest isn't a valid JSON file: %1$S at: %2$S
 validator.noAccessManifestURL=Unable to read manifest file: %1$S at: %2$S
--- a/browser/locales/en-US/chrome/browser/devtools/debugger.properties
+++ b/browser/locales/en-US/chrome/browser/devtools/debugger.properties
@@ -304,19 +304,19 @@ sealedTooltip=sealed
 extensibleTooltip=extensible
 overriddenTooltip=overridden
 WebIDLTooltip=WebIDL
 
 # LOCALIZATION NOTE (variablesSeparatorLabel): The text that is displayed
 # in the variables list as a separator between the name and value.
 variablesSeparatorLabel=:
 
-# LOCALIZATION NOTE (watchExpressionsSeparatorLabel): The text that is displayed
+# LOCALIZATION NOTE (watchExpressionsSeparatorLabel2): The text that is displayed
 # in the watch expressions list as a separator between the code and evaluation.
-watchExpressionsSeparatorLabel=\ →
+watchExpressionsSeparatorLabel2=\u0020→
 
 # LOCALIZATION NOTE (functionSearchSeparatorLabel): The text that is displayed
 # in the functions search panel as a separator between function's inferred name
 # and its real name (if available).
 functionSearchSeparatorLabel=←
 
 # LOCALIZATION NOTE (resumptionOrderPanelTitle): This is the text that appears
 # as a description in the notification panel popup, when multiple debuggers are
--- a/browser/locales/en-US/chrome/browser/tabbrowser.properties
+++ b/browser/locales/en-US/chrome/browser/tabbrowser.properties
@@ -37,8 +37,12 @@ tabs.closeSelectedTab.tooltip=Close tab 
 # LOCALIZATION NOTE (tabs.muteAudio.tooltip):
 # %S is the keyboard shortcut for "Mute tab"
 tabs.muteAudio.tooltip=Mute tab (%S)
 # LOCALIZATION NOTE (tabs.unmuteAudio.tooltip):
 # %S is the keyboard shortcut for "Unmute tab"
 tabs.unmuteAudio.tooltip=Unmute tab (%S)
 tabs.muteAudio.background.tooltip=Mute tab
 tabs.unmuteAudio.background.tooltip=Unmute tab
+
+# LOCALIZATION NOTE (tabs.allowTabFocusByPromptForSite):
+# %S is the hostname of the site where dialogs are allowed to switch tabs
+tabs.allowTabFocusByPromptForSite=Allow dialogs from %S to take you to their tab
--- a/browser/locales/jar.mn
+++ b/browser/locales/jar.mn
@@ -73,17 +73,16 @@
     locale/browser/devtools/inspector.dtd          (%chrome/browser/devtools/inspector.dtd)
     locale/browser/devtools/markers.properties     (%chrome/browser/devtools/markers.properties)
     locale/browser/devtools/projecteditor.properties     (%chrome/browser/devtools/projecteditor.properties)
     locale/browser/devtools/eyedropper.properties     (%chrome/browser/devtools/eyedropper.properties)
     locale/browser/devtools/connection-screen.dtd  (%chrome/browser/devtools/connection-screen.dtd)
     locale/browser/devtools/connection-screen.properties (%chrome/browser/devtools/connection-screen.properties)
     locale/browser/devtools/font-inspector.dtd     (%chrome/browser/devtools/font-inspector.dtd)
     locale/browser/devtools/har.properties         (%chrome/browser/devtools/har.properties)
-    locale/browser/devtools/app-manager.dtd        (%chrome/browser/devtools/app-manager.dtd)
     locale/browser/devtools/app-manager.properties (%chrome/browser/devtools/app-manager.properties)
     locale/browser/devtools/webide.dtd             (%chrome/browser/devtools/webide.dtd)
     locale/browser/devtools/webide.properties      (%chrome/browser/devtools/webide.properties)
     locale/browser/lightweightThemes.properties    (%chrome/browser/lightweightThemes.properties)
     locale/browser/loop/loop.properties            (%chrome/browser/loop/loop.properties)
     locale/browser/newTab.dtd                      (%chrome/browser/newTab.dtd)
     locale/browser/newTab.properties               (%chrome/browser/newTab.properties)
     locale/browser/pageInfo.dtd                    (%chrome/browser/pageInfo.dtd)
--- a/browser/modules/RemotePrompt.jsm
+++ b/browser/modules/RemotePrompt.jsm
@@ -58,17 +58,22 @@ var RemotePrompt = {
       browser.messageManager.removeMessageListener("Prompt:ForceClose", listener);
 
       if (newPrompt) {
         newPrompt.abortPrompt();
       }
     });
 
     try {
-      PromptUtils.fireDialogEvent(window, "DOMWillOpenModalDialog", browser);
+      let eventDetail = {
+        tabPrompt: true,
+        promptPrincipal: args.promptPrincipal,
+        inPermitUnload: args.inPermitUnload,
+      };
+      PromptUtils.fireDialogEvent(window, "DOMWillOpenModalDialog", browser, eventDetail);
 
       args.promptActive = true;
 
       newPrompt = tabPrompt.appendPrompt(args, onPromptClose);
 
       // TODO since we don't actually open a window, need to check if
       // there's other stuff in nsWindowWatcher::OpenWindowInternal
       // that we might need to do here as well.
--- a/browser/themes/shared/devedition.inc.css
+++ b/browser/themes/shared/devedition.inc.css
@@ -278,22 +278,28 @@ window:not([chromehidden~="toolbar"]) #u
 }
 
 .tabbrowser-tab {
   /* We normally rely on other tab elements for pointer events, but this
      theme hides those so we need it set here instead */
   pointer-events: auto;
 }
 
-.tabbrowser-tab[pinned][titlechanged]:not([visuallyselected="true"]) > .tab-stack > .tab-content {
+.tabbrowser-tab[image] > .tab-stack > .tab-content[attention]:not([visuallyselected="true"]),
+.tabbrowser-tab > .tab-stack > .tab-content[pinned][titlechanged]:not([visuallyselected="true"]) {
   background-image: var(--pinned-tab-glow);
   background-position: center;
   background-size: 100%;
 }
 
+.tabbrowser-tab[image] > .tab-stack > .tab-content[attention]:not([pinned]):not([visuallyselected="true"]) {
+  background-position: left bottom var(--tab-toolbar-navbar-overlap);
+  background-size: 34px 100%;
+}
+
 .tabbrowser-arrowscrollbox > .scrollbutton-down:not([disabled]):hover,
 .tabbrowser-arrowscrollbox > .scrollbutton-up:not([disabled]):hover,
 .tabbrowser-tab:hover {
   background-color: var(--tab-hover-background-color);
 }
 
 .tabbrowser-tab[visuallyselected] {
   color: var(--tab-selection-color) !important; /* Override color: inherit */
--- a/browser/themes/shared/tabs.inc.css
+++ b/browser/themes/shared/tabs.inc.css
@@ -439,23 +439,33 @@
 /* Pinned tabs */
 
 /* Pinned tab separators need position: absolute when positioned (during overflow). */
 #tabbrowser-tabs[positionpinnedtabs] > .tabbrowser-tab[pinned]::before {
   height: 100%;
   position: absolute;
 }
 
-.tabbrowser-tab[pinned][titlechanged]:not([visuallyselected="true"]) > .tab-stack > .tab-content {
+.tabbrowser-tab[image] > .tab-stack > .tab-content[attention]:not([visuallyselected="true"]),
+.tabbrowser-tab > .tab-stack > .tab-content[pinned][titlechanged]:not([visuallyselected="true"]) {
   background-image: radial-gradient(farthest-corner at center bottom, rgb(255,255,255) 3%, rgba(186,221,251,0.75) 20%, rgba(127,179,255,0.25) 40%, transparent 70%);
   background-position: center bottom var(--tab-toolbar-navbar-overlap);
   background-repeat: no-repeat;
   background-size: 85% 100%;
 }
 
+.tabbrowser-tab[image] > .tab-stack > .tab-content[attention]:not([pinned]):not([visuallyselected="true"]) {
+  background-position: left bottom var(--tab-toolbar-navbar-overlap);
+  background-size: 34px 100%;
+}
+
+.tab-label[attention]:not([visuallyselected="true"]) {
+  font-weight: bold;
+}
+
 /* Tab separators */
 
 .tabbrowser-tab::after,
 .tabbrowser-tab::before {
   width: 1px;
   -moz-margin-start: -1px;
   background-image: linear-gradient(transparent 5px,
                                     currentColor 5px,
deleted file mode 100644
--- a/devtools/client/app-manager/builtin-adb-store.js
+++ /dev/null
@@ -1,24 +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/. */
-
-const {Cu} = require("chrome");
-const ObservableObject = require("devtools/client/shared/observable-object");
-const {Devices} = Cu.import("resource://devtools/shared/apps/Devices.jsm");
-
-var store = new ObservableObject({versions:[]});
-
-function feedStore() {
-  store.object.available = Devices.helperAddonInstalled;
-  store.object.devices = Devices.available().map(n => {
-    return {name:n}
-  });
-}
-
-Devices.on("register", feedStore);
-Devices.on("unregister", feedStore);
-Devices.on("addon-status-updated", feedStore);
-
-feedStore();
-
-module.exports = store;
deleted file mode 100644
--- a/devtools/client/app-manager/connection-store.js
+++ /dev/null
@@ -1,54 +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/. */
-
-const ObservableObject = require("devtools/client/shared/observable-object");
-const {Connection} = require("devtools/shared/client/connection-manager");
-
-const _knownConnectionStores = new WeakMap();
-
-var ConnectionStore;
-
-module.exports = ConnectionStore = function(connection) {
-  // If we already know about this connection,
-  // let's re-use the existing store.
-  if (_knownConnectionStores.has(connection)) {
-    return _knownConnectionStores.get(connection);
-  }
-  _knownConnectionStores.set(connection, this);
-
-  ObservableObject.call(this, {status:null,host:null,port:null});
-
-  this.destroy = this.destroy.bind(this);
-  this._feedStore = this._feedStore.bind(this);
-
-  this._connection = connection;
-  this._connection.once(Connection.Events.DESTROYED, this.destroy);
-  this._connection.on(Connection.Events.STATUS_CHANGED, this._feedStore);
-  this._connection.on(Connection.Events.PORT_CHANGED, this._feedStore);
-  this._connection.on(Connection.Events.HOST_CHANGED, this._feedStore);
-  this._feedStore();
-  return this;
-}
-
-ConnectionStore.prototype = {
-  destroy: function() {
-    if (this._connection) {
-      // While this.destroy is bound using .once() above, that event may not
-      // have occurred when the ConnectionStore client calls destroy, so we
-      // manually remove it here.
-      this._connection.off(Connection.Events.DESTROYED, this.destroy);
-      this._connection.off(Connection.Events.STATUS_CHANGED, this._feedStore);
-      this._connection.off(Connection.Events.PORT_CHANGED, this._feedStore);
-      this._connection.off(Connection.Events.HOST_CHANGED, this._feedStore);
-      _knownConnectionStores.delete(this._connection);
-      this._connection = null;
-    }
-  },
-
-  _feedStore: function() {
-    this.object.status = this._connection.status;
-    this.object.host = this._connection.host;
-    this.object.port = this._connection.port;
-  }
-}
deleted file mode 100644
--- a/devtools/client/app-manager/content/connection-footer.js
+++ /dev/null
@@ -1,208 +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/. */
-
-var Cu = Components.utils;
-var Ci = Components.interfaces;
-Cu.import("resource://gre/modules/Services.jsm");
-Cu.import("resource://devtools/client/framework/gDevTools.jsm");
-
-const {Simulator} = Cu.import("resource://devtools/shared/apps/Simulator.jsm")
-const {Devices} = Cu.import("resource://devtools/shared/apps/Devices.jsm");
-const {require} = Cu.import("resource://devtools/shared/Loader.jsm", {});
-
-const {ConnectionManager, Connection} = require("devtools/shared/client/connection-manager");
-const {getDeviceFront} = require("devtools/server/actors/device");
-const ConnectionStore = require("devtools/client/app-manager/connection-store");
-const DeviceStore = require("devtools/client/app-manager/device-store");
-const simulatorsStore = require("devtools/client/app-manager/simulators-store");
-const adbStore = require("devtools/client/app-manager/builtin-adb-store");
-
-window.addEventListener("unload", function onUnload() {
-  window.removeEventListener("unload", onUnload);
-  UI.destroy();
-});
-
-var UI = {
-  init: function() {
-    this.useFloatingScrollbarsIfNeeded();
-    let connections = ConnectionManager.connections;
-    if (connections.length > 0) {
-      let hash = window.location.hash;
-      if (hash) {
-        let res = (/cid=([^&]+)/).exec(hash)
-        if (res) {
-          let [,cid] = res;
-          this.connection = connections.filter((({uid}) => uid == cid))[0];
-        }
-      }
-      if (!this.connection) {
-        // We take the first connection available.
-        this.connection = connections[0];
-      }
-    } else {
-      let host = Services.prefs.getCharPref("devtools.debugger.remote-host");
-      let port = Services.prefs.getIntPref("devtools.debugger.remote-port");
-      this.connection = ConnectionManager.createConnection(host, port);
-    }
-
-    window.location.hash = "cid=" + this.connection.uid;
-    window.parent.postMessage(JSON.stringify({name:"connection",cid:this.connection.uid}), "*");
-
-    this.store = Utils.mergeStores({
-      "device": new DeviceStore(this.connection),
-      "connection": new ConnectionStore(this.connection),
-      "simulators": simulatorsStore,
-      "adb": adbStore
-    });
-
-    let pre = document.querySelector("#logs > pre");
-    pre.textContent = this.connection.logs;
-    pre.scrollTop = pre.scrollTopMax;
-    this.connection.on(Connection.Events.NEW_LOG, this._onNewLog);
-
-    this.template = new Template(document.body, this.store, Utils.l10n);
-    this.template.start();
-
-    this._onSimulatorConnected = this._onSimulatorConnected.bind(this);
-    this._onSimulatorDisconnected = this._onSimulatorDisconnected.bind(this);
-  },
-
-  destroy: function() {
-    this.store.destroy();
-    this.connection.off(Connection.Events.NEW_LOG, this._onNewLog);
-    this.template.destroy();
-  },
-
-  _onNewLog: function(event, str) {
-    let pre = document.querySelector("#logs > pre");
-    pre.textContent += "\n" + str;
-    pre.scrollTop = pre.scrollTopMax;
-  },
-
-  useFloatingScrollbarsIfNeeded: function() {
-    if (Services.appinfo.OS == "Darwin") {
-      return;
-    }
-    let scrollbarsUrl = Services.io.newURI("chrome://devtools/skin/themes/floating-scrollbars-light.css", null, null);
-    let winUtils = window.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils);
-    winUtils.loadSheet(scrollbarsUrl, winUtils.AGENT_SHEET);
-    let computedStyle = window.getComputedStyle(document.documentElement);
-    if (computedStyle) { // Force a reflow to take the new css into account
-      let display = computedStyle.display; // Save display value
-      document.documentElement.style.display = "none";
-      window.getComputedStyle(document.documentElement).display; // Flush
-      document.documentElement.style.display = display; // Restore
-    }
-  },
-
-  disconnect: function() {
-    this.connection.disconnect();
-  },
-
-  connect: function() {
-    this.connection.connect();
-  },
-
-  editConnectionParameters: function() {
-    document.body.classList.add("edit-connection");
-    document.querySelector("input.host").focus();
-  },
-
-  saveConnectionInfo: function() {
-    document.body.classList.remove("edit-connection");
-    document.querySelector("#connect-button").focus();
-    let host = document.querySelector("input.host").value;
-    let port = document.querySelector("input.port").value;
-    this.connection.port = port;
-    this.connection.host = host;
-    Services.prefs.setCharPref("devtools.debugger.remote-host", host);
-    Services.prefs.setIntPref("devtools.debugger.remote-port", port);
-  },
-
-  showSimulatorList: function() {
-    document.body.classList.add("show-simulators");
-  },
-
-  cancelShowSimulatorList: function() {
-    document.body.classList.remove("show-simulators");
-  },
-
-  installSimulator: function() {
-    let url = "https://developer.mozilla.org/docs/Mozilla/Firefox_OS/Using_the_App_Manager#Simulator";
-    window.open(url);
-  },
-
-  startSimulator: function(version) {
-    this._portBeforeSimulatorStarted = this.connection.port;
-    let port = ConnectionManager.getFreeTCPPort();
-    let simulator = Simulator.getByName(version);
-    if (!simulator) {
-      this.connection.log("Error: can't find simulator: " + version);
-      return;
-    }
-    if (!simulator.launch) {
-      this.connection.log("Error: invalid simulator: " + version);
-      return;
-    }
-    this.connection.log("Found simulator: " + version);
-    this.connection.log("Starting simulator...");
-
-    this.simulator = simulator;
-    this.simulator.launch({ port: port })
-      .then(() => {
-        this.connection.log("Simulator ready. Connecting.");
-        this.connection.port = port;
-        this.connection.host = "localhost";
-        this.connection.once("connected",
-                             this._onSimulatorConnected);
-        this.connection.once("disconnected",
-                             this._onSimulatorDisconnected);
-        this.connection.keepConnecting = true;
-        this.connection.connect();
-      });
-    document.body.classList.remove("show-simulators");
-  },
-
-  _onSimulatorConnected: function() {
-    this.connection.log("Connected to simulator.");
-    this.connection.keepConnecting = false;
-
-    // This doesn't change the current (successful) connection,
-    // but makes sure that when the simulator is disconnected, the
-    // connection doesn't end up with a random port number (from
-    // getFreeTCPPort).
-    this.connection.port = this._portBeforeSimulatorStarted;
-  },
-
-  _onSimulatorDisconnected: function() {
-    this.connection.off("connected", this._onSimulatorConnected);
-  },
-
-  connectToAdbDevice: function(name) {
-    let device = Devices.getByName(name);
-    device.connect().then((port) => {
-      this.connection.host = "localhost";
-      this.connection.port = port;
-      this.connect();
-    });
-  },
-  
-  screenshot: function() {
-    this.connection.client.listTabs(
-      response => {
-        let front = getDeviceFront(this.connection.client, response);
-        front.screenshotToBlob().then(blob => {
-          let topWindow = Services.wm.getMostRecentWindow("navigator:browser");
-          let gBrowser = topWindow.gBrowser;
-          let url = topWindow.URL.createObjectURL(blob);
-          let tab = gBrowser.selectedTab = gBrowser.addTab(url);
-          tab.addEventListener("TabClose", function onTabClose() {
-            tab.removeEventListener("TabClose", onTabClose, false);
-            topWindow.URL.revokeObjectURL(url);
-          }, false);
-        }).then(null, console.error);
-      }
-    );
-  },
-}
deleted file mode 100644
--- a/devtools/client/app-manager/content/connection-footer.xhtml
+++ /dev/null
@@ -1,146 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- This Source Code Form is subject to the terms of the Mozilla Public
-     - License, v. 2.0. If a copy of the MPL was not distributed with this
-     - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
-<!DOCTYPE html [
-  <!ENTITY % appMgrDTD SYSTEM "chrome://browser/locale/devtools/app-manager.dtd" >
-  %appMgrDTD;
-]>
-
-
-<html xmlns="http://www.w3.org/1999/xhtml">
-  <head>
-    <meta charset="utf8"/>
-    <link rel="stylesheet" href="chrome://devtools/skin/themes/app-manager/connection-footer.css" type="text/css"/>
-  </head>
-
-  <body onload="UI.init()">
-
-    <div id="connection-footer" template='{"type":"attribute","path":"connection.status","name":"status"}'>
-      <div id="banners-and-logs">
-
-        <!-- Connected -->
-        <div id="banner-connected" class="banner">
-          <div class="connected-indicator"></div>
-          <div id="status" class="banner-box">
-            <div class="banner-content">
-              <span template='{"type":"localizedContent","property":"connection.connectedToDevice","paths":["device.description.name"]}'></span>
-              <button class="action-cancel" onclick="UI.disconnect()" title="&connection.disconnectTooltip;">&connection.disconnect;</button>
-              <button class="action-primary" onclick="UI.screenshot()" title="&device.screenshotTooltip;">&device.screenshot;</button>
-            </div>
-          </div>
-        </div>
-
-        <!-- Disconnected -->
-        <div id="banner-disconnected" class="banner">
-          <div class="connected-indicator"></div>
-          <div class="banner-box">
-            <div class="banner-content" template='{"type":"attribute","path":"adb.available","name":"adb-available"}'>
-              <span>&connection.notConnected2;</span>
-              <div id="connection-manual">
-                <button class="action-primary left" onclick="UI.connect()" id="connect-button" template='{"type":"localizedContent","property":"connection.connectTo","paths":["connection.host","connection.port"]}' title="&connection.connectTooltip;"></button>
-                <button class="right" onclick="UI.editConnectionParameters()" title="&connection.changeHostAndPortTooltip;">&connection.changeHostAndPort;</button>
-              </div>
-              <div id="connection-assisted" template='{"type":"attribute","path":"adb.devices.length","name":"device-count"}'>
-                <div id="connection-found-device">
-                  <span>&connection.connectTo;</span>
-                  <span template-loop='{"arrayPath":"adb.devices","childSelector":"#adb-devices-template"}'></span>
-                </div>
-                <div id="connection-no-device">
-                  <span>&connection.noDeviceFound;</span>
-                </div>
-              </div>
-              <div id="start-simulator-box">
-                <span>&connection.or;</span>
-                <button id="start-simulator-button" class="action-primary" onclick="UI.showSimulatorList()" title="&connection.startSimulatorTooltip;">&connection.startSimulator;</button>
-              </div>
-            </div>
-          </div>
-        </div>
-
-        <!-- Connecting -->
-        <div id="banner-connecting" class="banner">
-          <div class="connected-indicator"></div>
-          <div id="status" class="banner-box">
-            <div class="banner-content">
-              <span>&connection.connecting;</span>
-              <button class="action-cancel" onclick="UI.disconnect()" title="&connection.cancelConnectTooltip;">&connection.cancel;</button>
-            </div>
-          </div>
-        </div>
-
-        <!-- Disconnecting -->
-        <div id="banner-disconnecting" class="banner">
-          <div class="connected-indicator"></div>
-          <div id="status" class="banner-box">
-            <div class="banner-content">
-              <span>&connection.disconnecting;</span>
-            </div>
-          </div>
-        </div>
-
-        <!-- Editing -->
-        <div id="banner-editing" class="banner">
-          <div class="connected-indicator"></div>
-          <div class="banner-box">
-            <div class="banner-content">
-              <form onsubmit="UI.saveConnectionInfo()">
-                <input class="host" template='{"type":"attribute","path":"connection.host","name":"value"}'></input>
-                <input class="port" pattern="\d+" template='{"type":"attribute","path":"connection.port","name":"value"}' type="number"></input>
-                <button type="submit" title="&connection.saveConnectionInfoTooltip;">&connection.saveConnectionInfo;</button>
-              </form>
-            </div>
-          </div>
-        </div>
-
-        <!-- Simulator -->
-        <div id="banner-simulators" class="banner" template='{"type":"attribute","path":"simulators.versions.length","name":"simulator-count"}'>
-          <div class="connected-indicator"></div>
-          <div class="banner-box">
-            <div class="banner-content">
-              <div class="no-simulator">
-                <span>&connection.noSimulatorInstalled;</span>
-                <button class="action-primary" onclick="UI.installSimulator()" title="&connection.installOneSimulatorTooltip;">&connection.installOneSimulator;</button>
-              </div>
-              <div class="found-simulator">
-                <span>&connection.startRegisteredSimulator;</span>
-                <span template-loop='{"arrayPath":"simulators.versions","childSelector":"#simulator-item-template"}'></span>
-                <button class="action-primary" onclick="UI.installSimulator()" title="&connection.installAnotherSimulatorTooltip;">&connection.installAnotherSimulator;</button>
-              </div>
-              <button class="action-cancel" onclick="UI.cancelShowSimulatorList()" title="&connection.cancelShowSimulatorTooltip;">&connection.cancel;</button>
-            </div>
-          </div>
-        </div>
-
-
-        <!-- Logs -->
-        <div id="banner-logs">
-        <div id="logs" class="banner-box">
-          <pre></pre>
-        </div>
-        </div>
-
-      </div>
-    </div>
-  </body>
-
-  <template id="simulator-item-template">
-  <span>
-    <button class="simulator-item action-primary" onclick="UI.startSimulator(this.dataset.version)" template='{"type":"attribute","path":"version","name":"data-version"}' title="&connection.startSimulatorTooltip;">
-      <span template='{"type":"textContent", "path":"label"}'></span>
-    </button>
-  </span>
-  </template>
-
-  <template id="adb-devices-template">
-  <span>
-    <button class="adb-device action-primary" onclick="UI.connectToAdbDevice(this.dataset.name)" template='{"type":"attribute","path":"name","name":"data-name"}'>
-      <span template='{"type":"textContent", "path":"name"}'></span>
-    </button>
-  </span>
-  </template>
-
-  <script type="application/javascript;version=1.8" src="utils.js"></script>
-  <script type="application/javascript;version=1.8" src="template.js"></script>
-  <script type="application/javascript;version=1.8" src="connection-footer.js"></script>
-</html>
deleted file mode 100644
--- a/devtools/client/app-manager/content/device.js
+++ /dev/null
@@ -1,234 +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/. */
-
-var Cu = Components.utils;
-Cu.import("resource://gre/modules/Services.jsm");
-const {gDevTools} = Cu.import("resource://devtools/client/framework/gDevTools.jsm", {});
-
-const {require} = Cu.import("resource://devtools/shared/Loader.jsm", {});
-const {TargetFactory} = require("devtools/client/framework/target");
-
-const {ConnectionManager, Connection}
-  = require("devtools/shared/client/connection-manager");
-const {getDeviceFront} = require("devtools/server/actors/device");
-const {getTargetForApp, launchApp, closeApp}
-  = require("devtools/shared/apps/app-actor-front");
-const DeviceStore = require("devtools/client/app-manager/device-store");
-const WebappsStore = require("devtools/client/app-manager/webapps-store");
-const promise = require("devtools/shared/deprecated-sync-thenables");
-const DEFAULT_APP_ICON = "chrome://devtools/skin/themes/app-manager/images/default-app-icon.png";
-
-window.addEventListener("message", function(event) {
-  try {
-    let message = JSON.parse(event.data);
-    if (message.name == "connection") {
-      let cid = parseInt(message.cid);
-      for (let c of ConnectionManager.connections) {
-        if (c.uid == cid) {
-          UI.connection = c;
-          UI.onNewConnection();
-          break;
-        }
-      }
-    }
-  } catch(e) {
-    Cu.reportError(e);
-  }
-});
-
-window.addEventListener("unload", function onUnload() {
-  window.removeEventListener("unload", onUnload);
-  UI.destroy();
-});
-
-var UI = {
-  init: function() {
-    this.showFooterIfNeeded();
-    this.setTab("apps");
-    if (this.connection) {
-      this.onNewConnection();
-    } else {
-      this.hide();
-    }
-  },
-
-  destroy: function() {
-    if (this.connection) {
-      this.connection.off(Connection.Events.STATUS_CHANGED, this._onConnectionStatusChange);
-    }
-    if (this.store) {
-      this.store.destroy();
-    }
-    if (this.template) {
-      this.template.destroy();
-    }
-  },
-
-  showFooterIfNeeded: function() {
-    let footer = document.querySelector("#connection-footer");
-    if (window.parent == window) {
-      // We're alone. Let's add a footer.
-      footer.removeAttribute("hidden");
-      footer.src = "chrome://devtools/content/app-manager/content/connection-footer.xhtml";
-    } else {
-      footer.setAttribute("hidden", "true");
-    }
-  },
-
-  hide: function() {
-    document.body.classList.add("notconnected");
-  },
-
-  show: function() {
-    document.body.classList.remove("notconnected");
-  },
-
-  onNewConnection: function() {
-    this.connection.on(Connection.Events.STATUS_CHANGED, this._onConnectionStatusChange);
-
-    this.store = Utils.mergeStores({
-      "device": new DeviceStore(this.connection),
-      "apps": new WebappsStore(this.connection),
-    });
-
-    if (this.template) {
-      this.template.destroy();
-    }
-    this.template = new Template(document.body, this.store, Utils.l10n);
-
-    this.template.start();
-    this._onConnectionStatusChange();
-  },
-
-  setWallpaper: function(dataurl) {
-    document.getElementById("meta").style.backgroundImage = "url(" + dataurl + ")";
-  },
-
-  _onConnectionStatusChange: function() {
-    if (this.connection.status != Connection.Status.CONNECTED) {
-      this.hide();
-      this.listTabsResponse = null;
-    } else {
-      this.show();
-      this.connection.client.listTabs(
-        response => {
-          this.listTabsResponse = response;
-          let front = getDeviceFront(this.connection.client, this.listTabsResponse);
-          front.getWallpaper().then(longstr => {
-            longstr.string().then(dataURL => {
-              longstr.release().then(null, Cu.reportError);
-              this.setWallpaper(dataURL);
-            });
-          });
-          if (Services.prefs.getBoolPref("devtools.chrome.enabled")) {
-            let rootButton = document.getElementById("root-actor-debug");
-            if (response.consoleActor) {
-              rootButton.removeAttribute("hidden");
-            } else {
-              rootButton.setAttribute("hidden", "true");
-            }
-          }
-          let tabsButton = document.querySelector(".tab.browser-tabs");
-          if (response.tabs.length > 0) {
-            tabsButton.classList.remove("hidden");
-          } else {
-            tabsButton.classList.add("hidden");
-          }
-        }
-      );
-    }
-  },
-
-  get connected() { return !!this.listTabsResponse; },
-
-  setTab: function(name) {
-    var tab = document.querySelector(".tab.selected");
-    var panel = document.querySelector(".tabpanel.selected");
-
-    if (tab) tab.classList.remove("selected");
-    if (panel) panel.classList.remove("selected");
-
-    var tab = document.querySelector(".tab." + name);
-    var panel = document.querySelector(".tabpanel." + name);
-
-    if (tab) tab.classList.add("selected");
-    if (panel) panel.classList.add("selected");
-  },
-
-  openToolboxForRootActor: function() {
-    if (!this.connected) {
-      return;
-    }
-
-    let options = {
-      form: this.listTabsResponse,
-      client: this.connection.client,
-      chrome: true
-    };
-    TargetFactory.forRemoteTab(options).then((target) => {
-      top.UI.openAndShowToolboxForTarget(target, "Main process", null);
-    });
-  },
-
-  openToolboxForApp: function(manifest) {
-    if (!this.connected) {
-      return;
-    }
-
-    let app = this.store.object.apps.all.filter(a => a.manifestURL == manifest)[0];
-    getTargetForApp(this.connection.client,
-                    this.listTabsResponse.webappsActor,
-                    manifest).then((target) => {
-
-      top.UI.openAndShowToolboxForTarget(target, app.name, app.iconURL);
-    }, console.error);
-  },
-
-  _getTargetForTab: function (form) {
-      let options = {
-        form: form,
-        client: this.connection.client,
-        chrome: false
-      };
-      let deferred = promise.defer();
-      return TargetFactory.forRemoteTab(options);
-  },
-
-  openToolboxForTab: function (aNode) {
-    let index = Array.prototype.indexOf.apply(
-      aNode.parentNode.parentNode.parentNode.children,
-      [aNode.parentNode.parentNode]);
-    this.connection.client.listTabs(
-      response => {
-        let tab = response.tabs[index];
-        this._getTargetForTab(tab).then(target => {
-          top.UI.openAndShowToolboxForTarget(
-            target, tab.title, DEFAULT_APP_ICON);
-        }, console.error);
-      }
-    );
-  },
-
-  startApp: function(manifest) {
-    if (!this.connected) {
-      return promise.reject();
-    }
-    return launchApp(this.connection.client,
-                     this.listTabsResponse.webappsActor,
-                     manifest);
-  },
-
-  stopApp: function(manifest) {
-    if (!this.connected) {
-      return promise.reject();
-    }
-    return closeApp(this.connection.client,
-                    this.listTabsResponse.webappsActor,
-                    manifest);
-  },
-}
-
-// This must be bound immediately, as it might be used via the message listener
-// before UI.init() has been called.
-UI._onConnectionStatusChange = UI._onConnectionStatusChange.bind(UI);
deleted file mode 100644
--- a/devtools/client/app-manager/content/device.xhtml
+++ /dev/null
@@ -1,118 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- This Source Code Form is subject to the terms of the Mozilla Public
-     - License, v. 2.0. If a copy of the MPL was not distributed with this
-     - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
-<!DOCTYPE html [
-  <!ENTITY % appMgrDTD SYSTEM "chrome://browser/locale/devtools/app-manager.dtd" >
-  %appMgrDTD;
-]>
-
-
-<html xmlns="http://www.w3.org/1999/xhtml">
-
-  <head>
-    <meta charset="utf8"/>
-    <title>&device.title;</title>
-    <link rel="stylesheet" href="chrome://devtools/skin/themes/app-manager/device.css" type="text/css"/>
-  </head>
-
-  <body onload="UI.init()">
-
-    <div id="notConnectedMessage"><span>&device.notConnected;</span></div>
-
-    <section id="content">
-      <aside id="sidebar">
-        <div id="meta">
-          <header>
-            <h1>
-              <span template='{"type":"textContent","path":"device.description.name"}'></span>
-              <span template='{"type":"textContent","path":"device.description.version"}'></span>
-              <span template='{"type":"textContent","path":"device.description.channel"}'></span>
-            </h1>
-            <h3>
-              <span>Gecko </span>
-              <span template='{"type":"textContent","path":"device.description.geckoversion"}'></span>
-            </h3>
-            <p template='{"type":"localizedContent","property":"device.deviceSize", "paths":["device.description.width","device.description.height","device.description.dpi"]}'></p>
-            <button id="root-actor-debug" hidden="true" onclick="UI.openToolboxForRootActor()" title="&device.debugMainProcessTooltip;">&device.debugMainProcess;</button>
-          </header>
-          <div id="tabs-headers">
-            <div onclick="UI.setTab('apps')" class="tab sidebar-item apps" title="&device.installedAppsTooltip;">&device.installedApps;</div>
-            <div onclick="UI.setTab('permissions')" class="tab sidebar-item permissions" title="&device.permissionsTooltip;">
-              &device.permissions;
-              <a target="_blank" href="&device.permissionsHelpLink;">
-                <button class="help">&device.help;</button>
-              </a>
-            </div>
-            <div onclick="UI.setTab('browser-tabs')" class="tab sidebar-item browser-tabs" hidden="true" title="&device.browserTabsTooltip;">&device.browserTabs;</div>
-          </div>
-        </div>
-      </aside>
-      <section id="detail">
-        <div id="tabs">
-          <div class="tabpanel apps">
-            <div class="app-list" template-loop='{"arrayPath":"apps.all","childSelector":"#app-template"}'></div>
-          </div>
-          <div class="tabpanel permissions permission-table">
-            <div class="permission-table-header">
-              <div>&device.name;</div>
-              <div title="type:'web'">&device.plain;</div>
-              <div title="type:'privileged'">&device.privileged;</div>
-              <div title="type:'certified'">&device.certified;</div>
-            </div>
-            <div class="permission-table-body" >
-              <section template-loop='{"arrayPath":"device.permissions","childSelector":"#permission-template"}'></section>
-            </div>
-            <div class="permission-table-footer">
-              <div class="allow-label" title="&device.allowTooltip;">&device.allow;</div>
-              <div class="prompt-label" title="&device.promptTooltip;">&device.prompt;</div>
-              <div class="deny-label" title="&device.denyTooltip;">&device.deny;</div>
-            </div>
-          </div>
-          <div class="tabpanel browser-tabs">
-            <section template-loop='{"arrayPath":"device.tabs","childSelector":"#browser-tab-template"}'></section>
-          </div>
-        </div>
-      </section>
-    </section>
-    <iframe id="connection-footer" hidden="true"></iframe>
-  </body>
-
-  <template id="permission-template">
-  <div class="permission">
-    <div template='{"type":"textContent","path":"name"}'></div>
-    <div template='{"type":"attribute", "name":"permission", "path":"app"}'></div>
-    <div template='{"type":"attribute", "name":"permission", "path":"privileged"}'></div>
-    <div template='{"type":"attribute", "name":"permission", "path":"certified"}'></div>
-  </div>
-  </template>
-
-  <template id="browser-tab-template">
-  <div class="browser-tab">
-    <div class="browser-tab-details">
-      <p template='{"type":"textContent","path":"title"}'></p>
-      <p class="browser-tab-url-subheading" template='{"type":"textContent","path":"url"}'></p>
-    </div>
-    <div class="browser-tab-buttons">
-      <button class="button-debug" template='{"type":"attribute","path":"actor","name":"data-actor"}' onclick="UI.openToolboxForTab(this)" style="display: inline-block;" title="&device.debugBrowserTabTooltip;">&device.debugBrowserTab;</button>
-    </div>
-  </div>
-  </template>
-
-  <template id="app-template">
-  <div class="app" template='{"type":"attribute","path":"running","name":"running"}'>
-    <img class="app-icon" template='{"type":"attribute","path":"iconURL","name":"src"}'></img>
-    <span class="app-name" template='{"type":"textContent","path":"name"}'></span>
-    <div class="app-buttons">
-      <button class="button-debug" template='{"type":"attribute","path":"manifestURL","name":"data-manifest"}' onclick="UI.openToolboxForApp(this.dataset.manifest)" title="&device.debugAppTooltip;">&device.debugApp;</button>
-      <button class="button-start" template='{"type":"attribute","path":"manifestURL","name":"data-manifest"}' onclick="UI.startApp(this.dataset.manifest)" title="&device.startAppTooltip;">&device.startApp;</button>
-      <button class="button-stop" template='{"type":"attribute","path":"manifestURL","name":"data-manifest"}' onclick="UI.stopApp(this.dataset.manifest)" title="&device.stopAppTooltip;">&device.stopApp;</button>
-    </div>
-  </div>
-  </template>
-
-  <script type="application/javascript;version=1.8" src="utils.js"></script>
-  <script type="application/javascript;version=1.8" src="template.js"></script>
-  <script type="application/javascript;version=1.8" src="device.js"></script>
-
-</html>
deleted file mode 100644
--- a/devtools/client/app-manager/content/help.xhtml
+++ /dev/null
@@ -1,42 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- This Source Code Form is subject to the terms of the Mozilla Public
-     - License, v. 2.0. If a copy of the MPL was not distributed with this
-     - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
-<!DOCTYPE html [
-  <!ENTITY % appMgrDTD SYSTEM "chrome://browser/locale/devtools/app-manager.dtd" >
-  %appMgrDTD;
-]>
-
-<html xmlns="http://www.w3.org/1999/xhtml">
-
-  <head>
-    <meta charset="utf8"/>
-    <title>&help.title;</title>
-    <link rel="stylesheet" href="chrome://devtools/skin/themes/app-manager/help.css" type="text/css"/>
-  </head>
-
-  <body>
-
-    <h1>&help.title;</h1>
-
-    <p>&help.intro;</p>
-
-    <p>&help.usefullLinks;</p>
-    <ul>
-      <li><a target="mdn" href="https://developer.mozilla.org/docs/Mozilla/Firefox_OS/Using_the_App_Manager">&help.appMgrDoc;</a></li>
-      <li><a target="mdn" href="https://developer.mozilla.org/docs/Mozilla/Firefox_OS/Using_the_App_Manager#Configuring_device">&help.configuringDevice;</a></li>
-      <li><a target="mdn" href="https://developer.mozilla.org/docs/Mozilla/Firefox_OS/Using_the_App_Manager#Troubleshooting">&help.troubleShooting;</a></li>
-      <li><a target="mdn" href="https://developer.mozilla.org/docs/Mozilla/Firefox_OS/Using_the_App_Manager#Simulator">&help.simulatorAddon;</a></li>
-      <li><a target="mdn" href="https://developer.mozilla.org/docs/Mozilla/Firefox_OS/Using_the_App_Manager#Adb_Helper_Add-on">&help.adbHelperAddon;</a></li>
-    </ul>
-
-    <button id="close-button" onclick="closeHelp()">&help.close;</button>
-
-  </body>
-
-  <script type="application/javascript;version=1.8">
-    function closeHelp() {
-      window.parent.postMessage(JSON.stringify({name:"closeHelp"}), "*");
-    }
-  </script>
-</html>
deleted file mode 100644
--- a/devtools/client/app-manager/content/index.js
+++ /dev/null
@@ -1,212 +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/. */
-
-var {utils: Cu, interfaces: Ci} = Components;
-Cu.import("resource://devtools/client/framework/gDevTools.jsm");
-const {require} = Cu.import("resource://devtools/shared/Loader.jsm", {});
-const {Toolbox} = require("devtools/client/framework/toolbox");
-const {ConnectionManager, Connection} = require("devtools/shared/client/connection-manager");
-const promise = require("devtools/shared/deprecated-sync-thenables");
-const prefs = require("sdk/preferences/service");
-const Services = require("Services");
-const Strings = Services.strings.createBundle("chrome://browser/locale/devtools/app-manager.properties");
-
-var UI = {
-  _toolboxTabCursor: 0,
-  _handledTargets: new Map(),
-
-  connection: null,
-
-  init: function() {
-    this.onLoad = this.onLoad.bind(this);
-    this.onUnload = this.onUnload.bind(this);
-    this.onMessage = this.onMessage.bind(this);
-    this.onConnected = this.onConnected.bind(this);
-    this.onDisconnected = this.onDisconnected.bind(this);
-
-    window.addEventListener("load", this.onLoad);
-    window.addEventListener("unload", this.onUnload);
-    window.addEventListener("message", this.onMessage);
-  },
-
-  onLoad: function() {
-    window.removeEventListener("load", this.onLoad);
-    let defaultPanel = prefs.get("devtools.appmanager.lastTab");
-    let panelExists = !!document.querySelector("." + defaultPanel  + "-panel");
-    this.selectTab(panelExists ? defaultPanel : "projects");
-    this.showDeprecationNotice();
-  },
-
-  onUnload: function() {
-    for (let [target, toolbox] of this._handledTargets) {
-      toolbox.destroy();
-    }
-
-    window.removeEventListener("unload", this.onUnload);
-    window.removeEventListener("message", this.onMessage);
-    if (this.connection) {
-      this.connection.off(Connection.Status.CONNECTED, this.onConnected);
-      this.connection.off(Connection.Status.DISCONNECTED, this.onDisconnected);
-    }
-  },
-
-  onMessage: function(event) {
-    try {
-      let json = JSON.parse(event.data);
-      switch (json.name) {
-        case "connection":
-          let cid = +json.cid;
-          for (let c of ConnectionManager.connections) {
-            if (c.uid == cid) {
-              this.onNewConnection(c);
-              break;
-            }
-          }
-          break;
-        case "closeHelp":
-          this.selectTab("projects");
-          break;
-        case "toolbox-raise":
-          window.top.focus();
-          this.selectTab(json.uid);
-          break;
-        case "toolbox-close":
-          this.closeToolboxTab(json.uid);
-          break;
-        case "toolbox-title":
-          // Not implemented
-          break;
-        default:
-          Cu.reportError("Unknown message: " + json.name);
-      }
-    } catch(e) { Cu.reportError(e); }
-
-    // Forward message
-    let panels = document.querySelectorAll(".panel");
-    for (let frame of panels) {
-      frame.contentWindow.postMessage(event.data, "*");
-    }
-  },
-
-  selectTabFromButton: function(button) {
-    if (!button.hasAttribute("panel"))
-      return;
-    this.selectTab(button.getAttribute("panel"));
-  },
-
-  selectTab: function(panel) {
-    let isToolboxTab = false;
-    for (let type of ["button", "panel"]) {
-      let oldSelection = document.querySelector("." + type + "[selected]");
-      let newSelection = document.querySelector("." + panel + "-" + type);
-      if (oldSelection) oldSelection.removeAttribute("selected");
-      if (newSelection) {
-        newSelection.setAttribute("selected", "true");
-        if (newSelection.classList.contains("toolbox")) {
-          isToolboxTab = true;
-        }
-      }
-    }
-    if (!isToolboxTab) {
-      prefs.set("devtools.appmanager.lastTab", panel);
-    }
-  },
-
-  onNewConnection: function(connection) {
-    this.connection = connection;
-    this.connection.on(Connection.Status.CONNECTED, this.onConnected);
-    this.connection.on(Connection.Status.DISCONNECTED, this.onDisconnected);
-  },
-
-  onConnected: function() {
-    document.querySelector("#content").classList.add("connected");
-  },
-
-  onDisconnected: function() {
-    for (let [,toolbox] of this._handledTargets) {
-      if (toolbox) {
-        toolbox.destroy();
-      }
-    }
-    this._handledTargets.clear();
-    document.querySelector("#content").classList.remove("connected");
-  },
-
-  createToolboxTab: function(name, iconURL, uid) {
-    let button = document.createElement("button");
-    button.className = "button toolbox " + uid + "-button";
-    button.setAttribute("panel", uid);
-    button.textContent = name;
-    button.setAttribute("style", "background-image: url(" + iconURL + ")");
-    let toolboxTabs = document.querySelector("#toolbox-tabs");
-    toolboxTabs.appendChild(button);
-    let iframe = document.createElement("iframe");
-    iframe.setAttribute("flex", "1");
-    iframe.className = "panel toolbox " + uid + "-panel";
-    let panels = document.querySelector("#tab-panels");
-    panels.appendChild(iframe);
-    this.selectTab(uid);
-    return iframe;
-  },
-
-  closeToolboxTab: function(uid) {
-    let buttonToDestroy = document.querySelector("." + uid + "-button");
-    let panelToDestroy = document.querySelector("." + uid + "-panel");
-
-    if (buttonToDestroy.hasAttribute("selected")) {
-      let lastTab = prefs.get("devtools.appmanager.lastTab");
-      this.selectTab(lastTab);
-    }
-
-    buttonToDestroy.remove();
-    panelToDestroy.remove();
-  },
-
-  openAndShowToolboxForTarget: function(target, name, icon) {
-    let host = Toolbox.HostType.CUSTOM;
-    let toolbox = gDevTools.getToolbox(target);
-    if (!toolbox) {
-      let uid = "uid" + this._toolboxTabCursor++;
-      let iframe = this.createToolboxTab(name, icon, uid);
-      let options = { customIframe: iframe , uid: uid };
-      this._handledTargets.set(target, null);
-      return gDevTools.showToolbox(target, null, host, options).then(toolbox => {
-        this._handledTargets.set(target, toolbox);
-        toolbox.once("destroyed", () => {
-          this._handledTargets.delete(target)
-        });
-      });
-    } else {
-      return gDevTools.showToolbox(target, null, host);
-    }
-  },
-
-  showDeprecationNotice: function() {
-    let message = Strings.GetStringFromName("index.deprecationNotice");
-
-    let buttons = [
-      {
-        label: Strings.GetStringFromName("index.launchWebIDE"),
-        callback: gDevToolsBrowser.openWebIDE
-      },
-      {
-        label: Strings.GetStringFromName("index.readMoreAboutWebIDE"),
-        callback: () => {
-          window.open("https://developer.mozilla.org/docs/Tools/WebIDE");
-        }
-      }
-    ];
-
-    let docShell = window.QueryInterface(Ci.nsIInterfaceRequestor)
-                   .getInterface(Ci.nsIWebNavigation)
-                   .QueryInterface(Ci.nsIDocShell);
-    let browser = docShell.chromeEventHandler;
-    let nbox = browser.ownerDocument.defaultView.gBrowser
-               .getNotificationBox(browser);
-    nbox.appendNotification(message, "app-manager-deprecation", null,
-                            nbox.PRIORITY_WARNING_LOW, buttons);
-  }
-};
-
-UI.init();
deleted file mode 100644
--- a/devtools/client/app-manager/content/index.xul
+++ /dev/null
@@ -1,41 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- This Source Code Form is subject to the terms of the Mozilla Public
-     - License, v. 2.0. If a copy of the MPL was not distributed with this
-     - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
-<!DOCTYPE window [
-  <!ENTITY % appMgrDTD SYSTEM "chrome://browser/locale/devtools/app-manager.dtd" >
-  %appMgrDTD;
-]>
-
-<?xml-stylesheet href="chrome://global/skin/global.css"?>
-<?xml-stylesheet href="chrome://devtools/skin/themes/app-manager/index.css"?>
-
-<window id="app-manager-window"
-        xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
-        title="&index.title;"
-        windowtype="devtools:app-manager"
-        macanimationtype="document"
-        fullscreenbutton="true"
-        screenX="4" screenY="4"
-        width="800" height="600"
-        persist="screenX screenY width height sizemode">
-
-  <vbox id="root" flex="1">
-    <hbox id="content" flex="1">
-      <vbox id="tabs" onclick="UI.selectTabFromButton(event.target)">
-        <button class="button projects-button" panel="projects">&index.projects2;</button>
-        <button class="button device-button" panel="device">&index.device2;</button>
-        <vbox id="toolbox-tabs" flex="1"/>
-        <button class="button help-button" panel="help">&index.help;</button>
-      </vbox>
-      <hbox id="tab-panels" flex="1">
-        <iframe flex="1" class="panel projects-panel" src="chrome://devtools/content/app-manager/content/projects.xhtml"/>
-        <iframe flex="1" class="panel device-panel" src="chrome://devtools/content/app-manager/content/device.xhtml"/>
-        <iframe flex="1" class="panel help-panel" src="chrome://devtools/content/app-manager/content/help.xhtml"></iframe>
-      </hbox>
-    </hbox>
-    <iframe id="connection-footer" src="chrome://devtools/content/app-manager/content/connection-footer.xhtml"></iframe>
-  </vbox>
-
-  <script type="application/javascript;version=1.8" src="chrome://devtools/content/app-manager/content/index.js"></script>
-</window>
deleted file mode 100644
--- a/devtools/client/app-manager/content/manifest-editor.js
+++ /dev/null
@@ -1,146 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-"use strict";
-
-Cu.import("resource://gre/modules/osfile.jsm");
-const {VariablesView} =
-  Cu.import("resource://devtools/client/shared/widgets/VariablesView.jsm", {});
-
-const VARIABLES_VIEW_URL =
-  "chrome://devtools/content/shared/widgets/VariablesView.xul";
-
-function ManifestEditor(project) {
-  this.project = project;
-  this._onContainerReady = this._onContainerReady.bind(this);
-  this._onEval = this._onEval.bind(this);
-  this._onSwitch = this._onSwitch.bind(this);
-  this._onDelete = this._onDelete.bind(this);
-  this._onNew = this._onNew.bind(this);
-}
-
-ManifestEditor.prototype = {
-  get manifest() { return this.project.manifest; },
-
-  get editable() { return this.project.type == "packaged"; },
-
-  show: function(containerElement) {
-    let deferred = promise.defer();
-    let iframe = this._iframe = document.createElement("iframe");
-
-    iframe.addEventListener("load", function onIframeLoad() {
-      iframe.removeEventListener("load", onIframeLoad, true);
-      deferred.resolve(iframe.contentWindow);
-    }, true);
-
-    iframe.setAttribute("src", VARIABLES_VIEW_URL);
-    iframe.classList.add("variables-view");
-    containerElement.appendChild(iframe);
-
-    return deferred.promise.then(this._onContainerReady);
-  },
-
-  _onContainerReady: function(varWindow) {
-    let variablesContainer = varWindow.document.querySelector("#variables");
-
-    variablesContainer.classList.add("manifest-editor");
-
-    let editor = this.editor = new VariablesView(variablesContainer);
-
-    editor.onlyEnumVisible = true;
-    editor.alignedValues = true;
-    editor.actionsFirst = true;
-
-    if (this.editable) {
-      editor.eval = this._onEval;
-      editor.switch = this._onSwitch;
-      editor.delete = this._onDelete;
-      editor.new = this._onNew;
-    }
-
-    return this.update();
-  },
-
-  _onEval: function(variable, value) {
-    let parent = this._descend(variable.ownerView.symbolicPath);
-    try {
-      parent[variable.name] = JSON.parse(value);
-    } catch(e) {
-      Cu.reportError(e);
-    }
-
-    this.update();
-  },
-
-  _onSwitch: function(variable, newName) {
-    if (variable.name == newName) {
-      return;
-    }
-
-    let parent = this._descend(variable.ownerView.symbolicPath);
-    parent[newName] = parent[variable.name];
-    delete parent[variable.name];
-
-    this.update();
-  },
-
-  _onDelete: function(variable) {
-    let parent = this._descend(variable.ownerView.symbolicPath);
-    delete parent[variable.name];
-  },
-
-  _onNew: function(variable, newName, newValue) {
-    let parent = this._descend(variable.symbolicPath);
-    try {
-      parent[newName] = JSON.parse(newValue);
-    } catch(e) {
-      Cu.reportError(e);
-    }
-
-    this.update();
-  },
-
-  /**
-   * Returns the value located at a given path in the manifest.
-   * @param path array
-   *        A string for each path component: ["developer", "name"]
-   */
-  _descend: function(path) {
-    let parent = this.manifest;
-    while (path.length) {
-      parent = parent[path.shift()];
-    }
-    return parent;
-  },
-
-  update: function() {
-    this.editor.rawObject = this.manifest;
-    this.editor.commitHierarchy();
-
-    // Wait until the animation from commitHierarchy has completed
-    let deferred = promise.defer();
-    setTimeout(deferred.resolve, this.editor.lazyEmptyDelay + 1);
-    return deferred.promise;
-  },
-
-  save: function() {
-    if (this.editable) {
-      let validator = new AppValidator(this.project);
-      let manifestFile = validator._getPackagedManifestFile();
-      let path = manifestFile.path;
-
-      let encoder = new TextEncoder();
-      let data = encoder.encode(JSON.stringify(this.manifest, null, 2));
-
-      return OS.File.writeAtomic(path, data, { tmpPath: path + ".tmp" });
-    }
-
-    return promise.resolve();
-  },
-
-  destroy: function() {
-    if (this._iframe) {
-      this._iframe.remove();
-    }
-  }
-};
deleted file mode 100644
--- a/devtools/client/app-manager/content/projects.js
+++ /dev/null
@@ -1,451 +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/. */
-
-var Cc = Components.classes;
-var Ci = Components.interfaces;
-var Cu = Components.utils;
-var Cr = Components.results;
-Cu.import("resource://devtools/client/framework/gDevTools.jsm");
-const {require} = Cu.import("resource://devtools/shared/Loader.jsm", {});
-const {ConnectionManager, Connection} = require("devtools/shared/client/connection-manager");
-const {AppProjects} = require("devtools/client/app-manager/app-projects");
-const {AppValidator} = require("devtools/client/app-manager/app-validator");
-const {Services} = Cu.import("resource://gre/modules/Services.jsm");
-const {FileUtils} = Cu.import("resource://gre/modules/FileUtils.jsm");
-const {installHosted, installPackaged, getTargetForApp,
-       reloadApp, launchApp, closeApp} = require("devtools/shared/apps/app-actor-front");
-const {EventEmitter} = Cu.import("resource://devtools/shared/event-emitter.js");
-
-const promise = require("devtools/shared/deprecated-sync-thenables");
-
-const MANIFEST_EDITOR_ENABLED = "devtools.appmanager.manifestEditor.enabled";
-
-window.addEventListener("message", function(event) {
-  try {
-    let json = JSON.parse(event.data);
-    if (json.name == "connection") {
-      let cid = parseInt(json.cid);
-      for (let c of ConnectionManager.connections) {
-        if (c.uid == cid) {
-          UI.connection = c;
-          UI.onNewConnection();
-          break;
-        }
-      }
-    }
-  } catch(e) {}
-});
-
-window.addEventListener("unload", function onUnload() {
-  window.removeEventListener("unload", onUnload);
-  UI.destroy();
-});
-
-var UI = {
-  isReady: false,
-
-  onload: function() {
-    if (Services.prefs.getBoolPref(MANIFEST_EDITOR_ENABLED)) {
-      document.querySelector("#lense").setAttribute("manifest-editable", "");
-    }
-
-    this.template = new Template(document.body, AppProjects.store, Utils.l10n);
-    this.template.start();
-
-    AppProjects.load().then(() => {
-      AppProjects.store.object.projects.forEach(UI.validate);
-      this.isReady = true;
-      this.emit("ready");
-    });
-  },
-
-  destroy: function() {
-    if (this.connection) {
-      this.connection.off(Connection.Events.STATUS_CHANGED, this._onConnectionStatusChange);
-    }
-    this.template.destroy();
-  },
-
-  onNewConnection: function() {
-    this.connection.on(Connection.Events.STATUS_CHANGED, this._onConnectionStatusChange);
-    this._onConnectionStatusChange();
-  },
-
-  _onConnectionStatusChange: function() {
-    if (this.connection.status != Connection.Status.CONNECTED) {
-      document.body.classList.remove("connected");
-      this.listTabsResponse = null;
-    } else {
-      document.body.classList.add("connected");
-      this.connection.client.listTabs(
-        response => {this.listTabsResponse = response}
-      );
-    }
-  },
-
-  get connected() { return !!this.listTabsResponse; },
-
-  _selectFolder: function() {
-    let fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
-    fp.init(window, Utils.l10n("project.filePickerTitle"), Ci.nsIFilePicker.modeGetFolder);
-    let res = fp.show();
-    if (res != Ci.nsIFilePicker.returnCancel)
-      return fp.file;
-    return null;
-  },
-
-  addPackaged: function(folder) {
-    if (!folder) {
-      folder = this._selectFolder();
-    }
-    if (!folder)
-      return;
-    return AppProjects.addPackaged(folder)
-                      .then(function (project) {
-                        UI.validate(project);
-                        UI.selectProject(project.location);
-                      });
-  },
-
-  addHosted: function() {
-    let form = document.querySelector("#new-hosted-project-wrapper");
-    if (!form.checkValidity())
-      return;
-
-    let urlInput = document.querySelector("#url-input");
-    let manifestURL = urlInput.value;
-    return AppProjects.addHosted(manifestURL)
-                      .then(function (project) {
-                        UI.validate(project);
-                        UI.selectProject(project.location);
-                      });
-  },
-
-  _getLocalIconURL: function(project, manifest) {
-    let icon;
-    if (manifest.icons) {
-      let size = Object.keys(manifest.icons).sort((a, b) => b - a)[0];
-      if (size) {
-        icon = manifest.icons[size];
-      }
-    }
-    if (!icon)
-      return "chrome://devtools/skin/themes/app-manager/images/default-app-icon.png";
-    if (project.type == "hosted") {
-      let manifestURL = Services.io.newURI(project.location, null, null);
-      let origin = Services.io.newURI(manifestURL.prePath, null, null);
-      return Services.io.newURI(icon, null, origin).spec;
-    } else if (project.type == "packaged") {
-      let projectFolder = FileUtils.File(project.location);
-      let folderURI = Services.io.newFileURI(projectFolder).spec;
-      return folderURI + icon.replace(/^\/|\\/, "");
-    }
-  },
-
-  validate: function(project) {
-    let validation = new AppValidator(project);
-    return validation.validate()
-      .then(function () {
-        if (validation.manifest) {
-          project.icon = UI._getLocalIconURL(project, validation.manifest);
-          project.manifest = validation.manifest;
-        }
-
-        project.validationStatus = "valid";
-
-        if (validation.warnings.length > 0) {
-          project.warningsCount = validation.warnings.length;
-          project.warnings = validation.warnings.join(",\n ");
-          project.validationStatus = "warning";
-        } else {
-          project.warnings = "";
-          project.warningsCount = 0;
-        }
-
-        if (validation.errors.length > 0) {
-          project.errorsCount = validation.errors.length;
-          project.errors = validation.errors.join(",\n ");
-          project.validationStatus = "error";
-        } else {
-          project.errors = "";
-          project.errorsCount = 0;
-        }
-
-        if (project.warningsCount && project.errorsCount) {
-          project.validationStatus = "error warning";
-        }
-
-      });
-
-  },
-
-  update: function(button, location) {
-    button.disabled = true;
-    let project = AppProjects.get(location);
-
-    // Update the manifest editor view, in case the manifest was modified
-    // outside of the app manager.  This can happen in parallel with the other
-    // steps.
-    this._showManifestEditor(project);
-
-    this.validate(project)
-        .then(() => {
-           // Install the app to the device if we are connected,
-           // and there is no error
-           if (project.errorsCount == 0 && this.connected) {
-             return this.install(project);
-           }
-         })
-        .then(() => {
-           button.disabled = false;
-           // Finally try to reload the app if it is already opened
-           if (this.connected) {
-             this.reload(project);
-           }
-        },
-        (res) => {
-          button.disabled = false;
-          let message = res.error + ": " + res.message;
-          alert(message);
-          this.connection.log(message);
-        });
-  },
-
-  saveManifest: function(button) {
-    button.disabled = true;
-    this.manifestEditor.save().then(() => button.disabled = false);
-  },
-
-  reload: function (project) {
-    if (!this.connected) {
-      return promise.reject();
-    }
-    return reloadApp(this.connection.client,
-              this.listTabsResponse.webappsActor,
-              this._getProjectManifestURL(project)).
-      then(() => {
-        this.connection.log("App reloaded");
-      });
-  },
-
-  remove: function(location, event) {
-    if (event) {
-      // We don't want the "click" event to be propagated to the project item.
-      // That would trigger `selectProject()`.
-      event.stopPropagation();
-    }
-
-    let item = document.getElementById(location);
-
-    let toSelect = document.querySelector(".project-item.selected");
-    toSelect = toSelect ? toSelect.id : "";
-
-    if (toSelect == location) {
-      toSelect = null;
-      let sibling;
-      if (item.previousElementSibling) {
-        sibling = item.previousElementSibling;
-      } else {
-        sibling = item.nextElementSibling;
-      }
-      if (sibling && !!AppProjects.get(sibling.id)) {
-        toSelect = sibling.id;
-      }
-    }
-
-    AppProjects.remove(location).then(() => {
-      this.selectProject(toSelect);
-    });
-  },
-
-  _getProjectManifestURL: function (project) {
-    if (project.type == "packaged") {
-      return "app://" + project.packagedAppOrigin + "/manifest.webapp";
-    } else if (project.type == "hosted") {
-      return project.location;
-    }
-  },
-
-  install: function(project) {
-    if (!this.connected) {
-      return promise.reject();
-    }
-    this.connection.log("Installing the " + project.manifest.name + " app...");
-    let installPromise;
-    if (project.type == "packaged") {
-      installPromise = installPackaged(this.connection.client, this.listTabsResponse.webappsActor, project.location, project.packagedAppOrigin)
-        .then(({ appId }) => {
-          // If the packaged app specified a custom origin override,
-          // we need to update the local project origin
-          project.packagedAppOrigin = appId;
-          // And ensure the indexed db on disk is also updated
-          AppProjects.update(project);
-        });
-    } else {
-      let manifestURLObject = Services.io.newURI(project.location, null, null);
-      let origin = Services.io.newURI(manifestURLObject.prePath, null, null);
-      let appId = origin.host;
-      let metadata = {
-        origin: origin.spec,
-        manifestURL: project.location
-      };
-      installPromise = installHosted(this.connection.client, this.listTabsResponse.webappsActor, appId, metadata, project.manifest);
-    }
-
-    installPromise.then(() => {
-      this.connection.log("Install completed.");
-    }, () => {
-      this.connection.log("Install failed.");
-    });
-
-    return installPromise;
-  },
-
-  start: function(project) {
-    if (!this.connected) {
-      return promise.reject();
-    }
-    let manifestURL = this._getProjectManifestURL(project);
-    return launchApp(this.connection.client,
-                     this.listTabsResponse.webappsActor,
-                     manifestURL);
-  },
-
-  stop: function(location) {
-    if (!this.connected) {
-      return promise.reject();
-    }
-    let project = AppProjects.get(location);
-    let manifestURL = this._getProjectManifestURL(project);
-    return closeApp(this.connection.client,
-                    this.listTabsResponse.webappsActor,
-                    manifestURL);
-  },
-
-  debug: function(button, location) {
-    if (!this.connected) {
-      return promise.reject();
-    }
-    button.disabled = true;
-    let project = AppProjects.get(location);
-
-    let onFailedToStart = (error) => {
-      // If not installed, install and open it
-      if (error == "NO_SUCH_APP") {
-        return this.install(project);
-      } else {
-        throw error;
-      }
-    };
-    let onStarted = () => {
-      // Once we asked the app to launch, the app isn't necessary completely loaded.
-      // launch request only ask the app to launch and immediatly returns.
-      // We have to keep trying to get app tab actors required to create its target.
-      let deferred = promise.defer();
-      let loop = (count) => {
-        // Ensure not looping for ever
-        if (count >= 100) {
-          deferred.reject("Unable to connect to the app");
-          return;
-        }
-        // Also, in case the app wasn't installed yet, we also have to keep asking the
-        // app to launch, as launch request made right after install may race.
-        this.start(project);
-        getTargetForApp(
-          this.connection.client,
-          this.listTabsResponse.webappsActor,
-          this._getProjectManifestURL(project)).
-            then(deferred.resolve,
-                 (err) => {
-                   if (err == "appNotFound")
-                     setTimeout(loop, 500, count + 1);
-                   else
-                     deferred.reject(err);
-                 });
-      };
-      loop(0);
-      return deferred.promise;
-    };
-
-    // First try to open the app
-    this.start(project)
-        .then(null, onFailedToStart)
-        .then(onStarted)
-        .then((target) =>
-          top.UI.openAndShowToolboxForTarget(target,
-                                             project.manifest.name,
-                                             project.icon))
-        .then(() => {
-           // And only when the toolbox is opened, release the button
-           button.disabled = false;
-         },
-         (err) => {
-           button.disabled = false;
-           let message = err.error ? err.error + ": " + err.message : String(err);
-           alert(message);
-           this.connection.log(message);
-         });
-  },
-
-  reveal: function(location) {
-    let project = AppProjects.get(location);
-    if (project.type == "packaged") {
-      let projectFolder = FileUtils.File(project.location);
-      projectFolder.reveal();
-    } else {
-      // TODO: eventually open hosted apps in firefox
-      // when permissions are correctly supported by firefox
-    }
-  },
-
-  selectProject: function(location) {
-    let projects = AppProjects.store.object.projects;
-    let idx = 0;
-    for (; idx < projects.length; idx++) {
-      if (projects[idx].location == location) {
-        break;
-      }
-    }
-
-    let oldButton = document.querySelector(".project-item.selected");
-    if (oldButton) {
-      oldButton.classList.remove("selected");
-    }
-
-    if (idx == projects.length) {
-      // Not found. Empty lense.
-      let lense = document.querySelector("#lense");
-      lense.setAttribute("template-for", '{"path":"","childSelector":""}');
-      this.template._processFor(lense);
-      return;
-    }
-
-    let button = document.getElementById(location);
-    button.classList.add("selected");
-
-    let template = '{"path":"projects.' + idx + '","childSelector":"#lense-template"}';
-
-    let lense = document.querySelector("#lense");
-    lense.setAttribute("template-for", template);
-    this.template._processFor(lense);
-
-    let project = projects[idx];
-    this._showManifestEditor(project).then(() => this.emit("project-selected"));
-  },
-
-  _showManifestEditor: function(project) {
-    if (this.manifestEditor) {
-      this.manifestEditor.destroy();
-    }
-    let editorContainer = document.querySelector("#lense .manifest-editor");
-    this.manifestEditor = new ManifestEditor(project);
-    return this.manifestEditor.show(editorContainer);
-  }
-};
-
-// This must be bound immediately, as it might be used via the message listener
-// before UI.onload() has been called.
-UI._onConnectionStatusChange = UI._onConnectionStatusChange.bind(UI);
-
-EventEmitter.decorate(UI);
deleted file mode 100644
--- a/devtools/client/app-manager/content/projects.xhtml
+++ /dev/null
@@ -1,96 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- This Source Code Form is subject to the terms of the Mozilla Public
-     - License, v. 2.0. If a copy of the MPL was not distributed with this
-     - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
-<!DOCTYPE html [
-  <!ENTITY % appMgrDTD SYSTEM "chrome://browser/locale/devtools/app-manager.dtd" >
-  %appMgrDTD;
-]>
-
-<html xmlns="http://www.w3.org/1999/xhtml">
-
-  <head>
-    <meta charset="utf8"/>
-    <title>&projects.title;</title>
-    <link rel="stylesheet" href="chrome://devtools/skin/themes/app-manager/projects.css" type="text/css"/>
-    <script type="application/javascript;version=1.8" src="utils.js"></script>
-    <script type="application/javascript;version=1.8" src="projects.js"></script>
-    <script type="application/javascript;version=1.8" src="template.js"></script>
-    <script type="application/javascript;version=1.8" src="manifest-editor.js"></script>
-  </head>
-
-  <body onload="UI.onload()">
-    <aside id="sidebar">
-      <div id="project-list" template='{"type":"attribute","path":"projects.length","name":"projects-count"}'>
-        <div template-loop='{"arrayPath":"projects","childSelector":"#project-item-template"}'></div>
-        <div id="no-project">&projects.noProjects;</div>
-      </div>
-      <div id="new-packaged-project" onclick="UI.addPackaged()" title="&projects.addPackagedTooltip;">&projects.addPackaged;</div>
-      <div id="new-hosted-project">&projects.addHosted;
-        <form onsubmit="UI.addHosted(); return false;" id="new-hosted-project-wrapper">
-          <input value="" id="url-input" type="url" required="true" pattern="(https?|chrome)://.+" placeholder="&projects.hostedManifestPlaceHolder2;" size="50" />
-          <div onclick="UI.addHosted()" id="new-hosted-project-click" title="&projects.addHostedTooltip;"></div>
-          <input type="submit" hidden="true"></input>
-        </form>
-      </div>
-    </aside>
-    <section id="lense"></section>
-  </body>
-
-  <template id="project-item-template">
-  <div class="project-item" template='{"type":"attribute","path":"location","name":"id"}' onclick="UI.selectProject(this.id)">
-    <div class="project-item-status" template='{"type":"attribute","path":"validationStatus","name":"status"}'></div>
-    <img class="project-item-icon" template='{"type":"attribute","path":"icon","name":"src"}' />
-    <div class="project-item-meta">
-      <div class="button-remove" onclick="UI.remove(this.dataset.location, event)" template='{"type":"attribute","path":"location","name":"data-location"}' title="&projects.removeAppFromList;"></div>
-      <strong template='{"type":"textContent","path":"manifest.name"}'></strong>
-      <span class="project-item-type" template='{"type":"textContent","path":"type"}'></span>
-      <p class="project-item-description" template='{"type":"textContent","path":"manifest.description"}'></p>
-      <div template='{"type":"attribute","path":"validationStatus","name":"status"}'>
-        <div class="project-item-errors"><span template='{"type":"textContent","path":"errorsCount"}'></span></div>
-        <div class="project-item-warnings"><span template='{"type":"textContent","path":"warningsCount"}'></span></div>
-      </div>
-    </div>
-  </div>
-  </template>
-
-  <template id="lense-template">
-  <div>
-    <div class="project-details" template='{"type":"attribute","path":"validationStatus","name":"status"}'>
-      <div class="project-header">
-        <img class="project-icon" template='{"type":"attribute","path":"icon","name":"src"}'/>
-        <div class="project-metadata">
-          <div class="project-title">
-            <h1 template='{"type":"textContent","path":"manifest.name"}'></h1>
-            <div class="project-status" template='{"type":"attribute","path":"validationStatus","name":"status"}'>
-              <p class="project-validation valid">&projects.valid;</p>
-              <p class="project-validation warning">&projects.warning;</p>
-              <p class="project-validation error">&projects.error;</p>
-            </div>
-            <div class="project-status" template='{"type":"attribute","path":"type","name":"type"}'>
-            	<p class="project-type hosted">&projects.hosted;</p>
-            	<p class="project-type packaged">&projects.packaged;</p>
-            </div>
-          </div>
-          <span template='{"type":"textContent","path":"manifest.developer.name"}'></span>
-          <p class="project-location" template='{"type":"textContent","path":"location"}' onclick="UI.reveal(this.textContent)"></p>
-          <p class="project-description" template='{"type":"textContent","path":"manifest.description"}'></p>
-        </div>
-      </div>
-      <div class="project-buttons" template='{"type":"attribute","path":"type","name":"type"}'>
-        <button class="project-button-update" onclick="UI.update(this, this.dataset.location)" template='{"type":"attribute","path":"location","name":"data-location"}' title="&projects.updateAppTooltip;">&projects.updateApp;</button>
-        <button class="device-action project-button-debug" onclick="UI.debug(this, this.dataset.location)" template='{"type":"attribute","path":"location","name":"data-location"}' title="&projects.debugAppTooltip;">&projects.debugApp;</button>
-      </div>
-      <div class="project-errors" template='{"type":"textContent","path":"errors"}'></div>
-      <div class="project-warnings" template='{"type":"textContent","path":"warnings"}'></div>
-    </div>
-    <div class="manifest-editor">
-      <div class="manifest-header" template='{"type":"attribute","path":"type","name":"type"}'>
-        <h2 class="editable" title="&projects.manifestEditorTooltip;">&projects.manifestEditor;</h2>
-        <h2 class="viewable" title="&projects.manifestViewerTooltip;">&projects.manifestViewer;</h2>
-        <button class="editable manifest-button-save" onclick="UI.saveManifest(this, this.dataset.location)" template='{"type":"attribute","path":"location","name":"data-location"}' title="&projects.saveManifestTooltip;">&projects.saveManifest;</button>
-      </div>
-    </div>
-  </div>
-  </template>
-</html>
deleted file mode 100644
--- a/devtools/client/app-manager/content/template.js
+++ /dev/null
@@ -1,406 +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/. */
-
-/**
- * Template mechanism based on Object Emitters.
- *
- * The data used to expand the templates comes from
- * a ObjectEmitter object. The templates are automatically
- * updated as the ObjectEmitter is updated (via the "set"
- * event). See documentation in observable-object.js.
- *
- * Templates are used this way:
- *
- * (See examples in devtools/client/app-manager/content/*.xhtml)
- *
- * <div template="{JSON Object}">
- *
- * {
- *  type: "attribute"
- *  name: name of the attribute
- *  path: location of the attribute value in the ObjectEmitter
- * }
- *
- * {
- *  type: "textContent"
- *  path: location of the textContent value in the ObjectEmitter
- * }
- *
- * {
- *  type: "localizedContent"
- *  paths: array of locations of the value of the arguments of the property
- *  property: l10n property
- * }
- *
- * <div template-loop="{JSON Object}">
- *
- * {
- *  arrayPath: path of the array in the ObjectEmitter to loop from
- *  childSelector: selector of the element to duplicate in the loop
- * }
- *
- */
-
-const NOT_FOUND_STRING = "n/a";
-
-/**
- * let t = new Template(root, store, l10nResolver);
- * t.start();
- *
- * @param DOMNode root.
- *        Node from where templates are expanded.
- * @param ObjectEmitter store.
- *        ObjectEmitter object.
- * @param function (property, args). l10nResolver
- *        A function that returns localized content.
- */
-function Template(root, store, l10nResolver) {
-  this._store = store;
-  this._rootResolver = new Resolver(this._store.object);
-  this._l10n = l10nResolver;
-
-  // Listeners are stored in Maps.
-  // path => Set(node1, node2, ..., nodeN)
-  // For example: "foo.bar.4.name" => Set(div1,div2)
-
-  this._nodeListeners = new Map();
-  this._loopListeners = new Map();
-  this._forListeners = new Map();
-  this._root = root;
-  this._doc = this._root.ownerDocument;
-
-  this._queuedNodeRegistrations = [];
-
-  this._storeChanged = this._storeChanged.bind(this);
-  this._store.on("set", this._storeChanged);
-}
-
-Template.prototype = {
-  start: function() {
-    this._processTree(this._root);
-    this._registerQueuedNodes();
-  },
-
-  destroy: function() {
-    this._store.off("set", this._storeChanged);
-    this._root = null;
-    this._doc = null;
-  },
-
-  _storeChanged: function(event, path, value) {
-
-    // The store has changed (a "set" event has been emitted).
-    // We need to invalidate and rebuild the affected elements.
-
-    let strpath = path.join(".");
-    this._invalidate(strpath);
-
-    for (let [registeredPath, set] of this._nodeListeners) {
-      if (strpath != registeredPath &&
-          registeredPath.indexOf(strpath) > -1) {
-        this._invalidate(registeredPath);
-      }
-    }
-
-    this._registerQueuedNodes();
-  },
-
-  _invalidate: function(path) {
-    // Loops:
-    let set = this._loopListeners.get(path);
-    if (set) {
-      for (let elt of set) {
-        this._processLoop(elt);
-      }
-    }
-
-    // For:
-    set = this._forListeners.get(path);
-    if (set) {
-      for (let elt of set) {
-        this._processFor(elt);
-      }
-    }
-
-    // Nodes:
-    set = this._nodeListeners.get(path);
-    if (set) {
-      for (let elt of set) {
-        this._processNode(elt);
-      }
-    }
-  },
-
-  // Delay node registration until the last step of starting / updating the UI.
-  // This allows us to avoid doing double work in _storeChanged where the first
-  // call to |_invalidate| registers new nodes, which would then be visited a
-  // second time when it iterates over node listeners.
-  _queueNodeRegistration: function(path, element) {
-    this._queuedNodeRegistrations.push([path, element]);
-  },
-
-  _registerQueuedNodes: function() {
-    for (let [path, element] of this._queuedNodeRegistrations) {
-      // We map a node to a path.
-      // If the value behind this path is updated,
-      // we get notified from the ObjectEmitter,
-      // and then we know which objects to update.
-      if (!this._nodeListeners.has(path)) {
-        this._nodeListeners.set(path, new Set());
-      }
-      let set = this._nodeListeners.get(path);
-      set.add(element);
-    }
-    this._queuedNodeRegistrations.length = 0;
-  },
-
-  _unregisterNodes: function(nodes) {
-    for (let e of nodes) {
-      for (let registeredPath of e.registeredPaths) {
-        let set = this._nodeListeners.get(registeredPath);
-        if (!set) {
-          continue;
-        }
-        set.delete(e);
-        if (set.size === 0) {
-          this._nodeListeners.delete(registeredPath);
-        }
-      }
-      e.registeredPaths = null;
-    }
-  },
-
-  _registerLoop: function(path, element) {
-    if (!this._loopListeners.has(path)) {
-      this._loopListeners.set(path, new Set());
-    }
-    let set = this._loopListeners.get(path);
-    set.add(element);
-  },
-
-  _registerFor: function(path, element) {
-    if (!this._forListeners.has(path)) {
-      this._forListeners.set(path, new Set());
-    }
-    let set = this._forListeners.get(path);
-    set.add(element);
-  },
-
-  _processNode: function(element, resolver=this._rootResolver) {
-    // The actual magic.
-    // The element has a template attribute.
-    // The value is supposed to be a JSON string.
-    // resolver is a helper object that is used to retrieve data
-    // from the template's data store, give the current path into
-    // the data store, or descend down another level of the store.
-    // See the Resolver object below.
-
-    let e = element;
-    let str = e.getAttribute("template");
-
-    try {
-      let json = JSON.parse(str);
-      // Sanity check
-      if (!("type" in json)) {
-        throw new Error("missing property");
-      }
-      if (json.rootPath) {
-        // If node has been generated through a loop, we stored
-        // previously its rootPath.
-        resolver = this._rootResolver.descend(json.rootPath);
-      }
-
-      // paths is an array that will store all the paths we needed
-      // to expand the node. We will then, via
-      // _registerQueuedNodes, link this element to these paths.
-
-      let paths = [];
-
-      switch (json.type) {
-        case "attribute": {
-          if (!("name" in json) ||
-              !("path" in json)) {
-            throw new Error("missing property");
-          }
-          e.setAttribute(json.name, resolver.get(json.path, NOT_FOUND_STRING));
-          paths.push(resolver.rootPathTo(json.path));
-          break;
-        }
-        case "textContent": {
-          if (!("path" in json)) {
-            throw new Error("missing property");
-          }
-          e.textContent = resolver.get(json.path, NOT_FOUND_STRING);
-          paths.push(resolver.rootPathTo(json.path));
-          break;
-        }
-        case "localizedContent": {
-          if (!("property" in json) ||
-              !("paths" in json)) {
-            throw new Error("missing property");
-          }
-          let params = json.paths.map((p) => {
-            paths.push(resolver.rootPathTo(p));
-            let str = resolver.get(p, NOT_FOUND_STRING);
-            return str;
-          });
-          e.textContent = this._l10n(json.property, params);
-          break;
-        }
-      }
-      if (resolver !== this._rootResolver) {
-        // We save the rootPath if any.
-        json.rootPath = resolver.path;
-        e.setAttribute("template", JSON.stringify(json));
-      }
-      if (paths.length > 0) {
-        for (let path of paths) {
-          this._queueNodeRegistration(path, e);
-        }
-      }
-      // Store all the paths on the node, to speed up unregistering later
-      e.registeredPaths = paths;
-    } catch(exception) {
-      console.error("Invalid template: " + e.outerHTML + " (" + exception + ")");
-    }
-  },
-
-  _processLoop: function(element, resolver=this._rootResolver) {
-    // The element has a template-loop attribute.
-    // The related path must be an array. We go
-    // through the array, and build one child per
-    // item. The template for this child is pointed
-    // by the childSelector property.
-    let e = element;
-    try {
-      let template, count;
-      let str = e.getAttribute("template-loop");
-      let json = JSON.parse(str);
-      if (!("arrayPath" in json)     ||
-          !("childSelector" in json)) {
-        throw new Error("missing property");
-      }
-      let descendedResolver = resolver.descend(json.arrayPath);
-      let templateParent = this._doc.querySelector(json.childSelector);
-      if (!templateParent) {
-        throw new Error("can't find child");
-      }
-      template = this._doc.createElement("div");
-      template.innerHTML = templateParent.innerHTML;
-      template = template.firstElementChild;
-      let array = descendedResolver.get("", []);
-      if (!Array.isArray(array)) {
-        console.error("referenced array is not an array");
-      }
-      count = array.length;
-
-      let fragment = this._doc.createDocumentFragment();
-      for (let i = 0; i < count; i++) {
-        let node = template.cloneNode(true);
-        this._processTree(node, descendedResolver.descend(i));
-        fragment.appendChild(node);
-      }
-      this._registerLoop(descendedResolver.path, e);
-      this._registerLoop(descendedResolver.rootPathTo("length"), e);
-      this._unregisterNodes(e.querySelectorAll("[template]"));
-      e.innerHTML = "";
-      e.appendChild(fragment);
-    } catch(exception) {
-      console.error("Invalid template: " + e.outerHTML + " (" + exception + ")");
-    }
-  },
-
-  _processFor: function(element, resolver=this._rootResolver) {
-    let e = element;
-    try {
-      let template;
-      let str = e.getAttribute("template-for");
-      let json = JSON.parse(str);
-      if (!("path" in json) ||
-          !("childSelector" in json)) {
-        throw new Error("missing property");
-      }
-
-      if (!json.path) {
-        // Nothing to show.
-        this._unregisterNodes(e.querySelectorAll("[template]"));
-        e.innerHTML = "";
-        return;
-      }
-
-      let descendedResolver = resolver.descend(json.path);
-      let templateParent = this._doc.querySelector(json.childSelector);
-      if (!templateParent) {
-        throw new Error("can't find child");
-      }
-      let content = this._doc.createElement("div");
-      content.innerHTML = templateParent.innerHTML;
-      content = content.firstElementChild;
-
-      this._processTree(content, descendedResolver);
-
-      this._unregisterNodes(e.querySelectorAll("[template]"));
-      this._registerFor(descendedResolver.path, e);
-
-      e.innerHTML = "";
-      e.appendChild(content);
-
-    } catch(exception) {
-      console.error("Invalid template: " + e.outerHTML + " (" + exception + ")");
-    }
-  },
-
-  _processTree: function(parent, resolver=this._rootResolver) {
-    let loops = parent.querySelectorAll(":not(template) [template-loop]");
-    let fors = parent.querySelectorAll(":not(template) [template-for]");
-    let nodes = parent.querySelectorAll(":not(template) [template]");
-    for (let i = 0; i < loops.length; i++) {
-      this._processLoop(loops[i], resolver);
-    }
-    for (let i = 0; i < fors.length; i++) {
-      this._processFor(fors[i], resolver);
-    }
-    for (let i = 0; i < nodes.length; i++) {
-      this._processNode(nodes[i], resolver);
-    }
-    if (parent.hasAttribute("template")) {
-      this._processNode(parent, resolver);
-    }
-  },
-};
-
-function Resolver(object, path = "") {
-  this._object = object;
-  this.path = path;
-}
-
-Resolver.prototype = {
-
-  get: function(path, defaultValue = null) {
-    let obj = this._object;
-    if (path === "") {
-      return obj;
-    }
-    let chunks = path.toString().split(".");
-    for (let i = 0; i < chunks.length; i++) {
-      let word = chunks[i];
-      if ((typeof obj) == "object" &&
-          (word in obj)) {
-        obj = obj[word];
-      } else {
-        return defaultValue;
-      }
-    }
-    return obj;
-  },
-
-  rootPathTo: function(path) {
-    return this.path ? this.path + "." + path : path;
-  },
-
-  descend: function(path) {
-    return new Resolver(this.get(path), this.rootPathTo(path));
-  }
-
-};
deleted file mode 100644
--- a/devtools/client/app-manager/content/utils.js
+++ /dev/null
@@ -1,69 +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/. */
-
-/**
- *
- * Some helpers for common operations in the App Manager:
- *
- *  . mergeStores: merge several store into one.
- *  . l10n: resolves strings from app-manager.properties.
- *
- */
-
-var Utils = (function() {
-  const Cu = Components.utils;
-  const {Services} = Cu.import("resource://gre/modules/Services.jsm", {});
-  const {require} = Cu.import("resource://devtools/shared/Loader.jsm", {});
-  const EventEmitter = require("devtools/shared/event-emitter");
-
-
-  function _createSetEventForwarder(key, finalStore) {
-    return function(event, path, value) {
-      finalStore.emit("set", [key].concat(path), value);
-    };
-  }
-
-  function mergeStores(stores) {
-    let finalStore = {object:{}};
-
-    EventEmitter.decorate(finalStore);
-
-    let setEventForwarders = {};
-
-    for (let key in stores) {
-      let store = stores[key];
-      finalStore.object[key] = store.object;
-      setEventForwarders[key] = _createSetEventForwarder(key, finalStore);
-      store.on("set", setEventForwarders[key]);
-    }
-
-    finalStore.destroy = () => {
-      for (let key in stores) {
-        let store = stores[key];
-        store.off("set", setEventForwarders[key]);
-        if (store.destroy) {
-          store.destroy();
-        }
-      }
-    };
-
-    return finalStore;
-  }
-
-
-  let strings = Services.strings.createBundle("chrome://browser/locale/devtools/app-manager.properties");
-
-  function l10n (property, args = []) {
-    if (args && args.length > 0) {
-      return strings.formatStringFromName(property, args, args.length);
-    } else {
-      return strings.GetStringFromName(property);
-    }
-  }
-
-  return {
-    mergeStores: mergeStores,
-    l10n: l10n
-  }
-})();
deleted file mode 100644
--- a/devtools/client/app-manager/device-store.js
+++ /dev/null
@@ -1,123 +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/. */
-
-const ObservableObject = require("devtools/client/shared/observable-object");
-const {getDeviceFront} = require("devtools/server/actors/device");
-const {Connection} = require("devtools/shared/client/connection-manager");
-
-const {Cu} = require("chrome");
-
-const _knownDeviceStores = new WeakMap();
-
-var DeviceStore;
-
-module.exports = DeviceStore = function(connection) {
-  // If we already know about this connection,
-  // let's re-use the existing store.
-  if (_knownDeviceStores.has(connection)) {
-    return _knownDeviceStores.get(connection);
-  }
-
-  _knownDeviceStores.set(connection, this);
-
-  ObservableObject.call(this, {});
-
-  this._resetStore();
-
-  this.destroy = this.destroy.bind(this);
-  this._onStatusChanged = this._onStatusChanged.bind(this);
-
-  this._connection = connection;
-  this._connection.once(Connection.Events.DESTROYED, this.destroy);
-  this._connection.on(Connection.Events.STATUS_CHANGED, this._onStatusChanged);
-  this._onTabListChanged = this._onTabListChanged.bind(this);
-  this._onStatusChanged();
-  return this;
-};
-
-DeviceStore.prototype = {
-  destroy: function() {
-    if (this._connection) {
-      // While this.destroy is bound using .once() above, that event may not
-      // have occurred when the DeviceStore client calls destroy, so we
-      // manually remove it here.
-      this._connection.off(Connection.Events.DESTROYED, this.destroy);
-      this._connection.off(Connection.Events.STATUS_CHANGED, this._onStatusChanged);
-      _knownDeviceStores.delete(this._connection);
-      this._connection = null;
-    }
-  },
-
-  _resetStore: function() {
-    this.object.description = {};
-    this.object.permissions = [];
-    this.object.tabs = [];
-  },
-
-  _onStatusChanged: function() {
-    if (this._connection.status == Connection.Status.CONNECTED) {
-      // Watch for changes to remote browser tabs
-      this._connection.client.addListener("tabListChanged",
-                                          this._onTabListChanged);
-      this._listTabs();
-    } else {
-      if (this._connection.client) {
-        this._connection.client.removeListener("tabListChanged",
-                                               this._onTabListChanged);
-      }
-      this._resetStore();
-    }
-  },
-
-  _onTabListChanged: function() {
-    this._listTabs();
-  },
-
-  _listTabs: function() {
-    if (!this._connection) {
-      return;
-    }
-    this._connection.client.listTabs((resp) => {
-      if (resp.error) {
-        this._connection.disconnect();
-        return;
-      }
-      this._deviceFront = getDeviceFront(this._connection.client, resp);
-      // Save remote browser's tabs
-      this.object.tabs = resp.tabs;
-      this._feedStore();
-    });
-  },
-
-  _feedStore: function() {
-    this._getDeviceDescription();
-    this._getDevicePermissionsTable();
-  },
-
-  _getDeviceDescription: function() {
-    return this._deviceFront.getDescription()
-    .then(json => {
-      json.dpi = Math.ceil(json.dpi);
-      this.object.description = json;
-    });
-  },
-
-  _getDevicePermissionsTable: function() {
-    return this._deviceFront.getRawPermissionsTable()
-    .then(json => {
-      let permissionsTable = json.rawPermissionsTable;
-      let permissionsArray = [];
-      for (let name in permissionsTable) {
-        permissionsArray.push({
-          name: name,
-          app: permissionsTable[name].app,
-          trusted: permissionsTable[name].trusted,
-          privileged: permissionsTable[name].privileged,
-          certified: permissionsTable[name].certified,
-        });
-      }
-      this.object.permissions = permissionsArray;
-    });
-  }
-};
--- a/devtools/client/app-manager/moz.build
+++ b/devtools/client/app-manager/moz.build
@@ -1,18 +1,12 @@
 # -*- Mode: python; c-basic-offset: 4; 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/.
 
-BROWSER_CHROME_MANIFESTS += ['test/browser.ini']
 MOCHITEST_CHROME_MANIFESTS += ['test/chrome.ini']
 
 DevToolsModules(
     'app-projects.js',
     'app-validator.js',
-    'builtin-adb-store.js',
-    'connection-store.js',
-    'device-store.js',
-    'simulators-store.js',
-    'webapps-store.js',
 )
deleted file mode 100644
--- a/devtools/client/app-manager/simulators-store.js
+++ /dev/null
@@ -1,25 +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/. */
-
-const {Cu} = require("chrome");
-const ObservableObject = require("devtools/client/shared/observable-object");
-const {Simulator} = Cu.import("resource://devtools/shared/apps/Simulator.jsm");
-
-var store = new ObservableObject({versions:[]});
-
-function feedStore() {
-  store.object.versions = Simulator.availableNames().map(name => {
-    let simulator = Simulator.getByName(name);
-    return {
-      version: name,
-      label: simulator ? name : "Unknown"
-    }
-  });
-}
-
-Simulator.on("register", feedStore);
-Simulator.on("unregister", feedStore);
-feedStore();
-
-module.exports = store;
deleted file mode 100644
--- a/devtools/client/app-manager/test/browser.ini
+++ /dev/null
@@ -1,11 +0,0 @@
-[DEFAULT]
-tags = devtools
-skip-if = e10s # Bug ?????? - devtools tests disabled with e10s
-subsuite = devtools
-support-files =
-  head.js
-  hosted_app.manifest
-  manifest.webapp
-
-[browser_manifest_editor.js]
-skip-if = true # Bug 989169 - Very intermittent, but App Manager about to be removed
deleted file mode 100644
--- a/devtools/client/app-manager/test/browser_manifest_editor.js
+++ /dev/null
@@ -1,197 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
-   http://creativecommons.org/publicdomain/zero/1.0/ */
-"use strict";
-
-const {Services} = Cu.import("resource://gre/modules/Services.jsm");
-
-const MANIFEST_EDITOR_ENABLED = "devtools.appmanager.manifestEditor.enabled";
-
-var gManifestWindow, gManifestEditor;
-
-function test() {
-  waitForExplicitFinish();
-
-  Task.spawn(function*() {
-    Services.prefs.setBoolPref(MANIFEST_EDITOR_ENABLED, true);
-    let tab = yield openAppManager();
-    yield selectProjectsPanel();
-    yield addSamplePackagedApp();
-    yield showSampleProjectDetails();
-
-    gManifestWindow = getManifestWindow();
-    gManifestEditor = getProjectsWindow().UI.manifestEditor;
-    yield changeManifestValue("name", "the best app");
-    yield changeManifestValueBad("name", "the worst app");
-    yield addNewManifestProperty("developer", "foo", "bar");
-
-    // add duplicate property in the same parent doesn't create duplicates
-    yield addNewManifestProperty("developer", "foo", "bar2");
-
-    // add propery with same key in other parent is allowed
-    yield addNewManifestProperty("tester", "foo", "new");
-
-    yield addNewManifestPropertyBad("developer", "blob", "bob");
-    yield removeManifestProperty("developer", "foo");
-    gManifestWindow = null;
-    gManifestEditor = null;
-
-    yield removeSamplePackagedApp();
-    yield removeTab(tab);
-    Services.prefs.setBoolPref(MANIFEST_EDITOR_ENABLED, false);
-    finish();
-  });
-}
-
-// Wait until the animation from commitHierarchy has completed
-function waitForUpdate() {
-  return waitForTime(gManifestEditor.editor.lazyEmptyDelay + 1);
-}
-
-function changeManifestValue(key, value) {
-  return Task.spawn(function*() {
-    let propElem = gManifestWindow.document
-                   .querySelector("[id ^= '" + key + "']");
-    is(propElem.querySelector(".name").value, key,
-       "Key doesn't match expected value");
-
-    let valueElem = propElem.querySelector(".value");
-    EventUtils.sendMouseEvent({ type: "mousedown" }, valueElem, gManifestWindow);
-
-    let valueInput = propElem.querySelector(".element-value-input");
-    valueInput.value = '"' + value + '"';
-    EventUtils.sendKey("RETURN", gManifestWindow);
-
-    yield waitForUpdate();
-    // Elements have all been replaced, re-select them
-    propElem = gManifestWindow.document.querySelector("[id ^= '" + key + "']");
-    valueElem = propElem.querySelector(".value");
-    is(valueElem.value, '"' + value + '"',
-       "Value doesn't match expected value");
-
-    is(gManifestEditor.manifest[key], value,
-       "Manifest doesn't contain expected value");
-  });
-}
-
-function changeManifestValueBad(key, value) {
-  return Task.spawn(function*() {
-    let propElem = gManifestWindow.document
-                   .querySelector("[id ^= '" + key + "']");
-    is(propElem.querySelector(".name").value, key,
-       "Key doesn't match expected value");
-
-    let valueElem = propElem.querySelector(".value");
-    EventUtils.sendMouseEvent({ type: "mousedown" }, valueElem, gManifestWindow);
-
-    let valueInput = propElem.querySelector(".element-value-input");
-    // Leaving out quotes will result in an error, so no change should be made.
-    valueInput.value = value;
-    EventUtils.sendKey("RETURN", gManifestWindow);
-
-    yield waitForUpdate();
-    // Elements have all been replaced, re-select them
-    propElem = gManifestWindow.document.querySelector("[id ^= '" + key + "']");
-    valueElem = propElem.querySelector(".value");
-    isnot(valueElem.value, '"' + value + '"',
-       "Value was changed, but it should not have been");
-
-    isnot(gManifestEditor.manifest[key], value,
-       "Manifest was changed, but it should not have been");
-  });
-}
-
-function addNewManifestProperty(parent, key, value) {
-  info("Adding new property - parent: " + parent + "; key: " + key + "; value: " + value + "\n\n");
-  return Task.spawn(function*() {
-    let parentElem = gManifestWindow.document
-                     .querySelector("[id ^= '" + parent + "']");
-    ok(parentElem, "Found parent element: " + parentElem.id);
-
-    let addPropertyElem = parentElem.querySelector(".variables-view-add-property");
-    ok(addPropertyElem, "Found add-property button");
-
-    EventUtils.sendMouseEvent({ type: "mousedown" }, addPropertyElem, gManifestWindow);
-
-    let nameInput = parentElem.querySelector(".element-name-input");
-    nameInput.value = key;
-    EventUtils.sendKey("TAB", gManifestWindow);
-
-    let valueInput = parentElem.querySelector(".element-value-input");
-    valueInput.value = '"' + value + '"';
-    EventUtils.sendKey("RETURN", gManifestWindow);
-
-    yield waitForUpdate();
-
-    parentElem = gManifestWindow.document.querySelector("[id ^= '" + parent + "']");
-    let elems = parentElem.querySelectorAll("[id ^= '" + key + "']");
-    is(elems.length, 1, "No duplicate property is added");
-
-    let newElem = elems[0];
-    let nameElem = newElem.querySelector(".name");
-    is(nameElem.value, key, "Key doesn't match expected Key");
-
-    ok(key in gManifestEditor.manifest[parent],
-       "Manifest doesn't contain expected key");
-
-    let valueElem = newElem.querySelector(".value");
-    is(valueElem.value, '"' + value + '"',
-       "Value doesn't match expected value");
-
-    is(gManifestEditor.manifest[parent][key], value,
-       "Manifest doesn't contain expected value");
-  });
-}
-
-function addNewManifestPropertyBad(parent, key, value) {
-  return Task.spawn(function*() {
-    let parentElem = gManifestWindow.document
-                     .querySelector("[id ^= '" + parent + "']");
-    ok(parentElem,
-      "Found parent element");
-    let addPropertyElem = parentElem
-                          .querySelector(".variables-view-add-property");
-    ok(addPropertyElem,
-      "Found add-property button");
-
-    EventUtils.sendMouseEvent({ type: "mousedown" }, addPropertyElem, gManifestWindow);
-
-    let nameInput = parentElem.querySelector(".element-name-input");
-    nameInput.value = key;
-    EventUtils.sendKey("TAB", gManifestWindow);
-
-    let valueInput = parentElem.querySelector(".element-value-input");
-    // Leaving out quotes will result in an error, so no change should be made.
-    valueInput.value = value;
-    EventUtils.sendKey("RETURN", gManifestWindow);
-
-    yield waitForUpdate();
-
-    let newElem = gManifestWindow.document.querySelector("[id ^= '" + key + "']");
-    ok(!newElem, "Key was added, but it should not have been");
-    ok(!(key in gManifestEditor.manifest[parent]),
-       "Manifest contains key, but it should not");
-  });
-}
-
-function removeManifestProperty(parent, key) {
-  info("*** Remove property test ***");
-
-  return Task.spawn(function*() {
-    let parentElem = gManifestWindow.document
-                     .querySelector("[id ^= '" + parent + "']");
-    ok(parentElem, "Found parent element");
-
-    let keyExists = key in gManifestEditor.manifest[parent];
-    ok(keyExists,
-       "The manifest contains the key under the expected parent");
-
-    let newElem = gManifestWindow.document.querySelector("[id ^= '" + key + "']");
-    let removePropertyButton = newElem.querySelector(".variables-view-delete");
-    ok(removePropertyButton, "The remove property button was found");
-    removePropertyButton.click();
-
-    yield waitForUpdate();
-
-    ok(!(key in gManifestEditor.manifest[parent]), "Property was successfully removed");
-  });
-}
--- a/devtools/client/app-manager/test/chrome.ini
+++ b/devtools/client/app-manager/test/chrome.ini
@@ -1,13 +1,7 @@
 [DEFAULT]
 tags = devtools
 skip-if = buildapp == 'b2g'
 support-files =
-  hosted_app.manifest
   validator/*
 
-[test_connection_store.html]
-[test_device_store.html]
-[test_projects_store.html]
-[test_remain_connected.html]
-[test_template.html]
 [test_app_validator.html]
deleted file mode 100644
--- a/devtools/client/app-manager/test/head.js
+++ /dev/null
@@ -1,175 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
-   http://creativecommons.org/publicdomain/zero/1.0/ */
-"use strict";
-
-var {utils: Cu, classes: Cc, interfaces: Ci} = Components;
-
-const {Promise: promise} =
-  Cu.import("resource://devtools/shared/deprecated-sync-thenables.js", {});
-const {require} =
-  Cu.import("resource://devtools/shared/Loader.jsm", {});
-
-const {AppProjects} = require("devtools/client/app-manager/app-projects");
-const DevToolsUtils = require("devtools/shared/DevToolsUtils");
-
-const APP_MANAGER_URL = "about:app-manager";
-const TEST_BASE =
-  "chrome://mochitests/content/browser/devtools/client/app-manager/test/";
-const HOSTED_APP_MANIFEST = TEST_BASE + "hosted_app.manifest";
-
-const PACKAGED_APP_DIR_PATH = getTestFilePath(".");
-
-DevToolsUtils.testing = true;
-SimpleTest.registerCleanupFunction(() => {
-  DevToolsUtils.testing = false;
-});
-
-function addTab(url, targetWindow = window) {
-  info("Adding tab: " + url);
-
-  let deferred = promise.defer();
-  let targetBrowser = targetWindow.gBrowser;
-
-  targetWindow.focus();
-  let tab = targetBrowser.selectedTab = targetBrowser.addTab(url);
-  let linkedBrowser = tab.linkedBrowser;
-
-  linkedBrowser.addEventListener("load", function onLoad() {
-    linkedBrowser.removeEventListener("load", onLoad, true);
-    info("Tab added and finished loading: " + url);
-    deferred.resolve(tab);
-  }, true);
-
-  return deferred.promise;
-}
-
-function removeTab(tab, targetWindow = window) {
-  info("Removing tab.");
-
-  let deferred = promise.defer();
-  let targetBrowser = targetWindow.gBrowser;
-  let tabContainer = targetBrowser.tabContainer;
-
-  tabContainer.addEventListener("TabClose", function onClose(aEvent) {
-    tabContainer.removeEventListener("TabClose", onClose, false);
-    info("Tab removed and finished closing.");
-    deferred.resolve();
-  }, false);
-
-  targetBrowser.removeTab(tab);
-
-  return deferred.promise;
-}
-
-function openAppManager() {
-  return addTab(APP_MANAGER_URL);
-}
-
-function addSampleHostedApp() {
-  info("Adding sample hosted app");
-  let projectsWindow = getProjectsWindow();
-  let projectsDocument = projectsWindow.document;
-  let url = projectsDocument.querySelector("#url-input");
-  url.value = HOSTED_APP_MANIFEST;
-  return projectsWindow.UI.addHosted();
-}
-
-function removeSampleHostedApp() {
-  info("Removing sample hosted app");
-  return AppProjects.remove(HOSTED_APP_MANIFEST);
-}
-
-function addSamplePackagedApp() {
-  info("Adding sample packaged app");
-  let appDir = Cc['@mozilla.org/file/local;1'].createInstance(Ci.nsIFile);
-  appDir.initWithPath(PACKAGED_APP_DIR_PATH);
-  return getProjectsWindow().UI.addPackaged(appDir);
-}
-
-function removeSamplePackagedApp() {
-  info("Removing sample packaged app");
-  return AppProjects.remove(PACKAGED_APP_DIR_PATH);
-}
-
-function getProjectsWindow() {
-  return content.document.querySelector(".projects-panel").contentWindow;
-}
-
-function getManifestWindow() {
-  return getProjectsWindow().document.querySelector(".variables-view")
-         .contentWindow;
-}
-
-function waitForProjectsPanel(deferred = promise.defer()) {
-  info("Wait for projects panel");
-
-  let projectsWindow = getProjectsWindow();
-  let projectsUI = projectsWindow.UI;
-  if (!projectsUI) {
-    info("projectsUI false");
-    projectsWindow.addEventListener("load", function onLoad() {
-      info("got load event");
-      projectsWindow.removeEventListener("load", onLoad);
-      waitForProjectsPanel(deferred);
-    });
-    return deferred.promise;
-  }
-
-  if (projectsUI.isReady) {
-    info("projectsUI ready");
-    deferred.resolve();
-    return deferred.promise;
-  }
-
-  info("projectsUI not ready");
-  projectsUI.once("ready", deferred.resolve);
-  return deferred.promise;
-}
-
-function selectProjectsPanel() {
-  return Task.spawn(function*() {
-    let projectsButton = content.document.querySelector(".projects-button");
-    EventUtils.sendMouseEvent({ type: "click" }, projectsButton, content);
-
-    yield waitForProjectsPanel();
-  });
-}
-
-function waitForProjectSelection() {
-  info("Wait for project selection");
-
-  let deferred = promise.defer();
-  getProjectsWindow().UI.once("project-selected", deferred.resolve);
-  return deferred.promise;
-}
-
-function selectFirstProject() {
-  return Task.spawn(function*() {
-    let projectsFrame = content.document.querySelector(".projects-panel");
-    let projectsWindow = projectsFrame.contentWindow;
-    let projectsDoc = projectsWindow.document;
-    let projectItem = projectsDoc.querySelector(".project-item");
-    EventUtils.sendMouseEvent({ type: "click" }, projectItem, projectsWindow);
-
-    yield waitForProjectSelection();
-  });
-}
-
-function showSampleProjectDetails() {
-  return Task.spawn(function*() {
-    yield selectProjectsPanel();
-    yield selectFirstProject();
-  });
-}
-
-function waitForTick() {
-  let deferred = promise.defer();
-  executeSoon(deferred.resolve);
-  return deferred.promise;
-}
-
-function waitForTime(aDelay) {
-  let deferred = promise.defer();
-  setTimeout(deferred.resolve, aDelay);
-  return deferred.promise;
-}
deleted file mode 100644
--- a/devtools/client/app-manager/test/hosted_app.manifest
+++ /dev/null
@@ -1,3 +0,0 @@
-{
-  "name": "My hosted app"
-}
deleted file mode 100644
--- a/devtools/client/app-manager/test/manifest.webapp
+++ /dev/null
@@ -1,9 +0,0 @@
-{
-  "name": "My packaged app",
-  "developer": {
-    "name": "Foo Bar"
-  },
-  "tester" : {
-    "who": "qa"
-  }
-}
deleted file mode 100644
--- a/devtools/client/app-manager/test/test_connection_store.html
+++ /dev/null
@@ -1,107 +0,0 @@
-<!DOCTYPE html>
-
-<!--
-Bug 901519 - [app manager] data store for connections
--->
-
-<html>
-
-  <head>
-    <meta charset="utf8">
-    <title></title>
-
-    <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
-    <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
-  </head>
-
-  <body>
-
-    <div id="root">
-      <span id="status" template='{"type":"textContent","path":"status"}'></span>
-      <span id="host" template='{"type":"textContent","path":"host"}'></span>
-      <span id="port" template='{"type":"textContent","path":"port"}'></span>
-    </div>
-
-    <script type="application/javascript;version=1.8" src="chrome://devtools/content/app-manager/content/template.js"></script>
-    <script type="application/javascript;version=1.8">
-      const Cu = Components.utils;
-      const {require} = Cu.import("resource://devtools/shared/Loader.jsm");
-      const {DebuggerServer} = require("devtools/server/main");
-
-      if (!DebuggerServer.initialized) {
-        DebuggerServer.init();
-        DebuggerServer.addBrowserActors();
-      }
-
-      window.onload = function() {
-        SimpleTest.waitForExplicitFinish();
-
-        Cu.import("resource://gre/modules/Services.jsm");
-        Cu.import("resource://devtools/client/framework/gDevTools.jsm");
-
-        const {ConnectionManager} = require("devtools/shared/client/connection-manager");
-        const ConnectionStore = require("devtools/client/app-manager/connection-store");
-
-        let connection = ConnectionManager.createConnection();
-        let store = new ConnectionStore(connection);
-
-        let root = document.querySelector("#root");
-        let status = root.querySelector("#status");
-        let host = root.querySelector("#host");
-        let port = root.querySelector("#port");
-        let template = new Template(root, store, () => {});
-        template.start();
-
-        connection.host = "foobar";
-        connection.port = 42;
-
-        is(host.textContent, "foobar", "host updated");
-        is(port.textContent, "42", "port updated");
-
-        let been_through_connecting = false;
-        let been_through_connected = false;
-        let been_through_disconnected = false;
-
-        is(status.textContent, "disconnected", "status updated (diconnected)");
-
-        connection.once("connecting", (e) => {
-          SimpleTest.executeSoon(() => {
-            been_through_connecting = true;
-            is(status.textContent, "connecting", "status updated (connecting)");
-          })
-        });
-
-        connection.once("connected", (e) => {
-          SimpleTest.executeSoon(() => {
-            been_through_connected = true;
-            is(status.textContent, "connected", "status updated (connected)");
-            connection.disconnect();
-          })
-        });
-
-        connection.once("disconnected", (e) => {
-          SimpleTest.executeSoon(() => {
-            been_through_disconnected = true;
-            is(status.textContent, "disconnected", "status updated (disconnected)");
-            connection.destroy();
-            finishup();
-          })
-        });
-
-        function finishup() {
-          ok(been_through_connecting &&
-            been_through_connected &&
-            been_through_disconnected, "All updates happened");
-          DebuggerServer.destroy();
-          SimpleTest.finish();
-        }
-
-        connection.host = null; // force pipe
-        connection.port = null;
-
-        connection.connect();
-      }
-
-    </script>
-  </body>
-</html>
deleted file mode 100644
--- a/devtools/client/app-manager/test/test_device_store.html
+++ /dev/null
@@ -1,84 +0,0 @@
-<!DOCTYPE html>
-
-<!--
-Bug 901520 - [app manager] data store for device
--->
-
-<html>
-
-  <head>
-    <meta charset="utf8">
-    <title></title>
-
-    <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
-    <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
-  </head>
-
-  <body>
-
-    <script type="application/javascript;version=1.8" src="chrome://devtools/content/app-manager/content/template.js"></script>
-    <script type="application/javascript;version=1.8">
-      const Cu = Components.utils;
-      const {require} = Cu.import("resource://devtools/shared/Loader.jsm");
-      const {DebuggerServer} = require("devtools/server/main");
-
-      if (!DebuggerServer.initialized) {
-        DebuggerServer.init();
-        DebuggerServer.addBrowserActors();
-      }
-
-      function compare(o1, o2, msg) {
-        is(JSON.stringify(o1), JSON.stringify(o2), msg);
-      }
-
-      window.onload = function() {
-        SimpleTest.waitForExplicitFinish();
-
-        Cu.import("resource://gre/modules/Services.jsm");
-        Cu.import("resource://devtools/client/framework/gDevTools.jsm");
-
-        const {ConnectionManager} = require("devtools/shared/client/connection-manager");
-        const DeviceStore = require("devtools/client/app-manager/device-store");
-
-        let {getDeviceFront} = require("devtools/server/actors/device");
-
-        let connection = ConnectionManager.createConnection();
-        let store = new DeviceStore(connection);
-
-        connection.once("connected", function() {
-          store.on("set", function check(event, path, value) {
-            if (path.join(".") != "description") return;
-            store.off("set", check);
-            info("Connected");
-            connection.client.listTabs((resp) => {
-              info("List tabs response");
-              let deviceFront = getDeviceFront(connection.client, resp);
-              deviceFront.getDescription().then(json => {
-                info("getDescription response: " + JSON.stringify(json));
-                json.dpi = Math.ceil(json.dpi);
-                for (let key in json) {
-                  compare(json[key], store.object.description[key], "description." + key + " is valid");
-                  compare(json[key], value[key], "description." + key + " is valid");
-                }
-                connection.disconnect();
-              }).then(null, (error) => ok(false, "Error:" + error));
-            });
-          });
-        });
-
-        connection.once("disconnected", function() {
-          compare(store.object, {description:{},permissions:[],tabs:[]}, "empty store after disconnect")
-          connection.destroy();
-          DebuggerServer.destroy();
-          SimpleTest.finish();
-        });
-
-        compare(store.object, {description:{},permissions:[],tabs:[]}, "empty store before disconnect")
-
-        connection.connect();
-
-      }
-
-    </script>
-  </body>
-</html>
deleted file mode 100644
--- a/devtools/client/app-manager/test/test_projects_store.html
+++ /dev/null
@@ -1,59 +0,0 @@
-<!DOCTYPE html>
-
-<!--
-Bug 907206 - data store for local apps 
--->
-
-<html>
-
-  <head>
-    <meta charset="utf8">
-    <title></title>
-
-    <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
-    <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
-  </head>
-
-  <body>
-
-    <script type="application/javascript;version=1.8">
-      const Cu = Components.utils;
-
-      window.onload = function() {
-        SimpleTest.waitForExplicitFinish();
-
-        const { require } = Cu.import("resource://devtools/shared/Loader.jsm", {});
-
-        const { AppProjects } = require("devtools/client/app-manager/app-projects");
-
-        function testHosted(projects) {
-          let manifestURL = document.location.href.replace("test_projects_store.html", "hosted_app/webapp.manifest");
-          AppProjects.addHosted(manifestURL)
-            .then(function (app) {
-              is(projects.length, 1,
-                 "Hosted app has been added");
-              is(projects[0], app);
-              is(app.type, "hosted", "valid type");
-              is(app.location, manifestURL, "valid location");
-              is(AppProjects.get(manifestURL), app,
-                 "get() returns the same app object");
-              AppProjects.remove(manifestURL)
-                .then(function () {
-                  is(projects.length, 0,
-                     "Hosted app has been removed");
-                  SimpleTest.finish();
-                });
-            });
-        }
-
-        AppProjects.once("ready", function (event, projects) {
-          is(projects, AppProjects.store.object.projects,
-             "The ready event data is the store projects list");
-          testHosted(projects);
-        });
-
-      }
-
-    </script>
-  </body>
-</html>
deleted file mode 100644
--- a/devtools/client/app-manager/test/test_remain_connected.html
+++ /dev/null
@@ -1,121 +0,0 @@
-<!DOCTYPE html>
-
-<!--
-Bug 912646 - Closing app toolbox causes phone to disconnect
--->
-
-<html>
-
-  <head>
-    <meta charset="utf8">
-    <title></title>
-
-    <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
-    <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
-  </head>
-
-  <body>
-
-    <script type="application/javascript;version=1.8">
-      const Cu = Components.utils;
-      const {require} = Cu.import("resource://devtools/shared/Loader.jsm");
-      const {DebuggerServer} = require("devtools/server/main");
-
-      if (!DebuggerServer.initialized) {
-        DebuggerServer.init();
-        DebuggerServer.addBrowserActors();
-      }
-
-      window.onload = function() {
-        SimpleTest.waitForExplicitFinish();
-
-        Cu.import("resource://devtools/client/framework/gDevTools.jsm");
-
-        const {TargetFactory} = require("devtools/client/framework/target");
-        const {Toolbox} = require("devtools/client/framework/toolbox");
-
-        const {Connection, ConnectionManager} =
-          require("devtools/shared/client/connection-manager");
-        const ConnectionStore =
-          require("devtools/client/app-manager/connection-store");
-
-        let connection = ConnectionManager.createConnection();
-
-        connection.host = null; // force pipe
-        connection.port = null;
-
-        let been_through_connecting = false;
-        let been_through_connected = false;
-        let been_through_disconnected = false;
-
-        is(connection.status, Connection.Status.DISCONNECTED,
-           "status updated (diconnected)");
-
-        connection.once("connecting", () => {
-          SimpleTest.executeSoon(() => {
-            been_through_connecting = true;
-            is(connection.status, Connection.Status.CONNECTING,
-               "status updated (connecting)");
-          })
-        });
-
-        connection.once("connected", () => {
-          SimpleTest.executeSoon(() => {
-            been_through_connected = true;
-            is(connection.status, Connection.Status.CONNECTED,
-               "status updated (connected)");
-            cycleToolbox();
-          })
-        });
-
-        function cycleToolbox() {
-          connection.client.listTabs(response => {
-            let options = {
-              form: response.tabs[0],
-              client: connection.client,
-              chrome: true
-            };
-            TargetFactory.forRemoteTab(options).then(target => {
-              let hostType = Toolbox.HostType.WINDOW;
-              gDevTools.showToolbox(target,
-                                    null,
-                                    hostType).then(toolbox => {
-                SimpleTest.executeSoon(() => {
-                  toolbox.once("destroyed", onDestroyToolbox);
-                  toolbox.destroy();
-                });
-              });
-            });
-          });
-        }
-
-        function onDestroyToolbox() {
-          is(connection.status, Connection.Status.CONNECTED,
-             "toolbox cycled, still connected");
-          connection.disconnect();
-        }
-
-        connection.once("disconnected", () => {
-          SimpleTest.executeSoon(() => {
-            been_through_disconnected = true;
-            is(connection.status, Connection.Status.DISCONNECTED,
-               "status updated (disconnected)");
-            connection.destroy();
-            finishUp();
-          })
-        });
-
-        function finishUp() {
-          ok(been_through_connecting &&
-             been_through_connected &&
-             been_through_disconnected, "All updates happened");
-          DebuggerServer.destroy();
-          SimpleTest.finish();
-        }
-
-        connection.connect();
-      }
-
-    </script>
-  </body>
-</html>
deleted file mode 100644
--- a/devtools/client/app-manager/test/test_template.html
+++ /dev/null
@@ -1,255 +0,0 @@
-<!DOCTYPE html>
-
-<html>
-
-  <head>
-    <meta charset="utf8">
-    <title></title>
-
-    <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
-    <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
-  </head>
-
-  <div id="root">
-    <span template='{"type":"textContent","path":"title"}'></span>
-    <span template='{"type":"attribute","name":"title","path":"title"}'></span>
-    <span template='{"type":"localizedContent","paths":["foo2.foo_l10n","foo2.bar_l10n"],"property":"foo2"}'></span>
-    <div template-for='{"path":"mop","childSelector":"#template-for"}'></div>
-    <div template-loop='{"arrayPath":"foo1.bar1","childSelector":"#template-loop"}'></div>
-  </div>
-
-  <div id="ref0">
-    <span template='{"type":"textContent","path":"title"}'>ttt</span>
-    <span title="ttt" template='{"type":"attribute","name":"title","path":"title"}'></span>
-    <span template='{"type":"localizedContent","paths":["foo2.foo_l10n","foo2.bar_l10n"],"property":"foo2"}'>foo2:foo_l10n/bar_l10n</span>
-    <div template-for='{"path":"mop","childSelector":"#template-for"}'><span template='{"type":"textContent","path":"name","rootPath":"mop"}'>meh</span></div>
-    <div template-loop='{"arrayPath":"foo1.bar1","childSelector":"#template-loop"}'>
-      <div>
-        <span template='{"type":"textContent","path":"idx","rootPath":"foo1.bar1.0"}'>xx0</span>
-        <span template='{"type":"textContent","path":"a","rootPath":"foo1.bar1.0"}'>a</span>
-        <span template='{"type":"textContent","path":"b","rootPath":"foo1.bar1.0"}'>b</span>
-      </div>
-      <div>
-        <span template='{"type":"textContent","path":"idx","rootPath":"foo1.bar1.1"}'>xx1</span>
-        <span template='{"type":"textContent","path":"a","rootPath":"foo1.bar1.1"}'>a</span>
-        <span template='{"type":"textContent","path":"b","rootPath":"foo1.bar1.1"}'>b</span>
-      </div>
-    </div>
-  </div>
-
-
-  <div id="ref1">
-    <span template='{"type":"textContent","path":"title"}'>xxx</span>
-    <span title="xxx" template='{"type":"attribute","name":"title","path":"title"}'></span>
-    <span template='{"type":"localizedContent","paths":["foo2.foo_l10n","foo2.bar_l10n"],"property":"foo2"}'>foo2:foo2_l10n/bar_l10n</span>
-    <div template-for='{"path":"mop","childSelector":"#template-for"}'><span template='{"type":"textContent","path":"name","rootPath":"mop"}'>meh2</span></div>
-    <div template-loop='{"arrayPath":"foo1.bar1","childSelector":"#template-loop"}'>
-      <div>
-        <span template='{"type":"textContent","path":"idx","rootPath":"foo1.bar1.0"}'>xx0</span>
-        <span template='{"type":"textContent","path":"a","rootPath":"foo1.bar1.0"}'>a</span>
-        <span template='{"type":"textContent","path":"b","rootPath":"foo1.bar1.0"}'>b</span>
-      </div>
-      <div>
-        <span template='{"type":"textContent","path":"idx","rootPath":"foo1.bar1.1"}'>xx1</span>
-        <span template='{"type":"textContent","path":"a","rootPath":"foo1.bar1.1"}'>a</span>
-        <span template='{"type":"textContent","path":"b","rootPath":"foo1.bar1.1"}'>b</span>
-      </div>
-    </div>
-  </div>
-
-  <div id="ref2">
-    <span template='{"type":"textContent","path":"title"}'>xxx</span>
-    <span title="xxx" template='{"type":"attribute","name":"title","path":"title"}'></span>
-    <span template='{"type":"localizedContent","paths":["foo2.foo_l10n","foo2.bar_l10n"],"property":"foo2"}'>foo2:yyy/zzz</span>
-    <div template-for='{"path":"","childSelector":"#template-for"}'></div>
-    <div template-loop='{"arrayPath":"foo1.bar1","childSelector":"#template-loop"}'>
-      <div>
-        <span template='{"type":"textContent","path":"idx","rootPath":"foo1.bar1.0"}'>xx0</span>
-        <span template='{"type":"textContent","path":"a","rootPath":"foo1.bar1.0"}'>a</span>
-        <span template='{"type":"textContent","path":"b","rootPath":"foo1.bar1.0"}'>b</span>
-      </div>
-      <div>
-        <span template='{"type":"textContent","path":"idx","rootPath":"foo1.bar1.1"}'>xx1</span>
-        <span template='{"type":"textContent","path":"a","rootPath":"foo1.bar1.1"}'>a</span>
-        <span template='{"type":"textContent","path":"b","rootPath":"foo1.bar1.1"}'>b</span>
-      </div>
-    </div>
-  </div>
-
-  <div id="ref3">
-    <span template='{"type":"textContent","path":"title"}'>xxx</span>
-    <span title="xxx" template='{"type":"attribute","name":"title","path":"title"}'></span>
-    <span template='{"type":"localizedContent","paths":["foo2.foo_l10n","foo2.bar_l10n"],"property":"foo2"}'>foo2:yyy/zzz</span>
-    <div template-for='{"path":"","childSelector":"#template-for"}'></div>
-    <div template-loop='{"arrayPath":"foo1.bar1","childSelector":"#template-loop"}'>
-      <div>
-        <span template='{"type":"textContent","path":"idx","rootPath":"foo1.bar1.0"}'>xx0</span>
-        <span template='{"type":"textContent","path":"a","rootPath":"foo1.bar1.0"}'>a</span>
-        <span template='{"type":"textContent","path":"b","rootPath":"foo1.bar1.0"}'>b</span>
-      </div>
-      <div>
-        <span template='{"type":"textContent","path":"idx","rootPath":"foo1.bar1.1"}'>xx1</span>
-        <span template='{"type":"textContent","path":"a","rootPath":"foo1.bar1.1"}'>a</span>
-        <span template='{"type":"textContent","path":"b","rootPath":"foo1.bar1.1"}'>b</span>
-      </div>
-      <div>
-        <span template='{"type":"textContent","path":"idx","rootPath":"foo1.bar1.2"}'>xx2</span>
-        <span template='{"type":"textContent","path":"a","rootPath":"foo1.bar1.2"}'>a</span>
-        <span template='{"type":"textContent","path":"b","rootPath":"foo1.bar1.2"}'>b</span>
-      </div>
-      <div>
-        <span template='{"type":"textContent","path":"idx","rootPath":"foo1.bar1.3"}'>xx3</span>
-        <span template='{"type":"textContent","path":"a","rootPath":"foo1.bar1.3"}'>a</span>
-        <span template='{"type":"textContent","path":"b","rootPath":"foo1.bar1.3"}'>b</span>
-      </div>
-      <div>
-        <span template='{"type":"textContent","path":"idx","rootPath":"foo1.bar1.4"}'>xx4</span>
-        <span template='{"type":"textContent","path":"a","rootPath":"foo1.bar1.4"}'>a</span>
-        <span template='{"type":"textContent","path":"b","rootPath":"foo1.bar1.4"}'>b</span>
-      </div>
-    </div>
-  </div>
-
-  <div id="ref4">
-    <span template='{"type":"textContent","path":"title"}'>xxx</span>
-    <span title="xxx" template='{"type":"attribute","name":"title","path":"title"}'></span>
-    <span template='{"type":"localizedContent","paths":["foo2.foo_l10n","foo2.bar_l10n"],"property":"foo2"}'>foo2:yyy/zzz</span>
-    <div template-for='{"path":"","childSelector":"#template-for"}'></div>
-    <div template-loop='{"arrayPath":"foo1.bar1","childSelector":"#template-loop"}'>
-      <div>
-        <span template='{"type":"textContent","path":"idx","rootPath":"foo1.bar1.0"}'>xx0</span>
-        <span template='{"type":"textContent","path":"a","rootPath":"foo1.bar1.0"}'>a</span>
-        <span template='{"type":"textContent","path":"b","rootPath":"foo1.bar1.0"}'>b</span>
-      </div>
-      <div>
-        <span template='{"type":"textContent","path":"idx","rootPath":"foo1.bar1.1"}'>xx1</span>
-        <span template='{"type":"textContent","path":"a","rootPath":"foo1.bar1.1"}'>a</span>
-        <span template='{"type":"textContent","path":"b","rootPath":"foo1.bar1.1"}'>b</span>
-      </div>
-      <div>
-        <span template='{"type":"textContent","path":"idx","rootPath":"foo1.bar1.2"}'>xx2</span>
-        <span template='{"type":"textContent","path":"a","rootPath":"foo1.bar1.2"}'>a</span>
-        <span template='{"type":"textContent","path":"b","rootPath":"foo1.bar1.2"}'>b</span>
-      </div>
-      <div>
-        <span template='{"type":"textContent","path":"idx","rootPath":"foo1.bar1.3"}'>xx3</span>
-        <span template='{"type":"textContent","path":"a","rootPath":"foo1.bar1.3"}'>a</span>
-        <span template='{"type":"textContent","path":"b","rootPath":"foo1.bar1.3"}'>b</span>
-      </div>
-    </div>
-  </div>
-
-
-
-  <template id="template-loop">
-    <div>
-      <span template='{"type":"textContent","path":"idx"}'></span>
-      <span template='{"type":"textContent","path":"a"}'></span>
-      <span template='{"type":"textContent","path":"b"}'></span>
-    </div>
-  </template>
-
-  <template id="template-for">
-  <span template='{"type":"textContent","path":"name"}'></span>
-  </template>
-
-  <script type="application/javascript;version=1.8" src="chrome://devtools/content/app-manager/content/template.js"></script>
-  <script type="application/javascript;version=1.8">
-    SimpleTest.waitForExplicitFinish();
-
-    const Cu = Components.utils;
-    Cu.import("resource://devtools/client/framework/gDevTools.jsm");
-    const {require} = Cu.import("resource://devtools/shared/Loader.jsm", {});
-    const ObservableObject = require("devtools/client/shared/observable-object");
-
-    let data = {
-      title: "ttt",
-      mop: {
-        name: "meh",
-      },
-      foo1: {
-        bar1: [
-          {idx: "xx0", a: "a", b: "b"},
-          {idx: "xx1", a: "a", b: "b"},
-        ],
-      },
-      foo2: {
-        foo_l10n: "foo_l10n",
-        bar_l10n: "bar_l10n"
-      },
-    };
-
-    let store = new ObservableObject(data);
-
-    let changes = [
-      {
-        exec: function() {},
-        reference: document.querySelector("#ref0")
-      },
-      {
-        exec: function() {
-          store.object.title = "xxx";
-          store.object.foo2.foo_l10n = "foo2_l10n";
-          store.object.mop.name = "meh2";
-        },
-        reference: document.querySelector("#ref1")
-      },
-      {
-        exec: function() {
-          store.object.foo2 = {
-            foo_l10n: "yyy",
-            bar_l10n: "zzz",
-          }
-          let forElt = document.querySelector("#root > [template-for]");
-          forElt.setAttribute("template-for", '{"path":"","childSelector":"#template-for"}');
-          t._processFor(forElt);
-        },
-        reference: document.querySelector("#ref2")
-      },
-      {
-        exec: function() {
-          let items = [];
-          for (let i = 2; i < 5; i++) {
-            items.push({idx: "xx" + i, a: "a", b: "b"});
-          }
-
-          store.object.foo1.bar1 = store.object.foo1.bar1.concat(items);
-        },
-        reference: document.querySelector("#ref3")
-      },
-      {
-        exec: function() {
-          store.object.foo1.bar1.pop();
-        },
-        reference: document.querySelector("#ref4")
-      },
-    ];
-
-    function compare(node1, node2) {
-      let text1 = node1.innerHTML;
-      let text2 = node2.innerHTML;
-      text1 = text1.replace(/\n/g,"");
-      text2 = text2.replace(/\n/g,"");
-      text1 = text1.replace(/\s+/g,"");
-      text2 = text2.replace(/\s+/g,"");
-      return text1 == text2;
-    }
-
-
-    let root = document.querySelector("#root");
-
-    let t = new Template(root, store, (prop, args) => {
-      return prop + ":" + args.join("/");
-    });
-
-    t.start();
-
-    for (let i = 0; i < changes.length; i++) {
-      let change = changes[i];
-      change.exec();
-      ok(compare(change.reference, root), "Content " + i + " looks good.");
-    }
-  SimpleTest.finish();
-
-  </script>
-</html>
deleted file mode 100644
--- a/devtools/client/app-manager/webapps-store.js
+++ /dev/null
@@ -1,283 +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/. */
-
-const ObservableObject = require("devtools/client/shared/observable-object");
-const promise = require("devtools/shared/deprecated-sync-thenables");
-const {Connection} = require("devtools/shared/client/connection-manager");
-
-const {Cu} = require("chrome");
-const _knownWebappsStores = new WeakMap();
-
-var WebappsStore;
-
-module.exports = WebappsStore = function(connection) {
-  // If we already know about this connection,
-  // let's re-use the existing store.
-  if (_knownWebappsStores.has(connection)) {
-    return _knownWebappsStores.get(connection);
-  }
-
-  _knownWebappsStores.set(connection, this);
-
-  ObservableObject.call(this, {});
-
-  this._resetStore();
-
-  this.destroy = this.destroy.bind(this);
-  this._onStatusChanged = this._onStatusChanged.bind(this);
-
-  this._connection = connection;
-  this._connection.once(Connection.Events.DESTROYED, this.destroy);
-  this._connection.on(Connection.Events.STATUS_CHANGED, this._onStatusChanged);
-  this._onStatusChanged();
-  return this;
-}
-
-WebappsStore.prototype = {
-  destroy: function() {
-    if (this._connection) {
-      // While this.destroy is bound using .once() above, that event may not
-      // have occurred when the WebappsStore client calls destroy, so we
-      // manually remove it here.
-      this._connection.off(Connection.Events.DESTROYED, this.destroy);
-      this._connection.off(Connection.Events.STATUS_CHANGED, this._onStatusChanged);
-      _knownWebappsStores.delete(this._connection);
-      this._connection = null;
-    }
-  },
-
-  _resetStore: function() {
-    this.object.all = []; // list of app objects
-    this.object.running = []; // list of manifests
-  },
-
-  _getAppFromManifest: function(manifest) {
-    for (let app of this.object.all) {
-      if (app.manifestURL == manifest) {
-        return app;
-      }
-    }
-    return null;
-  },
-
-  _onStatusChanged: function() {
-    if (this._connection.status == Connection.Status.CONNECTED) {
-      this._listTabs();
-    } else {
-      this._resetStore();
-    }
-  },
-
-  _listTabs: function() {
-    this._connection.client.listTabs((resp) => {
-      this._webAppsActor = resp.webappsActor;
-      this._feedStore().then(() => {
-        this.emit("store-ready");
-      });
-    });
-  },
-
-  _feedStore: function() {
-    if (!this._webAppsActor) {
-      return promise.resolve();
-    }
-    this._listenToApps();
-    return this._getAllApps()
-               .then(this._getRunningApps.bind(this))
-               .then(this._getAppsIcons.bind(this));
-  },
-
-  _listenToApps: function() {
-    let deferred = promise.defer();
-    let client = this._connection.client;
-
-    let request = {
-      to: this._webAppsActor,
-      type: "watchApps"
-    };
-
-    client.request(request, (res) => {
-      if (res.error) {
-        return deferred.reject(res.error);
-      }
-
-      client.addListener("appOpen", (type, { manifestURL }) => {
-        this._onAppOpen(manifestURL);
-      });
-
-      client.addListener("appClose", (type, { manifestURL }) => {
-        this._onAppClose(manifestURL);
-      });
-
-      client.addListener("appInstall", (type, { manifestURL }) => {
-        this._onAppInstall(manifestURL);
-      });
-
-      client.addListener("appUninstall", (type, { manifestURL }) => {
-        this._onAppUninstall(manifestURL);
-      });
-
-      return deferred.resolve();
-    })
-    return deferred.promise;
-  },
-
-  _getAllApps: function() {
-    let deferred = promise.defer();
-    let request = {
-      to: this._webAppsActor,
-      type: "getAll"
-    };
-
-    this._connection.client.request(request, (res) => {
-      if (res.error) {
-        return deferred.reject(res.error);
-      }
-      let apps = res.apps;
-      for (let a of apps) {
-        a.running = false;
-      }
-      this.object.all = apps;
-      return deferred.resolve();
-    });
-    return deferred.promise;
-  },
-
-  _getRunningApps: function() {
-    let deferred = promise.defer();
-    let request = {
-      to: this._webAppsActor,
-      type: "listRunningApps"
-    };
-
-    this._connection.client.request(request, (res) => {
-      if (res.error) {
-        return deferred.reject(res.error);
-      }
-
-      let manifests = res.apps;
-      this.object.running = manifests;
-
-      for (let m of manifests) {
-        let a = this._getAppFromManifest(m);
-        if (a) {
-          a.running = true;
-        } else {
-          return deferred.reject("Unexpected manifest: " + m);
-        }
-      }
-
-      return deferred.resolve();
-    });
-    return deferred.promise;
-  },
-
-  _getAppsIcons: function() {
-    let deferred = promise.defer();
-    let allApps = this.object.all;
-
-    let request = {
-      to: this._webAppsActor,
-      type: "getIconAsDataURL"
-    };
-
-    let client = this._connection.client;
-
-    let idx = 0;
-    (function getIcon() {
-      if (idx == allApps.length) {
-        return deferred.resolve();
-      }
-      let a = allApps[idx++];
-      request.manifestURL = a.manifestURL;
-      return client.request(request, (res) => {
-        if (res.error) {
-          Cu.reportError(res.message || res.error);
-        }
-
-        if (res.url) {
-          a.iconURL = res.url;
-        }
-        getIcon();
-      });
-    })();
-
-    return deferred.promise;
-  },
-
-  _onAppOpen: function(manifest) {
-    let a = this._getAppFromManifest(manifest);
-    a.running = true;
-    let running = this.object.running;
-    if (running.indexOf(manifest) < 0) {
-      this.object.running.push(manifest);
-    }
-  },
-
-  _onAppClose: function(manifest) {
-    let a = this._getAppFromManifest(manifest);
-    a.running = false;
-    let running = this.object.running;
-    this.object.running = running.filter((m) => {
-      return m != manifest;
-    });
-  },
-
-  _onAppInstall: function(manifest) {
-    let client = this._connection.client;
-    let request = {
-      to: this._webAppsActor,
-      type: "getApp",
-      manifestURL: manifest
-    };
-
-    client.request(request, (res) => {
-      if (res.error) {
-        if (res.error == "forbidden") {
-          // We got a notification for an app we don't have access to.
-          // Ignore.
-          return;
-        }
-        Cu.reportError(res.message || res.error);
-        return;
-      }
-
-      let app = res.app;
-      app.running = false;
-
-      let notFound = true;
-      let proxifiedApp;
-      for (let i = 0; i < this.object.all.length; i++) {
-        let storedApp = this.object.all[i];
-        if (storedApp.manifestURL == app.manifestURL) {
-          this.object.all[i] = app;
-          proxifiedApp = this.object.all[i];
-          notFound = false;
-          break;
-        }
-      }
-      if (notFound) {
-        this.object.all.push(app);
-        proxifiedApp = this.object.all[this.object.all.length - 1];
-      }
-
-      request.type = "getIconAsDataURL";
-      client.request(request, (res) => {
-        if (res.url) {
-          proxifiedApp.iconURL = res.url;
-        }
-      });
-
-      // This app may have been running while being installed, so check the list
-      // of running apps again to get the right answer.
-      this._getRunningApps();
-    });
-  },
-
-  _onAppUninstall: function(manifest) {
-    this.object.all = this.object.all.filter((app) => {
-      return (app.manifestURL != manifest);
-    });
-  },
-}
--- a/devtools/client/debugger/debugger-controller.js
+++ b/devtools/client/debugger/debugger-controller.js
@@ -871,17 +871,17 @@ StackFrames.prototype = {
     // to contain all the values.
     if (this._syncedWatchExpressions && aDepth == 0) {
       let label = L10N.getStr("watchExpressionsScopeLabel");
       let scope = DebuggerView.Variables.addScope(label);
 
       // Customize the scope for holding watch expressions evaluations.
       scope.descriptorTooltip = false;
       scope.contextMenuId = "debuggerWatchExpressionsContextMenu";
-      scope.separatorStr = L10N.getStr("watchExpressionsSeparatorLabel");
+      scope.separatorStr = L10N.getStr("watchExpressionsSeparatorLabel2");
       scope.switch = DebuggerView.WatchExpressions.switchExpression;
       scope.delete = DebuggerView.WatchExpressions.deleteExpression;
 
       // The evaluation hasn't thrown, so fetch and add the returned results.
       this._fetchWatchExpressions(scope, this._currentEvaluation.return);
 
       // The watch expressions scope is always automatically expanded.
       scope.expand();
--- a/devtools/client/debugger/test/mochitest/browser.ini
+++ b/devtools/client/debugger/test/mochitest/browser.ini
@@ -569,16 +569,18 @@ skip-if = e10s && debug
 [browser_dbg_variables-view-reexpand-03.js]
 skip-if = e10s && debug
 [browser_dbg_variables-view-webidl.js]
 skip-if = e10s && debug
 [browser_dbg_watch-expressions-01.js]
 skip-if = e10s && debug
 [browser_dbg_watch-expressions-02.js]
 skip-if = e10s && debug
+[browser_dbg_worker-console.js]
+skip-if = e10s && debug
 [browser_dbg_worker-window.js]
 skip-if = e10s && debug
 [browser_dbg_WorkerActor.attach.js]
 skip-if = e10s && debug
 [browser_dbg_WorkerActor.attachThread.js]
 skip-if = e10s && debug
 [browser_dbg_split-console-keypress.js]
 skip-if = e10s && debug
--- a/devtools/client/debugger/test/mochitest/browser_dbg_break-on-next-console.js
+++ b/devtools/client/debugger/test/mochitest/browser_dbg_break-on-next-console.js
@@ -28,17 +28,17 @@ function test() {
       .then(() => closeDebuggerAndFinish(gPanel));
   });
 
   let testConsole = Task.async(function*() {
     info("Starting testConsole");
 
     let oncePaused = gTarget.once("thread-paused");
     EventUtils.sendMouseEvent({ type: "mousedown" }, gResumeButton, gDebugger);
-    let jsterm = yield getSplitConsole();
+    let jsterm = yield getSplitConsole(gDevTools.getToolbox(gPanel.target));
     let executed = jsterm.execute("1+1");
     yield oncePaused;
 
     let updatedFrame = yield waitForDebuggerEvents(gPanel, gDebugger.EVENTS.FETCHED_SCOPES);
     let variables = gDebugger.DebuggerView.Variables;
 
     is(variables._store.length, 3, "Correct number of scopes available");
     is(variables.getScopeAtIndex(0).name, "With scope [Object]",
@@ -49,25 +49,9 @@ function test() {
         "Paused with correct scope (2)");
 
     let onceResumed = gTarget.once("thread-resumed");
     EventUtils.sendMouseEvent({ type: "mousedown" }, gResumeButton, gDebugger);
     yield onceResumed;
 
     yield executed;
   });
-
-  function getSplitConsole() {
-    return new Promise(resolve => {
-      let toolbox = gDevTools.getToolbox(gPanel.target);
-      toolbox.once("webconsole-ready", () => {
-        ok(toolbox.splitConsole, "Split console is shown.");
-        let jsterm = toolbox.getPanel("webconsole").hud.jsterm;
-        resolve(jsterm);
-      });
-      EventUtils.synthesizeKey("VK_ESCAPE", {}, gDebugger);
-    });
-  }
 }
-
-registerCleanupFunction(() => {
-  Services.prefs.clearUserPref("devtools.toolbox.splitconsoleEnabled");
-});
--- a/devtools/client/debugger/test/mochitest/browser_dbg_split-console-keypress.js
+++ b/devtools/client/debugger/test/mochitest/browser_dbg_split-console-keypress.js
@@ -89,24 +89,9 @@ function test() {
     gPanel.target.on("thread-resumed", () => {
       is(gThreadClient.paused, false,
         "Should not be paused after resume");
       // Final test: did we preserve console inputNode focus during resume?
       is(consoleLostFocus, false, "Resume - console should keep focus");
       closeDebuggerAndFinish(gPanel);
     });
   });
-
-  function getSplitConsole(toolbox, theDebugger) {
-    return new Promise(resolve => {
-      toolbox.once("webconsole-ready", () => {
-        let jsterm = toolbox.getPanel("webconsole").hud.jsterm;
-        resolve(jsterm);
-      });
-      EventUtils.synthesizeKey("VK_ESCAPE", {}, theDebugger);
-    });
-  }
 }
-
-registerCleanupFunction(() => {
-  // We don't want the open split console to confuse other tests..
-  Services.prefs.clearUserPref("devtools.toolbox.splitconsoleEnabled");
-});
new file mode 100644
--- /dev/null
+++ b/devtools/client/debugger/test/mochitest/browser_dbg_worker-console.js
@@ -0,0 +1,145 @@
+// Check to make sure that a worker can be attached to a toolbox
+// and that the console works.
+
+var TAB_URL = EXAMPLE_URL + "doc_WorkerActor.attachThread-tab.html";
+var WORKER_URL = "code_WorkerActor.attachThread-worker.js";
+
+function* initWorkerDebugger(TAB_URL, WORKER_URL) {
+  if (!DebuggerServer.initialized) {
+    DebuggerServer.init();
+    DebuggerServer.addBrowserActors();
+  }
+
+  let client = new DebuggerClient(DebuggerServer.connectPipe());
+  yield connect(client);
+
+  let tab = yield addTab(TAB_URL);
+  let { tabs } = yield listTabs(client);
+  let [, tabClient] = yield attachTab(client, findTab(tabs, TAB_URL));
+
+  yield createWorkerInTab(tab, WORKER_URL);
+
+  let { workers } = yield listWorkers(tabClient);
+  let [, workerClient] = yield attachWorker(tabClient,
+                                             findWorker(workers, WORKER_URL));
+
+  let toolbox = yield gDevTools.showToolbox(TargetFactory.forWorker(workerClient),
+                                            "jsdebugger",
+                                            Toolbox.HostType.WINDOW);
+
+  let debuggerPanel = toolbox.getCurrentPanel();
+  let gDebugger = debuggerPanel.panelWin;
+
+  return {client,tab,tabClient,workerClient,toolbox,gDebugger};
+}
+
+add_task(function* testNormalExecution() {
+  let {client,tab,tabClient,workerClient,toolbox,gDebugger} =
+    yield initWorkerDebugger(TAB_URL, WORKER_URL);
+
+  let jsterm = yield getSplitConsole(toolbox);
+  let executed = yield jsterm.execute("this.location.toString()");
+  ok(executed.textContent.includes(WORKER_URL),
+      "Evaluating the global's location works");
+
+  yield gDevTools.closeToolbox(TargetFactory.forWorker(workerClient));
+  terminateWorkerInTab(tab, WORKER_URL);
+  yield waitForWorkerClose(workerClient);
+  yield close(client);
+  yield removeTab(tab);
+});
+
+add_task(function* testWhilePaused() {
+  let {client,tab,tabClient,workerClient,toolbox,gDebugger} =
+    yield initWorkerDebugger(TAB_URL, WORKER_URL);
+
+  let gTarget = gDebugger.gTarget;
+  let gResumeButton = gDebugger.document.getElementById("resume");
+  let gResumeKey = gDebugger.document.getElementById("resumeKey");
+
+  // Execute some basic math to make sure evaluations are working.
+  let jsterm = yield getSplitConsole(toolbox);
+  let executed = yield jsterm.execute("10000+1");
+  ok(executed.textContent.includes("10001"), "Text for message appeared correct");
+
+  // Pause the worker by waiting for next execution and then sending a message to
+  // it from the main thread.
+  let oncePaused = gTarget.once("thread-paused");
+  EventUtils.sendMouseEvent({ type: "mousedown" }, gResumeButton, gDebugger);
+  once(gDebugger.gClient, "willInterrupt").then(() => {
+    info("Posting message to worker, then waiting for a pause");
+    postMessageToWorkerInTab(tab, WORKER_URL, "ping");
+  });
+  yield oncePaused;
+
+  let command1 = jsterm.execute("10000+2");
+  let command2 = jsterm.execute("10000+3");
+  let command3 = jsterm.execute("foobar"); // throw an error
+
+  info ("Trying to get the result of command1");
+  executed = yield command1;
+  ok(executed.textContent.includes("10002"),
+      "command1 executed successfully");
+
+  info ("Trying to get the result of command2");
+  executed = yield command2;
+  ok(executed.textContent.includes("10003"),
+      "command2 executed successfully");
+
+  info ("Trying to get the result of command3")
+  executed = yield command3;
+  // XXXworkers This is failing until Bug 1215120 is resolved.
+  todo(executed.textContent.includes("ReferenceError: foobar is not defined"),
+      "command3 executed successfully");
+
+  let onceResumed = gTarget.once("thread-resumed");
+  EventUtils.sendMouseEvent({ type: "mousedown" }, gResumeButton, gDebugger);
+  yield onceResumed;
+
+  yield gDevTools.closeToolbox(TargetFactory.forWorker(workerClient));
+  terminateWorkerInTab(tab, WORKER_URL);
+  yield waitForWorkerClose(workerClient);
+  yield close(client);
+  yield removeTab(tab);
+});
+
+// Test to see if creating the pause from the console works.
+add_task(function* testPausedByConsole() {
+  let {client,tab,tabClient,workerClient,toolbox,gDebugger} =
+    yield initWorkerDebugger(TAB_URL, WORKER_URL);
+
+  let gTarget = gDebugger.gTarget;
+  let gResumeButton = gDebugger.document.getElementById("resume");
+  let gResumeKey = gDebugger.document.getElementById("resumeKey");
+
+  let jsterm = yield getSplitConsole(toolbox);
+  let executed = yield jsterm.execute("10000+1");
+  ok(executed.textContent.includes("10001"),
+      "Text for message appeared correct");
+
+  let oncePaused = gTarget.once("thread-paused");
+  EventUtils.sendMouseEvent({ type: "mousedown" }, gResumeButton, gDebugger);
+  let pausedExecution = jsterm.execute("10000+2");
+
+  info("Executed a command with 'break on next' active, waiting for pause");
+  yield oncePaused;
+
+  executed = yield jsterm.execute("10000+3");
+  ok(executed.textContent.includes("10003"),
+      "Text for message appeared correct");
+
+  info("Waiting for a resume");
+  let onceResumed = gTarget.once("thread-resumed");
+  EventUtils.sendMouseEvent({ type: "mousedown" }, gResumeButton, gDebugger);
+  yield onceResumed;
+
+  executed = yield pausedExecution;
+  ok(executed.textContent.includes("10002"),
+      "Text for message appeared correct");
+
+  yield gDevTools.closeToolbox(TargetFactory.forWorker(workerClient));
+  terminateWorkerInTab(tab, WORKER_URL);
+  yield waitForWorkerClose(workerClient);
+  yield close(client);
+  yield removeTab(tab);
+});
--- a/devtools/client/debugger/test/mochitest/head.js
+++ b/devtools/client/debugger/test/mochitest/head.js
@@ -1185,8 +1185,33 @@ function afterDispatch(store, type) {
       predicate: action => (
         action.type === type &&
         action.status ? action.status === "done" : true
       ),
       run: resolve
     });
   });
 }
+
+// Return a promise with a reference to jsterm, opening the split
+// console if necessary.  This cleans up the split console pref so
+// it won't pollute other tests.
+function getSplitConsole(toolbox, win) {
+  registerCleanupFunction(() => {
+    Services.prefs.clearUserPref("devtools.toolbox.splitconsoleEnabled");
+  });
+
+  if (!win) {
+    win = toolbox.doc.defaultView;
+  }
+
+  if (!toolbox.splitConsole) {
+    EventUtils.synthesizeKey("VK_ESCAPE", {}, win);
+  }
+
+  return new Promise(resolve => {
+    toolbox.getPanelWhenReady("webconsole").then(() => {
+      ok(toolbox.splitConsole, "Split console is shown.");
+      let jsterm = toolbox.getPanel("webconsole").hud.jsterm;
+      resolve(jsterm);
+    });
+  });
+}
--- a/devtools/client/framework/gDevTools.jsm
+++ b/devtools/client/framework/gDevTools.jsm
@@ -595,20 +595,16 @@ var gDevToolsBrowser = {
 
     let showWebIDEWidget = Services.prefs.getBoolPref("devtools.webide.widget.enabled");
     if (webIDEEnabled && showWebIDEWidget) {
       gDevToolsBrowser.installWebIDEWidget();
     } else {
       gDevToolsBrowser.uninstallWebIDEWidget();
     }
 
-    // Enable App Manager?
-    let appMgrEnabled = Services.prefs.getBoolPref("devtools.appmanager.enabled");
-    toggleCmd("Tools:DevAppMgr", !webIDEEnabled && appMgrEnabled);
-
     // Enable Browser Toolbox?
     let chromeEnabled = Services.prefs.getBoolPref("devtools.chrome.enabled");
     let devtoolsRemoteEnabled = Services.prefs.getBoolPref("devtools.debugger.remote-enabled");
     let remoteEnabled = chromeEnabled && devtoolsRemoteEnabled;
     toggleCmd("Tools:BrowserToolbox", remoteEnabled);
     toggleCmd("Tools:BrowserContentToolbox", remoteEnabled && win.gMultiProcessBrowser);
 
     // Enable Error Console?
@@ -682,23 +678,16 @@ var gDevToolsBrowser = {
   /**
    * Open a tab to allow connects to a remote browser
    */
   openConnectScreen: function(gBrowser) {
     gBrowser.selectedTab = gBrowser.addTab("chrome://devtools/content/framework/connect/connect.xhtml");
   },
 
   /**
-   * Open the App Manager
-   */
-  openAppManager: function(gBrowser) {
-    gBrowser.selectedTab = gBrowser.addTab("about:app-manager");
-  },
-
-  /**
    * Open WebIDE
    */
   openWebIDE: function() {
     let win = Services.wm.getMostRecentWindow("devtools:webide");
     if (win) {
       win.focus();
     } else {
       Services.ww.openWindow(null, "chrome://webide/content/", "webide", "chrome,centerscreen,resizable", null);
--- a/devtools/client/framework/target.js
+++ b/devtools/client/framework/target.js
@@ -727,18 +727,24 @@ WorkerTarget.prototype = {
   get isTabActor() {
     return true;
   },
 
   get url() {
     return this._workerClient.url;
   },
 
+  get isWorkerTarget() {
+    return true;
+  },
+
   get form() {
-    return {};
+    return {
+      consoleActor: this._workerClient.consoleActor
+    };
   },
 
   get activeTab() {
     return this._workerClient;
   },
 
   get client() {
     return this._workerClient.client;
--- a/devtools/client/jar.mn
+++ b/devtools/client/jar.mn
@@ -128,28 +128,16 @@ devtools.jar:
     content/framework/dev-edition-promo/dev-edition-promo.xul (framework/dev-edition-promo/dev-edition-promo.xul)
 *   content/framework/dev-edition-promo/dev-edition-promo.css (framework/dev-edition-promo/dev-edition-promo.css)
     content/framework/dev-edition-promo/dev-edition-logo.png (framework/dev-edition-promo/dev-edition-logo.png)
     content/inspector/inspector.xul (inspector/inspector.xul)
     content/inspector/inspector.css (inspector/inspector.css)
     content/framework/connect/connect.xhtml (framework/connect/connect.xhtml)
     content/framework/connect/connect.css (framework/connect/connect.css)
     content/framework/connect/connect.js (framework/connect/connect.js)
-    content/app-manager/content/template.js (app-manager/content/template.js)
-    content/app-manager/content/utils.js (app-manager/content/utils.js)
-    content/app-manager/content/connection-footer.js (app-manager/content/connection-footer.js)
-    content/app-manager/content/connection-footer.xhtml (app-manager/content/connection-footer.xhtml)
-    content/app-manager/content/device.js (app-manager/content/device.js)
-    content/app-manager/content/device.xhtml (app-manager/content/device.xhtml)
-    content/app-manager/content/projects.js (app-manager/content/projects.js)
-    content/app-manager/content/projects.xhtml (app-manager/content/projects.xhtml)
-    content/app-manager/content/index.xul (app-manager/content/index.xul)
-    content/app-manager/content/index.js (app-manager/content/index.js)
-    content/app-manager/content/help.xhtml (app-manager/content/help.xhtml)
-    content/app-manager/content/manifest-editor.js (app-manager/content/manifest-editor.js)
     content/shared/widgets/graphs-frame.xhtml (shared/widgets/graphs-frame.xhtml)
     content/shared/widgets/spectrum-frame.xhtml (shared/widgets/spectrum-frame.xhtml)
     content/shared/widgets/spectrum.css (shared/widgets/spectrum.css)
     content/shared/widgets/cubic-bezier-frame.xhtml (shared/widgets/cubic-bezier-frame.xhtml)
     content/shared/widgets/cubic-bezier.css (shared/widgets/cubic-bezier.css)
     content/shared/widgets/mdn-docs-frame.xhtml (shared/widgets/mdn-docs-frame.xhtml)
     content/shared/widgets/mdn-docs.css (shared/widgets/mdn-docs.css)
     content/shared/widgets/filter-frame.xhtml (shared/widgets/filter-frame.xhtml)
@@ -327,27 +315,16 @@ devtools.jar:
     skin/themes/images/cubic-bezier-swatch.png (themes/images/cubic-bezier-swatch.png)
     skin/themes/images/cubic-bezier-swatch@2x.png (themes/images/cubic-bezier-swatch@2x.png)
     skin/themes/images/undock@2x.png (themes/images/undock@2x.png)
     skin/themes/font-inspector.css (themes/font-inspector.css)
     skin/themes/computedview.css (themes/computedview.css)
     skin/themes/images/arrow-e.png (themes/images/arrow-e.png)
     skin/themes/images/arrow-e@2x.png (themes/images/arrow-e@2x.png)
     skin/themes/projecteditor/projecteditor.css (themes/projecteditor/projecteditor.css)
-    skin/themes/app-manager/connection-footer.css (themes/app-manager/connection-footer.css)
-    skin/themes/app-manager/index.css (themes/app-manager/index.css)
-    skin/themes/app-manager/device.css (themes/app-manager/device.css)
-    skin/themes/app-manager/projects.css (themes/app-manager/projects.css)
-    skin/themes/app-manager/help.css (themes/app-manager/help.css)
-    skin/themes/app-manager/images/warning.svg (themes/app-manager/images/warning.svg)
-    skin/themes/app-manager/images/error.svg (themes/app-manager/images/error.svg)
-    skin/themes/app-manager/images/plus.svg (themes/app-manager/images/plus.svg)
-    skin/themes/app-manager/images/remove.svg (themes/app-manager/images/remove.svg)
-    skin/themes/app-manager/images/add.svg (themes/app-manager/images/add.svg)
-    skin/themes/app-manager/images/index-icons.svg (themes/app-manager/images/index-icons.svg)
     skin/themes/app-manager/images/rocket.svg (themes/app-manager/images/rocket.svg)
     skin/themes/app-manager/images/noise.png (themes/app-manager/images/noise.png)
     skin/themes/app-manager/images/default-app-icon.png (themes/app-manager/images/default-app-icon.png)
     skin/themes/images/search-clear-failed.svg (themes/images/search-clear-failed.svg)
     skin/themes/images/search-clear-light.svg (themes/images/search-clear-light.svg)
     skin/themes/images/search-clear-dark.svg (themes/images/search-clear-dark.svg)
     skin/themes/tooltip/arrow-horizontal-dark.png (themes/tooltip/arrow-horizontal-dark.png)
     skin/themes/tooltip/arrow-horizontal-dark@2x.png (themes/tooltip/arrow-horizontal-dark@2x.png)
--- a/devtools/client/memory/app.js
+++ b/devtools/client/memory/app.js
@@ -60,17 +60,17 @@ const App = createClass({
           List({
             itemComponent: SnapshotListItem,
             items: snapshots,
             onClick: snapshot => dispatch(selectSnapshotAndRefresh(heapWorker, snapshot))
           }),
 
           HeapView({
             snapshot: selectedSnapshot,
-            onSnapshotClick: () => dispatch(takeSnapshotAndCensus(front, heapWorker))
+            onSnapshotClick: () => dispatch(takeSnapshotAndCensus(front, heapWorker)),
           }),
         ])
       ])
     );
   },
 });
 
 /**
--- a/devtools/client/memory/components/heap.js
+++ b/devtools/client/memory/components/heap.js
@@ -1,17 +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/. */
 
-const { DOM: dom, createClass, PropTypes } = require("devtools/client/shared/vendor/react");
+const { DOM: dom, createClass, PropTypes, createFactory } = require("devtools/client/shared/vendor/react");
+const Tree = createFactory(require("./tree"));
+const TreeItem = createFactory(require("./tree-item"));
 const { getSnapshotStatusText } = require("../utils");
 const { snapshotState: states } = require("../constants");
 const { snapshot: snapshotModel } = require("../models");
 const TAKE_SNAPSHOT_TEXT = "Take snapshot";
+const TREE_ROW_HEIGHT = 10;
+
+/**
+ * Creates a hash map mapping node IDs to its parent node.
+ *
+ * @param {CensusTreeNode} node
+ * @param {Object<number, CensusTreeNode>} aggregator
+ *
+ * @return {Object<number, CensusTreeNode>}
+ */
+function createParentMap (node, aggregator=Object.create(null)) {
+  for (let child of (node.children || [])) {
+    aggregator[child.id] = node;
+    createParentMap(child, aggregator);
+  }
+
+  return aggregator;
+}
+
+/**
+ * Creates properties to be passed into the Tree component.
+ *
+ * @param {CensusTreeNode} census
+ * @return {Object}
+ */
+function createTreeProperties (census) {
+  let map = createParentMap(census);
+
+  return {
+    // getParent only used for focusing parents when child selected;
+    // handle this later?
+    getParent: node => map(node.id),
+    getChildren: node => node.children || [],
+    renderItem: (item, depth, focused, arrow) => new TreeItem({ item, depth, focused, arrow }),
+    getRoots: () => census.children,
+    getKey: node => node.id,
+    itemHeight: TREE_ROW_HEIGHT,
+  };
+}
 
 /**
  * Main view for the memory tool -- contains several panels for different states;
  * an initial state of only a button to take a snapshot, loading states, and the
  * heap view tree.
  */
 
 const Heap = module.exports = createClass({
@@ -38,17 +79,19 @@ const Heap = module.exports = createClas
       case states.SAVED:
       case states.READING:
       case states.READ:
       case states.SAVING_CENSUS:
         pane = dom.div({ className: "heap-view-panel", "data-state": state },
           getSnapshotStatusText(snapshot));
         break;
       case states.SAVED_CENSUS:
-        pane = dom.div({ className: "heap-view-panel", "data-state": "loaded" }, JSON.stringify(census || {}));
+        pane = dom.div({ className: "heap-view-panel", "data-state": "loaded" },
+          Tree(createTreeProperties(snapshot.census))
+        );
         break;
     }
 
     return (
       dom.div({ id: "heap-view", "data-state": state }, pane)
     )
   }
 });
--- a/devtools/client/memory/components/moz.build
+++ b/devtools/client/memory/components/moz.build
@@ -3,9 +3,13 @@
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 DevToolsModules(
     'heap.js',
     'list.js',
     'snapshot-list-item.js',
     'toolbar.js',
+    'tree-item.js',
+    'tree.js',
 )
+
+MOCHITEST_CHROME_MANIFESTS += ['test/mochitest/chrome.ini']
new file mode 100644
--- /dev/null
+++ b/devtools/client/memory/components/test/mochitest/chrome.ini
@@ -0,0 +1,12 @@
+[DEFAULT]
+support-files =
+  head.js
+
+[test_tree_01.html]
+[test_tree_02.html]
+[test_tree_03.html]
+[test_tree_04.html]
+[test_tree_05.html]
+[test_tree_06.html]
+[test_tree_07.html]
+[test_tree_08.html]
new file mode 100644
--- /dev/null
+++ b/devtools/client/memory/components/test/mochitest/head.js
@@ -0,0 +1,205 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+"use strict";
+
+var { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
+
+Cu.import("resource://testing-common/Assert.jsm");
+Cu.import("resource://gre/modules/Task.jsm");
+var { Services } = Cu.import("resource://gre/modules/Services.jsm", {});
+
+// Disable logging for all the tests. Both the debugger server and frontend will
+// be affected by this pref.
+var gEnableLogging = Services.prefs.getBoolPref("devtools.debugger.log");
+Services.prefs.setBoolPref("devtools.debugger.log", false);
+
+// Enable the memory tool for all tests.
+var gMemoryToolEnabled = Services.prefs.getBoolPref("devtools.memory.enabled");
+Services.prefs.setBoolPref("devtools.memory.enabled", true);
+
+var { Promise: promise } = Cu.import("resource://gre/modules/Promise.jsm", {});
+var { require } = Cu.import("resource://gre/modules/devtools/shared/Loader.jsm", {});
+var { gDevTools } = Cu.import("resource:///modules/devtools/gDevTools.jsm", {});
+var { DebuggerServer } = Cu.import("resource://gre/modules/devtools/dbg-server.jsm", {});
+var { DebuggerClient } = Cu.import("resource://gre/modules/devtools/dbg-client.jsm", {});
+var DevToolsUtils = require("devtools/shared/DevToolsUtils");
+var { BrowserLoader } = Cu.import("resource://devtools/client/shared/browser-loader.js", {});
+var { TargetFactory } = require("devtools/client/framework/target");
+var { Toolbox } = require("devtools/client/framework/toolbox");
+
+DevToolsUtils.testing = true;
+var { require: bRequire } = BrowserLoader("resource://devtools/client/memory/", this);
+
+var EXAMPLE_URL = "http://example.com/browser/browser/devtools/memory/test/";
+
+// Encoding of the following tree/forest:
+//
+// A
+// |-- B
+// |   |-- E
+// |   |   |-- K
+// |   |   `-- L
+// |   |-- F
+// |   `-- G
+// |-- C
+// |   |-- H
+// |   `-- I
+// `-- D
+//     `-- J
+// M
+// `-- N
+//     `-- O
+var TEST_TREE = {
+  children: {
+    A: ["B", "C", "D"],
+    B: ["E", "F", "G"],
+    C: ["H", "I"],
+    D: ["J"],
+    E: ["K", "L"],
+    F: [],
+    G: [],
+    H: [],
+    I: [],
+    J: [],
+    K: [],
+    L: [],
+    M: ["N"],
+    N: ["O"],
+    O: []
+  },
+  parent: {
+    A: null,
+    B: "A",
+    C: "A",
+    D: "A",
+    E: "B",
+    F: "B",
+    G: "B",
+    H: "C",
+    I: "C",
+    J: "D",
+    K: "E",
+    L: "E",
+    M: null,
+    N: "M",
+    O: "N"
+  }
+};
+
+var TEST_TREE_INTERFACE = {
+  getParent: x => TEST_TREE.parent[x],
+  getChildren: x => TEST_TREE.children[x],
+  renderItem: (x, depth, focused, arrow) => "-".repeat(depth) + x + ":" + focused + "\n",
+  getRoots: () => ["A", "M"],
+  getKey: x => "key-" + x,
+  itemHeight: 1
+};
+
+// All tests are asynchronous.
+SimpleTest.waitForExplicitFinish();
+
+SimpleTest.registerCleanupFunction(() => {
+  info("finish() was called, cleaning up...");
+  Services.prefs.setBoolPref("devtools.debugger.log", gEnableLogging);
+  Services.prefs.setBoolPref("devtools.memory.enabled", gMemoryToolEnabled);
+});
+
+function addTab(url) {
+  info("Adding tab: " + url);
+
+  var deferred = promise.defer();
+  var tab = gBrowser.selectedTab = gBrowser.addTab(url);
+  var linkedBrowser = tab.linkedBrowser;
+
+  linkedBrowser.addEventListener("load", function onLoad() {
+    linkedBrowser.removeEventListener("load", onLoad, true);
+    info("Tab added and finished loading: " + url);
+    deferred.resolve(tab);
+  }, true);
+
+  return deferred.promise;
+}
+
+function removeTab(tab) {
+  info("Removing tab.");
+
+  var deferred = promise.defer();
+  var tabContainer = gBrowser.tabContainer;
+
+  tabContainer.addEventListener("TabClose", function onClose(aEvent) {
+    tabContainer.removeEventListener("TabClose", onClose, false);
+    info("Tab removed and finished closing.");
+    deferred.resolve();
+  }, false);
+
+  gBrowser.removeTab(tab);
+  return deferred.promise;
+}
+
+var withTab = Task.async(function* (url, generator) {
+  var tab = yield addTab(url);
+  try {
+    yield* generator(tab);
+  } finally {
+    yield removeTab(tab);
+  }
+});
+
+var openMemoryTool = Task.async(function* (tab) {
+  info("Initializing a memory panel.");
+
+  var target = TargetFactory.forTab(tab);
+  var debuggee = target.window.wrappedJSObject;
+
+  yield target.makeRemote();
+
+  var toolbox = yield gDevTools.showToolbox(target, "memory");
+  var panel = toolbox.getCurrentPanel();
+  return [target, debuggee, panel];
+});
+
+var closeMemoryTool = Task.async(function* (panel) {
+  info("Closing a memory panel");
+  yield panel._toolbox.destroy();
+});
+
+var withMemoryTool = Task.async(function* (tab, generator) {
+  var [target, debuggee, panel] = yield openMemoryTool(tab);
+  try {
+    yield* generator(target, debuggee, panel);
+  } finally {
+    yield closeMemoryTool(panel);
+  }
+});
+
+var withTabAndMemoryTool = Task.async(function* (url, generator) {
+  yield withTab(url, function* (tab) {
+    yield withMemoryTool(tab, function* (target, debuggee, panel) {
+      yield* generator(tab, target, debuggee, panel);
+    });
+  });
+});
+
+function reload(target) {
+  info("Reloading tab.");
+  var deferred = promise.defer();
+  target.once("navigate", deferred.resolve);
+  target.activeTab.reload();
+  return deferred.promise;
+}
+
+function setState(component, newState) {
+  var deferred = promise.defer();
+  component.setState(newState, deferred.resolve);
+  return deferred.promise;
+}
+
+function setProps(component, newState) {
+  var deferred = promise.defer();
+  component.setProps(newState, deferred.resolve);
+  return deferred.promise;
+}
+
+function isRenderedTree(actual, expectedDescription, msg) {
+    is(actual, expectedDescription.map(x => x + "\n").join(""), msg);
+}
new file mode 100644
--- /dev/null
+++ b/devtools/client/memory/components/test/mochitest/test_tree_01.html
@@ -0,0 +1,61 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+Test trees get displayed with the items in correct order and at the correct
+depth.
+-->
+<head>
+  <meta charset="utf-8">
+  <title>Tree component test</title>
+  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
+</head>
+<body>
+<pre id="test">
+<script src="head.js" type="application/javascript;version=1.8"></script>
+<script type="application/javascript;version=1.8">
+window.onload = Task.async(function* () {
+  try {
+    let React = bRequire("devtools/client/shared/vendor/react");
+    let Tree = React.createFactory(bRequire("devtools/client/memory/components/tree"));
+
+    ok(React, "Should get React");
+    ok(Tree, "Should get Tree");
+
+    const t = Tree(TEST_TREE_INTERFACE);
+    ok(t, "Should be able to create Tree instances");
+
+    const tree = React.render(t, window.document.body);
+    ok(tree, "Should be able to mount Tree instances");
+
+    yield setState(tree, {
+      expanded: new Set("ABCDEFGHIJKLMNO".split(""))
+    });
+
+    isRenderedTree(document.body.textContent, [
+      "A:false",
+      "-B:false",
+      "--E:false",
+      "---K:false",
+      "---L:false",
+      "--F:false",
+      "--G:false",
+      "-C:false",
+      "--H:false",
+      "--I:false",
+      "-D:false",
+      "--J:false",
+      "M:false",
+      "-N:false",
+      "--O:false",
+    ], "Should get the items rendered and indented as expected");
+  } catch(e) {
+    ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e));
+  } finally {
+    SimpleTest.finish();
+  }
+});
+</script>
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/devtools/client/memory/components/test/mochitest/test_tree_02.html
@@ -0,0 +1,42 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+Test that collapsed subtrees aren't rendered.
+-->
+<head>
+  <meta charset="utf-8">
+  <title>Tree component test</title>
+  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
+</head>
+<body>
+<pre id="test">
+<script src="head.js" type="application/javascript;version=1.8"></script>
+<script type="application/javascript;version=1.8">
+window.onload = Task.async(function* () {
+  try {
+    let React = bRequire("devtools/client/shared/vendor/react");
+    let Tree = React.createFactory(bRequire("devtools/client/memory/components/tree"));
+
+    const tree = React.render(Tree(TEST_TREE_INTERFACE), window.document.body);
+
+    yield setState(tree, {
+      expanded: new Set("MNO".split(""))
+    });
+
+    isRenderedTree(document.body.textContent, [
+      "A:false",
+      "M:false",
+      "-N:false",
+      "--O:false",
+    ], "Collapsed subtrees shouldn't be rendered");
+  } catch(e) {
+    ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e));
+  } finally {
+    SimpleTest.finish();
+  }
+});
+</script>
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/devtools/client/memory/components/test/mochitest/test_tree_03.html
@@ -0,0 +1,44 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+Test Tree's autoExpandDepth.
+-->
+<head>
+  <meta charset="utf-8">
+  <title>Tree component test</title>
+  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
+</head>
+<body>
+<pre id="test">
+<script src="head.js" type="application/javascript;version=1.8"></script>
+<script type="application/javascript;version=1.8">
+window.onload = Task.async(function* () {
+  try {
+    let React = bRequire("devtools/client/shared/vendor/react");
+    let Tree = React.createFactory(bRequire("devtools/client/memory/components/tree"));
+
+    const tree = React.render(Tree(TEST_TREE_INTERFACE), window.document.body);
+
+    yield setProps(tree, {
+      autoExpandDepth: 1
+    });
+
+    isRenderedTree(document.body.textContent, [
+      "A:false",
+      "-B:false",
+      "-C:false",
+      "-D:false",
+      "M:false",
+      "-N:false",
+    ], "Tree should be auto expanded one level");
+  } catch(e) {
+    ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e));
+  } finally {
+    SimpleTest.finish();
+  }
+});
+</script>
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/devtools/client/memory/components/test/mochitest/test_tree_04.html
@@ -0,0 +1,56 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+Test that we only render visible tree items.
+-->
+<head>
+  <meta charset="utf-8">
+  <title>Tree component test</title>
+  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
+</head>
+<body>
+<pre id="test">
+<script src="head.js" type="application/javascript;version=1.8"></script>
+<script type="application/javascript;version=1.8">
+window.onload = Task.async(function* () {
+  try {
+    const React = bRequire("devtools/client/shared/vendor/react");
+    const Tree = React.createFactory(bRequire("devtools/client/memory/components/tree"));
+    const tree = React.render(Tree(TEST_TREE_INTERFACE), window.document.body);
+
+    yield setState(tree, {
+      expanded: new Set("ABCDEFGHIJKLMNO".split("")),
+      height: 3,
+      scroll: 1
+    });
+
+    isRenderedTree(document.body.textContent, [
+      "A:false",
+      "-B:false",
+      "--E:false",
+      "---K:false",
+      "---L:false",
+    ], "Tree should show the second, third, and fourth items + buffer of 1 item at the end");
+
+    yield setState(tree, {
+      height: 2,
+      scroll: 3
+    });
+
+    isRenderedTree(document.body.textContent, [
+      "--E:false",
+      "---K:false",
+      "---L:false",
+      "--F:false",
+    ], "Tree should show the third and fourth item + buffer of 1 item at each end");
+  } catch(e) {
+    ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e));
+  } finally {
+    SimpleTest.finish();
+  }
+});
+</script>
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/devtools/client/memory/components/test/mochitest/test_tree_05.html
@@ -0,0 +1,79 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+Test focusing with the Tree component.
+-->
+<head>
+  <meta charset="utf-8">
+  <title>Tree component test</title>
+  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
+</head>
+<body>
+<pre id="test">
+<script src="head.js" type="application/javascript;version=1.8"></script>
+<script type="application/javascript;version=1.8">
+
+window.onload = Task.async(function* () {
+  try {
+    const React = bRequire("devtools/client/shared/vendor/react");
+    const { Simulate } = React.addons.TestUtils;
+    const Tree = React.createFactory(bRequire("devtools/client/memory/components/tree"));
+    const tree = React.render(Tree(TEST_TREE_INTERFACE), window.document.body);
+
+    yield setState(tree, {
+      focused: "G",
+      expanded: new Set("ABCDEFGHIJKLMNO".split(""))
+    });
+
+    isRenderedTree(document.body.textContent, [
+      "A:false",
+      "-B:false",
+      "--E:false",
+      "---K:false",
+      "---L:false",
+      "--F:false",
+      "--G:true",
+      "-C:false",
+      "--H:false",
+      "--I:false",
+      "-D:false",
+      "--J:false",
+      "M:false",
+      "-N:false",
+      "--O:false",
+    ], "G should be focused");
+
+    // Click the first tree node
+    Simulate.click(document.querySelector(".tree-node"));
+
+    // Let the next render happen.
+    yield setState(tree, {});
+
+    isRenderedTree(document.body.textContent, [
+      "A:true",
+      "-B:false",
+      "--E:false",
+      "---K:false",
+      "---L:false",
+      "--F:false",
+      "--G:false",
+      "-C:false",
+      "--H:false",
+      "--I:false",
+      "-D:false",
+      "--J:false",
+      "M:false",
+      "-N:false",
+      "--O:false",
+    ], "A should be focused");
+  } catch(e) {
+    ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e));
+  } finally {
+    SimpleTest.finish();
+  }
+});
+</script>
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/devtools/client/memory/components/test/mochitest/test_tree_06.html
@@ -0,0 +1,297 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+Test keyboard navigation with the Tree component.
+-->
+<head>
+  <meta charset="utf-8">
+  <title>Tree component test</title>
+  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
+</head>
+<body>
+<pre id="test">
+<script src="head.js" type="application/javascript;version=1.8"></script>
+<script type="application/javascript;version=1.8">
+window.onload = Task.async(function* () {
+  try {
+    const React = bRequire("devtools/client/shared/vendor/react");
+    const { Simulate } = React.addons.TestUtils;
+    const Tree = React.createFactory(bRequire("devtools/client/memory/components/tree"));
+    const tree = React.render(Tree(TEST_TREE_INTERFACE), window.document.body);
+
+    yield setState(tree, {
+      expanded: new Set("ABCDEFGHIJKLMNO".split(""))
+    });
+
+    // UP ----------------------------------------------------------------------
+
+    info("Up to the previous sibling.");
+
+    yield setState(tree, {
+      focused: "L"
+    });
+    Simulate.keyDown(document.querySelector(".tree"), { key: "ArrowUp" });
+    // Let the component re-render.
+    yield setState(tree, {});
+
+    isRenderedTree(document.body.textContent, [
+      "A:false",
+      "-B:false",
+      "--E:false",
+      "---K:true",
+      "---L:false",
+      "--F:false",
+      "--G:false",
+      "-C:false",
+      "--H:false",
+      "--I:false",
+      "-D:false",
+      "--J:false",
+      "M:false",
+      "-N:false",
+      "--O:false",
+    ], "After the UP, K should be focused.");
+
+    info("Up to the parent.");
+
+    Simulate.keyDown(document.querySelector(".tree"), { key: "ArrowUp" });
+    // Let the component re-render.
+    yield setState(tree, {});
+
+    isRenderedTree(document.body.textContent, [
+      "A:false",
+      "-B:false",
+      "--E:true",
+      "---K:false",
+      "---L:false",
+      "--F:false",
+      "--G:false",
+      "-C:false",
+      "--H:false",
+      "--I:false",
+      "-D:false",
+      "--J:false",
+      "M:false",
+      "-N:false",
+      "--O:false",
+    ], "After the UP, E should be focused.");
+
+    info("Try and navigate up, past the first item.");
+
+    yield setState(tree, {
+      focused: "A"
+    });
+    Simulate.keyDown(document.querySelector(".tree"), { key: "ArrowUp" });
+    // Let the component re-render.
+    yield setState(tree, {});
+
+    isRenderedTree(document.body.textContent, [
+      "A:true",
+      "-B:false",
+      "--E:false",
+      "---K:false",
+      "---L:false",
+      "--F:false",
+      "--G:false",
+      "-C:false",
+      "--H:false",
+      "--I:false",
+      "-D:false",
+      "--J:false",
+      "M:false",
+      "-N:false",
+      "--O:false",
+    ], "After the UP, A should be focused and we shouldn't have overflowed past it.");
+
+    // DOWN --------------------------------------------------------------------
+
+    yield setState(tree, {
+      focused: "K"
+    });
+
+    info("Down to next sibling.");
+
+    Simulate.keyDown(document.querySelector(".tree"), { key: "ArrowDown" });
+    // Let the component re-render.
+    yield setState(tree, {});
+
+    isRenderedTree(document.body.textContent, [
+      "A:false",
+      "-B:false",
+      "--E:false",
+      "---K:false",
+      "---L:true",
+      "--F:false",
+      "--G:false",
+      "-C:false",
+      "--H:false",
+      "--I:false",
+      "-D:false",
+      "--J:false",
+      "M:false",
+      "-N:false",
+      "--O:false",
+    ], "After the DOWN, L should be focused.");
+
+    info("Down to parent's next sibling.");
+
+    Simulate.keyDown(document.querySelector(".tree"), { key: "ArrowDown" });
+    // Let the component re-render.
+    yield setState(tree, {});
+
+    isRenderedTree(document.body.textContent, [
+      "A:false",
+      "-B:false",
+      "--E:false",
+      "---K:false",
+      "---L:false",
+      "--F:true",
+      "--G:false",
+      "-C:false",
+      "--H:false",
+      "--I:false",
+      "-D:false",
+      "--J:false",
+      "M:false",
+      "-N:false",
+      "--O:false",
+    ], "After the DOWN, F should be focused.");
+
+    info("Try and go down past the last item.");
+
+    yield setState(tree, {
+      focused: "O"
+    });
+    Simulate.keyDown(document.querySelector(".tree"), { key: "ArrowDown" });
+    // Let the component re-render.
+    yield setState(tree, {});
+
+    isRenderedTree(document.body.textContent, [
+      "A:false",
+      "-B:false",
+      "--E:false",
+      "---K:false",
+      "---L:false",
+      "--F:false",
+      "--G:false",
+      "-C:false",
+      "--H:false",
+      "--I:false",
+      "-D:false",
+      "--J:false",
+      "M:false",
+      "-N:false",
+      "--O:true",
+    ], "After the DOWN, O should still be focused and we shouldn't have overflowed past it.");
+
+    // LEFT --------------------------------------------------------------------
+
+    info("Left to go to parent.");
+
+    yield setState(tree, {
+      focused: "L"
+    })
+    Simulate.keyDown(document.querySelector(".tree"), { key: "ArrowLeft" });
+    // Let the component re-render.
+    yield setState(tree, {});
+
+    isRenderedTree(document.body.textContent, [
+      "A:false",
+      "-B:false",
+      "--E:true",
+      "---K:false",
+      "---L:false",
+      "--F:false",
+      "--G:false",
+      "-C:false",
+      "--H:false",
+      "--I:false",
+      "-D:false",
+      "--J:false",
+      "M:false",
+      "-N:false",
+      "--O:false",
+    ], "After the LEFT, E should be focused.");
+
+    info("Left to collapse children.");
+
+    Simulate.keyDown(document.querySelector(".tree"), { key: "ArrowLeft" });
+    // Let the component re-render.
+    yield setState(tree, {});
+
+    isRenderedTree(document.body.textContent, [
+      "A:false",
+      "-B:false",
+      "--E:true",
+      "--F:false",
+      "--G:false",
+      "-C:false",
+      "--H:false",
+      "--I:false",
+      "-D:false",
+      "--J:false",
+      "M:false",
+      "-N:false",
+      "--O:false",
+    ], "After the LEFT, E's children should be collapsed.");
+
+    // RIGHT -------------------------------------------------------------------
+
+    info("Right to expand children.");
+
+    Simulate.keyDown(document.querySelector(".tree"), { key: "ArrowRight" });
+    // Let the component re-render.
+    yield setState(tree, {});
+
+    isRenderedTree(document.body.textContent, [
+      "A:false",
+      "-B:false",
+      "--E:true",
+      "---K:false",
+      "---L:false",
+      "--F:false",
+      "--G:false",
+      "-C:false",
+      "--H:false",
+      "--I:false",
+      "-D:false",
+      "--J:false",
+      "M:false",
+      "-N:false",
+      "--O:false",
+    ], "After the RIGHT, E's children should be expanded again.");
+
+    info("Right to go to next item.");
+
+    Simulate.keyDown(document.querySelector(".tree"), { key: "ArrowRight" });
+    // Let the component re-render.
+    yield setState(tree, {});
+
+    isRenderedTree(document.body.textContent, [
+      "A:false",
+      "-B:false",
+      "--E:false",
+      "---K:true",
+      "---L:false",
+      "--F:false",
+      "--G:false",
+      "-C:false",
+      "--H:false",
+      "--I:false",
+      "-D:false",
+      "--J:false",
+      "M:false",
+      "-N:false",
+      "--O:false",
+    ], "After the RIGHT, K should be focused.");
+  } catch(e) {
+    ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e));
+  } finally {
+    SimpleTest.finish();
+  }
+});
+</script>
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/devtools/client/memory/components/test/mochitest/test_tree_07.html
@@ -0,0 +1,63 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+Test that arrows get the open attribute when their item's children are expanded.
+-->
+<head>
+  <meta charset="utf-8">
+  <title>Tree component test</title>
+  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
+  <link rel="stylesheet" href="chrome://browser/skin/devtools/light-theme.css" type="text/css">
+</head>
+<body>
+<pre id="test">
+<script src="head.js" type="application/javascript;version=1.8"></script>
+<script type="application/javascript;version=1.8">
+window.onload = Task.async(function* () {
+  try {
+    const React = bRequire("devtools/client/shared/vendor/react");
+    const Tree = React.createFactory(bRequire("devtools/client/memory/components/tree"));
+    const tree = React.render(Tree(TEST_TREE_INTERFACE), window.document.body);
+
+    yield setProps(tree, {
+      renderItem: (item, depth, focused, arrow) => {
+        return React.DOM.div(
+          {
+            id: item,
+            style: { marginLeft: depth * 16 + "px" }
+          },
+          arrow,
+          item
+        );
+      }
+    });
+
+    yield setState(tree, {
+      expanded: new Set("ABCDEFGHIJKLMNO".split(""))
+    });
+
+    let arrows = document.querySelectorAll(".arrow");
+    for (let a of arrows) {
+      ok(a.classList.contains("open"), "Every arrow should be open.");
+    }
+
+    yield setState(tree, {
+      expanded: new Set()
+    });
+
+    arrows = document.querySelectorAll(".arrow");
+    for (let a of arrows) {
+      ok(!a.classList.contains("open"), "Every arrow should be closed.");
+    }
+
+  } catch(e) {
+    ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e));
+  } finally {
+    SimpleTest.finish();
+  }
+});
+</script>
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/devtools/client/memory/components/test/mochitest/test_tree_08.html
@@ -0,0 +1,46 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+Test that when an item in the Tree component is clicked, it steals focus from
+other inputs.
+-->
+<head>
+  <meta charset="utf-8">
+  <title>Tree component test</title>
+  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
+  <link rel="stylesheet" href="chrome://browser/skin/devtools/light-theme.css" type="text/css">
+</head>
+<body>
+<pre id="test">
+<script src="head.js" type="application/javascript;version=1.8"></script>
+<script type="application/javascript;version=1.8">
+window.onload = Task.async(function* () {
+  try {
+    const React = bRequire("devtools/client/shared/vendor/react");
+    const { Simulate } = React.addons.TestUtils;
+    const Tree = React.createFactory(bRequire("devtools/client/memory/components/tree"));
+    const tree = React.render(Tree(TEST_TREE_INTERFACE), window.document.body);
+
+    const input = document.createElement("input");
+    document.body.appendChild(input);
+
+    input.focus();
+    is(document.activeElement, input, "The text input should be focused.");
+
+    Simulate.click(document.querySelector(".tree-node"));
+    // Let the tree re-render, because focus is dealt with on componentDidUpdate.
+    yield setState(tree, {});
+
+    isnot(document.activeElement, input,
+          "The input should have had it's focus stolen by clicking on a tree item.");
+  } catch(e) {
+    ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e));
+  } finally {
+    SimpleTest.finish();
+  }
+});
+</script>
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/devtools/client/memory/components/tree-item.js
@@ -0,0 +1,27 @@
+/* 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/. */
+
+const { DOM: dom, createClass, PropTypes } = require("devtools/client/shared/vendor/react");
+const INDENT = 10;
+
+/**
+ * An arrow that displays whether its node is expanded (▼) or collapsed
+ * (▶). When its node has no children, it is hidden.
+ */
+const TreeItem = module.exports = createClass({
+  displayName: "tree-item",
+
+  render() {
+    let { item, depth, arrow, focused } = this.props;
+
+    return dom.div({ className: "heap-tree-item", style: { marginLeft: depth * INDENT }},
+      arrow,
+      dom.span({ className: "heap-tree-item-name" }, item.name),
+      dom.span({ className: "heap-tree-item-bytes" }, item.bytes),
+      dom.span({ className: "heap-tree-item-count" }, item.count),
+      dom.span({ className: "heap-tree-item-total-bytes" }, item.totalBytes),
+      dom.span({ className: "heap-tree-item-total-count" }, item.totalCount)
+    );
+  }
+});
new file mode 100644
--- /dev/null
+++ b/devtools/client/memory/components/tree.js
@@ -0,0 +1,455 @@
+/* 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/. */
+
+const { DOM: dom, createClass, createFactory, PropTypes } = require("devtools/client/shared/vendor/react");
+const { ViewHelpers } = require("resource://devtools/client/shared/widgets/ViewHelpers.jsm");
+
+const AUTO_EXPAND_DEPTH = 3; // depth
+
+/**
+ * An arrow that displays whether its node is expanded (▼) or collapsed
+ * (▶). When its node has no children, it is hidden.
+ */
+const ArrowExpander = createFactory(createClass({
+  displayName: "ArrowExpander",
+
+  shouldComponentUpdate(nextProps, nextState) {
+    return this.props.item !== nextProps.item
+      || this.props.visible != nextProps.visible
+      || this.props.expanded !== nextProps.expanded;
+  },
+
+  render() {
+    const attrs = {
+      className: "arrow theme-twisty",
+      onClick: this.props.expanded
+        ? () => this.props.onCollapse(this.props.item)
+        : e => this.props.onExpand(this.props.item, e.altKey)
+    };
+
+    if (this.props.expanded) {
+      attrs.className += " open";
+    }
+
+    if (!this.props.visible) {
+      attrs.style = {
+        visibility: "hidden"
+      };
+    }
+
+    return dom.div(attrs);
+  }
+}));
+
+const TreeNode = createFactory(createClass({
+  componentDidUpdate() {
+    if (this.props.focused) {
+      this.refs.button.getDOMNode().focus();
+    }
+  },
+
+  render() {
+    const arrow = ArrowExpander({
+      item: this.props.item,
+      expanded: this.props.expanded,
+      visible: this.props.hasChildren,
+      onExpand: this.props.onExpand,
+      onCollapse: this.props.onCollapse
+    });
+
+    return dom.div(
+      {
+        className: "tree-node div",
+        onFocus: this.props.onFocus,
+        onClick: this.props.onFocus,
+        onBlur: this.props.onBlur,
+        style: {
+          padding: 0,
+          margin: 0
+        }
+      },
+
+      this.props.renderItem(this.props.item,
+                            this.props.depth,
+                            this.props.focused,
+                            arrow),
+
+      // XXX: OSX won't focus/blur regular elements even if you set tabindex
+      // unless there is an input/button child.
+      dom.button(this._buttonAttrs)
+    );
+  },
+
+  _buttonAttrs: {
+    ref: "button",
+    style: {
+      opacity: 0,
+      width: "0 !important",
+      height: "0 !important",
+      padding: "0 !important",
+      outline: "none",
+      MozAppearance: "none",
+      // XXX: Despite resetting all of the above properties (and margin), the
+      // button still ends up with ~79px width, so we set a large negative
+      // margin to completely hide it.
+      MozMarginStart: "-1000px !important",
+    }
+  }
+}));
+
+/**
+ * A generic tree component. See propTypes for the public API.
+ * 
+ * @see `devtools/client/memory/components/test/mochitest/head.js` for usage
+ * @see `devtools/client/memory/components/heap.js` for usage
+ */
+const Tree = module.exports = createClass({
+  displayName: "Tree",
+
+  propTypes: {
+    // Required props
+
+    // A function to get an item's parent, or null if it is a root.
+    getParent: PropTypes.func.isRequired,
+    // A function to get an item's children.
+    getChildren: PropTypes.func.isRequired,
+    // A function which takes an item and ArrowExpander and returns a
+    // component.
+    renderItem: PropTypes.func.isRequired,
+    // A function which returns the roots of the tree (forest).
+    getRoots: PropTypes.func.isRequired,
+    // A function to get a unique key for the given item.
+    getKey: PropTypes.func.isRequired,
+    // The height of an item in the tree including margin and padding, in
+    // pixels.
+    itemHeight: PropTypes.number.isRequired,
+
+    // Optional props
+
+    // A predicate function to filter out unwanted items from the tree.
+    filter: PropTypes.func,
+    // The depth to which we should automatically expand new items.
+    autoExpandDepth: PropTypes.number
+  },
+
+  getDefaultProps() {
+    return {
+      filter: item => true,
+      expanded: new Set(),
+      seen: new Set(),
+      focused: undefined,
+      autoExpandDepth: AUTO_EXPAND_DEPTH
+    };
+  },
+
+  getInitialState() {
+    return {
+      scroll: 0,
+      height: window.innerHeight,
+      expanded: new Set(),
+      seen: new Set(),
+      focused: undefined
+    };
+  },
+
+  componentDidMount() {
+    window.addEventListener("resize", this._updateHeight);
+    this._updateHeight();
+  },
+
+  componentWillUnmount() {
+    window.removeEventListener("resize", this._updateHeight);
+  },
+
+  componentWillReceiveProps(nextProps) {
+    // Automatically expand the first autoExpandDepth levels for new items.
+    for (let { item } of this._dfsFromRoots(this.props.autoExpandDepth)) {
+      if (!this.state.seen.has(item)) {
+        this.state.expanded.add(item);
+        this.state.seen.add(item);
+      }
+    }
+  },
+
+  render() {
+    const traversal = this._dfsFromRoots();
+
+    // Remove 1 from `begin` and add 2 to `end` so that the top and bottom of
+    // the page are filled with the previous and next items respectively,
+    // rather than whitespace if the item is not in full view.
+    const begin = Math.max(((this.state.scroll / this.props.itemHeight) | 0) - 1, 0);
+    const end = begin + 2 + ((this.state.height / this.props.itemHeight) | 0);
+    const toRender = traversal.slice(begin, end);
+
+    const nodes = [
+      dom.div({
+        key: "top-spacer",
+        style: {
+          padding: 0,
+          margin: 0,
+          height: begin * this.props.itemHeight + "px"
+        }
+      })
+    ];
+
+    for (let i = 0; i < toRender.length; i++) {
+      let { item, depth } = toRender[i];
+      nodes.push(TreeNode({
+        key: this.props.getKey(item),
+        item: item,
+        depth: depth,
+        renderItem: this.props.renderItem,
+        focused: this.state.focused === item,
+        expanded: this.state.expanded.has(item),
+        hasChildren: !!this.props.getChildren(item).length,
+        onExpand: this._onExpand,
+        onCollapse: this._onCollapse,
+        onFocus: () => this._onFocus(item)
+      }));
+    }
+
+    nodes.push(dom.div({
+      key: "bottom-spacer",
+      style: {
+        padding: 0,
+        margin: 0,
+        height: (traversal.length - 1 - end) * this.props.itemHeight + "px"
+      }
+    }));
+
+    return dom.div(
+      {
+        className: "tree",
+        ref: "tree",
+        onKeyDown: this._onKeyDown,
+        onScroll: this._onScroll,
+        style: {
+          padding: 0,
+          margin: 0
+        }
+      },
+      nodes
+    );
+  },
+
+  /**
+   * Updates the state's height based on clientHeight.
+   */
+  _updateHeight() {
+    this.setState({
+      height: this.refs.tree.getDOMNode().clientHeight
+    });
+  },
+
+  /**
+   * Perform a pre-order depth-first search from item.
+   */
+  _dfs(item, maxDepth = Infinity, traversal = [], _depth = 0) {
+    if (!this.props.filter(item)) {
+      return traversal;
+    }
+
+    traversal.push({ item, depth: _depth });
+
+    if (!this.state.expanded.has(item)) {
+      return traversal;
+    }
+
+    const nextDepth = _depth + 1;
+
+    if (nextDepth > maxDepth) {
+      return traversal;
+    }
+
+    for (let child of this.props.getChildren(item)) {
+      this._dfs(child, maxDepth, traversal, nextDepth);
+    }
+
+    return traversal;
+  },
+
+  /**
+   * Perform a pre-order depth-first search over the whole forest.
+   */
+  _dfsFromRoots(maxDepth = Infinity) {
+    const traversal = [];
+    for (let root of this.props.getRoots()) {
+      this._dfs(root, maxDepth, traversal);
+    }
+    return traversal;
+  },
+
+  /**
+   * Expands current row.
+   *
+   * @param {Object} item
+   * @param {Boolean} expandAllChildren
+   */
+  _onExpand(item, expandAllChildren) {
+    this.state.expanded.add(item);
+
+    if (expandAllChildren) {
+      for (let { item: child } of this._dfs(item)) {
+        this.state.expanded.add(child);
+      }
+    }
+
+    this.setState({
+      expanded: this.state.expanded
+    });
+  },
+
+  /**
+   * Collapses current row.
+   *
+   * @param {Object} item
+   */
+  _onCollapse(item) {
+    this.state.expanded.delete(item);
+    this.setState({
+      expanded: this.state.expanded
+    });
+  },
+
+  /**
+   * Sets the passed in item to be the focused item.
+   *
+   * @param {Object} item
+   */
+  _onFocus(item) {
+    this.setState({
+      focused: item
+    });
+  },
+
+  /**
+   * Sets the state to have no focused item.
+   */
+  _onBlur() {
+    this.setState({
+      focused: undefined
+    });
+  },
+
+  /**
+   * Fired on a scroll within the tree's container, updates
+   * the stored position of the view port to handle virtual view rendering.
+   *
+   * @param {Event} e
+   */
+  _onScroll(e) {
+    this.setState({
+      scroll: Math.max(this.refs.tree.getDOMNode().scrollTop, 0),
+      height: this.refs.tree.getDOMNode().clientHeight
+    });
+  },
+
+  /**
+   * Handles key down events in the tree's container.
+   *
+   * @param {Event} e
+   */
+  _onKeyDown(e) {
+    if (this.state.focused == null) {
+      return;
+    }
+
+    // Prevent scrolling when pressing navigation keys. Guard against mocked
+    // events received when testing.
+    if (e.nativeEvent && e.nativeEvent.preventDefault) {
+      ViewHelpers.preventScrolling(e.nativeEvent);
+    }
+
+    switch (e.key) {
+      case "ArrowUp":
+        this._focusPrevNode();
+        return false;
+
+      case "ArrowDown":
+        this._focusNextNode();
+        return false;
+
+      case "ArrowLeft":
+        if (this.state.expanded.has(this.state.focused)
+            && this.props.getChildren(this.state.focused).length) {
+          this._onCollapse(this.state.focused);
+        } else {
+          this._focusParentNode();
+        }
+        return false;
+
+      case "ArrowRight":
+        if (!this.state.expanded.has(this.state.focused)) {
+          this._onExpand(this.state.focused);
+        } else {
+          this._focusNextNode();
+        }
+        return false;
+    }
+  },
+
+  /**
+   * Sets the previous node relative to the currently focused item, to focused.
+   */
+  _focusPrevNode() {
+    // Start a depth first search and keep going until we reach the currently
+    // focused node. Focus the previous node in the DFS, if it exists. If it
+    // doesn't exist, we're at the first node already.
+
+    let prev;
+    for (let { item } of this._dfsFromRoots()) {
+      if (item === this.state.focused) {
+        break;
+      }
+      prev = item;
+    }
+
+    if (prev === undefined) {
+      return;
+    }
+
+    this.setState({
+      focused: prev
+    });
+  },
+
+  /**
+   * Handles the down arrow key which will focus either the next child
+   * or sibling row.
+   */
+  _focusNextNode() {
+    // Start a depth first search and keep going until we reach the currently
+    // focused node. Focus the next node in the DFS, if it exists. If it
+    // doesn't exist, we're at the last node already.
+
+    const traversal = this._dfsFromRoots();
+
+    let i = 0;
+    for (let { item } of traversal) {
+      if (item === this.state.focused) {
+        break;
+      }
+      i++;
+    }
+
+    if (i + 1 < traversal.length) {
+      this.setState({
+        focused: traversal[i + 1].item
+      });
+    }
+  },
+
+  /**
+   * Handles the left arrow key, going back up to the current rows'
+   * parent row.
+   */
+  _focusParentNode() {
+    const parent = this.props.getParent(this.state.focused);
+    if (parent) {
+      this.setState({
+        focused: parent
+      });
+    }
+  }
+});
--- a/devtools/client/memory/models.js
+++ b/devtools/client/memory/models.js
@@ -34,23 +34,23 @@ let snapshotModel = exports.snapshot = P
   // State the snapshot is in
   // @see ./constants.js
   state: function (props, propName) {
     let stateNames = Object.keys(states);
     let current = props.state;
     let shouldHavePath = [states.SAVED, states.READ, states.SAVING_CENSUS, states.SAVED_CENSUS];
     let shouldHaveCensus = [states.SAVED_CENSUS];
 
-    if (!stateNames.contains(current)) {
+    if (!stateNames.includes(current)) {
       throw new Error(`Snapshot state must be one of ${stateNames}.`);
     }
-    if (shouldHavePath.contains(current) && !path) {
+    if (shouldHavePath.includes(current) && !path) {
       throw new Error(`Snapshots in state ${current} must have a snapshot path.`);
     }
-    if (shouldHaveCensus.contains(current) && (!props.census || !props.breakdown)) {
+    if (shouldHaveCensus.includes(current) && (!props.census || !props.breakdown)) {
       throw new Error(`Snapshots in state ${current} must have a census and breakdown.`);
     }
   },
 });
 
 let allocationsModel = exports.allocations = PropTypes.shape({
   // True iff we are recording allocation stacks right now.
   recording: PropTypes.bool.isRequired,
--- a/devtools/client/shared/browser-loader.js
+++ b/devtools/client/shared/browser-loader.js
@@ -34,17 +34,17 @@ function BrowserLoader(baseURI, window) 
   const loaderOptions = devtools.require("@loader/options");
   const opts = {
     id: "browser-loader",
     sharedGlobal: true,
     sandboxPrototype: window,
     paths: Object.assign({}, loaderOptions.paths),
     invisibleToDebugger: loaderOptions.invisibleToDebugger,
     require: (id, require) => {
-      const uri = require.resolve(id);
+      let uri = require.resolve(id);
 
       if (!uri.startsWith(baseURI) &&
           !uri.startsWith(VENDOR_CONTENT_URL)) {
         return devtools.require(uri);
       }
       return require(uri);
     }
   };
--- a/devtools/client/shared/redux/middleware/log.js
+++ b/devtools/client/shared/redux/middleware/log.js
@@ -4,14 +4,14 @@
 "use strict";
 
 /**
  * A middleware that logs all actions coming through the system
  * to the console.
  */
 function log({ dispatch, getState }) {
   return next => action => {
-    console.log("[DISPATCH]", JSON.stringify(action));
+    console.log("[DISPATCH]", JSON.stringify(action, null, 2));
     next(action);
-  }
+  };
 }
 
 exports.log = log;
deleted file mode 100644
--- a/devtools/client/themes/app-manager/connection-footer.css
+++ /dev/null
@@ -1,224 +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/. */
-
-/************** LAYOUT **************/
-
-#connection-footer {
-  display: flex;
-  flex-direction: column;
-  height: 50px;
-}
-
-#banners-and-logs {
-  display: flex;
-  flex-grow: 1;
-  max-height: 100%;
-}
-
-#logs {
-  display: flex;
-  width: 40%;
-  padding: 0;
-  width: 100%;
-}
-
-.banner {
-  display: none;
-  width: 60%;
-}
-
-#connection-footer[status="connected"]     #banner-connected,
-#connection-footer[status="connecting"]    #banner-connecting,
-#connection-footer[status="disconnected"]  #banner-disconnected,
-#connection-footer[status="disconnecting"] #banner-disconnecting {
-  display: flex;
-}
-
-body.show-simulators .banner,
-body.edit-connection .banner {
-  display: none !important;
-}
-
-body.show-simulators #banner-simulators,
-body.edit-connection #banner-editing {
-  display: flex !important;
-}
-
-#banner-logs {
-  width: 40%;
-  display: flex;
-}
-
-#logs > pre {
-  overflow: auto;
-  white-space: pre-line;
-}
-
-#status.banner-box {
-  width: 100% !important;
-}
-
-.banner-box {
-  display: flex;
-  flex-direction: column;
-  justify-content: center;
-  width: 100%;
-}
-
-#banner-connected > .banner-box {
-  align-items: flex-start;
-}
-
-#start-simulator-box {
-  display: inline;
-}
-
-/************** PIXELS **************/
-
-* {
-  margin: 0;
-  padding: 0;
-  box-sizing: border-box;
-  font-size: 0.9rem;
-}
-
-body {
-  color: #333;
-  background-color: white;
-  font-family: Lucida Grande, Helvetica, Helvetica Neue, sans-serif;
-}
-
-button {
-  background: linear-gradient(to bottom, #49535C, #394148);
-  box-shadow: 0px 1px 1px #3C444D, inset 0 1px 0px rgba(255,255,255,0.1);
-  color: #9FA6AD;
-  text-shadow: 0px 1px 1px rgba(0,0,0,0.6);
-  border: 1px solid #111;
-  cursor: pointer;
-  border-radius: 3px;
-  padding: 3px 10px;
-}
-
-button.left {
-  margin-right: 0px;
-  border-top-right-radius: 0;
-  border-bottom-right-radius: 0;
-}
-
-button.right {
-  margin-left: -6px;
-  border-top-left-radius: 0;
-  border-bottom-left-radius: 0;
-}
-
-button.action-primary {
-  background: linear-gradient(to bottom, #276DA3, #1E5580);
-  color: #EEE;
-}
-
-button.action-cancel {
-  background: linear-gradient(to bottom, #B32B02, #942300);
-  color: #EEE;
-}
-
-#banners-and-logs {
-  border-top: #111 solid;
-  border-width: 1px 0;
-  background: linear-gradient(to bottom, #323A42, #29313A);
-  color: #A8BABF;
-  box-shadow: inset 0 0 1px #424A51;
-}
-
-#status {
-  background: linear-gradient(to bottom, #454F59, #404952);
-  box-shadow: inset 0 0 1px #606D78, inset 0 1px 0 #5E6973;
-}
-
-#logs > pre {
-  border: 1px solid #111;
-  box-shadow: 0px 1px 1px #49525A, inset 0 0 5px rgba(0,0,0,0.3);
-  font-size: 10px;
-  background: #22272D;
-  padding: 5px;
-  height: 100%;
-  padding-left: 20px;
-  position: relative;
-}
-
-#logs > pre span{
-  text-shadow: 0 1px 2px #000;
-  color: #3195FB;
-  position: fixed;
-  right: calc(30% - 15px);
-  bottom: -1px;
-}
-
-#logs > pre b {
-  font-size: 10px;
-  color: #70C4FF;
-}
-
-.banner-box {
-  box-shadow: inset 0 0 1px #667480, inset 0 1px 0 #5E6973;
-  border-right: 1px solid #111;
-  background-position: center right;
-  background-size: 1px 100%;
-  background-repeat: no-repeat;
-  padding: 10px 20px;
-  position: relative;
-}
-
-.connected-status {
-  color: #B3BFC9;
-  text-shadow: 0px 1px 2px rgba(0,0,0,0.9);
-  padding-bottom: 10px;
-}
-
-.connected-status {
-  font-size: 150%;
-  top: 10%;
-  padding-right: 3px;
-  position: relative;
-}
-
-.connected-indicator {
-  box-shadow: inset 0 1px 0 rgba(255,255,255,0.3), inset 0 0px 1px rgba(255,255,255,0.3);
-  height: 100%;
-  flex: 0 0 10px;
-}
-
-#banner-connected .connected-indicator,
-#banner-connecting .connected-indicator {
-  background: linear-gradient(to bottom, #69B8FF, #339FFF );
-}
-
-#banner-simulators .connected-indicator,
-#banner-disconnected .connected-indicator,
-#banner-editing .connected-indicator,
-#banner-disconnecting .connected-indicator {
-  background: linear-gradient(to bottom, #375A87, #1C4375 );
-}
-
-#banner-simulators .banner-content > * {
-  display: inline-block;
-}
-
-#banner-simulators[simulator-count="0"] .found-simulator,
-#banner-simulators:not([simulator-count="0"]) .no-simulator {
-  display: none;
-}
-
-#connection-no-device,
-[device-count="0"] > #connection-found-device,
-#connection-manual,
-#connection-assisted {
-  display: none;
-}
-
-#connection-found-device,
-[device-count="0"] > #connection-no-device,
-[adb-available="true"] > #connection-assisted,
-[adb-available="false"] > #connection-manual {
-  display: inline;
-}
deleted file mode 100644
--- a/devtools/client/themes/app-manager/device.css
+++ /dev/null
@@ -1,410 +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/. */
-
-/*****************      GENERAL      *****************/
-
-* {
-  margin: 0;
-  padding: 0;
-  box-sizing: border-box;
-}
-
-html, body {
-  height: 100%;
-}
-
-body {
-  font-size: 0.9rem;
-  color: #333;
-  background-color:  rgb(225, 225, 225);
-  font-family: Lucida Grande, Helvetica, Helvetica Neue, sans-serif;
-  display: flex;
-  flex-direction: column;
-}
-
-template {
-  display: none;
-}
-
-h1 {
-  font-size: 20px;
-}
-
-#content {
-  display: flex;
-  flex-direction: row;
-  height: 100%;
-  overflow: hidden;
-}
-
-#detail {
-  background-image: url('images/noise.png');
-  display: flex;
-  flex-grow: 1;
-  z-index: 1;
-  overflow: hidden;
-}
-
-#meta {
-  background-size: 100%;
-  padding-top: 50px;
-}
-
-#connection-footer {
-  border-width: 0;
-  height: 50px;
-  min-height: 50px;
-}
-
-
-#root-actor-debug {
-  background: white;
-}
-
-/*****************     APP BUTTONS      *****************/
-
-
-
-.app-buttons {
-  display: block;
-  margin-left: 20px;
-  color: #BBB;
-}
-
-button {
-  margin: 0;
-  font-size: 11px;
-  border: 1px solid #CCC;
-  padding: 5px 15px;
-  cursor: pointer;
-  background: rgba(255,255,255,0.4);
-  text-transform: uppercase;
-  border-radius: 3px;
-  border-width: 1px;
-}
-
-.app-buttons > button {
-  display: none;
-}
-
-.app-buttons > button[disabled] {
-  background-color: transparent;
-  opacity: 0.4;
-  pointer-events: none;
-}
-
-.app[running="false"] > .app-buttons > .button-start,
-.app[running="true"] > .app-buttons > .button-stop,
-.app[running="true"] > .app-buttons > .button-debug {
-  display: inline-block;
-}
-
-.button-debug {
-  color: #3498DB;
-}
-
-.button-debug:hover {
-  background-color: #3498DB;
-  color: #FFF;
-}
-
-.button-debug[disabled] {
-  color: #3498DB;
-}
-
-.button-start {
-  color: #18BC9C
-}
-
-.button-start:hover {
-  background-color: #18BC9C;
-  color: #FFF;
-}
-
-.button-start[disabled] {
-  color: #18BC9C
-}
-
-.button-stop {
-  color: #E74C3C;
-}
-
-.button-stop:hover {
-  background-color: #E74C3C;
-  color: #FFF;
-}
-
-.button-stop[disabled] {
-  color: #E74C3C;
-}
-
-
-
-/*****************     PERMISSIONS     *****************/
-
-
-
-
-.permission-table {
-  display: flex;
-  flex-direction: column;
-  height: 100%;
-}
-
-.permission-table-body {
-  overflow: auto;
-  display: flex;
-  flex-grow: 1;
-  flex-direction: column;
-}
-
-.permission-table-header,
-.permission-table-footer {
-  display: flex;
-  background: #FFF;
-  border-top: 1px solid #CCC;
-  z-index: 2;
-  flex-shrink: 0;
-}
-
-.permission-table-header > div,
-.permission-table-footer > div {
-  z-index: 2;
-  flex-grow: 1;
-  background: linear-gradient(to bottom, #49535C, #394148);
-  box-shadow: 0px 1px 3px rgba(12, 20, 30, 0.5), inset 0 1px 0px rgba(255,255,255,0.1);
-  color: #9FA6AD;
-  text-shadow: 0px 1px 1px rgba(0,0,0,0.6);
-  border: 0;
-  margin: auto 0;
-  padding: 5px;
-  text-align: center;
-  background: transparent;
-  box-shadow: none;
-  text-shadow: none;
-}
-
-.permission-table-header > div {
-  flex-basis: 20%;
-}
-
-.permission-table-header > div:first-child {
-  text-align: start;
-  padding-left: 10px;
-  flex-basis: 30%;
-}
-
-.permission-table-header {
-  border: 0;
-  border-bottom: 1px solid #CCC;
-  box-shadow: 0 1px 4px rgba(0,0,0,0.3);
-}
-
-.permission-table-footer {
-  box-shadow: 0 -1px 4px rgba(0,0,0,0.3);
-}
-
-.permission {
-  display: flex;
-  flex-grow: 1;
-}
-
-.permission:nth-child(odd) {
-  background: #E4E4E4;
-}
-
-.permission:hover {
-  background: #EEE;
-}
-
-.permission > div {
-  flex-grow: 1;
-  flex-basis: 20%;
-  text-align: center;
-  padding: 3px;
-  border-right: 1px solid #CCC;
-  border-bottom: 1px solid #CCC;
-}
-
-.permission > div:first-child {
-  text-align: start;
-  padding: 3px 10px;
-  flex-basis: 30%;
-  font-weight: bold;
-}
-
-.permission > div[permission="1"]:before, .allow-label:after {
-  color: #98CF39;
-  content: '   \2713';
-}
-
-.permission > div[permission="2"]:before, .deny-label:after {
-  color: #CC4908;
-  content: '   \2715';
-}
-
-.permission > div[permission="3"]:before, .prompt-label:after {
-  color: #009EED;
-  content: '   !';
-}
-
-
-
-
-/*****************     SIDEBAR      *****************/
-
-
-
-
-#sidebar {
-  background: #EEE;
-  position: relative;
-  box-shadow: 0 1px 6px rgba(0,0,0,0.3);
-  display: flex;
-  flex-direction: column;
-  flex: 0 0 350px;
-  overflow: hidden;
-  z-index: 100;
-}
-
-.sidebar-item {
-  background-color: #F6F6F6;
-  box-shadow: inset 0 -1px 0 rgba(0,0,0,0.1);
-  color: #666;
-  line-height: 120%;
-  cursor: pointer;
-  display: flex;
-  padding: 15px 10px;
-  display: block;
-  text-align: start;
-  flex-grow: 1;
-}
-
-.sidebar-item > * {
-  flex-shrink: 0;
-}
-
-.sidebar-item:hover {
-  background-color: #EEE;
-}
-
-.sidebar-item.selected {
-  background-color: #46AFE3;
-  color: #FFF;
-}
-
-.help {
-  float: right;
-  padding: 0 5px;
-}
-
-/*****************     HEADER      *****************/
-
-header {
-  padding-top: 140px;
-  background-image: linear-gradient(to bottom, transparent, rgba(0,0,0,0.7));
-  color: #FFF;
-  text-shadow: 0 1px 2px rgba(0,0,0,0.8);
-  padding: 10px;
-}
-
-
-
-/*****************      APPS & BROWSER TABS      *****************/
-
-
-
-
-.apps, .browser-tabs {
-  display: flex;
-  flex-direction: column;
-  overflow: auto;
-}
-
-.browser-tabs.hidden {
-  display: none;
-}
-
-.app, .browser-tab {
-  display: flex;
-  align-items: center;
-  order: 1;
-}
-
-.app-name, .browser-tab-details {
-  flex-grow: 1;
-  font-weight: bold;
-}
-
-.app, .browser-tab {
-  padding: 10px 20px;
-  border-bottom: 1px solid #CCC;
-}
-
-.app:hover, .browser-tab:hover {
-  background-color: #EFEFEF;
-}
-
-.app-icon {
-  width: 32px;
-  height: 32px;
-  margin-right: 10px;
-}
-
-.browser-tab-url-subheading {
-  font-size: 10px;
-}
-
-
-
-/*****************      NOT CONNECTED      *****************/
-
-
-
-body:not(.notconnected) > #notConnectedMessage,
-body.notconnected > #content {
-  display: none;
-}
-
-#notConnectedMessage {
-  flex-grow: 1;
-  flex-direction: column;
-  margin: 50px auto;
-}
-
-#notConnectedMessage > span {
-  padding: 20px;
-  border: 1px solid #CCC;
-  border-radius: 5px;
-}
-
-#notConnectedMessage > span:before {
-  content: '';
-  background: url('images/error.svg') no-repeat;
-  background-size: 18px;
-  height: 24px;
-  width: 24px;
-  position: relative;
-  top: 10px;
-  display: inline-block;
-}
-
-
-
-/*****************      TABS       *****************/
-
-#tabs {
-  flex-grow: 1;
-  overflow: auto;
-}
-
-.tabpanel:not(.selected) {
-  display: none;
-}
-
-#tabs-headers {
-  flex-shrink: 0;
-  display: flex;
-  flex-direction: column;
-}
deleted file mode 100644
--- a/devtools/client/themes/app-manager/help.css
+++ /dev/null
@@ -1,40 +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/. */
-
-html, body {
-  margin: 0;
-  height: 100%;
-}
-
-body {
-  color: #555;
-  font-family: Lucida Grande, Helvetica, Helvetica Neue, sans-serif;
-  overflow: hidden;
-  max-width: 600px;
-  margin: auto;
-  padding: 20px 0;
-  background-color: #FFF;
-}
-
-button {
-  border: 1px solid #CCC;
-  padding: 5px 15px;
-  cursor: pointer;
-  background: rgba(255,255,255,0.4);
-  text-transform: uppercase;
-  color: #3498DB;
-}
-
-button:hover {
-  background-color: #3498DB;
-  color: #FFF;
-}
-
-a, a:visited {
-  color: rgb(39,109,163);
-}
-
-#close-button {
-  float: right;
-}
deleted file mode 100644
--- a/devtools/client/themes/app-manager/images/add.svg
+++ /dev/null
@@ -1,7 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- This Source Code Form is subject to the terms of the Mozilla Public
-   - License, v. 2.0. If a copy of the MPL was not distributed with this
-   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
-<svg xmlns="http://www.w3.org/2000/svg" width="64px" height="64px" viewBox="0 0 64 64">
-  <path fill="#00b2f7" d="M32.336,3.894c-15.74,0-28.5,12.76-28.5,28.5s12.76,28.5,28.5,28.5s28.5-12.76,28.5-28.5 S48.076,3.894,32.336,3.894z M44.86,36.966h-7.823v7.62c0,2.582-2.12,4.702-4.702,4.702c-2.584,0-4.704-2.12-4.704-4.702v-7.62 h-7.817c-2.52,0-4.572-2.056-4.572-4.572s2.053-4.572,4.572-4.572h7.817v-7.62c0-2.582,2.12-4.702,4.704-4.702 c2.582,0,4.702,2.12,4.702,4.702v7.62h7.823c2.514,0,4.57,2.056,4.57,4.572S47.374,36.966,44.86,36.966z"/>
-</svg>
deleted file mode 100644
--- a/devtools/client/themes/app-manager/images/error.svg
+++ /dev/null
@@ -1,7 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- This Source Code Form is subject to the terms of the Mozilla Public
-   - License, v. 2.0. If a copy of the MPL was not distributed with this
-   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
-<svg xmlns="http://www.w3.org/2000/svg" width="64px" height="64px" viewBox="0 0 64 64">
-  <path fill="#e25026" d="M32,4.894c-15.74,0-28.5,12.76-28.5,28.5s12.76,28.5,28.5,28.5s28.5-12.76,28.5-28.5S47.74,4.894,32,4.894 z M46.903,48.674c-1.817,1.817-4.691,1.76-6.449,0.002l-8.327-8.327l-8.151,8.151c-1.877,1.877-4.87,1.814-6.685,0 c-1.877-1.877-1.879-4.811-0.002-6.687l8.151-8.151l-8.327-8.327c-1.76-1.76-1.817-4.634,0-6.451c1.76-1.76,4.691-1.76,6.451,0 l8.327,8.327l8.151-8.151c1.877-1.877,4.811-1.874,6.687,0.002c1.814,1.814,1.877,4.808,0,6.685l-8.151,8.151l8.327,8.327 C48.662,43.982,48.662,46.914,46.903,48.674z"/>
-</svg>
deleted file mode 100644
--- a/devtools/client/themes/app-manager/images/index-icons.svg
+++ /dev/null
@@ -1,18 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- This Source Code Form is subject to the terms of the Mozilla Public
-   - License, v. 2.0. If a copy of the MPL was not distributed with this
-   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
-<svg xmlns="http://www.w3.org/2000/svg" width="160px" height="240px" viewBox="0 0 160 240">
-  <path fill="#414042" d="M40,21.149c-2.044,0.001-6.042,6.745-7.404,13.436L28,39.181v12l3.204,0l4.467-4.466 c0.457,0.478,0.96,0.88,1.5,1.199h5.652c0.543-0.318,1.05-0.719,1.507-1.199l4.466,4.466l3.204,0v-12l-4.597-4.596 C46.042,27.895,42.044,21.149,40,21.149z M40.005,28.185c0.712,0,1.965,2,2.746,4.392c-0.846-0.157-1.747-0.244-2.686-0.244 c-0.987,0-1.933,0.099-2.815,0.271C38.03,30.199,39.289,28.185,40.005,28.185z"/>
-  <path fill="#414042" d="M41.436,50.98c0,1.41-1.094,2.553-1.459,2.553c-0.364,0-1.459-1.144-1.459-2.553 c0-0.546,0.099-1.051,0.266-1.466h-1.69c-0.198,0.731-0.31,1.549-0.31,2.412c0,3.108,2.411,5.627,3.215,5.627 s3.216-2.519,3.215-5.626c0-0.864-0.112-1.681-0.31-2.413l-1.734,0C41.337,49.929,41.436,50.434,41.436,50.98z"/>
-  <path fill="#b2b5b9" d="M40,21.149c-2.044,0.001-6.042,6.745-7.404,13.436L28,39.181v12l3.204,0l4.467-4.466 c0.457,0.478,0.96,0.88,1.5,1.199h5.652c0.543-0.318,1.05-0.719,1.507-1.199l4.466,4.466l3.204,0v-12l-4.597-4.596 C46.042,27.895,42.044,21.149,40,21.149z M40.005,28.185c0.712,0,1.965,2,2.746,4.392c-0.846-0.157-1.747-0.244-2.686-0.244 c-0.987,0-1.933,0.099-2.815,0.271C38.03,30.199,39.289,28.185,40.005,28.185z"/>
-  <path fill="#b2b5b9" d="M41.436,50.98c0,1.41-1.094,2.553-1.459,2.553c-0.364,0-1.459-1.144-1.459-2.553 c0-0.546,0.099-1.051,0.266-1.466h-1.69c-0.198,0.731-0.31,1.549-0.31,2.412c0,3.108,2.411,5.627,3.215,5.627 s3.216-2.519,3.215-5.626c0-0.864-0.112-1.681-0.31-2.413l-1.734,0C41.337,49.929,41.436,50.434,41.436,50.98z"/>
-  <path fill="#dce8f3" d="M120,21.149c-2.044,0.001-6.042,6.745-7.404,13.436L108,39.181v12l3.204,0l4.467-4.466 c0.457,0.478,0.96,0.88,1.5,1.199h5.652c0.543-0.318,1.05-0.719,1.507-1.199l4.467,4.466l3.204,0v-12l-4.597-4.596 C126.042,27.895,122.044,21.149,120,21.149z M120.005,28.185c0.712,0,1.965,2,2.746,4.392c-0.846-0.157-1.747-0.244-2.686-0.244 c-0.987,0-1.933,0.099-2.815,0.271C118.03,30.199,119.289,28.185,120.005,28.185z"/>
-  <path fill="#dce8f3" d="M121.436,50.98c0,1.41-1.094,2.553-1.459,2.553c-0.364,0-1.459-1.144-1.459-2.553 c0-0.546,0.099-1.051,0.266-1.466h-1.69c-0.198,0.731-0.31,1.549-0.31,2.412c0,3.108,2.411,5.627,3.215,5.627 s3.216-2.519,3.215-5.626c0-0.864-0.112-1.681-0.31-2.413l-1.734,0C121.337,49.929,121.436,50.434,121.436,50.98z"/>
-  <path fill="#b2b5b9" d="M52.5,136.017c0,2.279-1.888,4.167-4.167,4.167H31.667c-2.279,0-4.167-1.888-4.167-4.167v-33.333 c0-2.279,1.888-4.167,4.167-4.167h16.667c2.279,0,4.167,1.888,4.167,4.167V136.017z M49.375,107.892 c0-0.554-0.488-1.042-1.042-1.042H31.667c-0.553,0-1.042,0.488-1.042,1.042v22.917c0,0.554,0.488,1.042,1.042,1.042h16.667 c0.553,0,1.042-0.488,1.042-1.042V107.892z M42.604,102.684h-5.208c-0.293,0-0.521,0.228-0.521,0.521 c0,0.293,0.228,0.521,0.521,0.521h5.208c0.293,0,0.521-0.228,0.521-0.521C43.125,102.912,42.897,102.684,42.604,102.684z M40,133.413c-1.432,0-2.604,1.171-2.604,2.604c0,1.433,1.172,2.604,2.604,2.604s2.604-1.171,2.604-2.604 C42.604,134.585,41.432,133.413,40,133.413z"/>
-  <path fill="#dce8f3" d="M132.5,136.017c0,2.279-1.888,4.167-4.167,4.167h-16.667c-2.279,0-4.167-1.888-4.167-4.167v-33.333 c0-2.279,1.888-4.167,4.167-4.167h16.667c2.279,0,4.167,1.888,4.167,4.167V136.017z M129.375,107.892 c0-0.554-0.488-1.042-1.042-1.042h-16.667c-0.553,0-1.042,0.488-1.042,1.042v22.917c0,0.554,0.488,1.042,1.042,1.042h16.667 c0.553,0,1.042-0.488,1.042-1.042V107.892z M122.604,102.684h-5.208c-0.293,0-0.521,0.228-0.521,0.521 c0,0.293,0.228,0.521,0.521,0.521h5.208c0.293,0,0.521-0.228,0.521-0.521C123.125,102.912,122.897,102.684,122.604,102.684z M120,133.413c-1.432,0-2.604,1.171-2.604,2.604c0,1.433,1.172,2.604,2.604,2.604s2.604-1.171,2.604-2.604 C122.604,134.585,121.432,133.413,120,133.413z"/>
-  <path fill="#b2b5b9" d="M40,185.388c8.121,0,14.729,6.607,14.729,14.729S48.121,214.845,40,214.845s-14.729-6.607-14.729-14.729 S31.879,185.388,40,185.388 M40,182.75c-9.591,0-17.367,7.775-17.367,17.367c0,9.591,7.775,17.367,17.367,17.367 s17.367-7.775,17.367-17.367C57.367,190.525,49.591,182.75,40,182.75L40,182.75z"/>
-  <path fill="#b2b5b9" d="M39.565,204.504c-0.688,0-1.196-0.508-1.286-1.195l-0.299-2.57c-0.12-0.808,0.359-1.405,1.166-1.495 c2.81-0.269,4.364-1.345,4.364-3.229v-0.06c0-1.674-1.285-2.84-3.438-2.84c-1.584,0-2.87,0.568-4.065,1.645 c-0.299,0.239-0.688,0.418-1.106,0.418c-0.926,0-1.674-0.747-1.674-1.644c0-0.448,0.18-0.927,0.598-1.285 c1.584-1.495,3.587-2.481,6.337-2.481c4.185,0,7.024,2.331,7.024,6.068v0.06c0,3.767-2.72,5.47-6.038,6.038l-0.12,1.375 c-0.12,0.657-0.598,1.195-1.285,1.195H39.565z M39.565,206.687c1.226,0,2.122,0.896,2.122,2.062v0.299 c0,1.166-0.896,2.062-2.122,2.062s-2.123-0.896-2.123-2.062v-0.299C37.442,207.583,38.339,206.687,39.565,206.687z"/>
-  <path fill="#dce8f3" d="M120,185.388c8.121,0,14.729,6.607,14.729,14.729s-6.607,14.729-14.729,14.729s-14.729-6.607-14.729-14.729 S111.879,185.388,120,185.388 M120,182.75c-9.591,0-17.367,7.775-17.367,17.367c0,9.591,7.775,17.367,17.367,17.367 s17.367-7.775,17.367-17.367C137.367,190.525,129.591,182.75,120,182.75L120,182.75z"/>
-  <path fill="#dce8f3" d="M119.564,204.504c-0.688,0-1.195-0.508-1.285-1.195l-0.299-2.57c-0.12-0.808,0.358-1.405,1.166-1.495 c2.81-0.269,4.363-1.345,4.363-3.229v-0.06c0-1.674-1.285-2.84-3.438-2.84c-1.584,0-2.869,0.568-4.064,1.645 c-0.3,0.239-0.688,0.418-1.106,0.418c-0.927,0-1.674-0.747-1.674-1.644c0-0.448,0.18-0.927,0.598-1.285 c1.584-1.495,3.587-2.481,6.337-2.481c4.186,0,7.024,2.331,7.024,6.068v0.06c0,3.767-2.72,5.47-6.038,6.038l-0.119,1.375 c-0.12,0.657-0.598,1.195-1.285,1.195H119.564z M119.564,206.687c1.226,0,2.122,0.896,2.122,2.062v0.299 c0,1.166-0.896,2.062-2.122,2.062s-2.122-0.896-2.122-2.062v-0.299C117.442,207.583,118.339,206.687,119.564,206.687z"/>
-</svg>
deleted file mode 100644
--- a/devtools/client/themes/app-manager/images/plus.svg
+++ /dev/null
@@ -1,7 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- This Source Code Form is subject to the terms of the Mozilla Public
-   - License, v. 2.0. If a copy of the MPL was not distributed with this
-   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
-<svg xmlns="http://www.w3.org/2000/svg" width="64px" height="64px" viewBox="0 0 64 64">
-  <path fill="#ababab" d="M32.336,3.894c-15.74,0-28.5,12.76-28.5,28.5s12.76,28.5,28.5,28.5s28.5-12.76,28.5-28.5 S48.076,3.894,32.336,3.894z M44.86,36.966h-7.823v7.62c0,2.582-2.12,4.702-4.702,4.702c-2.584,0-4.704-2.12-4.704-4.702v-7.62 h-7.817c-2.52,0-4.572-2.056-4.572-4.572s2.053-4.572,4.572-4.572h7.817v-7.62c0-2.582,2.12-4.702,4.704-4.702 c2.582,0,4.702,2.12,4.702,4.702v7.62h7.823c2.514,0,4.57,2.056,4.57,4.572S47.374,36.966,44.86,36.966z"/>
-</svg>
deleted file mode 100644
--- a/devtools/client/themes/app-manager/images/remove.svg
+++ /dev/null
@@ -1,7 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<!-- 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="64px" height="64px" viewBox="0 0 64 64">
-  <path fill="#ff6b00" d="m 12.183457,12.241457 c -11.129861,11.12986 -11.129861,29.175226 0,40.305086 11.12986,11.129861 29.175226,11.129861 40.305086,0 11.129861,-11.12986 11.129861,-29.175226 0,-40.305086 -11.12986,-11.129861 -29.175226,-11.129861 -40.305086,0 z m 32.241241,14.52963 -5.531697,5.531696 5.388154,5.388154 c 1.82575,1.82575 1.82575,4.823882 0,6.649632 -1.827164,1.827164 -4.825297,1.827164 -6.651047,0.0014 l -5.388153,-5.388153 -5.527454,5.527453 c -1.781909,1.781909 -4.686704,1.779081 -6.465784,0 -1.779081,-1.77908 -1.781202,-4.684582 0,-6.465784 l 5.527453,-5.527454 -5.388153,-5.388153 c -1.82575,-1.82575 -1.82575,-4.823883 0.0014,-6.651047 1.82575,-1.82575 4.823882,-1.82575 6.649632,0 l 5.388154,5.388154 5.531696,-5.531697 c 1.777667,-1.777666 4.68529,-1.777666 6.46437,0.0014 1.779081,1.77908 1.779081,4.686703 0.0014,6.46437 z"/>
-</svg>
deleted file mode 100644
--- a/devtools/client/themes/app-manager/images/warning.svg
+++ /dev/null
@@ -1,7 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- This Source Code Form is subject to the terms of the Mozilla Public
-   - License, v. 2.0. If a copy of the MPL was not distributed with this
-   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
-<svg xmlns="http://www.w3.org/2000/svg" width="64px" height="64px" viewBox="0 0 64 64">
-  <path fill="#ecb51f" d="M61.689,51.121L36.437,7.384c-2.441-4.227-6.434-4.227-8.875,0L2.311,51.121 c-2.441,4.227-0.444,7.686,4.437,7.686h50.504C62.133,58.807,64.13,55.349,61.689,51.121z M35.968,47.68 c0,2.191-1.688,3.877-3.968,3.877s-3.968-1.686-3.968-3.877v-0.093c0-2.187,1.688-3.873,3.968-3.873s3.968,1.686,3.968,3.873V47.68z M36.059,21.548l-1.961,17.146c-0.137,1.233-0.958,2.009-2.098,2.009s-1.961-0.776-2.098-2.009l-1.961-17.146 c-0.137-1.322,0.592-2.325,1.825-2.325h4.469C35.466,19.223,36.196,20.226,36.059,21.548z"/>
-</svg>
deleted file mode 100644
--- a/devtools/client/themes/app-manager/index.css
+++ /dev/null
@@ -1,103 +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/. */
-
-* {
-  margin: 0;
-  padding: 0;
-  box-sizing: border-box;
-  font-family: Lucida Grande, Helvetica, Helvetica Neue, sans-serif;
-}
-
-#tabs {
-  box-shadow: inset -4px 0 0 rgba(0,0,0,0.3);
-  background: #252C33;
-}
-
-#toolbox-tabs {
-  overflow-y: auto;
-}
-
-.button {
-  width: 80px;
-  height: 85px;
-  padding-bottom: 5px;
-  -moz-appearance: none;
-  border: none;
-  border-bottom: 1px solid #121214;
-  background-color: transparent;
-  color: #B5B8BB;
-  cursor: pointer;
-  text-align: center;
-  -moz-box-align: end;
-  font-size: 10px;
-  text-shadow: 0 1px 0 #333;
-  color: #9FA6AD;
-}
-
-.button:first-child {
-  border-top: none;
-}
-
-.button[selected] {
-  box-shadow: inset -4px 0 0 rgba(0,0,0,0.3), inset 2px 0 0 #DEFFFF, inset 3px 0 0 #8DC7E8, inset 4px 0 1px #1D6496;
-  color: #DCE8F3;
-  background-color: #1A4766;
-  border-color: #191B1E;
-}
-
-.button::-moz-focus-inner {
-  border-width: 0;
-}
-
-.panel {
-  border-width: 0;
-}
-
-.panel:not([selected="true"]) {
-  display: none;
-}
-
-.button.toolbox {
-  background-repeat: no-repeat;
-  background-position: center 15px;
-  background-size: 40px 40px;
-}
-
-.projects-button {
-  background: url('chrome://devtools/skin/themes/app-manager/images/index-icons.svg') no-repeat;
-  background-position: left -5px;
-}
-
-.projects-button[selected] {
-  background-position: right -5px;
-}
-
-.device-button {
-  background-image: url('chrome://devtools/skin/themes/app-manager/images/index-icons.svg');
-  background-position: left -85px, top left;
-  background-repeat: no-repeat, no-repeat;
-  background-size: 160px 240px, 2px 80px;
-}
-
-.device-button[selected] {
-  background-position: right -85px, top left;
-}
-
-.help-button {
-  border-bottom: 0;
-  background-image: url('chrome://devtools/skin/themes/app-manager/images/index-icons.svg');
-  background-position: left -165px, top left;
-  background-repeat: no-repeat, no-repeat;
-  background-size: 160px 240px, 2px 80px;
-}
-
-.help-button[selected] {
-  background-position: right -165px, top left;
-}
-
-#connection-footer {
-  border-width: 0;
-  height: 50px;
-  min-height: 50px;
-}
deleted file mode 100644
--- a/devtools/client/themes/app-manager/manifest-editor.inc.css
+++ /dev/null
@@ -1,82 +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/. */
-
-/* Manifest Editor overrides */
-
-.variables-view-container.manifest-editor {
-  background-color: #F5F5F5;
-  padding: 20px 2px;
-}
-
-.manifest-editor .variable-or-property:focus > .title {
-  background-color: #EDEDED;
-  color: #000;
-  border-radius: 4px;
-}
-
-.manifest-editor .variables-view-property > .title > .name {
-  color: #27406A;
-}
-
-.manifest-editor .variable-or-property > .title > label,
-.manifest-editor textbox  {
-  font-family: monospace;
-}
-
-.manifest-editor .variable-or-property > .title > .token-string {
-  color: #54BC6A;
-  font-weight: bold;
-}
-
-.manifest-editor .variable-or-property > .title > .token-boolean,
-.manifest-editor .variable-or-property > .title > .token-number {
-  color: #009BD4;
-  font-weight: bold;
-}
-
-.manifest-editor .variable-or-property > .title > .token-undefined {
-  color: #bbb;
-}
-
-.manifest-editor .variable-or-property > .title > .token-null {
-  color: #999;
-}
-
-.manifest-editor .variable-or-property > .title > .token-other {
-  color: #333;
-}
-
-.manifest-editor .variables-view-variable {
-  border-bottom: none;
-}
-
-.manifest-editor .variables-view-delete,
-.manifest-editor .variables-view-delete:hover,
-.manifest-editor .variables-view-delete:active,
-.manifest-editor .variable-or-property:focus .variables-view-delete,
-.manifest-editor .variables-view-add-property,
-.manifest-editor .variables-view-add-property:hover,
-.manifest-editor .variables-view-add-property:active,
-.manifest-editor .variable-or-property:focus .variables-view-add-property {
-  list-style-image: none;
-  -moz-image-region: initial;
-}
-
-.manifest-editor .variables-view-delete::before,
-.manifest-editor .variables-view-add-property::before {
-  width: 11px;
-  height: 11px;
-  content: "";
-  display: inline-block;
-  background-size: 11px auto;
-}
-
-.manifest-editor .variables-view-delete::before {
-  background-image: url("images/app-manager/remove.svg");
-}
-
-.manifest-editor .variables-view-add-property::before {
-  background-image: url("images/app-manager/add.svg");
-  -moz-margin-end: 2px;
-}
deleted file mode 100644
--- a/devtools/client/themes/app-manager/projects.css
+++ /dev/null
@@ -1,556 +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/. */
-
-* {
-  margin: 0;
-  padding: 0;
-  box-sizing: border-box;
-  font-size: 0.9rem;
-}
-
-html, body {
-  height: 100%;
-}
-
-template {
-  display: none;
-}
-
-body {
-  display: flex;
-  color: #333;
-  background-color: white;
-  font-family: Lucida Grande, Helvetica, Helvetica Neue, sans-serif;
-  overflow: hidden;
-}
-
-body:not(.connected) button.device-action {
-  display: none;
-}
-
-strong {
-  color: #111;
-}
-
-
-/********* SIDEBAR ***********/
-
-
-
-#sidebar {
-  display: flex;
-  flex-direction: column;
-  flex: 0 0 350px;
-  overflow: hidden;
-  z-index: 100;
-  background-color: #E9EAEB;
-  position: relative;
-  box-shadow: 3px 0 1.5px rgba(0,0,0,0.08);
-}
-
-#project-list {
-  height: 100%;
-  overflow: auto;
-}
-
-#project-list:not([projects-count="0"]) > #no-project {
-  display: none;
-}
-
-#no-project {
-  padding: 100px 20px 0;
-  font-weight: bold;
-  color: #BBB;
-  font-size: 22px;
-}
-
-
-/********* PROJECT MENU ***********/
-
-
-.project-item {
-  padding: 10px 0;
-  background-color: #F0F1F2;
-  box-shadow: inset 0 -1px 0 rgba(0,0,0,0.1);
-  color: #666;
-  line-height: 120%;
-  cursor: pointer;
-  display: flex;
-  position: relative;
-}
-
-.project-item:hover {
-  background-color: #EEE;
-}
-
-.project-item > * {
-  flex-shrink: 0;
-}
-
-.project-item.selected {
-  background-color: #46AFE3;
-}
-
-.project-item.selected strong {
-  color: #FFF;
-}
-
-.project-item.selected p,
-.project-item.selected span {
-  color: #C1DCF0;
-}
-
-.button-remove {
-  background-image: url('images/remove.svg');
-  background-size: 20px;
-  width: 20px;
-  height: 20px;
-  position: absolute;
-  right: 5px;
-  bottom: 5px;
-  visibility: hidden;
-  opacity: 0.5;
-}
-
-.project-item:hover .button-remove {
-  visibility: visible;
-}
-
-.project-item-status {
-  width: 6px;
-  margin: -10px 0;
-  border-right: 1px solid rgba(0,0,0,0.1);
-  box-shadow: inset 0 0 1px 1px rgba(255,255,255,0.2), inset 0 -1px 0 0 rgba(0,0,0,0.2);
-}
-
-.project-item-status[status="valid"] {
-  background-color: #70BF53;
-}
-
-.project-item-status[status~="warning"] {
-  background-color: #F2B33F;
-}
-
-.project-item-status[status~="error"] {
-  background-color: #ED4C62;
-}
-
-.project-item-icon {
-  width: 32px;
-  height: 32px;
-  margin: 0 10px;
-}
-
-.project-item-meta {
-  flex-grow: 1;
-  flex-shrink: 1 !important;
-}
-
-.project-item-type {
-  font-size: 10px;
-  line-height: 20px;
-  float: right;
-  padding-right: 10px;
-  color: #7597B9;
-  text-transform: uppercase;
-}
-
-.project-item-description {
-  color: #888;
-  font-size: 90%;
-}
-
-/********* ADD PROJECT ***********/
-
-#new-packaged-project {
-  box-shadow: 0 -1px 5px rgba(0,0,0,0.1);
-  background-position: calc(100% - 10px) 10px;
-}
-
-#new-packaged-project,
-#new-hosted-project {
-  background-color: #EEE;
-  border: none;
-  border-top: 1px solid #DDD;
-  padding: 10px;
-  font-weight: bold;
-}
-
-#new-packaged-project:hover,
-#new-hosted-project:hover {
-  background-color: #DDD;
-}
-
-#new-hosted-project-wrapper {
-  display: flex;
-  align-items: center;
-}
-
-#new-packaged-project,
-#new-hosted-project-click {
-  background-image: url('images/plus.svg');
-  background-size: 20px;
-  background-repeat: no-repeat;
-  cursor: pointer;
-}
-
-#new-hosted-project-click {
-  background-position: top right;
-  width: 20px;
-  height: 20px;
-  margin-left: 5px;
-}
-
-#url-input {
-  flex-grow: 1;
-  width: 90%;
-  box-shadow: none;
-  border-radius: 3px;
-  border: 1px solid #DDD;
-  padding: 4px;
-  margin-top: 4px;
-}
-
-
-/********* LENSE ***********/
-
-
-#lense {
-  height: 100%;
-  flex-grow: 1;
-  display: flex;
-  z-index: 1;
-  overflow: hidden;
-  background-color: rgb(225, 225, 225);
-  background-image: url('images/rocket.svg'), url('images/noise.png');
-  background-repeat: no-repeat, repeat;
-  background-size: 35%, auto;
-  background-position: center center, top left;
-}
-
-#lense > div {
-  display: flex;
-  flex-grow: 1;
-  flex-direction: column;
-}
-
-
-/********* PROJECT ***********/
-
-
-.project-details {
-  background-color: rgb(225, 225, 225);
-  padding: 10px;
-  line-height: 160%;
-  display: flex;
-  flex-direction: column;
-}
-
-.project-metadata {
-  flex-grow: 1;
-}
-
-.project-status {
-  display: flex;
-}
-
-.project-title {
-  flex-direction: row;
-  display: flex;
-  align-items: flex-start;
-  padding-bottom: 10px;
-  border-bottom: 1px solid #CCC;
-  margin-bottom: 10px;
-}
-
-.project-title > h1 {
-  flex-grow: 1;
-  font-size: 24px;
-}
-
-.project-location {
-  color: gray;
-  font-size: 10px;
-  cursor: pointer;
-  font-family: monospace;
-}
-
-.project-location:hover {
-  text-decoration: underline;
-}
-
-.project-header {
-  display: flex;
-  border-bottom: 1px solid #CCC;
-  margin: 10px 20px 10px 20px;
-  padding-bottom: 10px;
-}
-
-.project-icon {
-  flex-shrink: 0;
-  width: 64px;
-  height: 64px;
-  margin-right: 10px;
-}
-
-.project-location {
-  font-size: 11px;
-  color: #999;
-}
-
-.project-description {
-  font-style: italic;
-  color: #333;
-}
-
-.project-status > p {
-  text-transform: uppercase;
-  font-size: 10px;
-  padding: 2px 10px;
-  border-radius: 2px;
-  margin-top: 6px;
-  line-height: 10px;
-}
-
-.project-validation {
-  color: #FFF;
-  display: none;
-  margin-left: 10px;
-}
-
-.project-validation.valid {
-  background-color: #70BF53;
-}
-
-.project-validation.warning {
-  background-color: #F2B33F;
-}
-
-.project-validation.error {
-  background-color: #ED4C62;
-}
-
-[status="valid"] > .project-validation.valid,
-[status~="warning"] > .project-validation.warning,
-[status~="error"] > .project-validation.error {
-  display: inline;
-}
-
-.project-type {
-  display: none;
-  margin-left: 10px;
-}
-[type="hosted"] > .project-type.hosted,
-[type="packaged"] > .project-type.packaged {
-  display: inline;
-}
-
-/********* PROJECT BUTTONS ***********/
-
-
-
-.project-buttons {
-  display: flex;
-  margin-left: 20px;
-  color: #BBB;
-}
-
-.project-buttons > button {
-  margin: 0;
-  font-size: 11px;
-  border: 1px solid #CCC;
-  border-left-width: 0;
-  padding: 5px 15px;
-  cursor: pointer;
-  background: rgba(255,255,255,0.4);
-  text-transform: uppercase;
-}
-
-.project-buttons > button[disabled] {
-  background-color: transparent;
-  opacity: 0.4;
-  pointer-events: none;
-}
-
-.project-buttons > button:first-child {
-  border-left-width: 1px;
-}
-
-.project-button-debug {
-  color: #3498DB;
-}
-
-.project-button-debug:hover {
-  background-color: #3498DB;
-  color: #FFF;
-}
-
-.project-button-debug[disabled] {
-  color: #3498DB;
-}
-
-.project-button-update {
-  color: #777;
-}
-
-.project-button-update:hover {
-  background-color: #777;
-  color: #FFF;
-}
-
-.project-button-update[disabled] {
-  color: #777;
-}
-
-
-
-/********* ERRORS AND WARNINGS ***********/
-
-.project-warnings,
-.project-errors,
-.project-item-warnings,
-.project-item-errors {
-  display: none;
-}
-
-[status~="warning"] .project-item-warnings,
-[status~="error"] .project-item-errors {
-  display: inline-block;
-}
-
-[status~="warning"] > .project-warnings,
-[status~="error"] > .project-errors {
-  display: block;
-}
-
-.project-warnings,
-.project-errors {
-  margin: 20px 20px 0;
-  padding: 10px 10px;
-  font-family: monospace;
-}
-
-.project-warnings {
-  border-left: 3px solid #ECB51E;
-  background-color: rgba(236, 181, 20, 0.1);
-}
-
-.project-errors {
-  border-left: 3px solid #ED4C62;
-  background-color: rgba(237,76,98,0.1);
-}
-
-.project-item-warnings {
-  background-image: url('images/warning.svg');
-}
-
-.project-item-errors {
-  background-image: url('images/error.svg');
-  color: rgb(227, 79, 34);
-}
-
-.project-item-warnings,
-.project-item-errors {
-  background-repeat: no-repeat;
-  background-size: 12px;
-  background-position: left center;
-  margin-top: 6px;
-}
-
-.project-item-warnings > span,
-.project-item-errors > span {
-  font-size: 11px;
-  padding-left: 16px;
-  font-weight: bold;
-}
-
-
-/********* MANIFEST EDITOR ***********/
-
-.manifest-editor {
-  display: flex;
-  flex-direction: column;
-  flex-grow: 1;
-  background-color: #E1E1E1;
-}
-
-.manifest-header {
-  display: flex;
-  flex-direction: row;
-}
-
-.manifest-header > h2 {
-  font-size: 18px;
-  margin: 1em 15px 1em 30px;
-  display: none;
-}
-
-.manifest-header > button {
-  margin: 18px 0;
-  font-size: 11px;
-  border: 1px solid #CCC;
-  border-right-width: 0;
-  padding: 2px;
-  cursor: pointer;
-  background: rgba(255,255,255,0.4);
-  text-transform: uppercase;
-  display: none;
-}
-
-.manifest-header > button[disabled] {
-  background-color: transparent;
-  opacity: 0.4;
-  pointer-events: none;
-}
-
-.manifest-header > button:last-child {
-  border-right-width: 1px;
-}
-
-[type="packaged"] > .editable {
-  display: block;
-}
-
-[type="hosted"] > .viewable {
-  display: block;
-}
-
-.manifest-button-save {
-  color: #777;
-}
-
-.manifest-button-save:hover {
-  background-color: #777;
-  color: #FFF;
-}
-
-.manifest-button-save[disabled] {
-  color: #777;
-}
-
-.variables-view {
-  flex-grow: 1;
-  border: 0;
-  border-top: 5px solid #C9C9C9;
-}
-
-/* Bug 925921: Remove when the manifest editor is always on */
-
-.manifest-editor {
-  display: none;
-}
-
-.project-details {
-  flex-grow: 1;
-}
-
-#lense[manifest-editable] .manifest-editor {
-  display: flex;
-}
-
-#lense[manifest-editable] .project-details {
-  flex-grow: 0;
-}
-
-/* End blocks to remove */
--- a/devtools/client/themes/dark-theme.css
+++ b/devtools/client/themes/dark-theme.css
@@ -274,17 +274,17 @@ div.CodeMirror span.eval-text {
   cursor: pointer;
   background-position: -28px -14px;
 }
 
 .theme-twisty:-moz-focusring {
   outline-style: none;
 }
 
-.theme-twisty[open] {
+.theme-twisty[open], .theme-twisty.open  {
   background-position: -42px -14px;
 }
 
 .theme-twisty[invisible] {
   visibility: hidden;
 }
 
 .theme-checkbox {
--- a/devtools/client/themes/light-theme.css
+++ b/devtools/client/themes/light-theme.css
@@ -274,17 +274,17 @@ div.CodeMirror span.eval-text {
   cursor: pointer;
   background-position: 0 -14px;
 }
 
 .theme-twisty:-moz-focusring {
   outline-style: none;
 }
 
-.theme-twisty[open] {
+.theme-twisty[open], .theme-twisty.open {
   background-position: -14px -14px;
 }
 
 .theme-twisty[invisible] {
   visibility: hidden;
 }
 
 /* Use white twisty when next to a selected item in markup view */
--- a/devtools/client/themes/toolbars.inc.css
+++ b/devtools/client/themes/toolbars.inc.css
@@ -375,16 +375,33 @@
 
 /* The spacing is accomplished with a padding on the searchbox */
 .devtools-searchbox > .devtools-textinput,
 .devtools-searchbox > .devtools-searchinput {
   margin-left: 0;
   margin-right: 0;
 }
 
+/* Don't add 'double spacing' for inputs that are at beginning / end
+   of a toolbar (since the toolbar has it's own spacing). */
+.devtools-toolbar > .devtools-textinput:first-child,
+.devtools-toolbar > .devtools-searchinput:first-child {
+  -moz-margin-start: 0;
+}
+.devtools-toolbar > .devtools-textinput:last-child,
+.devtools-toolbar > .devtools-searchinput:last-child {
+  -moz-margin-end: 0;
+}
+.devtools-toolbar > .devtools-searchbox:first-child {
+  -moz-padding-start: 0;
+}
+.devtools-toolbar > .devtools-searchbox:last-child {
+  -moz-padding-end: 0;
+}
+
 .devtools-rule-searchbox {
   -moz-box-flex: 1;
   padding-right: 23px;
   width: 100%;
   font: inherit;
 }
 
 .devtools-rule-searchbox[filled] {
--- a/devtools/client/themes/webconsole.css
+++ b/devtools/client/themes/webconsole.css
@@ -375,16 +375,19 @@ a {
   background-color: transparent;
 }
 
 .jsterm-complete-node {
   color: var(--theme-comment);
 }
 
 .jsterm-input-node {
+  /* Always allow scrolling on input - it auto expands in js by setting height,
+     but don't want it to get bigger than the window. 24px = toolbar height. */
+  max-height: calc(90vh - 24px);
   background-image: -moz-image-rect(url("chrome://devtools/skin/themes/images/commandline-icon.png"), 0, 32, 16, 16);
   background-repeat: no-repeat;
   background-size: 16px 16px;
   color: var(--theme-content-color1);
 }
 
 @media (min-resolution: 1.1dppx) {
   .jsterm-input-node {
--- a/devtools/client/themes/widgets.css
+++ b/devtools/client/themes/widgets.css
@@ -1512,10 +1512,8 @@
   background-image: url(chrome://devtools/skin/themes/images/filetypes/dir-open.svg);
 }
 
 .tree-widget-item[type="url"]::after {
   background-image: url(chrome://devtools/skin/themes/images/filetypes/globe.svg);
   background-size: auto 18px;
   width: 18px;
 }
-
-%include app-manager/manifest-editor.inc.css
--- a/devtools/client/webconsole/test/browser_webconsole_bug_632347_iterators_generators.js
+++ b/devtools/client/webconsole/test/browser_webconsole_bug_632347_iterators_generators.js
@@ -12,17 +12,17 @@ function test() {
   requestLongerTimeout(6);
 
   loadTab(TEST_URI).then(() => {
     openConsole().then(consoleOpened);
   });
 }
 
 function consoleOpened(HUD) {
-  let {JSPropertyProvider} = require("devtools/shared/webconsole/utils");
+  let {JSPropertyProvider} = require("devtools/shared/webconsole/js-property-provider");
 
   let tmp = Cu.import("resource://gre/modules/jsdebugger.jsm", {});
   tmp.addDebuggerToGlobal(tmp);
   let dbg = new tmp.Debugger();
 
   let jsterm = HUD.jsterm;
   let win = content.wrappedJSObject;
   let dbgWindow = dbg.makeGlobalObjectReference(win);
--- a/devtools/client/webconsole/test/browser_webconsole_property_provider.js
+++ b/devtools/client/webconsole/test/browser_webconsole_property_provider.js
@@ -11,17 +11,17 @@
 const TEST_URI = "data:text/html;charset=utf8,<p>test the JS property provider";
 
 function test() {
   loadTab(TEST_URI).then(testPropertyProvider);
 }
 
 function testPropertyProvider({browser}) {
   browser.removeEventListener("load", testPropertyProvider, true);
-  let {JSPropertyProvider} = require("devtools/shared/webconsole/utils");
+  let {JSPropertyProvider} = require("devtools/shared/webconsole/js-property-provider");
 
   let tmp = Cu.import("resource://gre/modules/jsdebugger.jsm", {});
   tmp.addDebuggerToGlobal(tmp);
   let dbg = new tmp.Debugger();
   let dbgWindow = dbg.makeGlobalObjectReference(content);
 
   let completion = JSPropertyProvider(dbgWindow, null, "thisIsNotDefined");
   is(completion.matches.length, 0, "no match for 'thisIsNotDefined");
--- a/devtools/client/webconsole/webconsole.js
+++ b/devtools/client/webconsole/webconsole.js
@@ -5028,23 +5028,27 @@ WebConsoleConnectionProxy.prototype = {
       this._connectTimer.cancel();
       this._connectTimer = null;
     }, () => {
       this._connectTimer = null;
     });
 
     let client = this.client = this.target.client;
 
-    client.addListener("logMessage", this._onLogMessage);
-    client.addListener("pageError", this._onPageError);
-    client.addListener("consoleAPICall", this._onConsoleAPICall);
-    client.addListener("fileActivity", this._onFileActivity);
-    client.addListener("reflowActivity", this._onReflowActivity);
-    client.addListener("serverLogCall", this._onServerLogCall);
-    client.addListener("lastPrivateContextExited", this._onLastPrivateContextExited);
+    if (this.target.isWorkerTarget) {
+      // XXXworkers: Not Console API yet inside of workers (Bug 1209353).
+    } else {
+      client.addListener("logMessage", this._onLogMessage);
+      client.addListener("pageError", this._onPageError);
+      client.addListener("consoleAPICall", this._onConsoleAPICall);
+      client.addListener("fileActivity", this._onFileActivity);
+      client.addListener("reflowActivity", this._onReflowActivity);
+      client.addListener("serverLogCall", this._onServerLogCall);
+      client.addListener("lastPrivateContextExited", this._onLastPrivateContextExited);
+    }
     this.target.on("will-navigate", this._onTabNavigated);
     this.target.on("navigate", this._onTabNavigated);
 
     this._consoleActor = this.target.form.consoleActor;
     if (this.target.isTabActor) {
       let tab = this.target.form;
       this.owner.onLocationChange(tab.url, tab.title);
     }
--- a/devtools/server/actors/webbrowser.js
+++ b/devtools/server/actors/webbrowser.js
@@ -7,17 +7,17 @@
 "use strict";
 
 var { Ci, Cu } = require("chrome");
 var Services = require("Services");
 var promise = require("promise");
 var { ActorPool, createExtraActors, appendExtraActors } = require("devtools/server/actors/common");
 var { DebuggerServer } = require("devtools/server/main");
 var DevToolsUtils = require("devtools/shared/DevToolsUtils");
-var { dbg_assert } = DevToolsUtils;
+var { assert, dbg_assert } = DevToolsUtils;
 var { TabSources } = require("./utils/TabSources");
 var makeDebugger = require("./utils/make-debugger");
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
 loader.lazyRequireGetter(this, "RootActor", "devtools/server/actors/root", true);
 loader.lazyRequireGetter(this, "ThreadActor", "devtools/server/actors/script", true);
 loader.lazyRequireGetter(this, "unwrapDebuggerObjectGlobal", "devtools/server/actors/script", true);
@@ -905,19 +905,19 @@ TabActor.prototype = {
    * calling |form| below.  It can be used to do any async work that may be
    * needed to assemble the form.
    */
   update: function() {
     return promise.resolve(this);
   },
 
   form: function BTA_form() {
-    dbg_assert(!this.exited,
+    assert(!this.exited,
                "form() shouldn't be called on exited browser actor.");
-    dbg_assert(this.actorID,
+    assert(this.actorID,
                "tab should have an actorID.");
 
     let response = {
       actor: this.actorID
     };
 
     // On xpcshell we are using tabactor even if there is no valid document.
     // Actors like chrome debugger can work.
@@ -1024,17 +1024,17 @@ TabActor.prototype = {
    * Does the actual work of attaching to a tab.
    */
   _attach: function BTA_attach() {
     if (this._attached) {
       return;
     }
 
     // Create a pool for tab-lifetime actors.
-    dbg_assert(!this._tabPool, "Shouldn't have a tab pool if we weren't attached.");
+    assert(!this._tabPool, "Shouldn't have a tab pool if we weren't attached.");
     this._tabPool = new ActorPool(this.conn);
     this.conn.addActorPool(this._tabPool);
 
     // ... and a pool for context-lifetime actors.
     this._pushContext();
 
     // on xpcshell, there is no document
     if (this.window) {
@@ -1273,31 +1273,31 @@ TabActor.prototype = {
                    });
   },
 
   /**
    * Creates a thread actor and a pool for context-lifetime actors. It then sets
    * up the content window for debugging.
    */
   _pushContext: function BTA_pushContext() {
-    dbg_assert(!this._contextPool, "Can't push multiple contexts");
+    assert(!this._contextPool, "Can't push multiple contexts");
 
     this._contextPool = new ActorPool(this.conn);
     this.conn.addActorPool(this._contextPool);
 
     this.threadActor = new ThreadActor(this, this.window);
     this._contextPool.addActor(this.threadActor);
   },
 
   /**
    * Exits the current thread actor and removes the context-lifetime actor pool.
    * The content window is no longer being debugged after this call.
    */
   _popContext: function BTA_popContext() {
-    dbg_assert(!!this._contextPool, "No context to pop.");
+    assert(!!this._contextPool, "No context to pop.");
 
     this.conn.removeActorPool(this._contextPool);
     this._contextPool = null;
     this.threadActor.exit();
     this.threadActor = null;
     this._sources = null;
   },
 
--- a/devtools/server/actors/webconsole.js
+++ b/devtools/server/actors/webconsole.js
@@ -1,55 +1,43 @@
 /* -*- js-indent-level: 2; indent-tabs-mode: nil -*- */
 /* vim: set ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
+const Services = require("Services");
 const { Cc, Ci, Cu } = require("chrome");
 const { DebuggerServer, ActorPool } = require("devtools/server/main");
 const { EnvironmentActor, ThreadActor } = require("devtools/server/actors/script");
 const { ObjectActor, LongStringActor, createValueGrip, stringIsLong } = require("devtools/server/actors/object");
 const DevToolsUtils = require("devtools/shared/DevToolsUtils");
 
-Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-
-XPCOMUtils.defineLazyModuleGetter(this, "Services",
-                                  "resource://gre/modules/Services.jsm");
-XPCOMUtils.defineLazyGetter(this, "NetworkMonitor", () => {
-  return require("devtools/shared/webconsole/network-monitor")
-         .NetworkMonitor;
-});
-XPCOMUtils.defineLazyGetter(this, "NetworkMonitorChild", () => {
-  return require("devtools/shared/webconsole/network-monitor")
-         .NetworkMonitorChild;
-});
-XPCOMUtils.defineLazyGetter(this, "ConsoleProgressListener", () => {
-  return require("devtools/shared/webconsole/network-monitor")
-         .ConsoleProgressListener;
-});
-XPCOMUtils.defineLazyGetter(this, "events", () => {
-  return require("sdk/event/core");
-});
-XPCOMUtils.defineLazyGetter(this, "ServerLoggingListener", () => {
-  return require("devtools/shared/webconsole/server-logger")
-         .ServerLoggingListener;
-});
+loader.lazyRequireGetter(this, "NetworkMonitor", "devtools/shared/webconsole/network-monitor", true);
+loader.lazyRequireGetter(this, "NetworkMonitorChild", "devtools/shared/webconsole/network-monitor", true);
+loader.lazyRequireGetter(this, "ConsoleProgressListener", "devtools/shared/webconsole/network-monitor", true);
+loader.lazyRequireGetter(this, "events", "sdk/event/core");
+loader.lazyRequireGetter(this, "ServerLoggingListener", "devtools/shared/webconsole/server-logger", true);
+loader.lazyRequireGetter(this, "JSPropertyProvider", "devtools/shared/webconsole/js-property-provider", true);
 
 for (let name of ["WebConsoleUtils", "ConsoleServiceListener",
-    "ConsoleAPIListener", "addWebConsoleCommands", "JSPropertyProvider",
+    "ConsoleAPIListener", "addWebConsoleCommands",
     "ConsoleReflowListener", "CONSOLE_WORKER_IDS"]) {
   Object.defineProperty(this, name, {
     get: function(prop) {
       if (prop == "WebConsoleUtils") {
         prop = "Utils";
       }
-      return require("devtools/shared/webconsole/utils")[prop];
+      if (isWorker) {
+        return require("devtools/shared/webconsole/worker-utils")[prop];
+      } else {
+        return require("devtools/shared/webconsole/utils")[prop];
+      }
     }.bind(null, name),
     configurable: true,
     enumerable: true
   });
 }
 
 /**
  * The WebConsoleActor implements capabilities needed for the Web Console
@@ -551,16 +539,21 @@ WebConsoleActor.prototype =
    *
    * @param object aRequest
    *        The JSON request object received from the Web Console client.
    * @return object
    *         The response object which holds the startedListeners array.
    */
   onStartListeners: function WCA_onStartListeners(aRequest)
   {
+    // XXXworkers: Not handling the Console API yet for workers (Bug 1209353).
+    if (isWorker) {
+       aRequest.listeners = [];
+    }
+
     let startedListeners = [];
     let window = !this.parentActor.isRootActor ? this.window : null;
     let appId = null;
     let messageManager = null;
 
     if (this._parentIsContentActor) {
       appId = this.parentActor.docShell.appId;
       messageManager = this.parentActor.messageManager;
@@ -844,18 +837,22 @@ WebConsoleActor.prototype =
     if (evalResult) {
       if ("return" in evalResult) {
         result = evalResult.return;
       } else if ("yield" in evalResult) {
         result = evalResult.yield;
       } else if ("throw" in evalResult) {
         let error = evalResult.throw;
         errorGrip = this.createValueGrip(error);
-        errorMessage = error && (typeof error === "object")
-          ? error.unsafeDereference().toString()
+        // XXXworkers: Calling unsafeDereference() returns an object with no
+        // toString method in workers. See Bug 1215120.
+        let unsafeDereference = error && (typeof error === "object") &&
+                                error.unsafeDereference();
+        errorMessage = unsafeDereference && unsafeDereference.toString
+          ? unsafeDereference.toString()
           : "" + error;
       }
     }
 
     // If a value is encountered that the debugger server doesn't support yet,
     // the console should remain functional.
     let resultGrip;
     try {
@@ -894,18 +891,18 @@ WebConsoleActor.prototype =
     // This is the case of the paused debugger
     if (frameActorId) {
       let frameActor = this.conn.getActor(frameActorId);
       if (frameActor) {
         let frame = frameActor.frame;
         environment = frame.environment;
       }
       else {
-        Cu.reportError("Web Console Actor: the frame actor was not found: " +
-                       frameActorId);
+        DevToolsUtils.reportException("onAutocomplete",
+          Error("The frame actor was not found: " + frameActorId));
       }
     }
     // This is the general case (non-paused debugger)
     else {
       dbgObject = this.dbg.makeGlobalObjectReference(this.evalWindow);
     }
 
     let result = JSPropertyProvider(dbgObject, environment, aRequest.text,
@@ -1038,19 +1035,23 @@ WebConsoleActor.prototype =
       // helpers like cd(), where we users sometimes want to pass a cross-origin
       // window. To circumvent this restriction, we use exportFunction along
       // with a special option designed for this purpose. See bug 1051224.
       obj[name] =
         Cu.exportFunction(obj[name], evalWindow, { allowCrossOriginArguments: true });
     }
     for (let name in helpers.sandbox) {
       let desc = Object.getOwnPropertyDescriptor(helpers.sandbox, name);
-      maybeExport(desc, 'get');
-      maybeExport(desc, 'set');
-      maybeExport(desc, 'value');
+
+      // Workers don't have access to Cu so won't be able to exportFunction.
+      if (!isWorker) {
+        maybeExport(desc, 'get');
+        maybeExport(desc, 'set');
+        maybeExport(desc, 'value');
+      }
       if (desc.value) {
         // Make sure the helpers can be used during eval.
         desc.value = aDebuggerGlobal.makeDebuggeeValue(desc.value);
       }
       Object.defineProperty(helpers.sandbox, name, desc);
     }
     return helpers;
   },
@@ -1133,18 +1134,18 @@ WebConsoleActor.prototype =
     // Find the Debugger.Frame of the given FrameActor.
     let frame = null, frameActor = null;
     if (aOptions.frameActor) {
       frameActor = this.conn.getActor(aOptions.frameActor);
       if (frameActor) {
         frame = frameActor.frame;
       }
       else {
-        Cu.reportError("Web Console Actor: the frame actor was not found: " +
-                       aOptions.frameActor);
+        DevToolsUtils.reportException("evalWithDebugger",
+          Error("The frame actor was not found: " + aOptions.frameActor));
       }
     }
 
     // If we've been given a frame actor in whose scope we should evaluate the
     // expression, be sure to use that frame's Debugger (that is, the JavaScript
     // debugger's Debugger) for the whole operation, not the console's Debugger.
     // (One Debugger will treat a different Debugger's Debugger.Object instances
     // as ordinary objects, not as references to be followed, so mixing
--- a/devtools/server/actors/worker.js
+++ b/devtools/server/actors/worker.js
@@ -37,16 +37,17 @@ function WorkerActor(dbg) {
 }
 
 WorkerActor.prototype = {
   actorPrefix: "worker",
 
   form: function () {
     return {
       actor: this.actorID,
+      consoleActor: this._consoleActor,