Merge m-c to inbound. a=merge
Merge m-c to inbound. a=merge
--- a/b2g/components/B2GComponents.manifest
+++ b/b2g/components/B2GComponents.manifest
@@ -32,16 +32,20 @@ contract @mozilla.org/dom/apps/inter-app
component {2846f034-e614-11e3-93cd-74d02b97e723} SystemMessageGlue.js
contract @mozilla.org/dom/messages/system-message-glue;1 {2846f034-e614-11e3-93cd-74d02b97e723}
# ProcessGlobal.js
component {1a94c87a-5ece-4d11-91e1-d29c29f21b28} ProcessGlobal.js
contract @mozilla.org/b2g-process-global;1 {1a94c87a-5ece-4d11-91e1-d29c29f21b28}
category app-startup ProcessGlobal service,@mozilla.org/b2g-process-global;1
+# OMAContentHandler.js
+component {a6b2ab13-9037-423a-9897-dde1081be323} OMAContentHandler.js
+contract @mozilla.org/uriloader/content-handler;1?type=application/vnd.oma.drm.message {a6b2ab13-9037-423a-9897-dde1081be323}
+
# PaymentGlue.js
component {8b83eabc-7929-47f4-8b48-4dea8d887e4b} PaymentGlue.js
contract @mozilla.org/payment/ui-glue;1 {8b83eabc-7929-47f4-8b48-4dea8d887e4b}
# TelProtocolHandler.js
component {782775dd-7351-45ea-aff1-0ffa872cfdd2} TelProtocolHandler.js
contract @mozilla.org/network/protocol;1?name=tel {782775dd-7351-45ea-aff1-0ffa872cfdd2}
new file mode 100644
--- /dev/null
+++ b/b2g/components/OMAContentHandler.js
@@ -0,0 +1,57 @@
+/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
+
+"use strict";
+
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+const Cr = Components.results;
+const Cu = Components.utils;
+
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+Cu.import("resource://gre/modules/Services.jsm");
+
+XPCOMUtils.defineLazyGetter(this, "cpmm", function() {
+ return Cc["@mozilla.org/childprocessmessagemanager;1"]
+ .getService(Ci.nsIMessageSender);
+});
+
+function debug(aMsg) {
+ //dump("--*-- OMAContentHandler: " + aMsg + "\n");
+}
+
+const NS_ERROR_WONT_HANDLE_CONTENT = 0x805d0001;
+
+function OMAContentHandler() {
+}
+
+OMAContentHandler.prototype = {
+ classID: Components.ID("{a6b2ab13-9037-423a-9897-dde1081be323}"),
+
+ _xpcom_factory: {
+ createInstance: function createInstance(outer, iid) {
+ if (outer != null) {
+ throw Cr.NS_ERROR_NO_AGGREGATION;
+ }
+ return new OMAContentHandler().QueryInterface(iid);
+ }
+ },
+
+ handleContent: function handleContent(aMimetype, aContext, aRequest) {
+ if (!(aRequest instanceof Ci.nsIChannel)) {
+ throw NS_ERROR_WONT_HANDLE_CONTENT;
+ }
+
+ let detail = {
+ "type": aMimetype,
+ "url": aRequest.URI.spec
+ };
+ cpmm.sendAsyncMessage("content-handler", detail);
+
+ aRequest.cancel(Cr.NS_BINDING_ABORTED);
+ },
+
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsIContentHandler])
+}
+
+this.NSGetFactory = XPCOMUtils.generateNSGetFactory([OMAContentHandler]);
--- a/b2g/components/moz.build
+++ b/b2g/components/moz.build
@@ -11,16 +11,17 @@ EXTRA_COMPONENTS += [
'AlertsService.js',
'B2GAboutRedirector.js',
'ContentPermissionPrompt.js',
'FilePicker.js',
'HelperAppDialog.js',
'InterAppCommUIGlue.js',
'MailtoProtocolHandler.js',
'MobileIdentityUIGlue.js',
+ 'OMAContentHandler.js',
'PaymentGlue.js',
'ProcessGlobal.js',
'SmsProtocolHandler.js',
'SystemMessageGlue.js',
'TelProtocolHandler.js',
'WebappsUpdateTimer.js',
'YoutubeProtocolHandler.js',
]
--- 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="336c30b6147cdd9122ad0b2bbffb81eb869a9ec2"/>
+ <project name="gaia.git" path="gaia" remote="mozillaorg" revision="82e957160ca017087bd374cd051339e55b641e68"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="44b04243e31cd16f3baf54fcd4b5fecf9deb8b94"/>
<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="18178e0035f603b68490f42672566d3adb33bead"/>
+ <project name="apitrace" path="external/apitrace" remote="apitrace" revision="4a7d860ef54cf6e119238b247522a85c13d17dd6"/>
<!-- 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="336c30b6147cdd9122ad0b2bbffb81eb869a9ec2"/>
+ <project name="gaia" path="gaia" remote="mozillaorg" revision="82e957160ca017087bd374cd051339e55b641e68"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="44b04243e31cd16f3baf54fcd4b5fecf9deb8b94"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="c510babaf88dfa2cfe2c202afb2649ee124569af"/>
- <project name="apitrace" path="external/apitrace" remote="apitrace" revision="18178e0035f603b68490f42672566d3adb33bead"/>
+ <project name="apitrace" path="external/apitrace" remote="apitrace" revision="4a7d860ef54cf6e119238b247522a85c13d17dd6"/>
<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"/>
@@ -123,14 +123,14 @@
<project name="platform/system/security" path="system/security" revision="f48ff68fedbcdc12b570b7699745abb6e7574907"/>
<project name="platform/system/vold" path="system/vold" revision="8de05d4a52b5a91e7336e6baa4592f945a6ddbea"/>
<default remote="caf" revision="refs/tags/android-4.3_r2.1" sync-j="4"/>
<!-- Emulator specific things -->
<project name="android-development" path="development" remote="b2g" revision="dab55669da8f48b6e57df95d5af9f16b4a87b0b1"/>
<project name="device/generic/armv7-a-neon" path="device/generic/armv7-a-neon" revision="3a9a17613cc685aa232432566ad6cc607eab4ec1"/>
<project name="device_generic_goldfish" path="device/generic/goldfish" remote="b2g" revision="0e31f35a2a77301e91baa8a237aa9e9fa4076084"/>
<project name="platform/external/libnfc-nci" path="external/libnfc-nci" revision="7d33aaf740bbf6c7c6e9c34a92b371eda311b66b"/>
- <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="ab6da65e9642e66b3a4cebc9d733975f8a318de7"/>
+ <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="973367035a1f2545f3dad6e40e354463dc56a7f4"/>
<project name="platform/external/wpa_supplicant_8" path="external/wpa_supplicant_8" revision="0e56e450367cd802241b27164a2979188242b95f"/>
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="832f4acaf481a19031e479a40b03d9ce5370ddee"/>
<project name="platform_system_nfcd" path="system/nfcd" remote="b2g" revision="d0aa65b140a45016975ed0ecf35f280dd336e1d3"/>
<project name="android-sdk" path="sdk" remote="b2g" revision="8b1365af38c9a653df97349ee53a3f5d64fd590a"/>
</manifest>
--- 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="336c30b6147cdd9122ad0b2bbffb81eb869a9ec2"/>
+ <project name="gaia" path="gaia" remote="mozillaorg" revision="82e957160ca017087bd374cd051339e55b641e68"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="44b04243e31cd16f3baf54fcd4b5fecf9deb8b94"/>
<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="18178e0035f603b68490f42672566d3adb33bead"/>
+ <project name="apitrace" path="external/apitrace" remote="apitrace" revision="4a7d860ef54cf6e119238b247522a85c13d17dd6"/>
<!-- 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="336c30b6147cdd9122ad0b2bbffb81eb869a9ec2"/>
+ <project name="gaia.git" path="gaia" remote="mozillaorg" revision="82e957160ca017087bd374cd051339e55b641e68"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="44b04243e31cd16f3baf54fcd4b5fecf9deb8b94"/>
<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="18178e0035f603b68490f42672566d3adb33bead"/>
+ <project name="apitrace" path="external/apitrace" remote="apitrace" revision="4a7d860ef54cf6e119238b247522a85c13d17dd6"/>
<!-- 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="336c30b6147cdd9122ad0b2bbffb81eb869a9ec2"/>
+ <project name="gaia" path="gaia" remote="mozillaorg" revision="82e957160ca017087bd374cd051339e55b641e68"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="44b04243e31cd16f3baf54fcd4b5fecf9deb8b94"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="c510babaf88dfa2cfe2c202afb2649ee124569af"/>
- <project name="apitrace" path="external/apitrace" remote="apitrace" revision="18178e0035f603b68490f42672566d3adb33bead"/>
+ <project name="apitrace" path="external/apitrace" remote="apitrace" revision="4a7d860ef54cf6e119238b247522a85c13d17dd6"/>
<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": "8aa934cd6409c5d83446942315143989bc2232e0",
+ "revision": "2f82bebb6bbcad97fbbf24640db6aa99b6177ddc",
"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="336c30b6147cdd9122ad0b2bbffb81eb869a9ec2"/>
+ <project name="gaia.git" path="gaia" remote="mozillaorg" revision="82e957160ca017087bd374cd051339e55b641e68"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="44b04243e31cd16f3baf54fcd4b5fecf9deb8b94"/>
<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="18178e0035f603b68490f42672566d3adb33bead"/>
+ <project name="apitrace" path="external/apitrace" remote="apitrace" revision="4a7d860ef54cf6e119238b247522a85c13d17dd6"/>
<!-- 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="336c30b6147cdd9122ad0b2bbffb81eb869a9ec2"/>
+ <project name="gaia.git" path="gaia" remote="mozillaorg" revision="82e957160ca017087bd374cd051339e55b641e68"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="44b04243e31cd16f3baf54fcd4b5fecf9deb8b94"/>
<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/mozconfigs/common
+++ b/b2g/config/mozconfigs/common
@@ -3,10 +3,8 @@
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
no_tooltool=1
no_sccache=1
# This file is included at the top of all b2g mozconfigs
. "$topsrcdir/build/mozconfig.common"
-
-ac_add_options --disable-unified-compilation
--- 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="336c30b6147cdd9122ad0b2bbffb81eb869a9ec2"/>
+ <project name="gaia" path="gaia" remote="mozillaorg" revision="82e957160ca017087bd374cd051339e55b641e68"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="44b04243e31cd16f3baf54fcd4b5fecf9deb8b94"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="c510babaf88dfa2cfe2c202afb2649ee124569af"/>
- <project name="apitrace" path="external/apitrace" remote="apitrace" revision="18178e0035f603b68490f42672566d3adb33bead"/>
+ <project name="apitrace" path="external/apitrace" remote="apitrace" revision="4a7d860ef54cf6e119238b247522a85c13d17dd6"/>
<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="336c30b6147cdd9122ad0b2bbffb81eb869a9ec2"/>
+ <project name="gaia.git" path="gaia" remote="mozillaorg" revision="82e957160ca017087bd374cd051339e55b641e68"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="44b04243e31cd16f3baf54fcd4b5fecf9deb8b94"/>
<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="18178e0035f603b68490f42672566d3adb33bead"/>
+ <project name="apitrace" path="external/apitrace" remote="apitrace" revision="4a7d860ef54cf6e119238b247522a85c13d17dd6"/>
<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/package-manifest.in
+++ b/b2g/installer/package-manifest.in
@@ -798,17 +798,17 @@ bin/components/@DLL_PREFIX@nkgnomevfs@DL
@BINPATH@/components/ContentPermissionPrompt.js
#ifdef MOZ_UPDATER
@BINPATH@/components/UpdatePrompt.js
#endif
@BINPATH@/components/WebappsUpdateTimer.js
@BINPATH@/components/DirectoryProvider.js
@BINPATH@/components/ActivitiesGlue.js
@BINPATH@/components/ProcessGlobal.js
-@BINPATH@/components/ContentHandler.js
+@BINPATH@/components/OMAContentHandler.js
@BINPATH@/components/PaymentGlue.js
@BINPATH@/components/YoutubeProtocolHandler.js
@BINPATH@/components/RecoveryService.js
@BINPATH@/components/MailtoProtocolHandler.js
@BINPATH@/components/SmsProtocolHandler.js
@BINPATH@/components/TelProtocolHandler.js
@BINPATH@/components/B2GAboutRedirector.js
@BINPATH@/components/FilePicker.js
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -6897,43 +6897,58 @@ let gRemoteTabsUI = {
* URI to search for
* @param aOpenNew
* True to open a new tab and switch to it, if no existing tab is found.
* If no suitable window is found, a new one will be opened.
* @param aOpenParams
* If switching to this URI results in us opening a tab, aOpenParams
* will be the parameter object that gets passed to openUILinkIn. Please
* see the documentation for openUILinkIn to see what parameters can be
- * passed via this object.
+ * passed via this object. This object also allows the 'ignoreFragment'
+ * property to be set to true to exclude fragment-portion matching when
+ * comparing URIs.
* @return True if an existing tab was found, false otherwise
*/
-function switchToTabHavingURI(aURI, aOpenNew, aOpenParams) {
+function switchToTabHavingURI(aURI, aOpenNew, aOpenParams={}) {
// Certain URLs can be switched to irrespective of the source or destination
// window being in private browsing mode:
const kPrivateBrowsingWhitelist = new Set([
"about:customizing",
]);
+
+ let ignoreFragment = aOpenParams.ignoreFragment;
+ // This property is only used by switchToTabHavingURI and should
+ // not be used as a parameter for the new load.
+ delete aOpenParams.ignoreFragment;
+
// This will switch to the tab in aWindow having aURI, if present.
function switchIfURIInWindow(aWindow) {
// Only switch to the tab if neither the source nor the destination window
// are private and they are not in permanent private browsing mode
if (!kPrivateBrowsingWhitelist.has(aURI.spec) &&
(PrivateBrowsingUtils.isWindowPrivate(window) ||
PrivateBrowsingUtils.isWindowPrivate(aWindow)) &&
!PrivateBrowsingUtils.permanentPrivateBrowsing) {
return false;
}
let browsers = aWindow.gBrowser.browsers;
for (let i = 0; i < browsers.length; i++) {
let browser = browsers[i];
- if (browser.currentURI.equals(aURI)) {
+ if (ignoreFragment ? browser.currentURI.equalsExceptRef(aURI) :
+ browser.currentURI.equals(aURI)) {
// Focus the matching window & tab
aWindow.focus();
aWindow.gBrowser.tabContainer.selectedIndex = i;
+ if (ignoreFragment) {
+ let spec = aURI.spec;
+ if (!aURI.ref)
+ spec += "#";
+ browser.loadURI(spec);
+ }
return true;
}
}
return false;
}
// This can be passed either nsIURI or a string.
if (!(aURI instanceof Ci.nsIURI))
--- a/browser/base/content/test/general/browser.ini
+++ b/browser/base/content/test/general/browser.ini
@@ -423,8 +423,9 @@ skip-if = (os == "win" && !debug) || e10
skip-if = e10s # Bug ?????? - test directly manipulates content (content.document.getElementById)
[browser_zbug569342.js]
skip-if = e10s # Bug 516755 - SessionStore disabled for e10s
[browser_registerProtocolHandler_notification.js]
skip-if = e10s # Bug 940206 - nsIWebContentHandlerRegistrar::registerProtocolHandler doesn't work in e10s
[browser_no_mcb_on_http_site.js]
skip-if = e10s # Bug 516755 - SessionStore disabled for e10s
[browser_bug1003461-switchtab-override.js]
+[browser_bug1025195_switchToTabHavingURI_ignoreFragment.js]
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/general/browser_bug1025195_switchToTabHavingURI_ignoreFragment.js
@@ -0,0 +1,35 @@
+/* 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/. */
+
+add_task(function() {
+ registerCleanupFunction(function() {
+ while (gBrowser.tabs.length > 1)
+ gBrowser.removeCurrentTab();
+ });
+ let tabRefAboutHome = gBrowser.addTab("about:home#1");
+ yield promiseTabLoaded(tabRefAboutHome);
+ let tabRefAboutMozilla = gBrowser.addTab("about:mozilla");
+ yield promiseTabLoaded(tabRefAboutMozilla);
+
+ gBrowser.selectedTab = tabRefAboutMozilla;
+ let numTabsAtStart = gBrowser.tabs.length;
+
+ switchTab("about:home#1", false, true);
+ switchTab("about:mozilla", false, true);
+ switchTab("about:home#2", true, true);
+ is(tabRefAboutHome, gBrowser.selectedTab, "The same about:home tab should be switched to");
+ is(gBrowser.currentURI.ref, "2", "The ref should be updated to the new ref");
+ switchTab("about:mozilla", false, true);
+ switchTab("about:home#1", false, false);
+ isnot(tabRefAboutHome, gBrowser.selectedTab, "Selected tab should not be initial about:blank tab");
+ is(gBrowser.tabs.length, numTabsAtStart + 1, "Should have one new tab opened");
+ switchTab("about:about", true, false);
+});
+
+function switchTab(aURI, aIgnoreFragment, aShouldFindExistingTab) {
+ let tabFound = switchToTabHavingURI(aURI, true, {ignoreFragment: aIgnoreFragment});
+ is(tabFound, aShouldFindExistingTab,
+ "Should switch to existing " + aURI + " tab if one existed, " +
+ (aIgnoreFragment ? "ignoring" : "including") + " fragment portion");
+}
--- a/browser/base/content/test/general/head.js
+++ b/browser/base/content/test/general/head.js
@@ -466,8 +466,9 @@ let FullZoomHelper = {
failAndContinue: function failAndContinue(func) {
return function (err) {
ok(false, err);
func();
};
},
};
+
--- a/browser/devtools/scratchpad/scratchpad.js
+++ b/browser/devtools/scratchpad/scratchpad.js
@@ -1953,33 +1953,35 @@ let scratchpadTargets = new WeakMap();
* @return Promise
* The promise for the connection information.
*/
ScratchpadTab.consoleFor = function consoleFor(aSubject)
{
if (!scratchpadTargets.has(aSubject)) {
scratchpadTargets.set(aSubject, new this(aSubject));
}
- return scratchpadTargets.get(aSubject).connect();
+ return scratchpadTargets.get(aSubject).connect(aSubject);
};
ScratchpadTab.prototype = {
/**
* The promise for the connection.
*/
_connector: null,
/**
* Initialize a debugger client and connect it to the debugger server.
*
+ * @param object aSubject
+ * The tab or window to obtain the connection for.
* @return Promise
* The promise for the result of connecting to this tab or window.
*/
- connect: function ST_connect()
+ connect: function ST_connect(aSubject)
{
if (this._connector) {
return this._connector;
}
let deferred = promise.defer();
this._connector = deferred.promise;
@@ -1987,17 +1989,17 @@ ScratchpadTab.prototype = {
deferred.reject({
error: "timeout",
message: Scratchpad.strings.GetStringFromName("connectionTimeout"),
});
}, REMOTE_TIMEOUT);
deferred.promise.then(() => clearTimeout(connectTimer));
- this._attach().then(aTarget => {
+ this._attach(aSubject).then(aTarget => {
let consoleActor = aTarget.form.consoleActor;
let client = aTarget.client;
client.attachConsole(consoleActor, [], (aResponse, aWebConsoleClient) => {
if (aResponse.error) {
reportError("attachConsole", aResponse);
deferred.reject(aResponse);
}
else {
@@ -2010,22 +2012,29 @@ ScratchpadTab.prototype = {
});
return deferred.promise;
},
/**
* Attach to this tab.
*
+ * @param object aSubject
+ * The tab or window to obtain the connection for.
* @return Promise
* The promise for the TabTarget for this tab.
*/
- _attach: function ST__attach()
+ _attach: function ST__attach(aSubject)
{
let target = TargetFactory.forTab(this._tab);
+ target.once("close", () => {
+ if (scratchpadTargets) {
+ scratchpadTargets.delete(aSubject);
+ }
+ });
return target.makeRemote().then(() => target);
},
};
/**
* Represents the DebuggerClient connection to a specific window as used by the
* Scratchpad.
--- a/browser/devtools/scratchpad/test/browser.ini
+++ b/browser/devtools/scratchpad/test/browser.ini
@@ -34,8 +34,9 @@ support-files = head.js
[browser_scratchpad_open_error_console.js]
[browser_scratchpad_throw_output.js]
[browser_scratchpad_pprint-02.js]
[browser_scratchpad_pprint.js]
[browser_scratchpad_pprint_error_goto_line.js]
[browser_scratchpad_restore.js]
[browser_scratchpad_tab_switch.js]
[browser_scratchpad_ui.js]
+[browser_scratchpad_close_toolbox.js]
new file mode 100644
--- /dev/null
+++ b/browser/devtools/scratchpad/test/browser_scratchpad_close_toolbox.js
@@ -0,0 +1,38 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Test that closing the toolbox after having opened a scratchpad leaves the
+// latter in a functioning state.
+
+let {Task} = Cu.import("resource://gre/modules/Task.jsm", {});
+let {devtools} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
+
+function test() {
+ const options = {
+ tabContent: "test closing toolbox and then reusing scratchpad"
+ };
+ openTabAndScratchpad(options)
+ .then(Task.async(runTests))
+ .then(finish, console.error);
+}
+
+function* runTests([win, sp]) {
+ // Use the scratchpad before opening the toolbox.
+ const source = "window.foobar = 7;";
+ sp.setText(source);
+ let [,,result] = yield sp.display();
+ is(result, 7, "Display produced the expected output.");
+
+ // Now open the toolbox and close it again.
+ let target = devtools.TargetFactory.forTab(gBrowser.selectedTab);
+ let toolbox = yield gDevTools.showToolbox(target, "webconsole");
+ ok(toolbox, "Toolbox was opened.");
+ let closed = yield gDevTools.closeToolbox(target);
+ is(closed, true, "Toolbox was closed.");
+
+ // Now see if using the scratcphad works as expected.
+ sp.setText(source);
+ let [,,result2] = yield sp.display();
+ is(result2, 7,
+ "Display produced the expected output after the toolbox was gone.");
+}
--- a/browser/devtools/styleeditor/StyleEditorUI.jsm
+++ b/browser/devtools/styleeditor/StyleEditorUI.jsm
@@ -755,17 +755,19 @@ StyleEditorUI.prototype = {
cond.className = "media-rule-condition"
if (!rule.matches) {
cond.classList.add("media-condition-unmatched");
}
div.appendChild(cond);
let link = this._panelDoc.createElement("div");
link.className = "media-rule-line theme-link";
- link.textContent = ":" + location.line;
+ if (location.line != -1) {
+ link.textContent = ":" + location.line;
+ }
div.appendChild(link);
list.appendChild(div);
}
sidebar.hidden = !showSidebar || !inSource;
this.emit("media-list-changed", editor);
--- a/browser/devtools/styleeditor/test/browser_styleeditor_media_sidebar.js
+++ b/browser/devtools/styleeditor/test/browser_styleeditor_media_sidebar.js
@@ -2,18 +2,19 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
// https rather than chrome to improve coverage
const TESTCASE_URI = TEST_BASE_HTTPS + "media-rules.html";
const MEDIA_PREF = "devtools.styleeditor.showMediaSidebar";
const RESIZE = 300;
-const LABELS = ["not all", "all", "(max-width: 400px)"];
-const LINE_NOS = [2, 8, 20];
+const LABELS = ["not all", "all", "(max-width: 400px)", "(max-width: 600px)"];
+const LINE_NOS = [2, 8, 20, 25];
+const NEW_RULE = "\n@media (max-width: 600px) { div { color: blue; } }";
waitForExplicitFinish();
let test = asyncTest(function*() {
let {UI} = yield addTabAndOpenStyleEditors(2, null, TESTCASE_URI);
is(UI.editors.length, 2, "correct number of editors");
@@ -23,46 +24,34 @@ let test = asyncTest(function*() {
testPlainEditor(plainEditor);
// Test editor with @media rules
let mediaEditor = UI.editors[1];
yield openEditor(mediaEditor);
testMediaEditor(mediaEditor);
// Test that sidebar hides when flipping pref
- testShowHide(mediaEditor);
+ yield testShowHide(UI, mediaEditor);
+
+ // Test adding a rule updates the list
+ yield testMediaRuleAdded(UI, mediaEditor);
// Test resizing and seeing @media matching state change
let originalWidth = window.outerWidth;
let originalHeight = window.outerHeight;
let onMatchesChange = listenForMediaChange(UI);
window.resizeTo(RESIZE, RESIZE);
yield onMatchesChange;
testMediaMatchChanged(mediaEditor);
window.resizeTo(originalWidth, originalHeight);
});
-function* testShowHide(editor) {
- let sidebarChange = listenForMediaChange(UI);
- Services.prefs.setBoolPref(MEDIA_PREF, false);
- yield sidebarChange;
-
- let sidebar = editor.details.querySelector(".stylesheet-sidebar");
- is(sidebar.hidden, true, "sidebar is hidden after flipping pref");
-
- sidebarChange = listenForMediaChange(UI);
- Services.prefs.clearUserPref(MEDIA_PREF);
- yield sidebarChange;
-
- is(sidebar.hidden, false, "sidebar is showing after flipping pref back");
-}
-
function testPlainEditor(editor) {
let sidebar = editor.details.querySelector(".stylesheet-sidebar");
is(sidebar.hidden, true, "sidebar is hidden on editor without @media");
}
function testMediaEditor(editor) {
let sidebar = editor.details.querySelector(".stylesheet-sidebar");
is(sidebar.hidden, false, "sidebar is showing on editor with @media");
@@ -79,16 +68,47 @@ function testMediaMatchChanged(editor) {
let sidebar = editor.details.querySelector(".stylesheet-sidebar");
let cond = sidebar.querySelectorAll(".media-rule-condition")[2];
is(cond.textContent, "(max-width: 400px)", "third rule condition text is correct");
ok(!cond.classList.contains("media-condition-unmatched"),
"media rule is now matched after resizing");
}
+function* testShowHide(UI, editor) {
+ let sidebarChange = listenForMediaChange(UI);
+ Services.prefs.setBoolPref(MEDIA_PREF, false);
+ yield sidebarChange;
+
+ let sidebar = editor.details.querySelector(".stylesheet-sidebar");
+ is(sidebar.hidden, true, "sidebar is hidden after flipping pref");
+
+ sidebarChange = listenForMediaChange(UI);
+ Services.prefs.clearUserPref(MEDIA_PREF);
+ yield sidebarChange;
+
+ is(sidebar.hidden, false, "sidebar is showing after flipping pref back");
+}
+
+function* testMediaRuleAdded(UI, editor) {
+ yield editor.getSourceEditor();
+ let text = editor.sourceEditor.getText();
+ text += NEW_RULE;
+
+ let listChange = listenForMediaChange(UI);
+ editor.sourceEditor.setText(text);
+ yield listChange;
+
+ let sidebar = editor.details.querySelector(".stylesheet-sidebar");
+ let entries = [...sidebar.querySelectorAll(".media-rule-label")];
+ is(entries.length, 4, "four @media rules after changing text");
+
+ testRule(entries[3], LABELS[3], false, LINE_NOS[3]);
+}
+
function testRule(rule, text, matches, lineno) {
let cond = rule.querySelector(".media-rule-condition");
is(cond.textContent, text, "media label is correct for " + text);
let matched = !cond.classList.contains("media-condition-unmatched");
ok(matches ? matched : !matched,
"media rule is " + (matches ? "matched" : "unmatched"));
--- a/browser/devtools/styleinspector/computed-view.js
+++ b/browser/devtools/styleinspector/computed-view.js
@@ -7,31 +7,30 @@
const {Cc, Ci, Cu} = require("chrome");
const ToolDefinitions = require("main").Tools;
const {CssLogic} = require("devtools/styleinspector/css-logic");
const {ELEMENT_STYLE} = require("devtools/server/actors/styles");
const {Promise: promise} = Cu.import("resource://gre/modules/Promise.jsm", {});
const {EventEmitter} = require("devtools/toolkit/event-emitter");
const {OutputParser} = require("devtools/output-parser");
-const {Tooltip} = require("devtools/shared/widgets/Tooltip");
const {PrefObserver, PREF_ORIG_SOURCES} = require("devtools/styleeditor/utils");
const {gDevTools} = Cu.import("resource:///modules/devtools/gDevTools.jsm", {});
+const overlays = require("devtools/styleinspector/style-inspector-overlays");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/devtools/Templater.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "PluralForm",
"resource://gre/modules/PluralForm.jsm");
const FILTER_CHANGED_TIMEOUT = 300;
const HTML_NS = "http://www.w3.org/1999/xhtml";
const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
-const TRANSFORM_HIGHLIGHTER_TYPE = "CssTransformHighlighter";
/**
* Helper for long-running processes that should yield occasionally to
* the mainloop.
*
* @param {Window} aWin
* Timeouts will be set on this window when appropriate.
* @param {Generator} aGenerator
@@ -129,16 +128,17 @@ UpdateProcess.prototype = {
*
* @constructor
*/
function CssHtmlTree(aStyleInspector, aPageStyle)
{
this.styleWindow = aStyleInspector.window;
this.styleDocument = aStyleInspector.window.document;
this.styleInspector = aStyleInspector;
+ this.inspector = this.styleInspector.inspector;
this.pageStyle = aPageStyle;
this.propertyViews = [];
this._outputParser = new OutputParser();
let chromeReg = Cc["@mozilla.org/chrome/chrome-registry;1"].
getService(Ci.nsIXULChromeRegistry);
this.getRTLAttr = chromeReg.isLocaleRTL("global") ? "rtl" : "ltr";
@@ -154,20 +154,20 @@ function CssHtmlTree(aStyleInspector, aP
this.styleDocument.addEventListener("copy", this._onCopy);
this.styleDocument.addEventListener("mousedown", this.focusWindow);
this.styleDocument.addEventListener("contextmenu", this._onContextMenu);
// Nodes used in templating
this.root = this.styleDocument.getElementById("root");
this.templateRoot = this.styleDocument.getElementById("templateRoot");
- this.propertyContainer = this.styleDocument.getElementById("propertyContainer");
+ this.element = this.styleDocument.getElementById("propertyContainer");
// Listen for click events
- this.propertyContainer.addEventListener("click", this._onClick, false);
+ this.element.addEventListener("click", this._onClick, false);
// No results text.
this.noResults = this.styleDocument.getElementById("noResults");
// Refresh panel when color unit changed.
this._handlePrefChange = this._handlePrefChange.bind(this);
gDevTools.on("pref-changed", this._handlePrefChange);
@@ -176,29 +176,24 @@ function CssHtmlTree(aStyleInspector, aP
this._prefObserver = new PrefObserver("devtools.");
this._prefObserver.on(PREF_ORIG_SOURCES, this._updateSourceLinks);
CssHtmlTree.processTemplate(this.templateRoot, this.root, this);
// The element that we're inspecting, and the document that it comes from.
this.viewedElement = null;
- // Properties preview tooltip
- this.tooltip = new Tooltip(this.styleInspector.inspector.panelDoc);
- this.tooltip.startTogglingOnHover(this.propertyContainer,
- this._onTooltipTargetHover.bind(this));
-
this._buildContextMenu();
this.createStyleViews();
- // Initialize the css transform highlighter if the target supports it
- let hUtils = this.styleInspector.inspector.toolbox.highlighterUtils;
- if (hUtils.hasCustomHighlighter(TRANSFORM_HIGHLIGHTER_TYPE)) {
- this._initTransformHighlighter();
- }
+ // Add the tooltips and highlightersoverlay
+ this.tooltips = new overlays.TooltipsOverlay(this);
+ this.tooltips.addToView();
+ this.highlighters = new overlays.HighlightersOverlay(this);
+ this.highlighters.addToView();
}
/**
* Memoized lookup of a l10n string from a string bundle.
* @param {string} aName The key to lookup.
* @returns A localized version of the given key.
*/
CssHtmlTree.l10n = function CssHtmlTree_l10n(aName)
@@ -305,28 +300,70 @@ CssHtmlTree.prototype = {
}
// Hiding all properties
for (let propView of this.propertyViews) {
propView.refresh();
}
return promise.resolve(undefined);
}
- this.tooltip.hide();
-
if (aElement === this.viewedElement) {
return promise.resolve(undefined);
}
this.viewedElement = aElement;
this.refreshSourceFilter();
return this.refreshPanel();
},
+ /**
+ * Get the type of a given node in the computed-view
+ * @param {DOMNode} node The node which we want information about
+ * @return {Object} The type information object contains the following props:
+ * - type {String} One of the VIEW_NODE_XXX_TYPE const in
+ * style-inspector-overlays
+ * - value {Object} Depends on the type of the node
+ * returns null of the node isn't anything we care about
+ */
+ getNodeInfo: function(node) {
+ let type, value;
+ let classes = node.classList;
+
+ if (classes.contains("property-name") ||
+ classes.contains("property-value") ||
+ (classes.contains("theme-link") && !classes.contains("link"))) {
+ // Go up to the common parent to find the property and value
+ let parent = node.parentNode;
+ while (!parent.classList.contains("property-view")) {
+ parent = parent.parentNode;
+ }
+ value = {
+ property: parent.querySelector(".property-name").textContent,
+ value: parent.querySelector(".property-value").textContent
+ };
+ }
+
+ if (classes.contains("property-name")) {
+ type = overlays.VIEW_NODE_PROPERTY_TYPE;
+ } else if (classes.contains("property-value")) {
+ type = overlays.VIEW_NODE_VALUE_TYPE;
+ } else if (classes.contains("theme-link")) {
+ type = overlays.VIEW_NODE_IMAGE_URL_TYPE;
+ value.url = node.textContent;
+ } else {
+ return null;
+ }
+
+ return {
+ type: type,
+ value: value
+ };
+ },
+
_createPropertyViews: function()
{
if (this._createViewsPromise) {
return this._createViewsPromise;
}
let deferred = promise.defer();
this._createViewsPromise = deferred.promise;
@@ -347,17 +384,17 @@ CssHtmlTree.prototype = {
}
this.propertyViews.push(propView);
},
onCancel: () => {
deferred.reject("_createPropertyViews cancelled");
},
onDone: () => {
// Completed callback.
- this.propertyContainer.appendChild(fragment);
+ this.element.appendChild(fragment);
this.noResults.hidden = this.numVisibleProperties > 0;
deferred.resolve(undefined);
}
});
this._createViewsProcess.schedule();
return deferred.promise;
},
@@ -402,17 +439,17 @@ CssHtmlTree.prototype = {
let deferred = promise.defer();
this._refreshProcess = new UpdateProcess(this.styleWindow, this.propertyViews, {
onItem: (aPropView) => {
aPropView.refresh();
},
onDone: () => {
this._refreshProcess = null;
this.noResults.hidden = this.numVisibleProperties > 0;
- this.styleInspector.inspector.emit("computed-view-refreshed");
+ this.inspector.emit("computed-view-refreshed");
deferred.resolve(undefined);
}
});
this._refreshProcess.schedule();
return deferred.promise;
}).then(null, (err) => console.error(err));
},
@@ -516,116 +553,16 @@ CssHtmlTree.prototype = {
*/
focusWindow: function(aEvent)
{
let win = this.styleDocument.defaultView;
win.focus();
},
/**
- * Get the css transform highlighter front, initializing it if needed
- * @param a promise that resolves to the highlighter
- */
- getTransformHighlighter: function() {
- if (this.transformHighlighterPromise) {
- return this.transformHighlighterPromise;
- }
-
- let utils = this.styleInspector.inspector.toolbox.highlighterUtils;
- this.transformHighlighterPromise =
- utils.getHighlighterByType(TRANSFORM_HIGHLIGHTER_TYPE).then(highlighter => {
- this.transformHighlighter = highlighter;
- return this.transformHighlighter;
- });
-
- return this.transformHighlighterPromise;
- },
-
- _initTransformHighlighter: function() {
- this.isTransformHighlighterShown = false;
-
- this._onMouseMove = this._onMouseMove.bind(this);
- this._onMouseLeave = this._onMouseLeave.bind(this);
-
- this.propertyContainer.addEventListener("mousemove", this._onMouseMove, false);
- this.propertyContainer.addEventListener("mouseleave", this._onMouseLeave, false);
- },
-
- _onMouseMove: function(event) {
- if (event.target === this._lastHovered) {
- return;
- }
-
- if (this.isTransformHighlighterShown) {
- this.isTransformHighlighterShown = false;
- this.getTransformHighlighter().then(highlighter => highlighter.hide());
- }
-
- this._lastHovered = event.target;
- if (this._lastHovered.classList.contains("property-value")) {
- let propName = this._lastHovered.parentNode.querySelector(".property-name");
-
- if (propName.textContent === "transform") {
- this.isTransformHighlighterShown = true;
- let node = this.styleInspector.inspector.selection.nodeFront;
- this.getTransformHighlighter().then(highlighter => highlighter.show(node));
- }
- }
- },
-
- _onMouseLeave: function(event) {
- this._lastHovered = null;
- if (this.isTransformHighlighterShown) {
- this.isTransformHighlighterShown = false;
- this.getTransformHighlighter().then(highlighter => highlighter.hide());
- }
- },
-
- /**
- * Executed by the tooltip when the pointer hovers over an element of the view.
- * Used to decide whether the tooltip should be shown or not and to actually
- * put content in it.
- * Checks if the hovered target is a css value we support tooltips for.
- */
- _onTooltipTargetHover: function(target)
- {
- let inspector = this.styleInspector.inspector;
-
- // Test for image url
- if (target.classList.contains("theme-link") && inspector.hasUrlToImageDataResolver) {
- let propValue = target.parentNode;
- let propName = propValue.parentNode.querySelector(".property-name");
- if (propName.textContent === "background-image") {
- let maxDim = Services.prefs.getIntPref("devtools.inspector.imagePreviewTooltipSize");
- let uri = CssLogic.getBackgroundImageUriFromProperty(propValue.textContent);
- return this.tooltip.setRelativeImageContent(uri, inspector.inspector, maxDim);
- }
- }
-
- if (target.classList.contains("property-value")) {
- let propValue = target;
- let propName = target.parentNode.querySelector(".property-name");
-
- // Test for font family
- if (propName.textContent === "font-family") {
- let prop = propValue.textContent.toLowerCase();
-
- if (prop !== "inherit" && prop !== "unset" && prop !== "initial") {
- return this.tooltip.setFontFamilyContent(propValue.textContent,
- inspector.selection.nodeFront);
- }
- }
- }
-
- // If the target isn't one that should receive a tooltip, signal it by rejecting
- // a promise
- return promise.reject();
- },
-
- /**
* Create a context menu.
*/
_buildContextMenu: function()
{
let doc = this.styleDocument.defaultView.parent.document;
this._contextmenu = this.styleDocument.createElementNS(XUL_NS, "menupopup");
this._contextmenu.addEventListener("popupshowing", this._contextMenuUpdate);
@@ -751,18 +688,17 @@ CssHtmlTree.prototype = {
},
_onClick: function(event) {
let target = event.target;
if (target.nodeName === "a") {
event.stopPropagation();
event.preventDefault();
- let browserWin = this.styleInspector.inspector.target
- .tab.ownerDocument.defaultView;
+ let browserWin = this.inspector.target.tab.ownerDocument.defaultView;
browserWin.openUILinkIn(target.href, "tab");
}
},
_onCopyColor: function() {
clipboardHelper.copyString(this._colorToCopy, this.styleDocument);
},
@@ -820,18 +756,18 @@ CssHtmlTree.prototype = {
Services.prefs.setBoolPref(PREF_ORIG_SOURCES, !isEnabled);
},
/**
* Destructor for CssHtmlTree.
*/
destroy: function CssHtmlTree_destroy()
{
- delete this.viewedElement;
- delete this._outputParser;
+ this.viewedElement = null;
+ this._outputParser = null;
// Remove event listeners
this.includeBrowserStylesCheckbox.removeEventListener("command",
this.includeBrowserStylesChanged);
this.searchField.removeEventListener("command", this.filterChanged);
gDevTools.off("pref-changed", this._handlePrefChange);
this._prefObserver.off(PREF_ORIG_SOURCES, this._updateSourceLinks);
@@ -840,17 +776,17 @@ CssHtmlTree.prototype = {
// Cancel tree construction
if (this._createViewsProcess) {
this._createViewsProcess.cancel();
}
if (this._refreshProcess) {
this._refreshProcess.cancel();
}
- this.propertyContainer.removeEventListener("click", this._onClick, false);
+ this.element.removeEventListener("click", this._onClick, false);
// Remove context menu
if (this._contextmenu) {
// Destroy the Select All menuitem.
this.menuitemCopy.removeEventListener("command", this._onCopy);
this.menuitemCopy = null;
// Destroy the Copy menuitem.
@@ -864,51 +800,41 @@ CssHtmlTree.prototype = {
// Destroy the context menu.
this._contextmenu.removeEventListener("popupshowing", this._contextMenuUpdate);
this._contextmenu.parentNode.removeChild(this._contextmenu);
this._contextmenu = null;
}
this.popupNode = null;
- this.tooltip.stopTogglingOnHover(this.propertyContainer);
- this.tooltip.destroy();
-
- if (this.transformHighlighter) {
- this.transformHighlighter.finalize();
- this.transformHighlighter = null;
-
- this.propertyContainer.removeEventListener("mousemove", this._onMouseMove, false);
- this.propertyContainer.removeEventListener("mouseleave", this._onMouseLeave, false);
-
- this._lastHovered = null;
- }
+ this.tooltips.destroy();
+ this.highlighters.destroy();
// Remove bound listeners
this.styleDocument.removeEventListener("contextmenu", this._onContextMenu);
this.styleDocument.removeEventListener("copy", this._onCopy);
this.styleDocument.removeEventListener("mousedown", this.focusWindow);
// Nodes used in templating
- delete this.root;
- delete this.propertyContainer;
- delete this.panel;
+ this.root = null;
+ this.element = null;
+ this.panel = null;
// The document in which we display the results (csshtmltree.xul).
- delete this.styleDocument;
+ this.styleDocument = null;
for (let propView of this.propertyViews) {
propView.destroy();
}
// The element that we're inspecting, and the document that it comes from.
- delete this.propertyViews;
- delete this.styleWindow;
- delete this.styleDocument;
- delete this.styleInspector;
+ this.propertyViews = null;
+ this.styleWindow = null;
+ this.styleDocument = null;
+ this.styleInspector = null;
}
};
function PropertyInfo(aTree, aName) {
this.tree = aTree;
this.name = aName;
}
PropertyInfo.prototype = {
@@ -1187,22 +1113,22 @@ PropertyView.prototype = {
if (!this.matchedExpanded) {
return;
}
this._matchedSelectorResponse = matched;
CssHtmlTree.processTemplate(this.templateMatchedSelectors,
this.matchedSelectorsContainer, this);
this.matchedExpander.setAttribute("open", "");
- this.tree.styleInspector.inspector.emit("computed-view-property-expanded");
+ this.tree.inspector.emit("computed-view-property-expanded");
}).then(null, console.error);
} else {
this.matchedSelectorsContainer.innerHTML = "";
this.matchedExpander.removeAttribute("open");
- this.tree.styleInspector.inspector.emit("computed-view-property-collapsed");
+ this.tree.inspector.emit("computed-view-property-collapsed");
return promise.resolve(undefined);
}
},
get matchedSelectors()
{
return this._matchedSelectorResponse;
},
@@ -1251,17 +1177,17 @@ PropertyView.prototype = {
aEvent.preventDefault();
},
/**
* The action when a user clicks on the MDN help link for a property.
*/
mdnLinkClick: function PropertyView_mdnLinkClick(aEvent)
{
- let inspector = this.tree.styleInspector.inspector;
+ let inspector = this.tree.inspector;
if (inspector.target.tab) {
let browserWin = inspector.target.tab.ownerDocument.defaultView;
browserWin.openUILinkIn(this.link, "tab");
}
aEvent.preventDefault();
},
@@ -1393,19 +1319,19 @@ SelectorView.prototype = {
/**
* Update the text of the source link to reflect whether we're showing
* original sources or not.
*/
updateSourceLink: function()
{
this.updateSource().then((oldSource) => {
- if (oldSource != this.source && this.tree.propertyContainer) {
+ if (oldSource != this.source && this.tree.element) {
let selector = '[sourcelocation="' + oldSource + '"]';
- let link = this.tree.propertyContainer.querySelector(selector);
+ let link = this.tree.element.querySelector(selector);
if (link) {
link.textContent = this.source;
link.setAttribute("sourcelocation", this.source);
}
}
});
},
@@ -1464,17 +1390,17 @@ SelectorView.prototype = {
*
* We can only view stylesheets contained in document.styleSheets inside the
* style editor.
*
* @param aEvent The click event
*/
openStyleEditor: function(aEvent)
{
- let inspector = this.tree.styleInspector.inspector;
+ let inspector = this.tree.inspector;
let rule = this.selectorInfo.rule;
// The style editor can only display stylesheets coming from content because
// chrome stylesheets are not listed in the editor's stylesheet selector.
//
// If the stylesheet is a content stylesheet we send it to the style
// editor else we display it in the view source window.
let sheet = rule.parentStyleSheet;
--- a/browser/devtools/styleinspector/rule-view.js
+++ b/browser/devtools/styleinspector/rule-view.js
@@ -7,29 +7,28 @@
"use strict";
const {Cc, Ci, Cu} = require("chrome");
const {Promise: promise} = Cu.import("resource://gre/modules/Promise.jsm", {});
const {CssLogic} = require("devtools/styleinspector/css-logic");
const {InplaceEditor, editableField, editableItem} = require("devtools/shared/inplace-editor");
const {ELEMENT_STYLE, PSEUDO_ELEMENTS} = require("devtools/server/actors/styles");
const {gDevTools} = Cu.import("resource:///modules/devtools/gDevTools.jsm", {});
-const {Tooltip, SwatchColorPickerTooltip} = require("devtools/shared/widgets/Tooltip");
const {OutputParser} = require("devtools/output-parser");
const {PrefObserver, PREF_ORIG_SOURCES} = require("devtools/styleeditor/utils");
const {parseSingleValue, parseDeclarations} = require("devtools/styleinspector/css-parsing-utils");
+const overlays = require("devtools/styleinspector/style-inspector-overlays");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
const HTML_NS = "http://www.w3.org/1999/xhtml";
const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
const PREF_UA_STYLES = "devtools.inspector.showUserAgentStyles";
const PREF_DEFAULT_COLOR_UNIT = "devtools.defaultColorUnit";
-const TRANSFORM_HIGHLIGHTER_TYPE = "CssTransformHighlighter";
/**
* These regular expressions are adapted from firebug's css.js, and are
* used to parse CSSStyleDeclaration's cssText attribute.
*/
// Used to split on css line separators
const CSS_LINE_RE = /(?:[^;\(]*(?:\([^\)]*?\))?[^;\(]*)*;?/g;
@@ -1095,33 +1094,24 @@ function CssRuleView(aInspector, aDoc, a
this.showUserAgentStyles = Services.prefs.getBoolPref(PREF_UA_STYLES);
let options = {
autoSelect: true,
theme: "auto"
};
this.popup = new AutocompletePopup(aDoc.defaultView.parent.document, options);
- // Create a tooltip for previewing things in the rule view (images for now)
- this.previewTooltip = new Tooltip(this.inspector.panelDoc);
- this.previewTooltip.startTogglingOnHover(this.element,
- this._onTooltipTargetHover.bind(this));
-
- // Also create a more complex tooltip for editing colors with the spectrum
- // color picker
- this.colorPicker = new SwatchColorPickerTooltip(this.inspector.panelDoc);
-
this._buildContextMenu();
this._showEmpty();
- // Initialize the css transform highlighter if the target supports it
- let hUtils = this.inspector.toolbox.highlighterUtils;
- if (hUtils.hasCustomHighlighter(TRANSFORM_HIGHLIGHTER_TYPE)) {
- this._initTransformHighlighter();
- }
+ // Add the tooltips and highlighters to the view
+ this.tooltips = new overlays.TooltipsOverlay(this);
+ this.tooltips.addToView();
+ this.highlighters = new overlays.HighlightersOverlay(this);
+ this.highlighters.addToView();
}
exports.CssRuleView = CssRuleView;
CssRuleView.prototype = {
// The element that we're inspecting.
_viewedElement: null,
@@ -1161,147 +1151,16 @@ CssRuleView.prototype = {
popupset = doc.createElementNS(XUL_NS, "popupset");
doc.documentElement.appendChild(popupset);
}
popupset.appendChild(this._contextmenu);
},
/**
- * Get the css transform highlighter front, initializing it if needed
- * @param a promise that resolves to the highlighter
- */
- getTransformHighlighter: function() {
- if (this.transformHighlighterPromise) {
- return this.transformHighlighterPromise;
- }
-
- let utils = this.inspector.toolbox.highlighterUtils;
- this.transformHighlighterPromise =
- utils.getHighlighterByType(TRANSFORM_HIGHLIGHTER_TYPE).then(highlighter => {
- this.transformHighlighter = highlighter;
- return this.transformHighlighter;
- });
-
- return this.transformHighlighterPromise;
- },
-
- _initTransformHighlighter: function() {
- this.isTransformHighlighterShown = false;
-
- this._onMouseMove = this._onMouseMove.bind(this);
- this._onMouseLeave = this._onMouseLeave.bind(this);
-
- this.element.addEventListener("mousemove", this._onMouseMove, false);
- this.element.addEventListener("mouseleave", this._onMouseLeave, false);
- },
-
- _onMouseMove: function(event) {
- if (event.target === this._lastHovered) {
- return;
- }
-
- if (this.isTransformHighlighterShown) {
- this.isTransformHighlighterShown = false;
- this.getTransformHighlighter().then(highlighter => highlighter.hide());
- }
-
- this._lastHovered = event.target;
- let prop = event.target.textProperty;
- let isHighlightable = prop && prop.name === "transform" &&
- prop.enabled && !prop.overridden &&
- !prop.rule.pseudoElement;
-
- if (isHighlightable) {
- this.isTransformHighlighterShown = true;
- let node = this.inspector.selection.nodeFront;
- this.getTransformHighlighter().then(highlighter => highlighter.show(node));
- }
- },
-
- _onMouseLeave: function(event) {
- this._lastHovered = null;
- if (this.isTransformHighlighterShown) {
- this.isTransformHighlighterShown = false;
- this.getTransformHighlighter().then(highlighter => highlighter.hide());
- }
- },
-
- /**
- * Which type of hover-tooltip should be shown for the given element?
- * This depends on the element: does it contain a URL, a font-family, ...
- * @param {DOMNode} el The element to test
- * @return {String} The type of hover-tooltip
- */
- _getHoverTooltipTypeForTarget: function(el) {
- let prop = el.textProperty;
-
- // Test for image
- let isUrl = el.classList.contains("theme-link") &&
- el.parentNode.classList.contains("ruleview-propertyvalue");
- if (this.inspector.hasUrlToImageDataResolver && isUrl) {
- return "image";
- }
-
- // Test for font-family
- let propertyRoot = el.parentNode;
- let propertyNameNode = propertyRoot.querySelector(".ruleview-propertyname");
- if (!propertyNameNode) {
- propertyRoot = propertyRoot.parentNode;
- propertyNameNode = propertyRoot.querySelector(".ruleview-propertyname");
- }
- let propertyName;
- if (propertyNameNode) {
- propertyName = propertyNameNode.textContent;
- }
- if (propertyName === "font-family" && el.classList.contains("ruleview-propertyvalue")) {
- return "font";
- }
- },
-
- /**
- * Executed by the tooltip when the pointer hovers over an element of the view.
- * Used to decide whether the tooltip should be shown or not and to actually
- * put content in it.
- * Checks if the hovered target is a css value we support tooltips for.
- * @param {DOMNode} target
- * @return {Boolean|Promise} Either a boolean or a promise, used by the
- * Tooltip class to wait for the content to be put in the tooltip and finally
- * decide whether or not the tooltip should be shown.
- */
- _onTooltipTargetHover: function(target) {
- let tooltipType = this._getHoverTooltipTypeForTarget(target);
- if (!tooltipType) {
- return false;
- }
-
- if (this.colorPicker.tooltip.isShown()) {
- this.colorPicker.revert();
- this.colorPicker.hide();
- }
-
- if (tooltipType === "image") {
- let prop = target.parentNode.textProperty;
- let dim = Services.prefs.getIntPref("devtools.inspector.imagePreviewTooltipSize");
- let uri = CssLogic.getBackgroundImageUriFromProperty(prop.value, prop.rule.domRule.href);
- return this.previewTooltip.setRelativeImageContent(uri, this.inspector.inspector, dim);
- }
- if (tooltipType === "font") {
- let prop = target.textContent.toLowerCase();
-
- if (prop !== "inherit" && prop !== "unset" && prop !== "initial") {
- return this.previewTooltip.setFontFamilyContent(target.textContent,
- this.inspector.selection.nodeFront);
- }
- }
-
- return false;
- },
-
- /**
* Update the context menu. This means enabling or disabling menuitems as
* appropriate.
*/
_contextMenuUpdate: function() {
let win = this.doc.defaultView;
// Copy selection.
let selection = win.getSelection();
@@ -1334,16 +1193,75 @@ CssRuleView.prototype = {
_strings.GetStringFromName(label));
let accessKey = label + ".accessKey";
this.menuitemSources.setAttribute("accesskey",
_strings.GetStringFromName(accessKey));
},
/**
+ * Get the type of a given node in the rule-view
+ * @param {DOMNode} node The node which we want information about
+ * @return {Object} The type information object contains the following props:
+ * - type {String} One of the VIEW_NODE_XXX_TYPE const in
+ * style-inspector-overlays
+ * - value {Object} Depends on the type of the node
+ * returns null of the node isn't anything we care about
+ */
+ getNodeInfo: function(node) {
+ let type, value;
+ let classes = node.classList;
+ let prop = getParentTextProperty(node);
+
+ if (classes.contains("ruleview-propertyname") && prop) {
+ type = overlays.VIEW_NODE_PROPERTY_TYPE;
+ value = {
+ property: node.textContent,
+ value: getPropertyNameAndValue(node).value,
+ enabled: prop.enabled,
+ overridden: prop.overridden,
+ pseudoElement: prop.rule.pseudoElement,
+ sheetHref: prop.rule.domRule.href
+ };
+ } else if (classes.contains("ruleview-propertyvalue") && prop) {
+ type = overlays.VIEW_NODE_VALUE_TYPE;
+ value = {
+ property: getPropertyNameAndValue(node).name,
+ value: node.textContent,
+ enabled: prop.enabled,
+ overridden: prop.overridden,
+ pseudoElement: prop.rule.pseudoElement,
+ sheetHref: prop.rule.domRule.href
+ };
+ } else if (classes.contains("theme-link") && prop) {
+ type = overlays.VIEW_NODE_IMAGE_URL_TYPE;
+ value = {
+ property: getPropertyNameAndValue(node).name,
+ value: node.parentNode.textContent,
+ url: node.textContent,
+ enabled: prop.enabled,
+ overridden: prop.overridden,
+ pseudoElement: prop.rule.pseudoElement,
+ sheetHref: prop.rule.domRule.href
+ };
+ } else if (classes.contains("ruleview-selector-unmatched") ||
+ classes.contains("ruleview-selector-matched")) {
+ type = overlays.VIEW_NODE_SELECTOR_TYPE;
+ value = node.textContent;
+ } else {
+ return null;
+ }
+
+ return {
+ type: type,
+ value: value
+ };
+ },
+
+ /**
* A helper that determines if the popup was opened with a click to a color
* value and saves the color to this._colorToCopy.
*
* @return {Boolean}
* true if click on color opened the popup, false otherwise.
*/
_isColorPopup: function () {
this._colorToCopy = "";
@@ -1440,17 +1358,17 @@ CssRuleView.prototype = {
this.pageStyle = aPageStyle;
},
/**
* Return {bool} true if the rule view currently has an input editor visible.
*/
get isEditing() {
return this.element.querySelectorAll(".styleinspector-propertyeditor").length > 0
- || this.colorPicker.tooltip.isShown();
+ || this.tooltips.colorPicker.tooltip.isShown();
},
_handlePrefChange: function(pref) {
if (pref === PREF_UA_STYLES) {
this.showUserAgentStyles = Services.prefs.getBoolPref(pref);
}
// Reselect the currently selected element
@@ -1512,29 +1430,18 @@ CssRuleView.prototype = {
this._contextmenu.removeEventListener("popupshowing", this._contextMenuUpdate);
this._contextmenu.parentNode.removeChild(this._contextmenu);
this._contextmenu = null;
}
// We manage the popupNode ourselves so we also need to destroy it.
this.doc.popupNode = null;
- this.previewTooltip.stopTogglingOnHover(this.element);
- this.previewTooltip.destroy();
- this.colorPicker.destroy();
-
- if (this.transformHighlighter) {
- this.transformHighlighter.finalize();
- this.transformHighlighter = null;
-
- this.element.removeEventListener("mousemove", this._onMouseMove, false);
- this.element.removeEventListener("mouseleave", this._onMouseLeave, false);
-
- this._lastHovered = null;
- }
+ this.tooltips.destroy();
+ this.highlighters.destroy();
if (this.element.parentNode) {
this.element.parentNode.removeChild(this.element);
}
if (this.elementStyle) {
this.elementStyle.destroy();
}
@@ -1635,19 +1542,16 @@ CssRuleView.prototype = {
/**
* Clear the rule view.
*/
clear: function() {
this._clearRules();
this._viewedElement = null;
this._elementStyle = null;
-
- this.previewTooltip.hide();
- this.colorPicker.hide();
},
/**
* Called when the user has made changes to the ElementStyle.
* Emits an event that clients can listen to.
*/
_changed: function() {
var evt = this.doc.createEvent("Events");
@@ -2149,18 +2053,18 @@ function TextPropertyEditor(aRuleEditor,
}
TextPropertyEditor.prototype = {
/**
* Boolean indicating if the name or value is being currently edited.
*/
get editing() {
return !!(this.nameSpan.inplaceEditor || this.valueSpan.inplaceEditor ||
- this.ruleEditor.ruleView.colorPicker.tooltip.isShown() ||
- this.ruleEditor.ruleView.colorPicker.eyedropperOpen) ||
+ this.ruleEditor.ruleView.tooltips.colorPicker.tooltip.isShown() ||
+ this.ruleEditor.ruleView.tooltips.colorPicker.eyedropperOpen) ||
this.popup.isOpen;
},
/**
* Create the property editor's DOM.
*/
_create: function() {
this.element = this.doc.createElementNS(HTML_NS, "li");
@@ -2202,19 +2106,20 @@ TextPropertyEditor.prototype = {
// Property value, editable when focused. Changes to the
// property value are applied as they are typed, and reverted
// if the user presses escape.
this.valueSpan = createChild(propertyContainer, "span", {
class: "ruleview-propertyvalue theme-fg-color1",
tabindex: this.ruleEditor.isEditable ? "0" : "-1",
});
- // Storing the TextProperty on the valuespan for easy access
+ // Storing the TextProperty on the elements for easy access
// (for instance by the tooltip)
this.valueSpan.textProperty = this.prop;
+ this.nameSpan.textProperty = this.prop;
// Save the initial value as the last committed value,
// for restoring after pressing escape.
this.committed = { name: this.prop.name,
value: this.prop.value,
priority: this.prop.priority };
appendText(propertyContainer, ";");
@@ -2400,17 +2305,17 @@ TextPropertyEditor.prototype = {
// Attach the color picker tooltip to the color swatches
this._swatchSpans = this.valueSpan.querySelectorAll("." + swatchClass);
if (this.ruleEditor.isEditable) {
for (let span of this._swatchSpans) {
// Capture the original declaration value to be able to revert later
let originalValue = this.valueSpan.textContent;
// Adding this swatch to the list of swatches our colorpicker knows about
- this.ruleEditor.ruleView.colorPicker.addSwatch(span, {
+ this.ruleEditor.ruleView.tooltips.colorPicker.addSwatch(span, {
onPreview: () => this._previewValue(this.valueSpan.textContent),
onCommit: () => this._applyNewValue(this.valueSpan.textContent),
onRevert: () => this._applyNewValue(originalValue)
});
}
}
// Populate the computed styles.
@@ -2541,22 +2446,23 @@ TextPropertyEditor.prototype = {
/**
* Remove property from style and the editors from DOM.
* Begin editing next available property.
*/
remove: function() {
if (this._swatchSpans && this._swatchSpans.length) {
for (let span of this._swatchSpans) {
- this.ruleEditor.ruleView.colorPicker.removeSwatch(span);
+ this.ruleEditor.ruleView.tooltips.colorPicker.removeSwatch(span);
}
}
this.element.parentNode.removeChild(this.element);
this.ruleEditor.rule.editClosestTextProperty(this.prop);
+ this.nameSpan.textProperty = null;
this.valueSpan.textProperty = null;
this.prop.remove();
},
/**
* Called when a value editor closes. If the user pressed escape,
* revert to the value this property had before editing.
*
@@ -2865,16 +2771,73 @@ function blurOnMultipleProperties(e) {
/**
* Append a text node to an element.
*/
function appendText(aParent, aText) {
aParent.appendChild(aParent.ownerDocument.createTextNode(aText));
}
+/**
+ * Walk up the DOM from a given node until a parent property holder is found.
+ * For elements inside the computed property list, the non-computed parent
+ * property holder will be returned
+ * @param {DOMNode} node The node to start from
+ * @return {DOMNode} The parent property holder node, or null if not found
+ */
+function getParentTextPropertyHolder(node) {
+ while (true) {
+ if (!node || !node.classList) {
+ return null;
+ }
+ if (node.classList.contains("ruleview-property")) {
+ return node;
+ }
+ node = node.parentNode;
+ }
+}
+
+/**
+ * For any given node, find the TextProperty it is in if any
+ * @param {DOMNode} node The node to start from
+ * @return {TextProperty}
+ */
+function getParentTextProperty(node) {
+ let parent = getParentTextPropertyHolder(node);
+ if (!parent) {
+ return null;
+ }
+ return parent.querySelector(".ruleview-propertyvalue").textProperty;
+}
+
+/**
+ * Walker up the DOM from a given node until a parent property holder is found,
+ * and return the textContent for the name and value nodes.
+ * Stops at the first property found, so if node is inside the computed property
+ * list, the computed property will be returned
+ * @param {DOMNode} node The node to start from
+ * @return {Object} {name, value}
+ */
+function getPropertyNameAndValue(node) {
+ while (true) {
+ if (!node || !node.classList) {
+ return null;
+ }
+ // Check first for ruleview-computed since it's the deepest
+ if (node.classList.contains("ruleview-computed") ||
+ node.classList.contains("ruleview-property")) {
+ return {
+ name: node.querySelector(".ruleview-propertyname").textContent,
+ value: node.querySelector(".ruleview-propertyvalue").textContent
+ };
+ }
+ node = node.parentNode;
+ }
+}
+
XPCOMUtils.defineLazyGetter(this, "clipboardHelper", function() {
return Cc["@mozilla.org/widget/clipboardhelper;1"].
getService(Ci.nsIClipboardHelper);
});
XPCOMUtils.defineLazyGetter(this, "_strings", function() {
return Services.strings.createBundle(
"chrome://global/locale/devtools/styleinspector.properties");
new file mode 100644
--- /dev/null
+++ b/browser/devtools/styleinspector/style-inspector-overlays.js
@@ -0,0 +1,375 @@
+/* -*- Mode: Javascript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+// The style-inspector overlays are:
+// - tooltips that appear when hovering over property values
+// - editor tooltips that appear when clicking color swatches, etc.
+// - in-content highlighters that appear when hovering over property values
+// - etc.
+
+const {Cc, Ci, Cu} = require("chrome");
+const {
+ Tooltip,
+ SwatchColorPickerTooltip
+} = require("devtools/shared/widgets/Tooltip");
+const {CssLogic} = require("devtools/styleinspector/css-logic");
+const {Promise:promise} = Cu.import("resource://gre/modules/Promise.jsm", {});
+Cu.import("resource://gre/modules/Task.jsm");
+Cu.import("resource://gre/modules/Services.jsm");
+
+const PREF_IMAGE_TOOLTIP_SIZE = "devtools.inspector.imagePreviewTooltipSize";
+
+// Types of existing tooltips
+const TOOLTIP_IMAGE_TYPE = "image";
+const TOOLTIP_FONTFAMILY_TYPE = "font-family";
+
+// Types of existing highlighters
+const HIGHLIGHTER_TRANSFORM_TYPE = "CssTransformHighlighter";
+const HIGHLIGHTER_TYPES = [
+ HIGHLIGHTER_TRANSFORM_TYPE
+];
+
+// Types of nodes in the rule/computed-view
+const VIEW_NODE_SELECTOR_TYPE = exports.VIEW_NODE_SELECTOR_TYPE = 1;
+const VIEW_NODE_PROPERTY_TYPE = exports.VIEW_NODE_PROPERTY_TYPE = 2;
+const VIEW_NODE_VALUE_TYPE = exports.VIEW_NODE_VALUE_TYPE = 3;
+const VIEW_NODE_IMAGE_URL_TYPE = exports.VIEW_NODE_IMAGE_URL_TYPE = 4;
+
+/**
+ * Manages all highlighters in the style-inspector.
+ * @param {CssRuleView|CssHtmlTree} view Either the rule-view or computed-view
+ * panel
+ */
+function HighlightersOverlay(view) {
+ this.view = view;
+
+ let {CssRuleView} = require("devtools/styleinspector/rule-view");
+ this.isRuleView = view instanceof CssRuleView;
+
+ this.highlighterUtils = this.view.inspector.toolbox.highlighterUtils;
+
+ this._onMouseMove = this._onMouseMove.bind(this);
+ this._onMouseLeave = this._onMouseLeave.bind(this);
+
+ this.promises = {};
+ this.highlighters = {};
+
+ // Only initialize the overlay if at least one of the highlighter types is
+ // supported
+ this.supportsHighlighters = HIGHLIGHTER_TYPES.some(type => {
+ return this.highlighterUtils.hasCustomHighlighter(type);
+ });
+}
+
+exports.HighlightersOverlay = HighlightersOverlay;
+
+HighlightersOverlay.prototype = {
+ /**
+ * Add the highlighters overlay to the view. This will start tracking mouse
+ * movements and display highlighters when needed
+ */
+ addToView: function() {
+ if (!this.supportsHighlighters || this._isStarted || this._isDestroyed) {
+ return;
+ }
+
+ let el = this.view.element;
+ el.addEventListener("mousemove", this._onMouseMove, false);
+ el.addEventListener("mouseleave", this._onMouseLeave, false);
+
+ this._isStarted = true;
+ },
+
+ /**
+ * Remove the overlay from the current view. This will stop tracking mouse
+ * movement and showing highlighters
+ */
+ removeFromView: function() {
+ if (!this.supportsHighlighters || !this._isStarted || this._isDestroyed) {
+ return;
+ }
+
+ this._hideCurrent();
+
+ let el = this.view.element;
+ el.removeEventListener("mousemove", this._onMouseMove, false);
+ el.removeEventListener("mouseleave", this._onMouseLeave, false);
+
+ this._isStarted = false;
+ },
+
+ _onMouseMove: function(event) {
+ // Bail out if the target is the same as for the last mousemove
+ if (event.target === this._lastHovered) {
+ return;
+ }
+
+ // Only one highlighter can be displayed at a time, hide the currently shown
+ this._hideCurrent();
+
+ this._lastHovered = event.target;
+
+ let nodeInfo = this.view.getNodeInfo(event.target);
+ if (!nodeInfo) {
+ return;
+ }
+
+ // Choose the type of highlighter required for the hovered node
+ let type;
+ if (this._isRuleViewTransform(nodeInfo) ||
+ this._isComputedViewTransform(nodeInfo)) {
+ type = HIGHLIGHTER_TRANSFORM_TYPE;
+ }
+
+ if (type) {
+ this.highlighterShown = type;
+ let node = this.view.inspector.selection.nodeFront;
+ this._getHighlighter(type).then(highlighter => highlighter.show(node));
+ }
+ },
+
+ _onMouseLeave: function(event) {
+ this._lastHovered = null;
+ this._hideCurrent();
+ },
+
+ /**
+ * Is the current hovered node a css transform property value in the rule-view
+ * @param {Object} nodeInfo
+ * @return {Boolean}
+ */
+ _isRuleViewTransform: function(nodeInfo) {
+ let isTransform = nodeInfo.type === VIEW_NODE_VALUE_TYPE &&
+ nodeInfo.value.property === "transform";
+ let isEnabled = nodeInfo.value.enabled &&
+ !nodeInfo.value.overridden &&
+ !nodeInfo.value.pseudoElement;
+ return this.isRuleView && isTransform && isEnabled;
+ },
+
+ /**
+ * Is the current hovered node a css transform property value in the
+ * computed-view
+ * @param {Object} nodeInfo
+ * @return {Boolean}
+ */
+ _isComputedViewTransform: function(nodeInfo) {
+ let isTransform = nodeInfo.type === VIEW_NODE_VALUE_TYPE &&
+ nodeInfo.value.property === "transform";
+ return !this.isRuleView && isTransform;
+ },
+
+ /**
+ * Hide the currently shown highlighter
+ */
+ _hideCurrent: function() {
+ if (this.highlighterShown) {
+ this._getHighlighter(this.highlighterShown).then(highlighter => {
+ highlighter.hide();
+ this.highlighterShown = null;
+ });
+ }
+ },
+
+ /**
+ * Get a highlighter front given a type. It will only be initialized once
+ * @param {String} type The highlighter type. One of this.highlighters
+ * @return a promise that resolves to the highlighter
+ */
+ _getHighlighter: function(type) {
+ let utils = this.highlighterUtils;
+ if (!utils.hasCustomHighlighter(type)) {
+ return promise.reject();
+ }
+
+ if (this.promises[type]) {
+ return this.promises[type];
+ }
+
+ return this.promises[type] = utils.getHighlighterByType(type).then(highlighter => {
+ this.highlighters[type] = highlighter;
+ return highlighter;
+ });
+ },
+
+ /**
+ * Destroy this overlay instance, removing it from the view and destroying
+ * all initialized highlighters
+ */
+ destroy: function() {
+ this.removeFromView();
+
+ for (let type in this.highlighters) {
+ if (this.highlighters[type]) {
+ this.highlighters[type].finalize();
+ this.highlighters[type] = null;
+ }
+ }
+
+ this.promises = null;
+ this.view = null;
+ this.highlighterUtils = null;
+
+ this._isDestroyed = true;
+ }
+};
+
+/**
+ * Manages all tooltips in the style-inspector.
+ * @param {CssRuleView|CssHtmlTree} view Either the rule-view or computed-view
+ * panel
+ */
+function TooltipsOverlay(view) {
+ this.view = view;
+
+ let {CssRuleView} = require("devtools/styleinspector/rule-view");
+ this.isRuleView = view instanceof CssRuleView;
+
+ this._onNewSelection = this._onNewSelection.bind(this);
+ this.view.inspector.selection.on("new-node-front", this._onNewSelection);
+}
+
+exports.TooltipsOverlay = TooltipsOverlay;
+
+TooltipsOverlay.prototype = {
+ /**
+ * Add the tooltips overlay to the view. This will start tracking mouse
+ * movements and display tooltips when needed
+ */
+ addToView: function() {
+ if (this._isStarted || this._isDestroyed) {
+ return;
+ }
+
+ // Image, fonts, ... preview tooltip
+ this.previewTooltip = new Tooltip(this.view.inspector.panelDoc);
+ this.previewTooltip.startTogglingOnHover(this.view.element,
+ this._onPreviewTooltipTargetHover.bind(this));
+
+ // Color picker tooltip
+ if (this.isRuleView) {
+ this.colorPicker = new SwatchColorPickerTooltip(this.view.inspector.panelDoc);
+ }
+
+ this._isStarted = true;
+ },
+
+ /**
+ * Remove the tooltips overlay from the view. This will stop tracking mouse
+ * movements and displaying tooltips
+ */
+ removeFromView: function() {
+ if (!this._isStarted || this._isDestroyed) {
+ return;
+ }
+
+ this.previewTooltip.stopTogglingOnHover(this.view.element);
+ this.previewTooltip.destroy();
+
+ if (this.colorPicker) {
+ this.colorPicker.destroy();
+ }
+
+ this._isStarted = false;
+ },
+
+ /**
+ * Given a hovered node info, find out which type of tooltip should be shown,
+ * if any
+ * @param {Object} nodeInfo
+ * @return {String} The tooltip type to be shown, or null
+ */
+ _getTooltipType: function({type, value:prop}) {
+ let tooltipType = null;
+ let inspector = this.view.inspector;
+
+ // Image preview tooltip
+ if (type === VIEW_NODE_IMAGE_URL_TYPE && inspector.hasUrlToImageDataResolver) {
+ let dim = Services.prefs.getIntPref(PREF_IMAGE_TOOLTIP_SIZE);
+ let uri = CssLogic.getBackgroundImageUriFromProperty(prop.value,
+ prop.sheetHref); // sheetHref is undefined for computed-view properties,
+ // but we don't care as URIs are absolute
+ tooltipType = TOOLTIP_IMAGE_TYPE;
+ }
+
+ // Font preview tooltip
+ if (type === VIEW_NODE_VALUE_TYPE && prop.property === "font-family") {
+ let value = prop.value.toLowerCase();
+ if (value !== "inherit" && value !== "unset" && value !== "initial") {
+ tooltipType = TOOLTIP_FONTFAMILY_TYPE;
+ }
+ }
+
+ return tooltipType;
+ },
+
+ /**
+ * Executed by the tooltip when the pointer hovers over an element of the view.
+ * Used to decide whether the tooltip should be shown or not and to actually
+ * put content in it.
+ * Checks if the hovered target is a css value we support tooltips for.
+ * @param {DOMNode} target The currently hovered node
+ */
+ _onPreviewTooltipTargetHover: function(target) {
+ let nodeInfo = this.view.getNodeInfo(target);
+ if (!nodeInfo) {
+ // The hovered node isn't something we care about
+ return promise.reject();
+ }
+
+ let type = this._getTooltipType(nodeInfo);
+ if (!type) {
+ // There is no tooltip type defined for the hovered node
+ return promise.reject();
+ }
+
+ if (this.isRuleView && this.colorPicker.tooltip.isShown()) {
+ this.colorPicker.revert();
+ this.colorPicker.hide();
+ }
+
+ let inspector = this.view.inspector;
+
+ if (type === TOOLTIP_IMAGE_TYPE) {
+ let dim = Services.prefs.getIntPref(PREF_IMAGE_TOOLTIP_SIZE);
+ let uri = CssLogic.getBackgroundImageUriFromProperty(nodeInfo.value.value,
+ nodeInfo.value.sheetHref); // sheetHref is undefined for computed-view
+ // properties, but we don't care as uris are
+ // absolute
+ return this.previewTooltip.setRelativeImageContent(uri,
+ inspector.inspector, dim);
+ }
+
+ if (type === TOOLTIP_FONTFAMILY_TYPE) {
+ return this.previewTooltip.setFontFamilyContent(nodeInfo.value.value,
+ inspector.selection.nodeFront);
+ }
+ },
+
+ _onNewSelection: function() {
+ if (this.previewTooltip) {
+ this.previewTooltip.hide();
+ }
+
+ if (this.colorPicker) {
+ this.colorPicker.hide();
+ }
+ },
+
+ /**
+ * Destroy this overlay instance, removing it from the view
+ */
+ destroy: function() {
+ this.removeFromView();
+
+ this.view.inspector.selection.off("new-node-front", this._onNewSelection);
+ this.view = null;
+
+ this._isDestroyed = true;
+ }
+};
--- a/browser/devtools/styleinspector/test/browser_ruleview_colorpicker-and-image-tooltip_01.js
+++ b/browser/devtools/styleinspector/test/browser_ruleview_colorpicker-and-image-tooltip_01.js
@@ -25,22 +25,22 @@ let test = asyncTest(function*() {
let value = getRuleViewProperty(view, "body", "background").valueSpan;
let swatch = value.querySelector(".ruleview-colorswatch");
let url = value.querySelector(".theme-link");
yield testImageTooltipAfterColorChange(swatch, url, view);
});
function* testImageTooltipAfterColorChange(swatch, url, ruleView) {
info("First, verify that the image preview tooltip works");
- let anchor = yield isHoverTooltipTarget(ruleView.previewTooltip, url);
+ let anchor = yield isHoverTooltipTarget(ruleView.tooltips.previewTooltip, url);
ok(anchor, "The image preview tooltip is shown on the url span");
is(anchor, url, "The anchor returned by the showOnHover callback is correct");
info("Open the color picker tooltip and change the color");
- let picker = ruleView.colorPicker;
+ let picker = ruleView.tooltips.colorPicker;
let onShown = picker.tooltip.once("shown");
swatch.click();
yield onShown;
yield simulateColorPickerChange(picker, [0, 0, 0, 1], {
element: content.document.body,
name: "backgroundImage",
value: 'url("chrome://global/skin/icons/warning-64.png"), linear-gradient(rgb(0, 0, 0), rgb(255, 0, 102) 400px)'
});
@@ -49,12 +49,12 @@ function* testImageTooltipAfterColorChan
let onHidden = picker.tooltip.once("hidden");
EventUtils.sendKey("RETURN", spectrum.element.ownerDocument.defaultView);
yield onHidden;
info("Verify again that the image preview tooltip works");
// After a color change, the property is re-populated, we need to get the new
// dom node
url = getRuleViewProperty(ruleView, "body", "background").valueSpan.querySelector(".theme-link");
- let anchor = yield isHoverTooltipTarget(ruleView.previewTooltip, url);
+ let anchor = yield isHoverTooltipTarget(ruleView.tooltips.previewTooltip, url);
ok(anchor, "The image preview tooltip is shown on the url span");
is(anchor, url, "The anchor returned by the showOnHover callback is correct");
}
--- a/browser/devtools/styleinspector/test/browser_ruleview_colorpicker-and-image-tooltip_02.js
+++ b/browser/devtools/styleinspector/test/browser_ruleview_colorpicker-and-image-tooltip_02.js
@@ -26,17 +26,17 @@ let test = asyncTest(function*() {
yield testColorChangeIsntRevertedWhenOtherTooltipIsShown(view);
});
function* testColorChangeIsntRevertedWhenOtherTooltipIsShown(ruleView) {
let swatch = getRuleViewProperty(ruleView, "body", "background").valueSpan
.querySelector(".ruleview-colorswatch");
info("Open the color picker tooltip and change the color");
- let picker = ruleView.colorPicker;
+ let picker = ruleView.tooltips.colorPicker;
let onShown = picker.tooltip.once("shown");
swatch.click();
yield onShown;
yield simulateColorPickerChange(picker, [0, 0, 0, 1], {
element: content.document.body,
name: "backgroundColor",
value: "rgb(0, 0, 0)"
@@ -44,18 +44,18 @@ function* testColorChangeIsntRevertedWhe
let spectrum = yield picker.spectrum;
let onHidden = picker.tooltip.once("hidden");
EventUtils.sendKey("RETURN", spectrum.element.ownerDocument.defaultView);
yield onHidden;
info("Open the image preview tooltip");
let value = getRuleViewProperty(ruleView, "body", "background").valueSpan;
let url = value.querySelector(".theme-link");
- let onShown = ruleView.previewTooltip.once("shown");
- let anchor = yield isHoverTooltipTarget(ruleView.previewTooltip, url);
- ruleView.previewTooltip.show(anchor);
+ let onShown = ruleView.tooltips.previewTooltip.once("shown");
+ let anchor = yield isHoverTooltipTarget(ruleView.tooltips.previewTooltip, url);
+ ruleView.tooltips.previewTooltip.show(anchor);
yield onShown;
info("Image tooltip is shown, verify that the swatch is still correct");
let swatch = value.querySelector(".ruleview-colorswatch");
is(swatch.style.backgroundColor, "rgb(0, 0, 0)", "The swatch's color is correct");
is(swatch.nextSibling.textContent, "#000", "The color name is correct");
}
--- a/browser/devtools/styleinspector/test/browser_ruleview_colorpicker-appears-on-swatch-click.js
+++ b/browser/devtools/styleinspector/test/browser_ruleview_colorpicker-appears-on-swatch-click.js
@@ -32,17 +32,17 @@ let test = asyncTest(function*() {
for (let swatch of [cSwatch, bgSwatch, bSwatch]) {
info("Testing that the colorpicker appears colorswatch click");
yield testColorPickerAppearsOnColorSwatchClick(view, swatch);
}
});
function* testColorPickerAppearsOnColorSwatchClick(view, swatch) {
- let cPicker = view.colorPicker;
+ let cPicker = view.tooltips.colorPicker;
ok(cPicker, "The rule-view has the expected colorPicker property");
let cPickerPanel = cPicker.tooltip.panel;
ok(cPickerPanel, "The XUL panel for the color picker exists");
let onShown = cPicker.tooltip.once("shown");
swatch.click();
yield onShown;
--- a/browser/devtools/styleinspector/test/browser_ruleview_colorpicker-commit-on-ENTER.js
+++ b/browser/devtools/styleinspector/test/browser_ruleview_colorpicker-commit-on-ENTER.js
@@ -21,17 +21,17 @@ let test = asyncTest(function*() {
let {toolbox, inspector, view} = yield openRuleView();
let swatch = getRuleViewProperty(view, "body" , "border").valueSpan
.querySelector(".ruleview-colorswatch");
yield testPressingEnterCommitsChanges(swatch, view);
});
function* testPressingEnterCommitsChanges(swatch, ruleView) {
- let cPicker = ruleView.colorPicker;
+ let cPicker = ruleView.tooltips.colorPicker;
let onShown = cPicker.tooltip.once("shown");
swatch.click();
yield onShown;
yield simulateColorPickerChange(cPicker, [0, 255, 0, .5], {
element: content.document.body,
name: "borderLeftColor",
--- a/browser/devtools/styleinspector/test/browser_ruleview_colorpicker-edit-gradient.js
+++ b/browser/devtools/styleinspector/test/browser_ruleview_colorpicker-edit-gradient.js
@@ -52,17 +52,17 @@ function testColorParsing(view) {
function* testPickingNewColor(view) {
// Grab the first color swatch and color in the gradient
let ruleEl = getRuleViewProperty(view, "body", "background-image");
let swatchEl = ruleEl.valueSpan.querySelector(".ruleview-colorswatch");
let colorEl = ruleEl.valueSpan.querySelector(".ruleview-color");
info("Getting the color picker tooltip and clicking on the swatch to show it");
- let cPicker = view.colorPicker;
+ let cPicker = view.tooltips.colorPicker;
let onShown = cPicker.tooltip.once("shown");
swatchEl.click();
yield onShown;
yield simulateColorPickerChange(cPicker, [1, 1, 1, 1]);
is(swatchEl.style.backgroundColor, "rgb(1, 1, 1)",
"The color swatch's background was updated");
--- a/browser/devtools/styleinspector/test/browser_ruleview_colorpicker-hides-on-tooltip.js
+++ b/browser/devtools/styleinspector/test/browser_ruleview_colorpicker-hides-on-tooltip.js
@@ -27,22 +27,22 @@ let test = asyncTest(function*() {
.querySelector(".ruleview-colorswatch");
yield testColorPickerHidesWhenImageTooltipAppears(view, swatch);
});
function* testColorPickerHidesWhenImageTooltipAppears(view, swatch) {
let bgImageSpan = getRuleViewProperty(view, "body", "background-image").valueSpan;
let uriSpan = bgImageSpan.querySelector(".theme-link");
- let tooltip = view.colorPicker.tooltip;
+ let tooltip = view.tooltips.colorPicker.tooltip;
info("Showing the color picker tooltip by clicking on the color swatch");
let onShown = tooltip.once("shown");
swatch.click();
yield onShown;
info("Now showing the image preview tooltip to hide the color picker");
let onHidden = tooltip.once("hidden");
- yield assertHoverTooltipOn(view.previewTooltip, uriSpan);
+ yield assertHoverTooltipOn(view.tooltips.previewTooltip, uriSpan);
yield onHidden;
ok(true, "The color picker closed when the image preview tooltip appeared");
}
--- a/browser/devtools/styleinspector/test/browser_ruleview_colorpicker-multiple-changes.js
+++ b/browser/devtools/styleinspector/test/browser_ruleview_colorpicker-multiple-changes.js
@@ -37,17 +37,17 @@ let test = asyncTest(function*() {
function* testSimpleMultipleColorChanges(inspector, ruleView) {
yield selectNode("p", inspector);
info("Getting the <p> tag's color property");
let swatch = getRuleViewProperty(ruleView, "p", "color").valueSpan
.querySelector(".ruleview-colorswatch");
info("Opening the color picker");
- let picker = ruleView.colorPicker;
+ let picker = ruleView.tooltips.colorPicker;
let onShown = picker.tooltip.once("shown");
swatch.click();
yield onShown;
info("Changing the color several times");
let colors = [
{rgba: [0, 0, 0, 1], computed: "rgb(0, 0, 0)"},
{rgba: [100, 100, 100, 1], computed: "rgb(100, 100, 100)"},
@@ -65,17 +65,17 @@ function* testSimpleMultipleColorChanges
function* testComplexMultipleColorChanges(inspector, ruleView) {
yield selectNode("body", inspector);
info("Getting the <body> tag's color property");
let swatch = getRuleViewProperty(ruleView, "body", "background").valueSpan
.querySelector(".ruleview-colorswatch");
info("Opening the color picker");
- let picker = ruleView.colorPicker;
+ let picker = ruleView.tooltips.colorPicker;
let onShown = picker.tooltip.once("shown");
swatch.click();
yield onShown;
info("Changing the color several times");
let colors = [
{rgba: [0, 0, 0, 1], computed: "rgb(0, 0, 0)"},
{rgba: [100, 100, 100, 1], computed: "rgb(100, 100, 100)"},
@@ -98,17 +98,17 @@ function* testComplexMultipleColorChange
function* testOverriddenMultipleColorChanges(inspector, ruleView) {
yield selectNode("p", inspector);
info("Getting the <body> tag's color property");
let swatch = getRuleViewProperty(ruleView, "body", "color").valueSpan
.querySelector(".ruleview-colorswatch");
info("Opening the color picker");
- let picker = ruleView.colorPicker;
+ let picker = ruleView.tooltips.colorPicker;
let onShown = picker.tooltip.once("shown");
swatch.click();
yield onShown;
info("Changing the color several times");
let colors = [
{rgba: [0, 0, 0, 1], computed: "rgb(0, 0, 0)"},
{rgba: [100, 100, 100, 1], computed: "rgb(100, 100, 100)"},
--- a/browser/devtools/styleinspector/test/browser_ruleview_colorpicker-revert-on-ESC.js
+++ b/browser/devtools/styleinspector/test/browser_ruleview_colorpicker-revert-on-ESC.js
@@ -21,17 +21,17 @@ let test = asyncTest(function*() {
let {toolbox, inspector, view} = yield openRuleView();
let swatch = getRuleViewProperty(view, "body", "background-color").valueSpan
.querySelector(".ruleview-colorswatch");
yield testPressingEscapeRevertsChanges(swatch, view);
});
function* testPressingEscapeRevertsChanges(swatch, ruleView) {
- let cPicker = ruleView.colorPicker;
+ let cPicker = ruleView.tooltips.colorPicker;
let onShown = cPicker.tooltip.once("shown");
swatch.click();
yield onShown;
yield simulateColorPickerChange(cPicker, [0, 0, 0, 1], {
element: content.document.body,
name: "backgroundColor",
--- a/browser/devtools/styleinspector/test/browser_ruleview_eyedropper.js
+++ b/browser/devtools/styleinspector/test/browser_ruleview_eyedropper.js
@@ -35,17 +35,17 @@ let test = asyncTest(function*() {
yield inspector.once("inspector-updated");
let property = getRuleViewProperty(view, "div", "background-color");
let swatch = property.valueSpan.querySelector(".ruleview-colorswatch");
ok(swatch, "Color swatch is displayed for the bg-color property");
let dropper = yield openEyedropper(view, swatch);
- let tooltip = view.colorPicker.tooltip;
+ let tooltip = view.tooltips.colorPicker.tooltip;
ok(tooltip.isHidden(),
"color picker tooltip is closed after opening eyedropper");
yield testESC(swatch, dropper);
dropper = yield openEyedropper(view, swatch);
ok(dropper, "dropper opened");
@@ -93,17 +93,17 @@ function testSelect(swatch, dropper) {
}
/* Helpers */
function openEyedropper(view, swatch) {
let deferred = promise.defer();
- let tooltip = view.colorPicker.tooltip;
+ let tooltip = view.tooltips.colorPicker.tooltip;
tooltip.once("shown", () => {
let tooltipDoc = tooltip.content.contentDocument;
let dropperButton = tooltipDoc.querySelector("#eyedropper-button");
tooltip.once("eyedropper-opened", (event, dropper) => {
deferred.resolve(dropper)
});
--- a/browser/devtools/styleinspector/test/browser_ruleview_user-agent-styles-uneditable.js
+++ b/browser/devtools/styleinspector/test/browser_ruleview_user-agent-styles-uneditable.js
@@ -39,12 +39,12 @@ function* userAgentStylesUneditable(insp
ok (rule.editor.element.hasAttribute("uneditable"), "UA rules have uneditable attribute");
ok (!rule.textProps[0].editor.nameSpan._editable, "nameSpan is not editable");
ok (!rule.textProps[0].editor.valueSpan._editable, "valueSpan is not editable");
ok (!rule.editor.closeBrace._editable, "closeBrace is not editable");
let colorswatch = rule.editor.element.querySelector(".ruleview-colorswatch");
if (colorswatch) {
- ok (!view.colorPicker.swatches.has(colorswatch), "The swatch is not editable");
+ ok (!view.tooltips.colorPicker.swatches.has(colorswatch), "The swatch is not editable");
}
}
}
--- a/browser/devtools/styleinspector/test/browser_styleinspector_context-menu-copy-color_02.js
+++ b/browser/devtools/styleinspector/test/browser_styleinspector_context-menu-copy-color_02.js
@@ -77,17 +77,17 @@ function* testManualEdit(inspector, view
function* testColorPickerEdit(inspector, view) {
info("Testing colors edited via color picker");
yield selectNode("div", inspector);
let swatch = getRuleViewProperty(view, "div", "color").valueSpan
.querySelector(".ruleview-colorswatch");
info("Opening the color picker");
- let picker = view.colorPicker;
+ let picker = view.tooltips.colorPicker;
let onShown = picker.tooltip.once("shown");
swatch.click();
yield onShown;
let rgbaColor = [83, 183, 89, 1];
let rgbaColorText = "rgba(83, 183, 89, 1)";
yield simulateColorPickerChange(picker, rgbaColor);
--- a/browser/devtools/styleinspector/test/browser_styleinspector_tooltip-background-image.js
+++ b/browser/devtools/styleinspector/test/browser_styleinspector_tooltip-background-image.js
@@ -45,77 +45,77 @@ let test = asyncTest(function*() {
let {view} = yield openComputedView();
info("Testing that the background-image computed style has a tooltip too");
yield testComputedView(view);
});
function* testBodyRuleView(view) {
info("Testing tooltips in the rule view");
- let panel = view.previewTooltip.panel;
+ let panel = view.tooltips.previewTooltip.panel;
// Check that the rule view has a tooltip and that a XUL panel has been created
- ok(view.previewTooltip, "Tooltip instance exists");
+ ok(view.tooltips.previewTooltip, "Tooltip instance exists");
ok(panel, "XUL panel exists");
// Get the background-image property inside the rule view
let {valueSpan} = getRuleViewProperty(view, "body", "background-image");
let uriSpan = valueSpan.querySelector(".theme-link");
- yield assertHoverTooltipOn(view.previewTooltip, uriSpan);
+ yield assertHoverTooltipOn(view.tooltips.previewTooltip, uriSpan);
let images = panel.getElementsByTagName("image");
is(images.length, 1, "Tooltip contains an image");
ok(images[0].getAttribute("src").indexOf("iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHe") !== -1,
"The image URL seems fine");
}
function* testDivRuleView(view) {
- let panel = view.previewTooltip.panel;
+ let panel = view.tooltips.previewTooltip.panel;
// Get the background property inside the rule view
let {valueSpan} = getRuleViewProperty(view, ".test-element", "background");
let uriSpan = valueSpan.querySelector(".theme-link");
- yield assertHoverTooltipOn(view.previewTooltip, uriSpan);
+ yield assertHoverTooltipOn(view.tooltips.previewTooltip, uriSpan);
let images = panel.getElementsByTagName("image");
is(images.length, 1, "Tooltip contains an image");
ok(images[0].getAttribute("src").startsWith("data:"), "Tooltip contains a data-uri image as expected");
}
function* testTooltipAppearsEvenInEditMode(view) {
- let panel = view.previewTooltip.panel;
+ let panel = view.tooltips.previewTooltip.panel;
info("Switching to edit mode in the rule view");
let editor = yield turnToEditMode(view);
info("Now trying to show the preview tooltip");
let {valueSpan} = getRuleViewProperty(view, ".test-element", "background");
let uriSpan = valueSpan.querySelector(".theme-link");
- yield assertHoverTooltipOn(view.previewTooltip, uriSpan);
+ yield assertHoverTooltipOn(view.tooltips.previewTooltip, uriSpan);
is(view.doc.activeElement, editor.input,
"Tooltip was shown in edit mode, and inplace-editor still focused");
}
function turnToEditMode(ruleView) {
let brace = ruleView.doc.querySelector(".ruleview-ruleclose");
return focusEditableField(brace);
}
function* testComputedView(view) {
- let tooltip = view.tooltip;
+ let tooltip = view.tooltips.previewTooltip;
ok(tooltip, "The computed-view has a tooltip defined");
let panel = tooltip.panel;
ok(panel, "The computed-view tooltip has a XUL panel");
let {valueSpan} = getComputedViewProperty(view, "background-image");
let uriSpan = valueSpan.querySelector(".theme-link");
- yield assertHoverTooltipOn(view.tooltip, uriSpan);
+ yield assertHoverTooltipOn(view.tooltips.previewTooltip, uriSpan);
let images = panel.getElementsByTagName("image");
is(images.length, 1, "Tooltip contains an image");
ok(images[0].getAttribute("src").startsWith("data:"), "Tooltip contains a data-uri in the computed-view too");
}
--- a/browser/devtools/styleinspector/test/browser_styleinspector_tooltip-closes-on-new-selection.js
+++ b/browser/devtools/styleinspector/test/browser_styleinspector_tooltip-closes-on-new-selection.js
@@ -17,31 +17,31 @@ let test = asyncTest(function*() {
info("Testing computed view tooltip closes on new selection");
let {view} = yield openComputedView();
yield testComputedView(view, inspector);
});
function* testRuleView(ruleView, inspector) {
info("Showing the tooltip");
- let tooltip = ruleView.previewTooltip;
+ let tooltip = ruleView.tooltips.previewTooltip;
let onShown = tooltip.once("shown");
tooltip.show();
yield onShown;
info("Selecting a new node");
let onHidden = tooltip.once("hidden");
yield selectNode(".two", inspector);
ok(true, "Rule view tooltip closed after a new node got selected");
}
function* testComputedView(computedView, inspector) {
info("Showing the tooltip");
- let tooltip = computedView.tooltip;
+ let tooltip = computedView.tooltips.previewTooltip;
let onShown = tooltip.once("shown");
tooltip.show();
yield onShown;
info("Selecting a new node");
let onHidden = tooltip.once("hidden");
yield selectNode(".one", inspector);
--- a/browser/devtools/styleinspector/test/browser_styleinspector_tooltip-longhand-fontfamily.js
+++ b/browser/devtools/styleinspector/test/browser_styleinspector_tooltip-longhand-fontfamily.js
@@ -35,43 +35,45 @@ let test = asyncTest(function*() {
let {toolbox, inspector, view} = yield openComputedView();
yield testComputedView(view, inspector.selection.nodeFront);
});
function* testRuleView(ruleView, nodeFront) {
info("Testing font-family tooltips in the rule view");
- let panel = ruleView.previewTooltip.panel;
+ let tooltip = ruleView.tooltips.previewTooltip;
+ let panel = tooltip.panel;
// Check that the rule view has a tooltip and that a XUL panel has been created
- ok(ruleView.previewTooltip, "Tooltip instance exists");
+ ok(tooltip, "Tooltip instance exists");
ok(panel, "XUL panel exists");
// Get the font family property inside the rule view
let {valueSpan} = getRuleViewProperty(ruleView, "#testElement", "font-family");
// And verify that the tooltip gets shown on this property
- yield assertHoverTooltipOn(ruleView.previewTooltip, valueSpan);
+ yield assertHoverTooltipOn(tooltip, valueSpan);
let images = panel.getElementsByTagName("image");
is(images.length, 1, "Tooltip contains an image");
ok(images[0].getAttribute("src").startsWith("data:"), "Tooltip contains a data-uri image as expected");
let dataURL = yield getFontFamilyDataURL(valueSpan.textContent, nodeFront);
is(images[0].getAttribute("src"), dataURL, "Tooltip contains the correct data-uri image");
}
function* testComputedView(computedView, nodeFront) {
info("Testing font-family tooltips in the computed view");
- let panel = computedView.tooltip.panel;
+ let tooltip = computedView.tooltips.previewTooltip;
+ let panel = tooltip.panel;
let {valueSpan} = getComputedViewProperty(computedView, "font-family");
- yield assertHoverTooltipOn(computedView.tooltip, valueSpan);
+ yield assertHoverTooltipOn(tooltip, valueSpan);
let images = panel.getElementsByTagName("image");
is(images.length, 1, "Tooltip contains an image");
ok(images[0].getAttribute("src").startsWith("data:"), "Tooltip contains a data-uri image as expected");
let dataURL = yield getFontFamilyDataURL(valueSpan.textContent, nodeFront);
is(images[0].getAttribute("src"), dataURL, "Tooltip contains the correct data-uri image");
}
--- a/browser/devtools/styleinspector/test/browser_styleinspector_tooltip-shorthand-fontfamily.js
+++ b/browser/devtools/styleinspector/test/browser_styleinspector_tooltip-shorthand-fontfamily.js
@@ -33,48 +33,51 @@ let test = asyncTest(function*() {
let {toolbox, inspector, view} = yield openComputedView();
yield testComputedView(view, inspector.selection.nodeFront);
});
function* testRuleView(ruleView, nodeFront) {
info("Testing font-family tooltips in the rule view");
- let panel = ruleView.previewTooltip.panel;
+ let tooltip = ruleView.tooltips.previewTooltip;
+ let panel = tooltip.panel;
// Check that the rule view has a tooltip and that a XUL panel has been created
- ok(ruleView.previewTooltip, "Tooltip instance exists");
+ ok(tooltip, "Tooltip instance exists");
ok(panel, "XUL panel exists");
// Get the computed font family property inside the font rule view
let propertyList = ruleView.element.querySelectorAll(".ruleview-propertylist");
let fontExpander = propertyList[1].querySelectorAll(".ruleview-expander")[0];
fontExpander.click();
let rule = getRuleViewRule(ruleView, "#testElement");
let valueSpan = rule.querySelector(".ruleview-computed .ruleview-propertyvalue");
// And verify that the tooltip gets shown on this property
- yield assertHoverTooltipOn(ruleView.previewTooltip, valueSpan);
+ yield assertHoverTooltipOn(tooltip, valueSpan);
let images = panel.getElementsByTagName("image");
is(images.length, 1, "Tooltip contains an image");
ok(images[0].getAttribute("src").startsWith("data:"), "Tooltip contains a data-uri image as expected");
let dataURL = yield getFontFamilyDataURL(valueSpan.textContent, nodeFront);
is(images[0].getAttribute("src"), dataURL, "Tooltip contains the correct data-uri image");
}
function* testComputedView(computedView, nodeFront) {
info("Testing font-family tooltips in the computed view");
- let panel = computedView.tooltip.panel;
+ let tooltip = computedView.tooltips.previewTooltip;
+ let panel = tooltip.panel;
+
let {valueSpan} = getComputedViewProperty(computedView, "font-family");
- yield assertHoverTooltipOn(computedView.tooltip, valueSpan);
+ yield assertHoverTooltipOn(tooltip, valueSpan);
let images = panel.getElementsByTagName("image");
is(images.length, 1, "Tooltip contains an image");
ok(images[0].getAttribute("src").startsWith("data:"), "Tooltip contains a data-uri image as expected");
let dataURL = yield getFontFamilyDataURL(valueSpan.textContent, nodeFront);
is(images[0].getAttribute("src"), dataURL, "Tooltip contains the correct data-uri image");
}
--- a/browser/devtools/styleinspector/test/browser_styleinspector_tooltip-size.js
+++ b/browser/devtools/styleinspector/test/browser_styleinspector_tooltip-size.js
@@ -26,17 +26,17 @@ let test = asyncTest(function*() {
yield testImageDimension(view);
yield testPickerDimension(view);
});
function* testImageDimension(ruleView) {
info("Testing background-image tooltip dimensions");
- let tooltip = ruleView.previewTooltip;
+ let tooltip = ruleView.tooltips.previewTooltip;
let panel = tooltip.panel;
let {valueSpan} = getRuleViewProperty(ruleView, "div", "background");
let uriSpan = valueSpan.querySelector(".theme-link");
// Make sure there is a hover tooltip for this property, this also will fill
// the tooltip with its content
yield assertHoverTooltipOn(tooltip, uriSpan);
@@ -60,17 +60,17 @@ function* testImageDimension(ruleView) {
yield onHidden;
}
function* testPickerDimension(ruleView) {
info("Testing color-picker tooltip dimensions");
let {valueSpan} = getRuleViewProperty(ruleView, "div", "background");
let swatch = valueSpan.querySelector(".ruleview-colorswatch");
- let cPicker = ruleView.colorPicker;
+ let cPicker = ruleView.tooltips.colorPicker;
let onShown = cPicker.tooltip.once("shown");
swatch.click();
yield onShown;
// The colorpicker spectrum's iframe has a fixed width height, so let's
// make sure the tooltip is at least as big as that
let w = cPicker.tooltip.panel.querySelector("iframe").width;
--- a/browser/devtools/styleinspector/test/browser_styleinspector_transform-highlighter-01.js
+++ b/browser/devtools/styleinspector/test/browser_styleinspector_transform-highlighter-01.js
@@ -10,29 +10,33 @@ const PAGE_CONTENT = [
'<style type="text/css">',
' body {',
' transform: skew(16deg);',
' }',
'</style>',
'Test the css transform highlighter'
].join("\n");
+const TYPE = "CssTransformHighlighter";
+
let test = asyncTest(function*() {
yield addTab("data:text/html," + PAGE_CONTENT);
let {view: rView} = yield openRuleView();
+ let overlay = rView.highlighters;
- ok(!rView.transformHighlighter, "No highlighter exists in the rule-view");
- let h = yield rView.getTransformHighlighter();
- ok(rView.transformHighlighter, "The highlighter has been created in the rule-view");
- is(h, rView.transformHighlighter, "The right highlighter has been created");
- let h2 = yield rView.getTransformHighlighter();
+ ok(!overlay.highlighters[TYPE], "No highlighter exists in the rule-view");
+ let h = yield overlay._getHighlighter(TYPE);
+ ok(overlay.highlighters[TYPE], "The highlighter has been created in the rule-view");
+ is(h, overlay.highlighters[TYPE], "The right highlighter has been created");
+ let h2 = yield overlay._getHighlighter(TYPE);
is(h, h2, "The same instance of highlighter is returned everytime in the rule-view");
let {view: cView} = yield openComputedView();
+ let overlay = cView.highlighters;
- ok(!cView.transformHighlighter, "No highlighter exists in the computed-view");
- let h = yield cView.getTransformHighlighter();
- ok(cView.transformHighlighter, "The highlighter has been created in the computed-view");
- is(h, cView.transformHighlighter, "The right highlighter has been created");
- let h2 = yield cView.getTransformHighlighter();
+ ok(!overlay.highlighters[TYPE], "No highlighter exists in the computed-view");
+ let h = yield overlay._getHighlighter(TYPE);
+ ok(overlay.highlighters[TYPE], "The highlighter has been created in the computed-view");
+ is(h, overlay.highlighters[TYPE], "The right highlighter has been created");
+ let h2 = yield overlay._getHighlighter(TYPE);
is(h, h2, "The same instance of highlighter is returned everytime in the computed-view");
});
--- a/browser/devtools/styleinspector/test/browser_styleinspector_transform-highlighter-02.js
+++ b/browser/devtools/styleinspector/test/browser_styleinspector_transform-highlighter-02.js
@@ -12,43 +12,52 @@ const PAGE_CONTENT = [
' body {',
' transform: skew(16deg);',
' color: yellow;',
' }',
'</style>',
'Test the css transform highlighter'
].join("\n");
+let TYPE = "CssTransformHighlighter";
+
let test = asyncTest(function*() {
yield addTab("data:text/html," + PAGE_CONTENT);
+
let {view: rView} = yield openRuleView();
- ok(!rView.transformHighlighter, "No highlighter exists in the rule-view (1)");
+ let hs = rView.highlighters;
+
+ ok(!hs.highlighters[TYPE], "No highlighter exists in the rule-view (1)");
+ ok(!hs.promises[TYPE], "No highlighter is being created in the rule-view (1)");
info("Faking a mousemove on a non-transform property");
let {valueSpan} = getRuleViewProperty(rView, "body", "color");
- rView._onMouseMove({target: valueSpan});
- ok(!rView.transformHighlighter, "No highlighter exists in the rule-view (2)");
- ok(!rView.transformHighlighterPromise, "No highlighter is being initialized");
+ hs._onMouseMove({target: valueSpan});
+ ok(!hs.highlighters[TYPE], "No highlighter exists in the rule-view (2)");
+ ok(!hs.promises[TYPE], "No highlighter is being created in the rule-view (2)");
info("Faking a mousemove on a transform property");
let {valueSpan} = getRuleViewProperty(rView, "body", "transform");
- rView._onMouseMove({target: valueSpan});
- ok(rView.transformHighlighterPromise, "The highlighter is being initialized");
- let h = yield rView.transformHighlighterPromise;
- is(h, rView.transformHighlighter, "The initialized highlighter is the right one");
+ hs._onMouseMove({target: valueSpan});
+ ok(hs.promises[TYPE], "The highlighter is being initialized");
+ let h = yield hs.promises[TYPE];
+ is(h, hs.highlighters[TYPE], "The initialized highlighter is the right one");
let {view: cView} = yield openComputedView();
- ok(!cView.transformHighlighter, "No highlighter exists in the computed-view (1)");
+ let hs = cView.highlighters;
+
+ ok(!hs.highlighters[TYPE], "No highlighter exists in the computed-view (1)");
+ ok(!hs.promises[TYPE], "No highlighter is being created in the computed-view (1)");
info("Faking a mousemove on a non-transform property");
let {valueSpan} = getComputedViewProperty(cView, "color");
- cView._onMouseMove({target: valueSpan});
- ok(!cView.transformHighlighter, "No highlighter exists in the computed-view (2)");
- ok(!cView.transformHighlighterPromise, "No highlighter is being initialized");
+ hs._onMouseMove({target: valueSpan});
+ ok(!hs.highlighters[TYPE], "No highlighter exists in the computed-view (2)");
+ ok(!hs.promises[TYPE], "No highlighter is being created in the computed-view (2)");
info("Faking a mousemove on a transform property");
let {valueSpan} = getComputedViewProperty(cView, "transform");
- cView._onMouseMove({target: valueSpan});
- ok(cView.transformHighlighterPromise, "The highlighter is being initialized");
- let h = yield cView.transformHighlighterPromise;
- is(h, cView.transformHighlighter, "The initialized highlighter is the right one");
+ hs._onMouseMove({target: valueSpan});
+ ok(hs.promises[TYPE], "The highlighter is being initialized");
+ let h = yield hs.promises[TYPE];
+ is(h, hs.highlighters[TYPE], "The initialized highlighter is the right one");
});
--- a/browser/devtools/styleinspector/test/browser_styleinspector_transform-highlighter-03.js
+++ b/browser/devtools/styleinspector/test/browser_styleinspector_transform-highlighter-03.js
@@ -18,16 +18,18 @@ const PAGE_CONTENT = [
' body {',
' transform: skew(16deg);',
' color: purple;',
' }',
'</style>',
'Test the css transform highlighter'
].join("\n");
+const TYPE = "CssTransformHighlighter";
+
let test = asyncTest(function*() {
yield addTab("data:text/html," + PAGE_CONTENT);
let {inspector, view: rView} = yield openRuleView();
// Mock the highlighter front to get the reference of the NodeFront
let HighlighterFront = {
isShown: false,
@@ -40,50 +42,50 @@ let test = asyncTest(function*() {
},
hide: function() {
this.nodeFront = null;
this.isShown = false;
}
};
// Inject the mock highlighter in the rule-view
- rView.transformHighlighterPromise = {
+ rView.highlighters.promises[TYPE] = {
then: function(cb) {
cb(HighlighterFront);
}
};
let {valueSpan} = getRuleViewProperty(rView, "body", "transform");
info("Checking that the HighlighterFront's show/hide methods are called");
- rView._onMouseMove({target: valueSpan});
+ rView.highlighters._onMouseMove({target: valueSpan});
ok(HighlighterFront.isShown, "The highlighter is shown");
- rView._onMouseLeave();
+ rView.highlighters._onMouseLeave();
ok(!HighlighterFront.isShown, "The highlighter is hidden");
info("Checking that hovering several times over the same property doesn't" +
" show the highlighter several times");
let nb = HighlighterFront.nbOfTimesShown;
- rView._onMouseMove({target: valueSpan});
+ rView.highlighters._onMouseMove({target: valueSpan});
is(HighlighterFront.nbOfTimesShown, nb + 1, "The highlighter was shown once");
- rView._onMouseMove({target: valueSpan});
- rView._onMouseMove({target: valueSpan});
+ rView.highlighters._onMouseMove({target: valueSpan});
+ rView.highlighters._onMouseMove({target: valueSpan});
is(HighlighterFront.nbOfTimesShown, nb + 1,
"The highlighter was shown once, after several mousemove");
info("Checking that the right NodeFront reference is passed");
yield selectNode(content.document.documentElement, inspector);
let {valueSpan} = getRuleViewProperty(rView, "html", "transform");
- rView._onMouseMove({target: valueSpan});
+ rView.highlighters._onMouseMove({target: valueSpan});
is(HighlighterFront.nodeFront.tagName, "HTML",
"The right NodeFront is passed to the highlighter (1)");
yield selectNode("body", inspector);
let {valueSpan} = getRuleViewProperty(rView, "body", "transform");
- rView._onMouseMove({target: valueSpan});
+ rView.highlighters._onMouseMove({target: valueSpan});
is(HighlighterFront.nodeFront.tagName, "BODY",
"The right NodeFront is passed to the highlighter (2)");
info("Checking that the highlighter gets hidden when hovering a non-transform property");
let {valueSpan} = getRuleViewProperty(rView, "body", "color");
- rView._onMouseMove({target: valueSpan});
+ rView.highlighters._onMouseMove({target: valueSpan});
ok(!HighlighterFront.isShown, "The highlighter is hidden");
});
--- a/browser/devtools/styleinspector/test/browser_styleinspector_transform-highlighter-04.js
+++ b/browser/devtools/styleinspector/test/browser_styleinspector_transform-highlighter-04.js
@@ -20,39 +20,43 @@ const PAGE_CONTENT = [
' }',
' .test {',
' transform: skew(25deg);',
' }',
'</style>',
'<div class="test"></div>'
].join("\n");
+const TYPE = "CssTransformHighlighter";
+
let test = asyncTest(function*() {
yield addTab("data:text/html," + PAGE_CONTENT);
let {view: rView, inspector} = yield openRuleView();
yield selectNode(".test", inspector);
+ let hs = rView.highlighters;
+
info("Faking a mousemove on the overriden property");
let {valueSpan} = getRuleViewProperty(rView, "div", "transform");
- rView._onMouseMove({target: valueSpan});
- ok(!rView.transformHighlighter, "No highlighter was created for the overriden property");
- ok(!rView.transformHighlighterPromise, "And no highlighter is being initialized either");
+ hs._onMouseMove({target: valueSpan});
+ ok(!hs.highlighters[TYPE], "No highlighter was created for the overriden property");
+ ok(!hs.promises[TYPE], "And no highlighter is being initialized either");
info("Disabling the applied property");
let classRuleEditor = rView.element.children[1]._ruleEditor;
let propEditor = classRuleEditor.rule.textProps[0].editor;
propEditor.enable.click();
yield classRuleEditor.rule._applyingModifications;
info("Faking a mousemove on the disabled property");
let {valueSpan} = getRuleViewProperty(rView, ".test", "transform");
- rView._onMouseMove({target: valueSpan});
- ok(!rView.transformHighlighter, "No highlighter was created for the disabled property");
- ok(!rView.transformHighlighterPromise, "And no highlighter is being initialized either");
+ hs._onMouseMove({target: valueSpan});
+ ok(!hs.highlighters[TYPE], "No highlighter was created for the disabled property");
+ ok(!hs.promises[TYPE], "And no highlighter is being initialized either");
info("Faking a mousemove on the now unoverriden property");
let {valueSpan} = getRuleViewProperty(rView, "div", "transform");
- rView._onMouseMove({target: valueSpan});
- ok(rView.transformHighlighterPromise, "The highlighter is being initialized now");
- let h = yield rView.transformHighlighterPromise;
- is(h, rView.transformHighlighter, "The initialized highlighter is the right one");
+ hs._onMouseMove({target: valueSpan});
+ ok(hs.promises[TYPE], "The highlighter is being initialized now");
+ let h = yield hs.promises[TYPE];
+ is(h, hs.highlighters[TYPE], "The initialized highlighter is the right one");
});
--- a/browser/themes/shared/devtools/styleeditor.css
+++ b/browser/themes/shared/devtools/styleeditor.css
@@ -60,17 +60,17 @@
font-style: italic;
}
.splitview-nav.empty > p {
padding: 0 10px;
}
.stylesheet-sidebar {
- width: 230px;
+ width: 237px;
-moz-border-start: 1px solid;
}
.theme-light .stylesheet-sidebar {
border-color: #aaa; /* Splitters */
}
.theme-dark .stylesheet-sidebar {
@@ -86,16 +86,20 @@
}
.media-rule-label {
padding: 4px;
cursor: pointer;
border-bottom: 1px solid;
}
+.media-rule-line {
+ -moz-padding-start: 4px;
+}
+
.theme-light .media-condition-unmatched {
color: grey;
}
.theme-dark .media-condition-unmatched {
color: #606C75;
}
--- a/dom/apps/src/OperatorApps.jsm
+++ b/dom/apps/src/OperatorApps.jsm
@@ -117,16 +117,21 @@ this.OperatorAppsRegistry = {
if (iccInfo && iccInfo.mcc) {
mcc = iccInfo.mcc;
}
if (iccInfo && iccInfo.mnc) {
mnc = iccInfo.mnc;
}
if (mcc && mnc) {
this._installOperatorApps(mcc, mnc);
+ let messenger = Cc["@mozilla.org/system-message-internal;1"]
+ .getService(Ci.nsISystemMessagesInternal);
+ messenger.broadcastMessage("first-run-with-sim", { mcc: mcc,
+ mnc: mnc });
+
} else {
iccProvider.registerIccMsg(clientId, iccListener);
}
} catch (e) {
debug("Error Initializing OperatorApps. " + e);
}
}.bind(this));
} else {
@@ -230,17 +235,17 @@ this.OperatorAppsRegistry = {
}
},
get appsDir() {
return this._baseDirectory;
},
eraseVariantAppsNotInList: function(aIdsApp) {
- if (!aIdsApp || !Array.isArray(aIdsApp)) {
+ if (!aIdsApp) {
aIdsApp = [ ];
}
let svDir;
try {
svDir = this.appsDir.clone();
} catch (e) {
debug("eraseVariantAppsNotInList --> Error getting Dir "+
@@ -301,24 +306,60 @@ this.OperatorAppsRegistry = {
} else {
debug("aId:" + aId + ". Installing as hosted app.");
appData.app.manifest = aManifest;
DOMApplicationRegistry.confirmInstall(appData);
}
},
_installOperatorApps: function(aMcc, aMnc) {
+ function normalizeCode(aCode) {
+ let ncode = "" + aCode;
+ while (ncode.length < 3) {
+ ncode = "0" + ncode;
+ }
+ return ncode;
+ }
+
Task.spawn(function() {
debug("Install operator apps ---> mcc:"+ aMcc + ", mnc:" + aMnc);
if (!isFirstRunWithSIM()) {
debug("Operator apps already installed.");
return;
}
- let aIdsApp = yield this._getSingleVariantApps(aMcc, aMnc);
+ let key = normalizeCode(aMcc) + "-" + normalizeCode(aMnc);
+ let aIdsApp = yield this._getSingleVariantDatas();
+
+ // aIdsApp will be undefined if the singleVariant config file not exist
+ // or will have the following format:
+ // {"mmc1-mnc1": [ap11,...,ap1N],..., "mmcM-mncM": [apM1,...,apMN]}
+ // Behavior:
+ // * If the configuration file does not exist, it's equivalent to
+ // passing []
+ // * If the configuration file has data and the phone boots with a SIM
+ // that isn't on the configuration file then we must have the same
+ // behavior as if the phone had booted without a SIM inserted
+ // (that is, don't do anything)
+ // * If the phone boots with a configured SIM (mcc-mnc exists on
+ // configuration file) then recover the app list to install
+ if (!aIdsApp) {
+ debug("No " + SINGLE_VARIANT_CONF_FILE + " in " + this.appsDir.path);
+ aIdsApp = [];
+ } else if (aIdsApp[key] === undefined) {
+ debug("First Run with SIM not configured");
+ return;
+ } else {
+ debug("First Run with configured SIM ");
+ aIdsApp = aIdsApp[key];
+ if (!Array.isArray(aIdsApp)) {
+ aIdsApp = [aIdsApp];
+ }
+ }
+
debug("installOperatorApps --> aIdsApp:" + JSON.stringify(aIdsApp));
for (let i = 0; i < aIdsApp.length; i++) {
let aId = aIdsApp[i];
let aMetadata = yield AppsUtils.loadJSONAsync(
Path.join(this.appsDir.path, aId, METADATA));
if (!aMetadata) {
debug("Error reading metadata file");
return;
@@ -348,33 +389,18 @@ this.OperatorAppsRegistry = {
this.eraseVariantAppsNotInList(aIdsApp);
Services.prefs.setBoolPref(PREF_FIRST_RUN_WITH_SIM, false);
Services.prefs.savePrefFile(null);
}.bind(this)).then(null, function(aError) {
debug("Error: " + aError);
});
},
- _getSingleVariantApps: function(aMcc, aMnc) {
-
- function normalizeCode(aCode) {
- let ncode = "" + aCode;
- while (ncode.length < 3) {
- ncode = "0" + ncode;
- }
- return ncode;
- }
-
+ _getSingleVariantDatas: function() {
return Task.spawn(function*() {
- let key = normalizeCode(aMcc) + "-" + normalizeCode(aMnc);
let file = Path.join(this.appsDir.path, SINGLE_VARIANT_CONF_FILE);
let aData = yield AppsUtils.loadJSONAsync(file);
-
- if (!aData || !(key in aData)) {
- return [];
- }
-
- return aData[key];
+ return aData;
}.bind(this));
}
};
OperatorAppsRegistry.init();
--- a/dom/bluetooth/bluedroid/BluetoothServiceBluedroid.cpp
+++ b/dom/bluetooth/bluedroid/BluetoothServiceBluedroid.cpp
@@ -336,17 +336,19 @@ AdapterPropertiesCallback(bt_status_t aS
bt_property_t *aProperties)
{
MOZ_ASSERT(!NS_IsMainThread());
BluetoothValue propertyValue;
InfallibleTArray<BluetoothNamedValue> props;
for (int i = 0; i < aNumProperties; i++) {
- bt_property_t p = aProperties[i];
+ bt_property_t p;
+ // See Bug 989976, consider aProperties address is not aligned
+ memcpy(&p, &aProperties[i], sizeof(p));
if (p.type == BT_PROPERTY_BDADDR) {
BdAddressTypeToString((bt_bdaddr_t*)p.val, sAdapterBdAddress);
propertyValue = sAdapterBdAddress;
BT_APPEND_NAMED_VALUE(props, "Address", propertyValue);
} else if (p.type == BT_PROPERTY_BDNAME) {
// Construct nsCString here because Bd name returned from bluedroid
// is missing a null terminated character after SetProperty.
--- a/dom/bluetooth/tests/marionette/head.js
+++ b/dom/bluetooth/tests/marionette/head.js
@@ -1,11 +1,9 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
- * vim: sw=2 ts=2 sts=2 et filetype=javascript
- * This Source Code Form is subject to the terms of the Mozilla Public
+/* 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/. */
// https://github.com/mozilla-b2g/platform_external_qemu/blob/master/vl-android.c#L765
// static int bt_hci_parse(const char *str) {
// ...
// bdaddr.b[0] = 0x52;
// bdaddr.b[1] = 0x54;
@@ -31,17 +29,24 @@ const EMULATOR_CLASS = 0x58020c;
// Use same definition in QEMU for special bluetooth address,
// which were defined at external/qemu/hw/bt.h:
const BDADDR_ANY = "00:00:00:00:00:00";
const BDADDR_ALL = "ff:ff:ff:ff:ff:ff";
const BDADDR_LOCAL = "ff:ff:ff:00:00:00";
// A user friendly name for remote BT device.
-const REMOTE_DEVICE_NAME = "Remote BT Device";
+const REMOTE_DEVICE_NAME = "Remote_BT_Device";
+
+// A system message signature of pairing request event
+const BT_PAIRING_REQ = "bluetooth-pairing-request";
+
+// Passkey and pincode used to reply pairing requst
+const BT_PAIRING_PASSKEY = 123456;
+const BT_PAIRING_PINCODE = "ABCDEFG";
let Promise =
SpecialPowers.Cu.import("resource://gre/modules/Promise.jsm").Promise;
let bluetoothManager;
let pendingEmulatorCmdCount = 0;
@@ -76,16 +81,43 @@ function runEmulatorCmdSafe(aCommand) {
deferred.reject(aResult);
}
});
return deferred.promise;
}
/**
+ * Wrap DOMRequest onsuccess/onerror events to Promise resolve/reject.
+ *
+ * Fulfill params: A DOMEvent.
+ * Reject params: A DOMEvent.
+ *
+ * @param aRequest
+ * A DOMRequest instance.
+ *
+ * @return A deferred promise.
+ */
+function wrapDomRequestAsPromise(aRequest) {
+ let deferred = Promise.defer();
+
+ ok(aRequest instanceof DOMRequest,
+ "aRequest is instanceof " + aRequest.constructor);
+
+ aRequest.onsuccess = function(aEvent) {
+ deferred.resolve(aEvent);
+ };
+ aRequest.onerror = function(aEvent) {
+ deferred.reject(aEvent);
+ };
+
+ return deferred.promise;
+}
+
+/**
* Add a Bluetooth remote device to scatternet and set its properties.
*
* Use QEMU command 'bt remote add' to add a virtual Bluetooth remote
* and set its properties by setEmulatorDeviceProperty().
*
* Fulfill params:
* result -- bluetooth address of the remote device.
* Reject params: (none)
@@ -183,144 +215,291 @@ function setEmulatorDeviceProperty(aAddr
* @param aPropertyName
* The property name of Bluetooth device.
*
* @return A deferred promise.
*/
function getEmulatorDeviceProperty(aAddress, aPropertyName) {
let cmd = "bt property " + aAddress + " " + aPropertyName;
return runEmulatorCmdSafe(cmd)
- .then(function(aResults) {
- return aResults[0];
- });
+ .then(aResults => aResults[0]);
}
/**
- * Start dicovering Bluetooth devices.
+ * Start discovering Bluetooth devices.
*
* Allows the device's adapter to start seeking for remote devices.
*
* Fulfill params: (none)
* Reject params: a DOMError
*
* @param aAdapter
- * A BluetoothAdapter which is used to interact with local BT dev
+ * A BluetoothAdapter which is used to interact with local BT device.
*
* @return A deferred promise.
*/
function startDiscovery(aAdapter) {
- let deferred = Promise.defer();
+ let request = aAdapter.startDiscovery();
- let request = aAdapter.startDiscovery();
- request.onsuccess = function () {
- log(" Start discovery - Success");
- // TODO (bug 892207): Make Bluetooth APIs available for 3rd party apps.
- // Currently, discovering state wouldn't change immediately here.
- // We would turn on this check when the redesigned API are landed.
- // is(aAdapter.discovering, true, "BluetoothAdapter.discovering");
- deferred.resolve();
- }
- request.onerror = function (aEvent) {
- ok(false, "Start discovery - Fail");
- deferred.reject(aEvent.target.error);
- }
-
- return deferred.promise;
+ return wrapDomRequestAsPromise(request)
+ .then(function resolve() {
+ // TODO (bug 892207): Make Bluetooth APIs available for 3rd party apps.
+ // Currently, discovering state wouldn't change immediately here.
+ // We would turn on this check when the redesigned API are landed.
+ // is(aAdapter.discovering, false, "BluetoothAdapter.discovering");
+ log(" Start discovery - Success");
+ }, function reject(aEvent) {
+ ok(false, "Start discovery - Fail");
+ throw aEvent.target.error;
+ });
}
/**
- * Stop dicovering Bluetooth devices.
+ * Stop discovering Bluetooth devices.
*
* Allows the device's adapter to stop seeking for remote devices.
*
* Fulfill params: (none)
* Reject params: a DOMError
*
* @param aAdapter
* A BluetoothAdapter which is used to interact with local BT device.
*
* @return A deferred promise.
*/
function stopDiscovery(aAdapter) {
+ let request = aAdapter.stopDiscovery();
+
+ return wrapDomRequestAsPromise(request)
+ .then(function resolve() {
+ // TODO (bug 892207): Make Bluetooth APIs available for 3rd party apps.
+ // Currently, discovering state wouldn't change immediately here.
+ // We would turn on this check when the redesigned API are landed.
+ // is(aAdapter.discovering, false, "BluetoothAdapter.discovering");
+ log(" Stop discovery - Success");
+ }, function reject(aEvent) {
+ ok(false, "Stop discovery - Fail");
+ throw aEvent.target.error;
+ });
+}
+
+/**
+ * Wait for 'devicefound' event of specified devices.
+ *
+ * Resolve if that every specified devices has been found. Never reject.
+ *
+ * Fulfill params: an array which contains addresses of remote devices.
+ *
+ * @param aAdapter
+ * A BluetoothAdapter which is used to interact with local BT device.
+ * @param aRemoteAddresses
+ * An array which contains addresses of remote devices.
+ *
+ * @return A deferred promise.
+ */
+function waitForDevicesFound(aAdapter, aRemoteAddresses) {
let deferred = Promise.defer();
- let request = aAdapter.stopDiscovery();
- request.onsuccess = function () {
- log(" Stop discovery - Success");
- // TODO (bug 892207): Make Bluetooth APIs available for 3rd party apps.
- // Currently, discovering state wouldn't change immediately here.
- // We would turn on this check when the redesigned API are landed.
- // is(aAdapter.discovering, false, "BluetoothAdapter.discovering");
- deferred.resolve();
- }
- request.onerror = function (aEvent) {
- ok(false, "Stop discovery - Fail");
- deferred.reject(aEvent.target.error);
- }
+ var addrArray = [];
+ aAdapter.addEventListener("devicefound", function onevent(aEvent) {
+ if(aRemoteAddresses.indexOf(aEvent.device.address) != -1) {
+ addrArray.push(aEvent.device.address);
+ }
+ if(addrArray.length == aRemoteAddresses.length) {
+ aAdapter.removeEventListener("devicefound", onevent);
+ ok(true, "BluetoothAdapter has found all remote devices.");
+
+ deferred.resolve(addrArray);
+ }
+ });
+
return deferred.promise;
}
/**
+ * Start discovering Bluetooth devices and wait for 'devicefound' events.
+ *
+ * Allows the device's adapter to start seeking for remote devices and wait for
+ * the 'devicefound' events of specified devices.
+ *
+ * Fulfill params: an array of addresses of found devices.
+ *
+ * @param aAdapter
+ * A BluetoothAdapter which is used to interact with local BT device.
+ * @param aRemoteAddresses
+ * An array which contains addresses of remote devices.
+ *
+ * @return A deferred promise.
+ */
+function startDiscoveryAndWaitDevicesFound(aAdapter, aRemoteAddresses) {
+ let promises = [];
+
+ promises.push(waitForDevicesFound(aAdapter, aRemoteAddresses));
+ promises.push(startDiscovery(aAdapter));
+ return Promise.all(promises)
+ .then(aResults => aResults[0]);
+}
+
+/**
+ * Start pairing a remote device.
+ *
+ * Start pairing a remote device with the device's adapter.
+ *
+ * Fulfill params: (none)
+ * Reject params: a DOMError
+ *
+ * @param aAdapter
+ * A BluetoothAdapter which is used to interact with local BT device.
+ * @param aDeviceAddress
+ * The string of remote Bluetooth address with format xx:xx:xx:xx:xx:xx.
+ *
+ * @return A deferred promise.
+ */
+function pair(aAdapter, aDeviceAddress) {
+ let request = aAdapter.pair(aDeviceAddress);
+
+ return wrapDomRequestAsPromise(request)
+ .then(function resolve() {
+ log(" Pair - Success");
+ }, function reject(aEvent) {
+ ok(false, "Pair - Fail");
+ throw aEvent.target.error;
+ });
+}
+
+/**
+ * Remove the paired device from the paired device list.
+ *
+ * Remove the paired device from the paired device list of the device's adapter.
+ *
+ * Fulfill params: (none)
+ * Reject params: a DOMError
+ *
+ * @param aAdapter
+ * A BluetoothAdapter which is used to interact with local BT device.
+ * @param aDeviceAddress
+ * The string of remote Bluetooth address with format xx:xx:xx:xx:xx:xx.
+ *
+ * @return A deferred promise.
+ */
+function unpair(aAdapter, aDeviceAddress) {
+ let request = aAdapter.unpair(aDeviceAddress);
+
+ return wrapDomRequestAsPromise(request)
+ .then(function resolve() {
+ log(" Unpair - Success");
+ }, function reject(aEvent) {
+ ok(false, "Unpair - Fail");
+ throw aEvent.target.error;
+ });
+}
+
+/**
+ * Start pairing a remote device and wait for "pairedstatuschanged" event.
+ *
+ * Start pairing a remote device with the device's adapter and wait for
+ * "pairedstatuschanged" event.
+ *
+ * Fulfill params: an array of promise results contains the fulfilled params of
+ * 'waitForAdapterEvent' and 'pair'.
+ *
+ * @param aAdapter
+ * A BluetoothAdapter which is used to interact with local BT device.
+ * @param aDeviceAddress
+ * The string of remote Bluetooth address with format xx:xx:xx:xx:xx:xx.
+ *
+ * @return A deferred promise.
+ */
+function pairDeviceAndWait(aAdapter, aDeviceAddress) {
+ let promises = [];
+ promises.push(waitForAdapterEvent(aAdapter, "pairedstatuschanged"));
+ promises.push(pair(aAdapter, aDeviceAddress));
+ return Promise.all(promises);
+}
+
+/**
+ * Get paried Bluetooth devices.
+ *
+ * The getPairedDevices method is used to retrieve the full list of all devices
+ * paired with the device's adapter.
+ *
+ * Fulfill params: a shallow copy of the Array of paired BluetoothDevice
+ * objects.
+ * Reject params: a DOMError
+ *
+ * @param aAdapter
+ * A BluetoothAdapter which is used to interact with local BT device.
+ *
+ * @return A deferred promise.
+ */
+function getPairedDevices(aAdapter) {
+ let request = aAdapter.getPairedDevices();
+
+ return wrapDomRequestAsPromise(request)
+ .then(function resolve() {
+ log(" getPairedDevices - Success");
+ let pairedDevices = request.result.slice();
+ return pairedDevices;
+ }, function reject(aEvent) {
+ ok(false, "getPairedDevices - Fail");
+ throw aEvent.target.error;
+ });
+}
+
+/**
* Get mozSettings value specified by @aKey.
*
* Resolve if that mozSettings value is retrieved successfully, reject
* otherwise.
*
* Fulfill params:
* The corresponding mozSettings value of the key.
* Reject params: (none)
*
* @param aKey
* A string.
*
* @return A deferred promise.
*/
function getSettings(aKey) {
- let deferred = Promise.defer();
+ let request = navigator.mozSettings.createLock().get(aKey);
- let request = navigator.mozSettings.createLock().get(aKey);
- request.addEventListener("success", function(aEvent) {
- ok(true, "getSettings(" + aKey + ")");
- deferred.resolve(aEvent.target.result[aKey]);
- });
- request.addEventListener("error", function() {
- ok(false, "getSettings(" + aKey + ")");
- deferred.reject();
- });
-
- return deferred.promise;
+ return wrapDomRequestAsPromise(request)
+ .then(function resolve(aEvent) {
+ ok(true, "getSettings(" + aKey + ")");
+ return aEvent.target.result[aKey];
+ }, function reject(aEvent) {
+ ok(false, "getSettings(" + aKey + ")");
+ throw aEvent.target.error;
+ });
}
/**
* Set mozSettings values.
*
* Resolve if that mozSettings value is set successfully, reject otherwise.
*
* Fulfill params: (none)
* Reject params: (none)
*
* @param aSettings
* An object of format |{key1: value1, key2: value2, ...}|.
*
* @return A deferred promise.
*/
function setSettings(aSettings) {
- let deferred = Promise.defer();
+ let request = navigator.mozSettings.createLock().set(aSettings);
- let request = navigator.mozSettings.createLock().set(aSettings);
- request.addEventListener("success", function() {
- ok(true, "setSettings(" + JSON.stringify(aSettings) + ")");
- deferred.resolve();
- });
- request.addEventListener("error", function() {
- ok(false, "setSettings(" + JSON.stringify(aSettings) + ")");
- deferred.reject();
- });
-
- return deferred.promise;
+ return wrapDomRequestAsPromise(request)
+ .then(function resolve() {
+ ok(true, "setSettings(" + JSON.stringify(aSettings) + ")");
+ }, function reject(aEvent) {
+ ok(false, "setSettings(" + JSON.stringify(aSettings) + ")");
+ throw aEvent.target.error;
+ });
}
/**
* Get mozSettings value of 'bluetooth.enabled'.
*
* Resolve if that mozSettings value is retrieved successfully, reject
* otherwise.
*
@@ -433,17 +612,17 @@ function waitForManagerEvent(aEventName)
/**
* Wait for one named BluetoothAdapter event.
*
* Resolve if that named event occurs. Never reject.
*
* Fulfill params: the DOMEvent passed.
*
* @param aAdapter
- * The BluetoothAdapter you want to use.
+ * A BluetoothAdapter which is used to interact with local BT device.
* @param aEventName
* The name of the EventHandler.
*
* @return A deferred promise.
*/
function waitForAdapterEvent(aAdapter, aEventName) {
let deferred = Promise.defer();
@@ -458,17 +637,18 @@ function waitForAdapterEvent(aAdapter, a
}
/**
* Convenient function for setBluetoothEnabled and waitForManagerEvent
* combined.
*
* Resolve if that named event occurs. Reject if we can't set settings.
*
- * Fulfill params: the DOMEvent passed.
+ * Fulfill params: an array of promise results contains the fulfill params of
+ * 'waitForManagerEvent' and 'setBluetoothEnabled'.
* Reject params: (none)
*
* @return A deferred promise.
*/
function setBluetoothEnabledAndWait(aEnabled) {
let promises = [];
// Bug 969109 - Intermittent test_dom_BluetoothManager_adapteradded.js
--- a/dom/bluetooth/tests/marionette/manifest.ini
+++ b/dom/bluetooth/tests/marionette/manifest.ini
@@ -1,10 +1,12 @@
[DEFAULT]
b2g = true
browser = false
qemu = true
+[test_navigate_to_default_url.py]
[test_dom_BluetoothManager_enabled.js]
[test_dom_BluetoothManager_adapteradded.js]
[test_dom_BluetoothAdapter_setters.js]
[test_dom_BluetoothAdapter_getters.js]
[test_dom_BluetoothAdapter_discovery.js]
+[test_dom_BluetoothAdapter_pair.js]
--- a/dom/bluetooth/tests/marionette/test_dom_BluetoothAdapter_discovery.js
+++ b/dom/bluetooth/tests/marionette/test_dom_BluetoothAdapter_discovery.js
@@ -1,43 +1,35 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
- * vim: sw=2 ts=2 sts=2 et filetype=javascript
- * This Source Code Form is subject to the terms of the Mozilla Public
+/* 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/. */
///////////////////////////////////////////////////////////////////////////////
// Test Purpose:
// To verify that discovery process of BluetoothAdapter is correct.
-// Use B2G emulator commands to add/remote remote devices to simulate
+// Use B2G emulator commands to add/remove remote devices to simulate
// discovering behavior.
//
// Test Coverage:
// - BluetoothAdapter.startDiscovery()
// - BluetoothAdapter.stopDiscovery()
// - BluetoothAdapter.ondevicefound()
// - BluetoothAdapter.discovering [Temporarily turned off until BT API update]
//
///////////////////////////////////////////////////////////////////////////////
MARIONETTE_TIMEOUT = 60000;
MARIONETTE_HEAD_JS = 'head.js';
startBluetoothTest(true, function testCaseMain(aAdapter) {
log("Testing the discovery process of BluetoothAdapter ...");
- // The properties of remote device.
- let theProperties = {
- "name": REMOTE_DEVICE_NAME,
- "discoverable": true
- };
-
return Promise.resolve()
.then(() => removeEmulatorRemoteDevice(BDADDR_ALL))
- .then(() => addEmulatorRemoteDevice(/*theProperties*/ null))
+ .then(() => addEmulatorRemoteDevice(null))
.then(function(aRemoteAddress) {
let promises = [];
promises.push(waitForAdapterEvent(aAdapter, "devicefound"));
promises.push(startDiscovery(aAdapter));
return Promise.all(promises)
.then(function(aResults) {
is(aResults[0].device.address, aRemoteAddress, "BluetoothDevice.address");
});
--- a/dom/bluetooth/tests/marionette/test_dom_BluetoothAdapter_getters.js
+++ b/dom/bluetooth/tests/marionette/test_dom_BluetoothAdapter_getters.js
@@ -1,11 +1,9 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
- * vim: sw=2 ts=2 sts=2 et filetype=javascript
- * This Source Code Form is subject to the terms of the Mozilla Public
+/* 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/. */
///////////////////////////////////////////////////////////////////////////////
// Test Purpose:
// To verify that the properties of BluetoothAdapter can be updated and
// retrieved correctly. Use B2G emulator commands to set properties for this
// test case.
new file mode 100644
--- /dev/null
+++ b/dom/bluetooth/tests/marionette/test_dom_BluetoothAdapter_pair.js
@@ -0,0 +1,66 @@
+/* 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/. */
+
+///////////////////////////////////////////////////////////////////////////////
+// Test Purpose:
+// To verify that pairing process of BluetoothAdapter is correct.
+// Use B2G emulator commands to add/remove remote devices to simulate
+// discovering behavior. With current emulator implemation, the pair method
+// between adapter and remote device would be 'confirmation'.
+//
+// Test Coverage:
+// - BluetoothAdapter.startDiscovery()
+// - BluetoothAdapter.stopDiscovery()
+// - BluetoothAdapter.pair()
+// - BluetoothAdapter.unpair()
+// - BluetoothAdapter.onpairedstatuschanged()
+// - BluetoothAdapter.setPairingConfirmation()
+//
+///////////////////////////////////////////////////////////////////////////////
+
+MARIONETTE_TIMEOUT = 60000;
+MARIONETTE_HEAD_JS = 'head.js';
+
+function replyPairingReq(aAdapter, aPairingEvent) {
+ switch (aPairingEvent.method) {
+ case 'confirmation':
+ log("The pairing passkey is " + aPairingEvent.passkey);
+ aAdapter.setPairingConfirmation(aPairingEvent.address, true);
+ break;
+ case 'pincode':
+ let pincode = BT_PAIRING_PINCODE;
+ aAdapter.setPinCode(aPairingEvent.address, pincode);
+ break;
+ case 'passkey':
+ let passkey = BT_PAIRING_PASSKEY;
+ aAdapter.setPasskey(aPairingEvent.address, passkey);
+ break;
+ default:
+ ok(false, "Unsupported pairing method. [" + aPairingEvent.method + "]");
+ }
+}
+
+startBluetoothTest(true, function testCaseMain(aAdapter) {
+ log("Testing the pairing process of BluetoothAdapter ...");
+
+ // listens to the system message BT_PAIRING_REQ
+ navigator.mozSetMessageHandler(BT_PAIRING_REQ,
+ (evt) => replyPairingReq(aAdapter, evt));
+
+ return Promise.resolve()
+ .then(() => removeEmulatorRemoteDevice(BDADDR_ALL))
+ .then(() => addEmulatorRemoteDevice())
+ .then((aRemoteAddress) =>
+ startDiscoveryAndWaitDevicesFound(aAdapter, [aRemoteAddress]))
+ .then((aRemoteAddresses) =>
+ stopDiscovery(aAdapter).then(() => aRemoteAddresses))
+ // 'aRemoteAddresses' is an arrary which contains addresses of discovered
+ // remote devices.
+ // Pairs with the first device in 'aRemoteAddresses' to test the functionality
+ // of BluetoothAdapter.pair and BluetoothAdapter.onpairedstatuschanged.
+ .then((aRemoteAddresses) => pairDeviceAndWait(aAdapter, aRemoteAddresses.pop()))
+ .then(() => getPairedDevices(aAdapter))
+ .then((aPairedDevices) => unpair(aAdapter, aPairedDevices.pop().address))
+ .then(() => removeEmulatorRemoteDevice(BDADDR_ALL));
+});
--- a/dom/bluetooth/tests/marionette/test_dom_BluetoothAdapter_setters.js
+++ b/dom/bluetooth/tests/marionette/test_dom_BluetoothAdapter_setters.js
@@ -1,11 +1,9 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
- * vim: sw=2 ts=2 sts=2 et filetype=javascript
- * This Source Code Form is subject to the terms of the Mozilla Public
+/* 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/. */
///////////////////////////////////////////////////////////////////////////////
// Test Purpose:
// To verify that all setters of BluetoothAdapter (except for pairing related
// APIs) can change properties correctly.
//
--- a/dom/bluetooth/tests/marionette/test_dom_BluetoothManager_adapteradded.js
+++ b/dom/bluetooth/tests/marionette/test_dom_BluetoothManager_adapteradded.js
@@ -1,11 +1,9 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
- * vim: sw=2 ts=2 sts=2 et filetype=javascript
- * This Source Code Form is subject to the terms of the Mozilla Public
+/* 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/. */
MARIONETTE_TIMEOUT = 60000;
MARIONETTE_HEAD_JS = 'head.js';
startBluetoothTest(true, function testCaseMain(aAdapter) {
log("Checking adapter attributes ...");
--- a/dom/bluetooth/tests/marionette/test_dom_BluetoothManager_enabled.js
+++ b/dom/bluetooth/tests/marionette/test_dom_BluetoothManager_enabled.js
@@ -1,11 +1,9 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
- * vim: sw=2 ts=2 sts=2 et filetype=javascript
- * This Source Code Form is subject to the terms of the Mozilla Public
+/* 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/. */
MARIONETTE_TIMEOUT = 60000;
MARIONETTE_HEAD_JS = 'head.js';
function waitEitherEnabledOrDisabled() {
let deferred = Promise.defer();
new file mode 100644
--- /dev/null
+++ b/dom/bluetooth/tests/marionette/test_navigate_to_default_url.py
@@ -0,0 +1,18 @@
+# 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/.
+
+from marionette_test import MarionetteTestCase
+
+class testNavigateToDefault(MarionetteTestCase):
+ def setUp(self):
+ MarionetteTestCase.setUp(self)
+ # Sets an appropriate timeout for this test.
+ # P.S. The timeout of next test wouldn't be affected by this statement.
+ self.marionette.timeouts(self.marionette.TIMEOUT_PAGE, 90000)
+
+ def test_navigate_to_default_url(self):
+ try:
+ self.marionette.navigate("app://system.gaiamobile.org/index.html")
+ except:
+ self.assertTrue(False, "Can not navigate to system app.")
--- a/dom/mobilemessage/src/gonk/MmsService.js
+++ b/dom/mobilemessage/src/gonk/MmsService.js
@@ -323,25 +323,35 @@ MmsConnection.prototype = {
*
* If the icc card is gsm card, the phone number is in msisdn.
* @see nsIDOMMozGsmIccInfo
*
* Otherwise, the phone number is in mdn.
* @see nsIDOMMozCdmaIccInfo
*/
getPhoneNumber: function() {
- let iccInfo = this.radioInterface.rilContext.iccInfo;
-
- if (!iccInfo) {
+ let number;
+ // Get the proper IccInfo based on the current card type.
+ try {
+ let iccInfo = null;
+ let baseIccInfo = this.radioInterface.rilContext.iccInfo;
+ if (baseIccInfo.iccType === 'ruim' || baseIccInfo.iccType === 'csim') {
+ iccInfo = baseIccInfo.QueryInterface(Ci.nsIDOMMozCdmaIccInfo);
+ number = iccInfo.mdn;
+ } else {
+ iccInfo = baseIccInfo.QueryInterface(Ci.nsIDOMMozGsmIccInfo);
+ number = iccInfo.msisdn;
+ }
+ } catch (e) {
+ if (DEBUG) {
+ debug("Exception - QueryInterface failed on iccinfo for GSM/CDMA info");
+ }
return null;
}
- let number = (iccInfo instanceof Ci.nsIDOMMozGsmIccInfo)
- ? iccInfo.msisdn : iccInfo.mdn;
-
// Workaround an xpconnect issue with undefined string objects.
// See bug 808220
if (number === undefined || number === "undefined") {
return null;
}
return number;
},
@@ -1130,18 +1140,18 @@ function SendTransaction(mmsConnection,
let tid = gUUIDGenerator.generateUUID().toString();
msg.headers["x-mms-transaction-id"] = tid;
}
msg.headers["x-mms-mms-version"] = MMS.MMS_VERSION;
// Insert Phone number if available.
// Otherwise, Let MMS Proxy Relay insert from address automatically for us.
let phoneNumber = mmsConnection.getPhoneNumber();
- msg.headers["from"] = (phoneNumber) ?
- { address: phoneNumber, type: "PLMN" } : null;
+ let from = (phoneNumber) ? { address: phoneNumber, type: "PLMN" } : null;
+ msg.headers["from"] = from;
msg.headers["date"] = new Date();
msg.headers["x-mms-message-class"] = "personal";
msg.headers["x-mms-expiry"] = 7 * 24 * 60 * 60;
msg.headers["x-mms-priority"] = 129;
try {
msg.headers["x-mms-read-report"] =
Services.prefs.getBoolPref("dom.mms.requestReadReport");
@@ -1428,18 +1438,18 @@ function ReadRecTransaction(mmsConnectio
headers["message-id"] = messageID;
let type = MMS.Address.resolveType(toAddress);
let to = {address: toAddress,
type: type}
headers["to"] = to;
// Insert Phone number if available.
// Otherwise, Let MMS Proxy Relay insert from address automatically for us.
let phoneNumber = mmsConnection.getPhoneNumber();
- headers["from"] = (phoneNumber) ?
- { address: phoneNumber, type: "PLMN" } : null;
+ let from = (phoneNumber) ? { address: phoneNumber, type: "PLMN" } : null;
+ headers["from"] = from;
headers["x-mms-read-status"] = MMS.MMS_PDU_READ_STATUS_READ;
this.istream = MMS.PduHelper.compose(null, {headers: headers});
if (!this.istream) {
throw Cr.NS_ERROR_FAILURE;
}
}
ReadRecTransaction.prototype = {
--- a/dom/system/gonk/NetworkManager.js
+++ b/dom/system/gonk/NetworkManager.js
@@ -361,23 +361,25 @@ NetworkManager.prototype = {
}
return interfaces;
}
}
},
getNetworkId: function(network) {
let id = "device";
+#ifdef MOZ_B2G_RIL
if (this.isNetworkTypeMobile(network.type)) {
if (!(network instanceof Ci.nsIRilNetworkInterface)) {
throw Components.Exception("Mobile network not an nsIRilNetworkInterface",
Cr.NS_ERROR_INVALID_ARG);
}
id = "ril" + network.serviceId;
}
+#endif
return id + "-" + network.type;
},
// nsINetworkManager
registerNetworkInterface: function(network) {
if (!(network instanceof Ci.nsINetworkInterface)) {
--- a/mobile/android/base/BrowserApp.java
+++ b/mobile/android/base/BrowserApp.java
@@ -1986,39 +1986,39 @@ abstract public class BrowserApp extends
}
/**
* Hides certain UI elements (e.g. button toast, tabs tray) when the
* user touches the main layout.
*/
private class HideOnTouchListener implements TouchEventInterceptor {
private boolean mIsHidingTabs = false;
+ private final Rect mTempRect = new Rect();
@Override
public boolean onInterceptTouchEvent(View view, MotionEvent event) {
// Only try to hide the button toast if it's already inflated.
if (mToast != null) {
mToast.hide(false, ButtonToast.ReasonHidden.TOUCH_OUTSIDE);
}
// We need to account for scroll state for the touched view otherwise
// tapping on an "empty" part of the view will still be considered a
// valid touch event.
if (view.getScrollX() != 0 || view.getScrollY() != 0) {
- Rect rect = new Rect();
- view.getHitRect(rect);
- rect.offset(-view.getScrollX(), -view.getScrollY());
+ view.getHitRect(mTempRect);
+ mTempRect.offset(-view.getScrollX(), -view.getScrollY());
int[] viewCoords = new int[2];
view.getLocationOnScreen(viewCoords);
int x = (int) event.getRawX() - viewCoords[0];
int y = (int) event.getRawY() - viewCoords[1];
- if (!rect.contains(x, y))
+ if (!mTempRect.contains(x, y))
return false;
}
// If the tab tray is showing, hide the tab tray and don't send the event to content.
if (event.getActionMasked() == MotionEvent.ACTION_DOWN && autoHideTabs()) {
mIsHidingTabs = true;
return true;
}
--- a/toolkit/components/crashmonitor/CrashMonitor.jsm
+++ b/toolkit/components/crashmonitor/CrashMonitor.jsm
@@ -160,19 +160,20 @@ this.CrashMonitor = {
// called after receiving it
CrashMonitorInternal.checkpoints["profile-after-change"] = true;
NOTIFICATIONS.forEach(function (aTopic) {
Services.obs.addObserver(this, aTopic, false);
}, this);
// Add shutdown blocker for profile-before-change
- AsyncShutdown.profileBeforeChange.addBlocker(
+ OS.File.profileBeforeChange.addBlocker(
"CrashMonitor: Writing notifications to file after receiving profile-before-change",
- CrashMonitorInternal.profileBeforeChangeDeferred.promise
+ CrashMonitorInternal.profileBeforeChangeDeferred.promise,
+ () => this.checkpoints
);
CrashMonitorInternal.initialized = true;
if (Services.metro && Services.metro.immersive) {
OS.File.makeDir(OS.Path.join(OS.Constants.Path.profileDir, "metro"));
}
return promise;
},
--- a/toolkit/components/osfile/modules/osfile_async_front.jsm
+++ b/toolkit/components/osfile/modules/osfile_async_front.jsm
@@ -449,16 +449,17 @@ let Scheduler = {
Scheduler.restartTimer();
let data;
let reply;
let isError = false;
try {
try {
+ Scheduler.Debugging.messagesSent++;
data = yield this.worker.post(method, ...args);
} finally {
Scheduler.Debugging.messagesReceived++;
}
reply = data;
} catch (error) {
reply = error;
isError = true;
@@ -603,17 +604,28 @@ if (SharedAll.Config.DEBUG && Scheduler.
const WEB_WORKERS_SHUTDOWN_TOPIC = "web-workers-shutdown";
// Preference used to configure test shutdown observer.
const PREF_OSFILE_TEST_SHUTDOWN_OBSERVER =
"toolkit.osfile.test.shutdown.observer";
AsyncShutdown.webWorkersShutdown.addBlocker(
"OS.File: flush pending requests, warn about unclosed files, shut down service.",
- () => Scheduler.kill({reset: false, shutdown: true})
+ Task.async(function*() {
+ // Give clients a last chance to enqueue requests.
+ yield Barriers.shutdown.wait({crashAfterMS: null});
+
+ // Wait until all requests are complete and kill the worker.
+ yield Scheduler.kill({reset: false, shutdown: true});
+ }),
+ () => {
+ let details = Barriers.getDetails();
+ details.clients = Barriers.shutdown.state;
+ return details;
+ }
);
// Attaching an observer for PREF_OSFILE_TEST_SHUTDOWN_OBSERVER to enable or
// disable the test shutdown event observer.
// Note: By default the PREF_OSFILE_TEST_SHUTDOWN_OBSERVER is unset.
// Note: This is meant to be used for testing purposes only.
Services.prefs.addObserver(PREF_OSFILE_TEST_SHUTDOWN_OBSERVER,
@@ -1501,51 +1513,62 @@ this.OS.Path = Path;
// Returns a resolved promise when all the queued operation have been completed.
Object.defineProperty(OS.File, "queue", {
get: function() {
return Scheduler.queue;
}
});
-// Auto-flush OS.File during profile-before-change. This ensures that any I/O
-// that has been queued *before* profile-before-change is properly completed.
-// To ensure that I/O queued *during* profile-before-change is completed,
-// clients should register using AsyncShutdown.addBlocker.
-AsyncShutdown.profileBeforeChange.addBlocker(
- "OS.File: flush I/O queued before profile-before-change",
- // Wait until the latest currently enqueued promise is satisfied/rejected
- function() {
- let DEBUG = false;
- try {
- DEBUG = Services.prefs.getBoolPref("toolkit.osfile.debug.failshutdown");
- } catch (ex) {
- // Ignore
- }
- if (DEBUG) {
- // Return a promise that will never be satisfied
- return Promise.defer().promise;
- } else {
- return Scheduler.queue;
- }
- },
- function getDetails() {
+/**
+ * Shutdown barriers, to let clients register to be informed during shutdown.
+ */
+let Barriers = {
+ profileBeforeChange: new AsyncShutdown.Barrier("OS.File: Waiting for clients before profile-before-shutdown"),
+ shutdown: new AsyncShutdown.Barrier("OS.File: Waiting for clients before full shutdown"),
+ /**
+ * Return the shutdown state of OS.File
+ */
+ getDetails: function() {
let result = {
launched: Scheduler.launched,
shutdown: Scheduler.shutdown,
worker: !!Scheduler._worker,
pendingReset: !!Scheduler.resetTimer,
latestSent: Scheduler.Debugging.latestSent,
latestReceived: Scheduler.Debugging.latestReceived,
messagesSent: Scheduler.Debugging.messagesSent,
messagesReceived: Scheduler.Debugging.messagesReceived,
messagesQueued: Scheduler.Debugging.messagesQueued,
- DEBUG: SharedAll.Config.DEBUG
+ DEBUG: SharedAll.Config.DEBUG,
};
// Convert dates to strings for better readability
for (let key of ["latestSent", "latestReceived"]) {
if (result[key] && typeof result[key][0] == "number") {
result[key][0] = Date(result[key][0]);
}
}
return result;
}
+};
+
+File.profileBeforeChange = Barriers.profileBeforeChange.client;
+File.shutdown = Barriers.shutdown.client;
+
+// Auto-flush OS.File during profile-before-change. This ensures that any I/O
+// that has been queued *before* profile-before-change is properly completed.
+// To ensure that I/O queued *during* profile-before-change is completed,
+// clients should register using AsyncShutdown.addBlocker.
+AsyncShutdown.profileBeforeChange.addBlocker(
+ "OS.File: flush I/O queued before profile-before-change",
+ Task.async(function*() {
+ // Give clients a last chance to enqueue requests.
+ yield Barriers.profileBeforeChange.wait({crashAfterMS: null});
+
+ // Wait until all currently enqueued requests are completed.
+ yield Scheduler.queue;
+ }),
+ () => {
+ let details = Barriers.getDetails();
+ details.clients = Barriers.profileBeforeChange.state;
+ return details;
+ }
);
--- a/toolkit/crashreporter/test/unit/test_crash_AsyncShutdown.js
+++ b/toolkit/crashreporter/test/unit/test_crash_AsyncShutdown.js
@@ -35,28 +35,30 @@ function after_crash(mdump, extra) {
}
// Test that AsyncShutdown + OS.File reports errors correctly, in a case in which
// the latest operation succeeded
function setup_osfile_crash_noerror() {
Components.utils.import("resource://gre/modules/Services.jsm", this);
Components.utils.import("resource://gre/modules/osfile.jsm", this);
+ Components.utils.import("resource://gre/modules/Promise.jsm", this);
- Services.prefs.setBoolPref("toolkit.osfile.debug.failshutdown", true);
Services.prefs.setIntPref("toolkit.asyncshutdown.crash_timeout", 1);
Services.prefs.setBoolPref("toolkit.osfile.native", false);
+ OS.File.profileBeforeChange.addBlocker("Adding a blocker that will never be resolved", () => Promise.defer().promise);
OS.File.getCurrentDirectory();
+
Services.obs.notifyObservers(null, "profile-before-change", null);
dump("Waiting for crash\n");
};
function after_osfile_crash_noerror(mdump, extra) {
- do_print("after OS.File crash: " + JSON.stringify(extra.AsyncShutdownTimeout));
+ do_print("after OS.File crash: " + extra.AsyncShutdownTimeout);
let info = JSON.parse(extra.AsyncShutdownTimeout);
let state = info.conditions[0].state;
do_print("Keys: " + Object.keys(state).join(", "));
do_check_eq(info.phase, "profile-before-change");
do_check_true(state.launched);
do_check_false(state.shutdown);
do_check_true(state.worker);
do_check_true(!!state.latestSent);
@@ -64,28 +66,30 @@ function after_osfile_crash_noerror(mdum
}
// Test that AsyncShutdown + OS.File reports errors correctly, in a case in which
// the latest operation failed
function setup_osfile_crash_exn() {
Components.utils.import("resource://gre/modules/Services.jsm", this);
Components.utils.import("resource://gre/modules/osfile.jsm", this);
+ Components.utils.import("resource://gre/modules/Promise.jsm", this);
- Services.prefs.setBoolPref("toolkit.osfile.debug.failshutdown", true);
Services.prefs.setIntPref("toolkit.asyncshutdown.crash_timeout", 1);
Services.prefs.setBoolPref("toolkit.osfile.native", false);
+ OS.File.profileBeforeChange.addBlocker("Adding a blocker that will never be resolved", () => Promise.defer().promise);
OS.File.read("I do not exist");
+
Services.obs.notifyObservers(null, "profile-before-change", null);
dump("Waiting for crash\n");
};
function after_osfile_crash_exn(mdump, extra) {
- do_print("after OS.File crash: " + JSON.stringify(extra.AsyncShutdownTimeout));
+ do_print("after OS.File crash: " + extra.AsyncShutdownTimeout);
let info = JSON.parse(extra.AsyncShutdownTimeout);
let state = info.conditions[0].state;
do_print("Keys: " + Object.keys(state).join(", "));
do_check_eq(info.phase, "profile-before-change");
do_check_false(state.shutdown);
do_check_true(state.worker);
do_check_true(!!state.latestSent);
do_check_eq(state.latestSent[1], "read");
--- a/toolkit/devtools/server/actors/stylesheets.js
+++ b/toolkit/devtools/server/actors/stylesheets.js
@@ -69,19 +69,16 @@ let StyleSheetsActor = protocol.ActorCla
return { actor: this.actorID };
},
initialize: function (conn, tabActor) {
protocol.Actor.prototype.initialize.call(this, null);
this.parentActor = tabActor;
- // so we can get events when stylesheets and rules are added
- this.document.styleSheetChangeEventsEnabled = true;
-
// keep a map of sheets-to-actors so we don't create two actors for one sheet
this._sheets = new Map();
},
/**
* Destroy the current StyleSheetsActor instance.
*/
destroy: function()
@@ -474,34 +471,16 @@ let StyleSheetActor = protocol.ActorClas
this._window = aWindow;
// text and index are unknown until source load
this.text = null;
this._styleSheetIndex = -1;
this._transitionRefCount = 0;
-
- this._onRuleAddedOrRemoved = this._onRuleAddedOrRemoved.bind(this);
-
- if (this.browser) {
- this.browser.addEventListener("StyleRuleAdded", this._onRuleAddedOrRemoved, true);
- this.browser.addEventListener("StyleRuleRemoved", this._onRuleAddedOrRemoved, true);
- }
- },
-
- _onRuleAddedOrRemoved: function(event) {
- if (event.target != this.document || event.stylesheet != this.rawSheet) {
- return;
- }
- if (event.rule && event.rule.type == Ci.nsIDOMCSSRule.MEDIA_RULE) {
- this._getMediaRules().then((rules) => {
- events.emit(this, "media-rules-changed", rules);
- });
- }
},
/**
* Get the raw stylesheet's cssRules once the sheet has been loaded.
*
* @return {Promise}
* Promise that resolves with a CSSRuleList
*/
@@ -916,16 +895,20 @@ let StyleSheetActor = protocol.ActorClas
this._notifyPropertyChanged("ruleCount");
if (transition) {
this._insertTransistionRule();
}
else {
this._notifyStyleApplied();
}
+
+ this._getMediaRules().then((rules) => {
+ events.emit(this, "media-rules-changed", rules);
+ });
}, {
request: {
text: Arg(0, "string"),
transition: Arg(1, "boolean")
}
}),
/**
--- a/toolkit/devtools/server/actors/webbrowser.js
+++ b/toolkit/devtools/server/actors/webbrowser.js
@@ -43,16 +43,18 @@ XPCOMUtils.defineLazyGetter(this, "event
function allAppShellDOMWindows(aWindowType)
{
let e = Services.wm.getEnumerator(aWindowType);
while (e.hasMoreElements()) {
yield e.getNext();
}
}
+exports.allAppShellDOMWindows = allAppShellDOMWindows;
+
/**
* Retrieve the window type of the top-level window |aWindow|.
*/
function appShellDOMWindowType(aWindow) {
/* This is what nsIWindowMediator's enumerator checks. */
return aWindow.document.documentElement.getAttribute('windowtype');
}
--- a/toolkit/identity/FirefoxAccounts.jsm
+++ b/toolkit/identity/FirefoxAccounts.jsm
@@ -39,22 +39,24 @@ log.addAppender(new Log.ConsoleAppender(
XPCOMUtils.defineLazyModuleGetter(this, "FxAccountsManager",
"resource://gre/modules/FxAccountsManager.jsm",
"FxAccountsManager");
Cu.import("resource://gre/modules/FxAccountsCommon.js");
#else
log.warn("The FxAccountsManager is only functional in B2G at this time.");
var FxAccountsManager = null;
var ONVERIFIED_NOTIFICATION = null;
+var ONLOGOUT_NOTIFICATION = null;
#endif
function FxAccountsService() {
Services.obs.addObserver(this, "quit-application-granted", false);
if (ONVERIFIED_NOTIFICATION) {
Services.obs.addObserver(this, ONVERIFIED_NOTIFICATION, false);
+ Services.obs.addObserver(this, ONLOGOUT_NOTIFICATION, false);
}
// Maintain interface parity with Identity.jsm and MinimalIdentity.jsm
this.RP = this;
this._rpFlows = new Map();
// Enable us to mock FxAccountsManager service in testing
@@ -62,24 +64,30 @@ function FxAccountsService() {
}
FxAccountsService.prototype = {
QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports, Ci.nsIObserver]),
observe: function observe(aSubject, aTopic, aData) {
switch (aTopic) {
case null:
- // Paranoia against matching null ONVERIFIED_NOTIFICATION
+ // Paranoia against matching null ONVERIFIED or ONLOGOUT
break;
case ONVERIFIED_NOTIFICATION:
log.debug("Received " + ONVERIFIED_NOTIFICATION + "; firing request()s");
for (let [rpId,] of this._rpFlows) {
this.request(rpId);
}
break;
+ case ONLOGOUT_NOTIFICATION:
+ log.debug("Received " + ONLOGOUT_NOTIFICATION + "; doLogout()s fired");
+ for (let [rpId,] of this._rpFlows) {
+ this.doLogout(rpId);
+ }
+ break;
case "quit-application-granted":
Services.obs.removeObserver(this, "quit-application-granted");
if (ONVERIFIED_NOTIFICATION) {
Services.obs.removeObserver(this, ONVERIFIED_NOTIFICATION);
}
break;
}
},
--- a/uriloader/base/nsURILoader.cpp
+++ b/uriloader/base/nsURILoader.cpp
@@ -362,21 +362,36 @@ nsresult nsDocumentOpenInfo::DispatchCon
// Check whether the data should be forced to be handled externally. This
// could happen because the Content-Disposition header is set so, or, in the
// future, because the user has specified external handling for the MIME
// type.
bool forceExternalHandling = false;
uint32_t disposition;
rv = aChannel->GetContentDisposition(&disposition);
- if (NS_SUCCEEDED(rv) && disposition == nsIChannel::DISPOSITION_ATTACHMENT)
+
+ bool allowContentDispositionToForceExternalHandling = true;
+
+#ifdef MOZ_B2G
+
+ // On B2G, OMA content files should never be handled by an external handler
+ // (even if the server specifies Content-Disposition: attachment) because the
+ // data should never be stored on an unencrypted form.
+ allowContentDispositionToForceExternalHandling =
+ !mContentType.LowerCaseEqualsASCII("application/vnd.oma.drm.message");
+
+#endif
+
+ if (NS_SUCCEEDED(rv) && (disposition == nsIChannel::DISPOSITION_ATTACHMENT) &&
+ allowContentDispositionToForceExternalHandling) {
forceExternalHandling = true;
+ }
LOG((" forceExternalHandling: %s", forceExternalHandling ? "yes" : "no"));
-
+
// We're going to try to find a contentListener that can handle our data
nsCOMPtr<nsIURIContentListener> contentListener;
// The type or data the contentListener wants.
nsXPIDLCString desiredContentType;
if (!forceExternalHandling)
{
//
--- a/webapprt/content/dbg-webapp-actors.js
+++ b/webapprt/content/dbg-webapp-actors.js
@@ -1,15 +1,19 @@
/* 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 { Promise } = Cu.import("resource://gre/modules/Promise.jsm", {});
+const { devtools } = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
+const { BrowserTabActor, BrowserTabList, allAppShellDOMWindows,
+ sendShutdownEvent } = devtools.require("devtools/server/actors/webbrowser");
+const { RootActor } = devtools.require("devtools/server/actors/root");
/**
* WebappRT-specific actors.
*/
/**
* Construct a root actor appropriate for use in a server running in the webapp
* runtime. The returned root actor: