Merge inbound to m-c a=merge
authorWes Kocher <wkocher@mozilla.com>
Wed, 25 Jun 2014 18:18:43 -0700
changeset 190849 464bca437658a87a7cb9846ba916f0e8d715dbc5
parent 190848 6401390a005df7ae8b6b44f179cb75cc7de79ba9 (diff)
parent 190764 e97f7d9d54d9461bea0cacd451f28831fc0e781b (current diff)
child 190850 c99c96d225597df2ddcac4c1b5e092237481f4ff
child 190935 19c155434605bccaab48d250ea48e04357bb37ca
child 190957 15e8c8100ed72fcfd6b8af2fe9d45adcd1537e5a
push id45406
push userkwierso@gmail.com
push dateThu, 26 Jun 2014 01:39:41 +0000
treeherdermozilla-inbound@c99c96d22559 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone33.0a1
first release with
nightly linux32
464bca437658 / 33.0a1 / 20140626030201 / files
nightly linux64
464bca437658 / 33.0a1 / 20140626030201 / files
nightly mac
464bca437658 / 33.0a1 / 20140626030201 / files
nightly win32
464bca437658 / 33.0a1 / 20140626030201 / files
nightly win64
464bca437658 / 33.0a1 / 20140626030201 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge inbound to m-c a=merge
--- a/b2g/config/emulator-ics/sources.xml
+++ b/b2g/config/emulator-ics/sources.xml
@@ -14,23 +14,23 @@
   <!--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="2a165bebfa19b11b697837409f9550dd2917c46c">
     <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="1bcd355855626640b2532f2ccb1f814711f7a6ad"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="aecb3041c71c5627aea30628ee82fc74e8af0d47"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="bbb7659d8ea2afb396f99b3dc971ab3c42da3778"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="cd88d860656c31c7da7bb310d6a160d0011b0961"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="3326b51017252e09ccdd715dec6c5e12a7d1ecfe"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="c510babaf88dfa2cfe2c202afb2649ee124569af"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="6f0e303999894aa657d3863dbaf00e4aa002b3ed"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="7f9ec13a30f1b2cc8bdb1a199b7da54b9ab8860f"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
   <project name="platform/bionic" path="bionic" revision="c72b8f6359de7ed17c11ddc9dfdde3f615d188a9"/>
   <project name="platform/bootable/recovery" path="bootable/recovery" revision="425f8b5fadf5889834c5acd27d23c9e0b2129c28"/>
   <project name="device/common" path="device/common" revision="42b808b7e93d0619286ae8e59110b176b7732389"/>
   <project name="device/sample" path="device/sample" revision="237bd668d0f114d801a8d6455ef5e02cc3577587"/>
   <project name="platform_external_apriori" path="external/apriori" remote="b2g" revision="11816ad0406744f963537b23d68ed9c2afb412bd"/>
   <project name="platform/external/bluetooth/bluez" path="external/bluetooth/bluez" revision="52a1a862a8bac319652b8f82d9541ba40bfa45ce"/>
--- a/b2g/config/emulator-jb/sources.xml
+++ b/b2g/config/emulator-jb/sources.xml
@@ -12,20 +12,20 @@
   <!--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="cc67f31dc638c0b7edba3cf7e3d87cadf0ed52bf">
     <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="1bcd355855626640b2532f2ccb1f814711f7a6ad"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="aecb3041c71c5627aea30628ee82fc74e8af0d47"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="bbb7659d8ea2afb396f99b3dc971ab3c42da3778"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="c510babaf88dfa2cfe2c202afb2649ee124569af"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="6f0e303999894aa657d3863dbaf00e4aa002b3ed"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="7f9ec13a30f1b2cc8bdb1a199b7da54b9ab8860f"/>
   <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"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="9025e50b9d29b3cabbbb21e1dd94d0d13121a17e"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="b89fda71fcd0fa0cf969310e75be3ea33e048b44"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" revision="2e7d5348f35575870b3c7e567a9a9f6d66f8d6c5"/>
--- a/b2g/config/emulator-kk/sources.xml
+++ b/b2g/config/emulator-kk/sources.xml
@@ -10,25 +10,25 @@
   <!--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="276ce45e78b09c4a4ee643646f691d22804754c1">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="1bcd355855626640b2532f2ccb1f814711f7a6ad"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="aecb3041c71c5627aea30628ee82fc74e8af0d47"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="bbb7659d8ea2afb396f99b3dc971ab3c42da3778"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="c510babaf88dfa2cfe2c202afb2649ee124569af"/>
   <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="6f0e303999894aa657d3863dbaf00e4aa002b3ed"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="7f9ec13a30f1b2cc8bdb1a199b7da54b9ab8860f"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="f92a936f2aa97526d4593386754bdbf02db07a12"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="6e47ff2790f5656b5b074407829ceecf3e6188c4"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" revision="1950e4760fa14688b83cdbb5acaa1af9f82ef434"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.7" revision="ac6eb97a37035c09fb5ede0852f0881e9aadf9ad"/>
   <project groups="linux,x86" name="platform/prebuilts/gcc/linux-x86/x86/i686-linux-android-4.7" path="prebuilts/gcc/linux-x86/x86/i686-linux-android-4.7" revision="737f591c5f95477148d26602c7be56cbea0cdeb9"/>
   <project groups="linux,x86" name="platform/prebuilts/python/linux-x86/2.7.5" path="prebuilts/python/linux-x86/2.7.5" revision="51da9b1981be481b92a59a826d4d78dc73d0989a"/>
   <project name="device/common" path="device/common" revision="798a3664597e6041985feab9aef42e98d458bc3d"/>
--- a/b2g/config/emulator/sources.xml
+++ b/b2g/config/emulator/sources.xml
@@ -14,23 +14,23 @@
   <!--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="2a165bebfa19b11b697837409f9550dd2917c46c">
     <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="1bcd355855626640b2532f2ccb1f814711f7a6ad"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="aecb3041c71c5627aea30628ee82fc74e8af0d47"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="bbb7659d8ea2afb396f99b3dc971ab3c42da3778"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="cd88d860656c31c7da7bb310d6a160d0011b0961"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="3326b51017252e09ccdd715dec6c5e12a7d1ecfe"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="c510babaf88dfa2cfe2c202afb2649ee124569af"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="6f0e303999894aa657d3863dbaf00e4aa002b3ed"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="7f9ec13a30f1b2cc8bdb1a199b7da54b9ab8860f"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
   <project name="platform/bionic" path="bionic" revision="c72b8f6359de7ed17c11ddc9dfdde3f615d188a9"/>
   <project name="platform/bootable/recovery" path="bootable/recovery" revision="425f8b5fadf5889834c5acd27d23c9e0b2129c28"/>
   <project name="device/common" path="device/common" revision="42b808b7e93d0619286ae8e59110b176b7732389"/>
   <project name="device/sample" path="device/sample" revision="237bd668d0f114d801a8d6455ef5e02cc3577587"/>
   <project name="platform_external_apriori" path="external/apriori" remote="b2g" revision="11816ad0406744f963537b23d68ed9c2afb412bd"/>
   <project name="platform/external/bluetooth/bluez" path="external/bluetooth/bluez" revision="52a1a862a8bac319652b8f82d9541ba40bfa45ce"/>
--- a/b2g/config/flame/sources.xml
+++ b/b2g/config/flame/sources.xml
@@ -12,20 +12,20 @@
   <!--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="cc67f31dc638c0b7edba3cf7e3d87cadf0ed52bf">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="1bcd355855626640b2532f2ccb1f814711f7a6ad"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="aecb3041c71c5627aea30628ee82fc74e8af0d47"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="bbb7659d8ea2afb396f99b3dc971ab3c42da3778"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="c510babaf88dfa2cfe2c202afb2649ee124569af"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="6f0e303999894aa657d3863dbaf00e4aa002b3ed"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="7f9ec13a30f1b2cc8bdb1a199b7da54b9ab8860f"/>
   <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="e95b4ce22c825da44d14299e1190ea39a5260bde"/>
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.2" path="prebuilts/clang/linux-x86/3.2" revision="471afab478649078ad7c75ec6b252481a59e19b8"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="95bb5b66b3ec5769c3de8d3f25d681787418e7d2"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="ebdad82e61c16772f6cd47e9f11936bf6ebe9aa0"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" revision="8b880805d454664b3eed11d0f053cdeafa1ff06e"/>
--- a/b2g/config/gaia.json
+++ b/b2g/config/gaia.json
@@ -1,9 +1,9 @@
 {
     "git": {
         "git_revision": "", 
         "remote": "", 
         "branch": ""
     }, 
-    "revision": "d2f6eae6c726c9e470bbd8f980e9d5c816cf49f2", 
+    "revision": "5aa4dc2b48dca49517a50d5ba07a0c907af0a4a0", 
     "repo_path": "/integration/gaia-central"
 }
--- a/b2g/config/hamachi/sources.xml
+++ b/b2g/config/hamachi/sources.xml
@@ -12,22 +12,22 @@
   <!--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="2a165bebfa19b11b697837409f9550dd2917c46c">
     <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="1bcd355855626640b2532f2ccb1f814711f7a6ad"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="aecb3041c71c5627aea30628ee82fc74e8af0d47"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="bbb7659d8ea2afb396f99b3dc971ab3c42da3778"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="c510babaf88dfa2cfe2c202afb2649ee124569af"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="6f0e303999894aa657d3863dbaf00e4aa002b3ed"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="7f9ec13a30f1b2cc8bdb1a199b7da54b9ab8860f"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
   <project name="platform/bionic" path="bionic" revision="d2eb6c7b6e1bc7643c17df2d9d9bcb1704d0b9ab"/>
   <project name="platform/bootable/recovery" path="bootable/recovery" revision="746bc48f34f5060f90801925dcdd964030c1ab6d"/>
   <project name="platform/development" path="development" revision="2460485184bc8535440bb63876d4e63ec1b4770c"/>
   <project name="device/common" path="device/common" revision="0dcc1e03659db33b77392529466f9eb685cdd3c7"/>
   <project name="device/sample" path="device/sample" revision="68b1cb978a20806176123b959cb05d4fa8adaea4"/>
   <project name="platform_external_apriori" path="external/apriori" remote="b2g" revision="11816ad0406744f963537b23d68ed9c2afb412bd"/>
--- 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="2a165bebfa19b11b697837409f9550dd2917c46c">
     <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="1bcd355855626640b2532f2ccb1f814711f7a6ad"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="aecb3041c71c5627aea30628ee82fc74e8af0d47"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="bbb7659d8ea2afb396f99b3dc971ab3c42da3778"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="c510babaf88dfa2cfe2c202afb2649ee124569af"/>
   <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/nexus-4/sources.xml
+++ b/b2g/config/nexus-4/sources.xml
@@ -12,20 +12,20 @@
   <!--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="cc67f31dc638c0b7edba3cf7e3d87cadf0ed52bf">
     <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="1bcd355855626640b2532f2ccb1f814711f7a6ad"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="aecb3041c71c5627aea30628ee82fc74e8af0d47"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="bbb7659d8ea2afb396f99b3dc971ab3c42da3778"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="c510babaf88dfa2cfe2c202afb2649ee124569af"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="6f0e303999894aa657d3863dbaf00e4aa002b3ed"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="7f9ec13a30f1b2cc8bdb1a199b7da54b9ab8860f"/>
   <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"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="9025e50b9d29b3cabbbb21e1dd94d0d13121a17e"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="b89fda71fcd0fa0cf969310e75be3ea33e048b44"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" revision="2e7d5348f35575870b3c7e567a9a9f6d66f8d6c5"/>
--- a/b2g/config/wasabi/sources.xml
+++ b/b2g/config/wasabi/sources.xml
@@ -12,22 +12,22 @@
   <!--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="2a165bebfa19b11b697837409f9550dd2917c46c">
     <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="1bcd355855626640b2532f2ccb1f814711f7a6ad"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="aecb3041c71c5627aea30628ee82fc74e8af0d47"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="bbb7659d8ea2afb396f99b3dc971ab3c42da3778"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="c510babaf88dfa2cfe2c202afb2649ee124569af"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="6f0e303999894aa657d3863dbaf00e4aa002b3ed"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="7f9ec13a30f1b2cc8bdb1a199b7da54b9ab8860f"/>
   <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="cd5dfce80bc3f0139a56b58aca633202ccaee7f8"/>
   <project name="platform/bootable/recovery" path="bootable/recovery" revision="e0a9ac010df3afaa47ba107192c05ac8b5516435"/>
   <project name="platform/development" path="development" revision="a384622f5fcb1d2bebb9102591ff7ae91fe8ed2d"/>
   <project name="device/common" path="device/common" revision="7c65ea240157763b8ded6154a17d3c033167afb7"/>
   <project name="device/sample" path="device/sample" revision="c328f3d4409db801628861baa8d279fb8855892f"/>
--- a/b2g/installer/Makefile.in
+++ b/b2g/installer/Makefile.in
@@ -92,16 +92,18 @@ ifdef MOZ_CHROME_MULTILOCALE
 	  printf '$(BINPATH)/chrome/'"$$LOCALE"'.manifest\n' >> $@; \
 	done
 endif
 
 GARBAGE += $(MOZ_PKG_MANIFEST)
 endif
 
 ifdef FXOS_SIMULATOR
+export MAKE
+
 .PHONY: simulator
 simulator: make-package
 	@echo 'Building simulator addon...'
 	$(PYTHON) $(topsrcdir)/b2g/simulator/build_xpi.py $(MOZ_PKG_PLATFORM)
 
 libs:: simulator
 
 # Ensure copying Simulator xpi to ftp
