Merge m-c to m-i
authorPhil Ringnalda <philringnalda@gmail.com>
Sat, 15 Mar 2014 12:32:04 -0700
changeset 173780 ad2a0cab20ac
parent 173779 7ed8689acdfa (current diff)
parent 173770 ba4c5a81d56a (diff)
child 173781 44bf014423b7
push id26420
push userkhuey@mozilla.com
push date2014-03-16 00:40 +0000
treeherdermozilla-central@e182de48f628 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone30.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 m-c to m-i
toolkit/devtools/gcli/source/lib/gcli/demo.js
toolkit/devtools/gcli/source/lib/gcli/ui/terminal.css
toolkit/devtools/gcli/source/lib/gcli/ui/terminal.html
toolkit/devtools/gcli/source/lib/gcli/ui/terminal.js
toolkit/devtools/gcli/source/mozilla/gcli/index.js
toolkit/devtools/gcli/source/mozilla/gcli/mozui/completer.js
toolkit/devtools/gcli/source/mozilla/gcli/mozui/ffdisplay.js
toolkit/devtools/gcli/source/mozilla/gcli/mozui/inputter.js
toolkit/devtools/gcli/source/mozilla/gcli/mozui/tooltip.js
toolkit/devtools/gcli/source/mozilla/gcli/settings.js
toolkit/devtools/gcli/source/mozilla/gcli/ui/menu.html
toolkit/devtools/gcli/source/mozilla/gcli/util/domtemplate.js
toolkit/devtools/gcli/source/mozilla/gcli/util/filesystem.js
toolkit/devtools/gcli/source/mozilla/gcli/util/host.js
toolkit/devtools/gcli/source/mozilla/gcli/util/l10n.js
toolkit/devtools/gcli/source/mozilla/gcli/util/promise.js
--- 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="1ad48c4be51b279f7f63c1a13025b52fe087d231">
     <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="f09ec7d9d0bb7c40998ddb6b5bf397e578add541"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="70978988d048737b1379f5452a679429dadcd35f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="d11f524d00cacf5ba0dfbf25e4aa2158b1c3a036"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="456499c44d1ef39b602ea02e9ed460b6aab85b44"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="cb16958e41105d7c551d9941f522db97b8312538"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="485846b2a40d8ac7d6c1c5f8af6d15b0c10af19d"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
--- 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="15d69a6789c638709911507f74d25c0425963636">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="f09ec7d9d0bb7c40998ddb6b5bf397e578add541"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="70978988d048737b1379f5452a679429dadcd35f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="cb16958e41105d7c551d9941f522db97b8312538"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="485846b2a40d8ac7d6c1c5f8af6d15b0c10af19d"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.1" path="prebuilts/clang/linux-x86/3.1" revision="5c45f43419d5582949284eee9cef0c43d866e03b"/>
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.2" path="prebuilts/clang/linux-x86/3.2" revision="3748b4168e7bd8d46457d4b6786003bc6a5223ce"/>
--- 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="a9e08b91e9cd1f0930f16cfc49ec72f63575d5fe">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="f09ec7d9d0bb7c40998ddb6b5bf397e578add541"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="70978988d048737b1379f5452a679429dadcd35f"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="cb16958e41105d7c551d9941f522db97b8312538"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="485846b2a40d8ac7d6c1c5f8af6d15b0c10af19d"/>
   <!-- Stock Android things -->
--- 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="1ad48c4be51b279f7f63c1a13025b52fe087d231">
     <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="f09ec7d9d0bb7c40998ddb6b5bf397e578add541"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="70978988d048737b1379f5452a679429dadcd35f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="d11f524d00cacf5ba0dfbf25e4aa2158b1c3a036"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="456499c44d1ef39b602ea02e9ed460b6aab85b44"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="cb16958e41105d7c551d9941f522db97b8312538"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="485846b2a40d8ac7d6c1c5f8af6d15b0c10af19d"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
--- a/b2g/config/gaia.json
+++ b/b2g/config/gaia.json
@@ -1,9 +1,9 @@
 {
     "git": {
         "remote": "", 
         "branch": "", 
         "revision": ""
     }, 
-    "revision": "a7c17d6cce9c60631e386b75885199a59b555a43", 
+    "revision": "1b537b86025c437cd428abbc2996f376f3b78799", 
     "repo_path": "/integration/gaia-central"
 }
--- a/b2g/config/hamachi/sources.xml
+++ b/b2g/config/hamachi/sources.xml
@@ -12,17 +12,17 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="b2g/ics_strawberry" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="1ad48c4be51b279f7f63c1a13025b52fe087d231">
     <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="f09ec7d9d0bb7c40998ddb6b5bf397e578add541"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="70978988d048737b1379f5452a679429dadcd35f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1f6a1fe07f81c5bc5e1d079c9b60f7f78ca2bf4f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="cb16958e41105d7c551d9941f522db97b8312538"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="485846b2a40d8ac7d6c1c5f8af6d15b0c10af19d"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
   <project name="platform/bionic" path="bionic" revision="d2eb6c7b6e1bc7643c17df2d9d9bcb1704d0b9ab"/>
--- a/b2g/config/helix/sources.xml
+++ b/b2g/config/helix/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <default remote="caf" revision="b2g/ics_strawberry" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="1ad48c4be51b279f7f63c1a13025b52fe087d231">
     <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="f09ec7d9d0bb7c40998ddb6b5bf397e578add541"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="70978988d048737b1379f5452a679429dadcd35f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1f6a1fe07f81c5bc5e1d079c9b60f7f78ca2bf4f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="cb16958e41105d7c551d9941f522db97b8312538"/>
   <project name="gonk-patches" path="patches" remote="b2g" revision="223a2421006e8f5da33f516f6891c87cae86b0f6"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
   <project name="platform/bionic" path="bionic" revision="d2eb6c7b6e1bc7643c17df2d9d9bcb1704d0b9ab"/>
--- a/b2g/config/inari/sources.xml
+++ b/b2g/config/inari/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="ics_chocolate_rb4.2" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="1ad48c4be51b279f7f63c1a13025b52fe087d231">
     <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="f09ec7d9d0bb7c40998ddb6b5bf397e578add541"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="70978988d048737b1379f5452a679429dadcd35f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1f6a1fe07f81c5bc5e1d079c9b60f7f78ca2bf4f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="cb16958e41105d7c551d9941f522db97b8312538"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="485846b2a40d8ac7d6c1c5f8af6d15b0c10af19d"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
   <project name="platform/bionic" path="bionic" revision="cd5dfce80bc3f0139a56b58aca633202ccaee7f8"/>
--- a/b2g/config/leo/sources.xml
+++ b/b2g/config/leo/sources.xml
@@ -12,17 +12,17 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="b2g/ics_strawberry" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="1ad48c4be51b279f7f63c1a13025b52fe087d231">
     <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="f09ec7d9d0bb7c40998ddb6b5bf397e578add541"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="70978988d048737b1379f5452a679429dadcd35f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1f6a1fe07f81c5bc5e1d079c9b60f7f78ca2bf4f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="cb16958e41105d7c551d9941f522db97b8312538"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="485846b2a40d8ac7d6c1c5f8af6d15b0c10af19d"/>
   <project name="gonk-patches" path="patches" remote="b2g" revision="223a2421006e8f5da33f516f6891c87cae86b0f6"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
--- a/b2g/config/mako/sources.xml
+++ b/b2g/config/mako/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="15d69a6789c638709911507f74d25c0425963636">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="f09ec7d9d0bb7c40998ddb6b5bf397e578add541"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="70978988d048737b1379f5452a679429dadcd35f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="cb16958e41105d7c551d9941f522db97b8312538"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="485846b2a40d8ac7d6c1c5f8af6d15b0c10af19d"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.1" path="prebuilts/clang/linux-x86/3.1" revision="5c45f43419d5582949284eee9cef0c43d866e03b"/>
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.2" path="prebuilts/clang/linux-x86/3.2" revision="3748b4168e7bd8d46457d4b6786003bc6a5223ce"/>
--- a/b2g/config/wasabi/sources.xml
+++ b/b2g/config/wasabi/sources.xml
@@ -12,17 +12,17 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="ics_chocolate_rb4.2" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="1ad48c4be51b279f7f63c1a13025b52fe087d231">
     <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="f09ec7d9d0bb7c40998ddb6b5bf397e578add541"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="70978988d048737b1379f5452a679429dadcd35f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1f6a1fe07f81c5bc5e1d079c9b60f7f78ca2bf4f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="cb16958e41105d7c551d9941f522db97b8312538"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="485846b2a40d8ac7d6c1c5f8af6d15b0c10af19d"/>
   <project name="gonk-patches" path="patches" remote="b2g" revision="223a2421006e8f5da33f516f6891c87cae86b0f6"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -1204,17 +1204,17 @@ pref("devtools.styleeditor.autocompletio
 
 // Enable the Shader Editor.
 pref("devtools.shadereditor.enabled", false);
 
 // Enable tools for Chrome development.
 pref("devtools.chrome.enabled", false);
 
 // Default theme ("dark" or "light")
-pref("devtools.theme", "dark");
+pref("devtools.theme", "light");
 
 // Display the introductory text
 pref("devtools.gcli.hideIntro", false);
 
 // How eager are we to show help: never=1, sometimes=2, always=3
 pref("devtools.gcli.eagerHelper", 2);
 
 // Remember the Web Console filters
--- a/browser/base/content/aboutaccounts/aboutaccounts.css
+++ b/browser/base/content/aboutaccounts/aboutaccounts.css
@@ -1,50 +1,14 @@
-* {
-  margin: 0;
-  padding: 0;
-}
-
 html, body {
   height: 100%;
 }
 
-#content {
-  width: 100%;
-  height: 100%;
-  border: 0;
-  display: flex;
-}
-
 #remote {
   width: 100%;
   height: 100%;
   border: 0;
   display: none;
 }
 
 #manage, #intro, #stage {
   display: none;
 }
-
-#stage {
-  opacity: 1;
-}
-
-.graphic-sync-intro {
-  background-image: url(images/graphic_sync_intro.png);
-}
-
-.description, .button-row {
-  margin-top: 30px;
-}
-
-/* Retina */
-@media
-only screen and (   min--moz-device-pixel-ratio: 2),
-only screen and (   -moz-min-device-pixel-ratio: 2),
-only screen and (        min-device-pixel-ratio: 2),
-only screen and (                min-resolution: 192dpi),
-only screen and (                min-resolution: 2dppx) {
-  .graphic-sync-intro {
-    background-image: url(images/graphic_sync_intro@2x.png);
-  }
-}
--- a/browser/base/content/aboutaccounts/aboutaccounts.js
+++ b/browser/base/content/aboutaccounts/aboutaccounts.js
@@ -76,16 +76,25 @@ function promptForRelink(acctName) {
                     (ps.BUTTON_POS_1 * ps.BUTTON_TITLE_CANCEL) +
                     ps.BUTTON_POS_1_DEFAULT;
   let pressed = Services.prompt.confirmEx(window, title, body, buttonFlags,
                                      continueLabel, null, null, null,
                                      {});
   return pressed == 0; // 0 is the "continue" button
 }
 
+// If the last fxa account used for sync isn't this account, we display
+// a modal dialog checking they really really want to do this...
+// (This is sync-specific, so ideally would be in sync's identity module,
+// but it's a little more seamless to do here, and sync is currently the
+// only fxa consumer, so...
+function shouldAllowRelink(acctName) {
+  return !needRelinkWarning(acctName) || promptForRelink(acctName);
+}
+
 let wrapper = {
   iframe: null,
 
   init: function (url=null) {
     let weave = Cc["@mozilla.org/weave/service;1"]
                   .getService(Ci.nsISupports)
                   .wrappedJSObject;
 
@@ -127,30 +136,30 @@ let wrapper = {
   onLogin: function (accountData) {
     log("Received: 'login'. Data:" + JSON.stringify(accountData));
 
     if (accountData.customizeSync) {
       Services.prefs.setBoolPref(PREF_SYNC_SHOW_CUSTOMIZATION, true);
       delete accountData.customizeSync;
     }
 
-    // If the last fxa account used for sync isn't this account, we display
-    // a modal dialog checking they really really want to do this...
-    // (This is sync-specific, so ideally would be in sync's identity module,
-    // but it's a little more seamless to do here, and sync is currently the
-    // only fxa consumer, so...
+    // We need to confirm a relink - see shouldAllowRelink for more
     let newAccountEmail = accountData.email;
-    if (needRelinkWarning(newAccountEmail) && !promptForRelink(newAccountEmail)) {
+    // The hosted code may have already checked for the relink situation
+    // by sending the can_link_account command. If it did, then
+    // it will indicate we don't need to ask twice.
+    if (!accountData.verifiedCanLinkAccount && !shouldAllowRelink(newAccountEmail)) {
       // we need to tell the page we successfully received the message, but
       // then bail without telling fxAccounts
       this.injectData("message", { status: "login" });
-      // and reload the page or else it remains in a "signed in" state.
-      window.location.reload();
+      // and re-init the page by navigating to about:accounts
+      window.location = "about:accounts";
       return;
     }
+    delete accountData.verifiedCanLinkAccount;
 
     // Remember who it was so we can log out next time.
     setPreviousAccountNameHash(newAccountEmail);
 
     // A sync-specific hack - we want to ensure sync has been initialized
     // before we set the signed-in user.
     let xps = Cc["@mozilla.org/weave/service;1"]
               .getService(Ci.nsISupports)
@@ -170,16 +179,22 @@ let wrapper = {
       // the verification completes (the browser will begin syncing, but
       // won't notify the user). If the email has already been verified,
       // the jelly will say "Welcome! You are successfully signed in as
       // EMAIL", but it won't then say "syncing started".
     }, (err) => this.injectData("message", { status: "error", error: err })
     );
   },
 
+  onCanLinkAccount: function(accountData) {
+    // We need to confirm a relink - see shouldAllowRelink for more
+    let ok = shouldAllowRelink(accountData.email);
+    this.injectData("message", { status: "can_link_account", data: { ok: ok } });
+  },
+
   /**
    * onSessionStatus sends the currently signed in user's credentials
    * to the jelly.
    */
   onSessionStatus: function () {
     log("Received: 'session_status'.");
 
     fxAccounts.getSignedInUser().then(
@@ -203,16 +218,19 @@ let wrapper = {
   handleRemoteCommand: function (evt) {
     log('command: ' + evt.detail.command);
     let data = evt.detail.data;
 
     switch (evt.detail.command) {
       case "login":
         this.onLogin(data);
         break;
+      case "can_link_account":
+        this.onCanLinkAccount(data);
+        break;
       case "session_status":
         this.onSessionStatus(data);
         break;
       case "sign_out":
         this.onSignOut(data);
         break;
       default:
         log("Unexpected remote command received: " + evt.detail.command + ". Ignoring command.");
--- a/browser/base/content/aboutaccounts/aboutaccounts.xhtml
+++ b/browser/base/content/aboutaccounts/aboutaccounts.xhtml
@@ -34,46 +34,44 @@
      href="chrome://browser/content/aboutaccounts/aboutaccounts.css"
      type="text/css" />
   </head>
   <body>
     <div id="stage">
 
       <div id="manage">
         <header>
-          <h1><span>&syncBrand.fullName.label;</span></h1>
-          <h2><span>&aboutAccounts.welcome;</span></h2>
+          <h1>&aboutAccounts.welcome;</h1>
         </header>
 
         <section>
             <div class="graphic graphic-sync-intro"> </div>
 
             <div class="button-row">
               <a class="button" href="#" onclick="openPrefs()">&aboutAccountsConfig.manage.label;</a>
             </div>
         </section>
       </div>
 
       <div id="intro">
         <header>
-          <h1><span>&syncBrand.fullName.label;</span></h1>
-          <h2><span>&aboutAccounts.welcome;</span></h2>
+          <h1>&aboutAccounts.welcome;</h1>
         </header>
 
         <section>
             <div class="graphic graphic-sync-intro"> </div>
 
             <div class="description">&aboutAccountsConfig.description;</div>
 
             <div class="button-row">
               <a class="button" href="#" onclick="getStarted()">&aboutAccountsConfig.startButton.label;</a>
             </div>
 
             <div class="links">
-              <a id="oldsync" href="#" onclick="handleOldSync();">&aboutAccountsConfig.useOldSync.label;</a>
+              <a id="oldsync" class="no-underline" href="#" onclick="handleOldSync();">&aboutAccountsConfig.useOldSync.label;</a>
             </div>
         </section>
       </div>
 
     </div>
 
     <iframe mozframetype="content" id="remote" />
 
index 983d8a654a4d06dd66e8dee5888aa93f1235dd25..0c18d6fd8c45bdf5e5d58b3fba2f6b0ceb32f53b
GIT binary patch
literal 9304
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
XG~b3D;8$S@_Fu?JDNFtqHx2tAuQ1M>
index dba656bd09b8129980ba9bf9f238f23c7e59c345..a36b4f038d72a7c8a966a8fceb297a13872bcdea
GIT binary patch
literal 19655
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%0AAV?`1UFj=&^A8WS
zb*p|&IO%m#+mRC6E;mOxQ^YBmrVDB91t!}xxXXPS`p-vY0u2n=ewCt^T~ns$?L)S)
zMjR7zcD=R)0sjTeq6RXWkE?<X3gpvFmY)QXC^2M3saN=o>G+6?9aXK!G3tx{1RyVS
z)ytdru-xliJ6l`9Z5w8UtqWMD+H-kz>o(dFzpODM4}OD?0XUziE_tTfePyf5z>{u=
zH<UDtTdq)%PPdQ5b9kfRS$;dr9_^{%<&#D2gVJ&KDDVM5e5k}JOY51D_P<DREvAjX
z+je7T=?=enywM?V%>@*=l+pAWl6Ma<PFcm{=TYi16>qaS&4mK*%2AbEObB+t{^iHr
z%zi%)cGH*Zh>mLeId8T~n?RTuXks3#egrb*CE_k@g})`%GD?(cI3Xr6$_IjG(CM_W
z!%meKu26eiPPns_iA&V~kw4vAc>V@Zy_Gi!hXl+;KYlPj<8t_3nG#U)2~57+(C9V4
zlPC%r3bWh>VegFwr7Y2t*JY0ci6;jyyIdC?#t!6B1ajk{`LVm0q2T?c&k}f>ISMq|
z!EI$Uva`F+2upjpjYSU2R#X3^AD+~g|1OEtWZ)jf$*}Y;43UwO_L8E<v<~^YA6)pr
z&YLI$PVAel7`>O9b<;O=$BtdKHpo{Bf3dsY`ybO=@`P#8$I0W!7hKM!NSEiN!$o_D
zDZn;)kIR|eXnIrEoMJJd!cZ4Xbfc=jNo9h1|Kr&IwAwnOq2DWS4DS9{OoW|i*HLeF
z{EXkLlY=@DSm=*lbL6lBoG(1tF1n1dQYM|Z|AJ$E8HP|sD>l#rWI{(V9kK6cJ^!<M
z<b9Ew9(fv06T@Kb!9F*?Z?MkIs?E<oxw>-uRJ5V?{igJ5Wu85BJ26#(owDDUI$l{P
zk9p|-{*b{w^IXRR9yB2He}zZX`uHwPPh^K9iVO2sySv@wY_4s5i2#*v!+~Z7rI)6a
zyLt!r<Ir?CHsGKsLOGm<1E10Jr^S{f7%aM91Ie<WNb^Si6VJ-v!<;a@;UTBdSw;D(
z692vs|3misW^Uhdeg@|x+H~=oZcZYK#}kPn%-2adA?238p^z70VQF!<b^1LUJ9=r;
z6F=~VGSgD2qTgFv=hEl`-7&?xxMz&68in`5X10iyO}itQXzxvPZqT)j&FX=%E9W&+
z<&<3kqM3hRIu0Wxv>=S(Q(a-ciDr9bElZ7kcbO}5{CuFJvHNJN6`QC1%3ZntIwVta
z`QK}g=)}ODx%tK7inu|wtHXFioW4jeo!VNjbv!m+&CUw69tKf5*xFlL8Q>N4D{2LX
zi@P>4wf>!ph?ZQKc5j&ehpr<3Lo;=q5`Dp_0Uxb=l`&h!B>1IMx0q*VG8)(n0q8Z|
zk#80gTV%7;$n0^s`ZLIJIC=igCh4?6uYJOfNGI6!3khxR$~p>`pr~*89tBE70SZYy
zv&MQ~kfZtfU7WOsL9#9=EUZ<vs$8RnQoW0`T;;HYB5?QW$Pp`#G@+aPw8EVdr`zQe
z2sUZ_bcLmA<&^2+_%dWgKJ*moJoXantwlygHco!aHZ+Tn26X-FLwuJ%=3G8(zQpDt
z)GSwa(9xJqxjk6S;)Q$Jzv%#FWeh6uu|A05aj^Yg^<ZYd+nt9BsN>$OUz0*t*Wk^~
z<xrIX<{Q1raqF!s-l_QmA>dpq5;#FE;-1SFH<tZoJVWYZZ;ll!5b6Lk{+jL)V$N(S
zGA`_`dIjQ-9-EVB>d)}hdMgzhH&X@BJL~Hhtlq_$duVkSUb)8=<=8@{a%?SiHthPr
ze*G4!Z*6V;?!kr=m_1bA@<Jmdou_g?KsV8hqp+kT+vfZlhnmBnJ1?`^TA3uT=1Ha2
zRWJ%<E&mx2f!od6NIrAHT}AtLz3}amwZd`rcoG+#@*Fq3RuF@MaI5iRi}pZeNCU7^
zTJh_0J-u-_$_2_mc(&2^BEB-nFZhjGVV_uVjI61w`?&s`gXg8f5!8%#(ILG2LB;5F
zt`SSM@stPeNv^&o`gZx2qd@6xOM#)_x@lt6VfvMc2N1L=f-CWe7#xr6Ki6_ciXS<P
zh0I7khEkUnT{_cww!8lJUe$A#)+D`6NH=T%`eyggN@B6!qGJg|pgf3;_TChHz|<S^
zU>!lEBkG;j?%^F)#ONer7@_ZCeVm74tgD^lk5)Dc5vQ45`sdB+=X$uD$f;A0Hgf-Z
z3=j0IPk%0O8ABr^M<uyVi}!h8Qv^ItvjN~T4aT+Vv)pgq@uOE(AE$^d%E|9Oh;M#m
z^}QY#9qWpJCb!-oGy)e&gm&}>z9KR72HYQN&CybX?-87zLjzfI{{8EW1UxV&`qvXZ
zHXca|1GLbp!T4LEZ)eH4Wdi(BYJv5y&AU^^ux}JOODprsotODY@^1<Pc!yi0aCgq$
zz8K<fWKR8oXH(MMbT2X<_(O_m4=EuKA>Z1==d<|Yg*n>lZZLWr*%s44{WMQ^)~?6+
zc!nU#uqMmv)Ad`pSq!C=Dh2+_oZaIk{^?FDjw{=)wC&TbTqa}{Oo)eCVP4upVHFN1
zw0r7X5)gIJh{=CRhs%nXADdI);3m#Yp*Y~UaDKn4sRJnru3q&JIG8QSI<jwx*|dYG
z76=0AeJASJ`vxS>RT{}MtpCV%=gb>k!uFe@Hg(OrMnX;07wqr5F?XNOyr;yd<Z)X$
zHPU0k@d6(@GYl{gQ1%#LJ&l!5^AX!3LS^Xi86IL41h0j?Nh8M$2`HldX>GawS=T{f
zuj6rXc{!H)OI`7ID0b_}sY(<X@9B)_6*Q$o`$lcv06YbibJjLBhlD}ZKKLaW{HfwG
zY;*2I{d8@cNArj1K+*H4fD0nxx|~PG<19lB(NxpS&<hq?1)*!cT;Cig<nUZve{{In
zhYTa(Tg%+dR&&D)`>S=zeO~3U8j;h{jnhVTnD?n!Bgo2WkAH4Jpy`sK{-}7t0A<Pq
z1`e8{KiA<<z>}9M8(<6nCoT87?Lpi{ofDqi-X4nA1DvmEjA7LvZ?(WmxRDRH)Oobr
zfR&0%c3aR!i(~gU`T)9;LOpBraC!)6zLgl;BP0ktA@W2b^kF`W!^T#TdyWR^>RR&%
zNo$nQQ=p!n<bh(~S>`^{_Y{ZJ?z{odG@SDzAt`m2%j^qht}p@OVJy&*9^BZgB*(lC
zj#Q)&{(ldbY*rvU-w6t!b%BgGSo{`d=Z4B+{A8$wtGvp~X`@xF!Nkb4`!Zv8zbrkD
z4lXGdH6c1(GO_{sgD5zP&8v1)(N3qUcU2AXRzlE9d+{fBFWu|TeiI>&@-R9*EHL6G
z6GLKMrvfPn5UuGVOjXItiedwn*>2)H4Gw=e=K`fB|Ia`xi*~zV**6&BPp9jp#N(oF
z24R+r4X~Byp?3Js5yu$8hpvMoOn46+MpA^K<KlyLC8l~VyPx(-Zsrw%T0Jeb+((&+
zj8U1GMI)#rek%3gXk`$m*@Gkt&Ik==tfz|X_$U~}CG|P>zY;rhN~IOF>>p!I>3`vc
z3d0Q|Xf(snLqY0$ut|vG<R+a8+ZnhSiOGF<kV~VxR`9>oVctSz1*N^28`x?LL!0|G
z-x`1A!Sjit-EFWmN#vl{q}-@fOV#z;XZr17GFc(PD{!Vqzi2<+ck%lK-yZz0V#y8U
ziNmNK9UVQ-D%=LF!LJz@LYzDdDnv%BIUE=ZX!~H0n(#XF(m(#mLHGq>P@t}w+t{43
zw7`CN8Jv2{J-|Ac+7@lY`Tq<j5J!G37<-%PYg(j~V`Hsu6>0u`eDs4sY)qM3=pR%M
z`w><w)g%>TL8V(?YB#oiu5hlo1UPg_5gDak6d&$2B2%IwPK-!}+$Lm$e^V5|uc2!k
zlh?SpV4qsi?Ddn+{CkQ&JjGg3G+p`R)Kc6$hj?yg)l==)!fL&^OmJ__=uR*hNq*#r
zfkqDtpjZ9-J}lFnu#34alQnmpvRW`PG^s1`s|-VU?eS1<HFkjDBBwvo20u(DedT72
z#bWR66G~P&Mh8Zse|JcBuRC#(;PzkYoVQ4cE3AB{TzAq5oA?t#l`k5x?e55-6B)2f
zo2LvIvs>^uw(i$$=RqKKepD9HvghcPf>|9~Q&Ub;lB>@P=+;_nlAMB^qzC@s2KrKB
z-imgkVh?WXE$dMn0(Wfy<0HvV6ToCh<J2c>yza6ko|U`$_l<b(Z<`Vy+qZ|~hv*a}
z=>b}{pq~miBVjgB3PdA=MG@LOr9hPt_=QEtr=~K<A&u62+ja3+NyMhw<-_b3=Z28f
z6sGkUXQu2*&uLv^%sM3Qm!P;U<iX4jiLzh2Z~O7JQk03+R-%yx$zEWaH>k|JrfeGB
zFdfCzdZMsFy2}TVchFx;v0a2x^ZMw>fYgJ=^owipS+YNhS!-Rqg9l_m$00!JofC%;
zkJgXGpU6;(sPpXCkd<K>L1weIn$4k=u=Uqu;96~ym68h6j`a8S!1^QCD{rr}^EmP$
zm3DqsE!R^&FVpfS{cV4{wISXSGnTQscA7sxJ^j+8pRjMkmFCqhQ)u17LLEuhQoI`f
zg%-5V4T8}D1k$^SNHQR6uudn<!&1HlGk<kRdTH4CqeI(CW<k%=WwC9SwuFJ+ikM?~
z8ta$HwS5ufm2cLzFy4+-tC_b1GS*bLC~i5^^g-J+nMZIyk=)u==Oxk<Zd2=9s$RMl
z-`%x9V7y{7*C0hFbzBBHBs&>!YsF}w=#PSz2ks3naja7>{;nM#Xi0K+(ttvz%lkaC
z<v`C7uX@0Zx|>e50-YFpF1V{YzyZstgjbdxE%xc?cPd<(H7?Ab?gu4uX3fQ%em!F9
zu?D$;1N%ak($}EA;GVpoU6zy;RgM?z7AG%XUs$_o=YtDRrdyJz38AhPBA59tbO(*o
zF`u%=elm|JusRv9JhOyOBiX|@z_)dE3*_z&Nxesw3*xb6J`)9XkO$d`4}I^Rme5&-
z@M^6&IF0GqNo_V<$&p3Kc_$8m)2!VH{>!|!$4;t;<_E8*Ldw*TnG?lRte!O22i*r5
z5Go#x`Jh~j$>8`e{$i@5X9VVeD%6)<tuFVEI1}mf>4s{tHfAO1ne4v{Ol|j10V1J{
zXh9*GIELUm+w$Kkn|ZGGf9(W?9O|}LseQ<R3h_FbCZY7T;JNmBEMzwSev25vk1uik
z{JJYwNYho*)0|}|0ftiMiC#p5*4Q!!amsmK8F6|sgaPRH&FDp?DGTo3TT)dogsx=w
zepW6|zyE^L{o!V$t8}qbd+1w|G<w%3ots|O%{hk-ufxwbnOeK)G|atNiBD79N5Uvz
zHmy{6Qf)p@er+_PFCfVG(`Zfyx~o(BV#ga{@d}pYLk89{9Z=TRtO9vfIM~M;UyF{q
z+)o19iOb>zyF&5#uA?Jzi$0zM0G#9pGzVlq&!ZZ_#)1J{RSOE}6voe`7Hyf^69NM!
z`^mKh7;Kx#*yHiw-i-i1*GF3qO=)`K%{{GK*<p&8`U?D@<Ax3CjK_N`AMn_tXuZ)!
z!Yu5&+K33thJfFz30@E`>n1HvFW_3A{h9R}nDqZqwga-k)N#eprVZY6ooqLh%>PcD
zsDSBHdN?Xn{W$P9o~66XR#Y;Zf5qFF_S%C^gCG~d5?)v}CJxF)7_UyF=9}<b{mt^o
zo^fCl$E6t}P$?%T*(@mOCIv3SWd(=|0f;T46QcX7Pt3DSw|II$@!w*KKavu7Fk0Yv
zlywo;YQFLJh3#>s`78>e+p#UY6__(FrS2ATPO9**N=h3VxccGr*HxvxVkhdFnQ}Fm
zZTXd+MWl?K#%rsR1$}i&-34Z=CjfSr<ED{&+m4IL51YXmKu4<G!0fv}!TWv@*80J7
zbI4f6vy(#0O3z!SIYz7s5L*>A*mY1I$OX(<-z>R4z3>tn;-IDS=$2Vf8jrN{5)V6H
zU;gmBA9jo@kAuo3FSxjnQ;>(jc@394l@Yz82yAF<eFgOXt{HHr4xJ)2X*@A^)RY$!
zsVtJzXj|M8tS)C5Tl5yn6mlJXjoUP`QZ;`(VApD%Ruu8TJdbkngDr9yq`Q)Ke3iLo
z?A#l0Z|@r4Q3&W7Ttt@h(g;PZ64RXD`;ee{(wesCx*Vwb{smHbX*)TAXB`)6$e0z}
z<87-HgM0+7P+G5omPQvFudjVpWBDP<FQr0;4TTdxI2KZir!7sUzp|=`wPmNU6$^jn
za>h!)&XgHf!T9YCAYR@h*bY0JzoWM(BqLEUVI!gk9^HnSu)FV46$R519w<Wtr{xL{
zZi<&oG}7=M?0y74c>+(FhS$_-@$2m4%kIufL`FQHhz7Eu%F>|Eqn6rjXm$d4{^>D#
zzy_ax)+c0ai(;e4j?Dz+<m*dU{kbVyy7dmO^{+{MnN))&*w5xW+P9XiPLi*_m79=V
zRu-F>4)3WfrdJW}C8zbDdV*~@Y`-aqjj@wN^ZP){+vnbny(zGBvX@vskTGlIFe03q
z0gdPU<wtxCs>ODY>JAe5Xs77@sY)|EWwa01m@!UGT;}efdkL%KX2Gdu-252Om8sC^
ziOj9|LM<lS=~t#Cjg#=mF$SV<Ao!i7>Ys(wu{o)VSF5Emvr~K&zUK;IO{1M?tK2}Q
z6}fmh@7$u;dHL;X&JnHdAks|u(l}gSQciiO?IfvKhFSl6!eR^f9#6T%n$@Rp^Fg$K
zPxrWgB$fp^*Z9EF<#Vt;5&p>DXQngUBs9=XHy*?^haAU#b|XHGIe-Jz{)dVZnhw9^
z^yXiYskrsg3I>q-q#+^$^6rY1n+Gg^avBp!R_awp^<46FMkbc!?8V~3<C?BY*5XGL
zGJcC<#(Sh1c&N-YvnLBzCXMyeJduS0#mqU&0BKaTXa`0D%S)}LkFZL8iEPECN+z5>
zRs9A3M~+vf?(ap06vaY_ayu{boYgJbPB(OSWHu<zhVz|KQF^fh2FIm`99*f)*W#vh
z-o7XSmfWs&xLy*}*ijlwT-c-_N{ykZ2fytJu#Y|i=D-mlz(Gg_w|y%I%?yqki3C$V
z&xFR0U5712Od}>~Ky|N9T<a`Bnv{m763hmjtG0Jq4=ZxC-8cm~I9l}%(8pbH;6MCt
zV<zW85o<S6f!6b+@`h#oLGF&6dxcNMWUG+QpNM4<Q+iIH<ThBx>Eu42lhnU0^~hM5
z+Z09~2Uq_aT_3IUU$-<nEeR*tYiIzsDygW|Ej5rO?~dA69O@RM-2!}$C~Hm?aynY+
zV!Be;<9<V|+T+eg7~0pNum^8X5dHQd0I78<CB$!LQZJ(nEE*`CN#=66tG@J{>=8sC
z<Nz1pPC3VS8~eAJXtmMHyJLa$HArY%Ym0+$PLCNcB>iWW4?n|HR}xDeJnhQ&wy6%`
zk>pLe!>v)FF?dU7aA^0E#T0Fv=>sa`hY>ijqY9R<H~1e=bMzeDy_dK5+h~LF$CJ*k
zwI-JQetw?2r~}h?BiVl|NU5KdB5E~HVi1c7m}sAjFr1I$qQvXy0a77ydUNkqX1qNg
zsJ%TVB(3895j7{rG&v?2Q|4i{o%0xJf9uES@E~^FRZheg!ePUj98ab?rf!<{jXfT-
z<N^uxHTJ6pGYQR8V6Bd^=(XuteQhj`_pG%3vrk<V73}(*bnRF*1c<43B0@|%*PA%x
zBXfEQnZ<*!Qo*CqcpO~z$m4flk+)tuCIdhJA)};zaN6^#!?t|gn27JynX5O=z+q?0
zr?}UgXyo-G^Fk~U5l(@X6KY9062(tPDbi-ZoXChb7eBs+RNnJv@Y3oY%*`pSb8{z^
zqd)Ji8aqXKsqvSG!XI;AF9vOj#d7hFJLjskvQfy3&OnLD2*8Oca>>DU->D|dc_|$=
zdh9XcAKlUvrZqXp`_JJhY88F<L;?P#udR~ZwtKc<oi~%H)GcY#eJAHDnMrapF2QY=
z(M4KoUV^;THl3TjKauW~To9DFpjPExbp~v2El!xQyohxN!gd0G?~DK!l-=$e{u=l{
zp?o3bW|65eT2^T9qv9AIWM(H5g&Ox}@R@bpjmdK@8k)Zw2rS`KJC<46Hib~OJ<mP7
zEbl$0bbbCM2vLwl3%!IY#ptIpw6r~rFR?=8ujPM4J&Em^QChuP6!VrWyvwdHWzFib
z;UzboSY<+_(u8{kI<G3n!IC}sYaI!s9arCuRJq;Jmga;5ENC1?9cQ|d%Chf39vcwy
zN@o*)<%_e+H*j{LuzWlt0%D3Ki+y)eGq9G8=f>MQ|JN`Kc=)PKnb;9K8wI9?4r=a_
zd#IQ$i6@_Zu$c)9U{l)ErX5FSERlHltLG5+n$zkD418sz2E0CPa281rL#Boo*LZ5$
zyFkx8MBq&13#IJ1|Dmz&w8qZ`PAt)(`pA>G66TK889$!RIix{kl9zx$wep(09k~&F
zVdvwoCc8|Exbl4+aknIKiP5~Ix@EuyYF4e%)w*1XTfC8?WbxPAvd^b<4j)Y=?9HY!
zmM4z?K*5n26oDmJ_B2IDwTRMynjdVqElZ0Z{o*aBqzoCG#!+UN2->o^#(mx1PW|(S
zkBjW@ozkyWJgDYAgISbpnhIMhn=43tXZJN#HXl;&a(oqj`t}(r=o9MjXFS`XI?&Cz
z1sZ3p?r0Q(Db@wCulXR=cedwwOaN=xB`KhTb_i?+p7z~DgV;wW-158jWy6`N{p_;a
zQ+I0V4c;d<9cSS#NIE+6wwL3kcAK*_h)Mxz(Z!E%+N3-tJ#$Da_3(=b_Ka@M!D7K=
zze&H#tadU%k5|jgnyzun(2yzG8`8hprkETHoYw(B+|v*`8=UCnO|F~zFl}LwaQ|Eb
z-f48q|JP+A&(uc8V6{25%bz3C<FmKKF&>`-!ddHG`B$#s%~pH1UsGpjXw+DzsjHH6
z5)I$!fnEpRGy#0%jeU+q(^!Z6n5V7XEUvcyf=^Nd0O}hxp;9dz<*z?t2fo_hyw-xS
zF^+c;gY<;9H_cZ^o6~*=&%632485U1U77vczn?9cevcP!*mUPGlO%lZ<UNglQqL))
zem@X0$J((OAdlKCaQ!T$M5!xH|C^CqEL)i6V<2&k;O3U4pw`z&1A0-ph$}IKjv4Q3
zq8<^{JP($miWPg+L@F$S+wy$C)b<*c^Bd2CU7sN#j(tu%hgzgOjMo{!WOykVzGO2%
zm~xxseHz2;JZ?aKTHtDJ@(kv*X{(m#g@haLR<uc~aFyx|me_g?d~Wlh?h1Jr3uBPC
zDQZ|dZ{!E!vNT3PXuAcs1eb<>ev#F;u*vLhxy5|_85Pv+G9ylR>1c<~oSFd2O9O~^
z81U1MxicB?$>KELPvCzUn<D($q;wZM4m{0pL3>jrD%;g-j7ZZkv7N#`G>W5OLyIHk
z*&+A=wfsVfXj4{}wWR1tmi0yHmpUu^z<IVTRk?Z48cV1gdS9(ZL)pj>|NZe&!s0WQ
zh+~lup6C8|-kleS>vm6aE`SZMDh(W#YcSrI;nff3q|QD{f_Kd7&iz<Ir@MbOrS?C{
z4Nu*wrqjxqtUBvP!|!o-$rv@g*B8&0AkBh=GTpjZTM)jvu^#AxHFwmN)izjN8*rG<
zWM?+M*c~uTprxB4)vi>M@%aZfC*$`e6q2{SD_b`q;`eZJ8vGI27(l^aa|Abx+Kf&{
z@%~uLdXI~B3-|bDwq+{%p*JuHtX;H9#m`l%T|mcPQk8(Q#1_m8*+YR?R2Ou+%6KC?
z4a9!T@H$!+fAlnL^glZ_njqCDxg5Ka^zZX;{_>yj(9ldDEHaC=c@m84lZo#K`nBzT
zX9A7<RAhIFBNyWGxLaH=o0AQOJK2hseqgg$@qquy1-n$_Q3N12SYV#C#DREnS-PEd
zl}a1v<yJm2Cq+UjV^u4$$J}t{+LDf|7op`5zXZ0w4gvIWCDohFEqwO4A8MELq`KH;
zQBIy|NZ?RHU{F^WXBRhAp87m`d!)+6zbX`Tl~sr0)t*q!eO&Y>gg=eGEziG`kh;7*
zkRboBfh{_&Of^NMA<3%<Z$pyFIe>m$@$wS?W$d7{2D+Y(c9Morptj4ZEcp9BWubnu
z`Ryh7N)%3=e>D!C^Xq54tP0p>^)ID<<&X8#hk!KKs_9^<SiqW_rY1_HwQ=v1VMzT?
zC?O$sW10qZ5pa&XZx{?Z9)XH*O4wC<tJ)=#h0sPj>b;vnBAEzCh*I$a2W)TmJ}lft
zv^|^yLjKQ?H&I4UzW>zWN7=7oy7beA)4RgQ;#>!p&~f2Nz#z)7Bd8)cO%Vcx_7F1<
zkj{-JCR%h_w>JqcKRwDqRN@~C_!T`Xj_~eTG4TJ}ZviI!IFef?ChZ)V1p>VWK{|bo
zw^Nyis29-b;UKh0urL&okBu_LOE>-!u<~WuXGW$HZ$j{&od>)WjuOo3u_FPWajB&>
zO5IBD^pC`tAj(L>^K)KFRZp!kdQl4tYLrp;!srZ2E-_f}K6K`vE&GgyWdp8NKCDM2
ziTC>>(Kvs-1wpPpd+wu*g|35THL>|Okr#pl7d)bLH~kLhWEv@&N~m+r$YQ~L!UZ&N
z5t@{;peb*XD-U&z!j|86iK;dyKJ)K<OKo-oNg>v4oPu*z;QC3w76#&`^u#ioN<p}r
z_%UcN!2!E);|PU|&?$ulwh-}-x7tpF-PQVpR#mc=cnuz3Ia3hAEer2_n+_eSBy<Rq
zQ79hDHmy&nKcTwC`*pETDs<;^wW6_@x^~19Ul(m<Q&J5b4<x)*hoprW>iITfR1Tvr
zX$C1lF%xB#ruE1WJJkJE9T`jMrb@nhMc%E|r}uZ3Brc;#l!zO)BZ`qJ>gn^mb+xs%
zozcy9-S{L-*BJ(T!dwWUqH3c*U9cWaBAbK@l%R$i5;Vso$r883XpL|UZh4ue94F2n
zPkJ(<Yk1((Oq*RV|L}B!9h>u2@vd>mjkb#vqN2>&0Bxoi*@qzmA<z@-^PeKT$QP<%
zmX<Op+@a_=uFFCZPro>~9r^w_Vbe>0)W{>3gFp>g{0w9HKG^#Rp}{9dsw28Vs|t>&
zuuwuIKOubc)N%|82wlZW=>N-RXjLKG7<i3l6>EGud`>BWN&D~5&#KS$Dph01I&^$T
z#-;%VtC)Mg&;SJ35NU`)Igk)ma1q=S5MG_cY<(lylP?MeS*G!ulyuDXKwra!K^{dy
zcjhS~PsFsNw*P$wn!LCtC|34WNP9cQ)LX$^Tn3x?V9cAJ+?I@OE2TlG%b5J8PCuIY
z<(ZR04W4TNU!dgH*1lvmaeF5Z|M?}gp6{7lH}y5JLq_pAx8D~hON~eFy&2}GZB{Ta
z5fit0HfOoFFVOY=!1}K5W;*<9mKyH?ZzCcw^7x5MaDEFT26RfV54gR~LC5`_L>^>T
z+EH70-r_;w*80rVD+~rDlP)Brib5FoOwV(Bl&q6elyKb~M*7I#Vt&I@D72@Kr%S*}
z_9F-k4wC`A<109@e`8~(7Ed8670h{gsL|Tc#P@L~(c>!p%8mGM5+dD?|M`rH4?wE5
z3EbmKr_cDT5lN{f38D;}Yz}AC^Gt<(F2fxBT>=k5vc~`cXz$&HOE;)@xp{I&Fw+`<
znqtl9srg6NuqF@aKehb3B=d9kzYuVPzISYNAS~|faX-kz)j$bN^n<oNBB$AKcC5LA
zC3toDoo=*s%MLhB1eHTUvg%BO>2qu-mpy6xjHb|4U`*MHvo$0Gt){c-;RTRFIqwu;
zN;k7sSsLVo3kcwY601lNGK?mM^Q%wT?sF+6&=_b$0vfbEe|CaC<l-+5|JOthj(W!l
zjz%l5%4-aIKQDbe6}S#-sf9~ZSL(>dWjk;mJsh?5(8b2`2@Yt3t5-VjCY;{v$+|yg
zv$ocET;kP)o}zu-#plcgN?^H<nKV4cw%@!uM`{TC&vU(`Q1>s6f@WCEXXWDZ$85KB
z=GJttn+5<Wu=1qN%C=hoSF;y$>A4iOPK|X9CJ+K+&lMB|+NcIi=DSIz-0Jx}<aGb(
zp$lor!>C>sskO4^(ONReSAJTf?0&ysUyNn$jH?X!?+SOI?OA*r_?!s)VYg|qEpxE2
z5(8ZKyZcVkHtuqdtp@NT3R_#<V$eZ8Kbg3k?Q>#y0Lgi)!GEeUeV`FDNA0U86<r_3
zG)M4(a^D|z{DEdb0xLPq8WUfXT^S@l&Ys<ChdiC3_@iC|HI=vaJfF3Ud^@hg$3=%>
zE*tdINzL8X943pgx9?u(ndMg|XD+UA_rnR35m1rSQX=f}@wq{;YT}`}u3xX#*U4c{
zA8k#f`(ua{8;s!M%&t>XNm2a_j1CAt3iOB|+dXr?<}?)HEGN(6(!e)*?np1nlFxn8
z2)jlYx1CPz;7phsFWcKa!;rqD9!ugdWdmC7Rhcjf=9Ulj@1XqgeQ3+>cNha0nx1H?
zdHBt#V{vBAOM7bT;(TPUuH1PL)foR%$^mXHMgA7*;8J6G`Re!x?SJU_|I;WCf=YoX
z&30}?OwSRN#X|!sl<1cxuw+YRR#(U{pF%m3!M%nA6cQ+t6%Q#@YDMEIj>Xodx>jl4
z-bKC*8JAf=*?gUs$1l-V^-YjHTZt<}4ykyVLO>8xkw9RRe_H6l^4?l+KfQyaWb@jV
zd5YAV=X)<4IH}~Tskx5UrxM}?1}MUvg*D_FyU1@}oN~Pn9|dKecN#_lQxjjZK(yot
z8Zs0}5CYT%m!oJ=OY5bK+Mw)bZr6Rh6|)in@CK(Q?1Z+TaCaQ6N$iJ_R?)Ngm$2T5
zX2n%O99|z4U25vNS`~w&dZ;v15I!~2#5`pev&OWlt!9ojvfQUR(H1GctjTY{%yrBn
z3?`vWNdy)}owIkmdP^~R+E0u^$J@b;)uZHpn9YU<=&}{Ec9Q0^fB4ke1eGSbwd4;B
zxS{@z4p5Mo(C4d#Ppids-U9tTLWJ<6>c_d(72M2^zy42LM3el;LU<Y^REbV=Y%?Z5
zfRJh(EZQOb9|+L{DZQ`Tseo^4xU<mUs5J5rsMU+r?kV_CKGI-8$5Yn93nWTeP$a-o
zty>xnxYr~wsHyZ7m}21qc6gzBnWi0OYt%bR@Li^qzstcWujqgnMtO)f-)n<PtA8(x
z^xcr9Vd2MMVie#IB+P*S+zbujbp(LHaSQ#QcCP#%%C!&AhG#4p#@ZB<NthN}qOy!F
zOLiqHnhvFu5m{cEv5e3uvYW~-HJCY$&>`6=gJWcwRKg&#B(iU3<~;hmpZEO_&il*z
z{B(cr=li*y`~KY5_qwk8m#1;f-Q+=3v&cj|Ui~AoQAUt_CqA-tHn31-Qj@WL!>$L&
zh+sf6=(xP=yML57dM$`Mn!6p3G2v1Con|ESBWmZpr|)NW8sccFTG{A?Q@EiagROBh
z^@sAu#|*<or_>*_<=NvX=4fF;t(GKL<gU4OKHTDAZ=JY-wO^^b{hif$y5;_q9~aG7
zxr9e8cV}kQytBeO5)|wFH-h3G?kQE&`Q=Mowa4xydjr1I#S8v{J)yMYiz1hYl+9=w
z{I}d-;jy!7+Z#LsBYImdh3$Jg$p~G&x_El|cM4CttptxRz1kDT@l}-z40$b>)L=!>
zKPbVlkJ#9CFFo#4D>@>~c;bY7D3dX()}rC_`Is1WB3KL?ks28Ye^XI1bVf)?0f!A*
z9Ln7e;pDfpDFh521u_P8BDbw-sBwu3(cTlIe_(UoM|oOIR?vcyGq0MIeJ)I_wS@)f
zItYG{sI}$tgwDAr=m#u&lFlQW0#XeZk0Dw80<SRlxV|vI(!o3*GufK!%7{+eP1fLk
z>=M&!21czyTJBA!(ayg9?~7Cu&F+q^1mhW1InmgmN-65Z0m>IK7&b{73b?pbqvXM?
z4zlKu?ljI11&Lw;gtGGH7#3)kB*{qPD=Wp&Xb9kC4xy$?A29=M=2j{sgXRw|hn?A&
zSWl7V{F$8^a^hJA!PHrsGKvY=DE+WdMm~2%Lk`RY5CkJ|RzX<^IwC=!y$d-j7%hZD
z?J7E>V3Mj3Zy(|pucS#SYpMSrF5W52LEu2ozY9T#%<bGCbV0V&7vtrGVOU6$2?hdM
zrx+B-@GB9H1ycl}?42MeK4epn<e4SZd~ms_LzPez1UiC>ByKz}A*uqAAe8Sv3dNPi
zU4vt}p0Fp+lS?sPs`#^MF{)C+V&|Xqs9A(-(>fp!z-i}S09S1v6EK%}&K(L{nSw%F
zFDf3$C!D%d^ZL%N)?YpLXa}@X?iXw;>j(fYc(I$&puR1{{F#9d<gy%V*77Z;yMkt~
z&)38I(bGSykiD61GDi+#j7Z<ov@@~-#x49Vh7aYN#?4wJ?B3XS#P4Yqw8^?#?2d6`
z1)`PFTM41e(~X{joX-)SS`a-6m-w9VNAN~bL&WAsi2;xB_Uo6nw!McZwLH2CR`7@$
z9~cOED&ae#wL<d6=9hEd^FAeH-nM6{cpLtp%l6M5{tXxLBYIx{O~Fg4=<kNbdXJDW
z3PP%{0x$5Q+^06!c`s#6h3_7;K9wxA&-G`rX+v6`TX^@vb2-jCXQ}!bJ5wd+JrCC=
zLnzDgejsI>hedoL<%yQje5(0P>FFq@({Gft!4QPkOmpUqfh{(PE*gG&NKl$OA!S<#
zhY1{TaFls&u(c+uCp&+jA%qX55<{!PiSocQ4kaw0Ci&~~;5q9ER+<ptqQ=qd5C)w9
z(zF;V&`d_DDuZ(ARN|W^jUd~B$G;?ecLVESy(5=r22~FN>sg>&ew7B6jPud}?&#N@
z(w9?6{S_{}{>V5-cUZshuZ6PM*63)EG#*MItdnn}4A@2cka?%|_ABf`K>3n#?wacX
z?H+heeyPXcWMfNtg0KWg^v?$%O#z^?7=hu@7i8RE5lR9i*v_!(24n+lx({H*|BL_O
zV$`ich{{sp+i|-Cah0Gf=%32FhUnC|cLIkxl=xEp!eo4vv`8``5^y)Eg#FZrg}OM-
zP~RNpgN77%FEoPH%Q7c&*SEF-?#%(@P4b3)=^zBYZ47ZYiRec&5l|En{Pa=y|5&R2
z1J$@Dvc3^}RP?Z3l%);bl3B&ws7`O1>LE>h%O<;{nYO>{)Bhy|Kji-y(qMvsBDBy1
znua#6bq5cC4(K0BOjEqNp4BK@?@#MKO&lRJDT&G{b5$~};8t&C6NsSiUfyn8@ri_D
zx%yG2?r%ciFi%%@6}k|BVI8nw2g0Jv%fYR%(T#bpmu!-ifa+7+8H``>;rUUP993se
zTCznu%0W7&v~Js}=H|zd9;STXwJo+w7>VN(HfG<QN+MS4jl@ElI%rt7P!s|x!HD2*
zo|~$p=cIU*Uo#eK|N8CO>8aI-E%AFUDK;dGTuVm+h7F4v2kv>N<6Vj+yc+gcuo5AN
zgGS+c7(@!95Tkd76-X|+?au^fMGiv|dz7%AE~YUyZE+`L+cp8B=)Wi7-?Th*T$9Gb
zMWMD&#Ur?3HIuy}woAEWp84k7ydvFI-|*WT$DkoSt#7ZCOi`*PpuyiNN}67G_*xTc
z(>mj<dxUN^Uec9#v(r$QS|S%~bY(>BJPVD8#bTXhhh3}o`HFs2Z~IH`P&b~l8o1?@
zDC=_)HPFxbtgipUK_2UlVwvStR6J-N)i75|wsAC|T%2@k?~gI*YQf9cy>j`*tvVid
zN}qSKx6*#{3BZO&k&%`2A5GCEjN)f?1|x+RjV_tZ{_xJ#HnPKv%c#g#q>zX{NtBko
zZxb=6Kk>4=x;(wvb!jSfwu&Tpk|F>^J?^7BhPugfbdJJUD8yZ7^W(Qaov6(^=KImQ
zr`pb0#lFDPeQ5dJxP4~y_ZEZO{FEzSrzW-DEM>TpeIE4>SUEPEE;R2$sRB{TDu)Yp
z;#TEYU+zK6%{M7iW2tt9exFy}&hv`9WlkrT<J-^;b!$b(#Tovb+yVI-|5SFBXoWR2
z*|-r3o7E*1z~grH1U*6;?(YJ9={mnDTDg6Cvs0bJV3f@0Ja{*p-IdwK&bh#t3XFl-
zcQ#Y!FDoqs&mGw0>5}LxnKRechAAy+41T$qx!sr>uWfIYB1W@drniu2#hDxFslC$Q
zKb`1v{}@Lj9_?Daa23OUdHvFPGP5FexOB+Uh_~9%JBn*7s(;Z=n^3LndG^+7Cnk1u
zEHYdlz8A+^s^n0blnhYok(X6z0_~5A(Y9x#(!B}TNWv&!lcSgXeY@AlY2}gT@`7S+
zf$jh)4>Yo^@Q&!d)?WWlulbM_v#>0-k859Qu}FaKLcB743JkcR^g#GebC)@|WY@wM
X+Mj}q444gGgn*Bgxt&?536c6YsDhKL
--- a/browser/base/content/aboutaccounts/main.css
+++ b/browser/base/content/aboutaccounts/main.css
@@ -1,386 +1,152 @@
-/* Border box all the things by default */
-*, *:before, *:after {
+*,
+*:before,
+*:after {
   box-sizing: border-box;
 }
 
 html {
-  background-color: #f1f1f1;
-  background-image: -webkit-linear-gradient(-90deg, #fefefe 0%, #f1f1f1 100%);
-  background-image:    -moz-linear-gradient(-90deg, #fefefe 0%, #f1f1f1 100%);
-  background-image:     -ms-linear-gradient(-90deg, #fefefe 0%, #f1f1f1 100%);
-  background-image:      -o-linear-gradient(-90deg, #fefefe 0%, #f1f1f1 100%);
-  background-image:         linear-gradient(-180deg, #fefefe 0%, #f1f1f1 100%);
-  background-repeat: no-repeat;
+  background-color: #F2F2F2;
+  height: 100%;
 }
 
 body {
-  color: #424F59;
+  color: #424f59;
   font-family: "Clear Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
   font-size: 14px;
+  height: 100%;
 }
 
 a {
-  color: #0096DD;
+  color: #0095dd;
   cursor: pointer; /* Use the correct cursor for anchors without an href */
 }
 
-noscript {
-  color: #f00;
-  display: block;
-  margin: 160px 0 0 0;
+a:active {
+  outline: none;
+}
+
+a:focus {
+  outline: 1px dotted #0095dd;
+}
+
+
+a.no-underline {
+  text-decoration: none;
 }
 
 #stage {
-  background: #fff url(images/fox.png) no-repeat left 50% bottom 30px;
-  background-size: 30px 31px;
-  -webkit-border-radius: 10px;
-  border-radius: 10px;
-  -webkit-box-shadow: 0px 1px 3px 0px rgba(0,0,0,0.25);;
-  box-shadow: 0px 1px 3px 0px rgba(0,0,0,0.25);
-  margin: 60px auto 0;
-  min-height: 550px;
-  opacity: 0;
-  /* padding-bottom = 30px (below icon) + 31px (firefox icon) + 40px (above icon) */
-  padding: 50px 25px 101px 25px;
+  background:#fff;
+  border-radius: 5px;
+  box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.25);
+  margin: 0 auto;
+  min-height: 300px;
+  padding: 60px 40px 40px 40px;
+  position: relative;
   text-align: center;
-  width: 320px;
+  top: 80px;
+  width: 420px;
 }
 
-header h1, header h2 {
+header h1
+{
   font-family: "Fira Sans", Helvetica, Arial, sans-serif;
+  font-size: 24px;
+  font-weight: 200;
   line-height: 1em;
-  margin: 0;
+  margin: 0 0 32px 0;
 }
 
-header h1 {
-  font-size: 24px;
-  font-weight: normal;
-  margin-bottom: 21px;
-}
-
-header h2 {
-  font-size: 20px;
-  font-weight: 200;
-  margin-bottom: 40px;
-}
-
-section p {
+.description {
   font-size: 18px;
 }
 
-.error {
-  background: #D63920;
-  color: #fff;
-  margin-bottom: 5px;
-  padding: 5px;
-}
-
-.error:empty {
-  display: none;
-}
-
-.graphic {
-  background-repeat: none;
-  background-size: 150px 130px;
-  height: 130px;
-  margin: 0 auto;
-  overflow: hidden;
-  text-indent: 100%;
-  white-space: nowrap;
-  width: 150px;
-}
-
-.graphic-checkbox {
-  background-image: url(../images/graphic_checkbox.png);
-}
-
-.graphic-mail {
-  background-image: url(../images/graphic_mail.png);
-}
-
-.input-row {
-  margin-bottom: 15px;
-  width: 100%;
-}
-
-.input-row input, .input-row select {
-  border: 1px solid #C0C9D0;
-  -webkit-border-radius: 5px;
-  border-radius: 5px;
-  color: #454f59;
-  font-size: 18px;
-  height: 45px;
-  padding: 0 20px;
-  width: 100%;
-}
-
-.input-row input::-webkit-input-placeholder {
-   color: #C0C9D0;
-}
-
-.input-row input:-moz-placeholder { /* Firefox 18- */
-   color: #C0C9D0;
+.button-row {
+  margin-top: 45px;
+  margin-bottom:20px;
 }
 
-.input-row input::-moz-placeholder {  /* Firefox 19+ */
-   color: #C0C9D0;
-}
-
-.input-row input:-ms-input-placeholder {
-   color: #C0C9D0;
-}
-
-.input-row select {
-  -webkit-appearance: none;
-     -moz-appearance: none;
-    -moz-user-select: none;
-          appearance: none;
-  background: transparent url(../images/ddarrow_inactive.png) no-repeat right 14px top 50%;
-  background-size: 10px 17px;
-  color: #C0C9D0;
-  height: auto;
-  outline: none;
-  padding: 8px 20px;
-
-  /* Some hackery for Firefox since moz-appearance: none doesn't remove the arrow */
-  text-indent: 0.01px;
-  text-overflow: "";
-
-  width: 100%;
-}
-
-.input-row input:focus, .input-row select:focus {
-  border-color: #6f7a85;
-}
-
-.input-row select:focus {
-  background-image: url(../images/ddarrow_active.png);
-  color: #454f59;
-}
-
-.input-row:last-child {
-  margin-bottom: 0;
-}
-
-.input-row .input-help {
-  margin-top: 10px;
-  color: #C0C9D0;
-}
-
-.button-row {
-  margin-bottom: 20px;
-  margin-top: 45px;
-}
-
-.button-row button, .button-row a.button {
-  background: #E66000;
+.button-row button,
+.button-row a.button {
+  background: #0095dd;
   border: none;
-  -webkit-border-radius: 5px;
   border-radius: 5px;
-  color: #fff;
+  color: #FFFFFF;
+  cursor: pointer;
+  font-family: "Clear Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
   font-size: 24px;
   padding: 15px 0;
+  transition-duration: 150ms;
+  transition-property: background-color;
   width: 100%;
 }
 
 .button-row a.button {
   display: inline-block;
   text-decoration: none;
 }
 
+.button-row a.button:active,
+.button-row a.button:hover,
+.button-row a.button:focus,
 .button-row button:active,
 .button-row button:hover,
 .button-row button:focus {
-  background: #FF9500;
-}
-
-.button-row button:disabled {
-  background: #C0C9D0;
-}
-
-/* Custom rows */
-.input-row-month-day select {
-  width: 45%;
-}
-
-.description {
-  font-size: 18px;
-}
-
-.links {
-  margin-bottom: 20px;
-  overflow: auto;
-}
-
-.links a {
-  text-decoration: none;
-}
-
-.links a:hover {
-  text-decoration: underline;
-}
-
-.links .left {
-  float: left;
-}
-
-.links .right {
-  float: right;
-}
-
-.privacy-links, .privacy-links a {
-  color: #C0C9D0;
-  margin-top: 35px;
-}
-
-.privacy-links + .button-row {
-  margin-top: 20px;
-}
-
-.verification-email-message {
-  word-wrap: break-word;
+  background: #08c;
 }
 
-.password-row {
-  position: relative;
-}
 
-.password-row > .password {
-  padding-right: 75px;
-}
-
-.password-row > .password + label {
-  border-left: 1px solid #C0C9D0;
-  color: #C0C9D0;
-  cursor: pointer;
-  font-size: 16px;
-  height: 44px;
-  line-height: 44px;
-  position: absolute;
-  right: 0;
-  top: 0;
-  /* it is very easy to accidentally select the text when clicking */
-  -webkit-touch-callout: none;
-  -webkit-user-select: none;
-   -khtml-user-select: none;
-     -moz-user-select: none;
-      -ms-user-select: none;
-          user-select: none;
-  width: 55px;
-}
-
-.password-row > .password:focus + label {
-  border-color: #6F7A85;
-}
-
-.password-row > .password + label > input[type=checkbox] {
-  visibility: hidden;
-}
-
-.links + .privacy-links {
-  margin: 20px 0 15px 0;
-}
-
-.cannot-create-account-content {
-  margin-top: 105px;
-}
-
-.spinner {
-  -webkit-animation: 0.9s spin infinite linear;
-  -moz-animation: 0.9s spin infinite linear;
-  animation: 0.9s spin infinite linear;
-  background-image: url(../images/spinnerlight.png);
-  background-size: 30px 30px;
-  display: block;
-  margin: 130px auto 0;
-  width: 30px;
-  height: 30px;
+.graphic-sync-intro {
+  background-image: url(images/graphic_sync_intro.png);
+  background-repeat: no-repeat;
+  background-size: 150px 195px;
+  height: 195px;
+  margin: 0 auto;
   overflow: hidden;
   text-indent: 100%;
   white-space: nowrap;
-}
-
-@-webkit-keyframes spin {
-  from {
-    -webkit-transform: rotate(0deg);
-  }
-  to {
-    -webkit-transform: rotate(365deg);
-  }
+  width: 150px;
 }
 
-@-moz-keyframes spin {
-  from {
-    -moz-transform: rotate(0deg);
-  }
-  to {
-    -moz-transform: rotate(365deg);
-  }
+.description,
+.button-row {
+  margin-top: 30px;
 }
 
-@keyframes spin {
-  from {
-    transform: rotate(0deg);
-  }
-  to {
-    transform: rotate(365deg);
-  }
+.links  {
+  margin: 20px 0;
 }
 
-/* TODO: These need further consideration */
-
-.hidden {
-  display: none;
-}
-
-/* Responsiveness */
-
-/* TODO: Confirm breakpoint sizes */
 @media only screen and (max-width: 500px) {
   html {
     background: #fff;
   }
 
   #stage {
-    -webkit-box-shadow: none;
     box-shadow: none;
-    margin: 0 auto;
+    margin: 30px auto 0 auto;
     min-height: none;
+    min-width: 320px;
+    padding: 0 10px;
     width: 100%;
   }
+
+  .button-row {
+    margin-top: 20px;
+  }
+
+  .button-row button,
+  .button-row a.button {
+    padding: 10px 0;
+  }
+
 }
 
 /* Retina */
-
 @media
-only screen and (-webkit-min-device-pixel-ratio: 2),
-only screen and (   min--moz-device-pixel-ratio: 2),
-only screen and (   -moz-min-device-pixel-ratio: 2),
-only screen and (     -o-min-device-pixel-ratio: 2/1),
-only screen and (        min-device-pixel-ratio: 2),
-only screen and (                min-resolution: 192dpi),
-only screen and (                min-resolution: 2dppx) {
-  #stage {
-    background-image: url(../images/fox@2x.png);
-  }
-
-  .graphic-checkbox {
-    background-image: url(../images/graphic_checkbox@2x.png);
-  }
-
-  .graphic-mail {
-    background-image: url(../images/graphic_mail@2x.png);
-  }
-
-  .spinner {
-    background-image: url(../images/spinnerlight@2x.png);
-  }
-
-  .input-row select {
-    background-image: url(../images/ddarrow_inactive@2x.png)
-  }
-
-  .input-row select:focus {
-    background-image: url(../images/ddarrow_active@2x.png);
+only screen and (min-device-pixel-ratio: 2),
+only screen and (        min-resolution: 192dpi),
+only screen and (        min-resolution: 2dppx) {
+  .graphic-sync-intro {
+    background-image: url(images/graphic_sync_intro@2x.png);
   }
 }
--- a/browser/base/content/browser-addons.js
+++ b/browser/base/content/browser-addons.js
@@ -400,26 +400,40 @@ let LightweightThemeListener = {
    * Append the headerImage to the background-image property of all rulesets in
    * browser-lightweightTheme.css.
    *
    * @param headerImage - a string containing a CSS image for the lightweight theme header.
    */
   updateStyleSheet: function(headerImage) {
     if (!this.styleSheet)
       return;
-    for (let i = 0; i < this.styleSheet.cssRules.length; i++) {
-      let rule = this.styleSheet.cssRules[i];
-      if (!rule.style.backgroundImage)
-        continue;
+    this.substituteRules(this.styleSheet.cssRules, headerImage);
+  },
 
-      if (!this._modifiedStyles[i])
-        this._modifiedStyles[i] = { backgroundImage: rule.style.backgroundImage };
+  substituteRules: function(ruleList, headerImage, existingStyleRulesModified = 0) {
+    let styleRulesModified = 0;
+    for (let i = 0; i < ruleList.length; i++) {
+      let rule = ruleList[i];
+      if (rule instanceof Ci.nsIDOMCSSGroupingRule) {
+        // Add the number of modified sub-rules to the modified count
+        styleRulesModified += this.substituteRules(rule.cssRules, headerImage, existingStyleRulesModified + styleRulesModified);
+      } else if (rule instanceof Ci.nsIDOMCSSStyleRule) {
+        if (!rule.style.backgroundImage)
+          continue;
+        let modifiedIndex = existingStyleRulesModified + styleRulesModified;
+        if (!this._modifiedStyles[modifiedIndex])
+          this._modifiedStyles[modifiedIndex] = { backgroundImage: rule.style.backgroundImage };
 
-      rule.style.backgroundImage = this._modifiedStyles[i].backgroundImage + ", " + headerImage;
+        rule.style.backgroundImage = this._modifiedStyles[modifiedIndex].backgroundImage + ", " + headerImage;
+        styleRulesModified++;
+      } else {
+        Cu.reportError("Unsupported rule encountered");
+      }
     }
+    return styleRulesModified;
   },
 
   // nsIObserver
   observe: function (aSubject, aTopic, aData) {
     if (aTopic != "lightweight-theme-styling-update" || !this.styleSheet)
       return;
 
     let themeData = JSON.parse(aData);
--- a/browser/components/customizableui/src/CustomizableUI.jsm
+++ b/browser/components/customizableui/src/CustomizableUI.jsm
@@ -1214,18 +1214,26 @@ let CustomizableUIInternal = {
       } else {
         //XXXunf Need to think this through more, and formalize.
         Services.obs.notifyObservers(aNode,
                                      "customizedui-widget-command",
                                      aWidget.id);
       }
     } else if (aWidget.type == "view") {
       let ownerWindow = aNode.ownerDocument.defaultView;
-      ownerWindow.PanelUI.showSubView(aWidget.viewId, aNode,
-                                      this.getPlacementOfWidget(aNode.id).area);
+      let area = this.getPlacementOfWidget(aNode.id).area;
+      let anchor = aNode;
+      if (area != CustomizableUI.AREA_PANEL) {
+        let wrapper = this.wrapWidget(aWidget.id).forWindow(ownerWindow);
+        if (wrapper && wrapper.anchor) {
+          this.hidePanelForNode(aNode);
+          anchor = wrapper.anchor;
+        }
+      }
+      ownerWindow.PanelUI.showSubView(aWidget.viewId, anchor, area);
     }
   },
 
   handleWidgetClick: function(aWidget, aNode, aEvent) {
     LOG("handleWidgetClick");
     if (aWidget.onClick) {
       try {
         aWidget.onClick.call(null, aEvent);
--- a/browser/components/downloads/content/indicator.js
+++ b/browser/components/downloads/content/indicator.js
@@ -293,16 +293,29 @@ const DownloadsIndicatorView = {
   //// Direct control functions
 
   /**
    * Set while we are waiting for a notification to fade out.
    */
   _notificationTimeout: null,
 
   /**
+   * Check if the panel containing aNode is open.
+   * @param aNode
+   *        the node whose panel we're interested in.
+   */
+  _isAncestorPanelOpen: function DIV_isAncestorPanelOpen(aNode)
+  {
+    while (aNode && aNode.localName != "panel") {
+      aNode = aNode.parentNode;
+    }
+    return aNode && aNode.state == "open";
+  },
+
+  /**
    * If the status indicator is visible in its assigned position, shows for a
    * brief time a visual notification of a relevant event, like a new download.
    *
    * @param aType
    *        Set to "start" for new downloads, "finish" for completed downloads.
    */
   showEventNotification: function DIV_showEventNotification(aType)
   {
@@ -318,18 +331,18 @@ const DownloadsIndicatorView = {
     if (DownloadsPanel.isPanelShowing) {
       return;
     }
 
     let anchor = DownloadsButton._placeholder;
     let widgetGroup = CustomizableUI.getWidget("downloads-button");
     let widgetInWindow = widgetGroup.forWindow(window);
     if (widgetInWindow.overflowed || widgetGroup.areaType == CustomizableUI.TYPE_MENU_PANEL) {
-      if (anchor && isElementVisible(anchor.parentNode)) {
-        // If the panel is open, don't do anything:
+      if (anchor && this._isAncestorPanelOpen(anchor)) {
+        // If the containing panel is open, don't do anything:
         return;
       }
 
       // Otherwise, try to use the anchor of the panel:
       anchor = widgetInWindow.anchor;
     }
     if (!anchor || !isElementVisible(anchor.parentNode)) {
       // Our container isn't visible, so can't show the animation:
--- a/browser/components/preferences/sync.xul
+++ b/browser/components/preferences/sync.xul
@@ -254,36 +254,39 @@
                          value="&forget.label;"/>
                 </vbox>
               </hbox>
             </deck>
           </groupbox>
 
           <groupbox id="syncOptions">
             <caption label="&syncBrand.shortName.label;"/>
-            <vbox id="fxaSyncEngines">
-              <checkbox label="&engine.tabs.label;"
-                        accesskey="&engine.tabs.accesskey;"
-                        preference="engine.tabs"/>
-              <checkbox label="&engine.bookmarks.label;"
-                        accesskey="&engine.bookmarks.accesskey;"
-                        preference="engine.bookmarks"/>
-              <checkbox label="&engine.passwords.label;"
-                        accesskey="&engine.passwords.accesskey;"
-                        preference="engine.passwords"/>
-              <checkbox label="&engine.history.label;"
-                        accesskey="&engine.history.accesskey;"
-                        preference="engine.history"/>
-              <checkbox label="&engine.addons.label;"
-                        accesskey="&engine.addons.accesskey;"
-                        preference="engine.addons"/>
-              <checkbox label="&engine.prefs.label;"
-                        accesskey="&engine.prefs.accesskey;"
-                        preference="engine.prefs"/>
-            </vbox>
+            <hbox id="fxaSyncEngines">
+              <vbox>
+                <checkbox label="&engine.tabs.label;"
+                          accesskey="&engine.tabs.accesskey;"
+                          preference="engine.tabs"/>
+                <checkbox label="&engine.bookmarks.label;"
+                          accesskey="&engine.bookmarks.accesskey;"
+                          preference="engine.bookmarks"/>
+                <checkbox label="&engine.passwords.label;"
+                          accesskey="&engine.passwords.accesskey;"
+                          preference="engine.passwords"/>
+                <checkbox label="&engine.history.label;"
+                          accesskey="&engine.history.accesskey;"
+                          preference="engine.history"/>
+                <checkbox label="&engine.addons.label;"
+                          accesskey="&engine.addons.accesskey;"
+                          preference="engine.addons"/>
+                <checkbox label="&engine.prefs.label;"
+                          accesskey="&engine.prefs.accesskey;"
+                          preference="engine.prefs"/>
+              </vbox>
+              <spacer/>
+            </hbox>
           </groupbox>
           <hbox align="center">
             <label value="&syncDeviceName.label;"
                    accesskey="&syncDeviceName.accesskey;"
                    control="syncComputerName"/>
             <textbox id="fxaSyncComputerName"
                      flex="1"
                      onchange="gSyncUtils.changeName(this)"/>
--- a/browser/devtools/styleeditor/StyleSheetEditor.jsm
+++ b/browser/devtools/styleeditor/StyleSheetEditor.jsm
@@ -591,19 +591,19 @@ function prettifyCSS(text)
       case ";":
       case "{":
         shouldIndent = true;
         break;
     }
 
     if (shouldIndent) {
       let la = text[i+1]; // one-character lookahead
-      if (!/\s/.test(la)) {
-        // following character should be a new line (or whitespace) but it isn't
-        // force indentation then
+      if (!/\n/.test(la) || /^\s+$/.test(text.substring(i+1, text.length))) {
+        // following character should be a new line, but isn't,
+        // or it's whitespace at the end of the file
         parts.push(indent + text.substring(partStart, i + 1));
         if (c == "}") {
           parts.push(""); // for extra line separator
         }
         partStart = i + 1;
       } else {
         return text; // assume it is not minified, early exit
       }
--- a/browser/devtools/styleeditor/test/browser.ini
+++ b/browser/devtools/styleeditor/test/browser.ini
@@ -9,16 +9,17 @@ support-files =
   import2.css
   inline-1.html
   inline-2.html
   longload.html
   media-small.css
   media.html
   minified.html
   nostyle.html
+  pretty.css
   resources_inpage.jsi
   resources_inpage1.css
   resources_inpage2.css
   simple.css
   simple.css.gz
   simple.css.gz^headers^
   simple.gz.html
   simple.html
@@ -40,17 +41,15 @@ skip-if = os == "linux" || "mac" # bug 9
 [browser_styleeditor_import.js]
 [browser_styleeditor_import_rule.js]
 [browser_styleeditor_init.js]
 [browser_styleeditor_inline_friendly_names.js]
 [browser_styleeditor_loading.js]
 [browser_styleeditor_new.js]
 [browser_styleeditor_nostyle.js]
 [browser_styleeditor_pretty.js]
-# Disabled because of intermittent failures - See Bug 942473
-skip-if = true
 [browser_styleeditor_private_perwindowpb.js]
 [browser_styleeditor_reload.js]
 [browser_styleeditor_sv_keynav.js]
 [browser_styleeditor_sv_resize.js]
 [browser_styleeditor_selectstylesheet.js]
 [browser_styleeditor_sourcemaps.js]
 [browser_styleeditor_sourcemap_watching.js]
--- a/browser/devtools/styleeditor/test/minified.html
+++ b/browser/devtools/styleeditor/test/minified.html
@@ -1,15 +1,13 @@
 <!doctype html>
 <html>
 <head>
   <title>minified testcase</title>
-  <style type="text/css"><!--
-body{background:white;}div{font-size:4em;color:red}
---></style>
+  <link rel="stylesheet" href="pretty.css"/>
   <style type="text/css">body { background: red; }
 div {
 font-size: 5em;
 color: red
 }</style>
 </head>
 <body>
 	<div>minified <span>testcase</span></div>
new file mode 100644
--- /dev/null
+++ b/browser/devtools/styleeditor/test/pretty.css
@@ -0,0 +1,5 @@
+
+
+body{background:white;}div{font-size:4em;color:red}
+
+
--- a/browser/themes/osx/browser-lightweightTheme.css
+++ b/browser/themes/osx/browser-lightweightTheme.css
@@ -21,8 +21,16 @@
 #tabbrowser-tabs:not([movingtab]) > .tabbrowser-tab > .tab-stack > .tab-background > .tab-background-middle[selected=true]:-moz-lwtheme {
   background-attachment: scroll, scroll, fixed;
   background-color: transparent;
   background-image: url(chrome://browser/skin/tabbrowser/tab-active-middle.png),
                     @fgTabTextureLWT@;/*,
                     lwtHeader;*/
   background-position: 0 0, 0 0, right top;
 }
+
+@media (min-resolution: 2dppx) {
+  #tabbrowser-tabs:not([movingtab]) > .tabbrowser-tab > .tab-stack > .tab-background > .tab-background-middle[selected=true]:-moz-lwtheme {
+    background-image: url(chrome://browser/skin/tabbrowser/tab-active-middle@2x.png),
+                      @fgTabTextureLWT@;/*,
+                      lwtHeader;*/
+  }
+}
--- a/browser/themes/windows/browser.css
+++ b/browser/themes/windows/browser.css
@@ -112,28 +112,69 @@
   }
 %ifdef WINDOWS_AERO
 }
 %endif
 
 /**
  * In the classic themes, the titlebar has a horizontal gradient, which is
  * problematic for reading the text of background tabs when they're in the
- * titlebar. We side-step this issue by layering our own gradient underneath
- * the tabs.
+ * titlebar. We side-step this issue by layering our own background underneath
+ * the tabs. Unfortunately, this requires a bunch of positioning in order to get
+ * text and icons to not appear fuzzy.
  */
 @media (-moz-windows-classic) {
-  #main-window[tabsintitlebar]:not([inFullscreen]) #TabsToolbar:not(:-moz-lwtheme) {
-    background: linear-gradient(to top, @toolbarShadowColor@ 2px, transparent 2px),
-                linear-gradient(rgba(50%,50%,50%,0), ActiveCaption 85%);
+  #main-window[tabsintitlebar]:not([sizemode=fullscreen]) .tabbrowser-tab:not([selected=true]),
+  #main-window[tabsintitlebar]:not([sizemode=fullscreen]) .tabs-newtab-button {
+    position: relative;
+    z-index: 1;
+  }
+
+  #main-window[tabsintitlebar] #TabsToolbar:not(:-moz-lwtheme) {
+    background-image: none;
+    position: relative;
   }
 
-  #main-window[tabsintitlebar]:not([inFullscreen]) #TabsToolbar:not(:-moz-lwtheme):-moz-window-inactive {
-    background: linear-gradient(to top, @toolbarShadowColor@ 2px, transparent 2px),
-                linear-gradient(rgba(50%,50%,50%,0), InactiveCaption 85%);
+  #main-window[tabsintitlebar]:not([sizemode=fullscreen]) #TabsToolbar:not(:-moz-lwtheme)::after {
+    /* Because we use placeholders for window controls etc. in the tabstrip,
+     * and position those with ordinal attributes, and because our layout code
+     * expects :before/:after nodes to come first/last in the frame list,
+     * we have to reorder this element to come last, hence the
+     * ordinal group value (see bug 853415). */
+    -moz-box-ordinal-group: 1001;
+    box-shadow: 0 0 50px 8px ActiveCaption;
+    content: "";
+    display: -moz-box;
+    height: 0;
+    margin: 0 50px;
+    position: absolute;
+    pointer-events: none;
+    top: 100%;
+    width: -moz-available;
+    z-index: 0;
+  }
+
+  #main-window[tabsintitlebar]:not([sizemode=fullscreen]) #TabsToolbar:not(:-moz-lwtheme):-moz-window-inactive::after {
+    box-shadow: 0 0 50px 8px InactiveCaption;
+  }
+
+  /* Need to constrain the box shadow fade to avoid overlapping layers, see bug 886281. */
+  #main-window[tabsintitlebar]:not([sizemode=fullscreen]) #navigator-toolbox:not(:-moz-lwtheme) {
+    overflow: -moz-hidden-unscrollable;
+  }
+
+  #main-window[tabsintitlebar][sizemode=normal] .tabbrowser-arrowscrollbox > .arrowscrollbox-scrollbox > .scrollbox-innerbox:not(:-moz-lwtheme) {
+    position: relative;
+  }
+
+  #main-window[tabsintitlebar]:not([sizemode=fullscreen]) #TabsToolbar .toolbarbutton-1,
+  #main-window[tabsintitlebar]:not([sizemode=fullscreen]) .tabbrowser-arrowscrollbox > .scrollbutton-up,
+  #main-window[tabsintitlebar]:not([sizemode=fullscreen]) .tabbrowser-arrowscrollbox > .scrollbutton-down {
+    position: relative;
+    z-index: 1;
   }
 
   #main-window[tabsintitlebar][sizemode="normal"] #titlebar-content:-moz-lwtheme {
     /* Render a window top border: */
     background-image: linear-gradient(to bottom,
           ThreeDLightShadow 0, ThreeDLightShadow 1px,
           ThreeDHighlight 1px, ThreeDHighlight 2px,
           ActiveBorder 2px, ActiveBorder 4px, transparent 4px);
@@ -917,16 +958,18 @@ toolbarbutton[sdk-button="true"][cui-are
 
 #main-window[tabsintitlebar]:not([inFullscreen]) :-moz-any(#TabsToolbar, #toolbar-menubar) > #bookmarks-menu-button > .toolbarbutton-menubutton-dropmarker > .dropmarker-icon:-moz-system-metric(windows-classic):not(:-moz-lwtheme),
 #main-window[tabsintitlebar]:not([inFullscreen]) :-moz-any(#TabsToolbar, #toolbar-menubar) > toolbarpaletteitem > #bookmarks-menu-button > .toolbarbutton-menubutton-dropmarker > .dropmarker-icon:-moz-system-metric(windows-classic):not(:-moz-lwtheme),
 #main-window[tabsintitlebar]:not([inFullscreen]) :-moz-any(#TabsToolbar, #toolbar-menubar) > toolbarpaletteitem > toolbaritem > :-moz-any(@nestedButtons@):-moz-system-metric(windows-classic):not(:-moz-lwtheme),
 #main-window[tabsintitlebar]:not([inFullscreen]) :-moz-any(#TabsToolbar, #toolbar-menubar) > toolbaritem > :-moz-any(@nestedButtons@):-moz-system-metric(windows-classic):not(:-moz-lwtheme),
 #main-window[tabsintitlebar]:not([inFullscreen]) :-moz-any(#TabsToolbar, #toolbar-menubar) > toolbarpaletteitem > :-moz-any(@primaryToolbarButtons@):-moz-system-metric(windows-classic):not(:-moz-lwtheme),
 #main-window[tabsintitlebar]:not([inFullscreen]) :-moz-any(#TabsToolbar, #toolbar-menubar) > :-moz-any(@primaryToolbarButtons@):-moz-system-metric(windows-classic):not(:-moz-lwtheme),
 #home-button.bookmark-item:-moz-lwtheme-brighttext {
+  position: relative;
+  z-index: 1;
   list-style-image: url("chrome://browser/skin/Toolbar-inverted.png");
 }
 
 #sync-button[cui-areatype="toolbar"][status="active"] {
   /* !important because we need to override the glass selectors that trigger
    * use of the Toolbar-inverted image. Those use a list of all primary toolbar
    * buttons, so we can't easily fix those selectors. */
   list-style-image: url("chrome://browser/skin/sync-throbber.png") !important;
--- a/dom/mobilemessage/src/MobileMessageManager.cpp
+++ b/dom/mobilemessage/src/MobileMessageManager.cpp
@@ -190,17 +190,17 @@ MobileMessageManager::Send(JS::Handle<JS
   nsCOMPtr<nsISmsService> smsService = do_GetService(SMS_SERVICE_CONTRACTID);
   NS_ENSURE_TRUE(smsService, NS_ERROR_FAILURE);
 
   // Use the default one unless |aSendParams.serviceId| is available.
   uint32_t serviceId;
   rv = smsService->GetSmsDefaultServiceId(&serviceId);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  if (aArgc == 3) {
+  if (aArgc == 1) {
     JS::Rooted<JS::Value> param(aCx, aSendParams);
     RootedDictionary<SmsSendParameters> sendParams(aCx);
     if (!sendParams.Init(aCx, param)) {
       return NS_ERROR_TYPE_ERR;
     }
     if (sendParams.mServiceId.WasPassed()) {
       serviceId = sendParams.mServiceId.Value();
     }
@@ -260,17 +260,17 @@ MobileMessageManager::SendMMS(JS::Handle
   nsCOMPtr<nsIMmsService> mmsService = do_GetService(MMS_SERVICE_CONTRACTID);
   NS_ENSURE_TRUE(mmsService, NS_ERROR_FAILURE);
 
   // Use the default one unless |aSendParams.serviceId| is available.
   uint32_t serviceId;
   nsresult rv = mmsService->GetMmsDefaultServiceId(&serviceId);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  if (aArgc == 2) {
+  if (aArgc == 1) {
     JS::Rooted<JS::Value> param(aCx, aSendParams);
     RootedDictionary<MmsSendParameters> sendParams(aCx);
     if (!sendParams.Init(aCx, param)) {
       return NS_ERROR_TYPE_ERR;
     }
     if (sendParams.mServiceId.WasPassed()) {
       serviceId = sendParams.mServiceId.Value();
     }
--- a/mobile/android/base/Makefile.in
+++ b/mobile/android/base/Makefile.in
@@ -177,16 +177,18 @@ preprocessed_package_KEEP_PATH := 1
 PP_TARGETS += preprocessed_package
 
 include $(topsrcdir)/config/rules.mk
 
 not_android_res_files := \
   *.mkdir.done* \
   *.DS_Store* \
   *\#* \
+  *.rej \
+  *.orig \
   $(NULL)
 
 # This uses the fact that Android resource directories list all
 # resource files one subdirectory below the parent resource directory.
 android_res_files := $(filter-out $(not_android_res_files),$(wildcard $(addsuffix /*,$(wildcard $(addsuffix /*,$(ANDROID_RES_DIRS))))))
 
 $(ANDROID_GENERATED_RESFILES): $(call mkdir_deps,$(sort $(dir $(ANDROID_GENERATED_RESFILES))))
 
@@ -248,19 +250,20 @@ R.txt: .aapt.deps ;
 
 gecko-nodeps/R.java: .aapt.nodeps ;
 gecko-nodeps.ap_: .aapt.nodeps ;
 gecko-nodeps/R.txt: .aapt.nodeps ;
 
 # This ignores the default set of resources ignored by aapt, plus
 # files starting with '#'.  (Emacs produces temp files named #temp#.)
 # This doesn't actually set the environment variable; it's used as a
-# parameter in the aapt invocation below.
+# parameter in the aapt invocation below.  Consider updating
+# not_android_res_files as well.
 
-ANDROID_AAPT_IGNORE := !.svn:!.git:.*:<dir>_*:!CVS:!thumbs.db:!picasa.ini:!*.scc:*~:\#*
+ANDROID_AAPT_IGNORE := !.svn:!.git:.*:<dir>_*:!CVS:!thumbs.db:!picasa.ini:!*.scc:*~:\#*:*.rej:*.orig
 
 # 1: target file.
 # 2: dependencies.
 # 3: name of ap_ file to write.
 # 4: directory to write R.java into.
 # 5: directory to write R.txt into.
 # We touch the target file before invoking aapt so that aapt's outputs
 # are fresher than the target, preventing a subsequent invocation from
--- a/mobile/android/base/sync/GlobalSession.java
+++ b/mobile/android/base/sync/GlobalSession.java
@@ -13,16 +13,17 @@ import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Set;
 import java.util.concurrent.atomic.AtomicLong;
 
+import org.json.simple.JSONArray;
 import org.json.simple.parser.ParseException;
 import org.mozilla.gecko.background.common.log.Logger;
 import org.mozilla.gecko.sync.crypto.CryptoException;
 import org.mozilla.gecko.sync.crypto.KeyBundle;
 import org.mozilla.gecko.sync.delegates.BaseGlobalSessionCallback;
 import org.mozilla.gecko.sync.delegates.ClientsDataDelegate;
 import org.mozilla.gecko.sync.delegates.FreshStartDelegate;
 import org.mozilla.gecko.sync.delegates.JSONRecordFetchDelegate;
@@ -369,16 +370,17 @@ public class GlobalSession implements Pr
       Logger.trace(LOG_TAG, "Uploading updated meta/global record since there are engine changes to meta/global.");
       Logger.trace(LOG_TAG, "Engines requesting update [" + Utils.toCommaSeparatedString(enginesToUpdate.keySet()) + "]");
     }
 
     return true;
   }
 
   public void updateMetaGlobalInPlace() {
+    config.metaGlobal.declined = this.declinedEngineNames();
     ExtendedJSONObject engines = config.metaGlobal.getEngines();
     for (Entry<String, EngineSettings> pair : enginesToUpdate.entrySet()) {
       if (pair.getValue() == null) {
         engines.remove(pair.getKey());
       } else {
         engines.put(pair.getKey(), pair.getValue().toJSONObject());
       }
     }
@@ -646,16 +648,48 @@ public class GlobalSession implements Pr
     if (config.enabledEngineNames == null) {
       Logger.warn(LOG_TAG, "meta/global reported no enabled engine names!");
     } else {
       if (Logger.shouldLogVerbose(LOG_TAG)) {
         Logger.trace(LOG_TAG, "Persisting enabled engine names '" +
             Utils.toCommaSeparatedString(config.enabledEngineNames) + "' from meta/global.");
       }
     }
+
+    // Persist declined.
+    // Our declined engines at any point are:
+    // Whatever they were remotely, plus whatever they were locally, less any
+    // engines that were just enabled locally or remotely.
+    // If remote just 'won', our recently enabled list just got cleared.
+    final HashSet<String> allDeclined = new HashSet<String>();
+
+    final Set<String> newRemoteDeclined = global.getDeclinedEngineNames();
+    final Set<String> oldLocalDeclined = config.declinedEngineNames;
+
+    allDeclined.addAll(newRemoteDeclined);
+    allDeclined.addAll(oldLocalDeclined);
+
+    if (config.userSelectedEngines != null) {
+      for (Entry<String, Boolean> selection : config.userSelectedEngines.entrySet()) {
+        if (selection.getValue()) {
+          allDeclined.remove(selection.getKey());
+        }
+      }
+    }
+
+    config.declinedEngineNames = allDeclined;
+    if (config.declinedEngineNames.isEmpty()) {
+      Logger.debug(LOG_TAG, "meta/global reported no declined engine names, and we have none declined locally.");
+    } else {
+      if (Logger.shouldLogVerbose(LOG_TAG)) {
+        Logger.trace(LOG_TAG, "Persisting declined engine names '" +
+            Utils.toCommaSeparatedString(config.declinedEngineNames) + "' from meta/global.");
+      }
+    }
+
     config.persistToPrefs();
     advance();
   }
 
   public void processMissingMetaGlobal(MetaGlobal global) {
     freshStart();
   }
 
@@ -897,16 +931,37 @@ public class GlobalSession implements Pr
     resetStages(this.getSyncStagesByEnum(stages));
   }
 
   public void resetStagesByName(Collection<String> names) {
     resetStages(this.getSyncStagesByName(names));
   }
 
   /**
+   * Engines to explicitly mark as declined in a fresh meta/global record.
+   * <p>
+   * Returns an empty array if the user hasn't elected to customize data types,
+   * or an array of engines that the user un-checked during customization.
+   * <p>
+   * Engines that Android Sync doesn't recognize are <b>not</b> included in
+   * the returned array.
+   *
+   * @return a new JSONArray of engine names.
+   */
+  @SuppressWarnings("unchecked")
+  protected JSONArray declinedEngineNames() {
+    final JSONArray declined = new JSONArray();
+    for (String engine : config.declinedEngineNames) {
+      declined.add(engine);
+    };
+
+    return declined;
+  }
+
+  /**
    * Engines to include in a fresh meta/global record.
    * <p>
    * Returns either the persisted engine names (perhaps we have been node
    * re-assigned and are initializing a clean server: we want to upload the
    * persisted engine names so that we don't accidentally disable engines that
    * Android Sync doesn't recognize), or the set of engines names that Android
    * Sync implements.
    *
@@ -978,16 +1033,20 @@ public class GlobalSession implements Pr
       engines.put(engineName, engineSettings.toJSONObject());
     }
 
     MetaGlobal metaGlobal = new MetaGlobal(metaURL, this.getAuthHeaderProvider());
     metaGlobal.setSyncID(newSyncID);
     metaGlobal.setStorageVersion(STORAGE_VERSION);
     metaGlobal.setEngines(engines);
 
+    // We assume that the config's declined engines have been updated
+    // according to the user's selections.
+    metaGlobal.setDeclinedEngineNames(this.declinedEngineNames());
+
     return metaGlobal;
   }
 
   /**
    * Suggest that your Sync client needs to be upgraded to work
    * with this server.
    */
   public void requiresUpgrade() {
--- a/mobile/android/base/sync/MetaGlobal.java
+++ b/mobile/android/base/sync/MetaGlobal.java
@@ -1,37 +1,40 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.gecko.sync;
 
 import java.io.IOException;
 import java.net.URISyntaxException;
+import java.util.Collection;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Map;
 import java.util.Set;
 
+import org.json.simple.JSONArray;
 import org.json.simple.parser.ParseException;
 import org.mozilla.gecko.background.common.log.Logger;
 import org.mozilla.gecko.sync.MetaGlobalException.MetaGlobalMalformedSyncIDException;
 import org.mozilla.gecko.sync.MetaGlobalException.MetaGlobalMalformedVersionException;
 import org.mozilla.gecko.sync.delegates.MetaGlobalDelegate;
 import org.mozilla.gecko.sync.net.AuthHeaderProvider;
 import org.mozilla.gecko.sync.net.SyncStorageRecordRequest;
 import org.mozilla.gecko.sync.net.SyncStorageRequestDelegate;
 import org.mozilla.gecko.sync.net.SyncStorageResponse;
 
 public class MetaGlobal implements SyncStorageRequestDelegate {
   private static final String LOG_TAG = "MetaGlobal";
   protected String metaURL;
 
   // Fields.
   protected ExtendedJSONObject  engines;
+  protected JSONArray           declined;
   protected Long                storageVersion;
   protected String              syncID;
 
   // Lookup tables.
   protected Map<String, String> syncIDs;
   protected Map<String, Integer> versions;
   protected Map<String, MetaGlobalException> exceptions;
 
@@ -72,54 +75,112 @@ public class MetaGlobal implements SyncS
     }
   }
 
   protected ExtendedJSONObject asRecordContents() {
     ExtendedJSONObject json = new ExtendedJSONObject();
     json.put("storageVersion", storageVersion);
     json.put("engines", engines);
     json.put("syncID", syncID);
+    json.put("declined", declined);
     return json;
   }
 
   /**
    * Return a copy ready for upload.
    * @return an unencrypted <code>CryptoRecord</code>.
    */
   public CryptoRecord asCryptoRecord() {
     ExtendedJSONObject payload = this.asRecordContents();
     CryptoRecord record = new CryptoRecord(payload);
     record.collection = "meta";
     record.guid       = "global";
     record.deleted    = false;
     return record;
   }
 
-  public void setFromRecord(CryptoRecord record) throws IllegalStateException, IOException, ParseException, NonObjectJSONException {
+  public void setFromRecord(CryptoRecord record) throws IllegalStateException, IOException, ParseException, NonObjectJSONException, NonArrayJSONException {
     if (record == null) {
       throw new IllegalArgumentException("Cannot set meta/global from null record");
     }
     Logger.debug(LOG_TAG, "meta/global is " + record.payload.toJSONString());
     this.storageVersion = (Long) record.payload.get("storageVersion");
     this.syncID = (String) record.payload.get("syncID");
+
     setEngines(record.payload.getObject("engines"));
+
+    // Accepts null -- declined can be missing.
+    setDeclinedEngineNames(record.payload.getArray("declined"));
   }
 
   public Long getStorageVersion() {
     return this.storageVersion;
   }
 
   public void setStorageVersion(Long version) {
     this.storageVersion = version;
   }
 
   public ExtendedJSONObject getEngines() {
     return engines;
   }
 
+  @SuppressWarnings("unchecked")
+  public void declineEngine(String engine) {
+    if (this.declined == null) {
+      JSONArray replacement = new JSONArray();
+      replacement.add(engine);
+      setDeclinedEngineNames(replacement);
+      return;
+    }
+
+    this.declined.add(engine);
+  }
+
+  @SuppressWarnings("unchecked")
+  public void declineEngineNames(Collection<String> additional) {
+    if (this.declined == null) {
+      JSONArray replacement = new JSONArray();
+      replacement.addAll(additional);
+      setDeclinedEngineNames(replacement);
+      return;
+    }
+
+    for (String engine : additional) {
+      if (!this.declined.contains(engine)) {
+        this.declined.add(engine);
+      }
+    }
+  }
+
+  public void setDeclinedEngineNames(JSONArray declined) {
+    if (declined == null) {
+      this.declined = new JSONArray();
+      return;
+    }
+    this.declined = declined;
+  }
+
+  /**
+   * Return the set of engines that we support (given as an argument)
+   * but the user hasn't explicitly declined on another device.
+   *
+   * Can return the input if the user hasn't declined any engines.
+   */
+  public Set<String> getNonDeclinedEngineNames(Set<String> supported) {
+    if (this.declined == null ||
+        this.declined.isEmpty()) {
+      return supported;
+    }
+
+    final Set<String> result = new HashSet<String>(supported);
+    result.removeAll(this.declined);
+    return result;
+  }
+
   public void setEngines(ExtendedJSONObject engines) {
     if (engines == null) {
       engines = new ExtendedJSONObject();
     }
     this.engines = engines;
     final int count = engines.size();
     versions   = new HashMap<String, Integer>(count);
     syncIDs    = new HashMap<String, String>(count);
@@ -191,16 +252,24 @@ public class MetaGlobal implements SyncS
    */
   public Set<String> getEnabledEngineNames() {
     if (engines == null) {
       return null;
     }
     return new HashSet<String>(engines.keySet());
   }
 
+  @SuppressWarnings("unchecked")
+  public Set<String> getDeclinedEngineNames() {
+    if (declined == null) {
+      return null;
+    }
+    return new HashSet<String>(declined);
+  }
+
   /**
    * Returns if the server settings and local settings match.
    * Throws a specific MetaGlobalException if that's not the case.
    */
   public void verifyEngineSettings(String engineName, EngineSettings engineSettings)
   throws MetaGlobalException {
 
     // We use syncIDs as our canary.
--- a/mobile/android/base/sync/SyncConfiguration.java
+++ b/mobile/android/base/sync/SyncConfiguration.java
@@ -193,16 +193,17 @@ public class SyncConfiguration {
    * <p>
    * Can contain engines Android Sync is not currently aware of, such as "prefs"
    * or "addons".
    * <p>
    * Copied from latest downloaded meta/global record and used to generate a
    * fresh meta/global record for upload.
    */
   public Set<String> enabledEngineNames;
+  public Set<String> declinedEngineNames = new HashSet<String>();
 
   /**
    * Names of stages to sync <it>this sync</it>, or <code>null</code> to sync
    * all known stages.
    * <p>
    * Generated <it>each sync</it> from extras bundle passed to
    * <code>SyncAdapter.onPerformSync</code> and not persisted.
    * <p>
@@ -243,16 +244,17 @@ public class SyncConfiguration {
 
   public static final String CLIENTS_COLLECTION_TIMESTAMP = "serverClientsTimestamp";  // When the collection was touched.
   public static final String CLIENT_RECORD_TIMESTAMP = "serverClientRecordTimestamp";  // When our record was touched.
 
   public static final String PREF_CLUSTER_URL = "clusterURL";
   public static final String PREF_SYNC_ID = "syncID";
 
   public static final String PREF_ENABLED_ENGINE_NAMES = "enabledEngineNames";
+  public static final String PREF_DECLINED_ENGINE_NAMES = "declinedEngineNames";
   public static final String PREF_USER_SELECTED_ENGINES_TO_SYNC = "userSelectedEngines";
   public static final String PREF_USER_SELECTED_ENGINES_TO_SYNC_TIMESTAMP = "userSelectedEnginesTimestamp";
 
   public static final String PREF_CLUSTER_URL_IS_STALE = "clusterurlisstale";
 
   public static final String PREF_ACCOUNT_GUID = "account.guid";
   public static final String PREF_CLIENT_NAME = "account.clientName";
   public static final String PREF_NUM_CLIENTS = "account.numClients";
@@ -306,38 +308,58 @@ public class SyncConfiguration {
    *        A ConfigurationBranch object representing this
    *        section of the preferences space.
    */
   public ConfigurationBranch getBranch(String prefix) {
     return new ConfigurationBranch(this, prefix);
   }
 
   /**
-   * Gets the engine names that are enabled in meta/global.
+   * Gets the engine names that are enabled, declined, or other (depending on pref) in meta/global.
    *
    * @param prefs
    *          SharedPreferences that the engines are associated with.
+   * @param pref
+   *          The preference name to use. E.g, PREF_ENABLED_ENGINE_NAMES.
    * @return Set<String> of the enabled engine names if they have been stored,
    *         or null otherwise.
    */
-  public static Set<String> getEnabledEngineNames(SharedPreferences prefs) {
-    String json = prefs.getString(PREF_ENABLED_ENGINE_NAMES, null);
+  protected static Set<String> getEngineNamesFromPref(SharedPreferences prefs, String pref) {
+    final String json = prefs.getString(pref, null);
     if (json == null) {
       return null;
     }
     try {
-      ExtendedJSONObject o = ExtendedJSONObject.parseJSONObject(json);
+      final ExtendedJSONObject o = ExtendedJSONObject.parseJSONObject(json);
       return new HashSet<String>(o.keySet());
     } catch (Exception e) {
-      // enabledEngineNames can be null.
       return null;
     }
   }
 
   /**
+   * Returns the set of engine names that the user has enabled. If none
+   * have been stored in prefs, <code>null</code> is returned.
+   */
+  public static Set<String> getEnabledEngineNames(SharedPreferences prefs) {
+      return getEngineNamesFromPref(prefs, PREF_ENABLED_ENGINE_NAMES);
+  }
+
+  /**
+   * Returns the set of engine names that the user has declined.
+   */
+  public static Set<String> getDeclinedEngineNames(SharedPreferences prefs) {
+    final Set<String> names = getEngineNamesFromPref(prefs, PREF_DECLINED_ENGINE_NAMES);
+    if (names == null) {
+        return new HashSet<String>();
+    }
+    return names;
+  }
+
+  /**
    * Gets the engines whose sync states have been changed by the user through the
    * SelectEnginesActivity.
    *
    * @param prefs
    *          SharedPreferences of account that the engines are associated with.
    * @return Map<String, Boolean> of changed engines. Key is the lower-cased
    *         engine name, Value is the new sync state.
    */
@@ -366,80 +388,106 @@ public class SyncConfiguration {
     } catch (Exception e) {
       return null;
     }
   }
 
   /**
    * Store a Map of engines and their sync states to prefs.
    *
+   * Any engine that's disabled in the input is also recorded
+   * as a declined engine, overwriting the stored values.
+   *
    * @param prefs
    *          SharedPreferences that the engines are associated with.
    * @param selectedEngines
    *          Map<String, Boolean> of engine name to sync state
    */
   public static void storeSelectedEnginesToPrefs(SharedPreferences prefs, Map<String, Boolean> selectedEngines) {
     ExtendedJSONObject jObj = new ExtendedJSONObject();
+    HashSet<String> declined = new HashSet<String>();
     for (Entry<String, Boolean> e : selectedEngines.entrySet()) {
-      jObj.put(e.getKey(), e.getValue());
+      final Boolean enabled = e.getValue();
+      final String engine = e.getKey();
+      jObj.put(engine, enabled);
+      if (!enabled) {
+        declined.add(engine);
+      }
     }
+
+    // Our history checkbox drives form history, too.
+    // We don't need to do this for enablement: that's done at retrieval time.
+    if (selectedEngines.containsKey("history") && !selectedEngines.get("history").booleanValue()) {
+        declined.add("forms");
+    }
+
     String json = jObj.toJSONString();
     long currentTime = System.currentTimeMillis();
     Editor edit = prefs.edit();
     edit.putString(PREF_USER_SELECTED_ENGINES_TO_SYNC, json);
+    edit.putString(PREF_DECLINED_ENGINE_NAMES, setToJSONObjectString(declined));
     edit.putLong(PREF_USER_SELECTED_ENGINES_TO_SYNC_TIMESTAMP, currentTime);
     Logger.error(LOG_TAG, "Storing user-selected engines at [" + currentTime + "].");
     edit.commit();
   }
 
   public void loadFromPrefs(SharedPreferences prefs) {
-
     if (prefs.contains(PREF_CLUSTER_URL)) {
       String u = prefs.getString(PREF_CLUSTER_URL, null);
       try {
         clusterURL = new URI(u);
         Logger.trace(LOG_TAG, "Set clusterURL from bundle: " + u);
       } catch (URISyntaxException e) {
         Logger.warn(LOG_TAG, "Ignoring bundle clusterURL (" + u + "): invalid URI.", e);
       }
     }
     if (prefs.contains(PREF_SYNC_ID)) {
       syncID = prefs.getString(PREF_SYNC_ID, null);
       Logger.trace(LOG_TAG, "Set syncID from bundle: " + syncID);
     }
     enabledEngineNames = getEnabledEngineNames(prefs);
+    declinedEngineNames = getDeclinedEngineNames(prefs);
     userSelectedEngines = getUserSelectedEngines(prefs);
     userSelectedEnginesTimestamp = prefs.getLong(PREF_USER_SELECTED_ENGINES_TO_SYNC_TIMESTAMP, 0);
     // We don't set crypto/keys here because we need the syncKeyBundle to decrypt the JSON
     // and we won't have it on construction.
     // TODO: MetaGlobal, password, infoCollections.
   }
 
   public void persistToPrefs() {
     this.persistToPrefs(this.getPrefs());
   }
 
+  private static String setToJSONObjectString(Set<String> set) {
+    ExtendedJSONObject o = new ExtendedJSONObject();
+    for (String name : set) {
+      o.put(name, 0);
+    }
+    return o.toJSONString();
+  }
+
   public void persistToPrefs(SharedPreferences prefs) {
     Editor edit = prefs.edit();
     if (clusterURL == null) {
       edit.remove(PREF_CLUSTER_URL);
     } else {
       edit.putString(PREF_CLUSTER_URL, clusterURL.toASCIIString());
     }
     if (syncID != null) {
       edit.putString(PREF_SYNC_ID, syncID);
     }
     if (enabledEngineNames == null) {
       edit.remove(PREF_ENABLED_ENGINE_NAMES);
     } else {
-      ExtendedJSONObject o = new ExtendedJSONObject();
-      for (String engineName : enabledEngineNames) {
-        o.put(engineName, 0);
-      }
-      edit.putString(PREF_ENABLED_ENGINE_NAMES, o.toJSONString());
+      edit.putString(PREF_ENABLED_ENGINE_NAMES, setToJSONObjectString(enabledEngineNames));
+    }
+    if (declinedEngineNames.isEmpty()) {
+      edit.remove(PREF_DECLINED_ENGINE_NAMES);
+    } else {
+      edit.putString(PREF_DECLINED_ENGINE_NAMES, setToJSONObjectString(declinedEngineNames));
     }
     if (userSelectedEngines == null) {
       edit.remove(PREF_USER_SELECTED_ENGINES_TO_SYNC);
       edit.remove(PREF_USER_SELECTED_ENGINES_TO_SYNC_TIMESTAMP);
     }
     // Don't bother saving userSelectedEngines - these should only be changed by
     // SelectEnginesActivity.
     edit.commit();
--- a/mobile/android/base/sync/stage/ServerSyncStage.java
+++ b/mobile/android/base/sync/stage/ServerSyncStage.java
@@ -510,17 +510,19 @@ public abstract class ServerSyncStage ex
       Logger.warn(LOG_TAG, "Remote engine syncID different from local engine syncID:" +
                            " resetting local engine and assuming remote engine syncID.");
       this.resetLocalWithSyncID(e.serverSyncID);
     } catch (MetaGlobalException.MetaGlobalEngineStateChangedException e) {
       boolean isEnabled = e.isEnabled;
       if (!isEnabled) {
         // Engine has been disabled; update meta/global with engine removal for upload.
         session.removeEngineFromMetaGlobal(name);
+        session.config.declinedEngineNames.add(name);
       } else {
+        session.config.declinedEngineNames.remove(name);
         // Add engine with new syncID to meta/global for upload.
         String newSyncID = Utils.generateGuid();
         session.recordForMetaGlobalUpdate(name, new EngineSettings(newSyncID, this.getStorageVersion()));
         // Update SynchronizerConfiguration w/ new engine syncID.
         this.resetLocalWithSyncID(newSyncID);
       }
       try {
         // Engine sync status has changed. Wipe server.
--- a/mobile/android/tests/background/junit3/src/sync/TestSyncConfiguration.java
+++ b/mobile/android/tests/background/junit3/src/sync/TestSyncConfiguration.java
@@ -20,16 +20,43 @@ public class TestSyncConfiguration exten
   /*
    * PrefsSource methods.
    */
   @Override
   public SharedPreferences getPrefs(String name, int mode) {
     return this.getApplicationContext().getSharedPreferences(name, mode);
   }
 
+  /**
+   * Ensure that declined engines persist through prefs.
+   */
+  public void testDeclinedEngineNames() {
+    SyncConfiguration config = null;
+    SharedPreferences prefs = getPrefs(TEST_PREFS_NAME, 0);
+
+    config = newSyncConfiguration();
+    config.declinedEngineNames = new HashSet<String>();
+    config.declinedEngineNames.add("test1");
+    config.declinedEngineNames.add("test2");
+    config.persistToPrefs();
+    assertTrue(prefs.contains(SyncConfiguration.PREF_DECLINED_ENGINE_NAMES));
+    config = newSyncConfiguration();
+    Set<String> expected = new HashSet<String>();
+    for (String name : new String[] { "test1", "test2" }) {
+      expected.add(name);
+    }
+    assertEquals(expected, config.declinedEngineNames);
+
+    config.declinedEngineNames = null;
+    config.persistToPrefs();
+    assertFalse(prefs.contains(SyncConfiguration.PREF_DECLINED_ENGINE_NAMES));
+    config = newSyncConfiguration();
+    assertNull(config.declinedEngineNames);
+  }
+
   public void testEnabledEngineNames() {
     SyncConfiguration config = null;
     SharedPreferences prefs = getPrefs(TEST_PREFS_NAME, 0);
 
     config = newSyncConfiguration();
     config.enabledEngineNames = new HashSet<String>();
     config.enabledEngineNames.add("test1");
     config.enabledEngineNames.add("test2");
--- a/security/manager/boot/src/nsSTSPreloadList.errors
+++ b/security/manager/boot/src/nsSTSPreloadList.errors
@@ -28,17 +28,16 @@ crypto.is: did not receive HSTS header
 csawctf.poly.edu: did not receive HSTS header
 dl.google.com: did not receive HSTS header (error ignored - included regardless)
 docs.google.com: did not receive HSTS header (error ignored - included regardless)
 drive.google.com: did not receive HSTS header (error ignored - included regardless)
 dropcam.com: did not receive HSTS header
 email.lookout.com: could not connect to host
 emailprivacytester.com: did not receive HSTS header
 encrypted.google.com: did not receive HSTS header (error ignored - included regardless)
-errors.zenpayroll.com: could not connect to host
 espra.com: could not connect to host
 fatzebra.com.au: did not receive HSTS header
 fj.simple.com: did not receive HSTS header
 get.zenpayroll.com: did not receive HSTS header
 glass.google.com: did not receive HSTS header (error ignored - included regardless)
 gmail.com: did not receive HSTS header
 gocardless.com: [Exception... "Component returned failure code: 0x80004005 (NS_ERROR_FAILURE) [nsISiteSecurityService.processHeader]"  nsresult: "0x80004005 (NS_ERROR_FAILURE)"  location: "JS frame :: /builds/slave/m-cen-l64-hsts-000000000000000/getHSTSPreloadList.js :: processStsHeader :: line 125"  data: no]
 googlemail.com: did not receive HSTS header
@@ -74,18 +73,16 @@ passport.yandex.com: did not receive HST
 passport.yandex.com.tr: did not receive HSTS header
 passport.yandex.kz: did not receive HSTS header
 passport.yandex.ru: did not receive HSTS header
 passport.yandex.ua: did not receive HSTS header
 paypal.com: max-age too low: 14400
 payroll.xero.com: max-age too low: 3600
 platform.lookout.com: could not connect to host
 play.google.com: did not receive HSTS header (error ignored - included regardless)
-plus.google.com: did not receive HSTS header (error ignored - included regardless)
-plus.sandbox.google.com: did not receive HSTS header (error ignored - included regardless)
 prodpad.com: did not receive HSTS header
 profiles.google.com: did not receive HSTS header (error ignored - included regardless)
 rapidresearch.me: did not receive HSTS header
 sah3.net: could not connect to host
 saturngames.co.uk: did not receive HSTS header
 script.google.com: did not receive HSTS header (error ignored - included regardless)
 security.google.com: did not receive HSTS header (error ignored - included regardless)
 serverdensity.io: did not receive HSTS header
--- a/security/manager/boot/src/nsSTSPreloadList.inc
+++ b/security/manager/boot/src/nsSTSPreloadList.inc
@@ -3,17 +3,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /*****************************************************************************/
 /* This is an automatically generated file. If you're not                    */
 /* nsSiteSecurityService.cpp, you shouldn't be #including it.     */
 /*****************************************************************************/
 
 #include <stdint.h>
-const PRTime gPreloadListExpirationTime = INT64_C(1405167392459000);
+const PRTime gPreloadListExpirationTime = INT64_C(1405764354321000);
 
 class nsSTSPreload
 {
   public:
     const char *mHost;
     const bool mIncludeSubdomains;
 };
 
@@ -148,21 +148,22 @@ static const nsSTSPreload kSTSPreloadLis
   { "paste.linode.com", false },
   { "pastebin.linode.com", false },
   { "pay.gigahost.dk", true },
   { "paymill.com", true },
   { "paymill.de", false },
   { "piratenlogin.de", true },
   { "pixi.me", true },
   { "play.google.com", false },
-  { "plus.google.com", true },
-  { "plus.sandbox.google.com", true },
+  { "plus.google.com", false },
+  { "plus.sandbox.google.com", false },
   { "profiles.google.com", true },
   { "publications.qld.gov.au", false },
   { "riseup.net", true },
+  { "roddis.net", false },
   { "romab.com", true },
   { "roundcube.mayfirst.org", false },
   { "sandbox.mydigipass.com", false },
   { "script.google.com", true },
   { "security.google.com", true },
   { "securityheaders.com", true },
   { "shodan.io", true },
   { "silentcircle.com", false },
@@ -206,16 +207,17 @@ static const nsSTSPreload kSTSPreloadLis
   { "www.irccloud.com", false },
   { "www.linode.com", false },
   { "www.lookout.com", false },
   { "www.makeyourlaws.org", false },
   { "www.mydigipass.com", false },
   { "www.mylookout.com", false },
   { "www.noisebridge.net", false },
   { "www.opsmate.com", true },
+  { "www.roddis.net", false },
   { "www.simbolo.co.uk", false },
   { "www.simple.com", false },
   { "www.therapynotes.com", false },
   { "www.torproject.org", false },
   { "www.twitter.com", false },
   { "www.zenpayroll.com", false },
   { "zenpayroll.com", false },
 };
--- a/security/sandbox/linux/seccomp_filter.h
+++ b/security/sandbox/linux/seccomp_filter.h
@@ -134,17 +134,18 @@
   ALLOW_SYSCALL(poll),
 
 #define SECCOMP_WHITELIST_B2G_LOW \
   SECCOMP_WHITELIST_ARCH_B2G_LOW \
   ALLOW_SYSCALL(getdents64), \
   ALLOW_SYSCALL(epoll_ctl), \
   ALLOW_SYSCALL(sched_yield), \
   ALLOW_SYSCALL(sched_getscheduler), \
-  ALLOW_SYSCALL(sched_setscheduler),
+  ALLOW_SYSCALL(sched_setscheduler), \
+  ALLOW_SYSCALL(sigaltstack),
 
 #else
 #define SECCOMP_WHITELIST_B2G_HIGH
 #define SECCOMP_WHITELIST_B2G_MED
 #define SECCOMP_WHITELIST_B2G_LOW
 #endif
 /* End of B2G specific syscalls */
 
--- a/services/fxaccounts/FxAccounts.jsm
+++ b/services/fxaccounts/FxAccounts.jsm
@@ -482,16 +482,22 @@ FxAccountsInternal.prototype = {
         throw new Error("Can't get keys; User is not signed in");
       }
       if (data.kA && data.kB) {
         return data;
       }
       if (!currentState.whenKeysReadyDeferred) {
         currentState.whenKeysReadyDeferred = Promise.defer();
         this.fetchAndUnwrapKeys(data.keyFetchToken).then(data => {
+          if (!data.kA || !data.kB) {
+            currentState.whenKeysReadyDeferred.reject(
+              new Error("user data missing kA or kB")
+            );
+            return;
+          }
           currentState.whenKeysReadyDeferred.resolve(data);
         });
       }
       return currentState.whenKeysReadyDeferred.promise;
     }).then(result => currentState.resolve(result));
    },
 
   fetchAndUnwrapKeys: function(keyFetchToken) {
--- a/services/sync/tests/unit/test_browserid_identity.js
+++ b/services/sync/tests/unit/test_browserid_identity.js
@@ -16,16 +16,27 @@ Cu.import("resource://services-common/to
 Cu.import("resource://services-sync/service.js");
 Cu.import("resource://services-sync/status.js");
 Cu.import("resource://services-sync/constants.js");
 
 const SECOND_MS = 1000;
 const MINUTE_MS = SECOND_MS * 60;
 const HOUR_MS = MINUTE_MS * 60;
 
+// This shouldn't be here - it should be part of the xpcshell harness.
+// Maybe as Assert.rejects - so we name it like that.
+function Assert_rejects(promise, message) {
+  let deferred = Promise.defer();
+  promise.then(
+    () => deferred.reject(message || "Expected the promise to be rejected"),
+    deferred.resolve
+  );
+  return deferred.promise;
+}
+
 let identityConfig = makeIdentityConfig();
 let browseridManager = new BrowserIDManager();
 configureFxAccountIdentity(browseridManager, identityConfig);
 
 /**
  * Mock client clock and skew vs server in FxAccounts signed-in user module and
  * API client.  browserid_identity.js queries these values to construct HAWK
  * headers.  We will use this to test clock skew compensation in these headers
@@ -249,30 +260,23 @@ add_task(function test_ensureLoggedIn() 
   // arrange for no logged in user.
   let fxa = browseridManager._fxaService
   let signedInUser = fxa.internal.currentAccountState.signedInUser;
   fxa.internal.currentAccountState.signedInUser = null;
   browseridManager.initializeWithCurrentIdentity();
   Assert.ok(!browseridManager._shouldHaveSyncKeyBundle,
             "_shouldHaveSyncKeyBundle should be false so we know we are testing what we think we are.");
   Status.login = LOGIN_FAILED_NO_USERNAME;
-  try {
-    yield browseridManager.ensureLoggedIn();
-    Assert.ok(false, "promise should have been rejected.")
-  } catch(_) {
-  }
+  yield Assert_rejects(browseridManager.ensureLoggedIn(), "expecting rejection due to no user");
   Assert.ok(browseridManager._shouldHaveSyncKeyBundle,
             "_shouldHaveSyncKeyBundle should always be true after ensureLogin completes.");
   fxa.internal.currentAccountState.signedInUser = signedInUser;
   Status.login = LOGIN_FAILED_LOGIN_REJECTED;
-  try {
-    yield browseridManager.ensureLoggedIn();
-    Assert.ok(false, "LOGIN_FAILED_LOGIN_REJECTED should have caused immediate rejection");
-  } catch (_) {
-  }
+  yield Assert_rejects(browseridManager.ensureLoggedIn(),
+                       "LOGIN_FAILED_LOGIN_REJECTED should have caused immediate rejection");
   Assert.equal(Status.login, LOGIN_FAILED_LOGIN_REJECTED,
                "status should remain LOGIN_FAILED_LOGIN_REJECTED");
   Status.login = LOGIN_FAILED_NETWORK_ERROR;
   yield browseridManager.ensureLoggedIn();
   Assert.equal(Status.login, LOGIN_SUCCEEDED, "final ensureLoggedIn worked");
 });
 
 add_test(function test_tokenExpiration() {
@@ -413,16 +417,62 @@ add_task(function test_getHAWKErrors() {
   yield initializeIdentityWithHAWKFailure({
     status: 200,
     headers: [],
     body: "",
   });
   Assert.equal(Status.login, LOGIN_FAILED_NETWORK_ERROR, "login state is LOGIN_FAILED_NETWORK_ERROR");
 });
 
+add_task(function test_getKeysError() {
+  _("BrowserIDManager correctly handles getKeys failures.");
+
+  let browseridManager = new BrowserIDManager();
+  let identityConfig = makeIdentityConfig();
+  // our mock identity config already has kA and kB - remove them or we never
+  // try and fetch them.
+  delete identityConfig.fxaccount.user.kA;
+  delete identityConfig.fxaccount.user.kB;
+
+  configureFxAccountIdentity(browseridManager, identityConfig);
+
+  // Mock a fxAccounts object that returns no keys
+  let fxa = new FxAccounts({
+    fetchAndUnwrapKeys: function () {
+      return Promise.resolve({});
+    },
+    fxAccountsClient: new MockFxAccountsClient()
+  });
+
+  // Add a mock to the currentAccountState object.
+  fxa.internal.currentAccountState.getCertificate = function(data, keyPair, mustBeValidUntil) {
+    this.cert = {
+      validUntil: fxa.internal.now() + CERT_LIFETIME,
+      cert: "certificate",
+    };
+    return Promise.resolve(this.cert.cert);
+  };
+
+  // Ensure the new FxAccounts mock has a signed-in user.
+  fxa.internal.currentAccountState.signedInUser = browseridManager._fxaService.internal.currentAccountState.signedInUser;
+
+  browseridManager._fxaService = fxa;
+
+  yield browseridManager.initializeWithCurrentIdentity();
+
+  let ex;
+  try {
+    yield browseridManager.whenReadyToAuthenticate.promise;
+  } catch (e) {
+    ex = e;
+  }
+
+  Assert.ok(ex.message.indexOf("missing kA or kB") >= 0);
+});
+
 // End of tests
 // Utility functions follow
 
 // Create a new browserid_identity object and initialize it with a
 // mocked TokenServerClient which always gets the specified response.
 function* initializeIdentityWithTokenServerFailure(response) {
   // First create a mock "request" object that well' hack into the token server.
   // A log for it
@@ -450,20 +500,18 @@ function* initializeIdentityWithTokenSer
     return new MockRESTRequest(url);
   }
   // tie it all together.
   let mockTSC = new MockTSC()
   configureFxAccountIdentity(browseridManager);
   browseridManager._tokenServerClient = mockTSC;
 
   yield browseridManager.initializeWithCurrentIdentity();
-  try {
-    yield browseridManager.whenReadyToAuthenticate.promise;
-    Assert.ok(false, "expecting this promise to resolve with an error");
-  } catch (ex) {}
+  yield Assert_rejects(browseridManager.whenReadyToAuthenticate.promise,
+                       "expecting rejection due to tokenserver error");
 }
 
 
 // Create a new browserid_identity object and initialize it with a
 // hawk mock that simulates a failure.
 // A token server mock will be used that doesn't hit a server, so we move
 // directly to a hawk request.
 function* initializeIdentityWithHAWKFailure(response) {
@@ -495,20 +543,18 @@ function* initializeIdentityWithHAWKFail
   let fxa = new FxAccounts(internal);
   fxa.internal.currentAccountState.signedInUser = {
       accountData: config.fxaccount.user,
   };
 
   browseridManager._fxaService = fxa;
   browseridManager._signedInUser = null;
   yield browseridManager.initializeWithCurrentIdentity();
-  try {
-    yield browseridManager.whenReadyToAuthenticate.promise;
-    Assert.ok(false, "expecting this promise to resolve with an error");
-  } catch (ex) {}
+  yield Assert_rejects(browseridManager.whenReadyToAuthenticate.promise,
+                       "expecting rejection due to hawk error");
 }
 
 
 function getTimestamp(hawkAuthHeader) {
   return parseInt(/ts="(\d+)"/.exec(hawkAuthHeader)[1], 10) * SECOND_MS;
 }
 
 function getTimestampDelta(hawkAuthHeader, now=Date.now()) {
--- a/toolkit/components/passwordmgr/LoginManagerContent.jsm
+++ b/toolkit/components/passwordmgr/LoginManagerContent.jsm
@@ -399,16 +399,24 @@ var LoginManagerContent = {
             return;
 
         var hostname = LoginUtils._getPasswordOrigin(doc.documentURI);
         if (!hostname) {
             log("(form submission ignored -- invalid hostname)");
             return;
         }
 
+        // Somewhat gross hack - we don't want to show the "remember password"
+        // notification on about:accounts for Firefox.
+        let topWin = win.top;
+        if (/^about:accounts($|\?)/i.test(topWin.document.documentURI)) {
+            log("(form submission ignored -- about:accounts)");
+            return;
+        }
+
         var formSubmitURL = LoginUtils._getActionOrigin(form)
         if (!Services.logins.getLoginSavingEnabled(hostname)) {
             log("(form submission ignored -- saving is disabled for:", hostname, ")");
             return;
         }
 
 
         // Get the appropriate fields from the form.
--- a/toolkit/devtools/Loader.jsm
+++ b/toolkit/devtools/Loader.jsm
@@ -114,17 +114,17 @@ SrcdirProvider.prototype = {
     let appActorURI = this.fileURI(OS.Path.join(toolkitDir, "apps", "app-actor-front.js"));
     let cssLogicURI = this.fileURI(OS.Path.join(toolkitDir, "styleinspector", "css-logic"));
     let cssColorURI = this.fileURI(OS.Path.join(toolkitDir, "css-color"));
     let outputParserURI = this.fileURI(OS.Path.join(toolkitDir, "output-parser"));
     let touchEventsURI = this.fileURI(OS.Path.join(toolkitDir, "touch-events"));
     let clientURI = this.fileURI(OS.Path.join(toolkitDir, "client"));
     let prettyFastURI = this.fileURI(OS.Path.join(toolkitDir), "pretty-fast.js");
     let asyncUtilsURI = this.fileURI(OS.Path.join(toolkitDir), "async-utils.js");
-    let gcliURI = this.fileURI(OS.Path.join(toolkitDir, "gcli"));
+    let gcliURI = this.fileURI(OS.Path.join(toolkitDir, "gcli", "source", "lib", "gcli"));
     let acornURI = this.fileURI(OS.Path.join(toolkitDir, "acorn"));
     let acornWalkURI = OS.Path.join(acornURI, "walk.js");
     this.loader = new loader.Loader({
       modules: {
         "Services": Object.create(Services),
         "toolkit/loader": loader,
         "source-map": SourceMap,
       },
--- a/toolkit/devtools/gcli/Makefile.in
+++ b/toolkit/devtools/gcli/Makefile.in
@@ -17,41 +17,32 @@ INSTALL_TARGETS += gcli_converters
 gcli_fields_FILES = $(wildcard $(srcdir)/source/lib/gcli/fields/*)
 gcli_fields_DEST = $(FINAL_TARGET)/modules/devtools/gcli/fields
 INSTALL_TARGETS += gcli_fields
 
 gcli_languages_FILES = $(wildcard $(srcdir)/source/lib/gcli/languages/*)
 gcli_languages_DEST = $(FINAL_TARGET)/modules/devtools/gcli/languages
 INSTALL_TARGETS += gcli_languages
 
-gcli_mozui_FILES = $(wildcard $(srcdir)/source/mozilla/gcli/mozui/*)
+gcli_mozui_FILES = $(wildcard $(srcdir)/source/lib/gcli/mozui/*)
 gcli_mozui_DEST = $(FINAL_TARGET)/modules/devtools/gcli/mozui
 INSTALL_TARGETS += gcli_mozui
 
 gcli_types_FILES = $(wildcard $(srcdir)/source/lib/gcli/types/*)
 gcli_types_DEST = $(FINAL_TARGET)/modules/devtools/gcli/types
 INSTALL_TARGETS += gcli_types
 
+gcli_ui_FILES = $(wildcard $(srcdir)/source/lib/gcli/ui/*)
+gcli_ui_DEST = $(FINAL_TARGET)/modules/devtools/gcli/ui
+INSTALL_TARGETS += gcli_ui
+
+gcli_util_FILES = $(wildcard $(srcdir)/source/lib/gcli/util/*)
+gcli_util_DEST = $(FINAL_TARGET)/modules/devtools/gcli/util
+INSTALL_TARGETS += gcli_util
+
+gcli_root_FILES = $(wildcard $(srcdir)/source/lib/gcli/*)
+gcli_root_DEST = $(FINAL_TARGET)/modules/devtools/gcli
+INSTALL_TARGETS += gcli_root
+
 include $(topsrcdir)/config/rules.mk
 
 libs::
 	$(INSTALL) $(IFLAGS1) $(srcdir)/*.jsm $(FINAL_TARGET)/modules/devtools
-	$(INSTALL) $(IFLAGS1) $(srcdir)/source/mozilla/gcli/util/domtemplate.js $(FINAL_TARGET)/modules/devtools/gcli/util
-	$(INSTALL) $(IFLAGS1) $(srcdir)/source/lib/gcli/util/fileparser.js $(FINAL_TARGET)/modules/devtools/gcli/util
-	$(INSTALL) $(IFLAGS1) $(srcdir)/source/mozilla/gcli/util/filesystem.js $(FINAL_TARGET)/modules/devtools/gcli/util
-	$(INSTALL) $(IFLAGS1) $(srcdir)/source/mozilla/gcli/util/host.js $(FINAL_TARGET)/modules/devtools/gcli/util
-	$(INSTALL) $(IFLAGS1) $(srcdir)/source/mozilla/gcli/util/l10n.js $(FINAL_TARGET)/modules/devtools/gcli/util
-	$(INSTALL) $(IFLAGS1) $(srcdir)/source/lib/gcli/util/legacy.js $(FINAL_TARGET)/modules/devtools/gcli/util
-	$(INSTALL) $(IFLAGS1) $(srcdir)/source/lib/gcli/util/prism.js $(FINAL_TARGET)/modules/devtools/gcli/util
-	$(INSTALL) $(IFLAGS1) $(srcdir)/source/mozilla/gcli/util/promise.js $(FINAL_TARGET)/modules/devtools/gcli/util
-	$(INSTALL) $(IFLAGS1) $(srcdir)/source/lib/gcli/util/spell.js $(FINAL_TARGET)/modules/devtools/gcli/util
-	$(INSTALL) $(IFLAGS1) $(srcdir)/source/lib/gcli/util/util.js $(FINAL_TARGET)/modules/devtools/gcli/util
-	$(INSTALL) $(IFLAGS1) $(srcdir)/source/lib/gcli/ui/focus.js $(FINAL_TARGET)/modules/devtools/gcli/ui
-	$(INSTALL) $(IFLAGS1) $(srcdir)/source/lib/gcli/ui/history.js $(FINAL_TARGET)/modules/devtools/gcli/ui
-	$(INSTALL) $(IFLAGS1) $(srcdir)/source/lib/gcli/ui/intro.js $(FINAL_TARGET)/modules/devtools/gcli/ui
-	$(INSTALL) $(IFLAGS1) $(srcdir)/source/lib/gcli/ui/menu.js $(FINAL_TARGET)/modules/devtools/gcli/ui
-	$(INSTALL) $(IFLAGS1) $(srcdir)/source/lib/gcli/ui/menu.css $(FINAL_TARGET)/modules/devtools/gcli/ui
-	$(INSTALL) $(IFLAGS1) $(srcdir)/source/lib/gcli/ui/view.js $(FINAL_TARGET)/modules/devtools/gcli/ui
-	$(INSTALL) $(IFLAGS1) $(srcdir)/source/mozilla/gcli/ui/menu.html $(FINAL_TARGET)/modules/devtools/gcli/ui
-	$(INSTALL) $(IFLAGS1) $(srcdir)/source/lib/gcli/api.js $(FINAL_TARGET)/modules/devtools/gcli
-	$(INSTALL) $(IFLAGS1) $(srcdir)/source/lib/gcli/cli.js $(FINAL_TARGET)/modules/devtools/gcli
-	$(INSTALL) $(IFLAGS1) $(srcdir)/source/mozilla/gcli/index.js $(FINAL_TARGET)/modules/devtools/gcli
-	$(INSTALL) $(IFLAGS1) $(srcdir)/source/mozilla/gcli/settings.js $(FINAL_TARGET)/modules/devtools/gcli
new file mode 100644
--- /dev/null
+++ b/toolkit/devtools/gcli/source/docs/design.md
@@ -0,0 +1,102 @@
+
+# The Design of GCLI
+
+## Design Goals
+
+GCLI should be:
+
+- primarily for technical users.
+- as fast as a traditional CLI. It should be possible to put your head down,
+  and look at the keyboard and use GCLI 'blind' at full speed without making
+  mistakes.
+- principled about the way it encourages people to build commands. There is
+  benefit from unifying the underlying concepts.
+- automatically helpful.
+
+GCLI should not attempt to:
+
+- convert existing GUI users to a CLI.
+- use natural language input. The closest we should get to natural language is
+  thinking of commands as ```verb noun --adjective```.
+- gain a touch based interface. Whilst it's possible (even probable) that touch
+  can provide further benefits to command line users, that can wait while we
+  catch up with 1985.
+- slavishly follow the syntax of existing commands, predictability is more
+  important.
+- be a programming language. Shell scripts are mini programming languages but
+  we have JavaScript sat just next door. It's better to integrate than compete.
+
+
+## Design Challenges
+
+What has changed since 1970 that might cause us to make changes to the design
+of the command line?
+
+
+### Connection limitations
+
+Unix pre-dates the Internet and treats almost everything as a file. Since the
+Internet it could be more useful to use URIs as ways to identify sources of data.
+
+
+### Memory limitations
+
+Modern computers have something like 6 orders of magnitude more memory than the
+PDP-7 on which Unix was developed. Innovations like stdin/stdout and pipes are
+ways to connect systems without long-term storage of the results. The ability
+to store results for some time (potentially in more than one format)
+significantly reduces the need for these concepts. We should make the results
+of past commands addressable for re-use at a later time.
+
+There are a number of possible policies for eviction of items from the history.
+We should investigate options other than a simple stack.
+
+
+### Multi-tasking limitations
+
+Multi-tasking was a problem in 1970; the problem was getting a computer to do
+many jobs on 1 core. Today the problem is getting a computer to do one job on
+many cores. However we're stuck with this legacy in 2 ways. Firstly that the
+default is to force everything to wait until the previous job is finished, but
+more importantly that output from parallel jobs frequently collides
+
+    $ find / -ctime 5d -print &
+    $ find / -uid 0 -print &
+    // good luck working out what came from where
+
+    $ tail -f logfile.txt &
+    $ vi main.c
+    // have a nice time editing that file
+
+GCLI should allow commands to be asynchronous and will provide UI elements to
+inform the user of job completion. It will also keep asynchronous command
+output contained within it's own display area.
+
+
+### Output limitations
+
+The PDP-7 had a teletype. There is something like 4 orders of magnitude more
+information that can be displayed on a modern display than a 80x24 character
+based console. We can use this flexibility to provide better help to the user
+in entering their command.
+
+The additional display richness can also allow interaction with result output.
+Command output can include links to follow-up commands, and even ask for
+additional input. (e.g. "your search returned zero results do you want to try
+again with a different search string")
+
+There is no reason why output must be static. For example, it could be
+informative to see the results of an "ls" command alter given changes made by
+subsequent commands. (It should be noted that there are times when historical
+information is important too)
+
+
+### Integration limitations
+
+In 1970, command execution meant retrieving a program from storage, and running
+it. This required minimal interaction between the command line processor and
+the program being run, and was good for resource constrained systems.
+This lack of interaction resulted in the processing of command line arguments
+being done everywhere, when the task was better suited to command line.
+We should provide metadata about the commands being run, to allow the command
+line to process, interpret and provide help on the input.
new file mode 100644
--- /dev/null
+++ b/toolkit/devtools/gcli/source/docs/developing-gcli.md
@@ -0,0 +1,213 @@
+
+# Developing GCLI
+
+## About the code
+
+The majority of the GCLI source is stored in the ``lib`` directory.
+
+The ``docs`` directory contains documentation.
+The ``scripts`` directory contains RequireJS that GCLI uses.
+The ``build`` directory contains files used when creating builds.
+The ``mozilla`` directory contains the mercurial patch queue of patches to apply
+to mozilla-central.
+The ``selenium-tests`` directory contains selenium web-page integration tests.
+
+The source in the ``lib`` directory is split into 4 sections:
+
+- ``lib/demo`` contains commands used in the demo page. It is not needed except
+  for demo purposes.
+- ``lib/test`` contains a small test harness for testing GCLI.
+- ``lib/gclitest`` contains tests that run in the test harness
+- ``lib/gcli`` contains the actual meat
+
+GCLI is split into a UI portion and a Model/Controller portion.
+
+
+## The GCLI Model
+
+The heart of GCLI is a ``Requisition``, which is an AST for the input. A
+``Requisition`` is a command that we'd like to execute, and we're filling out
+all the inputs required to execute the command.
+
+A ``Requisition`` has a ``Command`` that is to be executed. Each Command has a
+number of ``Parameter``s, each of which has a name and a type as detailed
+above.
+
+As you type, your input is split into ``Argument``s, which are then assigned to
+``Parameter``s using ``Assignment``s. Each ``Assignment`` has a ``Conversion``
+which stores the input argument along with the value that is was converted into
+according to the type of the parameter.
+
+There are special assignments called ``CommandAssignment`` which the
+``Requisition`` uses to link to the command to execute, and
+``UnassignedAssignment``used to store arguments that do not have a parameter
+to be assigned to.
+
+
+## The GCLI UI
+
+There are several components of the GCLI UI. Each can have a script portion,
+some template HTML and a CSS file. The template HTML is processed by
+``domtemplate`` before use.
+
+DomTemplate is fully documented in [it's own repository]
+(https://github.com/joewalker/domtemplate).
+
+The components are:
+
+- ``Inputter`` controls the input field, processing special keyboard events and
+  making sure that it stays in sync with the Requisition.
+- ``Completer`` updates a div that is located behind the input field and used
+  to display completion advice and hint highlights. It is stored in
+  completer.js.
+- ``Display`` is responsible for containing the popup hints that are displayed
+  above the command line. Typically Display contains a Hinter and a RequestsView
+  although these are not both required. Display itself is optional, and isn't
+  planned for use in the first release of GCLI in Firefox.
+- ``Hinter`` Is used to display input hints. It shows either a Menu or an
+  ArgFetch component depending on the state of the Requisition
+- ``Menu`` is used initially to select the command to be executed. It can act
+  somewhat like the Start menu on windows.
+- ``ArgFetch`` Once the command to be executed has been selected, ArgFetch
+  shows a 'dialog' allowing the user to enter the parameters to the selected
+  command.
+- ``RequestsView`` Contains a set of ``RequestView`` components, each of which
+  displays a command that has been invoked. RequestsView is a poor name, and
+  should better be called ReportView
+
+ArgFetch displays a number of Fields. There are fields for most of the Types
+discussed earlier. See 'Writing Fields' above for more information.
+
+
+## Testing
+
+GCLI contains 2 test suites:
+
+- JS level testing is run with the ``test`` command. The tests are located in
+  ``lib/gclitest`` and they use the test runner in ``lib/test``. This is fairly
+  comprehensive, however it does not do UI level testing.
+  If writing a new test it needs to be registered in ``lib/gclitest/index``.
+  For an example of how to write tests, see ``lib/gclitest/testSplit.js``.
+  The test functions are implemented in ``lib/test/assert``.
+- Browser integration tests are included in ``browser_webconsole_gcli_*.js``,
+  in ``toolkit/components/console/hudservice/tests/browser``. These are
+  run with the rest of the Mozilla test suite.
+
+
+## Coding Conventions
+
+The coding conventions for the GCLI project come from the Bespin/Skywriter and
+Ace projects. They are roughly [Crockford]
+(http://javascript.crockford.com/code.html) with a few exceptions and
+additions:
+
+* ``var`` does not need to be at the top of each function, we'd like to move
+  to ``let`` when it's generally available, and ``let`` doesn't have the same
+  semantic twists as ``var``.
+
+* Strings are generally enclosed in single quotes.
+
+* ``eval`` is to be avoided, but we don't declare it evil.
+
+The [Google JavaScript conventions]
+(https://google-styleguide.googlecode.com/svn/trunk/javascriptguide.xml) are
+more detailed, we tend to deviate in:
+
+* Custom exceptions: We generally just use ``throw new Error('message');``
+
+* Multi-level prototype hierarchies: Allowed; we don't have ``goog.inherits()``
+
+* ``else`` begins on a line by itself:
+
+        if (thing) {
+          doThis();
+        }
+        else {
+          doThat();
+        }
+
+
+## Startup
+
+Internally GCLI modules have ``startup()``/``shutdown()`` functions which are
+called on module init from the top level ``index.js`` of that 'package'.
+
+In order to initialize a package all that is needed is to require the package
+index (e.g. ``require('package/index')``).
+
+The ``shutdown()`` function was useful when GCLI was used in Bespin as part of
+dynamic registration/de-registration. It is not known if this feature will be
+useful in the future. So it has not been entirely removed, it may be at some
+future date.
+
+
+## Running the Unit Tests
+
+Start the GCLI static server:
+
+    cd path/to/gcli
+    node gcli.js
+
+Now point your browser to http://localhost:9999/localtest.html. When the page
+loads the tests will be automatically run outputting to the console, or you can
+enter the ``test`` command to run the unit tests.
+
+
+## Contributing Code
+
+Please could you do the following to help minimize the amount of rework that we
+do:
+
+1. Check the unit tests run correctly (see **Running the Unit Tests** above)
+2. Check the code follows the style guide. At a minimum it should look like the
+   code around it. For more detailed notes, see **Coding Conventions** above
+3. Help me review your work by using good commit comments. Which means 2 things
+   * Well formatted messages, i.e. 50 char summary including bug tag, followed
+     by a blank line followed by a more in-depth message wrapped to 72 chars
+     per line. This is basically the format used by the Linux Kernel. See the
+     [commit log](https://github.com/joewalker/gcli/commits/master) for
+     examples. The be extra helpful, please use the "shortdesc-BUGNUM: " if
+     possible which also helps in reviews.
+   * Commit your changes as a story. Make it easy for me to understand the
+     changes that you've made.
+4. Sign your work. To improve tracking of who did what, we follow the sign-off
+   procedure used in the Linux Kernel.
+   The sign-off is a simple line at the end of the explanation for the
+   patch, which certifies that you wrote it or otherwise have the right to
+   pass it on as an open-source patch.  The rules are pretty simple: if you
+   can certify the below:
+
+        Developer's Certificate of Origin 1.1
+
+        By making a contribution to this project, I certify that:
+
+        (a) The contribution was created in whole or in part by me and I
+            have the right to submit it under the open source license
+            indicated in the file; or
+
+        (b) The contribution is based upon previous work that, to the best
+            of my knowledge, is covered under an appropriate open source
+            license and I have the right under that license to submit that
+            work with modifications, whether created in whole or in part
+            by me, under the same open source license (unless I am
+            permitted to submit under a different license), as indicated
+            in the file; or
+
+        (c) The contribution was provided directly to me by some other
+            person who certified (a), (b) or (c) and I have not modified
+            it.
+
+        (d) I understand and agree that this project and the contribution
+            are public and that a record of the contribution (including all
+            personal information I submit with it, including my sign-off) is
+            maintained indefinitely and may be redistributed consistent with
+            this project or the open source license(s) involved.
+
+   then you just add a line saying
+
+        Signed-off-by: Random J Developer <random@developer.example.org>
+
+   using your real name (sorry, no pseudonyms or anonymous contributions.)
+
+Thanks for wanting to contribute code.
+
new file mode 100644
--- /dev/null
+++ b/toolkit/devtools/gcli/source/docs/index.md
@@ -0,0 +1,150 @@
+
+# About GCLI
+
+## GCLI is a Graphical Command Line Interpreter.
+
+GCLI is a command line for modern computers. When command lines were invented,
+computers were resource-limited, disconnected systems with slow multi-tasking
+and poor displays. The design of the Unix CLI made sense in 1970, but over 40
+years on, considering the pace of change, there are many improvements we can
+make.
+
+CLIs generally suffer from poor discoverability; It's hard when faced with a
+blank command line to work out what to do. As a result the majority of programs
+today use purely graphical user interfaces, however in doing so, they lose some
+of the benefits of CLIs. CLIs are still used because generally, in the hands of
+a skilled user they are faster, and have a wider range of available options.
+
+GCLI attempts to get the best of the GUI world and the CLI world to produce
+something that is both easy to use and learn as well as fast and powerful.
+
+GCLI has a type system to help ensure that users are inputting valid commands
+and to enable us to provide sensible context sensitive help. GCLI provides
+integration with JavaScript rather than being an alternative (like CoffeeScript).
+
+
+## History
+
+GCLI was born as part of the
+[Bespin](http://ajaxian.com/archives/canvas-for-a-text-editor) project and was
+[discussed at the time](http://j.mp/bespin-cli). The command line component
+survived the rename of Bepsin to Skywriter and the merger with Ace, got a name
+of it's own (Cockpit) which didn't last long before the project was named GCLI.
+It is now being used in the Firefox's web console where it doesn't have a
+separate identity but it's still called GCLI outside of Firefox. It is also
+used in [Eclipse Orion](http://www.eclipse.org/orion/).
+
+
+## Environments
+
+GCLI is designed to work in a number of environments:
+
+1. As a component of Firefox developer tools.
+2. As an adjunct to Orion/Ace and other online editors.
+3. As a plugin to any web-page wishing to provide its own set of commands.
+4. As part of a standalone web browser extension with it's own set of commands.
+
+
+## Related Pages
+
+Other sources of GCLI documentation:
+
+- [Writing Commands](writing-commands.md)
+- [Writing Types](writing-types.md)
+- [Developing GCLI](developing-gcli.md)
+- [Writing Tests](writing-tests.md) / [Running Tests](running-tests.md)
+- [The Design of GCLI](design.md)
+- Source
+  - The most up-to-date source is in [this Github repository](https://github.com/joewalker/gcli/).
+  - When a feature is 'done' it's merged into the [Mozilla clone](https://github.com/mozilla/gcli/).
+  - From which it flows into [Mozilla Central](https://hg.mozilla.org/mozilla-central/file/tip/browser/devtools/commandline).
+- [Demo of GCLI](http://mozilla.github.com/gcli/) with an arbitrary set of demo
+  commands
+- Other Documentation
+  - [Embedding docs](https://github.com/mozilla/gcli/blob/master/docs/index.md)
+  - [Status page](http://mozilla.github.com/devtools/2011/status.html#gcli)
+
+
+## Accessibility
+
+GCLI uses ARIA roles to guide a screen-reader as to the important sections to
+voice. We welcome [feedback on how these roles are implemented](https://bugzilla.mozilla.org/enter_bug.cgi?product=Firefox&component=Developer+Tools:+Graphic+Commandline+and+Toolbar&rep_platform=All&op_sys=All&short_desc=GCLI).
+
+The command line uses TAB as a method of completing current input, this
+prevents use of TAB for keyboard navigation. Instead of using TAB to move to
+the next field you can use F6. In addition to F6, ALT+TAB, CTRL+TAB, META+TAB
+make an attempt to move the focus on. How well this works depends on your
+OS/browser combination.
+
+
+## Embedding GCLI
+
+There are 3 basic steps in using GCLI in your system.
+
+1. Import a GCLI JavaScript file.
+   For serious use of GCLI you are likely to be creating a custom build (see
+   below) however if you just want to have a quick play, you can use
+   ``gcli-uncompressed.js`` from [the gh-pages branch of GCLI]
+   (https://github.com/mozilla/gcli/tree/gh-pages)
+   Just place the following wherever you place your script files.
+
+        <script src="path/to/gcli-uncompressed.js" type="text/javascript"></script>
+
+2. Having imported GCLI, we need to tell it where to display. The simplest
+   method is to include an elements with the id of ``gcli-input`` and
+   ``gcli-display``.
+
+        <input id="gcli-input" type="text"/>
+        <div id="gcli-display"></div>
+
+3. Tell GCLI what commands to make available. See the sections on Writing
+   Commands, Writing Types and Writing Fields for more information.
+
+   GCLI uses the CommonJS AMD format for it's files, so a 'require' statement
+   is needed to get started.
+
+        require([ 'gcli/index' ], function(gcli) {
+          gcli.addCommand(...); // Register custom commands
+          gcli.createTerminal(); // Create a user interface
+        });
+
+   The createTerminal() function takes an ``options`` objects which allows
+   customization. At the current time the documentation of these object is left
+   to the source.
+
+
+## Backwards Compatibility
+
+The goals of the GCLI project are:
+
+- Aim for very good backwards compatibility with code required from an
+  'index' module. This means we will not break code without a cycle of
+  deprecation warnings.
+
+  There are currently 3 'index' modules:
+  - gcli/index (all you need to get started with GCLI)
+  - demo/index (a number of demo commands)
+  - gclitest/index (GCLI test suite)
+
+  Code from these modules uses the module pattern to prevent access to internal
+  functions, so in essence, if you can get to it from an index module, you
+  should be ok.
+
+- We try to avoid needless change to other modules, however we don't make any
+  promises, and don't provide a deprecation cycle.
+
+  Code from other modules uses classes rather than modules, so member variables
+  are exposed. Many classes mark private members using the `_underscorePrefix`
+  pattern. Particular care should be taken if access is needed to a private
+  member.
+
+
+## Creating Custom Builds
+
+GCLI uses [DryIce](https://github.com/mozilla/dryice) to create custom builds.
+If dryice is installed (``npm install .``) then you can create a built
+version of GCLI simply using ``node gcli.js standard``. DryIce supplies a custom
+module loader to replace RequireJS for built applications.
+
+The build will be output to the ``built`` directory. The directory will be
+created if it doesn't exist.
new file mode 100644
--- /dev/null
+++ b/toolkit/devtools/gcli/source/docs/running-tests.md
@@ -0,0 +1,71 @@
+
+# Running Tests
+
+GCLI has a test suite that can be run in a number of different environments.
+Some of the tests don't work in all environments. These should be automatically
+skipped when not applicable.
+
+
+## Web
+
+Running a limited set of test from the web is the easiest. Simply load
+'localtest.html' and the unit tests should be run automatically, with results
+displayed on the console. Tests can be re-run using the 'test' command.
+
+It also creates a function 'testCommands()' to be run at a JS prompt, which
+enables the test commands for debugging purposes.
+
+
+## Firefox
+
+GCLI's test suite integrates with Mochitest and runs automatically on each test
+run. Dryice packages the tests to format them for the Firefox build system.
+
+For more information about running Mochitest on Firefox (including GCLI) see
+[the MDN, Mochitest docs](https://developer.mozilla.org/en/Mochitest)
+
+
+# Node
+
+Running the test suite under node can be done as follows:
+
+    $ node gcli.js test
+
+Or, using the `test` command:
+
+    $ node gcli.js
+    Serving GCLI to http://localhost:9999/
+    This is also a limited GCLI prompt.
+    Type 'help' for a list of commands, CTRL+C twice to exit:
+    : test
+    
+    testCli: Pass (funcs=9, checks=208)
+    testCompletion: Pass (funcs=1, checks=139)
+    testExec: Pass (funcs=1, checks=133)
+    testHistory: Pass (funcs=3, checks=13)
+    ....
+    
+    Summary: Pass (951 checks)
+
+
+# Phantom
+
+The GCLI test suite can also be run under PhantomJS as follows:
+
+    $ phantomjs ./phantom-test.js
+    
+    Summary: Pass (4289 checks)
+    
+    Finished running unit tests. (total 3.843s, ave response time 3.36ms, ...)
+
+
+# Travis CI
+
+GCLI check-ins are automatically tested by [Travis CI](https://travis-ci.org/joewalker/gcli).
+
+
+# Test Case Generation
+
+GCLI can generate test cases automagically. Load ```localtest.html```, type a
+command to be tested into GCLI, and the press F2. GCLI will output to the
+console a template test case for the entered command.
new file mode 100644
--- /dev/null
+++ b/toolkit/devtools/gcli/source/docs/writing-commands.md
@@ -0,0 +1,755 @@
+
+# Writing Commands
+
+## Basics
+
+GCLI has opinions about how commands should be written, and it encourages you
+to do The Right Thing. The opinions are based on helping users convert their
+intentions to commands and commands to what's actually going to happen.
+
+- Related commands should be sub-commands of a parent command. One of the goals
+  of GCLI is to support a large number of commands without things becoming
+  confusing, this will require some sort of namespacing or there will be
+  many people wanting to implement the ``add`` command. This style of
+  writing commands has become common place in Unix as the number of commands
+  has gone up.
+  The ```context``` command allows users to focus on a parent command, promoting
+  its sub-commands above others.
+
+- Each command should do exactly and only one thing. An example of a Unix
+  command that breaks this principle is the ``tar`` command
+
+        $ tar -zcf foo.tar.gz .
+        $ tar -zxf foo.tar.gz .
+
+  These 2 commands do exactly opposite things. Many a file has died as a result
+  of a x/c typo. In GCLI this would be better expressed:
+
+        $ tar create foo.tar.gz -z .
+        $ tar extract foo.tar.gz -z .
+
+  There may be commands (like tar) which have enough history behind them
+  that we shouldn't force everyone to re-learn a new syntax. The can be achieved
+  by having a single string parameter and parsing the input in the command)
+
+- Avoid errors. We try to avoid the user having to start again with a command
+  due to some problem. The majority of problems are simple typos which we can
+  catch using command metadata, but there are 2 things command authors can do
+  to prevent breakage.
+
+  - Where possible avoid the need to validate command line parameters in the
+    exec function. This can be done by good parameter design (see 'do exactly
+    and only one thing' above)
+
+  - If there is an obvious fix for an unpredictable problem, offer the
+    solution in the command output. So rather than use request.error (see
+    Request Object below) output some HTML which contains a link to a fixed
+    command line.
+
+Currently these concepts are not enforced at a code level, but they could be in
+the future.
+
+
+## How commands work
+
+This is how to create a basic ``greet`` command:
+
+    gcli.addCommand({
+      name: 'greet',
+      description: 'Show a greeting',
+      params: [
+        {
+          name: 'name',
+          type: 'string',
+          description: 'The name to greet'
+        }
+      ],
+      returnType: 'string',
+      exec: function(args, context) {
+        return 'Hello, ' + args.name;
+      }
+    });
+
+This command is used as follows:
+
+    : greet Joe
+    Hello, Joe
+
+Some terminology that isn't always obvious: a function has 'parameters', and
+when you call a function, you pass 'arguments' to it.
+
+
+## Internationalization (i18n)
+
+There are several ways that GCLI commands can be localized. The best method
+depends on what context you are writing your command for.
+
+### Firefox Embedding
+
+GCLI supports Mozilla style localization. To add a command that will only ever
+be used embedded in Firefox, this is the way to go. Your strings should be
+stored in ``browser/locales/en-US/chrome/browser/devtools/gclicommands.properties``,
+And you should access them using ``gcli.lookup(...)`` or ``gcli.lookupFormat()``
+
+For examples of existing commands, take a look in
+``browser/devtools/webconsole/GcliCommands.jsm``, which contains most of the
+current GCLI commands. If you will be adding a number of new commands, then
+consider starting a new JSM.
+
+Your command will then look something like this:
+
+    gcli.addCommand({
+      name: 'greet',
+      description: gcli.lookup("greetDesc")
+      ...
+    });
+
+### Web Commands
+
+There are 2 ways to provide translated strings for web use. The first is to
+supply the translated strings in the description:
+
+    gcli.addCommand({
+      name: 'greet',
+      description: {
+        'root': 'Show a greeting',
+        'fr-fr': 'Afficher un message d'accueil',
+        'de-de': 'Zeige einen Gruß',
+        'gk-gk': 'Εμφάνιση ένα χαιρετισμό',
+        ...
+      }
+      ...
+    });
+
+Each description should contain at least a 'root' entry which is the
+default if no better match is found. This method has the benefit of being
+compact and simple, however it has the significant drawback of being wasteful
+of memory and bandwidth to transmit and store a significant number of strings,
+the majority of which will never be used.
+
+More efficient is to supply a lookup key and ask GCLI to lookup the key from an
+appropriate localized strings file:
+
+    gcli.addCommand({
+      name: 'greet',
+      description: { 'key': 'demoGreetingDesc' }
+      ...
+    });
+
+For web usage, the central store of localized strings is
+``lib/gcli/nls/strings.js``. Other string files can be added using the
+``l10n.registerStringsSource(...)`` function.
+
+This method can be used both in Firefox and on the Web (see the help command
+for an example). However this method has the drawback that it will not work
+with DryIce built files until we fix bug 683844.
+
+
+## Default argument values
+
+The ``greet`` command requires the entry of the ``name`` parameter. This
+parameter can be made optional with the addition of a ``defaultValue`` to the
+parameter:
+
+    gcli.addCommand({
+      name: 'greet',
+      description: 'Show a message to someone',
+      params: [
+        {
+          name: 'name',
+          type: 'string',
+          description: 'The name to greet',
+          defaultValue: 'World!'
+        }
+      ],
+      returnType: 'string',
+      exec: function(args, context) {
+        return "Hello, " + args.name;
+      }
+    });
+
+Now we can also use the ``greet`` command as follows:
+
+    : greet
+    Hello, World!
+
+
+## Positional vs. named arguments
+
+Arguments can be entered either positionally or as named arguments. Generally
+users will prefer to type the positional version, however the named alternative
+can be more self documenting.
+
+For example, we can also invoke the greet command as follows:
+
+    : greet --name Joe
+    Hello, Joe
+
+
+## Short argument names
+
+GCLI allows you to specify a 'short' character for any parameter:
+
+    gcli.addCommand({
+      name: 'greet',
+      params: [
+        {
+          name: 'name',
+          short: 'n',
+          type: 'string',
+          ...
+        }
+      ],
+      ...
+    });
+
+This is used as follows:
+
+    : greet -n Fred
+    Hello, Fred
+
+Currently GCLI does not allow short parameter merging (i.e. ```ls -la```)
+however this is planned.
+
+
+## Parameter types
+
+Initially the available types are:
+
+- string
+- boolean
+- number
+- selection
+- delegate
+- date
+- array
+- file
+- node
+- nodelist
+- resource
+- command
+- setting
+
+This list can be extended. See [Writing Types](writing-types.md) on types for
+more information.
+
+The following examples assume the following definition of the ```greet```
+command:
+
+    gcli.addCommand({
+      name: 'greet',
+      params: [
+        { name: 'name', type: 'string' },
+        { name: 'repeat', type: 'number' }
+      ],
+      ...
+    });
+
+Parameters can be specified either with named arguments:
+
+    : greet --name Joe --repeat 2
+
+And sometimes positionally:
+
+    : greet Joe 2
+
+Parameters can be specified positionally if they are considered 'important'.
+Unimportant parameters must be specified with a named argument.
+
+Named arguments can be specified anywhere on the command line (after the
+command itself) however positional arguments must be in order. So
+these examples are the same:
+
+    : greet --name Joe --repeat 2
+    : greet --repeat 2 --name Joe
+
+However (obviously) these are not the same:
+
+    : greet Joe 2
+    : greet 2 Joe
+
+(The second would be an error because 'Joe' is not a number).
+
+Named arguments are assigned first, then the remaining arguments are assigned
+to the remaining parameters. So the following is valid and unambiguous:
+
+    : greet 2 --name Joe
+
+Positional parameters quickly become unwieldy with long parameter lists so we
+recommend only having 2 or 3 important parameters. GCLI provides hints for
+important parameters more obviously than unimportant ones.
+
+Parameters are 'important' if they are not in a parameter group. The easiest way
+to achieve this is to use the ```option: true``` property.
+
+For example, using:
+
+    gcli.addCommand({
+      name: 'greet',
+      params: [
+        { name: 'name', type: 'string' },
+        { name: 'repeat', type: 'number', option: true, defaultValue: 1 }
+      ],
+      ...
+    });
+
+Would mean that this is an error
+
+    : greet Joe 2
+
+You would instead need to do the following:
+
+    : greet Joe --repeat 2
+
+For more on parameter groups, see below.
+
+In addition to being 'important' and 'unimportant' parameters can also be
+optional. If is possible to be important and optional, but it is not possible
+to be unimportant and non-optional.
+
+Parameters are optional if they either:
+- Have a ```defaultValue``` property
+- Are of ```type=boolean``` (boolean arguments automatically default to being false)
+
+There is currently no way to make parameters mutually exclusive.
+
+
+## Selection types
+
+Parameters can have a type of ``selection``. For example:
+
+    gcli.addCommand({
+      name: 'greet',
+      params: [
+        { name: 'name', ... },
+        {
+          name: 'lang',
+          description: 'In which language should we greet',
+          type: { name: 'selection', data: [ 'en', 'fr', 'de', 'es', 'gk' ] },
+          defaultValue: 'en'
+        }
+      ],
+      ...
+    });
+
+GCLI will enforce that the value of ``arg.lang`` was one of the values
+specified. Alternatively ``data`` can be a function which returns an array of
+strings.
+
+The ``data`` property is useful when the underlying type is a string but it
+doesn't work when the underlying type is something else. For this use the
+``lookup`` property as follows:
+
+      type: {
+        name: 'selection',
+        lookup: {
+          'en': Locale.EN,
+          'fr': Locale.FR,
+          ...
+        }
+      },
+
+Similarly, ``lookup`` can be a function returning the data of this type.
+
+
+## Number types
+
+Number types are mostly self explanatory, they have one special property which
+is the ability to specify upper and lower bounds for the number:
+
+    gcli.addCommand({
+      name: 'volume',
+      params: [
+        {
+          name: 'vol',
+          description: 'How loud should we go',
+          type: { name: 'number', min: 0, max: 11 }
+        }
+      ],
+      ...
+    });
+
+You can also specify a ``step`` property which specifies by what amount we
+should increment and decrement the values. The ``min``, ``max``, and ``step``
+properties are used by the command line when up and down are pressed and in
+the input type of a dialog generated from this command.
+
+
+## Delegate types
+
+Delegate types are needed when the type of some parameter depends on the type
+of another parameter. For example:
+
+    : set height 100
+    : set name "Joe Walker"
+
+We can achieve this as follows:
+
+    gcli.addCommand({
+      name: 'set',
+      params: [
+        {
+          name: 'setting',
+          type: { name: 'selection', values: [ 'height', 'name' ] }
+        },
+        {
+          name: 'value',
+          type: {
+            name: 'delegate',
+            delegateType: function() { ... }
+          }
+        }
+      ],
+      ...
+    });
+
+Several details are left out of this example, like how the delegateType()
+function knows what the current setting is. See the ``pref`` command for an
+example.
+
+
+## Array types
+
+Parameters can have a type of ``array``. For example:
+
+    gcli.addCommand({
+      name: 'greet',
+      params: [
+        {
+          name: 'names',
+          type: { name: 'array', subtype: 'string' },
+          description: 'The names to greet',
+          defaultValue: [ 'World!' ]
+        }
+      ],
+      ...
+      exec: function(args, context) {
+        return "Hello, " + args.names.join(', ') + '.';
+      }
+    });
+
+This would be used as follows:
+
+    : greet Fred Jim Shiela
+    Hello, Fred, Jim, Shiela.
+
+Or using named arguments:
+
+    : greet --names Fred --names Jim --names Shiela
+    Hello, Fred, Jim, Shiela.
+
+There can only be one ungrouped parameter with an array type, and it must be
+at the end of the list of parameters (i.e. just before any parameter groups).
+This avoids confusion as to which parameter an argument should be assigned.
+
+
+## Sub-commands
+
+It is common for commands to be groups into those with similar functionality.
+Examples include virtually all VCS commands, ``apt-get``, etc. There are many
+examples of commands that should be structured as in a sub-command style -
+``tar`` being the obvious example, but others include ``crontab``.
+
+Groups of commands are specified with the top level command not having an
+exec function:
+
+    gcli.addCommand({
+      name: 'tar',
+      description: 'Commands to manipulate archives',
+    });
+    gcli.addCommand({
+      name: 'tar create',
+      description: 'Create a new archive',
+      exec: function(args, context) { ... },
+      ...
+    });
+    gcli.addCommand({
+      name: 'tar extract',
+      description: 'Extract from an archive',
+      exec: function(args, context) { ... },
+      ...
+    });
+
+
+## Parameter groups
+
+Parameters can be grouped into sections.
+
+There are 3 ways to assign a parameter to a group.
+
+The simplest uses ```option: true``` to put a parameter into the default
+'Options' group:
+
+    gcli.addCommand({
+      name: 'greet',
+      params: [
+        { name: 'repeat', type: 'number', option: true }
+      ],
+      ...
+    });
+
+The ```option``` property can also take a string to use an alternative parameter
+group:
+
+    gcli.addCommand({
+      name: 'greet',
+      params: [
+        { name: 'repeat', type: 'number', option: 'Advanced' }
+      ],
+      ...
+    });
+
+An example of how this can be useful is 'git' which categorizes parameters into
+'porcelain' and 'plumbing'.
+
+Finally, parameters can be grouped together as follows:
+
+    gcli.addCommand({
+      name: 'greet',
+      params: [
+        { name: 'name', type: 'string', description: 'The name to greet' },
+        {
+          group: 'Advanced Options',
+          params: [
+            { name: 'repeat', type: 'number', defaultValue: 1 },
+            { name: 'debug', type: 'boolean' }
+          ]
+        }
+      ],
+      ...
+    });
+
+This could be used as follows:
+
+    : greet Joe --repeat 2 --debug
+    About to send greeting
+    Hello, Joe
+    Hello, Joe
+    Done!
+
+Parameter groups must come after non-grouped parameters because non-grouped
+parameters can be assigned positionally, so their index is important. We don't
+want 'holes' in the order caused by parameter groups.
+
+
+## Command metadata
+
+Each command should have the following properties:
+
+- A string ``name``.
+- A short ``description`` string. Generally no more than 20 characters without
+  a terminating period/fullstop.
+- A function to ``exec``ute. (Optional for the parent containing sub-commands)
+  See below for more details.
+
+And optionally the following extra properties:
+
+- A declaration of the accepted ``params``.
+- A ``hidden`` property to stop the command showing up in requests for help.
+- A ``context`` property which defines the scope of the function that we're
+  calling. Rather than simply call ``exec()``, we do ``exec.call(context)``.
+- A ``manual`` property which allows a fuller description of the purpose of the
+  command.
+- A ``returnType`` specifying how we should handle the value returned from the
+  exec function.
+
+The ``params`` property is an array of objects, one for each parameter. Each
+parameter object should have the following 3 properties:
+
+- A string ``name``.
+- A short string ``description`` as for the command.
+- A ``type`` which refers to an existing Type (see Writing Types).
+
+Optionally each parameter can have these properties:
+
+- A ``defaultValue`` (which should be in the type specified in ``type``).
+  The defaultValue will be used when there is no argument supplied for this
+  parameter on the command line.
+  If the parameter has a ``defaultValue``, other than ``undefined`` then the
+  parameter is optional, and if unspecified on the command line, the matching
+  argument will have this value when the function is called.
+  If ``defaultValue`` is missing, or if it is set to ``undefined``, then the
+  system will ensure that a value is provided before anything is executed.
+  There are 2 special cases:
+  - If the type is ``selection``, then defaultValue must not be undefined.
+    The defaultValue must either be ``null`` (meaning that a value must be
+    supplied by the user) or one of the selection values.
+  - If the type is ``boolean``, then ``defaultValue:false`` is implied and
+    can't be changed. Boolean toggles are assumed to be off by default, and
+    should be named to match.
+- A ``manual`` property for parameters is exactly analogous to the ``manual``
+  property for commands - descriptive text that is longer than than 20
+  characters.
+
+
+## The Command Function (exec)
+
+The parameters to the exec function are designed to be useful when you have a
+large number of parameters, and to give direct access to the environment (if
+used).
+
+    gcli.addCommand({
+      name: 'echo',
+      description: 'The message to display.',
+      params: [
+        {
+          name: 'message',
+          type: 'string',
+          description: 'The message to display.'
+        }
+      ],
+      returnType: 'string',
+      exec: function(args, context) {
+        return args.message;
+      }
+    });
+
+The ``args`` object contains the values specified on the params section and
+provided on the command line. In this example it would contain the message for
+display as ``args.message``.
+
+The ``context`` object has the following signature:
+
+    {
+      environment: ..., // environment object passed to createTerminal()
+      exec: ...,        // function to execute a command
+      update: ...,      // function to alter the text of the input area
+      createView: ...,  // function to help creating rich output
+      defer: ...,       // function to create a deferred promise
+    }
+
+The ``environment`` object is opaque to GCLI. It can be used for providing
+arbitrary data to your commands about their environment. It is most useful
+when more than one command line exists on a page with similar commands in both
+which should act in their own ways.
+An example use for ``environment`` would be a page with several tabs, each
+containing an editor with a command line. Commands executed in those editors
+should apply to the relevant editor.
+The ``environment`` object is passed to GCLI at startup (probably in the
+``createTerminal()`` function).
+
+The ``document`` object is also passed to GCLI at startup. In some environments
+(e.g. embedded in Firefox) there is no global ``document``. This object
+provides a way to create DOM nodes.
+
+``defer()`` allows commands to execute asynchronously.
+
+
+## Returning data
+
+The command meta-data specifies the type of data returned by the command using
+the ``returnValue`` setting.
+
+``returnValue`` processing is currently functioning, but incomplete, and being
+tracked in [Bug 657595](http://bugzil.la/657595). Currently you should specify
+a ``returnType`` of ``string`` or ``html``. If using HTML, you can return
+either an HTML string or a DOM node.
+
+In the future, JSON will be strongly encouraged as the return type, with some
+formatting functions to convert the JSON to HTML.
+
+Asynchronous output is achieved using a promise created from the ``context``
+parameter: ``context.defer()``.
+
+Some examples of this is practice:
+
+    { returnType: "string" }
+    ...
+    return "example";
+
+GCLI interprets the output as a plain string. It will be escaped before display
+and available as input to other commands as a plain string.
+
+    { returnType: "html" }
+    ...
+    return "<p>Hello</p>";
+
+GCLI will interpret this as HTML, and parse it for display.
+
+    { returnType: "dom" }
+    ...
+    return util.createElement(context.document, 'div');
+
+``util.createElement`` is a utility to ensure use of the XHTML namespace in XUL
+and other XML documents. In an HTML document it's functionally equivalent to
+``context.document.createElement('div')``. If your command is likely to be used
+in Firefox or another XML environment, you should use it. You can import it
+with ``var util = require('util/util');``.
+
+GCLI will use the returned HTML element as returned. See notes on ``context``
+above.
+
+    { returnType: "number" }
+    ...
+    return 42;
+
+GCLI will display the element in a similar way to a string, but it the value
+will be available to future commands as a number.
+
+    { returnType: "date" }
+    ...
+    return new Date();
+
+    { returnType: "file" }
+    ...
+    return new File();
+
+Both these examples return data as a given type, for which a converter will
+be required before the value can be displayed. The type system is likely to
+change before this is finalized. Please contact the author for more
+information.
+
+    { returnType: "string" }
+    ...
+    var deferred = context.defer();
+    setTimeout(function() {
+      deferred.resolve("hello");
+    }, 500);
+    return deferred.promise;
+
+Errors can be signaled by throwing an exception. GCLI will display the message
+property (or the toString() value if there is no message property). (However
+see *3 principles for writing commands* above for ways to avoid doing this).
+
+
+## Specifying Types
+
+Types are generally specified by a simple string, e.g. ``'string'``. For most
+types this is enough detail. There are a number of exceptions:
+
+* Array types. We declare a parameter to be an array of things using ``[]``,
+  for example: ``number[]``.
+* Selection types. There are 3 ways to specify the options in a selection:
+  * Using a lookup map
+
+            type: {
+              name: 'selection',
+              lookup: { one:1, two:2, three:3 }
+            }
+
+    (The boolean type is effectively just a selection that uses
+    ``lookup:{ 'true': true, 'false': false }``)
+
+  * Using given strings
+
+            type: {
+              name: 'selection',
+              data: [ 'left', 'center', 'right' ]
+            }
+
+  * Using named objects, (objects with a ``name`` property)
+
+            type: {
+              name: 'selection',
+              data: [
+                { name: 'Google', url: 'http://www.google.com/' },
+                { name: 'Microsoft', url: 'http://www.microsoft.com/' },
+                { name: 'Yahoo', url: 'http://www.yahoo.com/' }
+              ]
+            }
+
+* Delegate type. It is generally best to inherit from Delegate in order to
+  provide a customization of this type. See settingValue for an example.
+
+See below for more information.
+
new file mode 100644
--- /dev/null
+++ b/toolkit/devtools/gcli/source/docs/writing-tests.md
@@ -0,0 +1,20 @@
+
+# Writing Tests
+
+There are several sources of GCLI tests and several environments in which they
+are run.
+
+The majority of GCLI tests are stored in
+[this repository](https://github.com/joewalker/gcli/) in files named like
+```./lib/gclitest/test*.js```. These tests run in Firefox, Chrome, Opera,
+PhantomJS, and NodeJS/JsDom
+
+See [Running Tests](running-tests.md) for further details.
+
+GCLI comes with a generic unit test harness (in ```./lib/test/```) and a
+set of helpers for creating GCLI tests (in ```./lib/gclitest/helpers.js```).
+
+# GCLI tests in Firefox
+
+The build process converts the GCLI tests to run under Mochitest inside the
+Firefox unit tests. It also adds some
new file mode 100644
--- /dev/null
+++ b/toolkit/devtools/gcli/source/docs/writing-types.md
@@ -0,0 +1,106 @@
+
+# Writing Types
+
+Commands are a fundamental building block because they are what the users
+directly interacts with, however they are built on ``Type``s. There are a
+number of built in types:
+
+* string. This is a JavaScript string
+* number. A JavaScript number
+* boolean. A Javascript boolean
+* selection. This is an selection from a number of alternatives
+* delegate. This type could change depending on other factors, but is well
+  defined when one of the conversion routines is called.
+
+There are a number of additional types defined by Pilot and GCLI as
+extensions to the ``selection`` and ``delegate`` types
+
+* setting. One of the defined settings
+* settingValue. A value that can be applied to an associated setting.
+* command. One of the defined commands
+
+Most of our types are 'static' e.g. there is only one type of 'string', however
+some types like 'selection' and 'delegate' are customizable.
+
+All types must inherit from Type and have the following methods:
+
+    /**
+     * Convert the given <tt>value</tt> to a string representation.
+     * Where possible, there should be round-tripping between values and their
+     * string representations.
+     */
+    stringify: function(value) { return 'string version of value'; },
+
+    /**
+     * Convert the given <tt>str</tt> to an instance of this type.
+     * Where possible, there should be round-tripping between values and their
+     * string representations.
+     * @return Conversion
+     */
+    parse: function(str) { return new Conversion(...); },
+
+    /**
+     * The plug-in system, and other things need to know what this type is
+     * called. The name alone is not enough to fully specify a type. Types like
+     * 'selection' and 'delegate' need extra data, however this function returns
+     * only the name, not the extra data.
+     * <p>In old bespin, equality was based on the name. This may turn out to be
+     * important in Ace too.
+     */
+    name: 'example',
+
+In addition, defining the following functions can be helpful, although Type
+contains default implementations:
+* increment(value)
+* decrement(value)
+
+Type, Conversion and Status are all declared by canon.js.
+
+The values produced by the parse function can be of any type, but if you are
+producing your own, you are strongly encouraged to include properties called
+``name`` and ``description`` where it makes sense. There are a number of
+places in GCLI where the UI will be able to provide better help to users if
+your values include these properties.
+
+
+# Writing Fields
+
+Fields are visual representations of types. For simple types like string it is
+enough to use ``<input type=...>``, however more complex types we may wish to
+provide a custom widget to allow the user to enter values of the given type.
+
+This is an example of a very simple new password field type:
+
+    function PasswordField(doc) {
+      this.doc = doc;
+    }
+
+    PasswordField.prototype = Object.create(Field.prototype);
+
+    PasswordField.prototype.createElement = function(assignment) {
+      this.assignment = assignment;
+      this.input = dom.createElement(this.doc, 'input');
+      this.input.type = 'password';
+      this.input.value = assignment.arg ? assignment.arg.text : '';
+
+      this.onKeyup = function() {
+          this.assignment.setValue(this.input.value);
+      }.bind(this);
+      this.input.addEventListener('keyup', this.onKeyup, false);
+
+      this.onChange = function() {
+          this.input.value = this.assignment.arg.text;
+      };
+      this.assignment.onAssignmentChange.add(this.onChange, this);
+
+      return this.input;
+    };
+
+    PasswordField.prototype.destroy = function() {
+      this.input.removeEventListener('keyup', this.onKeyup, false);
+      this.assignment.onAssignmentChange.remove(this.onChange, this);
+    };
+
+    PasswordField.claim = function(type) {
+      return type.name === 'password' ? Field.claim.MATCH : Field.claim.NO_MATCH;
+    };
new file mode 100644
--- /dev/null
+++ b/toolkit/devtools/gcli/source/lib/gcli/connectors/index.js
@@ -0,0 +1,183 @@
+/*
+ * Copyright 2012, Mozilla Foundation and contributors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+'use strict';
+
+var api = require('../api');
+var connectors = require('./connectors');
+var Canon = require('../commands/commands').Canon;
+var Types = require('../types/types').Types;
+
+// Patch-up IE9
+require('../util/legacy');
+
+/*
+ * GCLI is built from a number of components (called items) composed as
+ * required for each environment.
+ * When adding to or removing from this list, we should keep the basics in sync
+ * with the other environments.
+ * See:
+ * - lib/gcli/index.js: Generic basic set (without commands)
+ * - lib/gcli/demo.js: Adds demo commands to basic set for use in web demo
+ * - gcli.js: Add commands to basic set for use in Node command line
+ * - lib/gcli/index.js: (mozmaster branch) From scratch listing for Firefox
+ * - lib/gcli/connectors/index.js: Client only items when executing remotely
+ * - lib/gcli/connectors/direct.js: Test items for connecting to in-process GCLI
+ */
+var items = [
+  // First we need to add the local types which other types depend on
+  require('../types/delegate').items,
+  require('../types/selection').items,
+  require('../types/array').items,
+
+  require('../types/boolean').items,
+  require('../types/command').items,
+  require('../types/date').items,
+  require('../types/file').items,
+  require('../types/javascript').items,
+  require('../types/node').items,
+  require('../types/number').items,
+  require('../types/resource').items,
+  require('../types/setting').items,
+  require('../types/string').items,
+
+  require('../fields/delegate').items,
+  require('../fields/selection').items,
+
+  require('../ui/intro').items,
+  require('../ui/focus').items,
+
+  require('../converters/converters').items,
+  require('../converters/basic').items,
+  require('../converters/html').items,
+  require('../converters/terminal').items,
+
+  require('../languages/command').items,
+  require('../languages/javascript').items,
+
+  require('./direct').items,
+  // require('./rdp').items, // Firefox remote debug protocol
+  require('./websocket').items,
+  require('./xhr').items,
+
+  require('../commands/context').items,
+
+].reduce(function(prev, curr) { return prev.concat(curr); }, []);
+
+/**
+ * These are the commands stored on the remote side that have converters which
+ * we'll need to present the data
+ */
+var requiredConverters = [
+  require('../cli').items,
+
+  require('../commands/clear').items,
+  require('../commands/connect').items,
+  require('../commands/exec').items,
+  require('../commands/global').items,
+  require('../commands/help').items,
+  require('../commands/intro').items,
+  require('../commands/lang').items,
+  require('../commands/preflist').items,
+  require('../commands/pref').items,
+  require('../commands/test').items,
+
+].reduce(function(prev, curr) { return prev.concat(curr); }, [])
+ .filter(function(item) { return item.item === 'converter'; });
+
+/**
+ * Connect to a remote system and setup the canon/types/converters etc needed
+ * to make it all work
+ */
+exports.connect = function(options) {
+  options = options || {};
+
+  var gcli = api.getApi();
+
+  // Ugly hack, to aid testing
+  exports.api = gcli;
+
+  options.types = gcli.types = new Types();
+  options.canon = gcli.canon = new Canon({ types: gcli.types });
+
+  gcli.addItems(items);
+  gcli.addItems(requiredConverters);
+
+  var connector = connectors.get(options.connector);
+  return connector.connect(options.url).then(function(connection) {
+    options.connection = connection;
+    connection.on('canonChanged', function(specs) {
+      exports.addItems(gcli, specs, connection);
+    });
+
+    return connection.call('specs').then(function(specs) {
+      exports.addItems(gcli, specs, connection);
+      return connection;
+    });
+  });
+};
+
+exports.addItems = function(gcli, specs, connection) {
+  exports.removeRemoteItems(gcli, connection);
+  var remoteItems = exports.addLocalFunctions(specs, connection);
+  gcli.addItems(remoteItems);
+};
+
+/**
+ * Take the data from the 'specs' command (or the 'canonChanged' event) and
+ * add function to proxy the execution back over the connection
+ */
+exports.addLocalFunctions = function(specs, connection) {
+  // Inject an 'exec' function into the commands, and the connection into
+  // all the remote types
+  specs.forEach(function(commandSpec) {
+    //
+    commandSpec.connection = connection;
+    commandSpec.params.forEach(function(param) {
+      param.type.connection = connection;
+    });
+
+    if (!commandSpec.isParent) {
+      commandSpec.exec = function(args, context) {
+        var data = {
+          typed: (context.prefix ? context.prefix + ' ' : '') + context.typed
+        };
+
+        return connection.call('execute', data).then(function(reply) {
+          var typedData = context.typedData(reply.type, reply.data);
+          if (!reply.error) {
+            return typedData;
+          }
+          else {
+            throw typedData;
+          }
+        });
+      };
+    }
+
+    commandSpec.isProxy = true;
+  });
+
+  return specs;
+};
+
+exports.removeRemoteItems = function(gcli, connection) {
+  gcli.canon.getCommands().forEach(function(command) {
+    if (command.connection === connection) {
+      gcli.canon.removeCommand(command);
+    }
+  });
+};
new file mode 100644
--- /dev/null
+++ b/toolkit/devtools/gcli/source/lib/gcli/connectors/protocol.js
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2012, Mozilla Foundation and contributors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+'use strict';
+
+/**
+ * This is a quick and dirty stub that allows us to write code in remoted.js
+ * that looks like gcli.js
+ */
+exports.method = function(func, spec) {
+  // An array of strings, being the names of the parameters
+  var argSpecs = [];
+  if (spec.request != null) {
+    Object.keys(spec.request).forEach(function(name) {
+      var arg = spec.request[name];
+      argSpecs[arg.index] = name;
+    });
+  }
+
+  return function(data) {
+    var args = (data == null) ?
+               [] :
+               argSpecs.map(function(name) { return data[name]; });
+    return func.apply(this, args);
+  };
+};
+
+var Arg = exports.Arg = function(index, type) {
+  if (this == null) {
+    return new Arg(index, type);
+  }
+
+  this.index = index;
+  this.type = type;
+};
+
+var RetVal = exports.RetVal = function(type) {
+  if (this == null) {
+    return new RetVal(type);
+  }
+
+  this.type = type;
+};
new file mode 100644
--- /dev/null
+++ b/toolkit/devtools/gcli/source/lib/gcli/connectors/rdp.js
@@ -0,0 +1,147 @@
+/*
+ * Copyright 2012, Mozilla Foundation and contributors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+'use strict';
+
+var Cu = require('chrome').Cu;
+
+var debuggerSocketConnect = Cu.import('resource://gre/modules/devtools/dbg-client.jsm', {}).debuggerSocketConnect;
+var DebuggerClient = Cu.import('resource://gre/modules/devtools/dbg-client.jsm', {}).DebuggerClient;
+
+var promise = require('../util/promise');
+var Connection = require('./connectors').Connection;
+
+/**
+ * What port should we use by default?
+ */
+Object.defineProperty(exports, 'defaultPort', {
+  get: function() {
+    var Services = Cu.import('resource://gre/modules/Services.jsm', {}).Services;
+    try {
+      return Services.prefs.getIntPref('devtools.debugger.chrome-debugging-port');
+    }
+    catch (ex) {
+      console.error('Can\'t use default port from prefs. Using 9999');
+      return 9999;
+    }
+  },
+  enumerable: true
+});
+
+exports.items = [
+  {
+    item: 'connector',
+    name: 'rdp',
+
+    connect: function(url) {
+      return RdpConnection.create(url);
+    }
+  }
+];
+
+/**
+ * RdpConnection uses the Firefox Remote Debug Protocol
+ */
+function RdpConnection(url) {
+  throw new Error('Use RdpConnection.create');
+}
+
+/**
+ * Asynchronous construction
+ */
+RdpConnection.create = function(url) {
+  this.host = url;
+  this.port = undefined; // TODO: Split out the port number
+
+  this.requests = {};
+  this.nextRequestId = 0;
+
+  this._emit = this._emit.bind(this);
+
+  var deferred = promise.defer();
+
+  this.transport = debuggerSocketConnect(this.host, this.port);
+  this.client = new DebuggerClient(this.transport);
+
+  this.client.connect(function() {
+    this.client.listTabs(function(response) {
+      this.actor = response.gcliActor;
+      deferred.resolve();
+    }.bind(this));
+  }.bind(this));
+
+  return deferred.promise;
+};
+
+RdpConnection.prototype = Object.create(Connection.prototype);
+
+RdpConnection.prototype.call = function(command, data) {
+  var deferred = promise.defer();
+
+  var request = { to: this.actor, type: command, data: data };
+
+  this.client.request(request, function(response) {
+    deferred.resolve(response.commandSpecs);
+  });
+
+  return deferred.promise;
+};
+
+RdpConnection.prototype.disconnect = function() {
+  var deferred = promise.defer();
+
+  this.client.close(function() {
+    deferred.resolve();
+  });
+
+  delete this._emit;
+
+  return deferred.promise;
+};
+
+
+/**
+ * A Request is a command typed at the client which lives until the command
+ * has finished executing on the server
+ */
+function Request(actor, typed, args) {
+  this.json = {
+    to: actor,
+    type: 'execute',
+    typed: typed,
+    args: args,
+    requestId: 'id-' + Request._nextRequestId++,
+  };
+
+  this._deferred = promise.defer();
+  this.promise = this._deferred.promise;
+}
+
+Request._nextRequestId = 0;
+
+/**
+ * Called by the connection when a remote command has finished executing
+ * @param error boolean indicating output state
+ * @param type the type of the returned data
+ * @param data the data itself
+ */
+Request.prototype.complete = function(error, type, data) {
+  this._deferred.resolve({
+    error: error,
+    type: type,
+    data: data
+  });
+};
new file mode 100644
--- /dev/null
+++ b/toolkit/devtools/gcli/source/lib/gcli/connectors/remoted.js
@@ -0,0 +1,272 @@
+/*
+ * Copyright 2012, Mozilla Foundation and contributors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* jshint quotmark:false, newcap:false */
+
+'use strict';
+
+var promise = require('../util/promise');
+var host = require('../util/host');
+var fileparser = require('../util/fileparser');
+
+var protocol = require('./protocol');
+var method = protocol.method;
+var Arg = protocol.Arg;
+var RetVal = protocol.RetVal;
+
+/**
+ * Provide JSON mapping services to remote functionality of a Requisition
+ */
+var Remoter = exports.Remoter = function(requisition) {
+  this.requisition = requisition;
+  this._listeners = [];
+};
+
+/**
+ * Add a new listener
+ */
+Remoter.prototype.addListener = function(action) {
+  var listener = {
+    action: action,
+    caller: function() {
+      action('canonChanged', this.requisition.canon.getCommandSpecs());
+    }.bind(this)
+  };
+  this._listeners.push(listener);
+
+  this.requisition.canon.onCanonChange.add(listener.caller);
+};
+
+/**
+ * Remove an existing listener
+ */
+Remoter.prototype.removeListener = function(action) {
+  var listener;
+
+  this._listeners = this._listeners.filter(function(li) {
+    if (li.action === action) {
+      listener = li;
+      return false;
+    }
+    return true;
+  });
+
+  if (listener == null) {
+    throw new Error('action not a known listener');
+  }
+
+  this.requisition.canon.onCanonChange.remove(listener.caller);
+};
+
+/**
+ * These functions are designed to be remoted via RDP/XHR/websocket, etc
+ */
+Remoter.prototype.exposed = {
+  /**
+   * Retrieve a list of the remotely executable commands
+   */
+  specs: method(function() {
+    return this.requisition.canon.getCommandSpecs();
+  }, {
+    request: {},
+    response: RetVal("json")
+  }),
+
+  /**
+   * Execute a GCLI command
+   * @return a promise of an object with the following properties:
+   * - data: The output of the command
+   * - type: The type of the data to allow selection of a converter
+   * - error: True if the output was considered an error
+   */
+  execute: method(function(typed) {
+    return this.requisition.updateExec(typed).then(function(output) {
+      return output.toJson();
+    });
+  }, {
+    request: {
+      typed: Arg(0, "string") // The command string
+    },
+    response: RetVal("json")
+  }),
+
+  /**
+   * Get the state of an input string. i.e. requisition.getStateData()
+   */
+  state: method(function(typed, start, rank) {
+    return this.requisition.update(typed).then(function() {
+      return this.requisition.getStateData(start, rank);
+    }.bind(this));
+  }, {
+    request: {
+      typed: Arg(0, "string"), // The command string
+      start: Arg(1, "number"), // Cursor start position
+      rank: Arg(2, "number") // The prediction offset (# times UP/DOWN pressed)
+    },
+    response: RetVal("json")
+  }),
+
+  /**
+   * Call type.parse to check validity. Used by the remote type
+   * @return a promise of an object with the following properties:
+   * - status: Of of the following strings: VALID|INCOMPLETE|ERROR
+   * - message: The message to display to the user
+   * - predictions: An array of suggested values for the given parameter
+   */
+  typeparse: method(function(typed, param) {
+    return this.requisition.update(typed).then(function() {
+      var assignment = this.requisition.getAssignment(param);
+
+      return promise.resolve(assignment.predictions).then(function(predictions) {
+        return {
+          status: assignment.getStatus().toString(),
+          message: assignment.message,
+          predictions: predictions
+        };
+      });
+    }.bind(this));
+  }, {
+    request: {
+      typed: Arg(0, "string"), // The command string
+      param: Arg(1, "string") // The name of the parameter to parse
+    },
+    response: RetVal("json")
+  }),
+
+  /**
+   * Get the incremented value of some type
+   * @return a promise of a string containing the new argument text
+   */
+  typeincrement: method(function(typed, param) {
+    return this.requisition.update(typed).then(function() {
+      var assignment = this.requisition.getAssignment(param);
+      return this.requisition.increment(assignment).then(function() {
+        var arg = assignment.arg;
+        return arg == null ? undefined : arg.text;
+      });
+    });
+  }, {
+    request: {
+      typed: Arg(0, "string"), // The command string
+      param: Arg(1, "string") // The name of the parameter to parse
+    },
+    response: RetVal("string")
+  }),
+
+  /**
+   * See typeincrement
+   */
+  typedecrement: method(function(typed, param) {
+    return this.requisition.update(typed).then(function() {
+      var assignment = this.requisition.getAssignment(param);
+      return this.requisition.decrement(assignment).then(function() {
+        var arg = assignment.arg;
+        return arg == null ? undefined : arg.text;
+      });
+    });
+  }, {
+    request: {
+      typed: Arg(0, "string"), // The command string
+      param: Arg(1, "string") // The name of the parameter to parse
+    },
+    response: RetVal("string")
+  }),
+
+  /**
+   * Perform a lookup on a selection type to get the allowed values
+   */
+  selectioninfo: method(function(commandName, paramName, action) {
+    var command = this.requisition.canon.getCommand(commandName);
+    if (command == null) {
+      throw new Error('No command called \'' + commandName + '\'');
+    }
+
+    var type;
+    command.params.forEach(function(param) {
+      if (param.name === paramName) {
+        type = param.type;
+      }
+    });
+    if (type == null) {
+      throw new Error('No parameter called \'' + paramName + '\' in \'' +
+                      commandName + '\'');
+    }
+
+    switch (action) {
+      case 'lookup':
+        return type.lookup(this.requisition.executionContext);
+      case 'data':
+        return type.data(this.requisition.executionContext);
+      default:
+        throw new Error('Action must be either \'lookup\' or \'data\'');
+    }
+  }, {
+    request: {
+      commandName: Arg(0, "string"), // The command containing the parameter in question
+      paramName: Arg(1, "string"), // The name of the parameter
+      action: Arg(2, "string") // 'lookup' or 'data' depending on the function to call
+    },
+    response: RetVal("json")
+  }),
+
+  /**
+   * Execute a system command
+   * @return a promise of a string containing the output of the command
+   */
+  system: method(function(cmd, args, cwd, env) {
+    return host.exec({ cmd: cmd, args: args, cwd: cwd, env: env });
+  }, {
+    request: {
+      cmd: Arg(0, "string"), // The executable to call
+      args: Arg(1, "array:string"), // Arguments to the executable
+      cwd: Arg(2, "string"), // The working directory
+      env: Arg(3, "json") // A map of environment variables
+    },
+    response: RetVal("json")
+  }),
+
+  /**
+   * Examine the filesystem for file matches
+   */
+  parsefile: method(function(typed, filetype, existing, matches) {
+    var options = {
+      filetype: filetype,
+      existing: existing,
+      matches: new RegExp(matches)
+    };
+
+    return fileparser.parse(typed, options).then(function(reply) {
+      reply.status = reply.status.toString();
+      if (reply.predictor == null) {
+        return reply;
+      }
+
+      return reply.predictor().then(function(predictions) {
+        delete reply.predictor;
+        reply.predictions = predictions;
+        return reply;
+      });
+    });
+  }, {
+    request: {
+      typed: Arg(0, "string"), // The filename as typed by the user
+      filetype: Arg(1, "array:string"), // The expected filetype
+      existing: Arg(2, "string"), // Boolean which defines if a file/directory is expected to exist
+      matches: Arg(3, "json") // String of a regular expression which the result should match
+    },
+    response: RetVal("json")
+  })
+};
deleted file mode 100644
--- a/toolkit/devtools/gcli/source/lib/gcli/demo.js
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright 2012, Mozilla Foundation and contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-'use strict';
-
-/*
- * GCLI is built from a number of components (called items) composed as
- * required for each environment.
- * When adding to or removing from this list, we should keep the basics in sync
- * with the other environments.
- * See:
- * - lib/gcli/index.js: Generic basic set (without commands)
- * - lib/gcli/demo.js: Adds demo commands to basic set for use in web demo
- * - gcli.js: Add commands to basic set for use in Node command line
- * - mozilla/gcli/index.js: From scratch listing for Firefox
- * - lib/gcli/connectors/index.js: Client only items when executing remotely
- * - lib/gcli/connectors/direct.js: Test items for connecting to in-process GCLI
- */
-exports.items = [
-  require('./cli').items,
-  require('./commands/clear').items,
-  require('./commands/connect').items,
-  require('./commands/context').items,
-  require('./commands/exec').items,
-  require('./commands/global').items,
-  require('./commands/help').items,
-  require('./commands/intro').items,
-  require('./commands/lang').items,
-  require('./commands/mocks').items,
-  require('./commands/pref').items,
-  require('./commands/preflist').items,
-  require('./commands/test').items,
-
-  require('./commands/demo/alert').items,
-  require('./commands/demo/bugs').items,
-  require('./commands/demo/demo').items,
-  require('./commands/demo/echo').items,
-  require('./commands/demo/edit').items,
-  // require('./commands/demo/git').items,
-  // require('./commands/demo/hg').items,
-  require('./commands/demo/sleep').items,
-  require('./commands/demo/theme').items,
-
-  // Exclude Node commands on web
-].reduce(function(prev, curr) { return prev.concat(curr); }, []);
--- a/toolkit/devtools/gcli/source/lib/gcli/index.js
+++ b/toolkit/devtools/gcli/source/lib/gcli/index.js
@@ -11,33 +11,30 @@
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 'use strict';
 
-var api = require('./api');
-var Terminal = require('./ui/terminal').Terminal;
-var settings = require('./settings');
-
-// Patch-up old browsers
-require('./util/legacy');
+var Cc = require('chrome').Cc;
+var Ci = require('chrome').Ci;
+var Cu = require('chrome').Cu;
 
 /*
  * GCLI is built from a number of components (called items) composed as
  * required for each environment.
  * When adding to or removing from this list, we should keep the basics in sync
  * with the other environments.
  * See:
  * - lib/gcli/index.js: Generic basic set (without commands)
  * - lib/gcli/demo.js: Adds demo commands to basic set for use in web demo
  * - gcli.js: Add commands to basic set for use in Node command line
- * - mozilla/gcli/index.js: From scratch listing for Firefox
+ * - lib/gcli/index.js: (mozmaster branch) From scratch listing for Firefox
  * - lib/gcli/connectors/index.js: Client only items when executing remotely
  * - lib/gcli/connectors/direct.js: Test items for connecting to in-process GCLI
  */
 var items = [
   require('./types/delegate').items,
   require('./types/selection').items,
   require('./types/array').items,
 
@@ -55,45 +52,112 @@ var items = [
   require('./fields/delegate').items,
   require('./fields/selection').items,
 
   require('./ui/focus').items,
   require('./ui/intro').items,
 
   require('./converters/converters').items,
   require('./converters/basic').items,
-  require('./converters/html').items,
+  // require('./converters/html').items, // Prevent use of innerHTML
   require('./converters/terminal').items,
 
   require('./languages/command').items,
   require('./languages/javascript').items,
 
-  // require('./connectors/direct').items, // Loopback for testing only
-  // require('./connectors/rdp').items, // Firefox remote debug protocol
-  require('./connectors/websocket').items,
-  require('./connectors/xhr').items,
+  // require('./connectors/direct').items, // No need for loopback testing
+  // require('./connectors/rdp').items, // Needs fixing
+  // require('./connectors/websocket').items, // Not from chrome
+  // require('./connectors/xhr').items, // Not from chrome
 
-  // No commands in the basic set
+  // require('./cli').items, // No need for '{' with web console
+  require('./commands/clear').items,
+  // require('./commands/connect').items, // We need to fix our RDP connector
+  require('./commands/context').items,
+  // require('./commands/exec').items, // No exec in Firefox yet
+  require('./commands/global').items,
+  require('./commands/help').items,
+  // require('./commands/intro').items, // No need for intro command
+  require('./commands/lang').items,
+  // require('./commands/mocks').items, // Only for testing
+  require('./commands/pref').items,
+  // require('./commands/preflist').items, // Too slow in Firefox
+  // require('./commands/test').items, // Only for testing
+
+  // No demo or node commands
+
 ].reduce(function(prev, curr) { return prev.concat(curr); }, []);
 
+var api = require('./api');
 api.populateApi(exports);
 exports.addItems(items);
 
+var host = require('./util/host');
+
+exports.useTarget = host.script.useTarget;
+
 /**
- * createTerminal() calls 'Terminal.create()' but returns an object which
- * exposes a much restricted set of functions rather than all those exposed
- * by Terminal.
- * This allows for robust testing without exposing too many internals.
- * @param options See Terminal.create() for a description of the available
- * options.
+ * This code is internal and subject to change without notice.
+ * createDisplay() for Firefox requires an options object with the following
+ * members:
+ * - contentDocument: From the window of the attached tab
+ * - chromeDocument: GCLITerm.document
+ * - environment.hudId: GCLITerm.hudId
+ * - jsEnvironment.globalObject: 'window'
+ * - jsEnvironment.evalFunction: 'eval' in a sandbox
+ * - inputElement: GCLITerm.inputNode
+ * - completeElement: GCLITerm.completeNode
+ * - hintElement: GCLITerm.hintNode
+ * - inputBackgroundElement: GCLITerm.inputStack
  */
-exports.createTerminal = function(options) {
-  options = options || {};
-  if (options.settings != null) {
-    settings.setDefaults(options.settings);
-  }
+exports.createDisplay = function(opts) {
+  var FFDisplay = require('./mozui/ffdisplay').FFDisplay;
+  return new FFDisplay(opts);
+};
+
+var prefSvc = Cc['@mozilla.org/preferences-service;1']
+                        .getService(Ci.nsIPrefService);
+var prefBranch = prefSvc.getBranch(null).QueryInterface(Ci.nsIPrefBranch2);
+
+exports.hiddenByChromePref = function() {
+  return !prefBranch.prefHasUserValue('devtools.chrome.enabled');
+};
+
+
+try {
+  var Services = Cu.import('resource://gre/modules/Services.jsm', {}).Services;
+  var stringBundle = Services.strings.createBundle(
+          'chrome://browser/locale/devtools/gclicommands.properties');
 
-  return Terminal.create(options).then(function(terminal) {
-    options.terminal = terminal;
-    terminal.language.showIntro();
-    return terminal;
-  });
-};
+  /**
+   * Lookup a string in the GCLI string bundle
+   */
+  exports.lookup = function(name) {
+    try {
+      return stringBundle.GetStringFromName(name);
+    }
+    catch (ex) {
+      throw new Error('Failure in lookup(\'' + name + '\')');
+    }
+  };
+
+  /**
+   * Lookup a string in the GCLI string bundle
+   */
+  exports.lookupFormat = function(name, swaps) {
+    try {
+      return stringBundle.formatStringFromName(name, swaps, swaps.length);
+    }
+    catch (ex) {
+      throw new Error('Failure in lookupFormat(\'' + name + '\')');
+    }
+  };
+}
+catch (ex) {
+  console.error('Using string fallbacks', ex);
+
+  exports.lookup = function(name) {
+    return name;
+  };
+  exports.lookupFormat = function(name, swaps) {
+    return name;
+  };
+}
rename from toolkit/devtools/gcli/source/mozilla/gcli/mozui/completer.js
rename to toolkit/devtools/gcli/source/lib/gcli/mozui/completer.js
rename from toolkit/devtools/gcli/source/mozilla/gcli/mozui/ffdisplay.js
rename to toolkit/devtools/gcli/source/lib/gcli/mozui/ffdisplay.js
rename from toolkit/devtools/gcli/source/mozilla/gcli/mozui/inputter.js
rename to toolkit/devtools/gcli/source/lib/gcli/mozui/inputter.js
rename from toolkit/devtools/gcli/source/mozilla/gcli/mozui/tooltip.js
rename to toolkit/devtools/gcli/source/lib/gcli/mozui/tooltip.js
--- a/toolkit/devtools/gcli/source/lib/gcli/settings.js
+++ b/toolkit/devtools/gcli/source/lib/gcli/settings.js
@@ -11,178 +11,297 @@
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 'use strict';
 
-var util = require('./util/util');
+var imports = {};
 
+var Cc = require('chrome').Cc;
+var Ci = require('chrome').Ci;
+var Cu = require('chrome').Cu;
+
+var XPCOMUtils = Cu.import('resource://gre/modules/XPCOMUtils.jsm', {}).XPCOMUtils;
+var Services = Cu.import('resource://gre/modules/Services.jsm', {}).Services;
 
-/**
- * Where we store the settings that we've created
- */
-var settings = {};
+XPCOMUtils.defineLazyGetter(imports, 'prefBranch', function() {
+  var prefService = Cc['@mozilla.org/preferences-service;1']
+          .getService(Ci.nsIPrefService);
+  return prefService.getBranch(null).QueryInterface(Ci.nsIPrefBranch2);
+});
+
+XPCOMUtils.defineLazyGetter(imports, 'supportsString', function() {
+  return Cc['@mozilla.org/supports-string;1']
+          .createInstance(Ci.nsISupportsString);
+});
+
+var util = require('./util/util');
 
 /**
- * Where the values for the settings are stored while in use.
+ * All local settings have this prefix when used in Firefox
  */
-var settingValues = {};
-
-/**
- * Where the values for the settings are persisted for next use.
- */
-var settingStorage;
+var DEVTOOLS_PREFIX = 'devtools.gcli.';
 
 /**
  * The type library that we use in creating types for settings
  */
 var types;
 
 /**
- * Allow a system to setup a different set of defaults from what GCLI provides
+ * A class to wrap up the properties of a preference.
+ * @see toolkit/components/viewconfig/content/config.js
+ */
+function Setting(prefSpec) {
+  if (typeof prefSpec === 'string') {
+    // We're coming from getAll() i.e. a full listing of prefs
+    this.name = prefSpec;
+    this.description = '';
+  }
+  else {
+    // A specific addition by GCLI
+    this.name = DEVTOOLS_PREFIX + prefSpec.name;
+
+    if (prefSpec.ignoreTypeDifference !== true && prefSpec.type) {
+      if (this.type.name !== prefSpec.type) {
+        throw new Error('Locally declared type (' + prefSpec.type + ') != ' +
+            'Mozilla declared type (' + this.type.name + ') for ' + this.name);
+      }
+    }
+
+    this.description = prefSpec.description;
+  }
+
+  this.onChange = util.createEvent('Setting.onChange');
+}
+
+/**
+ * What type is this property: boolean/integer/string?
+ */
+Object.defineProperty(Setting.prototype, 'type', {
+  get: function() {
+    switch (imports.prefBranch.getPrefType(this.name)) {
+      case imports.prefBranch.PREF_BOOL:
+        return types.createType('boolean');
+
+      case imports.prefBranch.PREF_INT:
+        return types.createType('number');
+
+      case imports.prefBranch.PREF_STRING:
+        return types.createType('string');
+
+      default:
+        throw new Error('Unknown type for ' + this.name);
+    }
+  },
+  enumerable: true
+});
+
+/**
+ * What type is this property: boolean/integer/string?
  */
-exports.setDefaults = function(newValues) {
-  Object.keys(newValues).forEach(function(name) {
-    if (settingValues[name] === undefined) {
-      settingValues[name] = newValues[name];
+Object.defineProperty(Setting.prototype, 'value', {
+  get: function() {
+    switch (imports.prefBranch.getPrefType(this.name)) {
+      case imports.prefBranch.PREF_BOOL:
+        return imports.prefBranch.getBoolPref(this.name);
+
+      case imports.prefBranch.PREF_INT:
+        return imports.prefBranch.getIntPref(this.name);
+
+      case imports.prefBranch.PREF_STRING:
+        var value = imports.prefBranch.getComplexValue(this.name,
+                Ci.nsISupportsString).data;
+        // In case of a localized string
+        if (/^chrome:\/\/.+\/locale\/.+\.properties/.test(value)) {
+          value = imports.prefBranch.getComplexValue(this.name,
+                  Ci.nsIPrefLocalizedString).data;
+        }
+        return value;
+
+      default:
+        throw new Error('Invalid value for ' + this.name);
+    }
+  },
+
+  set: function(value) {
+    if (imports.prefBranch.prefIsLocked(this.name)) {
+      throw new Error('Locked preference ' + this.name);
+    }
+
+    switch (imports.prefBranch.getPrefType(this.name)) {
+      case imports.prefBranch.PREF_BOOL:
+        imports.prefBranch.setBoolPref(this.name, value);
+        break;
+
+      case imports.prefBranch.PREF_INT:
+        imports.prefBranch.setIntPref(this.name, value);
+        break;
+
+      case imports.prefBranch.PREF_STRING:
+        imports.supportsString.data = value;
+        imports.prefBranch.setComplexValue(this.name,
+                Ci.nsISupportsString,
+                imports.supportsString);
+        break;
+
+      default:
+        throw new Error('Invalid value for ' + this.name);
     }
+
+    Services.prefs.savePrefFile(null);
+  },
+
+  enumerable: true
+});
+
+/**
+ * Reset this setting to it's initial default value
+ */
+Setting.prototype.setDefault = function() {
+  imports.prefBranch.clearUserPref(this.name);
+  Services.prefs.savePrefFile(null);
+};
+
+
+/**
+ * Collection of preferences for sorted access
+ */
+var settingsAll = [];
+
+/**
+ * Collection of preferences for fast indexed access
+ */
+var settingsMap = new Map();
+
+/**
+ * Flag so we know if we've read the system preferences
+ */
+var hasReadSystem = false;
+
+/**
+ * Clear out all preferences and return to initial state
+ */
+function reset() {
+  settingsMap = new Map();
+  settingsAll = [];
+  hasReadSystem = false;
+}
+
+/**
+ * Reset everything on startup and shutdown because we're doing lazy loading
+ */
+exports.startup = function(t) {
+  reset();
+  types = t;
+  if (types == null) {
+    throw new Error('no types');
+  }
+};
+
+exports.shutdown = function() {
+  reset();
+};
+
+/**
+ * Load system prefs if they've not been loaded already
+ * @return true
+ */
+function readSystem() {
+  if (hasReadSystem) {
+    return;
+  }
+
+  imports.prefBranch.getChildList('').forEach(function(name) {
+    var setting = new Setting(name);
+    settingsAll.push(setting);
+    settingsMap.set(name, setting);
+  });
+
+  settingsAll.sort(function(s1, s2) {
+    return s1.name.localeCompare(s2.name);
+  });
+
+  hasReadSystem = true;
+}
+
+/**
+ * Get an array containing all known Settings filtered to match the given
+ * filter (string) at any point in the name of the setting
+ */
+exports.getAll = function(filter) {
+  readSystem();
+
+  if (filter == null) {
+    return settingsAll;
+  }
+
+  return settingsAll.filter(function(setting) {
+    return setting.name.indexOf(filter) !== -1;
   });
 };
 
 /**
- * Initialize the settingValues store from localStorage
- */
-exports.startup = function(t) {
-  types = t;
-  settingStorage = new LocalSettingStorage();
-  settingStorage.load(settingValues);
-};
-
-exports.shutdown = function() {
-};
-
-/**
- * 'static' function to get an array containing all known Settings
- */
-exports.getAll = function(filter) {
-  var all = [];
-  Object.keys(settings).forEach(function(name) {
-    if (filter == null || name.indexOf(filter) !== -1) {
-      all.push(settings[name]);
-    }
-  }.bind(this));
-  all.sort(function(s1, s2) {
-    return s1.name.localeCompare(s2.name);
-  }.bind(this));
-  return all;
-};
-
-/**
- * Add a new setting
- * @return The new Setting object
+ * Add a new setting.
  */
 exports.addSetting = function(prefSpec) {
-  var type = types.createType(prefSpec.type);
-  var setting = new Setting(prefSpec.name, type, prefSpec.description,
-                            prefSpec.defaultValue);
-  settings[setting.name] = setting;
+  var setting = new Setting(prefSpec);
+
+  if (settingsMap.has(setting.name)) {
+    // Once exists already, we're going to need to replace it in the array
+    for (var i = 0; i < settingsAll.length; i++) {
+      if (settingsAll[i].name === setting.name) {
+        settingsAll[i] = setting;
+      }
+    }
+  }
+
+  settingsMap.set(setting.name, setting);
   exports.onChange({ added: setting.name });
+
   return setting;
 };
 
 /**
  * Getter for an existing setting. Generally use of this function should be
  * avoided. Systems that define a setting should export it if they wish it to
  * be available to the outside, or not otherwise. Use of this function breaks
  * that boundary and also hides dependencies. Acceptable uses include testing
  * and embedded uses of GCLI that pre-define all settings (e.g. Firefox)
  * @param name The name of the setting to fetch
  * @return The found Setting object, or undefined if the setting was not found
  */
 exports.getSetting = function(name) {
-  return settings[name];
-};
+  // We might be able to give the answer without needing to read all system
+  // settings if this is an internal setting
+  var found = settingsMap.get(name);
+  if (!found) {
+    found = settingsMap.get(DEVTOOLS_PREFIX + name);
+  }
+
+  if (found) {
+    return found;
+  }
 
-/**
- * Remove a setting
- */
-exports.removeSetting = function(nameOrSpec) {
-  var name = typeof nameOrSpec === 'string' ? nameOrSpec : nameOrSpec.name;
-  delete settings[name];
-  exports.onChange({ removed: name });
+  if (hasReadSystem) {
+    return undefined;
+  }
+  else {
+    readSystem();
+    found = settingsMap.get(name);
+    if (!found) {
+      found = settingsMap.get(DEVTOOLS_PREFIX + name);
+    }
+    return found;
+  }
 };
 
 /**
  * Event for use to detect when the list of settings changes
  */
 exports.onChange = util.createEvent('Settings.onChange');
 
 /**
- * Implement the load() and save() functions to write a JSON string blob to
- * localStorage
- */
-function LocalSettingStorage() {
-}
-
-LocalSettingStorage.prototype.load = function(values) {
-  if (typeof localStorage === 'undefined') {
-    return;
-  }
-
-  var gcliSettings = localStorage.getItem('gcli-settings');
-  if (gcliSettings != null) {
-    var parsed = JSON.parse(gcliSettings);
-    Object.keys(parsed).forEach(function(name) {
-      values[name] = parsed[name];
-    });
-  }
-};
-
-LocalSettingStorage.prototype.save = function(values) {
-  if (typeof localStorage !== 'undefined') {
-    var json = JSON.stringify(values);
-    localStorage.setItem('gcli-settings', json);
-  }
-};
-
-exports.LocalSettingStorage = LocalSettingStorage;
-
-
-/**
- * A class to wrap up the properties of a Setting.
- * @see toolkit/components/viewconfig/content/config.js
+ * Remove a setting. A no-op in this case
  */
-function Setting(name, type, description, defaultValue) {
-  this.name = name;
-  this.type = type;
-  this.description = description;
-  this._defaultValue = defaultValue;
-
-  this.onChange = util.createEvent('Setting.onChange');
-  this.setDefault();
-}
-
-/**
- * Reset this setting to it's initial default value
- */
-Setting.prototype.setDefault = function() {
-  this.value = this._defaultValue;
-};
-
-/**
- * All settings 'value's are saved in the settingValues object
- */
-Object.defineProperty(Setting.prototype, 'value', {
-  get: function() {
-    return settingValues[this.name];
-  },
-
-  set: function(value) {
-    settingValues[this.name] = value;
-    settingStorage.save(settingValues);
-    this.onChange({ setting: this, value: value });
-  },
-
-  enumerable: true
-});
+exports.removeSetting = function() { };
--- a/toolkit/devtools/gcli/source/lib/gcli/ui/menu.html
+++ b/toolkit/devtools/gcli/source/lib/gcli/ui/menu.html
@@ -1,9 +1,11 @@
 
-<div class="gcli-menu-template">
-  <div class="gcli-menu-option" aria-live="polite" foreach="item in ${items}"
-      onclick="${onItemClickInternal}" title="${item.manual}">
-    <div class="gcli-menu-name">${item.name}</div>
-    <div class="gcli-menu-desc">${item.description}</div>
-  </div>
-  <div class="gcli-menu-more" if="${hasMore}">${l10n.fieldMenuMore}</div>
+<div>
+  <table class="gcli-menu-template" aria-live="polite">
+    <tr class="gcli-menu-option" foreach="item in ${items}"
+        onclick="${onItemClickInternal}" title="${item.manual}">
+      <td class="gcli-menu-name">${item.name}</td>
+      <td class="gcli-menu-desc">${item.description}</td>
+    </tr>
+  </table>
+  <div class="gcli-menu-more" if="${items.hasMore}">${l10n.fieldMenuMore}</div>
 </div>
deleted file mode 100644
--- a/toolkit/devtools/gcli/source/lib/gcli/ui/terminal.css
+++ /dev/null
@@ -1,544 +0,0 @@
-
-/* Layout */
-
-.gcli-display {
-  position: absolute;
-  top: 0;
-  bottom: 0;
-  left: 0;
-  right: 0;
-  overflow-x: hidden;
-  overflow-y: auto;
-}
-
-.gcli-in-input,
-.gcli-in-complete,
-.gcli-prompt {
-  position: absolute;
-  top: 0;
-  bottom: 0;
-  left: 0;
-  right: 0;
-}
-
-.gcli-in-top {
-  position: relative;
-}
-
-.gcli-in-input,
-.gcli-in-complete {
-  padding: 0;
-  background-color: transparent;
-}
-
-.gcli-in-input {
-  border: 0;
-  margin: 0;
-  outline: none;
-  width: 90%;
-}
-
-.gcli-in-complete {
-  color: transparent;
-  z-index: -1000;
-}
-
-.gcli-prompt {
-  z-index: -1001;
-  padding: 0 4px;
-}
-
-.gcli-panel {
-  overflow-y: auto;
-  overflow-x: hidden;
-  z-index: 2;
-  height: 5em;
-  left: 0;
-  bottom: 0;
-  margin-bottom: -3px;
-}
-
-.gcli-panel-hide {
-  opacity: 0;
-  height: 0;
-}
-
-.gcli-tt-description {
-  padding: 0 10px;
-}
-
-.gcli-tt-error {
-  padding: 0 10px;
-}
-
-.gcli-field {
-  width: 100%;
-}
-
-.gcli-field-javascript {
-  margin-bottom: 0;
-}
-
-.gcli-row-prompt {
-  padding: 0 7px 0 4px;
-}
-
-.gcli-section {
-  border-top: 1px solid #D1D0D0;
-  margin-top: 9px;
-}
-
-.gcli-row-in {
-  height: 20px;
-  line-height: 20px;
-}
-
-.gcli-row-out {
-  margin: 0;
-  padding: 0 21px;
-  line-height: 20px;
-}
-
-.gcli-row-out p {
-  margin: 5px 0;
-}
-
-.gcli-row-out input[type=password],
-.gcli-row-out input[type=text],
-.gcli-row-out textarea {
-  background: transparent;
-  padding: 3px;
-}
-
-.gcli-row-out button {
-  background-color: transparent;
-}
-
-.gcli-row-out table,
-.gcli-row-out td,
-.gcli-row-out th {
-  border: 0;
-  padding: 0 2px;
-}
-
-.gcli-row-throbber {
-  float: right;
-  width: 220px;
-  height: 19px;
-  background-image: url(data:image/gif;base64,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);
-}
-
-.gcli-row-terminal {
-  height: 200px;
-  width: 620px;
-}
-
-.gcli-row-subterminal {
-  height: 150px;
-  width: 300px;
-}
-
-.gcli-out-shortcut {
-  padding: 0 3px 1px;
-  margin: 1px 4px;
-  display: inline-block;
-}
-
-.gcli-out-shortcut:before {
-  content: ':';
-  padding-right: 2px;
-}
-
-.gcli-row-out a {
-  text-decoration: none;
-}
-
-.gcli-row-out a:hover {
-  cursor: pointer;
-}
-
-.gcli-row-in > img {
-  cursor: pointer;
-}
-
-.gcli-row-out button,
-.gcli-out-shortcut {
-  cursor: pointer;
-}
-
-.gcli-in-incomplete,
-.gcli-in-error {
-  border-bottom-width: 1px;
-  border-bottom-style: dotted;
-}
-
-.gcli-row-out a:hover {
-  border-bottom-width: 1px;
-  border-bottom-style: dotted;
-}
-
-.gcli-row-out input[type=password],
-.gcli-row-out input[type=text],
-.gcli-row-out textarea,
-.gcli-row-terminal,
-.gcli-row-subterminal {
-  border-width: 1px;
-  border-style: solid;
-  border-radius: 3px;
-}
-
-.gcli-row-out button,
-.gcli-out-shortcut {
-  border-width: 1px;
-  border-style: solid;
-  border-radius: 3px;
-}
-
-.gcli-out-shortcut {
-  border-radius: 3px;
-}
-
-
-/* Fonts */
-
-@font-face {
-  font-family: 'Source Sans Pro';
-  font-style: normal;
-  font-weight: 400;
-  src: local('Source Sans Pro'), local('SourceSansPro-Regular'), url(http://themes.googleusercontent.com/static/fonts/sourcesanspro/v5/ODelI1aHBYDBqgeIAH2zlNHq-FFgoDNV3GTKpHwuvtI.woff) format('woff');
-}
-
-@font-face {
-  font-family: 'Source Sans Pro';
-  font-style: normal;
-  font-weight: 700;
-  src: local('Source Sans Pro Bold'), local('SourceSansPro-Bold'), url(http://themes.googleusercontent.com/static/fonts/sourcesanspro/v5/toadOcfmlt9b38dHJxOBGIqjGYJUyOXcBwUQbRaNH6c.woff) format('woff');
-}
-
-@font-face {
-  font-family: 'Source Code Pro';
-  font-style: normal;
-  font-weight: 400;
-  src: local('Source Code Pro'), local('SourceCodePro-Regular'), url(http://themes.googleusercontent.com/static/fonts/sourcecodepro/v3/mrl8jkM18OlOQN8JLgasD9Hq-FFgoDNV3GTKpHwuvtI.woff) format('woff');
-}
-
-.gcli-in-input,
-.gcli-in-complete,
-.gcli-prompt,
-.gcli-display,
-.gcli-panel,
-.gcli-row-out button {
-  font-family: 'Source Sans Pro', sans-serif;
-  font-weight: 400;
-}
-
-.gcli-in-script .gcli-in-input,
-.gcli-in-script .gcli-in-complete,
-.gcli-in-script .gcli-prompt {
-  font-family: 'Source Code Pro', Monaco, monospace;
-  font-weight: 400;
-}
-
-.gcli-row-script {
-  font-family: 'Source Code Pro', Monaco, monospace;
-  font-weight: 400;
-  font-size: 100%;
-}
-
-.gcli-prompt,
-.gcli-row-prompt,
-.gcli-out-shortcut:before {
-  font-weight: 700;
-}
-
-.gcli-in-top,
-.gcli-in-input {
-  height: 20px;
-}
-
-.gcli-in-input,
-.gcli-in-complete,
-.gcli-prompt {
-  line-height: 20px;
-}
-
-.gcli-in-input,
-.gcli-in-complete {
-  padding-left: 17px;
-}
-
-.gcli-prompt,
-.gcli-row-prompt {
-  font-size: 120%;
-}
-
-.gcli-out-shortcut:before {
-  font-size: 110%;
-}
-
-.gcli-in-input,
-.gcli-in-complete {
-  font-size: 100%;
-}
-
-.gcli-row-script.gcli-row-out {
-  font-size: 100%;
-}
-
-.gcli-tt-description {
-  font-size: 90%;
-}
-
-.gcli-tt-error {
-  font-size: 80%;
-}
-
-.gcli-row-terminal {
-  font-size: 80%;
-}
-
-.gcli-row-subterminal {
-  font-size: 75%;
-}
-
-.gcli-row-out button,
-.gcli-out-shortcut {
-  font-size: 100%;
-}
-
-
-/* Themes */
-
-body.dark,
-.dark .theme-body {
-  background: #131c26;
-  background-image: url("data:image/png;base64,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");
-}
-  .dark .theme-body,
-  body.dark,
-  .dark .gcli-row-out strong,
-  .dark .gcli-row-out b,
-  .dark .gcli-row-out th,
-  .dark .gcli-row-out h1,
-  .dark .gcli-row-out h2,
-  .dark .gcli-row-out h3 {
-    color: #8fa1b2;
-  }
-  .dark .gcli-in-input,
-  .dark .gcli-row-in-typed,
-  .dark .gcli-out-shortcut,
-  .dark .gcli-row-out button {
-    color: white;
-  }
-
-body.light,
-.light .theme-body {
-  background: white;
-  color: black;
-}
-  .light .theme-body,
-  body.light,
-  .light .gcli-row-out strong,
-  .light .gcli-row-out b,
-  .light .gcli-row-out th,
-  .light .gcli-row-out h1,
-  .light .gcli-row-out h2,
-  .light .gcli-row-out h3 {
-    color: #303b47;
-  }
-  .light .gcli-in-input,
-  .light .gcli-row-in-typed,
-  .light .gcli-out-shortcut,
-  .light .gcli-row-out button {
-    color: black;
-  }
-
-.dark ::selection,
-.dark ::-moz-selection,
-.dark .theme-selected {
-  background-color: #26394D;
-}
-
-.light ::selection,
-.light ::-moz-selection,
-.light .theme-selected {
-  background-color: #CCC;
-}
-
-.dark .theme-link,
-.dark .gcli-row-out a {
-  color: #3689b2; /* blue */
-}
-
-.light .theme-link,
-.light .gcli-row-out a {
-  color: hsl(208,56%,40%); /* blue */
-}
-
-.dark .theme-comment {
-  color: #5c6773; /* grey */
-}
-  .dark .token.comment {
-    color: #5c6773;
-  }
-
-.light .theme-comment {
-  color: hsl(90,2%,46%); /* grey */
-}
-  .light .token.comment {
-    color: #5c6773;
-  }
-
-.dark .theme-gutter {
-  background-color: #0f171f;
-  color: #667380;
-  border-color: #303b47;
-}
-  .dark .token.punctuation {
-    color: #667380;
-  }
-
-.light .theme-gutter {
-  background-color: hsl(0,0%,90%);
-  color: #667380;
-  border-color: hsl(0,0%,65%);
-}
-  .light .token.punctuation {
-    color: #667380;
-  }
-
-.dark .theme-separator {
-  border-color: #303b47; /* grey */
-}
-
-.light .theme-separator {
-  border-color: #cddae5; /* grey */
-}
-
-.dark .theme-fg-color1,
-.dark .gcli-row-prompt.gcli-row-complete {
-  color: #5c9966; /* green */
-}
-  .dark .token.boolean,
-  .dark .token.number {
-    color: #5c9966;
-  }
-
-.light .theme-fg-color1,
-.light .gcli-row-prompt.gcli-row-complete {
-  color: hsl(72,100%,27%) /* green */;
-}
-  .light .token.boolean,
-  .light .token.number {
-    color: hsl(72,100%,27%);
-  }
-
-.dark .theme-fg-color2 {
-  color: #3689b2; /* blue */
-}
-  .dark .gcli-in-ontab {
-    color: hsl(211.6, 33.3%, 25%);
-  }
-  .dark .token.operator {
-    color: #3689b2;
-  }
-
-.light .theme-fg-color2,
-.light .gcli-in-ontab {
-  color: hsl(208,56%,40%); /* blue */
-}
-  .light .token.operator {
-    color: hsl(208,56%,40%);
-  }
-
-.dark .theme-fg-color3 {
-  color: #a673bf; /* pink/lavender */
-}
-  .dark .token.keyword {
-    color: #a673bf;
-  }
-
-.light .theme-fg-color3 {
-  color: hsl(208,81%,21%) /* dark blue */
-}
-  .light .token.keyword {
-    color: hsl(208,81%,21%);
-  }
-
-.dark .theme-fg-color4,
-.dark .gcli-row-prompt {
-  color: #6270b2; /* purple/violet */
-}
-  .dark .token.regex {
-    color: #6270b2;
-  }
-
-.light .theme-fg-color4,
-.light .gcli-row-prompt {
-  color: hsl(24,85%,39%); /* Orange */
-}
-  .light .token.regex {
-    color: hsl(24,85%,39%);
-  }
-
-.dark .theme-fg-color5 {
-  color: #a18650; /* Yellow */
-}
-.dark .gcli-in-todo {
-  color: hsl(211.6, 33.3%, 0%);
-}
-  .dark .token.string {
-    color: #a18650;
-  }
-
-.light .theme-fg-color5,
-.light .gcli-in-todo {
-  color: #a18650; /* Yellow */
-}
-  .light .token.string {
-    color: #a18650;
-  }
-
-.dark .theme-fg-color6,
-.dark .gcli-out-shortcut:before {
-  color: #b26b47; /* Orange */
-}
-
-.light .theme-fg-color6,
-.light .gcli-out-shortcut:before {
-  color: hsl(24,85%,39%); /* Orange */
-}
-
-.dark .theme-fg-color7,
-.dark .gcli-row-prompt.gcli-row-error {
-  color: #bf5656; /* Red */
-}
-  .dark .gcli-in-error {
-    border-bottom-color: #bf5656;
-  }
-
-.light .theme-fg-color7,
-.light .gcli-row-prompt.gcli-row-error {
-  color: #bf5656; /* Red */
-}
-  .light .gcli-in-error {
-    border-bottom-color: #bf5656;
-  }
-
-.dark .gcli-tt {
-  background-color: rgba(0, 0, 0, 0.14);
-}
-
-.light .gcli-tt {
-  background-color: rgba(0, 0, 0, 0.1);
-}
-
-.dark .gcli-row-out button,
-.dark .gcli-out-shortcut {
-  border-color: #333;
-}
-
-.light .gcli-row-out button,
-.light .gcli-out-shortcut {
-  border-color: #ccc;
-}
deleted file mode 100644
--- a/toolkit/devtools/gcli/source/lib/gcli/ui/terminal.html
+++ /dev/null
@@ -1,32 +0,0 @@
-
-<div>
-  <div save="${displayElement}" class="gcli-display">
-    <div save="${topElement}" class="gcli-in-top">
-      <input save="${inputElement}" class="gcli-in-input" type="text" autofocus="autofocus" spellcheck="false"/>
-      <div save="${completeElement}" class="gcli-in-complete" tabindex="-1" aria-live="polite">
-        <!-- Sub template used to show completion -->
-        <div>
-          <loop foreach="member in ${statusMarkup}">
-            <span class="${member.className}">${member.string}</span>
-          </loop>
-          <span class="gcli-in-ontab">${directTabText}</span>
-          <span class="gcli-in-todo" foreach="param in ${emptyParameters}">${param}</span>
-          <span class="gcli-in-ontab">${arrowTabText}</span>
-          <span class="gcli-in-closebrace theme-comment" if="${unclosedJs}">}</span>
-        </div>
-      </div>
-      <div save="${promptElement}" class="gcli-prompt theme-fg-color6"></div>
-    </div>
-    <div save="${panelElement}" class="gcli-panel">
-      <div save="${tooltipElement}" class="gcli-tooltip">
-        <!-- Sub template used for popup hints -->
-        <div class="gcli-tt" aria-live="polite">
-          <div save="${descriptionEle}" class="gcli-tt-description">${language.description}</div>
-          ${field.element}
-          <div save="${errorEle}" class="gcli-tt-error theme-fg-color7">${language.message}</div>
-          <div class="gcli-tt-highlight"></div>
-        </div>
-      </div>
-    </div>
-  </div>
-</div>
deleted file mode 100644
--- a/toolkit/devtools/gcli/source/lib/gcli/ui/terminal.js
+++ /dev/null
@@ -1,668 +0,0 @@
-/*
- * Copyright 2012, Mozilla Foundation and contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-'use strict';
-
-var promise = require('../util/promise');
-var util = require('../util/util');
-var domtemplate = require('../util/domtemplate');
-var KeyEvent = require('../util/util').KeyEvent;
-var host = require('../util/host');
-
-var languages = require('../languages/languages');
-var History = require('./history').History;
-
-var FocusManager = require('./focus').FocusManager;
-
-var RESOLVED = promise.resolve(undefined);
-
-/**
- * Shared promises for loading resource files
- */
-var resourcesPromise;
-
-/**
- * Asynchronous construction. Use Terminal.create();
- * @private
- */
-function Terminal() {
-  throw new Error('Use Terminal.create().then(...) rather than new Terminal()');
-}
-
-/**
- * A wrapper to take care of the functions concerning an input element
- * @param components Object that links to other UI components. GCLI provided:
- * - requisition
- * - document
- */
-Terminal.create = function(options) {
-  if (resourcesPromise == null) {
-    resourcesPromise = promise.all([
-      host.staticRequire(module, './terminal.css'),
-      host.staticRequire(module, './terminal.html')
-    ]);
-  }
-  return resourcesPromise.then(function(resources) {
-    var terminal = Object.create(Terminal.prototype);
-    return terminal._init(options, resources[0], resources[1]);
-  });
-};
-
-/**
- * Asynchronous construction. Use Terminal.create();
- * @private
- */
-Terminal.prototype._init = function(options, terminalCss, terminalHtml) {
-  this.document = options.document || document;
-  this.options = options;
-
-  this.focusManager = new FocusManager(this.document);
-  this.onInputChange = util.createEvent('Terminal.onInputChange');
-
-  // Configure the UI
-  this.rootElement = this.document.getElementById('gcli-root');
-  if (!this.rootElement) {
-    throw new Error('Missing element, id=gcli-root');
-  }
-  this.rootElement.terminal = this;
-
-  // terminal.html contains sub-templates which we detach for later processing
-  var template = util.toDom(this.document, terminalHtml);
-
-  // JSDom appears to have a broken parentElement, so this is a workaround
-  // this.tooltipTemplate = template.querySelector('.gcli-tt');
-  // this.tooltipTemplate.parentElement.removeChild(this.tooltipTemplate);
-  var tooltipParent = template.querySelector('.gcli-tooltip');
-  this.tooltipTemplate = tooltipParent.children[0];
-  tooltipParent.removeChild(this.tooltipTemplate);
-
-  // this.completerTemplate = template.querySelector('.gcli-in-complete > div');
-  // this.completerTemplate.parentElement.removeChild(this.completerTemplate);
-  var completerParent = template.querySelector('.gcli-in-complete');
-  this.completerTemplate = completerParent.children[0];
-  completerParent.removeChild(this.completerTemplate);
-  // We want the spans to line up without the spaces in the template
-  util.removeWhitespace(this.completerTemplate, true);
-
-  // Now we've detached the sub-templates, load what is left
-  // The following elements are stored into 'this' by this template process:
-  // displayElement, panelElement, tooltipElement,
-  // inputElement, completeElement, promptElement
-  domtemplate.template(template, this, { stack: 'terminal.html' });
-  while (template.hasChildNodes()) {
-    this.rootElement.appendChild(template.firstChild);
-  }
-
-  if (terminalCss != null) {
-    this.style = util.importCss(terminalCss, this.document, 'gcli-tooltip');
-  }
-
-  this.tooltipElement.classList.add('gcli-panel-hide');
-
-  // Firefox doesn't autofocus with dynamically added elements (Bug 662496)
-  this.inputElement.focus();
-
-  // Used to distinguish focus from TAB in CLI. See onKeyUp()
-  this.lastTabDownAt = 0;
-
-  // Setup History
-  this.history = new History();
-  this._scrollingThroughHistory = false;
-
-  // Initially an asynchronous completion isn't in-progress
-  this._completed = RESOLVED;
-
-  // Avoid updating when the keyUp results in no change
-  this._previousValue = undefined;
-
-  // We cache the fields we create so we can destroy them later
-  this.fields = [];
-
-  // Bind handlers
-  this.focus = this.focus.bind(this);
-  this.onKeyDown = this.onKeyDown.bind(this);
-  this.onKeyUp = this.onKeyUp.bind(this);
-  this.onMouseUp = this.onMouseUp.bind(this);
-  this.onOutput = this.onOutput.bind(this);
-
-  this.rootElement.addEventListener('click', this.focus, false);
-
-  // Ensure that TAB/UP/DOWN isn't handled by the browser
-  this.inputElement.addEventListener('keydown', this.onKeyDown, false);
-  this.inputElement.addEventListener('keyup', this.onKeyUp, false);
-
-  // Cursor position affects hint severity
-  this.inputElement.addEventListener('mouseup', this.onMouseUp, false);
-
-  this.focusManager.onVisibilityChange.add(this.visibilityChanged, this);
-  this.focusManager.addMonitoredElement(this.tooltipElement, 'tooltip');
-  this.focusManager.addMonitoredElement(this.inputElement, 'input');
-
-  this.onInputChange.add(this.updateCompletion, this);
-
-  host.script.onOutput.add(this.onOutput);
-
-  // Use the default language
-  return this.switchLanguage(null).then(function() {
-    return this;
-  }.bind(this));
-};
-
-/**
- * Avoid memory leaks
- */
-Terminal.prototype.destroy = function() {
-  this.focusManager.removeMonitoredElement(this.inputElement, 'input');
-  this.focusManager.removeMonitoredElement(this.tooltipElement, 'tooltip');
-  this.focusManager.onVisibilityChange.remove(this.visibilityChanged, this);
-
-  this.inputElement.removeEventListener('mouseup', this.onMouseUp, false);
-  this.inputElement.removeEventListener('keydown', this.onKeyDown, false);
-  this.inputElement.removeEventListener('keyup', this.onKeyUp, false);
-  this.rootElement.removeEventListener('click', this.focus, false);
-
-  this.language.destroy();
-  this.history.destroy();
-  this.focusManager.destroy();
-
-  if (this.style) {
-    this.style.parentNode.removeChild(this.style);
-    this.style = undefined;
-  }
-
-  this.field.onFieldChange.remove(this.fieldChanged, this);
-  this.field.destroy();
-
-  this.onInputChange.remove(this.updateCompletion, this);
-
-  // Remove the output elements so they free the event handers
-  util.clearElement(this.displayElement);
-
-  this.focus = undefined;
-  this.onMouseUp = undefined;
-  this.onKeyDown = undefined;
-  this.onKeyUp = undefined;
-
-  this.rootElement = undefined;
-  this.inputElement = undefined;
-  this.promptElement = undefined;
-  this.completeElement = undefined;
-  this.tooltipElement = undefined;
-  this.panelElement = undefined;
-  this.displayElement = undefined;
-
-  this.completerTemplate = undefined;
-  this.tooltipTemplate = undefined;
-
-  this.errorEle = undefined;
-  this.descriptionEle = undefined;
-
-  this.document = undefined;
-};
-
-/**
- * Use an alternative language
- */
-Terminal.prototype.switchLanguage = function(name) {
-  if (this.language != null) {
-    this.language.destroy();
-  }
-
-  return languages.createLanguage(name, this).then(function(language) {
-    this._updateLanguage(language);
-  }.bind(this));
-};
-
-/**
- * Temporarily use an alternative language
- */
-Terminal.prototype.pushLanguage = function(name) {
-  return languages.createLanguage(name, this).then(function(language) {
-    this.origLanguage = this.language;
-    this._updateLanguage(language);
-  }.bind(this));
-};
-
-/**
- * Return to use the original language
- */
-Terminal.prototype.popLanguage = function() {
-  if (this.origLanguage == null) {
-    return RESOLVED;
-  }
-
-  this._updateLanguage(this.origLanguage);
-  this.origLanguage = undefined;
-
-  return RESOLVED;
-};
-
-/**
- * Internal helper to make sure everything knows about the new language
- */
-Terminal.prototype._updateLanguage = function(language) {
-  this.language = language;
-
-  if (this.language.proportionalFonts) {
-    this.topElement.classList.remove('gcli-in-script');
-  }
-  else {
-    this.topElement.classList.add('gcli-in-script');
-  }
-
-  this.language.updateHints();
-  this.updateCompletion();
-  this.promptElement.innerHTML = this.language.prompt;
-};
-
-/**
- * Sometimes the environment provides asynchronous output, we display it here
- */
-Terminal.prototype.onOutput = function(ev) {
-  console.log('onOutput', ev);
-
-  var rowoutEle = this.document.createElement('pre');
-  rowoutEle.classList.add('gcli-row-out');
-  rowoutEle.classList.add('gcli-row-script');
-  rowoutEle.setAttribute('aria-live', 'assertive');
-
-  var output = '         // ';
-  if (ev.level === 'warn') {
-    output += '!';
-  }
-  else if (ev.level === 'error') {
-    output += '✖';
-  }
-  else {
-    output += '→';
-  }
-
-  output += ' ' + ev.arguments.map(function(arg) {
-    return arg;
-  }).join(',');
-
-  rowoutEle.innerHTML = output;
-
-  this.addElement(rowoutEle);
-};
-
-/**
- * Handler for the input-element.onMouseUp event
- */
-Terminal.prototype.onMouseUp = function(ev) {
-  this.language.caretMoved(this.inputElement.selectionStart);
-};
-
-/**
- * Set the input field to a value, for external use.
- * It does not execute the input or affect the history.
- * This function should not be called internally, by Terminal and never as a
- * result of a keyboard event on this.inputElement or bug 676520 could be
- * triggered.
- */
-Terminal.prototype.setInput = function(str) {
-  this._scrollingThroughHistory = false;
-  return this._setInput(str);
-};
-
-/**
- * @private Internal version of setInput
- */
-Terminal.prototype._setInput = function(str) {
-  this.inputElement.value = str;
-  this._previousValue = this.inputElement.value;
-
-  this._completed = this.language.handleInput(str);
-  return this._completed;
-};
-
-/**
- * Focus the input element
- */
-Terminal.prototype.focus = function() {
-  this.inputElement.focus();
-  this.language.caretMoved(this.inputElement.selectionStart);
-};
-
-/**
- * Ensure certain keys (arrows, tab, etc) that we would like to handle
- * are not handled by the browser
- */
-Terminal.prototype.onKeyDown = function(ev) {
-  if (ev.keyCode === KeyEvent.DOM_VK_UP ||
-      ev.keyCode === KeyEvent.DOM_VK_DOWN) {
-    ev.preventDefault();
-    return;
-  }
-
-  // The following keys do not affect the state of the command line so we avoid
-  // informing the focusManager about keyboard events that involve these keys
-  if (ev.keyCode === KeyEvent.DOM_VK_F1 ||
-      ev.keyCode === KeyEvent.DOM_VK_ESCAPE ||
-      ev.keyCode === KeyEvent.DOM_VK_UP ||
-      ev.keyCode === KeyEvent.DOM_VK_DOWN) {
-    return;
-  }
-
-  if (this.focusManager) {
-    this.focusManager.onInputChange();
-  }
-
-  if (ev.keyCode === KeyEvent.DOM_VK_BACK_SPACE &&
-      this.inputElement.value === '') {
-    return this.popLanguage();
-  }
-
-  if (ev.keyCode === KeyEvent.DOM_VK_TAB) {
-    this.lastTabDownAt = 0;
-    if (!ev.shiftKey) {
-      ev.preventDefault();
-      // Record the timestamp of this TAB down so onKeyUp can distinguish
-      // focus from TAB in the CLI.
-      this.lastTabDownAt = ev.timeStamp;
-    }
-    if (ev.metaKey || ev.altKey || ev.crtlKey) {
-      if (this.document.commandDispatcher) {
-        this.document.commandDispatcher.advanceFocus();
-      }
-      else {
-        this.inputElement.blur();
-      }
-    }
-  }
-};
-
-/**
- * Handler for use with DOM events, which just calls the promise enabled
- * handleKeyUp function but checks the exit state of the promise so we know
- * if something went wrong.
- */
-Terminal.prototype.onKeyUp = function(ev) {
-  this.handleKeyUp(ev).then(null, util.errorHandler);
-};
-
-/**
- * The main keyboard processing loop
- * @return A promise that resolves (to undefined) when the actions kicked off
- * by this handler are completed.
- */
-Terminal.prototype.handleKeyUp = function(ev) {
-  if (this.focusManager && ev.keyCode === KeyEvent.DOM_VK_F1) {
-    this.focusManager.helpRequest();
-    return RESOLVED;
-  }
-
-  if (this.focusManager && ev.keyCode === KeyEvent.DOM_VK_ESCAPE) {
-    if (this.focusManager.isTooltipVisible ||
-        this.focusManager.isOutputVisible) {
-      this.focusManager.removeHelp();
-    }
-    else if (this.inputElement.value === '') {
-      return this.popLanguage();
-    }
-    return RESOLVED;
-  }
-
-  if (ev.keyCode === KeyEvent.DOM_VK_UP) {
-    if (this.isMenuShowing) {
-      return this.incrementChoice();
-    }
-
-    if (this.inputElement.value === '' || this._scrollingThroughHistory) {
-      this._scrollingThroughHistory = true;
-      return this._setInput(this.history.backward());
-    }
-
-    return this.language.handleUpArrow().then(function(handled) {
-      if (!handled) {
-        return this.incrementChoice();
-      }
-    }.bind(this));
-  }
-
-  if (ev.keyCode === KeyEvent.DOM_VK_DOWN) {
-    if (this.isMenuShowing) {
-      return this.decrementChoice();
-    }
-
-    if (this.inputElement.value === '' || this._scrollingThroughHistory) {
-      this._scrollingThroughHistory = true;
-      return this._setInput(this.history.forward());
-    }
-
-    return this.language.handleDownArrow().then(function(handled) {
-      if (!handled) {
-        return this.decrementChoice();
-      }
-    }.bind(this));
-  }
-
-  if (ev.keyCode === KeyEvent.DOM_VK_RETURN) {
-    var input = this.inputElement.value;
-    return this.language.handleReturn(input).then(function(handled) {
-      if (!handled) {
-        this._scrollingThroughHistory = false;
-
-        if (!this.selectChoice()) {
-          this.focusManager.setError(true);
-        }
-        else {
-          return this.popLanguage();
-        }
-      }
-      else {
-        return this.popLanguage();
-      }
-    }.bind(this));
-  }
-
-  if (ev.keyCode === KeyEvent.DOM_VK_TAB && !ev.shiftKey) {
-    // Being able to complete 'nothing' is OK if there is some context, but
-    // when there is nothing on the command line it just looks bizarre.
-    var hasContents = (this.inputElement.value.length > 0);
-
-    // If the TAB keypress took the cursor from another field to this one,
-    // then they get the keydown/keypress, and we get the keyup. In this
-    // case we don't want to do any completion.
-    // If the time of the keydown/keypress of TAB was close (i.e. within
-    // 1 second) to the time of the keyup then we assume that we got them
-    // both, and do the completion.
-    if (hasContents && this.lastTabDownAt + 1000 > ev.timeStamp) {
-      this._completed = this.language.handleTab();
-    }
-    else {
-      this._completed = RESOLVED;
-    }
-
-    this.lastTabDownAt = 0;
-    this._scrollingThroughHistory = false;
-
-    return this._completed;
-  }
-
-  if (this._previousValue === this.inputElement.value) {
-    return RESOLVED;
-  }
-
-  var value = this.inputElement.value;
-  this._scrollingThroughHistory = false;
-  this._previousValue = this.inputElement.value;
-
-  this._completed = this.language.handleInput(value);
-  return this._completed;
-};
-
-/**
- * What is the index of the currently highlighted option?
- */
-Terminal.prototype.getChoiceIndex = function() {
-  return this.field && this.field.menu ? this.field.menu.getChoiceIndex() : 0;
-};
-
-/**
- * Don't show any menu options
- */
-Terminal.prototype.unsetChoice = function() {
-  if (this.field && this.field.menu) {
-    this.field.menu.unsetChoice();
-  }
-  return this.updateCompletion();
-};
-
-/**
- * Select the previous option in a list of choices
- */
-Terminal.prototype.incrementChoice = function() {
-  if (this.field && this.field.menu) {
-    this.field.menu.incrementChoice();
-  }
-  return this.updateCompletion();
-};
-
-/**
- * Select the next option in a list of choices
- */
-Terminal.prototype.decrementChoice = function() {
-  if (this.field && this.field.menu) {
-    this.field.menu.decrementChoice();
-  }
-  return this.updateCompletion();
-};
-
-/**
- * Pull together an input object, which may include XUL hacks
- */
-Terminal.prototype.getInputState = function() {
-  var input = {
-    typed: this.inputElement.value,
-    cursor: {
-      start: this.inputElement.selectionStart,
-      end: this.inputElement.selectionEnd
-    }
-  };
-
-  // Workaround for potential XUL bug 676520 where textbox gives incorrect
-  // values for its content
-  if (input.typed == null) {
-    input = { typed: '', cursor: { start: 0, end: 0 } };
-  }
-
-  return input;
-};
-
-/**
- * Bring the completion element up to date with what the language says
- */
-Terminal.prototype.updateCompletion = function() {
-  return this.language.getCompleterTemplateData().then(function(data) {
-    var template = this.completerTemplate.cloneNode(true);
-    domtemplate.template(template, data, { stack: 'terminal.html#completer' });
-
-    util.clearElement(this.completeElement);
-    while (template.hasChildNodes()) {
-      this.completeElement.appendChild(template.firstChild);
-    }
-  }.bind(this));
-};
-
-/**
- * The terminal acts on UP/DOWN if there is a menu showing
- */
-Object.defineProperty(Terminal.prototype, 'isMenuShowing', {
-  get: function() {
-    return this.focusManager.isTooltipVisible &&
-           this.field != null &&
-           this.field.menu != null;
-  },
-  enumerable: true
-});
-
-/**
- * Allow the terminal to use RETURN to chose the current menu item when
- * it can't execute the command line
- * @return true if there was a selection to use, false otherwise
- */
-Terminal.prototype.selectChoice = function(ev) {
-  if (this.field && this.field.selectChoice) {
-    return this.field.selectChoice();
-  }
-  return false;
-};
-
-/**
- * Called by the onFieldChange event on the current Field
- */
-Terminal.prototype.fieldChanged = function(ev) {
-  this.language.fieldChanged(ev);
-
-  // Nasty hack, the terminal won't know about the text change yet, so it will
-  // get it's calculations wrong. We need to wait until the current set of
-  // changes has had a chance to propagate
-  this.document.defaultView.setTimeout(function() {
-    this.focus();
-  }.bind(this), 10);
-};
-
-/**
- * Tweak CSS to show/hide the output
- */
-Terminal.prototype.visibilityChanged = function(ev) {
-  if (!this.panelElement) {
-    return;
-  }
-
-  if (ev.tooltipVisible) {
-    this.tooltipElement.classList.remove('gcli-panel-hide');
-  }
-  else {
-    this.tooltipElement.classList.add('gcli-panel-hide');
-  }
-  this.scrollToBottom();
-};
-
-/**
- * For language to add elements to the output
- */
-Terminal.prototype.addElement = function(element) {
-  this.displayElement.insertBefore(element, this.topElement);
-};
-
-/**
- * Clear the added added output
- */
-Terminal.prototype.clear = function() {
-  while (this.displayElement.hasChildNodes()) {
-    if (this.displayElement.firstChild === this.topElement) {
-      break;
-    }
-    this.displayElement.removeChild(this.displayElement.firstChild);
-  }
-};
-
-/**
- * Scroll the output area down to make the input visible
- */
-Terminal.prototype.scrollToBottom = function() {
-  // We need to see the output of the latest command entered
-  // Certain browsers have a bug such that scrollHeight is too small
-  // when content does not fill the client area of the element
-  var scrollHeight = Math.max(this.displayElement.scrollHeight,
-                              this.displayElement.clientHeight);
-  this.displayElement.scrollTop =
-                      scrollHeight - this.displayElement.clientHeight;
-};
-
-exports.Terminal = Terminal;
--- a/toolkit/devtools/gcli/source/lib/gcli/util/domtemplate.js
+++ b/toolkit/devtools/gcli/source/lib/gcli/util/domtemplate.js
@@ -9,594 +9,13 @@
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
-/* jshint strict:false */
-//
-//
-//
-
-'do not use strict';
-
-// WARNING: do not 'use strict' without reading the notes in envEval();
-// Also don't remove the 'do not use strict' marker. The orion build uses these
-// markers to know where to insert AMD headers.
-
-/**
- * For full documentation, see:
- * https://github.com/mozilla/domtemplate/blob/master/README.md
- */
-
-/**
- * Begin a new templating process.
- * @param node A DOM element or string referring to an element's id
- * @param data Data to use in filling out the template
- * @param options Options to customize the template processing. One of:
- * - allowEval: boolean (default false) Basic template interpolations are
- *   either property paths (e.g. ${a.b.c.d}), or if allowEval=true then we
- *   allow arbitrary JavaScript
- * - stack: string or array of strings (default empty array) The template
- *   engine maintains a stack of tasks to help debug where it is. This allows
- *   this stack to be prefixed with a template name
- * - blankNullUndefined: By default DOMTemplate exports null and undefined
- *   values using the strings 'null' and 'undefined', which can be helpful for
- *   debugging, but can introduce unnecessary extra logic in a template to
- *   convert null/undefined to ''. By setting blankNullUndefined:true, this
- *   conversion is handled by DOMTemplate
- */
-var template = function(node, data, options) {
-  var state = {
-    options: options || {},
-    // We keep a track of the nodes that we've passed through so we can keep
-    // data.__element pointing to the correct node
-    nodes: []
-  };
-
-  state.stack = state.options.stack;
-
-  if (!Array.isArray(state.stack)) {
-    if (typeof state.stack === 'string') {
-      state.stack = [ options.stack ];
-    }
-    else {
-      state.stack = [];
-    }
-  }
-
-  processNode(state, node, data);
-};
-
-if (typeof exports !== 'undefined') {
-  exports.template = template;
-}
-
-/**
- * Helper for the places where we need to act asynchronously and keep track of
- * where we are right now
- */
-function cloneState(state) {
-  return {
-    options: state.options,
-    stack: state.stack.slice(),
-    nodes: state.nodes.slice()
-  };
-}
-
-/**
- * Regex used to find ${...} sections in some text.
- * Performance note: This regex uses ( and ) to capture the 'script' for
- * further processing. Not all of the uses of this regex use this feature so
- * if use of the capturing group is a performance drain then we should split
- * this regex in two.
- */
-var TEMPLATE_REGION = /\$\{([^}]*)\}/g;
-
-/**
- * Recursive function to walk the tree processing the attributes as it goes.
- * @param node the node to process. If you pass a string in instead of a DOM
- * element, it is assumed to be an id for use with document.getElementById()
- * @param data the data to use for node processing.
- */
-function processNode(state, node, data) {
-  if (typeof node === 'string') {
-    node = document.getElementById(node);
-  }
-  if (data == null) {
-    data = {};
-  }
-  state.stack.push(node.nodeName + (node.id ? '#' + node.id : ''));
-  var pushedNode = false;
-  try {
-    // Process attributes
-    if (node.attributes && node.attributes.length) {
-      // We need to handle 'foreach' and 'if' first because they might stop
-      // some types of processing from happening, and foreach must come first
-      // because it defines new data on which 'if' might depend.
-      if (node.hasAttribute('foreach')) {
-        processForEach(state, node, data);
-        return;
-      }
-      if (node.hasAttribute('if')) {
-        if (!processIf(state, node, data)) {
-          return;
-        }
-      }
-      // Only make the node available once we know it's not going away
-      state.nodes.push(data.__element);
-      data.__element = node;
-      pushedNode = true;
-      // It's good to clean up the attributes when we've processed them,
-      // but if we do it straight away, we mess up the array index
-      var attrs = Array.prototype.slice.call(node.attributes);
-      for (var i = 0; i < attrs.length; i++) {
-        var value = attrs[i].value;
-        var name = attrs[i].name;
-
-        state.stack.push(name);
-        try {
-          if (name === 'save') {
-            // Save attributes are a setter using the node
-            value = stripBraces(state, value);
-            property(state, value, data, node);
-            node.removeAttribute('save');
-          }
-          else if (name.substring(0, 2) === 'on') {
-            // If this attribute value contains only an expression
-            if (value.substring(0, 2) === '${' && value.slice(-1) === '}' &&
-                    value.indexOf('${', 2) === -1) {
-              value = stripBraces(state, value);
-              var func = property(state, value, data);
-              if (typeof func === 'function') {
-                node.removeAttribute(name);
-                var capture = node.hasAttribute('capture' + name.substring(2));
-                node.addEventListener(name.substring(2), func, capture);
-                if (capture) {
-                  node.removeAttribute('capture' + name.substring(2));
-                }
-              }
-              else {
-                // Attribute value is not a function - use as a DOM-L0 string
-                node.setAttribute(name, func);
-              }
-            }
-            else {
-              // Attribute value is not a single expression use as DOM-L0
-              node.setAttribute(name, processString(state, value, data));
-            }
-          }
-          else {
-            node.removeAttribute(name);
-            // Remove '_' prefix of attribute names so the DOM won't try
-            // to use them before we've processed the template
-            if (name.charAt(0) === '_') {
-              name = name.substring(1);
-            }
-
-            // Async attributes can only work if the whole attribute is async
-            var replacement;
-            if (value.indexOf('${') === 0 &&
-                value.charAt(value.length - 1) === '}') {
-              replacement = envEval(state, value.slice(2, -1), data, value);
-              if (replacement && typeof replacement.then === 'function') {
-                node.setAttribute(name, '');
-                /* jshint loopfunc:true */
-                replacement.then(function(newValue) {
-                  node.setAttribute(name, newValue);
-                }).then(null, console.error);
-              }
-              else {
-                if (state.options.blankNullUndefined && replacement == null) {
-                  replacement = '';
-                }
-                node.setAttribute(name, replacement);
-              }
-            }
-            else {
-              node.setAttribute(name, processString(state, value, data));
-            }
-          }
-        }
-        finally {
-          state.stack.pop();
-        }
-      }
-    }
-
-    // Loop through our children calling processNode. First clone them, so the
-    // set of nodes that we visit will be unaffected by additions or removals.
-    var childNodes = Array.prototype.slice.call(node.childNodes);
-    for (var j = 0; j < childNodes.length; j++) {
-      processNode(state, childNodes[j], data);
-    }
-
-    if (node.nodeType === 3 /*Node.TEXT_NODE*/) {
-      processTextNode(state, node, data);
-    }
-  }
-  finally {
-    if (pushedNode) {
-      data.__element = state.nodes.pop();
-    }
-    state.stack.pop();
-  }
-}
-
-/**
- * Handle attribute values where the output can only be a string
- */
-function processString(state, value, data) {
-  return value.replace(TEMPLATE_REGION, function(path) {
-    var insert = envEval(state, path.slice(2, -1), data, value);
-    return state.options.blankNullUndefined && insert == null ? '' : insert;
-  });
-}
-
-/**
- * Handle <x if="${...}">
- * @param node An element with an 'if' attribute
- * @param data The data to use with envEval()
- * @returns true if processing should continue, false otherwise
- */
-function processIf(state, node, data) {
-  state.stack.push('if');
-  try {
-    var originalValue = node.getAttribute('if');
-    var value = stripBraces(state, originalValue);
-    var recurse = true;
-    try {
-      var reply = envEval(state, value, data, originalValue);
-      recurse = !!reply;
-    }
-    catch (ex) {
-      handleError(state, 'Error with \'' + value + '\'', ex);
-      recurse = false;
-    }
-    if (!recurse) {
-      node.parentNode.removeChild(node);
-    }
-    node.removeAttribute('if');
-    return recurse;
-  }
-  finally {
-    state.stack.pop();
-  }
-}
-
-/**
- * Handle <x foreach="param in ${array}"> and the special case of
- * <loop foreach="param in ${array}">.
- * This function is responsible for extracting what it has to do from the
- * attributes, and getting the data to work on (including resolving promises
- * in getting the array). It delegates to processForEachLoop to actually
- * unroll the data.
- * @param node An element with a 'foreach' attribute
- * @param data The data to use with envEval()
- */
-function processForEach(state, node, data) {
-  state.stack.push('foreach');
-  try {
-    var originalValue = node.getAttribute('foreach');
-    var value = originalValue;
+'use strict';
 
-    var paramName = 'param';
-    if (value.charAt(0) === '$') {
-      // No custom loop variable name. Use the default: 'param'
-      value = stripBraces(state, value);
-    }
-    else {
-      // Extract the loop variable name from 'NAME in ${ARRAY}'
-      var nameArr = value.split(' in ');
-      paramName = nameArr[0].trim();
-      value = stripBraces(state, nameArr[1].trim());
-    }
-    node.removeAttribute('foreach');
-    try {
-      var evaled = envEval(state, value, data, originalValue);
-      var cState = cloneState(state);
-      handleAsync(evaled, node, function(reply, siblingNode) {
-        processForEachLoop(cState, reply, node, siblingNode, data, paramName);
-      });
-      node.parentNode.removeChild(node);
-    }
-    catch (ex) {
-      handleError(state, 'Error with \'' + value + '\'', ex);
-    }
-  }
-  finally {
-    state.stack.pop();
-  }
-}
-
-/**
- * Called by processForEach to handle looping over the data in a foreach loop.
- * This works with both arrays and objects.
- * Calls processForEachMember() for each member of 'set'
- * @param set The object containing the data to loop over
- * @param templNode The node to copy for each set member
- * @param sibling The sibling node to which we add things
- * @param data the data to use for node processing
- * @param paramName foreach loops have a name for the parameter currently being
- * processed. The default is 'param'. e.g. <loop foreach="param in ${x}">...
- */
-function processForEachLoop(state, set, templNode, sibling, data, paramName) {
-  if (Array.isArray(set)) {
-    set.forEach(function(member, i) {
-      processForEachMember(state, member, templNode, sibling,
-                           data, paramName, '' + i);
-    });
-  }
-  else {
-    for (var member in set) {
-      if (set.hasOwnProperty(member)) {
-        processForEachMember(state, member, templNode, sibling,
-                             data, paramName, member);
-      }
-    }
-  }
-}
-
-/**
- * Called by processForEachLoop() to resolve any promises in the array (the
- * array itself can also be a promise, but that is resolved by
- * processForEach()). Handle <LOOP> elements (which are taken out of the DOM),
- * clone the template node, and pass the processing on to processNode().
- * @param member The data item to use in templating
- * @param templNode The node to copy for each set member
- * @param siblingNode The parent node to which we add things
- * @param data the data to use for node processing
- * @param paramName The name given to 'member' by the foreach attribute
- * @param frame A name to push on the stack for debugging
- */
-function processForEachMember(state, member, templNode, siblingNode, data, paramName, frame) {
-  state.stack.push(frame);
-  try {
-    var cState = cloneState(state);
-    handleAsync(member, siblingNode, function(reply, node) {
-      data[paramName] = reply;
-      if (node.parentNode != null) {
-        var clone;
-        if (templNode.nodeName.toLowerCase() === 'loop') {
-          for (var i = 0; i < templNode.childNodes.length; i++) {
-            clone = templNode.childNodes[i].cloneNode(true);
-            node.parentNode.insertBefore(clone, node);
-            processNode(cState, clone, data);
-          }
-        }
-        else {
-          clone = templNode.cloneNode(true);
-          clone.removeAttribute('foreach');
-          node.parentNode.insertBefore(clone, node);
-          processNode(cState, clone, data);
-        }
-      }
-      delete data[paramName];
-    });
-  }
-  finally {
-    state.stack.pop();
-  }
-}
-
-/**
- * Take a text node and replace it with another text node with the ${...}
- * sections parsed out. We replace the node by altering node.parentNode but
- * we could probably use a DOM Text API to achieve the same thing.
- * @param node The Text node to work on
- * @param data The data to use in calls to envEval()
- */
-function processTextNode(state, node, data) {
-  // Replace references in other attributes
-  var value = node.data;
-  // We can't use the string.replace() with function trick (see generic
-  // attribute processing in processNode()) because we need to support
-  // functions that return DOM nodes, so we can't have the conversion to a
-  // string.
-  // Instead we process the string as an array of parts. In order to split
-  // the string up, we first replace '${' with '\uF001$' and '}' with '\uF002'
-  // We can then split using \uF001 or \uF002 to get an array of strings
-  // where scripts are prefixed with $.
-  // \uF001 and \uF002 are just unicode chars reserved for private use.
-  value = value.replace(TEMPLATE_REGION, '\uF001$$$1\uF002');
-  // Split a string using the unicode chars F001 and F002.
-  var parts = value.split(/\uF001|\uF002/);
-  if (parts.length > 1) {
-    parts.forEach(function(part) {
-      if (part === null || part === undefined || part === '') {
-        return;
-      }
-      if (part.charAt(0) === '$') {
-        part = envEval(state, part.slice(1), data, node.data);
-      }
-      var cState = cloneState(state);
-      handleAsync(part, node, function(reply, siblingNode) {
-        var doc = siblingNode.ownerDocument;
-        if (reply == null) {
-          reply = cState.options.blankNullUndefined ? '' : '' + reply;
-        }
-        if (typeof reply.cloneNode === 'function') {
-          // i.e. if (reply instanceof Element) { ...
-          reply = maybeImportNode(cState, reply, doc);
-          siblingNode.parentNode.insertBefore(reply, siblingNode);
-        }
-        else if (typeof reply.item === 'function' && reply.length) {
-          // NodeLists can be live, in which case maybeImportNode can
-          // remove them from the document, and thus the NodeList, which in
-          // turn breaks iteration. So first we clone the list
-          var list = Array.prototype.slice.call(reply, 0);
-          list.forEach(function(child) {
-            var imported = maybeImportNode(cState, child, doc);
-            siblingNode.parentNode.insertBefore(imported, siblingNode);
-          });
-        }
-        else {
-          // if thing isn't a DOM element then wrap its string value in one
-          reply = doc.createTextNode(reply.toString());
-          siblingNode.parentNode.insertBefore(reply, siblingNode);
-        }
-      });
-    });
-    node.parentNode.removeChild(node);
-  }
-}
-
-/**
- * Return node or a import of node, if it's not in the given document
- * @param node The node that we want to be properly owned
- * @param doc The document that the given node should belong to
- * @return A node that belongs to the given document
- */
-function maybeImportNode(state, node, doc) {
-  return node.ownerDocument === doc ? node : doc.importNode(node, true);
-}
-
-/**
- * A function to handle the fact that some nodes can be promises, so we check
- * and resolve if needed using a marker node to keep our place before calling
- * an inserter function.
- * @param thing The object which could be real data or a promise of real data
- * we use it directly if it's not a promise, or resolve it if it is.
- * @param siblingNode The element before which we insert new elements.
- * @param inserter The function to to the insertion. If thing is not a promise
- * then handleAsync() is just 'inserter(thing, siblingNode)'
- */
-function handleAsync(thing, siblingNode, inserter) {
-  if (thing != null && typeof thing.then === 'function') {
-    // Placeholder element to be replaced once we have the real data
-    var tempNode = siblingNode.ownerDocument.createElement('span');
-    siblingNode.parentNode.insertBefore(tempNode, siblingNode);
-    thing.then(function(delayed) {
-      inserter(delayed, tempNode);
-      if (tempNode.parentNode != null) {
-        tempNode.parentNode.removeChild(tempNode);
-      }
-    }).then(null, function(error) {
-      console.error(error.stack);
-    });
-  }
-  else {
-    inserter(thing, siblingNode);
-  }
-}
-
-/**
- * Warn of string does not begin '${' and end '}'
- * @param str the string to check.
- * @return The string stripped of ${ and }, or untouched if it does not match
- */
-function stripBraces(state, str) {
-  if (!str.match(TEMPLATE_REGION)) {
-    handleError(state, 'Expected ' + str + ' to match ${...}');
-    return str;
-  }
-  return str.slice(2, -1);
-}
-
-/**
- * Combined getter and setter that works with a path through some data set.
- * For example:
- * <ul>
- * <li>property(state, 'a.b', { a: { b: 99 }}); // returns 99
- * <li>property(state, 'a', { a: { b: 99 }}); // returns { b: 99 }
- * <li>property(state, 'a', { a: { b: 99 }}, 42); // returns 99 and alters the
- * input data to be { a: { b: 42 }}
- * </ul>
- * @param path An array of strings indicating the path through the data, or
- * a string to be cut into an array using <tt>split('.')</tt>
- * @param data the data to use for node processing
- * @param newValue (optional) If defined, this value will replace the
- * original value for the data at the path specified.
- * @return The value pointed to by <tt>path</tt> before any
- * <tt>newValue</tt> is applied.
- */
-function property(state, path, data, newValue) {
-  try {
-    if (typeof path === 'string') {
-      path = path.split('.');
-    }
-    var value = data[path[0]];
-    if (path.length === 1) {
-      if (newValue !== undefined) {
-        data[path[0]] = newValue;
-      }
-      if (typeof value === 'function') {
-        return value.bind(data);
-      }
-      return value;
-    }
-    if (!value) {
-      handleError(state, '"' + path[0] + '" is undefined');
-      return null;
-    }
-    return property(state, path.slice(1), value, newValue);
-  }
-  catch (ex) {
-    handleError(state, 'Path error with \'' + path + '\'', ex);
-    return '${' + path + '}';
-  }
-}
-
-/**
- * Like eval, but that creates a context of the variables in <tt>env</tt> in
- * which the script is evaluated.
- * WARNING: This script uses 'with' which is generally regarded to be evil.
- * The alternative is to create a Function at runtime that takes X parameters
- * according to the X keys in the env object, and then call that function using
- * the values in the env object. This is likely to be slow, but workable.
- * @param script The string to be evaluated.
- * @param data The environment in which to eval the script.
- * @param frame Optional debugging string in case of failure.
- * @return The return value of the script, or the error message if the script
- * execution failed.
- */
-function envEval(state, script, data, frame) {
-  try {
-    state.stack.push(frame.replace(/\s+/g, ' '));
-    // Detect if a script is capable of being interpreted using property()
-    if (/^[_a-zA-Z0-9.]*$/.test(script)) {
-      return property(state, script, data);
-    }
-    else {
-      if (!state.options.allowEval) {
-        handleError(state, 'allowEval is not set, however \'' + script + '\'' +
-            ' can not be resolved using a simple property path.');
-        return '${' + script + '}';
-      }
-      /* jshint -W085 */
-      with (data) {
-        return eval(script);
-      }
-    }
-  }
-  catch (ex) {
-    handleError(state, 'Template error evaluating \'' + script + '\'', ex);
-    return '${' + script + '}';
-  }
-  finally {
-    state.stack.pop();
-  }
-}
-
-/**
- * A generic way of reporting errors, for easy overloading in different
- * environments.
- * @param message the error message to report.
- * @param ex optional associated exception.
- */
-function handleError(state, message, ex) {
-  logError(message + ' (In: ' + state.stack.join(' > ') + ')');
-  if (ex) {
-    logError(ex);
-  }
-}
-
-/**
- * A generic way of reporting errors, for easy overloading in different
- * environments.
- * @param message the error message to report.
- */
-function logError(message) {
-  console.error(message);
-}
+var Cu = require('chrome').Cu;
+var template = Cu.import('resource://gre/modules/devtools/Templater.jsm', {}).template;
+exports.template = template;
--- a/toolkit/devtools/gcli/source/lib/gcli/util/filesystem.js
+++ b/toolkit/devtools/gcli/source/lib/gcli/util/filesystem.js
@@ -11,120 +11,115 @@
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 'use strict';
 
-var fs = require('fs');
-var path = require('path');
+var Cu = require('chrome').Cu;
+var Cc = require('chrome').Cc;
+var Ci = require('chrome').Ci;
 
+var OS = Cu.import('resource://gre/modules/osfile.jsm', {}).OS;
 var promise = require('./promise');
-var util = require('./util');
 
 /**
- * A set of functions defining a filesystem API for fileparser.js
+ * A set of functions that don't really belong in 'fs' (because they're not
+ * really universal in scope) but also kind of do (because they're not specific
+ * to GCLI
  */
 
-exports.join = path.join.bind(path);
-exports.dirname = path.dirname.bind(path);
-exports.sep = path.sep;
-exports.home = process.env.HOME;
+exports.join = OS.Path.join;
+exports.sep = OS.Path.sep;
+exports.dirname = OS.Path.dirname;
+
+var dirService = Cc['@mozilla.org/file/directory_service;1']
+                           .getService(Ci.nsIProperties);
+exports.home = dirService.get('Home', Ci.nsIFile).path;
+
+if ('winGetDrive' in OS.Path) {
+  exports.sep = '\\';
+}
+else {
+  exports.sep = '/';
+}
 
 /**
- * The NodeJS docs suggest using ``pathname.split(path.sep)`` to cut a path
- * into a number of components. But this doesn't take into account things like
- * path normalization and removing the initial (or trailing) blanks from
- * absolute (or / terminated) paths.
- * http://www.nodejs.org/api/path.html#path_path_sep
+ * Split a path into its components.
  * @param pathname (string) The part to cut up
  * @return An array of path components
  */
 exports.split = function(pathname) {
-  pathname = path.normalize(pathname);
-  var parts = pathname.split(exports.sep);
-  return parts.filter(function(part) {
-    return part !== '';
-  });
+  return OS.Path.split(pathname).components;
 };
 
 /**
  * @param pathname string, path of an existing directory
  * @param matches optional regular expression - filter output to include only
  * the files that match the regular expression. The regexp is applied to the
  * filename only not to the full path
  * @return A promise of an array of stat objects for each member of the
  * directory pointed to by ``pathname``, each containing 2 extra properties:
  * - pathname: The full pathname of the file
  * - filename: The final filename part of the pathname
  */
 exports.ls = function(pathname, matches) {
-  var deferred = promise.defer();
+  var iterator = new OS.File.DirectoryIterator(pathname);
+  var entries = [];
 
-  fs.readdir(pathname, function(err, files) {
-    if (err) {
-      deferred.reject(err);
-    }
-    else {
-      if (matches) {
-        files = files.filter(function(file) {
-          return matches.test(file);
-        });
-      }
-
-      var statsPromise = util.promiseEach(files, function(filename) {
-        var filepath = path.join(pathname, filename);
-        return exports.stat(filepath).then(function(stats) {
-          stats.filename = filename;
-          stats.pathname = filepath;
-          return stats;
-        });
-      });
-
-      statsPromise.then(deferred.resolve, deferred.reject);
-    }
+  var iteratePromise = iterator.forEach(function(entry) {
+    entries.push({
+      exists: true,
+      isDir: entry.isDir,
+      isFile: !entry.isFile,
+      filename: entry.name,
+      pathname: entry.path
+    });
   });
 
-  return deferred.promise;
+  return iteratePromise.then(function onSuccess() {
+      iterator.close();
+      return entries;
+    },
+    function onFailure(reason) {
+      iterator.close();
+      throw reason;
+    }
+  );
 };
 
 /**
  * stat() is annoying because it considers stat('/doesnt/exist') to be an
  * error, when the point of stat() is to *find* *out*. So this wrapper just
  * converts 'ENOENT' i.e. doesn't exist to { exists:false } and adds
  * exists:true to stat blocks from existing paths
  */
 exports.stat = function(pathname) {
-  var deferred = promise.defer();
+  var onResolve = function(stats) {
+    return {
+      exists: true,
+      isDir: stats.isDir,
+      isFile: !stats.isFile
+    };
+  };
 
-  fs.stat(pathname, function(err, stats) {
-    if (err) {
-      if (err.code === 'ENOENT') {
-        deferred.resolve({
-          exists: false,
-          isDir: false,
-          isFile: false
-        });
-      }
-      else {
-        deferred.reject(err);
-      }
+  var onReject = function(err) {
+    if (err instanceof OS.File.Error && err.becauseNoSuchFile) {
+      return {
+        exists: false,
+        isDir: false,
+        isFile: false
+      };
     }
-    else {
-      deferred.resolve({
-        exists: true,
-        isDir: stats.isDirectory(),
-        isFile: stats.isFile()
-      });
-    }
-  });
+    throw err;
+  };
 
-  return deferred.promise;
+  return OS.File.stat(pathname).then(onResolve, onReject);
 };
 
 /**
  * We may read the first line of a file to describe it?
  * Right now, however, we do nothing.
  */
 exports.describe = function(pathname) {
   return promise.resolve('');
--- a/toolkit/devtools/gcli/source/lib/gcli/util/host.js
+++ b/toolkit/devtools/gcli/source/lib/gcli/util/host.js
@@ -11,29 +11,25 @@
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 'use strict';
 
-// Warning - gcli.js causes this version of host.js to be favored in NodeJS
-// which means that it's also used in testing in NodeJS
+var Cu = require('chrome').Cu;
+var Cc = require('chrome').Cc;
+var Ci = require('chrome').Ci;
 
-var childProcess = require('child_process');
-var fs = require('fs');
-var path = require('path');
+var OS = Cu.import('resource://gre/modules/osfile.jsm', {}).OS;
 
 var promise = require('./promise');
 var util = require('./util');
 
-var ATTR_NAME = '__gcli_border';
-var HIGHLIGHT_STYLE = '1px dashed black';
-
 function Highlighter(document) {
   this._document = document;
   this._nodes = util.createEmptyNodeList(this._document);
 }
 
 Object.defineProperty(Highlighter.prototype, 'nodelist', {
   set: function(nodes) {
     Array.prototype.forEach.call(this._nodes, this._unhighlightNode, this);
@@ -48,111 +44,166 @@ Object.defineProperty(Highlighter.protot
   enumerable: true
 });
 
 Highlighter.prototype.destroy = function() {
   this.nodelist = null;
 };
 
 Highlighter.prototype._highlightNode = function(node) {
-  if (node.hasAttribute(ATTR_NAME)) {
-    return;
-  }
-
-  var styles = this._document.defaultView.getComputedStyle(node);
-  node.setAttribute(ATTR_NAME, styles.border);
-  node.style.border = HIGHLIGHT_STYLE;
+  // Enable when the highlighter rewrite is done
 };
 
 Highlighter.prototype._unhighlightNode = function(node) {
-  var previous = node.getAttribute(ATTR_NAME);
-  node.style.border = previous;
-  node.removeAttribute(ATTR_NAME);
+  // Enable when the highlighter rewrite is done
 };
 
 exports.Highlighter = Highlighter;
 
 /**
  * See docs in lib/gcli/util/host.js:exec
  */
 exports.exec = function(execSpec) {
-  var deferred = promise.defer();
-
-  var output = { data: '' };
-  var child = childProcess.spawn(execSpec.cmd, execSpec.args, {
-    env: execSpec.env,
-    cwd: execSpec.cwd
-  });
-
-  child.stdout.on('data', function(data) {
-    output.data += data;
-  });
-
-  child.stderr.on('data', function(data) {
-    output.data += data;
-  });
-
-  child.on('close', function(code) {
-    output.code = code;
-    if (code === 0) {
-      deferred.resolve(output);
-    }
-    else {
-      deferred.reject(output);
-    }
-  });
-
-  return deferred.promise;
+  throw new Error('Not supported');
 };
 
 /**
  * Asynchronously load a text resource
- * @param requistingModule Typically just 'module' to pick up the 'module'
- * variable from the calling modules scope
- * @param name The name of the resource to load, as a path (including extension)
- * relative to that of the requiring module
- * @return A promise of the contents of the file as a string
+ * @see lib/gcli/util/host.js
  */
 exports.staticRequire = function(requistingModule, name) {
   var deferred = promise.defer();
 
-  var parent = path.dirname(requistingModule.id);
-  var filename = parent + '/' + name;
+  if (name.match(/\.css$/)) {
+    deferred.resolve('');
+  }
+  else {
+    var filename = OS.Path.dirname(requistingModule.id) + '/' + name;
+    filename = filename.replace(/\/\.\//g, '/');
+    filename = 'resource://gre/modules/devtools/' + filename;
+
+    var xhr = Cc['@mozilla.org/xmlextras/xmlhttprequest;1']
+                .createInstance(Ci.nsIXMLHttpRequest);
 
-  fs.readFile(filename, { encoding: 'utf8' }, function(err, data) {
-    if (err) {
+    xhr.onload = function onload() {
+      deferred.resolve(xhr.responseText);
+    }.bind(this);
+
+    xhr.onabort = xhr.onerror = xhr.ontimeout = function(err) {
       deferred.reject(err);
+    }.bind(this);
+
+    try {
+      xhr.open('GET', filename);
+      xhr.send();
     }
-
-    deferred.resolve(data);
-  });
+    catch (ex) {
+      deferred.reject(ex);
+    }
+  }
 
   return deferred.promise;
 };
 
 /**
  * A group of functions to help scripting. Small enough that it doesn't need
  * a separate module (it's basically a wrapper around 'eval' in some contexts)
  */
-exports.script = {
-  onOutput: util.createEvent('Script.onOutput'),
+var client;
+var target;
+var consoleActor;
+var webConsoleClient;
+
+exports.script = { };
+
+exports.script.onOutput = util.createEvent('Script.onOutput');
+
+/**
+ * Setup the environment to eval JavaScript
+ */
+exports.script.useTarget = function(tgt) {
+  target = tgt;
+
+  // Local debugging needs to make the target remote.
+  var targetPromise = target.isRemote ?
+                      promise.resolve(target) :
+                      target.makeRemote();
 
-  // Setup the environment to eval JavaScript, a no-op on the web
-  useTarget: function(tgt) { },
+  return targetPromise.then(function() {
+    var deferred = promise.defer();
+
+    client = target._client;
+
+    client.addListener('pageError', function(packet) {
+      if (packet.from === consoleActor) {
+        // console.log('pageError', packet.pageError);
+        exports.script.onOutput({
+          level: 'exception',
+          message: packet.exception.class
+        });
+      }
+    });
+
+    client.addListener('consoleAPICall', function(type, packet) {
+      if (packet.from === consoleActor) {
+        var data = packet.message;
+
+        var ev = {
+          level: data.level,
+          arguments: data.arguments,
+        };
 
-  // Execute some JavaScript
-  eval: function(javascript) {
-    try {
-      return promise.resolve({
-        input: javascript,
-        output: eval(javascript),
-        exception: null
-      });
+        if (data.filename !== 'debugger eval code') {
+          ev.source = {
+            filename: data.filename,
+            lineNumber: data.lineNumber,
+            functionName: data.functionName
+          };
+        }
+
+        exports.script.onOutput(ev);
+      }
+    });
+
+    consoleActor = target._form.consoleActor;
+
+    var onAttach = function(response, wcc) {
+      webConsoleClient = wcc;
+
+      if (response.error != null) {
+        deferred.reject(response);
+      }
+      else {
+        deferred.resolve(response);
+      }
+
+      // TODO: add _onTabNavigated code?
+    };
+
+    var listeners = [ 'PageError', 'ConsoleAPI' ];
+    client.attachConsole(consoleActor, listeners, onAttach);
+
+    return deferred.promise;
+  });
+};
+
+/**
+ * Execute some JavaScript
+ */
+exports.script.eval = function(javascript) {
+  var deferred = promise.defer();
+
+  var onResult = function(response) {
+    var output = response.result;
+    if (typeof output === 'object' && output.type === 'undefined') {
+      output = undefined;
     }
-    catch (ex) {
-      return promise.resolve({
-        input: javascript,
-        output: null,
-        exception: ex
-      });
-    }
-  }
+
+    deferred.resolve({
+      input: response.input,
+      output: output,
+      exception: response.exception
+    });
+  };
+
+  webConsoleClient.evaluateJS(javascript, onResult, {});
+  return deferred.promise;
 };
--- a/toolkit/devtools/gcli/source/lib/gcli/util/l10n.js
+++ b/toolkit/devtools/gcli/source/lib/gcli/util/l10n.js
@@ -11,668 +11,79 @@
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 'use strict';
 
-var strings = {};
+var Cu = require('chrome').Cu;
+
+var XPCOMUtils = Cu.import('resource://gre/modules/XPCOMUtils.jsm', {}).XPCOMUtils;
+var Services = Cu.import('resource://gre/modules/Services.jsm', {}).Services;
 
-/**
- * Add a CommonJS module to the list of places in which we look for
- * localizations. Before calling this function, it's important to make a call
- * to require(modulePath) to ensure that the dependency system (either require
- * or dryice) knows to make the module ready.
- * @param modulePath A CommonJS module (as used in calls to require). Don't
- * add the 'i18n!' prefix used by requirejs.
- * @see unregisterStringsSource()
+var imports = {};
+XPCOMUtils.defineLazyGetter(imports, 'stringBundle', function () {
+  return Services.strings.createBundle('chrome://browser/locale/devtools/gcli.properties');
+});
+
+/*
+ * Not supported when embedded - we're doing things the Mozilla way not the
+ * require.js way.
  */
 exports.registerStringsSource = function(modulePath) {
-  //  Bug 683844: Should be require('i18n!' + module);
-  var additions = require(modulePath).root;
-  Object.keys(additions).forEach(function(key) {
-    if (strings[key]) {
-      console.error('Key \'' + key + '\' (loaded from ' + modulePath + ') ' +
-          'already exists. Ignoring.');
-      return;
-    }
-    strings[key] = additions[key];
-  }, this);
-};
-
-/**
- * The main GCLI strings source is always required.
- * We have to load it early on in the process (in the require phase) so that
- * we can define settingSpecs and commandSpecs at the top level too.
- */
-require('../nls/strings');
-exports.registerStringsSource('../nls/strings');
-
-/**
- * Undo the effects of registerStringsSource().
- * @param modulePath A CommonJS module (as used in calls to require).
- * @see registerStringsSource()
- */
-exports.unregisterStringsSource = function(modulePath) {
-  //  Bug 683844: Should be require('i18n!' + module);
-  var additions = require(modulePath).root;
-  Object.keys(additions).forEach(function(key) {
-    delete strings[key];
-  }, this);
-};
-
-/**
- * Finds the preferred locales of the user as an array of RFC 4646 strings
- * (e.g. 'pt-br').
- * . There is considerable confusion as to the correct value
- * since there are a number of places the information can be stored:
- * - In the OS (IE:navigator.userLanguage, IE:navigator.systemLanguage)
- * - In the browser (navigator.language, IE:navigator.browserLanguage)
- * - By GEO-IP
- * - By website specific settings
- * This implementation uses navigator.language || navigator.userLanguage as
- * this is compatible with requirejs.
- * See http://tools.ietf.org/html/rfc4646
- * See http://stackoverflow.com/questions/1043339/javascript-for-detecting-browser-language-preference
- * See http://msdn.microsoft.com/en-us/library/ms534713.aspx
- * @return The current locale as an RFC 4646 string
- */
-exports.getPreferredLocales = function() {
-  var language = typeof navigator !== 'undefined' ?
-      (navigator.language || navigator.userLanguage).toLowerCase() :
-      'en-us';
-  var parts = language.split('-');
-  var reply = parts.map(function(part, index) {
-    return parts.slice(0, parts.length - index).join('-');
-  });
-  reply.push('root');
-  return reply;
-};
-
-/**
- * Lookup a key in our strings file using localized versions if possible,
- * throwing an error if that string does not exist.
- * @param key The string to lookup
- * This should generally be in the general form 'filenameExportIssue' where
- * filename is the name of the module (all lowercase without underscores) and
- * export is the name of a top level thing in which the message is used and
- * issue is a short string indicating the issue.
- * The point of a 'standard' like this is to keep strings fairly short whilst
- * still allowing users to have an idea where they come from, and preventing
- * name clashes.
- * @return The string resolved from the correct locale
- */
-exports.lookup = function(key) {
-  var str = strings[key];
-  if (str == null) {
-    throw new Error('No i18n key: ' + key);
-  }
-  return str;
+  throw new Error('registerStringsSource is not available in mozilla');
 };
 
-/**
- * An alternative to lookup().
- * <tt>l10n.lookup('x') === l10n.propertyLookup.x</tt>
- * We should go easy on this method until we are sure that we don't have too
- * many 'old-browser' problems. However this works particularly well with the
- * templater because you can pass this in to a template that does not do
- * <tt>{ allowEval: true }</tt>
- */
-if (typeof Proxy !== 'undefined') {
-  exports.propertyLookup = Proxy.create({
-    get: function(rcvr, name) {
-      return exports.lookup(name);
-    }
-  });
-}
-else {
-  exports.propertyLookup = strings;
-}
-
-/**
- * Helper function to process swaps.
- * For example:
- *   swap('the {subject} {verb} {preposition} the {object}', {
- *     subject: 'cat', verb: 'sat', preposition: 'on', object: 'mat'
- *   });
- * Returns 'the cat sat on the mat'.
- * @param str The string containing parts delimited by { and } to be replaced
- * @param swaps Lookup map containing the replacement strings
- */
-function swap(str, swaps) {
-  return str.replace(/\{[^}]*\}/g, function(name) {
-    name = name.slice(1, -1);
-    if (swaps == null) {
-      console.log('Missing swaps while looking up \'' + name + '\'');
-      return '';
-    }
-    var replacement = swaps[name];
-    if (replacement == null) {
-      console.log('Can\'t find \'' + name + '\' in ' + JSON.stringify(swaps));
-      replacement = '';
-    }
-    return replacement;
-  });
-}
-
-/**
- * Lookup a key in our strings file using localized versions if possible,
- * and perform string interpolation to inject runtime values into the string.
- * l10n lookup is required for user visible strings, but not required for
- * console messages and throw strings.
- * lookupSwap() is virtually identical in function to lookupFormat(), except
- * that lookupSwap() is easier to use, however lookupFormat() is required if
- * your code is to work with Mozilla's i10n system.
- * @param key The string to lookup
- * This should generally be in the general form 'filename_export_issue' where
- * filename is the name of the module (all lowercase without underscores) and
- * export is the name of a top level thing in which the message is used and
- * issue is a short string indicating the issue.
- * The point of a 'standard' like this is to keep strings fairly short whilst
- * still allowing users to have an idea where they come from, and preventing
- * name clashes.
- * The value looked up may contain {variables} to be exchanged using swaps
- * @param swaps A map of variable values to be swapped.
- * @return A looked-up and interpolated message for display to the user.
- * @see lookupFormat()
- */
-exports.lookupSwap = function(key, swaps) {
-  var str = exports.lookup(key);
-  return swap(str, swaps);
+exports.unregisterStringsSource = function(modulePath) {
+  throw new Error('unregisterStringsSource is not available in mozilla');
 };
 
-/**
- * Perform the string swapping required by format().
- * @see format() for details of the swaps performed.
- */
-function format(str, swaps) {
-  // First replace the %S strings
-  var index = 0;
-  str = str.replace(/%S/g, function() {
-    return swaps[index++];
-  });
-  // Then %n$S style strings
-  str = str.replace(/%([0-9])\$S/g, function(match, idx) {
-    return swaps[idx - 1];
-  });
-  return str;
-}
-
-/**
- * Lookup a key in our strings file using localized versions if possible,
- * and perform string interpolation to inject runtime values into the string.
- * l10n lookup is required for user visible strings, but not required for
- * console messages and throw strings.
- * lookupFormat() is virtually identical in function to lookupSwap(), except
- * that lookupFormat() works with strings held in the mozilla repo in addition
- * to files held outside.
- * @param key Looks up the format string for the given key in the string bundle
- * and returns a formatted copy where each occurrence of %S (uppercase) is
- * replaced by each successive element in the supplied array.
- * Alternatively, numbered indices of the format %n$S (e.g. %1$S, %2$S, etc.)
- * can be used to specify the position of the corresponding parameter
- * explicitly.
- * The mozilla version performs more advances formatting than these simple
- * cases, however these cases are not supported so far, mostly because they are
- * not well documented.
- * @param swaps An array of strings to be swapped.
- * @return A looked-up and interpolated message for display to the user.
- * @see https://developer.mozilla.org/en/XUL/Method/getFormattedString
- */
-exports.lookupFormat = function(key, swaps) {
-  var str = exports.lookup(key);
-  return format(str, swaps);
+exports.lookupSwap = function(key, swaps) {
+  throw new Error('lookupSwap is not available in mozilla');
 };
 
-/**
- * Lookup the correct pluralization of a word/string.
- * The first ``key`` and ``swaps`` parameters of lookupPlural() are the same
- * as for lookupSwap(), however there is an extra ``ord`` parameter which indicates
- * the plural ordinal to use.
- * For example, in looking up the string '39 steps', the ordinal would be 39.
- *
- * More detailed example:
- * French has 2 plural forms: the first for 0 and 1, the second for everything
- * else. English also has 2, but the first only covers 1. Zero is lumped into
- * the 'everything else' category. Vietnamese has only 1 plural form - so it
- * uses the same noun form however many of them there are.
- * The following localization strings describe how to pluralize the phrase
- * '1 minute':
- *   'en-us': { demo_plural_time: [ '{ord} minute', '{ord} minutes' ] },
- *   'fr-fr': { demo_plural_time: [ '{ord} minute', '{ord} minutes' ] },
- *   'vi-vn': { demo_plural_time: [ '{ord} phut' ] },
- *
- *   l10n.lookupPlural('demo_plural_time', 0); // '0 minutes' in 'en-us'
- *   l10n.lookupPlural('demo_plural_time', 1); // '1 minute' in 'en-us'
- *   l10n.lookupPlural('demo_plural_time', 9); // '9 minutes' in 'en-us'
- *
- *   l10n.lookupPlural('demo_plural_time', 0); // '0 minute' in 'fr-fr'
- *   l10n.lookupPlural('demo_plural_time', 1); // '1 minute' in 'fr-fr'
- *   l10n.lookupPlural('demo_plural_time', 9); // '9 minutes' in 'fr-fr'
- *
- *   l10n.lookupPlural('demo_plural_time', 0); // '0 phut' in 'vi-vn'
- *   l10n.lookupPlural('demo_plural_time', 1); // '1 phut' in 'vi-vn'
- *   l10n.lookupPlural('demo_plural_time', 9); // '9 phut' in 'vi-vn'
- *
- * The
- * Note that the localization strings are (correctly) the same (since both
- * the English and the French words have the same etymology)
- * @param key The string to lookup in gcli/nls/strings.js
- * @param ord The number to use in plural lookup
- * @param swaps A map of variable values to be swapped.
- */
 exports.lookupPlural = function(key, ord, swaps) {
-  var index = getPluralRule().get(ord);
-  var words = exports.lookup(key);
-  var str = words[index];
+  throw new Error('lookupPlural is not available in mozilla');
+};
 
-  swaps = swaps || {};
-  swaps.ord = ord;
-
-  return swap(str, swaps);
+exports.getPreferredLocales = function() {
+  return [ 'root' ];
 };
 
-/**
- * Find the correct plural rule for the current locale
- * @return a plural rule with a 'get()' function
- */
-function getPluralRule() {
-  if (!pluralRule) {
-    var lang = navigator.language || navigator.userLanguage;
-    // Convert lang to a rule index
-    pluralRules.some(function(rule) {
-      if (rule.locales.indexOf(lang) !== -1) {
-        pluralRule = rule;
-        return true;
-      }
-      return false;
-    });
-
-    // Use rule 0 by default, which is no plural forms at all
-    if (!pluralRule) {
-      console.error('Failed to find plural rule for ' + lang);
-      pluralRule = pluralRules[0];
-    }
-  }
-
-  return pluralRule;
-}
-
-/**
- * A plural form is a way to pluralize a noun. There are 2 simple plural forms
- * in English, with (s) and without - e.g. tree and trees. There are many other
- * ways to pluralize (e.g. witches, ladies, teeth, oxen, axes, data, alumini)
- * However they all follow the rule that 1 is 'singular' while everything
- * else is 'plural' (words without a plural form like sheep can be seen as
- * following this rule where the singular and plural forms are the same)
- * <p>Non-English languages have different pluralization rules, for example
- * French uses singular for 0 as well as 1. Japanese has no plurals while
- * Arabic and Russian are very complex.
- *
- * See https://developer.mozilla.org/en/Localization_and_Plurals
- * See https://secure.wikimedia.org/wikipedia/en/wiki/List_of_ISO_639-1_codes
- *
- * Contains code inspired by Mozilla L10n code originally developed by
- *     Edward Lee <edward.lee@engineering.uiuc.edu>
- */
-var pluralRules = [
-  /**
-   * Index 0 - Only one form for all
-   * Asian family: Japanese, Vietnamese, Korean
-   */
-  {
-    locales: [
-      'fa', 'fa-ir',
-      'id',
-      'ja', 'ja-jp-mac',
-      'ka',
-      'ko', 'ko-kr',
-      'th', 'th-th',
-      'tr', 'tr-tr',
-      'zh', 'zh-tw', 'zh-cn'
-    ],
-    numForms: 1,
-    get: function(n) {
-      return 0;
+/** @see lookup() in lib/gcli/util/l10n.js */
+exports.lookup = function(key) {
+  try {
+    // Our memory leak hunter walks reachable objects trying to work out what
+    // type of thing they are using object.constructor.name. If that causes
+    // problems then we can avoid the unknown-key-exception with the following:
+    /*
+    if (key === 'constructor') {
+      return { name: 'l10n-mem-leak-defeat' };
     }
-  },
-
-  /**
-   * Index 1 - Two forms, singular used for one only
-   * Germanic family: English, German, Dutch, Swedish, Danish, Norwegian,
-   *          Faroese
-   * Romanic family: Spanish, Portuguese, Italian, Bulgarian
-   * Latin/Greek family: Greek
-   * Finno-Ugric family: Finnish, Estonian
-   * Semitic family: Hebrew
-   * Artificial: Esperanto
-   * Finno-Ugric family: Hungarian
-   * Turkic/Altaic family: Turkish
-   */
-  {
-    locales: [
-      'af', 'af-za',
-      'as', 'ast',
-      'bg',
-      'br',
-      'bs', 'bs-ba',
-      'ca',
-      'cy', 'cy-gb',
-      'da',
-      'de', 'de-de', 'de-ch',
-      'en', 'en-gb', 'en-us', 'en-za',
-      'el', 'el-gr',
-      'eo',
-      'es', 'es-es', 'es-ar', 'es-cl', 'es-mx',
-      'et', 'et-ee',
-      'eu',
-      'fi', 'fi-fi',
-      'fy', 'fy-nl',
-      'gl', 'gl-gl',
-      'he',
-     //     'hi-in', Without an unqualified language, looks dodgy
-      'hu', 'hu-hu',
-      'hy', 'hy-am',
-      'it', 'it-it',
-      'kk',
-      'ku',
-      'lg',
-      'mai',
-     // 'mk', 'mk-mk', Should be 14?
-      'ml', 'ml-in',
-      'mn',
-      'nb', 'nb-no',
-      'no', 'no-no',
-      'nl',
-      'nn', 'nn-no',
-      'no', 'no-no',
-      'nb', 'nb-no',
-      'nso', 'nso-za',
-      'pa', 'pa-in',
-      'pt', 'pt-pt',
-      'rm', 'rm-ch',
-     // 'ro', 'ro-ro', Should be 5?
-      'si', 'si-lk',
-     // 'sl',      Should be 10?
-      'son', 'son-ml',
-      'sq', 'sq-al',
-      'sv', 'sv-se',
-      'vi', 'vi-vn',
-      'zu', 'zu-za'
-    ],
-    numForms: 2,
-    get: function(n) {
-      return n != 1 ?
-        1 :
-        0;
-    }
-  },
-
-  /**
-   * Index 2 - Two forms, singular used for zero and one
-   * Romanic family: Brazilian Portuguese, French
-   */
-  {
-    locales: [
-      'ak', 'ak-gh',
-      'bn', 'bn-in', 'bn-bd',
-      'fr', 'fr-fr',
-      'gu', 'gu-in',
-      'kn', 'kn-in',
-      'mr', 'mr-in',
-      'oc', 'oc-oc',
-      'or', 'or-in',
-            'pt-br',
-      'ta', 'ta-in', 'ta-lk',
-      'te', 'te-in'
-    ],
-    numForms: 2,
-    get: function(n) {
-      return n > 1 ?
-        1 :
-        0;
-    }
-  },
-
-  /**
-   * Index 3 - Three forms, special case for zero
-   * Latvian
-   */
-  {
-    locales: [ 'lv' ],
-    numForms: 3,
-    get: function(n) {
-      return n % 10 == 1 && n % 100 != 11 ?
-        1 :
-        n !== 0 ?
-          2 :
-          0;
-    }
-  },
-
-  /**
-   * Index 4 -
-   * Scottish Gaelic
-   */
-  {
-    locales: [ 'gd', 'gd-gb' ],
-    numForms: 4,
-    get: function(n) {
-      return n == 1 || n == 11 ?
-        0 :
-        n == 2 || n == 12 ?
-          1 :
-          n > 0 && n < 20 ?
-            2 :
-            3;
-    }
-  },
+    */
 
-  /**
-   * Index 5 - Three forms, special case for numbers ending in 00 or [2-9][0-9]
-   * Romanian
-   */
-  {
-    locales: [ 'ro', 'ro-ro' ],
-    numForms: 3,
-    get: function(n) {
-      return n == 1 ?
-        0 :
-        n === 0 || n % 100 > 0 && n % 100 < 20 ?
-          1 :
-          2;
-    }
-  },
-
-  /**
-   * Index 6 - Three forms, special case for numbers ending in 1[2-9]
-   * Lithuanian
-   */
-  {
-    locales: [ 'lt' ],
-    numForms: 3,
-    get: function(n) {
-      return n % 10 == 1 && n % 100 != 11 ?
-        0 :
-        n % 10 >= 2 && (n % 100 < 10 || n % 100 >= 20) ?
-          2 :
-          1;
-    }
-  },
-
-  /**
-   * Index 7 - Three forms, special cases for numbers ending in 1 and
-   *       2, 3, 4, except those ending in 1[1-4]
-   * Slavic family: Russian, Ukrainian, Serbian, Croatian
-   */
-  {
-    locales: [
-      'be', 'be-by',
-      'hr', 'hr-hr',
-      'ru', 'ru-ru',
-      'sr', 'sr-rs', 'sr-cs',
-      'uk'
-    ],
-    numForms: 3,
-    get: function(n) {
-      return n % 10 == 1 && n % 100 != 11 ?
-        0 :
-        n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 10 || n % 100 >= 20) ?
-          1 :
-          2;
-    }
-  },
-
-  /**
-   * Index 8 - Three forms, special cases for 1 and 2, 3, 4
-   * Slavic family: Czech, Slovak
-   */
-  {
-    locales: [ 'cs', 'sk' ],
-    numForms: 3,
-    get: function(n) {
-      return n == 1 ?
-        0 :
-        n >= 2 && n <= 4 ?
-          1 :
-          2;
-    }
-  },
-
-  /**
-   * Index 9 - Three forms, special case for one and some numbers ending in
-   *       2, 3, or 4
-   * Polish
-   */
-  {
-    locales: [ 'pl' ],
-    numForms: 3,
-    get: function(n) {
-      return n == 1 ?
-        0 :
-        n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 10 || n % 100 >= 20) ?
-          1 :
-          2;
-    }
-  },
+    return imports.stringBundle.GetStringFromName(key);
+  }
+  catch (ex) {
+    console.error('Failed to lookup ', key, ex);
+    return key;
+  }
+};
 
-  /**
-   * Index 10 - Four forms, special case for one and all numbers ending in
-   *      02, 03, or 04
-   * Slovenian
-   */
-  {
-    locales: [ 'sl' ],
-    numForms: 4,
-    get: function(n) {
-      return n % 100 == 1 ?
-        0 :
-        n % 100 == 2 ?
-          1 :
-          n % 100 == 3 || n % 100 == 4 ?
-            2 :
-            3;
-    }
-  },
-
-  /**
-   * Index 11 -
-   * Irish Gaeilge
-   */
-  {
-    locales: [ 'ga-ie', 'ga-ie', 'ga', 'en-ie' ],
-    numForms: 5,
-    get: function(n) {
-      return n == 1 ?
-        0 :
-        n == 2 ?
-          1 :
-          n >= 3 && n <= 6 ?
-            2 :
-            n >= 7 && n <= 10 ?
-              3 :
-              4;
-    }
-  },
+/** @see propertyLookup in lib/gcli/util/l10n.js */
+exports.propertyLookup = Proxy.create({
+  get: function(rcvr, name) {
+    return exports.lookup(name);
+  }
+});
 
-  /**
-   * Index 12 -
-   * Arabic
-   */
-  {
-    locales: [ 'ar' ],
-    numForms: 6,
-    get: function(n) {
-      return n === 0 ?
-        5 :
-        n == 1 ?
-          0 :
-          n == 2 ?
-            1 :
-            n % 100 >= 3 && n % 100 <= 10 ?
-              2 :
-              n % 100 >= 11 && n % 100 <= 99 ?
-                3 :
-                4;
-    }
-  },
-
-  /**
-   * Index 13 -
-   * Maltese
-   */
-  {
-    locales: [ 'mt' ],
-    numForms: 4,
-    get: function(n) {
-      return n == 1 ?
-        0 :
-        n === 0 || n % 100 > 0 && n % 100 <= 10 ?
-          1 :
-          n % 100 > 10 && n % 100 < 20 ?
-            2 :
-            3;
-    }
-  },
-
-  /**
-   * Index 14 -
-   * Macedonian
-   */
-  {
-    locales: [ 'mk', 'mk-mk' ],
-    numForms: 3,
-    get: function(n) {
-      return n % 10 == 1 ?
-        0 :
-        n % 10 == 2 ?
-          1 :
-          2;
-    }
-  },
-
-  /**
-   * Index 15 -
-   * Icelandic
-   */
-  {
-    locales: [ 'is' ],
-    numForms: 2,
-    get: function(n) {
-      return n % 10 == 1 && n % 100 != 11 ?
-        0 :
-        1;
-    }
+/** @see lookupFormat in lib/gcli/util/l10n.js */
+exports.lookupFormat = function(key, swaps) {
+  try {
+    return imports.stringBundle.formatStringFromName(key, swaps, swaps.length);
   }
-
-  /*
-  // Known locales without a known plural rule
-  'km', 'ms', 'ne-np', 'ne-np', 'ne', 'nr', 'nr-za', 'rw', 'ss', 'ss-za',
-  'st', 'st-za', 'tn', 'tn-za', 'ts', 'ts-za', 've', 've-za', 'xh', 'xh-za'
-  */
-];
-
-/**
- * The cached plural rule
- */
-var pluralRule;
+  catch (ex) {
+    console.error('Failed to format ', key, ex);
+    return key;
+  }
+};
--- a/toolkit/devtools/gcli/source/lib/gcli/util/promise.js
+++ b/toolkit/devtools/gcli/source/lib/gcli/util/promise.js
@@ -11,286 +11,11 @@
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 'use strict';
 
-/**
- * This is a copy of util.errorHandler to avoid dependency loops
- */
-var util = {
-  errorHandler: function(ex) {
-    if (ex instanceof Error) {
-      // V8 weirdly includes the exception message in the stack
-      if (ex.stack.indexOf(ex.message) !== -1) {
-        console.error(ex.stack);
-      }
-      else {
-        console.error('' + ex);
-        console.error(ex.stack);
-      }
-    }
-    else {
-      console.error(ex);
-    }
-  }
-};
-
-/**
- * Internal utility: Wraps given `value` into simplified promise, successfully
- * fulfilled to a given `value`. Note the result is not a complete promise
- * implementation, as its method `then` does not returns anything.
- */
-function fulfilled(value) {
-  return { then: function then(fulfill) { fulfill(value); } };
-}
-
-/**
- * Internal utility: Wraps given input into simplified promise, pre-rejected
- * with a given `reason`. Note the result is not a complete promise
- * implementation, as its method `then` does not returns anything.
- */
-function rejected(reason) {
-  return { then: function then(fulfill, reject) { reject(reason); } };
-}
-
-/**
- * Internal utility: Returns `true` if given `value` is a promise. Value is
- * assumed to be a promise if it implements method `then`.
- */
-function isPromise(value) {
-  return value && typeof(value.then) === 'function';
-}
-
-/**
- * Creates deferred object containing fresh promise & methods to either resolve
- * or reject it. The result is an object with the following properties:
- * - `promise` Eventual value representation implementing CommonJS [Promises/A]
- *   (http://wiki.commonjs.org/wiki/Promises/A) API.
- * - `resolve` Single shot function that resolves enclosed `promise` with a
- *   given `value`.
- * - `reject` Single shot function that rejects enclosed `promise` with a given
- *   `reason`.
- *
- * An optional `prototype` argument is used as a prototype of the returned
- * `promise` allowing one to implement additional API. If prototype is not
- * passed then it falls back to `Object.prototype`.
- *
- *  ## Example
- *
- *  function fetchURI(uri, type) {
- *    var deferred = defer();
- *    var request = new XMLHttpRequest();
- *    request.open("GET", uri, true);
- *    request.responseType = type;
- *    request.onload = function onload() {
- *      deferred.resolve(request.response);
- *    }
- *    request.onerror = function(event) {
- *     deferred.reject(event);
- *    }
- *    request.send();
- *
- *    return deferred.promise;
- *  }
- */
-function defer(prototype) {
-  // Define FIFO queue of observer pairs. Once promise is resolved & all queued
-  // observers are forwarded to `result` and variable is set to `null`.
-  var observers = [];
-
-  // Promise `result`, which will be assigned a resolution value once promise
-  // is resolved. Note that result will always be assigned promise (or alike)
-  // object to take care of propagation through promise chains. If result is
-  // `null` promise is not resolved yet.
-  var result = null;
-
-  prototype = (prototype || prototype === null) ? prototype : Object.prototype;
-
-  // Create an object implementing promise API.
-  var promise = Object.create(prototype, {
-    then: { value: function then(onFulfill, onError) {
-      var deferred = defer(prototype);
-
-      function resolve(value) {
-        // If `onFulfill` handler is provided resolve `deferred.promise` with
-        // result of invoking it with a resolution value. If handler is not
-        // provided propagate value through.
-        try {
-          deferred.resolve(onFulfill ? onFulfill(value) : value);
-        }
-        // `onFulfill` may throw exception in which case resulting promise
-        // is rejected with thrown exception.
-        catch(error) {
-          if (exports._reportErrors && typeof(console) === 'object') {
-            util.errorHandler(error);
-          }
-          // Note: Following is equivalent of `deferred.reject(error)`,
-          // we use this shortcut to reduce a stack.
-          deferred.resolve(rejected(error));
-        }
-      }
-
-      function reject(reason) {
-        try {
-          if (onError) { deferred.resolve(onError(reason)); }
-          else { deferred.resolve(rejected(reason)); }
-        }
-        catch(error) {
-          if (exports._reportErrors && typeof(console) === 'object') {
-            util.errorHandler(error);
-          }
-          deferred.resolve(rejected(error));
-        }
-      }
-
-      // If enclosed promise (`this.promise`) observers queue is still alive
-      // enqueue a new observer pair into it. Note that this does not
-      // necessary means that promise is pending, it may already be resolved,
-      // but we still have to queue observers to guarantee an order of
-      // propagation.
-      if (observers) {
-        observers.push({ resolve: resolve, reject: reject });
-      }
-      // Otherwise just forward observer pair right to a `result` promise.
-      else {
-        result.then(resolve, reject);
-      }
-
-      return deferred.promise;
-    }},
-    done: { value: function() {
-      this.then(null, util.errorHandler);
-    }},
-  });
-
-  var deferred = {
-    promise: promise,
-    /**
-     * Resolves associated `promise` to a given `value`, unless it's already
-     * resolved or rejected. Note that resolved promise is not necessary a
-     * successfully fulfilled. Promise may be resolved with a promise `value`
-     * in which case `value` promise's fulfillment / rejection will propagate
-     * up to a promise resolved with `value`.
-     */
-    resolve: function resolve(value) {
-      if (!result) {
-        // Store resolution `value` in a `result` as a promise, so that all
-        // the subsequent handlers can be simply forwarded to it. Since
-        // `result` will be a promise all the value / error propagation will
-        // be uniformly taken care of.
-        result = isPromise(value) ? value : fulfilled(value);
-
-        // Forward already registered observers to a `result` promise in the
-        // order they were registered. Note that we intentionally dequeue
-        // observer at a time until queue is exhausted. This makes sure that
-        // handlers registered as side effect of observer forwarding are
-        // queued instead of being invoked immediately, guaranteeing FIFO
-        // order.
-        while (observers.length) {
-          var observer = observers.shift();
-          result.then(observer.resolve, observer.reject);
-        }
-
-        // Once `observers` queue is exhausted we `null`-ify it, so that
-        // new handlers are forwarded straight to the `result`.
-        observers = null;
-      }
-    },
-    /**
-     * Rejects associated `promise` with a given `reason`, unless it's already
-     * resolved / rejected. This is just a (better performing) convenience
-     * shortcut for `deferred.resolve(reject(reason))`.
-     */
-    reject: function reject(reason) {
-      // Note that if promise is resolved that does not necessary means that it
-      // is successfully fulfilled. Resolution value may be a promise in which
-      // case its result propagates. In other words if promise `a` is resolved
-      // with promise `b`, `a` is either fulfilled or rejected depending
-      // on weather `b` is fulfilled or rejected. Here `deferred.promise` is
-      // resolved with a promise pre-rejected with a given `reason`, there for
-      // `deferred.promise` is rejected with a given `reason`. This may feel
-      // little awkward first, but doing it this way greatly simplifies
-      // propagation through promise chains.
-      deferred.resolve(rejected(reason));
-    }
-  };
-
-  return deferred;
-}
-exports.defer = defer;
-
-/**
- * Returns a promise resolved to a given `value`. Optionally a second
- * `prototype` argument may be provided to be used as a prototype for the
- * returned promise.
- */
-function resolve(value, prototype) {
-  var deferred = defer(prototype);
-  deferred.resolve(value);
-  return deferred.promise;
-}
-exports.resolve = resolve;
-
-/**
- * Returns a promise rejected with a given `reason`. Optionally a second
- * `prototype` argument may be provided to be used as a prototype for the
- * returned promise.
- */
-function reject(reason, prototype) {
-  var deferred = defer(prototype);
-  deferred.reject(reason);
-  return deferred.promise;
-}
-exports.reject = reject;
-
-var promised = (function() {
-  // Note: Define shortcuts and utility functions here in order to avoid
-  // slower property accesses and unnecessary closure creations on each
-  // call of this popular function.
-
-  var call = Function.call;
-  var concat = Array.prototype.concat;
-
-  // Utility function that does following:
-  // execute([ f, self, args...]) => f.apply(self, args)
-  function execute(args) { return call.apply(call, args); }
-
-  // Utility function that takes promise of `a` array and maybe promise `b`
-  // as arguments and returns promise for `a.concat(b)`.
-  function promisedConcat(promises, unknown) {
-    return promises.then(function(values) {
-      return resolve(unknown).then(function(value) {
-        return values.concat([ value ]);
-      });
-    });
-  }
-
-  return function promised(f, prototype) {
-    /**
-    Returns a wrapped `f`, which when called returns a promise that resolves to
-    `f(...)` passing all the given arguments to it, which by the way may be
-    promises. Optionally second `prototype` argument may be provided to be used
-    a prototype for a returned promise.
-
-    ## Example
-
-    var promise = promised(Array)(1, promise(2), promise(3))
-    promise.then(console.log) // => [ 1, 2, 3 ]
-    **/
-
-    return function promised() {
-      // create array of [ f, this, args... ]
-      return concat.apply([ f, this ], arguments).
-        // reduce it via `promisedConcat` to get promised array of fulfillments
-        reduce(promisedConcat, resolve([], prototype)).
-        // finally map that to promise of `f.apply(this, args...)`
-        then(execute);
-    };
-  };
-})();
-exports.promised = promised;
-
-var all = promised(Array);
-exports.all = all;
+var Cu = require('chrome').Cu;
+module.exports = exports =
+    Cu.import('resource://gre/modules/commonjs/sdk/core/promise.js', {}).Promise;
deleted file mode 100644
--- a/toolkit/devtools/gcli/source/mozilla/gcli/index.js
+++ /dev/null
@@ -1,163 +0,0 @@
-/*
- * Copyright 2012, Mozilla Foundation and contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-'use strict';
-
-var Cc = require('chrome').Cc;
-var Ci = require('chrome').Ci;
-var Cu = require('chrome').Cu;
-
-/*
- * GCLI is built from a number of components (called items) composed as
- * required for each environment.
- * When adding to or removing from this list, we should keep the basics in sync
- * with the other environments.
- * See:
- * - lib/gcli/index.js: Generic basic set (without commands)
- * - lib/gcli/demo.js: Adds demo commands to basic set for use in web demo
- * - gcli.js: Add commands to basic set for use in Node command line
- * - mozilla/gcli/index.js: From scratch listing for Firefox
- * - lib/gcli/connectors/index.js: Client only items when executing remotely
- * - lib/gcli/connectors/direct.js: Test items for connecting to in-process GCLI
- */
-var items = [
-  require('./types/delegate').items,
-  require('./types/selection').items,
-  require('./types/array').items,
-
-  require('./types/boolean').items,
-  require('./types/command').items,
-  require('./types/date').items,
-  require('./types/file').items,
-  require('./types/javascript').items,
-  require('./types/node').items,
-  require('./types/number').items,
-  require('./types/resource').items,
-  require('./types/setting').items,
-  require('./types/string').items,
-
-  require('./fields/delegate').items,
-  require('./fields/selection').items,
-
-  require('./ui/focus').items,
-  require('./ui/intro').items,
-
-  require('./converters/converters').items,
-  require('./converters/basic').items,
-  // require('./converters/html').items, // Prevent use of innerHTML
-  require('./converters/terminal').items,
-
-  require('./languages/command').items,
-  require('./languages/javascript').items,
-
-  // require('./connectors/direct').items, // No need for loopback testing
-  // require('./connectors/rdp').items, // Needs fixing
-  // require('./connectors/websocket').items, // Not from chrome
-  // require('./connectors/xhr').items, // Not from chrome
-
-  // require('./cli').items, // No need for '{' with web console
-  require('./commands/clear').items,
-  // require('./commands/connect').items, // We need to fix our RDP connector
-  require('./commands/context').items,
-  // require('./commands/exec').items, // No exec in Firefox yet
-  require('./commands/global').items,
-  require('./commands/help').items,
-  // require('./commands/intro').items, // No need for intro command
-  require('./commands/lang').items,
-  // require('./commands/mocks').items, // Only for testing
-  require('./commands/pref').items,
-  // require('./commands/preflist').items, // Too slow in Firefox
-  // require('./commands/test').items, // Only for testing
-
-  // No demo or node commands
-
-].reduce(function(prev, curr) { return prev.concat(curr); }, []);
-
-var api = require('./api');
-api.populateApi(exports);
-exports.addItems(items);
-
-var host = require('./util/host');
-
-exports.useTarget = host.script.useTarget;
-
-/**
- * This code is internal and subject to change without notice.
- * createDisplay() for Firefox requires an options object with the following
- * members:
- * - contentDocument: From the window of the attached tab
- * - chromeDocument: GCLITerm.document
- * - environment.hudId: GCLITerm.hudId
- * - jsEnvironment.globalObject: 'window'
- * - jsEnvironment.evalFunction: 'eval' in a sandbox
- * - inputElement: GCLITerm.inputNode
- * - completeElement: GCLITerm.completeNode
- * - hintElement: GCLITerm.hintNode
- * - inputBackgroundElement: GCLITerm.inputStack
- */
-exports.createDisplay = function(opts) {
-  var FFDisplay = require('./mozui/ffdisplay').FFDisplay;
-  return new FFDisplay(opts);
-};
-
-var prefSvc = Cc['@mozilla.org/preferences-service;1']
-                        .getService(Ci.nsIPrefService);
-var prefBranch = prefSvc.getBranch(null).QueryInterface(Ci.nsIPrefBranch2);
-
-exports.hiddenByChromePref = function() {
-  return !prefBranch.prefHasUserValue('devtools.chrome.enabled');
-};
-
-
-try {
-  var Services = Cu.import('resource://gre/modules/Services.jsm', {}).Services;
-  var stringBundle = Services.strings.createBundle(
-          'chrome://browser/locale/devtools/gclicommands.properties');
-
-  /**
-   * Lookup a string in the GCLI string bundle
-   */
-  exports.lookup = function(name) {
-    try {
-      return stringBundle.GetStringFromName(name);
-    }
-    catch (ex) {
-      throw new Error('Failure in lookup(\'' + name + '\')');
-    }
-  };
-
-  /**
-   * Lookup a string in the GCLI string bundle
-   */
-  exports.lookupFormat = function(name, swaps) {
-    try {
-      return stringBundle.formatStringFromName(name, swaps, swaps.length);
-    }
-    catch (ex) {
-      throw new Error('Failure in lookupFormat(\'' + name + '\')');
-    }
-  };
-}
-catch (ex) {
-  console.error('Using string fallbacks', ex);
-
-  exports.lookup = function(name) {
-    return name;
-  };
-  exports.lookupFormat = function(name, swaps) {
-    return name;
-  };
-}
deleted file mode 100644
--- a/toolkit/devtools/gcli/source/mozilla/gcli/settings.js
+++ /dev/null
@@ -1,307 +0,0 @@
-/*
- * Copyright 2012, Mozilla Foundation and contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-'use strict';
-
-var imports = {};
-
-var Cc = require('chrome').Cc;
-var Ci = require('chrome').Ci;
-var Cu = require('chrome').Cu;
-
-var XPCOMUtils = Cu.import('resource://gre/modules/XPCOMUtils.jsm', {}).XPCOMUtils;
-var Services = Cu.import('resource://gre/modules/Services.jsm', {}).Services;
-
-XPCOMUtils.defineLazyGetter(imports, 'prefBranch', function() {
-  var prefService = Cc['@mozilla.org/preferences-service;1']
-          .getService(Ci.nsIPrefService);
-  return prefService.getBranch(null).QueryInterface(Ci.nsIPrefBranch2);
-});
-
-XPCOMUtils.defineLazyGetter(imports, 'supportsString', function() {
-  return Cc['@mozilla.org/supports-string;1']
-          .createInstance(Ci.nsISupportsString);
-});
-
-var util = require('./util/util');
-
-/**
- * All local settings have this prefix when used in Firefox
- */
-var DEVTOOLS_PREFIX = 'devtools.gcli.';
-
-/**
- * The type library that we use in creating types for settings
- */
-var types;
-
-/**
- * A class to wrap up the properties of a preference.
- * @see toolkit/components/viewconfig/content/config.js
- */
-function Setting(prefSpec) {
-  if (typeof prefSpec === 'string') {
-    // We're coming from getAll() i.e. a full listing of prefs
-    this.name = prefSpec;
-    this.description = '';
-  }
-  else {
-    // A specific addition by GCLI
-    this.name = DEVTOOLS_PREFIX + prefSpec.name;
-
-    if (prefSpec.ignoreTypeDifference !== true && prefSpec.type) {
-      if (this.type.name !== prefSpec.type) {
-        throw new Error('Locally declared type (' + prefSpec.type + ') != ' +
-            'Mozilla declared type (' + this.type.name + ') for ' + this.name);
-      }
-    }
-
-    this.description = prefSpec.description;
-  }
-
-  this.onChange = util.createEvent('Setting.onChange');
-}
-
-/**
- * What type is this property: boolean/integer/string?
- */
-Object.defineProperty(Setting.prototype, 'type', {
-  get: function() {
-    switch (imports.prefBranch.getPrefType(this.name)) {
-      case imports.prefBranch.PREF_BOOL:
-        return types.createType('boolean');
-
-      case imports.prefBranch.PREF_INT:
-        return types.createType('number');
-
-      case imports.prefBranch.PREF_STRING:
-        return types.createType('string');
-
-      default:
-        throw new Error('Unknown type for ' + this.name);
-    }
-  },
-  enumerable: true
-});
-
-/**
- * What type is this property: boolean/integer/string?
- */
-Object.defineProperty(Setting.prototype, 'value', {
-  get: function() {
-    switch (imports.prefBranch.getPrefType(this.name)) {
-      case imports.prefBranch.PREF_BOOL:
-        return imports.prefBranch.getBoolPref(this.name);
-
-      case imports.prefBranch.PREF_INT:
-        return imports.prefBranch.getIntPref(this.name);
-
-      case imports.prefBranch.PREF_STRING:
-        var value = imports.prefBranch.getComplexValue(this.name,
-                Ci.nsISupportsString).data;
-        // In case of a localized string
-        if (/^chrome:\/\/.+\/locale\/.+\.properties/.test(value)) {
-          value = imports.prefBranch.getComplexValue(this.name,
-                  Ci.nsIPrefLocalizedString).data;
-        }
-        return value;
-
-      default:
-        throw new Error('Invalid value for ' + this.name);
-    }
-  },
-
-  set: function(value) {
-    if (imports.prefBranch.prefIsLocked(this.name)) {
-      throw new Error('Locked preference ' + this.name);
-    }
-
-    switch (imports.prefBranch.getPrefType(this.name)) {
-      case imports.prefBranch.PREF_BOOL:
-        imports.prefBranch.setBoolPref(this.name, value);
-        break;
-
-      case imports.prefBranch.PREF_INT:
-        imports.prefBranch.setIntPref(this.name, value);
-        break;
-
-      case imports.prefBranch.PREF_STRING:
-        imports.supportsString.data = value;
-        imports.prefBranch.setComplexValue(this.name,
-                Ci.nsISupportsString,
-                imports.supportsString);
-        break;
-
-      default:
-        throw new Error('Invalid value for ' + this.name);
-    }
-
-    Services.prefs.savePrefFile(null);
-  },
-
-  enumerable: true
-});
-
-/**
- * Reset this setting to it's initial default value
- */
-Setting.prototype.setDefault = function() {
-  imports.prefBranch.clearUserPref(this.name);
-  Services.prefs.savePrefFile(null);
-};
-
-
-/**
- * Collection of preferences for sorted access
- */
-var settingsAll = [];
-
-/**
- * Collection of preferences for fast indexed access
- */
-var settingsMap = new Map();
-
-/**
- * Flag so we know if we've read the system preferences
- */
-var hasReadSystem = false;
-
-/**
- * Clear out all preferences and return to initial state
- */
-function reset() {
-  settingsMap = new Map();
-  settingsAll = [];
-  hasReadSystem = false;
-}
-
-/**
- * Reset everything on startup and shutdown because we're doing lazy loading
- */
-exports.startup = function(t) {
-  reset();
-  types = t;
-  if (types == null) {
-    throw new Error('no types');
-  }
-};
-
-exports.shutdown = function() {
-  reset();
-};
-
-/**
- * Load system prefs if they've not been loaded already
- * @return true
- */
-function readSystem() {
-  if (hasReadSystem) {
-    return;
-  }
-
-  imports.prefBranch.getChildList('').forEach(function(name) {
-    var setting = new Setting(name);
-    settingsAll.push(setting);
-    settingsMap.set(name, setting);
-  });
-
-  settingsAll.sort(function(s1, s2) {
-    return s1.name.localeCompare(s2.name);
-  });
-
-  hasReadSystem = true;
-}
-
-/**
- * Get an array containing all known Settings filtered to match the given
- * filter (string) at any point in the name of the setting
- */
-exports.getAll = function(filter) {
-  readSystem();
-
-  if (filter == null) {
-    return settingsAll;
-  }
-
-  return settingsAll.filter(function(setting) {
-    return setting.name.indexOf(filter) !== -1;
-  });
-};
-
-/**
- * Add a new setting.
- */
-exports.addSetting = function(prefSpec) {
-  var setting = new Setting(prefSpec);
-
-  if (settingsMap.has(setting.name)) {
-    // Once exists already, we're going to need to replace it in the array
-    for (var i = 0; i < settingsAll.length; i++) {
-      if (settingsAll[i].name === setting.name) {
-        settingsAll[i] = setting;
-      }
-    }
-  }
-
-  settingsMap.set(setting.name, setting);
-  exports.onChange({ added: setting.name });
-
-  return setting;
-};
-
-/**
- * Getter for an existing setting. Generally use of this function should be
- * avoided. Systems that define a setting should export it if they wish it to
- * be available to the outside, or not otherwise. Use of this function breaks
- * that boundary and also hides dependencies. Acceptable uses include testing
- * and embedded uses of GCLI that pre-define all settings (e.g. Firefox)
- * @param name The name of the setting to fetch
- * @return The found Setting object, or undefined if the setting was not found
- */
-exports.getSetting = function(name) {
-  // We might be able to give the answer without needing to read all system
-  // settings if this is an internal setting
-  var found = settingsMap.get(name);
-  if (!found) {
-    found = settingsMap.get(DEVTOOLS_PREFIX + name);
-  }
-
-  if (found) {
-    return found;
-  }
-
-  if (hasReadSystem) {
-    return undefined;
-  }
-  else {
-    readSystem();
-    found = settingsMap.get(name);
-    if (!found) {
-      found = settingsMap.get(DEVTOOLS_PREFIX + name);
-    }
-    return found;
-  }
-};
-
-/**
- * Event for use to detect when the list of settings changes
- */
-exports.onChange = util.createEvent('Settings.onChange');
-
-/**
- * Remove a setting. A no-op in this case
- */
-exports.removeSetting = function() { };
deleted file mode 100644
--- a/toolkit/devtools/gcli/source/mozilla/gcli/ui/menu.html
+++ /dev/null
@@ -1,11 +0,0 @@
-
-<div>
-  <table class="gcli-menu-template" aria-live="polite">
-    <tr class="gcli-menu-option" foreach="item in ${items}"
-        onclick="${onItemClickInternal}" title="${item.manual}">
-      <td class="gcli-menu-name">${item.name}</td>
-      <td class="gcli-menu-desc">${item.description}</td>
-    </tr>
-  </table>
-  <div class="gcli-menu-more" if="${items.hasMore}">${l10n.fieldMenuMore}</div>
-</div>
deleted file mode 100644
--- a/toolkit/devtools/gcli/source/mozilla/gcli/util/domtemplate.js
+++ /dev/null
@@ -1,21 +0,0 @@
-/*
- * Copyright 2012, Mozilla Foundation and contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-'use strict';
-
-var Cu = require('chrome').Cu;
-var template = Cu.import('resource://gre/modules/devtools/Templater.jsm', {}).template;
-exports.template = template;
deleted file mode 100644
--- a/toolkit/devtools/gcli/source/mozilla/gcli/util/filesystem.js
+++ /dev/null
@@ -1,126 +0,0 @@
-/*
- * Copyright 2012, Mozilla Foundation and contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-'use strict';
-
-var Cu = require('chrome').Cu;
-var Cc = require('chrome').Cc;
-var Ci = require('chrome').Ci;
-
-var OS = Cu.import('resource://gre/modules/osfile.jsm', {}).OS;
-var promise = require('./promise');
-
-/**
- * A set of functions that don't really belong in 'fs' (because they're not
- * really universal in scope) but also kind of do (because they're not specific
- * to GCLI
- */
-
-exports.join = OS.Path.join;
-exports.sep = OS.Path.sep;
-exports.dirname = OS.Path.dirname;
-
-var dirService = Cc['@mozilla.org/file/directory_service;1']
-                           .getService(Ci.nsIProperties);
-exports.home = dirService.get('Home', Ci.nsIFile).path;
-
-if ('winGetDrive' in OS.Path) {
-  exports.sep = '\\';
-}
-else {
-  exports.sep = '/';
-}
-
-/**
- * Split a path into its components.
- * @param pathname (string) The part to cut up
- * @return An array of path components
- */
-exports.split = function(pathname) {
-  return OS.Path.split(pathname).components;
-};
-
-/**
- * @param pathname string, path of an existing directory
- * @param matches optional regular expression - filter output to include only
- * the files that match the regular expression. The regexp is applied to the
- * filename only not to the full path
- * @return A promise of an array of stat objects for each member of the
- * directory pointed to by ``pathname``, each containing 2 extra properties:
- * - pathname: The full pathname of the file
- * - filename: The final filename part of the pathname
- */
-exports.ls = function(pathname, matches) {
-  var iterator = new OS.File.DirectoryIterator(pathname);
-  var entries = [];
-
-  var iteratePromise = iterator.forEach(function(entry) {
-    entries.push({
-      exists: true,
-      isDir: entry.isDir,
-      isFile: !entry.isFile,
-      filename: entry.name,
-      pathname: entry.path
-    });
-  });
-
-  return iteratePromise.then(function onSuccess() {
-      iterator.close();
-      return entries;
-    },
-    function onFailure(reason) {
-      iterator.close();
-      throw reason;
-    }
-  );
-};
-
-/**
- * stat() is annoying because it considers stat('/doesnt/exist') to be an
- * error, when the point of stat() is to *find* *out*. So this wrapper just
- * converts 'ENOENT' i.e. doesn't exist to { exists:false } and adds
- * exists:true to stat blocks from existing paths
- */
-exports.stat = function(pathname) {
-  var onResolve = function(stats) {
-    return {
-      exists: true,
-      isDir: stats.isDir,
-      isFile: !stats.isFile
-    };
-  };
-
-  var onReject = function(err) {
-    if (err instanceof OS.File.Error && err.becauseNoSuchFile) {
-      return {
-        exists: false,
-        isDir: false,
-        isFile: false
-      };
-    }
-    throw err;
-  };
-
-  return OS.File.stat(pathname).then(onResolve, onReject);
-};
-
-/**
- * We may read the first line of a file to describe it?
- * Right now, however, we do nothing.
- */
-exports.describe = function(pathname) {
-  return promise.resolve('');
-};
deleted file mode 100644
--- a/toolkit/devtools/gcli/source/mozilla/gcli/util/host.js
+++ /dev/null
@@ -1,209 +0,0 @@
-/*
- * Copyright 2012, Mozilla Foundation and contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-'use strict';
-
-var Cu = require('chrome').Cu;
-var Cc = require('chrome').Cc;
-var Ci = require('chrome').Ci;
-
-var OS = Cu.import('resource://gre/modules/osfile.jsm', {}).OS;
-
-var promise = require('./promise');
-var util = require('./util');
-
-function Highlighter(document) {
-  this._document = document;
-  this._nodes = util.createEmptyNodeList(this._document);
-}
-
-Object.defineProperty(Highlighter.prototype, 'nodelist', {
-  set: function(nodes) {
-    Array.prototype.forEach.call(this._nodes, this._unhighlightNode, this);
-    this._nodes = (nodes == null) ?
-        util.createEmptyNodeList(this._document) :
-        nodes;
-    Array.prototype.forEach.call(this._nodes, this._highlightNode, this);
-  },
-  get: function() {
-    return this._nodes;
-  },
-  enumerable: true
-});
-
-Highlighter.prototype.destroy = function() {
-  this.nodelist = null;
-};
-
-Highlighter.prototype._highlightNode = function(node) {
-  // Enable when the highlighter rewrite is done
-};
-
-Highlighter.prototype._unhighlightNode = function(node) {
-  // Enable when the highlighter rewrite is done
-};
-
-exports.Highlighter = Highlighter;
-
-/**
- * See docs in lib/gcli/util/host.js:exec
- */
-exports.exec = function(execSpec) {
-  throw new Error('Not supported');
-};
-
-/**
- * Asynchronously load a text resource
- * @see lib/gcli/util/host.js
- */
-exports.staticRequire = function(requistingModule, name) {
-  var deferred = promise.defer();
-
-  if (name.match(/\.css$/)) {
-    deferred.resolve('');
-  }
-  else {
-    var filename = OS.Path.dirname(requistingModule.id) + '/' + name;
-    filename = filename.replace(/\/\.\//g, '/');
-    filename = 'resource://gre/modules/devtools/' + filename;
-
-    var xhr = Cc['@mozilla.org/xmlextras/xmlhttprequest;1']
-                .createInstance(Ci.nsIXMLHttpRequest);
-
-    xhr.onload = function onload() {
-      deferred.resolve(xhr.responseText);
-    }.bind(this);
-
-    xhr.onabort = xhr.onerror = xhr.ontimeout = function(err) {
-      deferred.reject(err);
-    }.bind(this);
-
-    try {
-      xhr.open('GET', filename);
-      xhr.send();
-    }
-    catch (ex) {
-      deferred.reject(ex);
-    }
-  }
-
-  return deferred.promise;
-};
-
-/**
- * A group of functions to help scripting. Small enough that it doesn't need
- * a separate module (it's basically a wrapper around 'eval' in some contexts)
- */
-var client;
-var target;
-var consoleActor;
-var webConsoleClient;
-
-exports.script = { };
-
-exports.script.onOutput = util.createEvent('Script.onOutput');
-
-/**
- * Setup the environment to eval JavaScript
- */
-exports.script.useTarget = function(tgt) {
-  target = tgt;
-
-  // Local debugging needs to make the target remote.
-  var targetPromise = target.isRemote ?
-                      promise.resolve(target) :
-                      target.makeRemote();
-
-  return targetPromise.then(function() {
-    var deferred = promise.defer();
-
-    client = target._client;
-
-    client.addListener('pageError', function(packet) {
-      if (packet.from === consoleActor) {
-        // console.log('pageError', packet.pageError);
-        exports.script.onOutput({
-          level: 'exception',
-          message: packet.exception.class
-        });
-      }
-    });
-
-    client.addListener('consoleAPICall', function(type, packet) {
-      if (packet.from === consoleActor) {
-        var data = packet.message;
-
-        var ev = {
-          level: data.level,
-          arguments: data.arguments,
-        };
-
-        if (data.filename !== 'debugger eval code') {
-          ev.source = {
-            filename: data.filename,
-            lineNumber: data.lineNumber,
-            functionName: data.functionName
-          };
-        }
-
-        exports.script.onOutput(ev);
-      }
-    });
-
-    consoleActor = target._form.consoleActor;
-
-    var onAttach = function(response, wcc) {
-      webConsoleClient = wcc;
-
-      if (response.error != null) {
-        deferred.reject(response);
-      }
-      else {
-        deferred.resolve(response);
-      }
-
-      // TODO: add _onTabNavigated code?
-    };
-
-    var listeners = [ 'PageError', 'ConsoleAPI' ];
-    client.attachConsole(consoleActor, listeners, onAttach);
-
-    return deferred.promise;
-  });
-};
-
-/**
- * Execute some JavaScript
- */
-exports.script.eval = function(javascript) {
-  var deferred = promise.defer();
-
-  var onResult = function(response) {
-    var output = response.result;
-    if (typeof output === 'object' && output.type === 'undefined') {
-      output = undefined;
-    }
-
-    deferred.resolve({
-      input: response.input,
-      output: output,
-      exception: response.exception
-    });
-  };
-
-  webConsoleClient.evaluateJS(javascript, onResult, {});
-  return deferred.promise;
-};
deleted file mode 100644
--- a/toolkit/devtools/gcli/source/mozilla/gcli/util/l10n.js
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * Copyright 2012, Mozilla Foundation and contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-'use strict';
-
-var Cu = require('chrome').Cu;
-
-var XPCOMUtils = Cu.import('resource://gre/modules/XPCOMUtils.jsm', {}).XPCOMUtils;
-var Services = Cu.import('resource://gre/modules/Services.jsm', {}).Services;
-
-var imports = {};
-XPCOMUtils.defineLazyGetter(imports, 'stringBundle', function () {
-  return Services.strings.createBundle('chrome://browser/locale/devtools/gcli.properties');
-});
-
-/*
- * Not supported when embedded - we're doing things the Mozilla way not the
- * require.js way.
- */
-exports.registerStringsSource = function(modulePath) {
-  throw new Error('registerStringsSource is not available in mozilla');
-};
-
-exports.unregisterStringsSource = function(modulePath) {
-  throw new Error('unregisterStringsSource is not available in mozilla');
-};
-
-exports.lookupSwap = function(key, swaps) {
-  throw new Error('lookupSwap is not available in mozilla');
-};
-
-exports.lookupPlural = function(key, ord, swaps) {
-  throw new Error('lookupPlural is not available in mozilla');
-};
-
-exports.getPreferredLocales = function() {
-  return [ 'root' ];
-};
-
-/** @see lookup() in lib/gcli/util/l10n.js */
-exports.lookup = function(key) {
-  try {
-    // Our memory leak hunter walks reachable objects trying to work out what
-    // type of thing they are using object.constructor.name. If that causes
-    // problems then we can avoid the unknown-key-exception with the following:
-    /*
-    if (key === 'constructor') {
-      return { name: 'l10n-mem-leak-defeat' };
-    }
-    */
-
-    return imports.stringBundle.GetStringFromName(key);
-  }
-  catch (ex) {
-    console.error('Failed to lookup ', key, ex);
-    return key;
-  }
-};
-
-/** @see propertyLookup in lib/gcli/util/l10n.js */
-exports.propertyLookup = Proxy.create({
-  get: function(rcvr, name) {
-    return exports.lookup(name);
-  }
-});
-
-/** @see lookupFormat in lib/gcli/util/l10n.js */
-exports.lookupFormat = function(key, swaps) {
-  try {
-    return imports.stringBundle.formatStringFromName(key, swaps, swaps.length);
-  }
-  catch (ex) {
-    console.error('Failed to format ', key, ex);
-    return key;
-  }
-};
deleted file mode 100644
--- a/toolkit/devtools/gcli/source/mozilla/gcli/util/promise.js
+++ /dev/null
@@ -1,21 +0,0 @@
-/*
- * Copyright 2012, Mozilla Foundation and contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-'use strict';
-
-var Cu = require('chrome').Cu;
-module.exports = exports =
-    Cu.import('resource://gre/modules/commonjs/sdk/core/promise.js', {}).Promise;