--- a/browser/components/customizableui/test/browser_967000_button_sync.js
+++ b/browser/components/customizableui/test/browser_967000_button_sync.js
@@ -4,16 +4,17 @@
 
 "use strict";
 
 let initialLocation = gBrowser.currentURI.spec;
 let newTab = null;
 
 add_task(function() {
   info("Check Sync button functionality");
+  Services.prefs.setCharPref("identity.fxaccounts.remote.signup.uri", "http://example.com/");
 
   // add the Sync button to the panel
   CustomizableUI.addWidgetToArea("sync-button", CustomizableUI.AREA_PANEL);
 
   // check the button's functionality
   yield PanelUI.show();
 
   let syncButton = document.getElementById("sync-button");
@@ -29,16 +30,17 @@ add_task(function() {
   if(isPanelUIOpen()) {
     let panelHidePromise = promisePanelHidden(window);
     PanelUI.hide();
     yield panelHidePromise;
   }
 });
 
 add_task(function asyncCleanup() {
+  Services.prefs.clearUserPref("identity.fxaccounts.remote.signup.uri");
   // reset the panel UI to the default state
   yield resetCustomization();
   ok(CustomizableUI.inDefaultState, "The panel UI is in default state again.");
 
   // restore the tabs
   gBrowser.addTab(initialLocation);
   gBrowser.removeTab(newTab);
 });
--- a/browser/devtools/commandline/commands-index.js
+++ b/browser/devtools/commandline/commands-index.js
@@ -10,16 +10,17 @@ const commandModules = [
   "devtools/tilt/tilt-commands",
   "gcli/commands/addon",
   "gcli/commands/appcache",
   "gcli/commands/calllog",
   "gcli/commands/cmd",
   "gcli/commands/cookie",
   "gcli/commands/csscoverage",
   "gcli/commands/folder",
+  "gcli/commands/highlight",
   "gcli/commands/inject",
   "gcli/commands/jsb",
   "gcli/commands/listen",
   "gcli/commands/media",
   "gcli/commands/pagemod",
   "gcli/commands/paintflashing",
   "gcli/commands/restart",
   "gcli/commands/screenshot",
--- a/browser/devtools/commandline/test/browser.ini
+++ b/browser/devtools/commandline/test/browser.ini
@@ -46,16 +46,18 @@ support-files =
  browser_cmd_csscoverage_page1.html
  browser_cmd_csscoverage_page2.html
  browser_cmd_csscoverage_page3.html
  browser_cmd_csscoverage_sheetA.css
  browser_cmd_csscoverage_sheetB.css
  browser_cmd_csscoverage_sheetC.css
  browser_cmd_csscoverage_sheetD.css
 [browser_cmd_folder.js]
+[browser_cmd_highlight_01.js]
+[browser_cmd_highlight_02.js]
 [browser_cmd_inject.js]
 support-files =
  browser_cmd_inject.html
 [browser_cmd_csscoverage_util.js]
 [browser_cmd_jsb.js]
 support-files =
   browser_cmd_jsb_script.jsi
 [browser_cmd_media.js]
@@ -98,8 +100,9 @@ skip-if = true || e10s # Disabled until 
 [browser_gcli_node.js]
 [browser_gcli_resource.js]
 [browser_gcli_short.js]
 [browser_gcli_spell.js]
 [browser_gcli_split.js]
 [browser_gcli_tokenize.js]
 [browser_gcli_tooltip.js]
 [browser_gcli_types.js]
+[browser_gcli_union.js]
new file mode 100644
--- /dev/null
+++ b/browser/devtools/commandline/test/browser_cmd_highlight_01.js
@@ -0,0 +1,257 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Tests the various highlight command parameters and options
+
+// Creating a test page with many elements to test the --showall option
+let TEST_PAGE = "data:text/html;charset=utf-8,<body><ul>";
+for (let i = 0; i < 200; i ++) {
+  TEST_PAGE += "<li class='item'>" + i + "</li>";
+}
+TEST_PAGE += "</ul></body>";
+
+function test() {
+  return Task.spawn(spawnTest).then(finish, helpers.handleError);
+}
+
+function* spawnTest() {
+  let options = yield helpers.openTab(TEST_PAGE);
+  yield helpers.openToolbar(options);
+
+  yield helpers.audit(options, [
+    {
+      setup: 'highlight',
+      check: {
+        input:  'highlight',
+        hints:           ' [selector] [options]',
+        markup: 'VVVVVVVVV',
+        status: 'VALID'
+      },
+      exec: {
+        output: '0 nodes highlighted'
+      }
+    },
+    {
+      setup: 'highlight bo',
+      check: {
+        input:  'highlight bo',
+        hints:              ' [options]',
+        markup: 'VVVVVVVVVVII',
+        status: 'ERROR'
+      },
+      exec: {
+        output: 'Error: No matches'
+      }
+    },
+    {
+      setup: 'highlight body',
+      check: {
+        input:  'highlight body',
+        hints:                ' [options]',
+        markup: 'VVVVVVVVVVVVVV',
+        status: 'VALID'
+      },
+      exec: {
+        output: '1 nodes highlighted'
+      }
+    },
+    {
+      setup: 'highlight body --hide',
+      check: {
+        input:  'highlight body --hide',
+        hints:                       'guides [options]',
+        markup: 'VVVVVVVVVVVVVVVIIIIII',
+        status: 'ERROR'
+      },
+      exec: {
+        output: 'Error: Too many arguments'
+      }
+    },
+    {
+      setup: 'highlight body --hideguides',
+      check: {
+        input:  'highlight body --hideguides',
+        hints:                             ' [options]',
+        markup: 'VVVVVVVVVVVVVVVVVVVVVVVVVVV',
+        status: 'VALID'
+      },
+      exec: {
+        output: '1 nodes highlighted'
+      }
+    },
+    {
+      setup: 'highlight body --show',
+      check: {
+        input:  'highlight body --show',
+        hints:                       'infobar [options]',
+        markup: 'VVVVVVVVVVVVVVVIIIIII',
+        status: 'ERROR'
+      },
+      exec: {
+        output: 'Error: Too many arguments'
+      }
+    },
+    {
+      setup: 'highlight body --showinfobar',
+      check: {
+        input:  'highlight body --showinfobar',
+        hints:                              ' [options]',
+        markup: 'VVVVVVVVVVVVVVVVVVVVVVVVVVVV',
+        status: 'VALID'
+      },
+      exec: {
+        output: '1 nodes highlighted'
+      }
+    },
+    {
+      setup: 'highlight body --showa',
+      check: {
+        input:  'highlight body --showa',
+        hints:                        'll [options]',
+        markup: 'VVVVVVVVVVVVVVVIIIIIII',
+        status: 'ERROR'
+      },
+      exec: {
+        output: 'Error: Too many arguments'
+      }
+    },
+    {
+      setup: 'highlight body --showall',
+      check: {
+        input:  'highlight body --showall',
+        hints:                          ' [options]',
+        markup: 'VVVVVVVVVVVVVVVVVVVVVVVV',
+        status: 'VALID'
+      },
+      exec: {
+        output: '1 nodes highlighted'
+      }
+    },
+    {
+      setup: 'highlight body --r',
+      check: {
+        input:  'highlight body --r',
+        hints:                    'egion [options]',
+        markup: 'VVVVVVVVVVVVVVVIII',
+        status: 'ERROR'
+      },
+      exec: {
+        output: 'Error: Too many arguments'
+      }
+    },
+    {
+      setup: 'highlight body --region',
+      check: {
+        input:  'highlight body --region',
+        hints:                         ' <selection> [options]',
+        markup: 'VVVVVVVVVVVVVVVIIIIIIII',
+        status: 'ERROR'
+      },
+      exec: {
+        output: 'Error: Value required for \'region\'.'
+      }
+    },
+    {
+      setup: 'highlight body --fi',
+      check: {
+        input:  'highlight body --fi',
+        hints:                     'll [options]',
+        markup: 'VVVVVVVVVVVVVVVIIII',
+        status: 'ERROR'
+      },
+      exec: {
+        output: 'Error: Too many arguments'
+      }
+    },
+    {
+      setup: 'highlight body --fill',
+      check: {
+        input:  'highlight body --fill',
+        hints:                       ' <string> [options]',
+        markup: 'VVVVVVVVVVVVVVVIIIIII',
+        status: 'ERROR'
+      },
+      exec: {
+        output: 'Error: Value required for \'fill\'.'
+      }
+    },
+    {
+      setup: 'highlight body --ke',
+      check: {
+        input:  'highlight body --ke',
+        hints:                     'ep [options]',
+        markup: 'VVVVVVVVVVVVVVVIIII',
+        status: 'ERROR'
+      },
+      exec: {
+        output: 'Error: Too many arguments'
+      }
+    },
+    {
+      setup: 'highlight body --keep',
+      check: {
+        input:  'highlight body --keep',
+        hints:                       ' [options]',
+        markup: 'VVVVVVVVVVVVVVVVVVVVV',
+        status: 'VALID'
+      },
+      exec: {
+        output: '1 nodes highlighted'
+      }
+    },
+    {
+      setup: 'highlight body --hideguides --showinfobar --showall --region ' +
+        'content --fill red --keep',
+      check: {
+        input:  'highlight body --hideguides --showinfobar --showall --region ' +
+          'content --fill red --keep',
+        hints: '',
+        markup: 'VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV' +
+          'VVVVVVVVVVVVVVVVVVVVVVVVV',
+        status: 'VALID'
+      },
+      exec: {
+        output: '1 nodes highlighted'
+      }
+    },
+    {
+      setup: 'highlight .item',
+      check: {
+        input:  'highlight .item',
+        hints:                 ' [options]',
+        markup: 'VVVVVVVVVVVVVVV',
+        status: 'VALID'
+      },
+      exec: {
+        output: '200 nodes matched, but only 100 nodes highlighted. Use ' +
+          '\'--showall\' to show all'
+      }
+    },
+    {
+      setup: 'highlight .item --showall',
+      check: {
+        input:  'highlight .item --showall',
+        hints:                           ' [options]',
+        markup: 'VVVVVVVVVVVVVVVVVVVVVVVVV',
+        status: 'VALID'
+      },
+      exec: {
+        output: '200 nodes highlighted'
+      }
+    },
+    {
+      setup: 'unhighlight',
+      check: {
+        input:  'unhighlight',
+        hints:  '',
+        markup: 'VVVVVVVVVVV',
+        status: 'VALID'
+      }
+    }
+  ]);
+
+  yield helpers.closeToolbar(options);
+  yield helpers.closeTab(options);
+}
new file mode 100644
--- /dev/null
+++ b/browser/devtools/commandline/test/browser_cmd_highlight_02.js
@@ -0,0 +1,43 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Tests that the highlight command actually creates a highlighter
+
+const TEST_PAGE = "data:text/html;charset=utf-8,<div></div>";
+
+function test() {
+  return Task.spawn(function*() {
+    let options = yield helpers.openTab(TEST_PAGE);
+    yield helpers.openToolbar(options);
+
+    info("highlighting the body node");
+    yield runCommand("highlight body", options);
+    is(getHighlighters().length, 1, "The highlighter element exists for body");
+
+    info("highlighting the div node");
+    yield runCommand("highlight div", options);
+    is(getHighlighters().length, 1, "The highlighter element exists for div");
+
+    info("highlighting the body node again, asking to keep the div");
+    yield runCommand("highlight body --keep", options);
+    is(getHighlighters().length, 2, "2 highlighter elements have been created");
+
+    info("unhighlighting all nodes");
+    yield runCommand("unhighlight", options);
+    is(getHighlighters().length, 0, "All highlighters have been removed");
+
+    yield helpers.closeToolbar(options);
+    yield helpers.closeTab(options);
+  }).then(finish, helpers.handleError);
+}
+
+function getHighlighters() {
+  return gBrowser.selectedBrowser.parentNode
+    .querySelectorAll(".highlighter-container");
+}
+
+function* runCommand(cmd, options) {
+  yield helpers.audit(options, [{ setup: cmd, exec: {} }]);
+}
--- a/browser/devtools/commandline/test/browser_cmd_inject.js
+++ b/browser/devtools/commandline/test/browser_cmd_inject.js
@@ -5,71 +5,65 @@
 
 const TEST_URI = 'http://example.com/browser/browser/devtools/commandline/'+
                  'test/browser_cmd_inject.html';
 
 function test() {
   helpers.addTabWithToolbar(TEST_URI, function(options) {
     return helpers.audit(options, [
       {
-        setup: 'inject',
+        setup:    'inject',
         check: {
           input:  'inject',
-          hints:  ' <library>',
           markup: 'VVVVVV',
+          hints:        ' <library>',
+          status: 'ERROR'
+        },
+      },
+      {
+        setup:    'inject j',
+        check: {
+          input:  'inject j',
+          markup: 'VVVVVVVI',
+          hints:          'Query',
           status: 'ERROR'
         },
       },
       {
-        setup: 'inject j',
+        setup: 'inject notauri',
         check: {
-          input:  'inject j',
-          hints:  'Query',
-          markup: 'VVVVVVVI',
-          status: 'ERROR'
-        },
+          input:  'inject notauri',
+          hints:                ' -> http://notauri/',
+          markup: 'VVVVVVVIIIIIII',
+          status: 'ERROR',
+          args: {
+            library: {
+              value: undefined,
+              status: 'INCOMPLETE'
+            }
+          }
+        }
       },
       {
-        setup: 'inject http://example.com/browser/browser/devtools/commandline/test/browser_cmd_inject.js',
+        setup:    'inject http://example.com/browser/browser/devtools/commandline/test/browser_cmd_inject.js',
         check: {
           input:  'inject http://example.com/browser/browser/devtools/commandline/test/browser_cmd_inject.js',
-          hints:  '',
           markup: 'VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV',
+          hints:                                                                                            '',
           status: 'VALID',
           args: {
             library: {
               value: function(library) {
-                is(library.type, 'string', 'inject type name');
-                is(library.string, 'http://example.com/browser/browser/devtools/commandline/test/browser_cmd_inject.js',
-                  'inject uri data');
+                is(library.type, 'url', 'inject type name');
+                is(library.url.origin, 'http://example.com', 'inject url hostname');
+                ok(library.url.path.indexOf('_inject.js') != -1, 'inject url path');
               },
               status: 'VALID'
             }
           }
         },
         exec: {
           output: [ /http:\/\/example.com\/browser\/browser\/devtools\/commandline\/test\/browser_cmd_inject.js loaded/ ]
         }
-      },
-      {
-        setup: 'inject notauri',
-        check: {
-          input:  'inject notauri',
-          hints:  '',
-          markup: 'VVVVVVVVVVVVVV',
-          status: 'VALID',
-          args: {
-            library: {
-              value: function(library) {
-                is(library.type, 'string', 'inject type name');
-                is(library.string, 'notauri', 'inject notauri data');
-              },
-              status: 'VALID'
-            }
-          }
-        },
-        exec: {
-          output: [ /Failed to load notauri - Invalid URI/ ]
-        }
       }
     ]);
   }).then(finish, helpers.handleError);
 }
--- a/browser/devtools/commandline/test/browser_gcli_types.js
+++ b/browser/devtools/commandline/test/browser_gcli_types.js
@@ -72,29 +72,29 @@ function forEachType(options, typeSpec, 
     }
     else if (name === 'array') {
       typeSpec.subtype = 'string';
     }
     else if (name === 'remote') {
       return;
     }
     else if (name === 'union') {
-      typeSpec.types = [{ name: "string" }];
+      typeSpec.alternatives = [{ name: 'string' }];
     }
 
     var type = types.createType(typeSpec);
     var reply = callback(type);
     return Promise.resolve(reply).then(function(value) {
       // Clean up
       delete typeSpec.name;
       delete typeSpec.requisition;
       delete typeSpec.data;
       delete typeSpec.delegateType;
       delete typeSpec.subtype;
-      delete typeSpec.types;
+      delete typeSpec.alternatives;
 
       return value;
     });
   });
 }
 
 exports.testDefault = function(options) {
   if (options.isNoDom) {
@@ -129,8 +129,25 @@ exports.testNullDefault = function(optio
 
   return forEachType(options, { defaultValue: null }, function(type) {
     var reply = type.stringify(null, context);
     return Promise.resolve(reply).then(function(str) {
       assert.is(str, '', 'stringify(null) for ' + type.name);
     });
   });
 };
+
+exports.testGetSpec = function(options) {
+  return forEachType(options, {}, function(type) {
+    if (type.name === 'param') {
+      return;
+    }
+
+    var spec = type.getSpec('cmd', 'param');
+    assert.ok(spec != null, 'non null spec for ' + type.name);
+
+    var str = JSON.stringify(spec);
+    assert.ok(str != null, 'serializable spec for ' + type.name);
+
+    var example = options.requisition.types.createType(spec);
+    assert.ok(example != null, 'creatable spec for ' + type.name);
+  });
+};
new file mode 100644
--- /dev/null
+++ b/browser/devtools/commandline/test/browser_gcli_union.js
@@ -0,0 +1,188 @@
+/*
+ * 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';
+// <INJECTED SOURCE:START>
+
+// THIS FILE IS GENERATED FROM SOURCE IN THE GCLI PROJECT
+// DO NOT EDIT IT DIRECTLY
+
+var exports = {};
+
+var TEST_URI = "data:text/html;charset=utf-8,<p id='gcli-input'>gcli-testUnion.js</p>";
+
+function test() {
+  return Task.spawn(function() {
+    let options = yield helpers.openTab(TEST_URI);
+    yield helpers.openToolbar(options);
+    gcli.addItems(mockCommands.items);
+
+    yield helpers.runTests(options, exports);
+
+    gcli.removeItems(mockCommands.items);
+    yield helpers.closeToolbar(options);
+    yield helpers.closeTab(options);
+  }).then(finish, helpers.handleError);
+}
+
+// <INJECTED SOURCE:END>
+
+// var assert = require('../testharness/assert');
+// var helpers = require('./helpers');
+
+exports.testDefault = function(options) {
+  return helpers.audit(options, [
+    {
+      setup:    'unionc1',
+      check: {
+        input:  'unionc1',
+        markup: 'VVVVVVV',
+        hints:         ' <first>',
+        status: 'ERROR',
+        args: {
+          first: {
+            value: undefined,
+            arg: '',
+            status: 'INCOMPLETE'
+          }
+        }
+      }
+    },
+    {
+      setup:    'unionc1 three',
+      check: {
+        input:  'unionc1 three',
+        markup: 'VVVVVVVVVVVVV',
+        hints:               '',
+        status: 'VALID',
+        args: {
+          first: {
+            value: function(data) {
+              assert.is(Object.keys(data).length, 2, 'union3 Object.keys');
+              assert.is(data.type, 'string', 'union3 val type');
+              assert.is(data.string, 'three', 'union3 val string');
+            },
+            arg: ' three',
+            status: 'VALID'
+          }
+        }
+      },
+      exec: {
+        output: [
+          /"type": ?"string"/,
+          /"string": ?"three"/
+        ]
+      },
+      post: function(output, text) {
+        var data = output.data.first;
+        assert.is(Object.keys(data).length, 2, 'union3 Object.keys');
+        assert.is(data.type, 'string', 'union3 val type');
+        assert.is(data.string, 'three', 'union3 val string');
+      }
+    },
+    {
+      setup:    'unionc1 one',
+      check: {
+        input:  'unionc1 one',
+        markup: 'VVVVVVVVVVV',
+        hints:             '',
+        status: 'VALID',
+        args: {
+          first: {
+            value: function(data) {
+              assert.is(Object.keys(data).length, 2, 'union1 Object.keys');
+              assert.is(data.type, 'selection', 'union1 val type');
+              assert.is(data.selection, 1, 'union1 val selection');
+            },
+            arg: ' one',
+            status: 'VALID'
+          }
+        }
+      },
+      exec: {
+        output: [
+          /"type": ?"selection"/,
+          /"selection": ?1/
+        ]
+      },
+      post: function(output, text) {
+        var data = output.data.first;
+        assert.is(Object.keys(data).length, 2, 'union1 Object.keys');
+        assert.is(data.type, 'selection', 'union1 val type');
+        assert.is(data.selection, 1, 'union1 val selection');
+      }
+    },
+    {
+      skipIf: options.isPhantomjs, // Phantom goes weird with predictions
+      setup:    'unionc1 5',
+      check: {
+        input:  'unionc1 5',
+        markup: 'VVVVVVVVV',
+        hints:           ' -> two',
+        predictions: [ 'two' ],
+        status: 'VALID',
+        args: {
+          first: {
+            value: function(data) {
+              assert.is(Object.keys(data).length, 2, 'union5 Object.keys');
+              assert.is(data.type, 'number', 'union5 val type');
+              assert.is(data.number, 5, 'union5 val number');
+            },
+            arg: ' 5',
+            status: 'VALID'
+          }
+        }
+      },
+      exec: {
+        output: [
+          /"type": ?"number"/,
+          /"number": ?5/
+        ]
+      },
+      post: function(output, text) {
+        var data = output.data.first;
+        assert.is(Object.keys(data).length, 2, 'union5 Object.keys');
+        assert.is(data.type, 'number', 'union5 val type');
+        assert.is(data.number, 5, 'union5 val number');
+      }
+    },
+    {
+      skipRemainingIf: options.isPhantomjs,
+      setup:    'unionc2 on',
+      check: {
+        input:  'unionc2 on',
+        hints:            'e',
+        markup: 'VVVVVVVVII',
+        current: 'first',
+        status: 'ERROR',
+        predictionsContains: [
+          'one',
+          'http://on/',
+          'https://on/'
+        ],
+        args: {
+          command: { name: 'unionc2' },
+          first: {
+            value: undefined,
+            arg: ' on',
+            status: 'INCOMPLETE',
+            message: 'Can\'t use \'on\'.'
+          },
+        }
+      }
+    }
+  ]);
+};
new file mode 100644
--- /dev/null
+++ b/browser/devtools/commandline/test/browser_gcli_url.js
@@ -0,0 +1,122 @@
+/*
+ * 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';
+// <INJECTED SOURCE:START>
+
+// THIS FILE IS GENERATED FROM SOURCE IN THE GCLI PROJECT
+// DO NOT EDIT IT DIRECTLY
+
+var exports = {};
+
+var TEST_URI = "data:text/html;charset=utf-8,<p id='gcli-input'>gcli-testUrl.js</p>";
+
+function test() {
+  return Task.spawn(function() {
+    let options = yield helpers.openTab(TEST_URI);
+    yield helpers.openToolbar(options);
+    gcli.addItems(mockCommands.items);
+
+    yield helpers.runTests(options, exports);
+
+    gcli.removeItems(mockCommands.items);
+    yield helpers.closeToolbar(options);
+    yield helpers.closeTab(options);
+  }).then(finish, helpers.handleError);
+}
+
+// <INJECTED SOURCE:END>
+
+// var assert = require('../testharness/assert');
+// var helpers = require('./helpers');
+
+exports.testDefault = function(options) {
+  return helpers.audit(options, [
+    {
+      skipRemainingIf: options.isPhantomjs,
+      setup:    'urlc',
+      check: {
+        input:  'urlc',
+        markup: 'VVVV',
+        hints:        ' <url>',
+        status: 'ERROR',
+        args: {
+          url: {
+            value: undefined,
+            arg: '',
+            status: 'INCOMPLETE'
+          }
+        }
+      }
+    },
+    {
+      setup:    'urlc example',
+      check: {
+        input:  'urlc example',
+        markup: 'VVVVVIIIIIII',
+        hints:              ' -> http://example/',
+        predictions: [
+          'http://example/',
+          'https://example/',
+          'http://localhost:9999/example'
+        ],
+        status: 'ERROR',
+        args: {
+          url: {
+            value: undefined,
+            arg: ' example',
+            status: 'INCOMPLETE'
+          }
+        }
+      },
+    },
+    {
+      setup:    'urlc example.com/',
+      check: {
+        input:  'urlc example.com/',
+        markup: 'VVVVVIIIIIIIIIIII',
+        hints:                   ' -> http://example.com/',
+        status: 'ERROR',
+        args: {
+          url: {
+            value: undefined,
+            arg: ' example.com/',
+            status: 'INCOMPLETE'
+          }
+        }
+      },
+    },
+    {
+      setup:    'urlc http://example.com/index?q=a#hash',
+      check: {
+        input:  'urlc http://example.com/index?q=a#hash',
+        markup: 'VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV',
+        hints:                                        '',
+        status: 'VALID',
+        args: {
+          url: {
+            value: function(data) {
+              assert.is(data.hash, '#hash', 'url hash');
+            },
+            arg: ' http://example.com/index?q=a#hash',
+            status: 'VALID'
+          }
+        }
+      },
+      exec: { output: /"url": ?/ }
+    }
+  ]);
+};
--- a/browser/devtools/commandline/test/mockCommands.js
+++ b/browser/devtools/commandline/test/mockCommands.js
@@ -19,46 +19,53 @@
 
 // THIS FILE IS GENERATED FROM SOURCE IN THE GCLI PROJECT
 // DO NOT EDIT IT DIRECTLY
 
 // <INJECTED SOURCE:END>
 
 
 var Promise = require('gcli/util/promise').Promise;
+var converters = require('gcli/converters/converters');
 var mockCommands = {};
 
 // We use an alias for exports here because this module is used in Firefox
 // mochitests where we don't have define/require
 
 /**
  * Registration and de-registration.
  */
 mockCommands.setup = function(requisition) {
   mockCommands.items.forEach(function(item) {
     if (item.item === 'command') {
       requisition.canon.addCommand(item);
     }
     else if (item.item === 'type') {
       requisition.types.addType(item);
     }
+    else if (item.item === 'converter') {
+      converters.addConverter(item);
+    }
     else {
       console.error('Ignoring item ', item);
     }
   });
 };
 
 mockCommands.shutdown = function(requisition) {
   mockCommands.items.forEach(function(item) {
     if (item.item === 'command') {
       requisition.canon.removeCommand(item);
     }
     else if (item.item === 'type') {
       requisition.types.removeType(item);
     }
+    else if (item.item === 'converter') {
+      converters.removeConverter(item);
+    }
     else {
       console.error('Ignoring item ', item);
     }
   });
 };
 
 function createExec(name) {
   return function(args, executionContext) {
@@ -66,16 +73,35 @@ function createExec(name) {
       return key + '=' + args[key];
     }).join(', ');
     return 'Exec: ' + name + ' ' + argsOut;
   };
 }
 
 mockCommands.items = [
   {
+    item: 'converter',
+    from: 'json',
+    to: 'string',
+    exec: function(json, context) {
+      return JSON.stringify(json, null, '  ');
+    }
+  },
+  {
+    item: 'converter',
+    from: 'json',
+    to: 'view',
+    exec: function(json, context) {
+      var html = JSON.stringify(json, null, '&#160;').replace(/\n/g, '<br/>');
+      return {
+        html: '<pre>' + html + '</pre>'
+      };
+    }
+  },
+  {
     item: 'type',
     name: 'optionType',
     parent: 'selection',
     lookup: [
       {
         name: 'option1',
         value: 'string'
       },
@@ -665,10 +691,79 @@ mockCommands.items = [
             });
           }
         }
       }
     ],
     exec: function(args, context) {
       return 'Test completed';
     }
+  },
+  {
+    item: 'command',
+    name: 'urlc',
+    params: [
+      {
+        name: 'url',
+        type: 'url'
+      }
+    ],
+    returnType: 'json',
+    exec: function(args, context) {
+      return args;
+    }
+  },
+  {
+    item: 'command',
+    name: 'unionc1',
+    params: [
+      {
+        name: 'first',
+        type: {
+          name: 'union',
+          alternatives: [
+            {
+              name: 'selection',
+              lookup: [
+                { name: 'one', value: 1 },
+                { name: 'two', value: 2 },
+              ]
+            },
+            'number',
+            { name: 'string' }
+          ]
+        }
+      }
+    ],
+    returnType: 'json',
+    exec: function(args, context) {
+      return args;
+    }
+  },
+  {
+    item: 'command',
+    name: 'unionc2',
+    params: [
+      {
+        name: 'first',
+        type: {
+          name: 'union',
+          alternatives: [
+            {
+              name: 'selection',
+              lookup: [
+                { name: 'one', value: 1 },
+                { name: 'two', value: 2 },
+              ]
+            },
+            {
+              name: 'url'
+            }
+          ]
+        }
+      }
+    ],
+    returnType: 'json',
+    exec: function(args, context) {
+      return args;
+    }
   }
 ];
--- a/browser/devtools/eyedropper/commands.js
+++ b/browser/devtools/eyedropper/commands.js
@@ -36,15 +36,15 @@ exports.items = [{
   },
   exec: function(args, context) {
     let chromeWindow = context.environment.chromeWindow;
     let target = context.environment.target;
 
     let dropper = EyedropperManager.createInstance(chromeWindow);
     dropper.open();
 
-    eventEmitter.emit("changed", target.tab);
+    eventEmitter.emit("changed", { target: target });
 
     dropper.once("destroy", () => {
-      eventEmitter.emit("changed", target.tab);
+      eventEmitter.emit("changed", { target: target });
     });
   }
 }];
--- a/browser/devtools/markupview/markup-view.js
+++ b/browser/devtools/markupview/markup-view.js
@@ -180,47 +180,46 @@ MarkupView.prototype = {
       this.getContainer(this._hoveredNode).hovered = false;
     }
     this._hoveredNode = null;
   },
 
   /**
    * Show the box model highlighter on a given node front
    * @param {NodeFront} nodeFront The node to show the highlighter for
-   * @param {Object} options Options for the highlighter
    * @return a promise that resolves when the highlighter for this nodeFront is
    * shown, taking into account that there could already be highlighter requests
    * queued up
    */
-  _showBoxModel: function(nodeFront, options={}) {
-    return this._inspector.toolbox.highlighterUtils.highlightNodeFront(nodeFront, options);
+  _showBoxModel: function(nodeFront) {
+    return this._inspector.toolbox.highlighterUtils.highlightNodeFront(nodeFront);
   },
 
   /**
    * Hide the box model highlighter on a given node front
    * @param {NodeFront} nodeFront The node to hide the highlighter for
    * @param {Boolean} forceHide See toolbox-highlighter-utils/unhighlight
    * @return a promise that resolves when the highlighter for this nodeFront is
    * hidden, taking into account that there could already be highlighter requests
    * queued up
    */
   _hideBoxModel: function(forceHide) {
     return this._inspector.toolbox.highlighterUtils.unhighlight(forceHide);
   },
 
   _briefBoxModelTimer: null,
-  _brieflyShowBoxModel: function(nodeFront, options) {
+  _brieflyShowBoxModel: function(nodeFront) {
     let win = this._frame.contentWindow;
 
     if (this._briefBoxModelTimer) {
       win.clearTimeout(this._briefBoxModelTimer);
       this._briefBoxModelTimer = null;
     }
 
-    this._showBoxModel(nodeFront, options);
+    this._showBoxModel(nodeFront);
 
     this._briefBoxModelTimer = this._frame.contentWindow.setTimeout(() => {
       this._hideBoxModel();
     }, NEW_SELECTION_HIGHLIGHTER_TIMER);
   },
 
   template: function(aName, aDest, aOptions={stack: "markup-view.xhtml"}) {
     let node = this.doc.getElementById("template-" + aName).cloneNode(true);
@@ -315,17 +314,17 @@ MarkupView.prototype = {
     if (this._hoveredNode && this._hoveredNode !== selection.nodeFront) {
       this.getContainer(this._hoveredNode).hovered = false;
       this._hoveredNode = null;
     }
 
     let done = this._inspector.updating("markup-view");
     if (selection.isNode()) {
       if (this._shouldNewSelectionBeHighlighted()) {
-        this._brieflyShowBoxModel(selection.nodeFront, {});
+        this._brieflyShowBoxModel(selection.nodeFront);
       }
 
       this.showNode(selection.nodeFront, true).then(() => {
         if (selection.reason !== "treepanel") {
           this.markNodeAsSelected(selection.nodeFront);
         }
         done();
       }, (e) => {
--- a/browser/devtools/projecteditor/chrome/content/projecteditor-loader.js
+++ b/browser/devtools/projecteditor/chrome/content/projecteditor-loader.js
@@ -3,58 +3,76 @@ const {devtools} = Cu.import("resource:/
 const {FileUtils} = Cu.import("resource://gre/modules/FileUtils.jsm", {});
 const {NetUtil} = Cu.import("resource://gre/modules/NetUtil.jsm", {});
 const require = devtools.require;
 const promise = require("projecteditor/helpers/promise");
 const ProjectEditor = require("projecteditor/projecteditor");
 
 const SAMPLE_PATH = buildTempDirectoryStructure();
 const SAMPLE_NAME = "DevTools Content Application Name";
-const SAMPLE_PROJECT_URL = "http://mozilla.org";
+const SAMPLE_PROJECT_URL = "data:text/html;charset=utf-8,<body><h1>Project Overview</h1></body>";
 const SAMPLE_ICON = "chrome://browser/skin/devtools/tool-debugger.svg";
 
 /**
  * Create a workspace for working on projecteditor, available at
  * chrome://browser/content/devtools/projecteditor-loader.xul.
  * This emulates the integration points that the app manager uses.
  */
+let appManagerEditor;
+
+// Log a message to the project overview URL to make development easier
+function log(msg) {
+  if (!appManagerEditor) {
+    return;
+  }
+
+  let doc = appManagerEditor.iframe.contentDocument;
+  let el = doc.createElement("p");
+  el.textContent = msg;
+  doc.body.appendChild(el);
+}
+
 document.addEventListener("DOMContentLoaded", function onDOMReady(e) {
   document.removeEventListener("DOMContentLoaded", onDOMReady, false);
   let iframe = document.getElementById("projecteditor-iframe");
   window.projecteditor = ProjectEditor.ProjectEditor(iframe);
 
-  projecteditor.on("onEditorCreated", (editor) => {
-    console.log("editor created: " + editor);
+  projecteditor.on("onEditorCreated", (editor, a) => {
+    log("editor created: " + editor);
+    if (editor.label === "app-manager") {
+      appManagerEditor = editor;
+      appManagerEditor.on("load", function foo() {
+        appManagerEditor.off("load", foo);
+        log("Working on: " + SAMPLE_PATH);
+      })
+    }
   });
   projecteditor.on("onEditorDestroyed", (editor) => {
-    console.log("editor destroyed: " + editor);
+    log("editor destroyed: " + editor);
   });
   projecteditor.on("onEditorSave", (editor, resource) => {
-    console.log("editor saved: " + editor, resource.path);
+    log("editor saved: " + editor, resource.path);
   });
   projecteditor.on("onTreeSelected", (resource) => {
-    console.log("tree selected: " + resource.path);
+    log("tree selected: " + resource.path);
   });
   projecteditor.on("onEditorLoad", (editor) => {
-    console.log("editor loaded: " + editor);
+    log("editor loaded: " + editor);
   });
   projecteditor.on("onEditorActivated", (editor) => {
-    console.log("editor focused: " + editor);
+    log("editor focused: " + editor);
   });
   projecteditor.on("onEditorDeactivated", (editor) => {
-    console.log("editor blur: " + editor);
+    log("editor blur: " + editor);
   });
   projecteditor.on("onEditorChange", (editor) => {
-    console.log("editor changed: " + editor);
-  });
-  projecteditor.on("onEditorCursorActivity", (editor) => {
-    console.log("editor cursor activity: " + editor);
+    log("editor changed: " + editor);
   });
   projecteditor.on("onCommand", (cmd) => {
-    console.log("Command: " + cmd);
+    log("Command: " + cmd);
   });
 
   projecteditor.loaded.then(() => {
     projecteditor.setProjectToAppPath(SAMPLE_PATH, {
       name: SAMPLE_NAME,
       iconUrl: SAMPLE_ICON,
       projectOverviewURL: SAMPLE_PROJECT_URL,
       validationStatus: "valid"
--- a/browser/devtools/projecteditor/lib/plugins/app-manager/app-project-editor.js
+++ b/browser/devtools/projecteditor/lib/plugins/app-manager/app-project-editor.js
@@ -22,23 +22,35 @@ var AppProjectEditor = Class({
   },
 
   destroy: function() {
     this.elt.remove();
     this.elt = null;
   },
 
   load: function(resource) {
-    this.elt.textContent = "";
     let {appManagerOpts} = this.host.project;
-    let iframe = this.iframe = this.elt.ownerDocument.createElement("iframe");
-    iframe.setAttribute("flex", "1");
-    iframe.setAttribute("src", appManagerOpts.projectOverviewURL);
-    this.elt.appendChild(iframe);
+
+    // Only load the frame the first time it is selected
+    if (!this.iframe || this.iframe.getAttribute("src") !== appManagerOpts.projectOverviewURL) {
+
+      this.elt.textContent = "";
+      let iframe = this.iframe = this.elt.ownerDocument.createElement("iframe");
+      let iframeLoaded = this.iframeLoaded = promise.defer();
 
-    // Wait for other `appended` listeners before emitting load.
-    this.appended.then(() => {
+      iframe.addEventListener("load", function onLoad() {
+        iframe.removeEventListener("load", onLoad);
+        iframeLoaded.resolve();
+      });
+
+      iframe.setAttribute("flex", "1");
+      iframe.setAttribute("src", appManagerOpts.projectOverviewURL);
+      this.elt.appendChild(iframe);
+
+    }
+
+    promise.all([this.iframeLoaded.promise, this.appended]).then(() => {
       this.emit("load");
     });
   }
 });
 
 exports.AppProjectEditor = AppProjectEditor;
--- a/browser/devtools/projecteditor/lib/plugins/image-view/image-editor.js
+++ b/browser/devtools/projecteditor/lib/plugins/image-view/image-editor.js
@@ -14,28 +14,37 @@ var ImageEditor = Class({
 
   initialize: function(document) {
     ItchEditor.prototype.initialize.apply(this, arguments);
     this.label = "image";
     this.appended = promise.resolve();
   },
 
   load: function(resource) {
-    let image = this.doc.createElement("image");
+    this.elt.innerHTML = "";
+    let image = this.image = this.doc.createElement("image");
     image.className = "editor-image";
     image.setAttribute("src", resource.uri);
 
     let box1 = this.doc.createElement("box");
     box1.appendChild(image);
 
     let box2 = this.doc.createElement("box");
     box2.setAttribute("flex", 1);
 
     this.elt.appendChild(box1);
     this.elt.appendChild(box2);
 
     this.appended.then(() => {
       this.emit("load");
     });
+  },
+
+  destroy: function() {
+    if (this.image) {
+      this.image.remove();
+      this.image = null;
+    }
   }
+
 });
 
 exports.ImageEditor = ImageEditor;
--- a/browser/devtools/projecteditor/lib/shells.js
+++ b/browser/devtools/projecteditor/lib/shells.js
@@ -35,32 +35,30 @@ var Shell = Class({
     this.elt.shell = this;
 
     let constructor = this._editorTypeForResource();
 
     this.editor = constructor(this.doc, this.host);
     this.editor.shell = this;
     this.editorAppended = this.editor.appended;
 
-    let loadDefer = promise.defer();
     this.editor.on("load", () => {
-      loadDefer.resolve();
+      this.editorDeferred.resolve();
     });
-
-    this.editorLoaded = loadDefer.promise;
-
     this.elt.appendChild(this.editor.elt);
   },
 
   /**
    * Start loading the resource.  The 'load' event happens as
    * a result of this function, so any listeners to 'editorAppended'
    * need to be added before calling this.
    */
   load: function() {
+    this.editorDeferred = promise.defer();
+    this.editorLoaded = this.editorDeferred.promise;
     this.editor.load(this.resource);
   },
 
   /**
    * Destroy the shell and its associated editor
    */
   destroy: function() {
     this.editor.destroy();
@@ -188,16 +186,18 @@ var ShellDeck = Class({
   selectShell: function(shell) {
     // Don't fire another activate if this is already the active shell
     if (this._activeShell != shell) {
       if (this._activeShell) {
         emit(this, "editor-deactivated", this._activeShell.editor, this._activeShell.resource);
       }
       this.deck.selectedPanel = shell.elt;
       this._activeShell = shell;
+
+      shell.load();
       shell.editorLoaded.then(() => {
         // Handle case where another shell has been requested before this
         // one is finished loading.
         if (this._activeShell === shell) {
           emit(this, "editor-activated", shell.editor, shell.resource);
         }
       });
     }
--- a/browser/devtools/projecteditor/test/browser.ini
+++ b/browser/devtools/projecteditor/test/browser.ini
@@ -1,14 +1,17 @@
 [DEFAULT]
 subsuite = devtools
 support-files =
   head.js
   helper_homepage.html
+  helper_edits.js
 
 [browser_projecteditor_app_options.js]
 [browser_projecteditor_delete_file.js]
 [browser_projecteditor_editing_01.js]
+[browser_projecteditor_editors_image.js]
+[browser_projecteditor_external_change.js]
 [browser_projecteditor_immediate_destroy.js]
 [browser_projecteditor_init.js]
 [browser_projecteditor_new_file.js]
 [browser_projecteditor_stores.js]
 [browser_projecteditor_tree_selection.js]
--- a/browser/devtools/projecteditor/test/browser_projecteditor_editing_01.js
+++ b/browser/devtools/projecteditor/test/browser_projecteditor_editing_01.js
@@ -1,68 +1,34 @@
 /* vim: set ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
  http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
+loadHelperScript("helper_edits.js");
+
 // Test ProjectEditor basic functionality
 let test = asyncTest(function*() {
   let projecteditor = yield addProjectEditorTabForTempDirectory();
   let TEMP_PATH = projecteditor.project.allPaths()[0];
 
   is (getTempFile("").path, TEMP_PATH, "Temp path is set correctly.");
 
   ok (projecteditor.currentEditor, "There is an editor for projecteditor");
   let resources = projecteditor.project.allResources();
 
-  resources.forEach((r, i) => {
-    console.log("Resource detected", r.path, i);
-  });
-
-  let stylesCss = resources.filter(r=>r.basename === "styles.css")[0];
-  yield selectFile(projecteditor, stylesCss);
-  yield testEditFile(projecteditor, getTempFile("css/styles.css").path, "body,html { color: orange; }");
-
-  let indexHtml = resources.filter(r=>r.basename === "index.html")[0];
-  yield selectFile(projecteditor, indexHtml);
-  yield testEditFile(projecteditor, getTempFile("index.html").path, "<h1>Changed Content Again</h1>");
-
-  let license = resources.filter(r=>r.basename === "LICENSE")[0];
-  yield selectFile(projecteditor, license);
-  yield testEditFile(projecteditor, getTempFile("LICENSE").path, "My new license");
-
-  let readmeMd = resources.filter(r=>r.basename === "README.md")[0];
-  yield selectFile(projecteditor, readmeMd);
-  yield testEditFile(projecteditor, getTempFile("README.md").path, "My new license");
-
-  let scriptJs = resources.filter(r=>r.basename === "script.js")[0];
-  yield selectFile(projecteditor, scriptJs);
-  yield testEditFile(projecteditor, getTempFile("js/script.js").path, "alert('hi')");
-
-  let vectorSvg = resources.filter(r=>r.basename === "vector.svg")[0];
-  yield selectFile(projecteditor, vectorSvg);
-  yield testEditFile(projecteditor, getTempFile("img/icons/vector.svg").path, "<svg></svg>");
+  for (let data of helperEditData) {
+    info ("Processing " + data.path);
+    let resource = resources.filter(r=>r.basename === data.basename)[0];
+    yield selectFile(projecteditor, resource);
+    yield testEditFile(projecteditor, getTempFile(data.path).path, data.newContent);
+  }
 });
 
-function selectFile (projecteditor, resource) {
-  ok (resource && resource.path, "A valid resource has been passed in for selection " + (resource && resource.path));
-  projecteditor.projectTree.selectResource(resource);
-
-  if (resource.isDir) {
-    return;
-  }
-
-  let [editorActivated] = yield promise.all([
-    onceEditorActivated(projecteditor)
-  ]);
-
-  is (editorActivated, projecteditor.currentEditor,  "Editor has been activated for " + resource.path);
-}
-
 function testEditFile(projecteditor, filePath, newData) {
   info ("Testing file editing for: " + filePath);
 
   let initialData = yield getFileData(filePath);
   let editor = projecteditor.currentEditor;
   let resource = projecteditor.resourceFor(editor);
   let viewContainer= projecteditor.projectTree.getViewContainer(resource);
   let originalTreeLabel = viewContainer.label.textContent;
new file mode 100644
--- /dev/null
+++ b/browser/devtools/projecteditor/test/browser_projecteditor_editors_image.js
@@ -0,0 +1,68 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+loadHelperScript("helper_edits.js");
+
+// Test ProjectEditor image editor functionality
+let test = asyncTest(function*() {
+  let projecteditor = yield addProjectEditorTabForTempDirectory();
+  let TEMP_PATH = projecteditor.project.allPaths()[0];
+
+  is (getTempFile("").path, TEMP_PATH, "Temp path is set correctly.");
+
+  ok (projecteditor.currentEditor, "There is an editor for projecteditor");
+  let resources = projecteditor.project.allResources();
+
+  let helperImageData = [
+    {
+      basename: "16x16.png",
+      path: "img/icons/16x16.png"
+    },
+    {
+      basename: "32x32.png",
+      path: "img/icons/32x32.png"
+    },
+    {
+      basename: "128x128.png",
+      path: "img/icons/128x128.png"
+    },
+  ];
+
+  for (let data of helperImageData) {
+    info ("Processing " + data.path);
+    let resource = resources.filter(r=>r.basename === data.basename)[0];
+    yield selectFile(projecteditor, resource);
+    yield testEditor(projecteditor, getTempFile(data.path).path);
+  }
+});
+
+function testEditor(projecteditor, filePath) {
+  info ("Testing file editing for: " + filePath);
+
+  let editor = projecteditor.currentEditor;
+  let resource = projecteditor.resourceFor(editor);
+
+  is (resource.path, filePath, "Resource path is set correctly");
+
+  let images = editor.elt.querySelectorAll("image");
+  is (images.length, 1, "There is one image inside the editor");
+  is (images[0], editor.image, "The image property is set correctly with the DOM");
+  is (editor.image.getAttribute("src"), resource.uri, "The image has the resource URL");
+
+  info ("Selecting another resource, then reselecting this one");
+  projecteditor.projectTree.selectResource(resource.store.root);
+  yield onceEditorActivated(projecteditor);
+  projecteditor.projectTree.selectResource(resource);
+  yield onceEditorActivated(projecteditor);
+
+  let editor = projecteditor.currentEditor;
+  let images = editor.elt.querySelectorAll("image");
+  ok (images.length, 1, "There is one image inside the editor");
+  is (images[0], editor.image, "The image property is set correctly with the DOM");
+  is (editor.image.getAttribute("src"), resource.uri, "The image has the resource URL");
+
+  info ("Finished checking saving for " + filePath);
+}
new file mode 100644
--- /dev/null
+++ b/browser/devtools/projecteditor/test/browser_projecteditor_external_change.js
@@ -0,0 +1,52 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+loadHelperScript("helper_edits.js");
+
+// Test ProjectEditor reaction to external changes (made outside of the)
+// editor.
+let test = asyncTest(function*() {
+  let projecteditor = yield addProjectEditorTabForTempDirectory();
+  let TEMP_PATH = projecteditor.project.allPaths()[0];
+
+  is (getTempFile("").path, TEMP_PATH, "Temp path is set correctly.");
+
+  ok (projecteditor.currentEditor, "There is an editor for projecteditor");
+  let resources = projecteditor.project.allResources();
+
+  for (let data of helperEditData) {
+    info ("Processing " + data.path);
+    let resource = resources.filter(r=>r.basename === data.basename)[0];
+    yield selectFile(projecteditor, resource);
+    yield testChangeFileExternally(projecteditor, getTempFile(data.path).path, data.newContent);
+  }
+});
+
+function testChangeFileExternally(projecteditor, filePath, newData) {
+  info ("Testing file external changes for: " + filePath);
+
+  let editor = projecteditor.currentEditor;
+  let resource = projecteditor.resourceFor(editor);
+  let initialData = yield getFileData(filePath);
+
+  is (resource.path, filePath, "Resource path is set correctly");
+  is (editor.editor.getText(), initialData, "Editor is loaded with correct file contents");
+
+  info ("Editor has been selected, writing to file externally");
+  yield writeToFile(resource.path, newData);
+
+  info ("Selecting another resource, then reselecting this one");
+  projecteditor.projectTree.selectResource(resource.store.root);
+  yield onceEditorActivated(projecteditor);
+  projecteditor.projectTree.selectResource(resource);
+  yield onceEditorActivated(projecteditor);
+
+  let editor = projecteditor.currentEditor;
+  info ("Checking to make sure the editor is now populated correctly");
+  is (editor.editor.getText(), newData, "Editor has been updated with correct file contents");
+
+  info ("Finished checking saving for " + filePath);
+}
--- a/browser/devtools/projecteditor/test/head.js
+++ b/browser/devtools/projecteditor/test/head.js
@@ -67,16 +67,32 @@ function addTab(url) {
       def.resolve(tab);
     }, content);
   }, true);
   content.location = url;
 
   return def.promise;
 }
 
+/**
+ * Some tests may need to import one or more of the test helper scripts.
+ * A test helper script is simply a js file that contains common test code that
+ * is either not common-enough to be in head.js, or that is located in a separate
+ * directory.
+ * The script will be loaded synchronously and in the test's scope.
+ * @param {String} filePath The file path, relative to the current directory.
+ *                 Examples:
+ *                 - "helper_attributes_test_runner.js"
+ *                 - "../../../commandline/test/helpers.js"
+ */
+function loadHelperScript(filePath) {
+  let testDir = gTestPath.substr(0, gTestPath.lastIndexOf("/"));
+  Services.scriptloader.loadSubScript(testDir + "/" + filePath, this);
+}
+
 function addProjectEditorTabForTempDirectory() {
   TEMP_PATH = buildTempDirectoryStructure();
   let CUSTOM_OPTS = {
     name: "Test",
     iconUrl: "chrome://browser/skin/devtools/tool-options.svg",
     projectOverviewURL: SAMPLE_WEBAPP_URL
   };
 
@@ -168,32 +184,37 @@ function buildTempDirectoryStructure() {
   FileUtils.getFile("TmpD", ["ProjectEditor", "img", "icons", "128x128.png"]).createUnique(Components.interfaces.nsIFile.NORMAL_FILE_TYPE, FileUtils.PERMS_FILE);
   FileUtils.getFile("TmpD", ["ProjectEditor", "img", "icons", "vector.svg"]).createUnique(Components.interfaces.nsIFile.NORMAL_FILE_TYPE, FileUtils.PERMS_FILE);
 
   return TEMP_DIR.path;
 }
 
 // https://developer.mozilla.org/en-US/Add-ons/Code_snippets/File_I_O#Writing_to_a_file
 function writeToFile(file, data) {
-  console.log("Writing to file: " + file.path, file.exists());
+  if (typeof file === "string") {
+    file = new FileUtils.File(file);
+  }
+  info("Writing to file: " + file.path + " (exists? " + file.exists() + ")");
   let defer = promise.defer();
   var ostream = FileUtils.openSafeFileOutputStream(file);
 
   var converter = Components.classes["@mozilla.org/intl/scriptableunicodeconverter"].
                   createInstance(Components.interfaces.nsIScriptableUnicodeConverter);
   converter.charset = "UTF-8";
   var istream = converter.convertToInputStream(data);
 
   // The last argument (the callback) is optional.
   NetUtil.asyncCopy(istream, ostream, function(status) {
     if (!Components.isSuccessCode(status)) {
       // Handle error!
       info("ERROR WRITING TEMP FILE", status);
     }
+    defer.resolve();
   });
+  return defer.promise;
 }
 
 // This is used when setting up the test.
 // You should typically use the async version of this, writeToFile.
 // https://developer.mozilla.org/en-US/Add-ons/Code_snippets/File_I_O#More
 function writeToFileSync(file, data) {
   // file is nsIFile, data is a string
   var foStream = Components.classes["@mozilla.org/network/file-output-stream;1"].
@@ -216,18 +237,20 @@ function writeToFileSync(file, data) {
 
 function getTempFile(path) {
   let parts = ["ProjectEditor"];
   parts = parts.concat(path.split("/"));
   return FileUtils.getFile("TmpD", parts);
 }
 
 // https://developer.mozilla.org/en-US/Add-ons/Code_snippets/File_I_O#Writing_to_a_file
-function* getFileData(path) {
-  let file = new FileUtils.File(path);
+function* getFileData(file) {
+  if (typeof file === "string") {
+    file = new FileUtils.File(file);
+  }
   let def = promise.defer();
 
   NetUtil.asyncFetch(file, function(inputStream, status) {
     if (!Components.isSuccessCode(status)) {
       info("ERROR READING TEMP FILE", status);
     }
 
     // Detect if an empty file is loaded
new file mode 100644
--- /dev/null
+++ b/browser/devtools/projecteditor/test/helper_edits.js
@@ -0,0 +1,53 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+let helperEditData = [
+  {
+    basename: "styles.css",
+    path: "css/styles.css",
+    newContent: "body,html { color: orange; }"
+  },
+  {
+    basename: "index.html",
+    path: "index.html",
+    newContent: "<h1>Changed Content Again</h1>"
+  },
+  {
+    basename: "LICENSE",
+    path: "LICENSE",
+    newContent: "My new license"
+  },
+  {
+    basename: "README.md",
+    path: "README.md",
+    newContent: "My awesome readme"
+  },
+  {
+    basename: "script.js",
+    path: "js/script.js",
+    newContent: "alert('hi')"
+  },
+  {
+    basename: "vector.svg",
+    path: "img/icons/vector.svg",
+    newContent: "<svg></svg>"
+  },
+];
+
+function selectFile (projecteditor, resource) {
+  ok (resource && resource.path, "A valid resource has been passed in for selection " + (resource && resource.path));
+  projecteditor.projectTree.selectResource(resource);
+
+  if (resource.isDir) {
+    return;
+  }
+
+  let [editorActivated] = yield promise.all([
+    onceEditorActivated(projecteditor)
+  ]);
+
+  is (editorActivated, projecteditor.currentEditor,  "Editor has been activated for " + resource.path);
+}
--- a/browser/devtools/responsivedesign/responsivedesign.jsm
+++ b/browser/devtools/responsivedesign/responsivedesign.jsm
@@ -208,17 +208,18 @@ function ResponsiveUI(aWindow, aTab)
   this.browser.addEventListener("load", this.bound_onPageLoad, true);
   this.browser.addEventListener("unload", this.bound_onPageUnload, true);
 
   if (this.browser.contentWindow.document &&
       this.browser.contentWindow.document.readyState == "complete") {
     this.onPageLoad();
   }
 
-  ResponsiveUIManager.emit("on", this.tab, this);
+  // E10S: We should be using target here. See bug 1028234
+  ResponsiveUIManager.emit("on", { tab: this.tab });
 }
 
 ResponsiveUI.prototype = {
   _transitionsEnabled: true,
   get transitionsEnabled() this._transitionsEnabled,
   set transitionsEnabled(aValue) {
     this._transitionsEnabled = aValue;
     if (aValue && !this._resizing && this.stack.hasAttribute("responsivemode")) {
@@ -300,17 +301,18 @@ ResponsiveUI.prototype = {
     this.container.removeAttribute("responsivemode");
     this.stack.removeAttribute("responsivemode");
 
     delete this.docShell;
     delete this.tab.__responsiveUI;
     if (this.touchEventHandler)
       this.touchEventHandler.stop();
     this._telemetry.toolClosed("responsive");
-    ResponsiveUIManager.emit("off", this.tab, this);
+    // E10S: We should be using target here. See bug 1028234
+    ResponsiveUIManager.emit("off", { tab: this.tab });
   },
 
   /**
    * Handle keypressed.
    *
    * @param aEvent
    */
   onKeypress: function RUI_onKeypress(aEvent) {
--- a/browser/devtools/shared/DeveloperToolbar.jsm
+++ b/browser/devtools/shared/DeveloperToolbar.jsm
@@ -102,46 +102,75 @@ let CommandUtils = {
 
         // Ignore invalid commands
         let command = requisition.commandAssignment.value;
         if (command == null) {
           throw new Error("No command '" + typed + "'");
         }
         if (command.buttonId != null) {
           button.id = command.buttonId;
+          if (command.buttonClass != null) {
+            button.className = command.buttonClass;
+          }
         }
-        if (command.buttonClass != null) {
-          button.className = command.buttonClass;
+        else {
+          button.setAttribute("text-as-image", "true");
+          button.setAttribute("label", command.name);
+          button.className = "devtools-toolbarbutton";
         }
         if (command.tooltipText != null) {
           button.setAttribute("tooltiptext", command.tooltipText);
         }
         else if (command.description != null) {
           button.setAttribute("tooltiptext", command.description);
         }
 
         button.addEventListener("click", () => {
           requisition.updateExec(typed);
         }, false);
 
         // Allow the command button to be toggleable
         if (command.state) {
           button.setAttribute("autocheck", false);
-          let onChange = (event, eventTab) => {
-            if (eventTab == target.tab) {
-              if (command.state.isChecked(target)) {
-                button.setAttribute("checked", true);
+
+          /**
+           * The onChange event should be called with an event object that
+           * contains a target property which specifies which target the event
+           * applies to. For legacy reasons the event object can also contain
+           * a tab property.
+           */
+          let onChange = (eventName, ev) => {
+            if (ev.target == target || ev.tab == target.tab) {
+
+              let updateChecked = (checked) => {
+                if (checked) {
+                  button.setAttribute("checked", true);
+                }
+                else if (button.hasAttribute("checked")) {
+                  button.removeAttribute("checked");
+                }
+              };
+
+              // isChecked would normally be synchronous. An annoying quirk
+              // of the 'csscoverage toggle' command forces us to accept a
+              // promise here, but doing Promise.resolve(reply).then(...) here
+              // makes this async for everyone, which breaks some tests so we
+              // treat non-promise replies separately to keep then synchronous.
+              let reply = command.state.isChecked(target);
+              if (typeof reply.then == "function") {
+                reply.then(updateChecked, console.error);
               }
-              else if (button.hasAttribute("checked")) {
-                button.removeAttribute("checked");
+              else {
+                updateChecked(reply);
               }
             }
           };
+
           command.state.onChange(target, onChange);
-          onChange(null, target.tab);
+          onChange("", { target: target });
           document.defaultView.addEventListener("unload", () => {
             command.state.offChange(target, onChange);
           }, false);
         }
 
         requisition.clear();
 
         return button;
--- a/browser/devtools/styleinspector/rule-view.js
+++ b/browser/devtools/styleinspector/rule-view.js
@@ -1618,16 +1618,20 @@ CssRuleView.prototype = {
    */
   _createEditors: function() {
     // Run through the current list of rules, attaching
     // their editors in order.  Create editors if needed.
     let lastInheritedSource = "";
     let seenPseudoElement = false;
     let seenNormalElement = false;
 
+    if (!this._elementStyle.rules) {
+      return;
+    }
+
     for (let rule of this._elementStyle.rules) {
       if (rule.domRule.system) {
         continue;
       }
 
       // Only print header for this element if there are pseudo elements
       if (seenPseudoElement && !seenNormalElement && !rule.pseudoElement) {
         seenNormalElement = true;
--- a/browser/devtools/tilt/tilt.js
+++ b/browser/devtools/tilt/tilt.js
@@ -139,17 +139,18 @@ Tilt.prototype = {
     // make sure the visualizer object was initialized properly
     if (!this.visualizers[id].isInitialized()) {
       this.destroy(id);
       this.failureCallback && this.failureCallback();
       return;
     }
 
     this.lastInstanceId = id;
-    this.emit("change", this.chromeWindow.gBrowser.selectedTab);
+    // E10S: We should be using target here. See bug 1028234
+    this.emit("change", { tab: this.chromeWindow.gBrowser.selectedTab });
     Services.obs.notifyObservers(contentWindow, TILT_NOTIFICATIONS.INITIALIZING, null);
   },
 
   /**
    * Starts destroying a specific instance of the visualizer.
    *
    * @param {String} aId
    *                 the identifier of the instance in the visualizers array
@@ -196,17 +197,18 @@ Tilt.prototype = {
   {
     let contentWindow = this.visualizers[aId].presenter.contentWindow;
     this.visualizers[aId].removeOverlay();
     this.visualizers[aId].cleanup();
     this.visualizers[aId] = null;
 
     this._isDestroying = false;
     this.chromeWindow.gBrowser.selectedBrowser.focus();
-    this.emit("change", this.chromeWindow.gBrowser.selectedTab);
+    // E10S: We should be using target here. See bug 1028234
+    this.emit("change", { tab: this.chromeWindow.gBrowser.selectedTab });
     Services.obs.notifyObservers(contentWindow, TILT_NOTIFICATIONS.DESTROYED, null);
   },
 
   /**
    * Handles the event fired when a tab is selected.
    */
   _onTabSelect: function T__onTabSelect()
   {
--- a/browser/devtools/webconsole/console-commands.js
+++ b/browser/devtools/webconsole/console-commands.js
@@ -11,17 +11,17 @@ const { gDevTools } = require("resource:
 const eventEmitter = new EventEmitter();
 
 gDevTools.on("toolbox-ready", (e, toolbox) => {
   if (!toolbox.target) {
     return;
   }
 
   let fireChangeForTab = () => {
-    eventEmitter.emit("changed", toolbox.target.tab);
+    eventEmitter.emit("changed", { target: toolbox.target });
   };
 
   toolbox.on("split-console", fireChangeForTab);
   toolbox.on("select", fireChangeForTab);
 
   toolbox.once("destroyed", () => {
     toolbox.off("split-console", fireChangeForTab);
     toolbox.off("select", fireChangeForTab);
@@ -33,17 +33,17 @@ exports.items = [
     name: 'splitconsole',
     hidden: true,
     buttonId: "command-button-splitconsole",
     buttonClass: "command-button command-button-invertable",
     tooltipText: gcli.lookup("splitconsoleTooltip"),
     state: {
       isChecked: function(target) {
         let toolbox = gDevTools.getToolbox(target);
-        return toolbox && toolbox.splitConsole;
+        return !!(toolbox && toolbox.splitConsole);
       },
       onChange: function(target, changeHandler) {
         eventEmitter.on("changed", changeHandler);
       },
       offChange: function(target, changeHandler) {
         eventEmitter.off("changed", changeHandler);
       },
     },
--- a/browser/devtools/webconsole/test/browser_webconsole_closure_inspection.js
+++ b/browser/devtools/webconsole/test/browser_webconsole_closure_inspection.js
@@ -32,17 +32,17 @@ function test()
         panelWin.once(panelWin.EVENTS.FETCHED_SCOPES, (aEvent, aPacket) => {
           ok(true, "Scopes were fetched");
           toolbox.selectTool("webconsole").then(() => consoleOpened(hud));
           deferred.resolve();
         });
 
         let button = content.document.querySelector("button");
         ok(button, "button element found");
-        button.click();
+        EventUtils.synthesizeMouseAtCenter(button, {}, content);
 
         return deferred.promise;
       });
     });
   }, true);
 }
 
 function consoleOpened(hud)
@@ -62,17 +62,17 @@ function consoleOpened(hud)
 }
 
 function onExecuteGetName(aResults)
 {
   let clickable = aResults[0].clickableElements[0];
   ok(clickable, "clickable object found");
 
   gJSTerm.once("variablesview-fetched", onGetNameFetch);
-  EventUtils.synthesizeMouse(clickable, 2, 2, {}, gWebConsole.iframeWindow)
+  EventUtils.synthesizeMouse(clickable, 2, 2, {}, gWebConsole.iframeWindow);
 }
 
 function onGetNameFetch(aEvent, aVar)
 {
   gVariablesView = aVar._variablesView;
   ok(gVariablesView, "variables view object");
 
   findVariableViewProperties(aVar, [
@@ -82,10 +82,10 @@ function onGetNameFetch(aEvent, aVar)
 
 function onExpandClosure(aResults)
 {
   let prop = aResults[0].matchedProp;
   ok(prop, "matched the name property in the variables view");
 
   gVariablesView.window.focus();
   gJSTerm.once("sidebar-closed", finishTest);
-  EventUtils.synthesizeKey("VK_ESCAPE", {}, gVariablesView.window);
+  EventUtils.synthesizeKey("VK_ESCAPE", {});
 }
--- a/browser/locales/en-US/chrome/browser/devtools/gclicommands.properties
+++ b/browser/locales/en-US/chrome/browser/devtools/gclicommands.properties
@@ -123,16 +123,123 @@ screenshotErrorCopying=Error occurred wh
 
 # LOCALIZATION NOTE (screenshotCopied) Text displayed to user when the
 # screenshot is successfully copied to the clipboard.
 screenshotCopied=Copied to clipboard.
 
 # LOCALIZATION NOTE (screenshotTooltip) Text displayed as tooltip for screenshot button in devtools ToolBox.
 screenshotTooltip=Take a fullpage screenshot
 
+# LOCALIZATION NOTE (highlightDesc) A very short description of the
+# 'highlight' command. See highlightManual for a fuller description of what
+# it does. This string is designed to be shown in a menu alongside the
+# command name, which is why it should be as short as possible.
+highlightDesc=Highlight nodes
+
+# LOCALIZATION NOTE (highlightManual) A fuller description of the 'highlight'
+# command, displayed when the user asks for help on what it does.
+highlightManual=Highlight nodes that match a selector on the page
+
+# LOCALIZATION NOTE (highlightSelectorDesc) A very short string to describe
+# the 'selector' parameter to the 'highlight' command, which is displayed in
+# a dialog when the user is using this command.
+highlightSelectorDesc=CSS selector
+
+# LOCALIZATION NOTE (highlightSelectorManual) A fuller description of the
+# 'selector' parameter to the 'highlight' command, displayed when the user
+# asks for help on what it does.
+highlightSelectorManual=The CSS selector used to match nodes in the page
+
+# LOCALIZATION NOTE (highlightOptionsDesc) The title of a set of options to
+# the 'highlight' command, displayed as a heading to the list of option.
+highlightOptionsDesc=Options
+
+# LOCALIZATION NOTE (highlightHideGuidesDesc) A very short string to describe
+# the 'hideguides' option parameter to the 'highlight' command, which is
+# displayed in a dialog when the user is using this command.
+highlightHideGuidesDesc=Hide guides
+
+# LOCALIZATION NOTE (highlightHideGuidesManual) A fuller description of the
+# 'hideguides' option parameter to the 'highlight' command, displayed when the
+# user asks for help on what it does.
+highlightHideGuidesManual=Hide the guides around the highlighted node
+
+# LOCALIZATION NOTE (highlightShowInfoBarDesc) A very short string to describe
+# the 'showinfobar' option parameter to the 'highlight' command, which is
+# displayed in a dialog when the user is using this command.
+highlightShowInfoBarDesc=Show the node infobar
+
+# LOCALIZATION NOTE (highlightShowInfoBarManual) A fuller description of the
+# 'showinfobar' option parameter to the 'highlight' command, displayed when the
+# user asks for help on what it does.
+highlightShowInfoBarManual=Show the infobar above the highlighted node (the infobar displays the tagname, attributes and dimension)
+
+# LOCALIZATION NOTE (highlightShowAllDesc) A very short string to describe
+# the 'showall' option parameter to the 'highlight' command, which is
+# displayed in a dialog when the user is using this command.
+highlightShowAllDesc=Show all matches
+
+# LOCALIZATION NOTE (highlightShowAllManual) A fuller description of the
+# 'showall' option parameter to the 'highlight' command, displayed when the
+# user asks for help on what it does.
+highlightShowAllManual=If too many nodes match the selector, only the first 100 will be shown to avoid slowing down the page too much. Use this option to show all matches instead
+
+# LOCALIZATION NOTE (highlightRegionDesc) A very short string to describe the
+# 'region' option parameter to the 'highlight' command, which is displayed in a
+# dialog when the user is using this command.
+highlightRegionDesc=Box model region
+
+# LOCALIZATION NOTE (highlightRegionManual) A fuller description of the 'region'
+# option parameter to the 'highlight' command, displayed when the user asks for
+# help on what it does.
+highlightRegionManual=Which box model region should be highlighted: 'content', 'padding', 'border' or 'margin'
+
+# LOCALIZATION NOTE (highlightFillDesc) A very short string to describe the
+# 'fill' option parameter to the 'highlight' command, which is displayed in a
+# dialog when the user is using this command.
+highlightFillDesc=Fill style
+
+# LOCALIZATION NOTE (highlightFillManual) A fuller description of the 'fill'
+# option parameter to the 'highlight' command, displayed when the user asks for
+# help on what it does.
+highlightFillManual=Override the default region fill style with a custom color
+
+# LOCALIZATION NOTE (highlightKeepDesc) A very short string to describe the
+# 'keep' option parameter to the 'highlight' command, which is displayed in a
+# dialog when the user is using this command.
+highlightKeepDesc=Keep existing highlighters
+
+# LOCALIZATION NOTE (highlightKeepManual) A fuller description of the 'keep'
+# option parameter to the 'highlight' command, displayed when the user asks for
+# help on what it does.
+highlightKeepManual=By default, existing highlighters are hidden when running the command, unless this option is set
+
+# LOCALIZATION NOTE (highlightOutputConfirm) A confirmation message for the
+# 'highlight' command, displayed to the user once the command has been entered,
+# informing the user how many nodes have been highlighted successfully and how
+# to turn highlighting off
+highlightOutputConfirm=%1$S nodes highlighted
+
+# LOCALIZATION NOTE (highlightOutputMaxReached) A confirmation message for the
+# 'highlight' command, displayed to the user once the command has been entered,
+# informing the user how many nodes have been highlighted successfully and that
+# some nodes could not be highlighted due to the maximum number of nodes being
+# reached, and how to turn highlighting off
+highlightOutputMaxReached=%1$S nodes matched, but only %2$S nodes highlighted. Use '--showall' to show all
+
+# LOCALIZATION NOTE (unhighlightDesc) A very short description of the
+# 'unhighlight' command. See unhighlightManual for a fuller description of what
+# it does. This string is designed to be shown in a menu alongside the
+# command name, which is why it should be as short as possible.
+unhighlightDesc=Unhighlight all nodes
+
+# LOCALIZATION NOTE (unhighlightManual) A fuller description of the 'unhighlight'
+# command, displayed when the user asks for help on what it does.
+unhighlightManual=Unhighlight all nodes previously highlighted with the 'highlight' command
+
 # LOCALIZATION NOTE (restartBrowserDesc) A very short description of the
 # 'restart' command. This string is designed to be shown in a menu alongside the
 # command name, which is why it should be as short as possible.
 # The argument (%1$S) is the browser name.
 restartBrowserDesc=Restart %1$S
 
 # LOCALIZATION NOTE (restartBrowserNocacheDesc) A very short string to
 # describe the 'nocache' parameter to the 'restart' command, which is
--- a/browser/modules/AboutHome.jsm
+++ b/browser/modules/AboutHome.jsm
@@ -236,17 +236,17 @@ let AboutHome = {
       };
 
       if (AboutHomeUtils.showKnowYourRights) {
         // Set pref to indicate we've shown the notification.
         let currentVersion = Services.prefs.getIntPref("browser.rights.version");
         Services.prefs.setBoolPref("browser.rights." + currentVersion + ".shown", true);
       }
 
-      if (target) {
+      if (target && target.messageManager) {
         target.messageManager.sendAsyncMessage("AboutHome:Update", data);
       } else {
         let mm = Cc["@mozilla.org/globalmessagemanager;1"].getService(Ci.nsIMessageListenerManager);
         mm.broadcastAsyncMessage("AboutHome:Update", data);
       }
     }).then(null, function onError(x) {
       Cu.reportError("Error in AboutHome.sendAboutHomeData: " + x);
     });
--- a/browser/themes/shared/devtools/highlighter.inc.css
+++ b/browser/themes/shared/devtools/highlighter.inc.css
@@ -34,16 +34,17 @@ svg|polygon.box-model-margin {
 }
 
 svg|line.box-model-guide-top,
 svg|line.box-model-guide-right,
 svg|line.box-model-guide-bottom,
 svg|line.box-model-guide-left {
   stroke: #08C;
   stroke-dasharray: 5 3;
+  shape-rendering: crispEdges;
 }
 
 /* Highlighter - Node Infobar */
 
 .highlighter-nodeinfobar {
   color: hsl(216,33%,97%);
   border-radius: 3px;
   background: hsl(214,13%,24%) no-repeat padding-box;
--- a/browser/themes/shared/devtools/toolbars.inc.css
+++ b/browser/themes/shared/devtools/toolbars.inc.css
@@ -69,16 +69,22 @@
   min-height: 2em;
 }
 
 .devtools-toolbarbutton:not([label]),
 .devtools-toolbarbutton[text-as-image] {
   min-width: 32px;
 }
 
+#toolbox-buttons .devtools-toolbarbutton[text-as-image] {
+  -moz-padding-start: 5px;
+  -moz-padding-end: 5px;
+  min-width: inherit;
+}
+
 .devtools-toolbarbutton:not([label]) > .toolbarbutton-text {
   display: none;
 }
 
 .devtools-toolbar .devtools-toolbarbutton {
   border-width: 0;
 }
 
@@ -119,68 +125,75 @@
   padding: 0 3px;
 }
 
 .theme-dark .devtools-menulist,
 .theme-dark .devtools-toolbarbutton {
   color: #a9bacb; /* Body text - high contrast */
   background-color: #343c45; /* Toolbars */
   border-color: rgba(0, 0, 0, .4); /* Splitters */
-  color: inherit;
 }
 .theme-light .devtools-menulist,
 .theme-light .devtools-toolbarbutton {
   color: #292e33; /* Body text - high contrast */
   background-color: #f0f1f2; /* Toolbars */
   border-color: rgba(170, 170, 170, .5); /* Splitters */
 }
 
 /* Text-only buttons */
-.theme-light .devtools-toolbarbutton[label]:not([text-as-image]):not([type=menu-button]) {
+.theme-light .devtools-toolbarbutton[label]:not([text-as-image]):not([type=menu-button]),
+.theme-light #toolbox-buttons .devtools-toolbarbutton[text-as-image] {
   background-color: rgba(170, 170, 170, .2); /* Splitter */
 }
-.theme-dark .devtools-toolbarbutton[label]:not([text-as-image]):not([type=menu-button]) {
+.theme-dark .devtools-toolbarbutton[label]:not([text-as-image]):not([type=menu-button]),
+.theme-dark #toolbox-buttons .devtools-toolbarbutton[text-as-image] {
   background-color: rgba(0, 0, 0, .2); /* Splitter */
 }
 
 /* Button States */
 .theme-dark .devtools-toolbarbutton:hover,
+.theme-dark #toolbox-buttons .devtools-toolbarbutton[text-as-image]:hover,
 .theme-dark .devtools-toolbarbutton[label]:not([text-as-image]):not([type=menu-button]):hover {
   background: rgba(0, 0, 0, .3); /* Splitters */
 }
 .theme-light .devtools-toolbarbutton:hover,
+.theme-light #toolbox-buttons .devtools-toolbarbutton[text-as-image]:hover,
 .theme-light .devtools-toolbarbutton[label]:not([text-as-image]):not([type=menu-button]):hover {
   background: rgba(170, 170, 170, .3); /* Splitters */
 }
 
 .theme-dark .devtools-toolbarbutton:hover:active,
+.theme-dark #toolbox-buttons .devtools-toolbarbutton[text-as-image]:hover:active,
 .theme-dark .devtools-toolbarbutton[label]:not([text-as-image]):not([type=menu-button]):hover:active {
   background: rgba(0, 0, 0, .4); /* Splitters */
 }
 .theme-light .devtools-toolbarbutton:hover:active,
+.theme-light #toolbox-buttons .devtools-toolbarbutton[text-as-image]:hover:active,
 .theme-light .devtools-toolbarbutton[label]:not([text-as-image]):not([type=menu-button]):hover:active {
   background: rgba(170, 170, 170, .4); /* Splitters */
 }
 
 /* Menu type buttons and checked states */
 .theme-dark .devtools-menulist[open=true],
 .theme-dark .devtools-toolbarbutton[open=true],
 .theme-dark .devtools-toolbarbutton[open=true]:hover,
 .theme-dark .devtools-toolbarbutton[open=true]:hover:active,
 .theme-dark .devtools-toolbarbutton[checked=true],
-.theme-dark .devtools-toolbarbutton[checked=true]:hover {
+.theme-dark .devtools-toolbarbutton[checked=true]:hover,
+.theme-dark #toolbox-buttons .devtools-toolbarbutton[text-as-image][checked] {
   background: rgba(29, 79, 115, .7); /* Select highlight blue */
   color: #f5f7fa;
 }
 .theme-light .devtools-menulist[open=true],
 .theme-light .devtools-toolbarbutton[open=true],
 .theme-light .devtools-toolbarbutton[open=true]:hover,
 .theme-light .devtools-toolbarbutton[open=true]:hover:active,
 .theme-light .devtools-toolbarbutton[checked=true],
-.theme-light .devtools-toolbarbutton[checked=true]:hover {
+.theme-light .devtools-toolbarbutton[checked=true]:hover,
+.theme-light #toolbox-buttons .devtools-toolbarbutton[text-as-image][checked] {
   background: rgba(76, 158, 217, .2); /* Select highlight blue */
 }
 
 .devtools-option-toolbarbutton {
   -moz-appearance: none;
   list-style-image: url("chrome://browser/skin/devtools/tool-options.svg");
   background: none;
   opacity: .8;
--- a/build/pymake/make.py
+++ b/build/pymake/make.py
@@ -13,23 +13,22 @@ import gc
 
 if __name__ == '__main__':
   if 'TINDERBOX_OUTPUT' in os.environ:
     # When building on mozilla build slaves, execute mozmake instead. Until bug
     # 978211, this is the easiest, albeit hackish, way to do this.
     import subprocess
     mozmake = os.path.join(os.path.dirname(__file__), '..', '..',
         'mozmake.exe')
-    if os.path.exists(mozmake):
-        cmd = [mozmake]
-        cmd.extend(sys.argv[1:])
-        shell = os.environ.get('SHELL')
-        if shell and not shell.lower().endswith('.exe'):
-            cmd += ['SHELL=%s.exe' % shell]
-        sys.exit(subprocess.call(cmd))
+    cmd = [mozmake]
+    cmd.extend(sys.argv[1:])
+    shell = os.environ.get('SHELL')
+    if shell and not shell.lower().endswith('.exe'):
+        cmd += ['SHELL=%s.exe' % shell]
+    sys.exit(subprocess.call(cmd))
 
   sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0)
   sys.stderr = os.fdopen(sys.stderr.fileno(), 'w', 0)
 
   gc.disable()
 
   pymake.command.main(sys.argv[1:], os.environ, os.getcwd(), cb=sys.exit)
   pymake.process.ParallelContext.spin()
--- a/config/baseconfig.mk
+++ b/config/baseconfig.mk
@@ -20,37 +20,36 @@ endif
 endif
 
 # We do magic with OBJ_SUFFIX in config.mk, the following ensures we don't
 # manually use it before config.mk inclusion
 _OBJ_SUFFIX := $(OBJ_SUFFIX)
 OBJ_SUFFIX = $(error config/config.mk needs to be included before using OBJ_SUFFIX)
 
 ifeq ($(HOST_OS_ARCH),WINNT)
-# We only support building with pymake or a non-msys gnu make version
+# We only support building with a non-msys gnu make version
 # strictly above 4.0.
-ifndef .PYMAKE
+ifdef .PYMAKE
+$(error Pymake is no longer supported. Please upgrade to MozillaBuild 1.9 or newer and build with 'mach' or 'mozmake')
+endif
+
 ifeq (a,$(firstword a$(subst /, ,$(abspath .))))
 $(error MSYS make is not supported)
 endif
 # 4.0- happens to be greater than 4.0, lower than the mozmake version,
 # and lower than 4.0.1 or 4.1, whatever next version of gnu make will
 # be released.
 ifneq (4.0-,$(firstword $(sort 4.0- $(MAKE_VERSION))))
 $(error Make version too old. Only versions strictly greater than 4.0 are supported.)
 endif
-endif
+
 ifdef INCLUDED_AUTOCONF_MK
 ifeq (a,$(firstword a$(subst /, ,$(srcdir))))
 $(error MSYS-style srcdir are not supported for Windows builds.)
 endif
 endif
 endif # WINNT
 
-ifdef .PYMAKE
-include_deps = $(eval $(if $(2),,-)includedeps $(1))
-else
 include_deps = $(eval $(if $(2),,-)include $(1))
-endif
 
 ifndef INCLUDED_AUTOCONF_MK
 default::
 endif
--- a/dom/camera/GonkCameraControl.cpp
+++ b/dom/camera/GonkCameraControl.cpp
@@ -207,29 +207,39 @@ nsGonkCameraControl::~nsGonkCameraContro
 
 nsresult
 nsGonkCameraControl::SetConfigurationInternal(const Configuration& aConfig)
 {
   DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__);
 
   nsresult rv;
 
-  switch (aConfig.mMode) {
-    case kPictureMode:
-      rv = SetPictureConfiguration(aConfig);
-      break;
+  {
+    ICameraControlParameterSetAutoEnter set(this);
+
+    switch (aConfig.mMode) {
+      case kPictureMode:
+        rv = SetPictureConfiguration(aConfig);
+        break;
+
+      case kVideoMode:
+        rv = SetVideoConfiguration(aConfig);
+        break;
 
-    case kVideoMode:
-      rv = SetVideoConfiguration(aConfig);
-      break;
+      default:
+        MOZ_ASSERT_UNREACHABLE("Unanticipated camera mode in SetConfigurationInternal()");
+        rv = NS_ERROR_FAILURE;
+        break;
+    }
 
-    default:
-      MOZ_ASSERT_UNREACHABLE("Unanticipated camera mode in SetConfigurationInternal()");
-      rv = NS_ERROR_FAILURE;
-      break;
+    nsresult rv = Set(CAMERA_PARAM_RECORDINGHINT,
+                      aConfig.mMode == kVideoMode);
+    if (NS_FAILED(rv)) {
+      DOM_CAMERA_LOGE("Failed to set recording hint (0x%x)\n", rv);
+    }
   }
 
   DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__);
   NS_ENSURE_SUCCESS(rv, rv);
 
   mCurrentConfiguration.mMode = aConfig.mMode;
   mCurrentConfiguration.mRecorderProfile = aConfig.mRecorderProfile;
   if (aConfig.mMode == kVideoMode) {
@@ -244,18 +254,22 @@ nsresult
 nsGonkCameraControl::SetConfigurationImpl(const Configuration& aConfig)
 {
   DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__);
   MOZ_ASSERT(NS_GetCurrentThread() == mCameraThread);
 
   // Stop any currently running preview
   nsresult rv = PausePreview();
   if (NS_FAILED(rv)) {
-    // warn, but plow ahead
-    NS_WARNING("PausePreview() in SetConfigurationImpl() failed");
+    DOM_CAMERA_LOGW("PausePreview() in SetConfigurationImpl() failed (0x%x)\n", rv);
+    if (rv == NS_ERROR_NOT_INITIALIZED) {
+      // If there no hardware available, nothing else we try will work,
+      // so bail out here.
+      return rv;
+    }
   }
 
   DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__);
   rv = SetConfigurationInternal(aConfig);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     StopPreviewImpl();
     return rv;
   }
@@ -273,21 +287,16 @@ nsGonkCameraControl::SetPictureConfigura
   // remove any existing recorder profile
   mRecorderProfile = nullptr;
 
   nsresult rv = SetPreviewSize(aConfig.mPreviewSize);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
-  rv = PushParameters();
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
   mParams.Get(CAMERA_PARAM_PREVIEWFRAMERATE, mPreviewFps);
 
   DOM_CAMERA_LOGI("picture mode preview: wanted %ux%u, got %ux%u (%u fps)\n",
     aConfig.mPreviewSize.width, aConfig.mPreviewSize.height,
     mCurrentConfiguration.mPreviewSize.width, mCurrentConfiguration.mPreviewSize.height,
     mPreviewFps);
 
   return NS_OK;
@@ -1051,26 +1060,18 @@ nsGonkCameraControl::StopRecordingImpl()
 
   mRecorder->stop();
   mRecorder = nullptr;
   OnRecorderStateChange(CameraControlListener::kRecorderStopped);
 
   {
     ICameraControlParameterSetAutoEnter set(this);
 
-    // clear the recording hint once stopped because some drivers will solely
-    // check this flag to determine the mode, despite its actual internal state,
-    // thus causing problems when taking pictures
-    nsresult rv = mParams.Set(CAMERA_PARAM_RECORDINGHINT, false);
-    if (NS_FAILED(rv)) {
-      DOM_CAMERA_LOGE("Failed to set recording hint (0x%x)\n", rv);
-    }
-
     if (mAutoFlashModeOverridden) {
-      rv = mParams.Set(CAMERA_PARAM_FLASHMODE, NS_LITERAL_STRING("auto"));
+      nsresult rv = Set(CAMERA_PARAM_FLASHMODE, NS_LITERAL_STRING("auto"));
       if (NS_FAILED(rv)) {
         DOM_CAMERA_LOGE("Failed to set flash mode (0x%x)\n", rv);
       }
     }
   }
 
   // notify DeviceStorage that the new video file is closed and ready
   return NS_DispatchToMainThread(new RecordingComplete(mVideoFile));
@@ -1243,17 +1244,17 @@ nsGonkCameraControl::OnTakePictureError(
 }
 
 nsresult
 nsGonkCameraControl::SetPreviewSize(const Size& aSize)
 {
   MOZ_ASSERT(NS_GetCurrentThread() == mCameraThread);
 
   nsTArray<Size> previewSizes;
-  nsresult rv = mParams.Get(CAMERA_PARAM_SUPPORTED_PREVIEWSIZES, previewSizes);
+  nsresult rv = Get(CAMERA_PARAM_SUPPORTED_PREVIEWSIZES, previewSizes);
   if (NS_FAILED(rv)) {
     DOM_CAMERA_LOGE("Camera failed to return any preview sizes (0x%x)\n", rv);
     return rv;
   }
 
   Size best;
   rv  = GetSupportedSize(aSize, previewSizes, best);
   if (NS_FAILED(rv)) {
@@ -1264,40 +1265,40 @@ nsGonkCameraControl::SetPreviewSize(cons
 
   // Some camera drivers will ignore our preview size if it's larger
   // than the currently set video recording size, so we need to set
   // the video size here as well, just in case.
   if (best.width > mLastRecorderSize.width || best.height > mLastRecorderSize.height) {
     SetVideoSize(best);
   }
   mCurrentConfiguration.mPreviewSize = best;
-  return mParams.Set(CAMERA_PARAM_PREVIEWSIZE, best);
+  return Set(CAMERA_PARAM_PREVIEWSIZE, best);
 }
 
 nsresult
 nsGonkCameraControl::SetVideoSize(const Size& aSize)
 {
   MOZ_ASSERT(NS_GetCurrentThread() == mCameraThread);
 
   nsTArray<Size> videoSizes;
-  nsresult rv = mParams.Get(CAMERA_PARAM_SUPPORTED_VIDEOSIZES, videoSizes);
+  nsresult rv = Get(CAMERA_PARAM_SUPPORTED_VIDEOSIZES, videoSizes);
   if (NS_FAILED(rv)) {
     DOM_CAMERA_LOGE("Camera failed to return any video sizes (0x%x)\n", rv);
     return rv;
   }
 
   Size best;
   rv = GetSupportedSize(aSize, videoSizes, best);
   if (NS_FAILED(rv)) {
     DOM_CAMERA_LOGE("Failed to find a supported video size, requested size %dx%d",
         aSize.width, aSize.height);
     return rv;
   }
   mLastRecorderSize = best;
-  return mParams.Set(CAMERA_PARAM_VIDEOSIZE, best);
+  return Set(CAMERA_PARAM_VIDEOSIZE, best);
 }
 
 nsresult
 nsGonkCameraControl::GetSupportedSize(const Size& aSize,
                                       const nsTArray<Size>& supportedSizes,
                                       Size& best)
 {
   nsresult rv = NS_ERROR_INVALID_ARG;
@@ -1393,30 +1394,24 @@ nsGonkCameraControl::SetupVideoMode(cons
     }
 
     rv = SetPreviewSize(size);
     if (NS_FAILED(rv)) {
       DOM_CAMERA_LOGE("Failed to set video mode preview size (0x%x)\n", rv);
       return rv;
     }
 
-    rv = mParams.Set(CAMERA_PARAM_PREVIEWFRAMERATE, fps);
+    rv = Set(CAMERA_PARAM_PREVIEWFRAMERATE, fps);
     if (NS_FAILED(rv)) {
       DOM_CAMERA_LOGE("Failed to set video mode frame rate (0x%x)\n", rv);
       return rv;
     }
+  }
 
-    rv = PushParameters();
-    if (NS_FAILED(rv)) {
-      DOM_CAMERA_LOGE("Failed to set video mode settings (0x%x)\n", rv);
-      return rv;
-    }
-
-    mPreviewFps = fps;
-  }
+  mPreviewFps = fps;
   return NS_OK;
 }
 
 class GonkRecorderListener : public IMediaRecorderClient
 {
 public:
   GonkRecorderListener(nsGonkCameraControl* aCameraControl)
     : mCameraControl(aCameraControl)
--- a/dom/mobilemessage/src/gonk/MmsService.js
+++ b/dom/mobilemessage/src/gonk/MmsService.js
@@ -278,38 +278,16 @@ MmsConnection.prototype = {
       this.radioInterface.deactivateDataCallByType("mms");
     }
   },
 
   init: function() {
     Services.obs.addObserver(this, kNetworkConnStateChangedTopic,
                              false);
     Services.obs.addObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
-
-    this.connected = this.radioInterface.getDataCallStateByType("mms") ==
-      Ci.nsINetworkInterface.NETWORK_STATE_CONNECTED;
-    // If the MMS network is connected during the initialization, it means the
-    // MMS network must share the same APN with the mobile network by default.
-    // Under this case, |networkManager.active| should keep the mobile network,
-    // which is supposed be an instance of |nsIRilNetworkInterface| for sure.
-    if (this.connected) {
-      let networkManager =
-        Cc["@mozilla.org/network/manager;1"].getService(Ci.nsINetworkManager);
-      let activeNetwork = networkManager.active;
-
-      let rilNetwork = activeNetwork.QueryInterface(Ci.nsIRilNetworkInterface);
-      if (rilNetwork.serviceId != this.serviceId) {
-        if (DEBUG) debug("Sevice ID between active/MMS network doesn't match.");
-        return;
-      }
-
-      // Set up the MMS APN setting based on the connected MMS network,
-      // which is going to be used for the HTTP requests later.
-      this.setApnSetting(rilNetwork);
-    }
   },
 
   /**
    * Return the roaming status of voice call.
    *
    * @return true if voice call is roaming.
    */
   isVoiceRoaming: function() {
@@ -468,25 +446,23 @@ MmsConnection.prototype = {
       case kNetworkConnStateChangedTopic: {
         // The network for MMS connection must be nsIRilNetworkInterface.
         if (!(subject instanceof Ci.nsIRilNetworkInterface)) {
           return;
         }
 
         // Check if the network state change belongs to this service.
         let network = subject.QueryInterface(Ci.nsIRilNetworkInterface);
-        if (network.serviceId != this.serviceId) {
+        if (network.serviceId != this.serviceId ||
+            network.type != Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE_MMS) {
           return;
         }
 
-        // We only need to capture the state change of MMS network. Using
-        // |network.state| isn't reliable due to the possibilty of shared APN.
         let connected =
-          this.radioInterface.getDataCallStateByType("mms") ==
-            Ci.nsINetworkInterface.NETWORK_STATE_CONNECTED;
+          network.state == Ci.nsINetworkInterface.NETWORK_STATE_CONNECTED;
 
         // Return if the MMS network state doesn't change, where the network
         // state change can come from other non-MMS networks.
         if (connected == this.connected) {
           return;
         }
 
         this.connected = connected;
--- a/dom/telephony/test/marionette/head.js
+++ b/dom/telephony/test/marionette/head.js
@@ -107,22 +107,22 @@ let emulator = (function() {
    */
   function clearCalls() {
     log("Clear existing calls.");
 
     // Hang up all calls.
     let hangUpPromises = [];
 
     for (let call of telephony.calls) {
-      log(".. hangUp " + call.number);
+      log(".. hangUp " + call.id.number);
       hangUpPromises.push(hangUp(call));
     }
 
     for (let call of conference.calls) {
-      log(".. hangUp " + call.number);
+      log(".. hangUp " + call.id.number);
       hangUpPromises.push(hangUp(call));
     }
 
     return Promise.all(hangUpPromises)
       .then(() => {
         return emulator.run("gsm clear");
       })
       .then(waitForNoCall);
--- a/mobile/android/base/BrowserApp.java
+++ b/mobile/android/base/BrowserApp.java
@@ -8,19 +8,19 @@ package org.mozilla.gecko;
 import java.io.File;
 import java.io.FileNotFoundException;
 import java.net.URLEncoder;
 import java.util.EnumSet;
 import java.util.List;
 import java.util.Locale;
 import java.util.Vector;
 
-import org.json.JSONArray;
 import org.json.JSONException;
 import org.json.JSONObject;
+
 import org.mozilla.gecko.DynamicToolbar.PinReason;
 import org.mozilla.gecko.DynamicToolbar.VisibilityTransition;
 import org.mozilla.gecko.GeckoProfileDirectories.NoMozillaDirectoryException;
 import org.mozilla.gecko.animation.PropertyAnimator;
 import org.mozilla.gecko.animation.ViewHelper;
 import org.mozilla.gecko.db.BrowserContract.Combined;
 import org.mozilla.gecko.db.BrowserContract.ReadingListItems;
 import org.mozilla.gecko.db.BrowserDB;
@@ -785,26 +785,31 @@ public class BrowserApp extends GeckoApp
                     Log.e(LOGTAG, "error building json arguments");
                 }
                 GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("Feeds:Subscribe", args.toString()));
             }
             return true;
         }
 
         if (itemId == R.id.add_search_engine) {
+            // This can be selected from either the browser menu or the contextmenu, depending on the size and version (v11+) of the phone.
             Tab tab = Tabs.getInstance().getSelectedTab();
             if (tab != null && tab.hasOpenSearch()) {
                 JSONObject args = new JSONObject();
                 try {
                     args.put("tabId", tab.getId());
                 } catch (JSONException e) {
                     Log.e(LOGTAG, "error building json arguments");
                     return true;
                 }
                 GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("SearchEngines:Add", args.toString()));
+
+                if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
+                    Telemetry.sendUIEvent(TelemetryContract.Event.ACTION, TelemetryContract.Method.CONTEXT_MENU, "add_search_engine");
+                }
             }
             return true;
         }
 
         if (itemId == R.id.copyurl) {
             Tab tab = Tabs.getInstance().getSelectedTab();
             if (tab != null) {
                 String url = tab.getURL();
--- a/mobile/android/chrome/content/browser.js
+++ b/mobile/android/chrome/content/browser.js
@@ -6815,16 +6815,17 @@ var SearchEngines = {
       }
     };
     SelectionHandler.addAction({
       id: "search_add_action",
       label: Strings.browser.GetStringFromName("contextmenu.addSearchEngine"),
       icon: "drawable://ab_add_search_engine",
       selector: filter,
       action: function(aElement) {
+        UITelemetry.addEvent("action.1", "actionbar", null, "add_search_engine");
         SearchEngines.addEngine(aElement);
       }
     });
   },
 
   uninit: function uninit() {
     Services.obs.removeObserver(this, "SearchEngines:Add");
     Services.obs.removeObserver(this, "SearchEngines:GetVisible");
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..826e5340843246732f7001c836f4a3ec37681c86
GIT binary patch
literal 3409
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
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..980e787310a9a1391715ba8d3b9bd9bfbc2b5b5c
GIT binary patch
literal 3382
zc$@(?4axF}P)<h;3K|Lk000e1NJLTq001fg001fo1^@s6#ly*400009a7bBm000XU
z000XU0RWnu7ytkYPiaF#P*7-ZbZ>KLZ*U+<Lqi~Na&Km7Y-Iodc-oy)XH-+^7Crag
z^g>IBfRsybQWXdwQbLP>6p<z>Aqfylh#{fb6;Z(vMMVS~$e@S=j*ftg6;Uh<iVD~V
z<RPMtgQJLw%KPDaqifc@_vX$1wbwr9tn;0-&j-K=43<bUQ8j=JsX`tR;Dg7+#^K~H
zK!FM*Z~zbpvt%K2{UZSY_<lS*D<Z%Lz5oGu(+dayz)hRLFdT>f59&ghTmgWD0l;*T
zI7<kC6aYYajzXpYKt=(8otP$50H6c_V9R4-;{Z@C0AMG7=F<Rxo%or10RUT+Ar%3j
zkpLhQWr#!oXgdI`&sK^>09Y^p6lP1rIRMx#05C~cW=H_Aw*bJ-5DT&Z2n+x)QHX^p
z00esgV8|mQcmRZ%02D^@S3L16t`O%c004NIvOKvYIYoh62rY33S640`D9%Y2D-<?i
z0%4j!F2Z@488U%158(66005wo6%pWr^Zj_v4zAA5HjcIqUoGmt2LB>rV&neh&#Q1i
z007~1e$oCcFS8neI|hJl{-P!B1ZZ9hpmq0)X0i`JwE&>$+E?>%_<lS*MWK+n+1cgf
z<k(8YLR(?VSAG6x!e78w{cQPuJpA|d;J)G{fihizM+Erb!p!tcr5w+a34~(Y=8s4G
zw+sLL9n&JjNn*KJDiq^U5^;`1nvC-@r6P$!k}1U{(*I=Q-z@tBKHoI}uxdU5dyy@u
zU1J0GOD7Ombim^G008p4Z^6_k2m^p<gW=D2|L;HjN1!DDfM!XOaR2~bL?kX$%CkSm
z2mk;?pn)o|K^yeJ7%adB9Ki+L!3+FgHiSYX#KJ-lLJDMn9CBbOtb#%)hRv`YDqt_v
zKpix|QD}yfa1JiQRk#j4a1Z)n2%f<xynzV>LC6RbVIkUx0b+_+BaR3cnT7Zv!AJxW
zizFb)h!jyGOOZ85F;a?DAXP{m@;!0_Ifq<Ex{*7`05XF7hP+2Hl!3BQJ=6@fL%FCo
z8iYoo3(#bAF`ADSpqtQgv>H8(HlgRxt7s3}k3K`kFu>>-2Q$QMFfPW!La{h336o>X
zu_CMttHv6zR;&ZNiS=X8v3CR#fknUxHUxJ<AYmRsNLWl*PS{AOARHt#5!wki2?K;t
z!Y3k=s7tgax)J%r7-BLphge7~Bi0g+6E6^Zh(p9TBoc{3GAFr^0!gu?RMHaCM$&Fl
zBk3%un>0uoBa_M6WNWeqIg~6QE69c9o#eyhGvpiOA@W-aonk<7r1(?fC{oI5N*U!4
z<uv66WtcKSRim0x-Ke2d5jBrmLam{;Qm;{ms1r1GnmNsb7D-E`t)i9F8fX`2_i3-_
zbh;7Ul^#x)&{xvS=|||7=mYe33=M`AgU5(xC>fg=2N-7=cNnjjOr{yriy6mMFgG#l
znCF=fnQv8CDz++o6_Lscl}eQ+l^ZHARH>?_s@|##Rr6KLRFA1%Q+=*RRWnoLsR`7U
zt5vF<Q0r40Q)j6=sE4X&sBct1q<&fbi3VB2Ov6t@q*0);U*o*SAPZv|vv@2aYYnT0
zb%8a+Cb7-ge0D0knEf5Qi#@8Tp*ce{N;6lpQuCB%KL_KOarm5cP6_8Ir<e17iry6O
zDdH&`rZh~sF=bq9s+O0QSgS~@QL9Jmy*94xr=6y~MY~!1fet~(N+(<=M`w@D1)b+p
z*;C!83a1uLJv#NSE~;y#8=<>IcfW3@?wFpwUVxrVZ>QdQz32KIeJ}k~{cZZE^+ya?
z2D1z#2HOnI7(B%_ac?{wFUQ;QQA1tBKtrWrm0_3Rgps+?Jfqb{jYbcQX~taRB;#$y
zZN{S}1|}gUOHJxc?wV3fxuz+mJ4`!F$IZ;mqRrNsHJd##*D~ju=bP7?-?v~|cv>vB
zsJ6IeNwVZxrdjT`yl#bBIa#GxRa#xMMy;K#CDyyGyQdMSxlWT#tDe?p!?5wT$+oGt
z8L;Kp2HUQ-ZMJ=3XJQv;x5ci*?vuTfeY$;({XGW_huIFR9a<fJbF^|4I#xQ~n$Dc=
zKYhjYmgz5NSkDm8*fZm{6U!;YX`NG>(?@3)XSs8O^N5RyOM=TTmp(3=8^+zpz2r)C
z^>JO{deZfso3oq3?Wo(Y?l$ge?uXo;%ru`Vo>?<<(8I_>;8Eq#KMS9gFl*neeosSB
zfoHYnBQIkwkyowPu(zdms`p{<7e4kra-ZWq<2*OsGTvEV%s0Td$hXT+!*8Bnh2KMe
zBmZRodjHV?r+_5^X9J0WL4jKW`}lf%A-|44I@@LTvf1rHjG(ze6+w@Jt%Bvjts!X0
z?2xS?_ve_-k<Mujg;0Lz*3buG=3$G&ehepthlN*$KaOySSQ^nWmo<0M+(UEUMEXRQ
zMBbZcF;6+KElM>iKB_KiJlZ$9G`c^=E@oNG)mWWaNo-3TIW8)$Hg0Ub-~8?KhvJ>$
z3*&nim@mj(aCxE5!t{lw7O5^0EIO7zOo&c6l<+|iDySBWCGrz@C5{St!X3hAA}`T4
z(TLbXTq+(;@<=L8dXnssyft|w#WSTW<++3>sgS%(4NTpeI-VAqb|7ssJvzNHgOZVu
zaYCvgO_R1~>SyL=cFU|~g|hy|Zi}}s9+d~lYqOB71z9Z$wnC=pR9Yz4DhIM>Wmjgu
z&56o6maCpC&F##y%G;1PobR9i?GnNg;gYtchD%p19a!eQtZF&3JaKv33gZ<8D~47E
ztUS1iwkmDaPpj=$m#%)jCVEY4fnLGNg2A-`YwHVD3gv};>)hAvT~AmqS>Lr``i7kw
zJ{5_It`yrBmlc25DBO7E8;5VoznR>Ww5hAaxn$2~(q`%A-YuS64wkBy=9dm`4cXeX
z4c}I@?e+FW+b@^RDBHV(wnMq2zdX3SWv9u`%{xC-q*U}&`cyXV(%rRT*Z6MH?i+i&
z_B8C(+grT%{XWUQ+f@NoP1R=AW&26{v-dx)iK^-Nmiuj8txj!m?Z*Ss1N{dh4z}01
z)YTo*JycSU)+_5r4#yw9{+;i4Ee$peRgIj+;v;ZGdF1K$3E%e~4LaI(jC-u%2h$&R
z9cLXcYC@Xwnns&bn)_Q~Te?roKGD|d-g^8;+aC{{G(1^(O7m37Y1-+6)01cN&y1aw
zoqc{T`P^XJqPBbIW6s}d4{z_f5Om?vMgNQEJG?v2T=KYd^0M3I6IZxbny)%vZR&LD
zJpPl@Psh8QyPB@KTx+@RdcC!KX7}kEo;S|j^u2lU7XQ}Oo;f|;z4Ll+_r>@1-xl3|
zawq-H%e&ckC+@AhPrP6BK<z=<L*0kfKU@CX*zeqbYQT4(^U>T#_XdT7&;F71j}Joy
zkC~6lh7E@6o;W@^IpRNZ{ptLtL(gQ-CY~4mqW;US7Zxvm_|@yz&e53Bp_lTPlfP|z
zrTyx_>lv@x#=^!PzR7qqF<$gm`|ZJZ+;<)Cqu&ot<a{81DF0~rvGr5Xr~8u`lav1h
z1DNytV>2z=0000WV@Og>004R=004l4008;_004mL004C`008P>0026e000+nl3&F}
z0007ENkl<Zc-rijO^ee|6o%heWdxxE+L3{Rf?*aF#)aa_jR>xsE(HtX!bLld`Uey%
z^K;yY(p{$?*>vF#XiF<1q#q<`3l;*QBzfJW5M$C@O<NqvfwQ{zJ?G(G?m6d5Byk$-
z3{Ht9pb2OKnt=WXpj80$xdWgipe?z3{rajTNn+Vaiu!M+X<9X%PTvNw6QNBRhVd~d
z27o=0{IRaS+wHbyv)Kf|_lT|b*4EbhkhS#MPt<W7TUFIV0qtXwR>+Co63HJ6R1h{4
za&4fRrd1V1Id&Y!_QUpiy+;9>2YyJ$aco6Vjx|lIt_!s2KAdZ+svdb^P1Dp1g~ELR
z+dj>%ZQCO+WTVk21Gp*6^5u5B{c7c)u4Mp70I+~HO><N(mmdPy2XM}>({=rZX__N1
zB!Ek9ObI|{9iYEhzuzz9^Z9!&<oPgdy<X2&DwP*rNN-$`{F3N|zoam4129Nh5&E+L
zE|C1;k1N6u8#L}$Miev|O8zNOk_T=jh_VcT1H?@BSFfJr6Oxu~+h3>C>HZS!b&_95
zTDq>6g5s{c;FC;wAs2;wKA&r?R_khjW|!o^*KUs%6BWtgIzVX{#s|x?o(8qWN~Q8V
zpx_6#(7|BvlH@zLg=RK3l+<dqH*PWaw@{47<D!2y*!cA1gS;4~RaNyc40a=+&1Um$
z6y(B(!{M_yps~tRE|<F_%kq%qG-7Kio6X+cNO>Z85_J2WPUrQa6)*7;NyjgqOeUX1
zPxMcF{a(l)M<w|!LcasxO2E>L<mZ(QB}uPOKoigeGyzROH)1~r05ZK3ifEm@rT_o{
M07*qoM6N<$f>WkX<^TWy
--- a/mobile/android/themes/core/jar.mn
+++ b/mobile/android/themes/core/jar.mn
@@ -52,16 +52,18 @@ chrome.jar:
   skin/images/checkbox_unchecked.png          (images/checkbox_unchecked.png)
   skin/images/checkbox_unchecked_disabled.png (images/checkbox_unchecked_disabled.png)
   skin/images/checkbox_unchecked_pressed.png  (images/checkbox_unchecked_pressed.png)
   skin/images/chevron.png                   (images/chevron.png)
   skin/images/default-app-icon.png          (images/default-app-icon.png)
   skin/images/dropmarker.svg                (images/dropmarker.svg)
   skin/images/dropmarker-right.svg          (images/dropmarker-right.svg)
   skin/images/errorpage-warning.png         (images/errorpage-warning.png)
+  skin/images/exitfullscreen-hdpi.png       (images/exitfullscreen-hdpi.png)
+  skin/images/fullscreen-hdpi.png           (images/fullscreen-hdpi.png)
   skin/images/certerror-warning.png         (images/certerror-warning.png)
   skin/images/errorpage-larry-white.png     (images/errorpage-larry-white.png)
   skin/images/errorpage-larry-black.png     (images/errorpage-larry-black.png)
   skin/images/marketplace-logo.png          (images/marketplace-logo.png)
   skin/images/throbber.png                  (images/throbber.png)
   skin/images/search-clear-30.png           (images/search-clear-30.png)
   skin/images/play-hdpi.png                 (images/play-hdpi.png)
   skin/images/pause-hdpi.png                (images/pause-hdpi.png)
--- a/mobile/android/themes/core/touchcontrols.css
+++ b/mobile/android/themes/core/touchcontrols.css
@@ -3,78 +3,86 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 @namespace url(http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul);
 
 /* video controls */
 .controlsOverlay {
   -moz-box-pack: center;
   -moz-box-align: end;
-  padding: 20px;
   -moz-box-flex: 1;
   -moz-box-orient: horizontal;
 }
 
 .controlsOverlay[scaled] {
   /* scaled attribute in videocontrols.css causes conflict
      due to different -moz-box-orient values */
   -moz-box-align: end;
 }
 
 .controlBar {
   -moz-box-flex: 1;
   font-size: 16pt;
-  padding: 10px;
   background-color: rgba(50,50,50,0.8);
-  border-radius: 8px;
   width: 100%;
 }
 
 .controlsSpacer {
   display: none;
   -moz-box-flex: 0;
 }
 
 .playButton,
 .castingButton,
-.muteButton {
+.muteButton,
+.fullscreenButton {
   -moz-appearance: none;
   min-height: 42px;
   min-width: 42px;
   border: none !important;
 }
 
 .playButton {
-  -moz-transform: translateX(21px);
   background: url("chrome://browser/skin/images/pause-hdpi.png") no-repeat center;
 }
 
 .playButton[paused="true"] {
   background: url("chrome://browser/skin/images/play-hdpi.png") no-repeat center;
 }
 
 .castingButton {
   background: url("chrome://browser/skin/images/cast-ready-hdpi.png") no-repeat center;
 }
 
 .castingButton[active="true"] {
   background: url("chrome://browser/skin/images/cast-active-hdpi.png") no-repeat center;
 }
 
+/* If the casting button is showing, there will be two buttons on the right side of the controls.
+ * This shifts the play button to be centered.
+ */
+.castingButton:not([hidden="true"]) + .fullscreenButton + spacer + .playButton {
+  transform: translateX(-21px);
+}
+
 .muteButton {
   background: url("chrome://browser/skin/images/mute-hdpi.png") no-repeat center;
 }
 
 .muteButton[muted="true"] {
   background: url("chrome://browser/skin/images/unmute-hdpi.png") no-repeat center;
 }
 
-/* This button is hidden until bug 704229 is fixed. */
 .fullscreenButton {
-  display: none;
+  background-color: transparent;
+  background: url("chrome://browser/skin/images/fullscreen-hdpi.png") no-repeat center;
+}
+
+.fullscreenButton[fullscreened] {
+  background: url("chrome://browser/skin/images/exitfullscreen-hdpi.png") no-repeat center;
 }
 
 /* bars */
 .scrubberStack {
   width: 100%;
   min-height: 32px;
   max-height: 32px;
   padding: 0px 8px;
--- a/python/mozbuild/mozbuild/base.py
+++ b/python/mozbuild/mozbuild/base.py
@@ -391,33 +391,32 @@ class MozbuildObject(ProcessExecutionMix
 
     def _wrap_path_argument(self, arg):
         return PathArgument(arg, self.topsrcdir, self.topobjdir)
 
     def _run_make(self, directory=None, filename=None, target=None, log=True,
             srcdir=False, allow_parallel=True, line_handler=None,
             append_env=None, explicit_env=None, ignore_errors=False,
             ensure_exit_code=0, silent=True, print_directory=True,
-            pass_thru=False, num_jobs=0, force_pymake=False):
+            pass_thru=False, num_jobs=0):
         """Invoke make.
 
         directory -- Relative directory to look for Makefile in.
         filename -- Explicit makefile to run.
         target -- Makefile target(s) to make. Can be a string or iterable of
             strings.
         srcdir -- If True, invoke make from the source directory tree.
             Otherwise, make will be invoked from the object directory.
         silent -- If True (the default), run make in silent mode.
         print_directory -- If True (the default), have make print directories
         while doing traversal.
-        force_pymake -- If True, pymake will be used instead of GNU make.
         """
         self._ensure_objdir_exists()
 
-        args = self._make_path(force_pymake=force_pymake)
+        args = self._make_path()
 
         if directory:
             args.extend(['-C', directory.replace(os.sep, '/')])
 
         if filename:
             args.extend(['-f', filename])
 
         if allow_parallel:
@@ -470,54 +469,55 @@ class MozbuildObject(ProcessExecutionMix
             'ignore_children': True,
         }
 
         if log:
             params['log_name'] = 'make'
 
         return fn(**params)
 
-    def _make_path(self, force_pymake=False):
-        if self._is_windows() and not force_pymake:
-            # Use gnumake if it's available and we can verify it's a working
-            # version.
-            baseconfig = os.path.join(self.topsrcdir, 'config', 'baseconfig.mk')
-            if os.path.exists(baseconfig):
-                try:
-                    make = which.which('gnumake')
-                    subprocess.check_call([make, '-f', baseconfig, 'HOST_OS_ARCH=WINNT'],
-                        stdout=open(os.devnull, 'wb'), stderr=subprocess.STDOUT)
-                    return [make]
-                except subprocess.CalledProcessError:
-                    pass
-                except which.WhichError:
-                    pass
+    def _make_path(self):
+        baseconfig = os.path.join(self.topsrcdir, 'config', 'baseconfig.mk')
 
-            # Use mozmake if it's available.
-            try:
-                return [which.which('mozmake')]
-            except which.WhichError:
-                pass
+        def validate_make(make):
+            if os.path.exists(baseconfig) and os.path.exists(make):
+                cmd = [make, '-f', baseconfig]
+                if self._is_windows():
+                    cmd.append('HOST_OS_ARCH=WINNT')
+                try:
+                    subprocess.check_call(cmd, stdout=open(os.devnull, 'wb'),
+                        stderr=subprocess.STDOUT)
+                except subprocess.CalledProcessError:
+                    return False
+                return True
+            return False
 
-        if self._is_windows() or force_pymake:
-            make_py = os.path.join(self.topsrcdir, 'build', 'pymake',
-                'make.py').replace(os.sep, '/')
+        possible_makes = ['gmake', 'make', 'mozmake', 'gnumake']
 
-            # We might want to consider invoking with the virtualenv's Python
-            # some day. But, there is a chicken-and-egg problem w.r.t. when the
-            # virtualenv is created.
-            return [sys.executable, make_py]
+        if 'MAKE' in os.environ:
+            make = os.environ['MAKE']
+            if os.path.isabs(make):
+                if validate_make(make):
+                    return [make]
+            else:
+                possible_makes.insert(0, make)
 
-        for test in ['gmake', 'make']:
+        for test in possible_makes:
             try:
-                return [which.which(test)]
+                make = which.which(test)
             except which.WhichError:
                 continue
+            if validate_make(make):
+                return [make]
 
-        raise Exception('Could not find a suitable make implementation.')
+        if self._is_windows():
+            raise Exception('Could not find a suitable make implementation.\n'
+                'Please use MozillaBuild 1.9 or newer')
+        else:
+            raise Exception('Could not find a suitable make implementation.')
 
     def _run_command_in_srcdir(self, **args):
         return self.run_process(cwd=self.topsrcdir, **args)
 
     def _run_command_in_objdir(self, **args):
         return self.run_process(cwd=self.topobjdir, **args)
 
     def _is_windows(self):
--- a/python/mozbuild/mozbuild/mach_commands.py
+++ b/python/mozbuild/mozbuild/mach_commands.py
@@ -262,25 +262,23 @@ class BuildOutputManager(LoggingMixin):
 @CommandProvider
 class Build(MachCommandBase):
     """Interface to build the tree."""
 
     @Command('build', category='build', description='Build the tree.')
     @CommandArgument('--jobs', '-j', default='0', metavar='jobs', type=int,
         help='Number of concurrent jobs to run. Default is the number of CPUs.')
     @CommandArgument('what', default=None, nargs='*', help=BUILD_WHAT_HELP)
-    @CommandArgument('-p', '--pymake', action='store_true',
-        help='Force using pymake over GNU make.')
     @CommandArgument('-X', '--disable-extra-make-dependencies',
                      default=False, action='store_true',
                      help='Do not add extra make dependencies.')
     @CommandArgument('-v', '--verbose', action='store_true',
         help='Verbose output for what commands the build is running.')
-    def build(self, what=None, pymake=False,
-        disable_extra_make_dependencies=None, jobs=0, verbose=False):
+    def build(self, what=None, disable_extra_make_dependencies=None, jobs=0,
+        verbose=False):
         import which
         from mozbuild.controller.building import BuildMonitor
         from mozbuild.util import resolve_target_to_make
 
         self.log_manager.register_structured_logger(logging.getLogger('mozbuild'))
 
         warnings_path = self._get_state_filename('warnings.json')
         monitor = self._spawn(BuildMonitor)
@@ -338,49 +336,47 @@ class Build(MachCommandBase):
 
                 # Ensure build backend is up to date. The alternative is to
                 # have rules in the invoked Makefile to rebuild the build
                 # backend. But that involves make reinvoking itself and there
                 # are undesired side-effects of this. See bug 877308 for a
                 # comprehensive history lesson.
                 self._run_make(directory=self.topobjdir,
                     target='backend.RecursiveMakeBackend',
-                    force_pymake=pymake, line_handler=output.on_line,
-                    log=False, print_directory=False)
+                    line_handler=output.on_line, log=False,
+                    print_directory=False)
 
                 # Build target pairs.
                 for make_dir, make_target in target_pairs:
                     # We don't display build status messages during partial
                     # tree builds because they aren't reliable there. This
                     # could potentially be fixed if the build monitor were more
                     # intelligent about encountering undefined state.
                     status = self._run_make(directory=make_dir, target=make_target,
                         line_handler=output.on_line, log=False, print_directory=False,
                         ensure_exit_code=False, num_jobs=jobs, silent=not verbose,
-                        append_env={b'NO_BUILDSTATUS_MESSAGES': b'1'},
-                        force_pymake=pymake)
+                        append_env={b'NO_BUILDSTATUS_MESSAGES': b'1'})
 
                     if status != 0:
                         break
             else:
                 monitor.start_resource_recording()
                 status = self._run_make(srcdir=True, filename='client.mk',
                     line_handler=output.on_line, log=False, print_directory=False,
                     allow_parallel=False, ensure_exit_code=False, num_jobs=jobs,
-                    silent=not verbose, force_pymake=pymake)
+                    silent=not verbose)
 
                 make_extra = self.mozconfig['make_extra'] or []
                 make_extra = dict(m.split('=', 1) for m in make_extra)
 
                 moz_automation = os.getenv('MOZ_AUTOMATION') or make_extra.get('export MOZ_AUTOMATION', None)
                 if moz_automation and status == 0:
                     status = self._run_make(target='automation/build',
                         line_handler=output.on_line, log=False, print_directory=False,
-                        ensure_exit_code=False, num_jobs=jobs, silent=not verbose,
-                        force_pymake=pymake)
+                        ensure_exit_code=False, num_jobs=jobs, silent=not verbose)
 
                 self.log(logging.WARNING, 'warning_summary',
                     {'count': len(monitor.warnings_database)},
                     '{count} compiler warnings present.')
 
             monitor.finish(record_usage=status==0)
 
         high_finder, finder_percent = monitor.have_high_finder_usage()
--- a/toolkit/content/tests/widgets/test_videocontrols_standalone.html
+++ b/toolkit/content/tests/widgets/test_videocontrols_standalone.html
@@ -63,17 +63,17 @@ function runTestAudioPre() {
       })
     }
   })
 }
 
 function runTestAudio(aAudio) {
   info("User agent (help diagnose bug #943556): " + navigator.userAgent);
   var isAndroid = navigator.userAgent.contains("Android");
-  var expectedHeight = isAndroid ? 123 : 28;
+  var expectedHeight = isAndroid ? 103 : 28;
   var condition = function () {
     var boundingRect = aAudio.getBoundingClientRect();
     return boundingRect.height == expectedHeight;
   };
   waitForCondition(condition, function () {
     var boundingRect = aAudio.getBoundingClientRect();
     is(boundingRect.height, expectedHeight,
        "Height of audio element should be " + expectedHeight + ", which is equal to the controls bar.");
new file mode 100644
--- /dev/null
+++ b/toolkit/devtools/gcli/commands/highlight.js
@@ -0,0 +1,142 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+const {Cc, Ci, Cu} = require("chrome");
+const gcli = require("gcli/index");
+require("devtools/server/actors/inspector");
+const {HIGHLIGHTER_CLASSES} = require("devtools/server/actors/highlighter");
+const {BoxModelHighlighter} = HIGHLIGHTER_CLASSES;
+
+// How many maximum nodes can be highlighted in parallel
+const MAX_HIGHLIGHTED_ELEMENTS = 100;
+
+// Stores the highlighters instances so they can be destroyed
+let highlighters = [];
+
+/**
+ * Destroy all existing highlighters
+ */
+function unhighlightAll() {
+  for (let highlighter of highlighters) {
+    highlighter.destroy();
+  }
+  highlighters = [];
+}
+
+exports.items = [
+  {
+    name: "highlight",
+    description: gcli.lookup("highlightDesc"),
+    manual: gcli.lookup("highlightManual"),
+    params: [
+      {
+        name: "selector",
+        type: "nodelist",
+        description: gcli.lookup("highlightSelectorDesc"),
+        manual: gcli.lookup("highlightSelectorManual")
+      },
+      {
+        group: gcli.lookup("highlightOptionsDesc"),
+        params: [
+          {
+            name: "hideguides",
+            type: "boolean",
+            description: gcli.lookup("highlightHideGuidesDesc"),
+            manual: gcli.lookup("highlightHideGuidesManual")
+          },
+          {
+            name: "showinfobar",
+            type: "boolean",
+            description: gcli.lookup("highlightShowInfoBarDesc"),
+            manual: gcli.lookup("highlightShowInfoBarManual")
+          },
+          {
+            name: "showall",
+            type: "boolean",
+            description: gcli.lookup("highlightShowAllDesc"),
+            manual: gcli.lookup("highlightShowAllManual")
+          },
+          {
+            name: "region",
+            type: {
+              name: "selection",
+              data: ["content", "padding", "border", "margin"]
+            },
+            description: gcli.lookup("highlightRegionDesc"),
+            manual: gcli.lookup("highlightRegionManual"),
+            defaultValue: "border"
+          },
+          {
+            name: "fill",
+            type: "string",
+            description: gcli.lookup("highlightFillDesc"),
+            manual: gcli.lookup("highlightFillManual"),
+            defaultValue: null
+          },
+          {
+            name: "keep",
+            type: "boolean",
+            description: gcli.lookup("highlightKeepDesc"),
+            manual: gcli.lookup("highlightKeepManual"),
+          }
+        ]
+      }
+    ],
+    exec: function(args, context) {
+      // Remove all existing highlighters unless told otherwise
+      if (!args.keep) {
+        unhighlightAll();
+      }
+
+      let env = context.environment;
+
+      // Unhighlight on navigate
+      env.target.once("navigate", unhighlightAll);
+
+      // Build a tab context for the highlighter (which normally takes a
+      // TabActor as parameter to its constructor)
+      let tabContext = {
+        browser: env.chromeWindow.gBrowser.getBrowserForDocument(env.document),
+        window: env.window
+      };
+
+      let i = 0;
+      for (let node of args.selector) {
+        if (!args.showall && i >= MAX_HIGHLIGHTED_ELEMENTS) {
+          break;
+        }
+
+        let highlighter = new BoxModelHighlighter(tabContext);
+        if (args.fill) {
+          highlighter.regionFill[args.region] = args.fill;
+        }
+        highlighter.show(node, {
+          region: args.region,
+          hideInfoBar: !args.showinfobar,
+          hideGuides: args.hideguides,
+          showOnly: args.region
+        });
+        highlighters.push(highlighter);
+        i ++;
+      }
+
+      let output = gcli.lookupFormat("highlightOutputConfirm",
+        ["" + args.selector.length]);
+      if (args.selector.length > i) {
+        output = gcli.lookupFormat("highlightOutputMaxReached",
+          ["" + args.selector.length, "" + i]);
+      }
+
+      return output;
+    }
+  },
+  {
+    name: "unhighlight",
+    description: gcli.lookup("unhighlightDesc"),
+    manual: gcli.lookup("unhighlightManual"),
+    exec: unhighlightAll
+  }
+];
--- a/toolkit/devtools/gcli/commands/inject.js
+++ b/toolkit/devtools/gcli/commands/inject.js
@@ -9,20 +9,20 @@ const { listenOnce } = require("devtools
 const gcli = require("gcli/index");
 
 exports.items = [
   {
     name: "inject",
     description: gcli.lookup("injectDesc"),
     manual: gcli.lookup("injectManual2"),
     params: [{
-      name: 'library',
+      name: "library",
       type: {
         name: "union",
-        types: [
+        alternatives: [
           {
             name: "selection",
             lookup: [
               {
                 name: "jQuery",
                 value: {
                   name: "jQuery",
                   src: Services.prefs.getCharPref("devtools.gcli.jquerySrc")
@@ -40,29 +40,33 @@ exports.items = [
                 value: {
                   name: "underscore",
                   src: Services.prefs.getCharPref("devtools.gcli.underscoreSrc")
                 }
               }
             ]
           },
           {
-            name: "string"
+            name: "url"
           }
         ]
       },
       description: gcli.lookup("injectLibraryDesc")
     }],
     exec: function*(args, context) {
       let document = context.environment.document;
       let library = args.library;
       let name = (library.type === "selection") ?
-          library.selection.name : library.string;
+          library.selection.name : library.url;
       let src = (library.type === "selection") ?
-          library.selection.src : library.string;
+          library.selection.src : library.url;
+
+      if (context.environment.window.location.protocol == "https:") {
+        src = src.replace(/^http:/, "https:");
+      }
 
       try {
         // Check if URI is valid
         Services.io.newURI(src, null, null);
       } catch(e) {
         return gcli.lookupFormat("injectFailed", [name]);
       }
 
--- a/toolkit/devtools/gcli/commands/paintflashing.js
+++ b/toolkit/devtools/gcli/commands/paintflashing.js
@@ -12,21 +12,23 @@ const telemetry = new Telemetry();
 
 const EventEmitter = require("devtools/toolkit/event-emitter");
 const eventEmitter = new EventEmitter();
 
 const gcli = require("gcli/index");
 
 function onPaintFlashingChanged(context) {
   let tab = context.environment.chromeWindow.gBrowser.selectedTab;
-  eventEmitter.emit("changed", tab);
+  let target = TargetFactory.forTab(tab);
+
+  eventEmitter.emit("changed", { target: target });
   function fireChange() {
-    eventEmitter.emit("changed", tab);
+    eventEmitter.emit("changed", { target: target });
   }
-  let target = TargetFactory.forTab(tab);
+
   target.off("navigate", fireChange);
   target.once("navigate", fireChange);
 
   let window = context.environment.window;
   let wUtils = window.QueryInterface(Ci.nsIInterfaceRequestor)
                      .getInterface(Ci.nsIDOMWindowUtils);
   if (wUtils.paintFlashing) {
     telemetry.toolOpened("paintflashing");
--- a/toolkit/devtools/gcli/source/lib/gcli/commands/test.js
+++ b/toolkit/devtools/gcli/source/lib/gcli/commands/test.js
@@ -16,16 +16,17 @@
 
 'use strict';
 
 require('../test/suite');
 
 var examiner = require('../testharness/examiner');
 var stati = require('../testharness/status').stati;
 var helpers = require('../test/helpers');
+var cli = require('../cli');
 var Requisition = require('../cli').Requisition;
 var createRequisitionAutomator = require('../test/automators/requisition').createRequisitionAutomator;
 
 exports.optionsContainer = [];
 
 exports.items = [
   {
     item: 'type',
@@ -80,34 +81,35 @@ exports.items = [
 
       var requisition = options.requisition;
       requisition.canon.getCommand('mocks').on(requisition);
       helpers.resetResponseTimes();
       examiner.reset();
 
       return args.suite.run(options).then(function() {
         requisition.canon.getCommand('mocks').off(requisition);
-        return examiner.toRemote();
+        var output = context.typedData('examiner-output', examiner.toRemote());
+
+        if (output.data.summary.status === stati.pass) {
+          return output;
+        }
+        else {
+          cli.logErrors = false;
+          throw output;
+        }
       });
     }
   },
   {
     item: 'converter',
     from: 'examiner-output',
     to: 'string',
     exec: function(output, conversionContext) {
-      var reply = '\n' + examiner.detailedResultLog('NodeJS/NoDom') +
-                  '\n' + helpers.timingSummary;
-
-      if (output.summary.status === stati.pass) {
-        return reply;
-      }
-      else {
-        throw reply;
-      }
+      return '\n' + examiner.detailedResultLog('NodeJS/NoDom') +
+             '\n' + helpers.timingSummary;
     }
   },
   {
     item: 'converter',
     from: 'examiner-output',
     to: 'view',
     exec: function(output, conversionContext) {
       return {
--- a/toolkit/devtools/gcli/source/lib/gcli/connectors/index.js
+++ b/toolkit/devtools/gcli/source/lib/gcli/connectors/index.js
@@ -49,16 +49,17 @@ var 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('../types/union').items,
+  require('../types/url').items,
 
   require('../fields/delegate').items,
   require('../fields/selection').items,
 
   require('../ui/intro').items,
   require('../ui/focus').items,
 
   require('../converters/converters').items,
--- a/toolkit/devtools/gcli/source/lib/gcli/fields/selection.js
+++ b/toolkit/devtools/gcli/source/lib/gcli/fields/selection.js
@@ -44,17 +44,17 @@ function SelectionField(type, options) {
 }
 
 SelectionField.prototype = Object.create(Field.prototype);
 
 SelectionField.claim = function(type, context) {
   if (context == null) {
     return Field.NO_MATCH;
   }
-  return type.getType(context).isSelection ? Field.MATCH : Field.NO_MATCH;
+  return type.getType(context).hasPredictions ? Field.DEFAULT : Field.NO_MATCH;
 };
 
 SelectionField.prototype.destroy = function() {
   this.menu.onItemClick.remove(this.itemClicked, this);
   this.menu.destroy();
   this.menu = undefined;
   this.element = undefined;
   Field.prototype.destroy.call(this);
--- a/toolkit/devtools/gcli/source/lib/gcli/index.js
+++ b/toolkit/devtools/gcli/source/lib/gcli/index.js
@@ -44,16 +44,17 @@ var 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('./types/union').items,
+  require('./types/url').items,
 
   require('./fields/delegate').items,
   require('./fields/selection').items,
 
   require('./ui/focus').items,
   require('./ui/intro').items,
 
   require('./converters/converters').items,
--- a/toolkit/devtools/gcli/source/lib/gcli/types/delegate.js
+++ b/toolkit/devtools/gcli/source/lib/gcli/types/delegate.js
@@ -34,17 +34,17 @@ exports.items = [
           typeof this.delegateType !== 'string') {
         throw new Error('Instances of DelegateType need typeSpec.delegateType' +
                         ' to be a function that returns a type');
       }
     },
 
     getSpec: function(commandName, paramName) {
       return {
-        name: 'remote',
+        name: 'delegate',
         param: paramName
       };
     },
 
     // Child types should implement this method to return an instance of the type
     // that should be used. If no type is available, or some sort of temporary
     // placeholder is required, BlankType can be used.
     delegateType: function(context) {
@@ -137,16 +137,20 @@ exports.items = [
     }
   },
   // 'blank' is a type for use with DelegateType when we don't know yet.
   // It should not be used anywhere else.
   {
     item: 'type',
     name: 'blank',
 
+    getSpec: function(commandName, paramName) {
+      return 'blank';
+    },
+
     stringify: function(value, context) {
       return '';
     },
 
     parse: function(arg, context) {
       return Promise.resolve(new Conversion(undefined, arg));
     }
   }
--- a/toolkit/devtools/gcli/source/lib/gcli/types/file.js
+++ b/toolkit/devtools/gcli/source/lib/gcli/types/file.js
@@ -44,17 +44,17 @@ exports.items = [
   {
     item: 'type',
     name: 'file',
 
     filetype: 'any',    // One of 'file', 'directory', 'any'
     existing: 'maybe',  // Should be one of 'yes', 'no', 'maybe'
     matches: undefined, // RegExp to match the file part of the path
 
-    isSelection: true,  // It's not really a selection, but acts like one
+    hasPredictions: true,
 
     constructor: function() {
       if (this.filetype !== 'any' && this.filetype !== 'file' &&
           this.filetype !== 'directory') {
         throw new Error('filetype must be one of [any|file|directory]');
       }
 
       if (this.existing !== 'yes' && this.existing !== 'no' &&
--- a/toolkit/devtools/gcli/source/lib/gcli/types/selection.js
+++ b/toolkit/devtools/gcli/source/lib/gcli/types/selection.js
@@ -376,17 +376,17 @@ SelectionType.prototype._findValue = fun
       index = i;
       break;
     }
   }
   return index;
 };
 
 /**
- * SelectionType is designed to be inherited from, so SelectionField needs a way
- * to check if something works like a selection without using 'name'
+ * This is how we indicate to SelectionField that we have predictions that
+ * might work in a menu.
  */
-SelectionType.prototype.isSelection = true;
+SelectionType.prototype.hasPredictions = true;
 
 SelectionType.prototype.name = 'selection';
 
 exports.SelectionType = SelectionType;
 exports.items = [ SelectionType ];
--- a/toolkit/devtools/gcli/source/lib/gcli/types/types.js
+++ b/toolkit/devtools/gcli/source/lib/gcli/types/types.js
@@ -1135,23 +1135,18 @@ Types.prototype.createType = function(ty
     // clone 'type'
     newType = {};
     util.copyProperties(NewTypeCtor, newType);
   }
 
   // Copy the properties of typeSpec onto the new type
   util.copyProperties(typeSpec, newType);
 
-  // Delegate and Array types need special powers to create types. Injecting
-  // ourselves at this point seems nasty, but better than the alternative of
-  // forcing all children of delegate types to require the full types API, and
-  // not know where to get it from
-  if (newType.name === 'delegate' || newType.name === 'array') {
-    newType.types = this;
-  }
+  // Several types need special powers to create child types
+  newType.types = this;
 
   if (typeof NewTypeCtor !== 'function') {
     if (typeof newType.constructor === 'function') {
       newType.constructor();
     }
   }
 
   return newType;
--- a/toolkit/devtools/gcli/source/lib/gcli/types/union.js
+++ b/toolkit/devtools/gcli/source/lib/gcli/types/union.js
@@ -13,84 +13,106 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 'use strict';
 
 var Promise = require('../util/promise').Promise;
 var l10n = require('../util/l10n');
-var centralTypes = require("./types").centralTypes;
-var Conversion = require("./types").Conversion;
-var Status = require("./types").Status;
+var Conversion = require('./types').Conversion;
+var Status = require('./types').Status;
 
 exports.items = [
   {
     // The union type allows for a combination of different parameter types.
-    item: "type",
-    name: "union",
+    item: 'type',
+    name: 'union',
+    hasPredictions: true,
 
     constructor: function() {
-      // Get the properties of the type. The last object in the list of types
-      // should always be a string type.
-      this.types = this.types.map(function(typeData) {
-        typeData.type = centralTypes.createType(typeData);
-        typeData.lookup = typeData.lookup;
-        return typeData;
-      });
+      // Get the properties of the type. Later types in the list should always
+      // be more general, so 'catch all' types like string must be last
+      this.alternatives = this.alternatives.map(function(typeData) {
+        return this.types.createType(typeData);
+      }.bind(this));
+    },
+
+    getSpec: function(command, param) {
+      var spec = { name: 'union', alternatives: [] };
+      this.alternatives.forEach(function(type) {
+        spec.alternatives.push(type.getSpec(command, param));
+      }.bind(this));
+      return spec;
     },
 
     stringify: function(value, context) {
       if (value == null) {
-        return "";
+        return '';
       }
 
-      var type = this.types.find(function(typeData) {
-        return typeData.name == value.type;
-      }).type;
+      var type = this.alternatives.find(function(typeData) {
+        return typeData.name === value.type;
+      });
 
       return type.stringify(value[value.type], context);
     },
 
     parse: function(arg, context) {
-      // Try to parse the given argument in the provided order of the parameter
-      // types. Returns a promise containing the Conversion of the value that
-      // was parsed.
-      var self = this;
+      var conversionPromises = this.alternatives.map(function(type) {
+        return type.parse(arg, context);
+      }.bind(this));
+
+      return Promise.all(conversionPromises).then(function(conversions) {
+        // Find a list of the predictions made by any conversion
+        var predictionPromises = conversions.map(function(conversion) {
+          return conversion.getPredictions(context);
+        }.bind(this));
 
-      var onError = function(i) {
-        if (i >= self.types.length) {
-          return Promise.reject(new Conversion(undefined, arg, Status.ERROR,
-            l10n.lookup("commandParseError")));
-        } else {
-          return tryNext(i + 1);
-        }
-      };
+        return Promise.all(predictionPromises).then(function(allPredictions) {
+          // Take one prediction from each set of predictions, ignoring
+          // duplicates, until we've got up to Conversion.maxPredictions
+          var maxIndex = allPredictions.reduce(function(prev, prediction) {
+            return Math.max(prev, prediction.length);
+          }.bind(this), 0);
+          var predictions = [];
 
-      var tryNext = function(i) {
-        var type = self.types[i].type;
+          indexLoop:
+          for (var index = 0; index < maxIndex; index++) {
+            for (var p = 0; p <= allPredictions.length; p++) {
+              if (predictions.length >= Conversion.maxPredictions) {
+                break indexLoop;
+              }
 
-        try {
-          return type.parse(arg, context).then(function(conversion) {
-            if (conversion.getStatus() === Status.VALID ||
-                conversion.getStatus() === Status.INCOMPLETE) {
-              // Converts the conversion value of the union type to an
-              // object that identifies the current working type and the
-              // data associated with it
-              if (conversion.value) {
-                var oldConversionValue = conversion.value;
-                conversion.value = { type: type.name };
-                conversion.value[type.name] = oldConversionValue;
+              if (allPredictions[p] != null) {
+                var prediction = allPredictions[p][index];
+                if (prediction != null && predictions.indexOf(prediction) === -1) {
+                  predictions.push(prediction);
+                }
               }
-              return conversion;
-            } else {
-              return onError(i);
+            }
+          }
+
+          var bestStatus = Status.ERROR;
+          var value;
+          for (var i = 0; i < conversions.length; i++) {
+            var conversion = conversions[i];
+            var thisStatus = conversion.getStatus(arg);
+            if (thisStatus < bestStatus) {
+              bestStatus = thisStatus;
             }
-          });
-        } catch(e) {
-          return onError(i);
-        }
-      };
+            if (bestStatus === Status.VALID) {
+              var type = this.alternatives[i].name;
+              value = { type: type };
+              value[type] = conversion.value;
+              break;
+            }
+          }
 
-      return Promise.resolve(tryNext(0));
+          var msg = (bestStatus === Status.VALID) ?
+                    '' :
+                    l10n.lookupFormat('typesSelectionNomatch', [ arg.text ]);
+          return new Conversion(value, arg, bestStatus, msg, predictions);
+        }.bind(this));
+      }.bind(this));
     },
   }
 ];
new file mode 100644
--- /dev/null
+++ b/toolkit/devtools/gcli/source/lib/gcli/types/url.js
@@ -0,0 +1,87 @@
+/*
+ * 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 host = require('../util/host');
+var Promise = require('../util/promise').Promise;
+var Status = require('./types').Status;
+var Conversion = require('./types').Conversion;
+
+exports.items = [
+  {
+    item: 'type',
+    name: 'url',
+
+    getSpec: function() {
+      return 'url';
+    },
+
+    stringify: function(value, context) {
+      if (value == null) {
+        return '';
+      }
+      return value.href;
+    },
+
+    parse: function(arg, context) {
+      var conversion;
+
+      try {
+        var url = host.createUrl(arg.text);
+        conversion = new Conversion(url, arg);
+      }
+      catch (ex) {
+        var predictions = [];
+        var status = Status.ERROR;
+
+        // Maybe the URL was missing a scheme?
+        if (arg.text.indexOf('://') === -1) {
+          [ 'http', 'https' ].forEach(function(scheme) {
+            try {
+              var http = host.createUrl(scheme + '://' + arg.text);
+              predictions.push({ name: http.href, value: http });
+            }
+            catch (ex) {
+              // Ignore
+            }
+          }.bind(this));
+
+          // Try to create a URL with the current page as a base ref
+          if (context.environment.window) {
+            try {
+              var base = context.environment.window.location.href;
+              var localized = host.createUrl(arg.text, base);
+              predictions.push({ name: localized.href, value: localized });
+            }
+            catch (ex) {
+              // Ignore
+            }
+          }
+        }
+
+        if (predictions.length > 0) {
+          status = Status.INCOMPLETE;
+        }
+
+        conversion = new Conversion(undefined, arg, status,
+                                    ex.message, predictions);
+      }
+
+      return Promise.resolve(conversion);
+    }
+  }
+];
--- a/toolkit/devtools/gcli/source/lib/gcli/util/host.js
+++ b/toolkit/devtools/gcli/source/lib/gcli/util/host.js
@@ -13,16 +13,17 @@
  * 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 URL = require("sdk/url").URL;
 
 var Task = require('resource://gre/modules/Task.jsm').Task;
 
 var Promise = require('../util/promise').Promise;
 var util = require('./util');
 
 function Highlighter(document) {
   this._document = document;
@@ -67,16 +68,23 @@ exports.spawn = function(spawnSpec) {
 /**
  * See docs in lib/gcli/util/host.js
  */
 exports.exec = function(task) {
   return Task.spawn(task);
 };
 
 /**
+ * The URL API is new enough that we need specific platform help
+ */
+exports.createUrl = function(uristr, base) {
+  return URL(uristr, base);
+};
+
+/**
  * Load some HTML into the given document and return a DOM element.
  * This utility assumes that the html has a single root (other than whitespace)
  */
 exports.toDom = function(document, html) {
   var div = util.createElement(document, 'div');
   util.setContents(div, html);
   return div.children[0];
 };
--- a/toolkit/devtools/gcli/source/lib/gcli/util/legacy.js
+++ b/toolkit/devtools/gcli/source/lib/gcli/util/legacy.js
@@ -71,16 +71,50 @@ if (typeof document !== 'undefined' && t
       });
 
       return ret;
     }
   });
 }
 
 /**
+ * Array.find
+ * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find
+ */
+if (!Array.prototype.find) {
+  Object.defineProperty(Array.prototype, 'find', {
+    enumerable: false,
+    configurable: true,
+    writable: true,
+    value: function(predicate) {
+      if (this == null) {
+        throw new TypeError('Array.prototype.find called on null or undefined');
+      }
+      if (typeof predicate !== 'function') {
+        throw new TypeError('predicate must be a function');
+      }
+      var list = Object(this);
+      var length = list.length >>> 0;
+      var thisArg = arguments[1];
+      var value;
+
+      for (var i = 0; i < length; i++) {
+        if (i in list) {
+          value = list[i];
+          if (predicate.call(thisArg, value, i, list)) {
+            return value;
+          }
+        }
+      }
+      return undefined;
+    }
+  });
+}
+
+/**
  * String.prototype.trimLeft is non-standard, but it works in Firefox,
  * Chrome and Opera. It's easiest to create a shim here.
  */
 if (!String.prototype.trimLeft) {
   String.prototype.trimLeft = function() {
     return String(this).replace(/\s*$/, '');
   };
 }
--- a/toolkit/devtools/server/actors/canvas.js
+++ b/toolkit/devtools/server/actors/canvas.js
@@ -48,20 +48,22 @@ const INTERESTING_CALLS = [
   "restore",
 
   // WebGL
   "useProgram"
 ];
 
 exports.register = function(handle) {
   handle.addTabActor(CanvasActor, "canvasActor");
+  handle.addGlobalActor(CanvasActor, "canvasActor");
 };
 
 exports.unregister = function(handle) {
   handle.removeTabActor(CanvasActor);
+  handle.removeGlobalActor(CanvasActor);
 };
 
 /**
  * Type representing an Uint32Array buffer, serialized fast(er).
  *
  * XXX: It would be nice if on local connections (only), we could just *give*
  * the buffer directly to the front, instead of going through all this
  * serialization redundancy.
--- a/toolkit/devtools/server/actors/framerate.js
+++ b/toolkit/devtools/server/actors/framerate.js
@@ -9,20 +9,22 @@ const events = require("sdk/event/core")
 const protocol = require("devtools/server/protocol");
 const DevToolsUtils = require("devtools/toolkit/DevToolsUtils.js");
 
 const {on, once, off, emit} = events;
 const {method, custom, Arg, Option, RetVal} = protocol;
 
 exports.register = function(handle) {
   handle.addTabActor(FramerateActor, "framerateActor");
+  handle.addGlobalActor(FramerateActor, "framerateActor");
 };
 
 exports.unregister = function(handle) {
   handle.removeTabActor(FramerateActor);
+  handle.removeGlobalActor(FramerateActor);
 };
 
 /**
  * A very simple utility for monitoring framerate.
  */
 let FramerateActor = exports.FramerateActor = protocol.ActorClass({
   typeName: "framerate",
   initialize: function(conn, tabActor) {
@@ -116,21 +118,23 @@ let FramerateFront = exports.FramerateFr
   /**
    * Plots the frames per second on a timeline.
    *
    * @param array ticks
    *        The raw data received from the framerate actor, which represents
    *        the elapsed time on each refresh driver tick.
    * @param number interval
    *        The maximum amount of time to wait between calculations.
+   * @param number clamp
+   *        The maximum allowed framerate value.
    * @return array
    *         A collection of { delta, value } objects representing the
    *         framerate value at every delta time.
    */
-  plotFPS: function(ticks, interval = 100) {
+  plotFPS: function(ticks, interval = 100, clamp = 60) {
     let timeline = [];
     let totalTicks = ticks.length;
 
     // If the refresh driver didn't get a chance to tick before the
     // recording was stopped, assume framerate was 0.
     if (totalTicks == 0) {
       timeline.push({ delta: 0, value: 0 });
       timeline.push({ delta: interval, value: 0 });
@@ -144,17 +148,17 @@ let FramerateFront = exports.FramerateFr
       let currTime = ticks[i];
       frameCount++;
 
       let elapsedTime = currTime - prevTime;
       if (elapsedTime < interval) {
         continue;
       }
 
-      let framerate = 1000 / (elapsedTime / frameCount);
+      let framerate = Math.min(1000 / (elapsedTime / frameCount), clamp);
       timeline.push({ delta: prevTime, value: framerate });
       timeline.push({ delta: currTime, value: framerate });
 
       frameCount = 0;
       prevTime = currTime;
     }
 
     return timeline;
--- a/toolkit/devtools/server/actors/highlighter.js
+++ b/toolkit/devtools/server/actors/highlighter.js
@@ -290,26 +290,28 @@ let CustomHighlighterActor = exports.Cus
   destroy: function() {
     protocol.Actor.prototype.destroy.call(this);
     this.finalize();
   },
 
   /**
    * Display the highlighter on a given NodeActor.
    * @param NodeActor The node to be highlighted
+   * @param Object Options for the custom highlighter
    */
-  show: method(function(node) {
+  show: method(function(node, options) {
     if (!node || !isNodeValid(node.rawNode) || !this._highlighter) {
       return;
     }
 
-    this._highlighter.show(node.rawNode);
+    this._highlighter.show(node.rawNode, options);
   }, {
     request: {
-      node: Arg(0, "domnode")
+      node: Arg(0, "domnode"),
+      options: Arg(1, "nullable:json")
     }
   }),
 
   /**
    * Hide the highlighter if it was shown before
    */
   hide: method(function() {
     if (this._highlighter) {
@@ -442,20 +444,33 @@ XULBasedHighlighter.prototype = {
 /**
  * The BoxModelHighlighter is the class that actually draws the the box model
  * regions on top of a node.
  * It is used by the HighlighterActor.
  *
  * Usage example:
  *
  * let h = new BoxModelHighlighter(browser);
- * h.show(node);
+ * h.show(node, options);
  * h.hide();
  * h.destroy();
  *
+ * Available options:
+ * - region {String}
+ *   "content", "padding", "border" or "margin"
+ *   This specifies the region that the guides should outline.
+ *   Defaults to "content"
+ * - hideGuides {Boolean}
+ *   Defaults to false
+ * - hideInfoBar {Boolean}
+ *   Defaults to false
+ * - showOnly {String}
+ *   "content", "padding", "border" or "margin"
+ *    If set, only this region will be highlighted
+ *
  * Structure:
  * <stack class="highlighter-container">
  *   <svg class="box-model-root" hidden="true">
  *     <g class="box-model-container">
  *       <polygon class="box-model-margin" points="317,122 747,36 747,181 317,267" />
  *       <polygon class="box-model-border" points="317,128 747,42 747,161 317,247" />
  *       <polygon class="box-model-padding" points="323,127 747,42 747,161 323,246" />
  *       <polygon class="box-model-content" points="335,137 735,57 735,152 335,232" />
@@ -482,16 +497,22 @@ XULBasedHighlighter.prototype = {
  * </stack>
  */
 function BoxModelHighlighter(tabActor) {
   XULBasedHighlighter.call(this, tabActor);
   this.layoutHelpers = new LayoutHelpers(this.win);
   this._initMarkup();
   EventEmitter.decorate(this);
 
+  /**
+   * Optionally customize each region's fill color by adding an entry to the
+   * regionFill property: `highlighter.regionFill.margin = "red";
+   */
+  this.regionFill = {};
+
   this._currentNode = null;
 }
 
 BoxModelHighlighter.prototype = Heritage.extend(XULBasedHighlighter.prototype, {
   get zoom() {
     return this.win.QueryInterface(Ci.nsIInterfaceRequestor)
                .getInterface(Ci.nsIDOMWindowUtils).fullZoom;
   },
@@ -658,18 +679,16 @@ BoxModelHighlighter.prototype = Heritage
     this._detachPageListeners();
     this.currentNode = node;
     this._attachPageListeners();
     this._show();
   },
 
   /**
    * Show the highlighter on a given node
-   * @param {Object} options
-   *        Object used for passing options
    */
   _show: function() {
     this._update();
     this._trackMutations();
     this.emit("ready");
   },
 
   /**
@@ -693,17 +712,19 @@ BoxModelHighlighter.prototype = Heritage
 
   /**
    * Update the highlighter on the current highlighted node (the one that was
    * passed as an argument to show(node)).
    * Should be called whenever node size or attributes change
    */
   _update: function() {
     if (this._updateBoxModel()) {
-      this._showInfobar();
+      if (!this.options.hideInfoBar) {
+        this._showInfobar();
+      }
       this._showBoxModel();
     } else {
       // Nothing to highlight (0px rectangle like a <script> tag for instance)
       this._hide();
     }
   },
 
   /**
@@ -746,42 +767,61 @@ BoxModelHighlighter.prototype = Heritage
 
   /**
    * Update the box model as per the current node.
    *
    * @return {boolean}
    *         True if the current node has a box model to be highlighted
    */
   _updateBoxModel: function() {
+    this.options.region = this.options.region || "content";
+
     if (this._nodeNeedsHighlighting()) {
       for (let boxType in this._boxModelNodes) {
         let {p1, p2, p3, p4} =
           this.layoutHelpers.getAdjustedQuads(this.currentNode, boxType);
 
         let boxNode = this._boxModelNodes[boxType];
-        boxNode.setAttribute("points",
-                             p1.x + "," + p1.y + " " +
-                             p2.x + "," + p2.y + " " +
-                             p3.x + "," + p3.y + " " +
-                             p4.x + "," + p4.y);
+
+        if (this.regionFill[boxType]) {
+          boxNode.setAttribute("style", "fill:" + this.regionFill[boxType]);
+        } else {
+          boxNode.removeAttribute("style");
+        }
 
-        if (boxType === this.options.region) {
+        if (!this.options.showOnly || this.options.showOnly === boxType) {
+          boxNode.setAttribute("points",
+                               p1.x + "," + p1.y + " " +
+                               p2.x + "," + p2.y + " " +
+                               p3.x + "," + p3.y + " " +
+                               p4.x + "," + p4.y);
+        } else {
+          boxNode.setAttribute("points", "");
+        }
+
+        if (boxType === this.options.region && !this.options.hideGuides) {
           this._showGuides(p1, p2, p3, p4);
+        } else if (this.options.hideGuides) {
+          this._hideGuides();
         }
       }
 
       return true;
     }
 
     this._hideBoxModel();
     return false;
   },
 
   _nodeNeedsHighlighting: function() {
-    if (!this.currentNode) {
+    if (!this.currentNode ||
+        Cu.isDeadWrapper(this.currentNode) ||
+        this.currentNode.nodeType !== Ci.nsIDOMNode.ELEMENT_NODE ||
+        !this.currentNode.ownerDocument ||
+        !this.currentNode.ownerDocument.defaultView) {
       return false;
     }
 
     if (!this._computedStyle) {
       this._computedStyle =
         this.currentNode.ownerDocument.defaultView.getComputedStyle(this.currentNode);
     }
 
@@ -821,27 +861,23 @@ BoxModelHighlighter.prototype = Heritage
     };
   },
 
   /**
    * We only want to show guides for horizontal and vertical edges as this helps
    * to line them up. This method finds these edges and displays a guide there.
    *
    * @param  {DOMPoint} p1
-   *                    Point 1
    * @param  {DOMPoint} p2
-   *                    Point 2
-   * @param  {DOMPoint} p3 [description]
-   *                    Point 3
-   * @param  {DOMPoint} p4 [description]
-   *                    Point 4
+   * @param  {DOMPoint} p3
+   * @param  {DOMPoint} p4
    */
   _showGuides: function(p1, p2, p3, p4) {
-    let allX = [p1.x, p2.x, p3.x, p4.x].sort();
-    let allY = [p1.y, p2.y, p3.y, p4.y].sort();
+    let allX = [p1.x, p2.x, p3.x, p4.x].sort((a, b) => a - b);
+    let allY = [p1.y, p2.y, p3.y, p4.y].sort((a, b) => a - b);
     let toShowX = [];
     let toShowY = [];
 
     for (let arr of [allX, allY]) {
       for (let i = 0; i < arr.length; i++) {
         let val = arr[i];
 
         if (i !== arr.lastIndexOf(val)) {
@@ -857,52 +893,59 @@ BoxModelHighlighter.prototype = Heritage
 
     // Move guide into place or hide it if no valid co-ordinate was found.
     this._updateGuide(this._guideNodes.top, toShowY[0]);
     this._updateGuide(this._guideNodes.right, toShowX[1]);
     this._updateGuide(this._guideNodes.bottom, toShowY[1]);
     this._updateGuide(this._guideNodes.left, toShowX[0]);
   },
 
+  _hideGuides: function() {
+    for (let side in this._guideNodes) {
+      this._guideNodes[side].setAttribute("hidden", "true");
+    }
+  },
+
   /**
    * Move a guide to the appropriate position and display it. If no point is
    * passed then the guide is hidden.
    *
    * @param  {SVGLine} guide
    *         The guide to update
    * @param  {Integer} point
    *         x or y co-ordinate. If this is undefined we hide the guide.
    */
   _updateGuide: function(guide, point=-1) {
-    if (point > 0) {
-      let offset = GUIDE_STROKE_WIDTH / 2;
-
-      if (guide === this._guideNodes.top || guide === this._guideNodes.left) {
-        point -= offset;
-      } else {
-        point += offset;
-      }
-
-      if (guide === this._guideNodes.top || guide === this._guideNodes.bottom) {
-        guide.setAttribute("x1", 0);
-        guide.setAttribute("y1", point);
-        guide.setAttribute("x2", "100%");
-        guide.setAttribute("y2", point);
-      } else {
-        guide.setAttribute("x1", point);
-        guide.setAttribute("y1", 0);
-        guide.setAttribute("x2", point);
-        guide.setAttribute("y2", "100%");
-      }
-      guide.removeAttribute("hidden");
-      return true;
-    } else {
+    if (point <= 0) {
       guide.setAttribute("hidden", "true");
       return false;
     }
+
+    let offset = GUIDE_STROKE_WIDTH / 2;
+
+    if (guide === this._guideNodes.top || guide === this._guideNodes.left) {
+      point -= offset;
+    } else {
+      point += offset;
+    }
+
+    if (guide === this._guideNodes.top || guide === this._guideNodes.bottom) {
+      guide.setAttribute("x1", 0);
+      guide.setAttribute("y1", point);
+      guide.setAttribute("x2", "100%");
+      guide.setAttribute("y2", point);
+    } else {
+      guide.setAttribute("x1", point);
+      guide.setAttribute("y1", 0);
+      guide.setAttribute("x2", point);
+      guide.setAttribute("y2", "100%");
+    }
+    guide.removeAttribute("hidden");
+
+    return true;
   },
 
   /**
    * Update node information (tagName#id.class)
    */
   _updateInfobar: function() {
     if (!this.currentNode) {
       return;
--- a/toolkit/devtools/server/actors/webaudio.js
+++ b/toolkit/devtools/server/actors/webaudio.js
@@ -14,20 +14,22 @@ const protocol = require("devtools/serve
 const { CallWatcherActor, CallWatcherFront } = require("devtools/server/actors/call-watcher");
 const { ThreadActor } = require("devtools/server/actors/script");
 
 const { on, once, off, emit } = events;
 const { method, Arg, Option, RetVal } = protocol;
 
 exports.register = function(handle) {
   handle.addTabActor(WebAudioActor, "webaudioActor");
+  handle.addGlobalActor(WebAudioActor, "webaudioActor");
 };
 
 exports.unregister = function(handle) {
   handle.removeTabActor(WebAudioActor);
+  handle.removeGlobalActor(WebAudioActor);
 };
 
 const AUDIO_GLOBALS = [
   "AudioContext", "AudioNode"
 ];
 
 const NODE_CREATION_METHODS = [
   "createBufferSource", "createMediaElementSource", "createMediaStreamSource",
--- a/toolkit/devtools/server/actors/webgl.js
+++ b/toolkit/devtools/server/actors/webgl.js
@@ -15,20 +15,22 @@ const WEBGL_CONTEXT_NAMES = ["webgl", "e
 
 // These traits are bit masks. Make sure they're powers of 2.
 const PROGRAM_DEFAULT_TRAITS = 0;
 const PROGRAM_BLACKBOX_TRAIT = 1;
 const PROGRAM_HIGHLIGHT_TRAIT = 2;
 
 exports.register = function(handle) {
   handle.addTabActor(WebGLActor, "webglActor");
+  handle.addGlobalActor(WebGLActor, "webglActor");
 }
 
 exports.unregister = function(handle) {
   handle.removeTabActor(WebGLActor);
+  handle.removeGlobalActor(WebGLActor);
 }
 
 /**
  * A WebGL Shader contributing to building a WebGL Program.
  * You can either retrieve, or compile the source of a shader, which will
  * automatically inflict the necessary changes to the WebGL state.
  */
 let ShaderActor = protocol.ActorClass({
--- a/toolkit/devtools/server/tests/mochitest/chrome.ini
+++ b/toolkit/devtools/server/tests/mochitest/chrome.ini
@@ -18,16 +18,18 @@ support-files =
 [test_Debugger.Script.prototype.global.html]
 [test_connection-manager.html]
 [test_css-logic.html]
 [test_device.html]
 [test_framerate_01.html]
 [test_framerate_02.html]
 [test_framerate_03.html]
 [test_framerate_04.html]
+[test_highlighter-boxmodel_01.html]
+[test_highlighter-boxmodel_02.html]
 [test_highlighter-csstransform_01.html]
 [test_highlighter-csstransform_02.html]
 [test_highlighter-csstransform_03.html]
 [test_inspector-changeattrs.html]
 [test_inspector-changevalue.html]
 [test_inspector-hide.html]
 [test_inspector-insert.html]
 [test_inspector-mutations-attr.html]
--- a/toolkit/devtools/server/tests/mochitest/test_framerate_01.html
+++ b/toolkit/devtools/server/tests/mochitest/test_framerate_01.html
@@ -81,20 +81,20 @@ window.onload = function() {
 
     for (var i = 0; i < timeline.length; i += 2) {
       var currFramerateStart = timeline[i].value;
       var currFramerateEnd = timeline[i + 1].value;
       info("Testing framerate: " + currFramerateStart);
 
       is(currFramerateStart, currFramerateEnd,
         "The start and end framerate values should be equal.");
-      isnot(currFramerateStart, prevFramerateValue,
-        "There should be different framerate values for each bucket.");
 
       is(typeof currFramerateStart, "number", "All values should be numbers.");
+      ok(currFramerateStart <= 60, "All values were correctly clamped.")
+
       prevFramerateValue = currFramerateStart;
     }
 
     client.close(() => {
       DebuggerServer.destroy();
       SimpleTest.finish()
     });
   }
new file mode 100644
--- /dev/null
+++ b/toolkit/devtools/server/tests/mochitest/test_highlighter-boxmodel_01.html
@@ -0,0 +1,69 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+Box Model Highlighter
+Test the creation of the SVG highlighter elements in the browser
+-->
+<head>
+  <meta charset="utf-8">
+  <title>box model highlighter actor test</title>
+  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
+</head>
+<body>
+<pre id="test">
+<script type="application/javascript;version=1.8">
+
+window.onload = function() {
+  var Cu = Components.utils;
+  var Cc = Components.classes;
+  var Ci = Components.interfaces;
+
+  Cu.import("resource://gre/modules/Services.jsm");
+  Cu.import("resource://gre/modules/devtools/Loader.jsm");
+  Cu.import("resource://gre/modules/devtools/dbg-client.jsm");
+  Cu.import("resource://gre/modules/devtools/dbg-server.jsm");
+  Cu.import("resource://gre/modules/Task.jsm");
+  const promise = Cu.import("resource://gre/modules/Promise.jsm", {}).Promise;
+
+  SimpleTest.waitForExplicitFinish();
+
+  var {InspectorFront} = devtools.require("devtools/server/actors/inspector");
+
+  DebuggerServer.init(() => true);
+  DebuggerServer.addBrowserActors();
+
+  var client = new DebuggerClient(DebuggerServer.connectPipe());
+  client.connect(() => {
+    client.listTabs(response => {
+      var form = response.tabs[response.selected];
+      var front = InspectorFront(client, form);
+
+      Task.spawn(function*() {
+        let walkerFront = yield front.getWalker();
+        let highlighterFront = yield front.getHighlighterByType(
+          "BoxModelHighlighter");
+
+        let gBrowser = Services.wm.getMostRecentWindow("navigator:browser").gBrowser;
+        let container =
+          gBrowser.selectedBrowser.parentNode.querySelector(".highlighter-container");
+
+        ok(container, "The highlighter container was found");
+        is(container.querySelectorAll("polygon").length, 4, "Found 4 polygons");
+        is(container.querySelectorAll("line").length, 4, "Found 4 guides");
+        ok(container.querySelector(".highlighter-nodeinfobar-container"), "Found the infobar");
+
+        yield highlighterFront.finalize();
+      }).then(null, ok.bind(null, false)).then(() => {
+        client.close(() => {
+          DebuggerServer.destroy();
+          SimpleTest.finish();
+        });
+      });
+    });
+  });
+}
+</script>
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/toolkit/devtools/server/tests/mochitest/test_highlighter-boxmodel_02.html
@@ -0,0 +1,260 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+Box Model Highlighter
+Test available configuration options of the highlighter
+-->
+<head>
+  <meta charset="utf-8">
+  <title>box model highlighter actor test</title>
+  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
+  <style>
+    body {
+      margin: 0;
+      padding: 0;
+    }
+    #test-element {
+      width: 100px;
+      height: 100px;
+      padding: 10px;
+      margin: 5px;
+      border: 20px solid red;
+      background: yellow;
+    }
+  </style>
+</head>
+<body style="margin">
+<div id="test-element"></div>
+<pre id="test">
+<script type="application/javascript;version=1.8">
+
+window.onload = function() {
+  var Cu = Components.utils;
+  var Cc = Components.classes;
+  var Ci = Components.interfaces;
+
+  Cu.import("resource://gre/modules/Services.jsm");
+  Cu.import("resource://gre/modules/devtools/Loader.jsm");
+  Cu.import("resource://gre/modules/devtools/dbg-client.jsm");
+  Cu.import("resource://gre/modules/devtools/dbg-server.jsm");
+  Cu.import("resource://gre/modules/Task.jsm");
+  const promise = Cu.import("resource://gre/modules/Promise.jsm", {}).Promise;
+
+  SimpleTest.waitForExplicitFinish();
+
+  var {InspectorFront} = devtools.require("devtools/server/actors/inspector");
+
+  DebuggerServer.init(() => true);
+  DebuggerServer.addBrowserActors();
+
+  var client = new DebuggerClient(DebuggerServer.connectPipe());
+  client.connect(() => {
+    client.listTabs(response => {
+      var form = response.tabs[response.selected];
+      var front = InspectorFront(client, form);
+
+      Task.spawn(function*() {
+        let walkerFront = yield front.getWalker();
+        let highlighterFront = yield front.getHighlighterByType(
+          "BoxModelHighlighter");
+
+        let gBrowser = Services.wm.getMostRecentWindow("navigator:browser").gBrowser;
+        let container =
+          gBrowser.selectedBrowser.parentNode.querySelector(".highlighter-container");
+
+        yield guidesAndInforBarShownByDefault(highlighterFront, walkerFront, container);
+        yield allRegionsAreShownByDefault(highlighterFront, walkerFront, container);
+        yield guidesCanBeHidden(highlighterFront, walkerFront, container);
+        yield infobarCanBeHidden(highlighterFront, walkerFront, container);
+        yield onlyOneRegionCanBeShown(highlighterFront, walkerFront, container);
+        yield guidesCanBeDrawnOnAGivenRegion(highlighterFront, walkerFront, container);
+
+        yield highlighterFront.finalize();
+      }).then(null, ok.bind(null, false)).then(() => {
+        client.close(() => {
+          DebuggerServer.destroy();
+          SimpleTest.finish();
+        });
+      });
+    });
+  });
+
+  function getTestNode(walkerFront) {
+    let rawNode = document.getElementById("test-element");
+    return walkerFront.frontForRawNode(rawNode);
+  }
+
+  function* guidesAndInforBarShownByDefault(highlighterFront, walkerFront, container) {
+    info("Checking that by default, guides and infobar are shown");
+
+    let node = getTestNode(walkerFront);
+
+    let infobar = container.querySelector(".highlighter-nodeinfobar-positioner");
+    let svg = container.querySelector("svg");
+    let lines = svg.querySelectorAll("line");
+
+    info("Showing the highlighter, with no options");
+    yield highlighterFront.show(node);
+
+    ok(!infobar.hasAttribute("hidden"), "Infobar shown");
+    ok(!svg.hasAttribute("hidden"), "SVG markup shown");
+    for (let line of lines) {
+      ok(!line.hasAttribute("hidden"), "Guide shown");
+    }
+
+    yield highlighterFront.hide();
+  }
+
+  function* allRegionsAreShownByDefault(highlighterFront, walkerFront, container) {
+    info("Checking that by default, all regions are shown");
+
+    let node = getTestNode(walkerFront);
+
+    info("Showing the highlighter, with no options");
+    yield highlighterFront.show(node);
+
+    let content = container.querySelector(".box-model-content");
+    let padding = container.querySelector(".box-model-padding");
+    let border = container.querySelector(".box-model-border");
+    let margin = container.querySelector(".box-model-margin");
+
+    ok(content.getAttribute("points"), "The points of the content region are set");
+    ok(padding.getAttribute("points"), "The points of the padding region are set");
+    ok(border.getAttribute("points"), "The points of the border region are set");
+    ok(margin.getAttribute("points"), "The points of the margin region are set");
+
+    yield highlighterFront.hide();
+  }
+
+  function* guidesCanBeHidden(highlighterFront, walkerFront, container) {
+    info("Checking that guides can be hidden");
+
+    let node = getTestNode(walkerFront);
+
+    let svg = container.querySelector("svg");
+    let lines = svg.querySelectorAll("line");
+
+    info("Showing the highlighter, with the hideGuides option");
+    yield highlighterFront.show(node, {
+      hideGuides: true
+    });
+
+    for (let line of lines) {
+      ok(line.hasAttribute("hidden"), "Guide hidden");
+    }
+
+    yield highlighterFront.hide();
+  }
+
+  function* infobarCanBeHidden(highlighterFront, walkerFront, container) {
+    info("Checking that the infobar can be hidden");
+
+    let node = getTestNode(walkerFront);
+
+    let svg = container.querySelector("svg");
+    let lines = svg.querySelectorAll("line");
+
+    info("Showing the highlighter, with the hideInfoBar option");
+    yield highlighterFront.show(node, {
+      hideInfoBar: true
+    });
+
+    let infobar = container.querySelector(".highlighter-nodeinfobar-positioner");
+    ok(infobar.hasAttribute("hidden"), "Infobar hidden");
+
+    yield highlighterFront.hide();
+  }
+
+  function* onlyOneRegionCanBeShown(highlighterFront, walkerFront, container) {
+    info("Checking that it's possible to select which region to show");
+
+    let node = getTestNode(walkerFront);
+
+    let svg = container.querySelector("svg");
+    let lines = svg.querySelectorAll("line");
+
+    let content = container.querySelector(".box-model-content");
+    let padding = container.querySelector(".box-model-padding");
+    let border = container.querySelector(".box-model-border");
+    let margin = container.querySelector(".box-model-margin");
+
+    info("Showing the highlighter, with the showOnly option set to content");
+    yield highlighterFront.show(node, {
+      showOnly: "content"
+    });
+
+    ok(content.getAttribute("points"), "The points of the content region are set");
+    ok(!padding.getAttribute("points"), "The points of the padding region are not set");
+    ok(!border.getAttribute("points"), "The points of the border region are not set");
+    ok(!margin.getAttribute("points"), "The points of the margin region are not set");
+
+    info("Hiding the highlighter and showing it again with the showOnly option " +
+      "set to margin");
+    yield highlighterFront.hide();
+    yield highlighterFront.show(node, {
+      showOnly: "margin"
+    });
+
+    ok(!content.getAttribute("points"), "The points of the content region are not set");
+    ok(!padding.getAttribute("points"), "The points of the padding region are not set");
+    ok(!border.getAttribute("points"), "The points of the border region are not set");
+    ok(margin.getAttribute("points"), "The points of the margin region are set");
+
+    yield highlighterFront.hide();
+  }
+
+  function* guidesCanBeDrawnOnAGivenRegion(highlighterFront, walkerFront, container) {
+    info("Checking that it's possible to choose which region the guides surround");
+
+    let node = getTestNode(walkerFront);
+
+    info("Showing the highlighter, with the region option set to padding");
+    yield highlighterFront.show(node, {
+      region: "padding"
+    });
+
+    info("Getting the guides and padding region element to compare coordinates");
+    let guideTop = container.querySelector(".box-model-guide-top");
+    let guideRight = container.querySelector(".box-model-guide-right");
+    let guideBottom = container.querySelector(".box-model-guide-bottom");
+    let guideLeft = container.querySelector(".box-model-guide-left");
+
+    let padding = container.querySelector(".box-model-padding");
+    let points = padding.getAttribute("points").split(" ").map(xy => xy.split(","));
+
+    is(Math.ceil(guideTop.getAttribute("y1")), points[0][1],
+      "Top guide's y1 is correct");
+    is(Math.floor(guideBottom.getAttribute("y1")), points[2][1],
+      "Bottom guide's y1 is correct");
+    is(Math.floor(guideRight.getAttribute("x1")), points[1][0],
+      "Right guide's x1 is correct");
+    is(Math.ceil(guideLeft.getAttribute("x1")), points[3][0],
+      "Left guide's x1 is correct");
+
+    info("Hiding the highlighter and showing it again with the margin region");
+    yield highlighterFront.hide();
+    yield highlighterFront.show(node, {
+      region: "margin"
+    });
+
+    let margin = container.querySelector(".box-model-margin");
+    let points = margin.getAttribute("points").split(" ").map(xy => xy.split(","));
+
+    is(Math.ceil(guideTop.getAttribute("y1")), points[0][1],
+      "Top guide's y1 is correct");
+    is(Math.floor(guideBottom.getAttribute("y1")), points[2][1],
+      "Bottom guide's y1 is correct");
+    is(Math.floor(guideRight.getAttribute("x1")), points[1][0],
+      "Right guide's x1 is correct");
+    is(Math.ceil(guideLeft.getAttribute("x1")), points[3][0],
+      "Left guide's x1 is correct");
+
+    yield highlighterFront.hide();
+  }
+
+}
+</script>
+</pre>
+</body>
+</html>