Merge m-c to inbound a=merge CLOSED TREE

This commit is contained in:
Wes Kocher 2015-02-26 18:52:43 -08:00
commit 81c5733880
84 changed files with 1946 additions and 369 deletions

View File

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="ef937d1aca7c4cf89ecb5cc43ae8c21c2000a9db">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="22230c9a340a0a2c3af73320cf47e1cb544517ad"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="7512026a377271a0cade12d70846557f0bc7781c"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3905180d9e2dbe03d7ce3b3a6debe4d4adc938d1"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>

View File

@ -19,7 +19,7 @@
<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="22230c9a340a0a2c3af73320cf47e1cb544517ad"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="7512026a377271a0cade12d70846557f0bc7781c"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3905180d9e2dbe03d7ce3b3a6debe4d4adc938d1"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="93f9ba577f68d772093987c2f1c0a4ae293e1802"/>

View File

@ -17,7 +17,7 @@
</project>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="22230c9a340a0a2c3af73320cf47e1cb544517ad"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="7512026a377271a0cade12d70846557f0bc7781c"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3905180d9e2dbe03d7ce3b3a6debe4d4adc938d1"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="c42985975f2bbc42859b9136ed348186d989b93d"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="a1ddea3133e0807350326cee5dcf0d06fad00c08"/>
@ -135,7 +135,7 @@
<project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="c5f8d282efe4a4e8b1e31a37300944e338e60e4f"/>
<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="9f28c4faea3b2f01db227b2467b08aeba96d9bec"/>
<project name="platform_system_nfcd" path="system/nfcd" remote="b2g" revision="d19dad5844e8803d5e88d1577a2742b4f5cbc467"/>
<project name="platform_system_nfcd" path="system/nfcd" remote="b2g" revision="84395037a7a04546e8ef7cb81572eb516b85562b"/>
<project name="android-sdk" path="sdk" remote="b2g" revision="8b1365af38c9a653df97349ee53a3f5d64fd590a"/>
<project name="darwinstreamingserver" path="system/darwinstreamingserver" remote="b2g" revision="cf85968c7f85e0ec36e72c87ceb4837a943b8af6"/>
</manifest>

View File

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="ef937d1aca7c4cf89ecb5cc43ae8c21c2000a9db">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="22230c9a340a0a2c3af73320cf47e1cb544517ad"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="7512026a377271a0cade12d70846557f0bc7781c"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3905180d9e2dbe03d7ce3b3a6debe4d4adc938d1"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>

View File

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="52775e03a2d8532429dff579cb2cd56718e488c3">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="22230c9a340a0a2c3af73320cf47e1cb544517ad"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="7512026a377271a0cade12d70846557f0bc7781c"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3905180d9e2dbe03d7ce3b3a6debe4d4adc938d1"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>

View File

@ -19,7 +19,7 @@
<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="22230c9a340a0a2c3af73320cf47e1cb544517ad"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="7512026a377271a0cade12d70846557f0bc7781c"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3905180d9e2dbe03d7ce3b3a6debe4d4adc938d1"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="93f9ba577f68d772093987c2f1c0a4ae293e1802"/>

View File

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="ef937d1aca7c4cf89ecb5cc43ae8c21c2000a9db">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="22230c9a340a0a2c3af73320cf47e1cb544517ad"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="7512026a377271a0cade12d70846557f0bc7781c"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3905180d9e2dbe03d7ce3b3a6debe4d4adc938d1"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
@ -146,7 +146,7 @@
<project name="platform/hardware/ril" path="hardware/ril" revision="12b1977cc704b35f2e9db2bb423fa405348bc2f3"/>
<project name="platform/system/bluetooth" path="system/bluetooth" revision="985bf15264d865fe7b9c5b45f61c451cbaafa43d"/>
<project name="platform/system/core" path="system/core" revision="42839aedcf70bf6bc92a3b7ea4a5cc9bf9aef3f9"/>
<project name="platform_system_nfcd" path="system/nfcd" remote="b2g" revision="d19dad5844e8803d5e88d1577a2742b4f5cbc467"/>
<project name="platform_system_nfcd" path="system/nfcd" remote="b2g" revision="84395037a7a04546e8ef7cb81572eb516b85562b"/>
<project name="platform/system/qcom" path="system/qcom" revision="63e3f6f176caad587d42bba4c16b66d953fb23c2"/>
<project name="platform/vendor/qcom-opensource/wlan/prima" path="vendor/qcom/opensource/wlan/prima" revision="d8952a42771045fca73ec600e2b42a4c7129d723"/>
<project name="platform/vendor/qcom/msm8610" path="device/qcom/msm8610" revision="4c187c1f3a0dffd8e51a961735474ea703535b99"/>

View File

@ -17,7 +17,7 @@
</project>
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="22230c9a340a0a2c3af73320cf47e1cb544517ad"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="7512026a377271a0cade12d70846557f0bc7781c"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3905180d9e2dbe03d7ce3b3a6debe4d4adc938d1"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="c42985975f2bbc42859b9136ed348186d989b93d"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="a1ddea3133e0807350326cee5dcf0d06fad00c08"/>
@ -145,7 +145,7 @@
<project name="platform/hardware/ril" path="hardware/ril" revision="c4e2ac95907a5519a0e09f01a0d8e27fec101af0"/>
<project name="platform/system/bluetooth" path="system/bluetooth" revision="e1eb226fa3ad3874ea7b63c56a9dc7012d7ff3c2"/>
<project name="platform/system/core" path="system/core" revision="adc485d8755af6a61641d197de7cfef667722580"/>
<project name="platform_system_nfcd" path="system/nfcd" remote="b2g" revision="d19dad5844e8803d5e88d1577a2742b4f5cbc467"/>
<project name="platform_system_nfcd" path="system/nfcd" remote="b2g" revision="84395037a7a04546e8ef7cb81572eb516b85562b"/>
<project name="platform/system/qcom" path="system/qcom" revision="1cdab258b15258b7f9657da70e6f06ebd5a2fc25"/>
<project name="platform/vendor/qcom/msm8610" path="device/qcom/msm8610" revision="4ae5df252123591d5b941191790e7abed1bce5a4"/>
<project name="platform/vendor/qcom-opensource/wlan/prima" path="vendor/qcom/opensource/wlan/prima" revision="ce18b47b4a4f93a581d672bbd5cb6d12fe796ca9"/>

View File

@ -1,9 +1,9 @@
{
"git": {
"git_revision": "22230c9a340a0a2c3af73320cf47e1cb544517ad",
"git_revision": "7512026a377271a0cade12d70846557f0bc7781c",
"remote": "https://git.mozilla.org/releases/gaia.git",
"branch": ""
},
"revision": "298fece36f2564e042e347a48fcc104aca78e3b5",
"revision": "53713e514b92cccfd55404cebd2b627cb9804bc2",
"repo_path": "integration/gaia-central"
}

View File

@ -17,7 +17,7 @@
</project>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="22230c9a340a0a2c3af73320cf47e1cb544517ad"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="7512026a377271a0cade12d70846557f0bc7781c"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3905180d9e2dbe03d7ce3b3a6debe4d4adc938d1"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="c42985975f2bbc42859b9136ed348186d989b93d"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="a1ddea3133e0807350326cee5dcf0d06fad00c08"/>
@ -130,7 +130,7 @@
<project name="device-mako" path="device/lge/mako" remote="b2g" revision="78d17f0c117f0c66dd55ee8d5c5dde8ccc93ecba"/>
<project name="device/generic/armv7-a-neon" path="device/generic/armv7-a-neon" revision="3a9a17613cc685aa232432566ad6cc607eab4ec1"/>
<project name="device/lge/mako-kernel" path="device/lge/mako-kernel" revision="d1729e53d71d711c8fde25eab8728ff2b9b4df0e"/>
<project name="platform_system_nfcd" path="system/nfcd" remote="b2g" revision="d19dad5844e8803d5e88d1577a2742b4f5cbc467"/>
<project name="platform_system_nfcd" path="system/nfcd" remote="b2g" revision="84395037a7a04546e8ef7cb81572eb516b85562b"/>
<project name="platform/external/libnfc-nci" path="external/libnfc-nci" revision="7d33aaf740bbf6c7c6e9c34a92b371eda311b66b"/>
<project name="platform/external/wpa_supplicant_8" path="external/wpa_supplicant_8" revision="0e56e450367cd802241b27164a2979188242b95f"/>
<project name="platform/hardware/broadcom/wlan" path="hardware/broadcom/wlan" revision="0e1929fa3aa38bf9d40e9e953d619fab8164c82e"/>

View File

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="52775e03a2d8532429dff579cb2cd56718e488c3">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="22230c9a340a0a2c3af73320cf47e1cb544517ad"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="7512026a377271a0cade12d70846557f0bc7781c"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3905180d9e2dbe03d7ce3b3a6debe4d4adc938d1"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
@ -141,7 +141,7 @@
<default remote="caf" revision="refs/tags/android-5.0.0_r6" sync-j="4"/>
<!-- Nexus 5 specific things -->
<project name="device/generic/armv7-a-neon" path="device/generic/armv7-a-neon" revision="ba62cc8b78c30d36181b8060a2016cc8da166236"/>
<project name="device-hammerhead" path="device/lge/hammerhead" remote="b2g" revision="b765862a60e0063fc2451f5e3e137247dd9838c3"/>
<project name="device-hammerhead" path="device/lge/hammerhead" remote="b2g" revision="dc525eeb67301a44f514a7ac40a59ec592b34e31"/>
<project name="device/qcom/common" path="device/qcom/common" remote="caf" revision="3697e5acf25629b82658334e3f8ee3b6df5becab"/>
<project name="device_lge_hammerhead-kernel" path="device/lge/hammerhead-kernel" remote="b2g" revision="1268f640184df5ef759ada669f101a613451673a"/>
<project name="platform/external/libnfc-nci" path="external/libnfc-nci" revision="0cb8574d338bf9f15b45ace7c08ad6deae9673ee"/>
@ -157,5 +157,5 @@
<project name="platform/hardware/qcom/sensors" path="hardware/qcom/sensors" revision="3724fd91ef5183684d97e2bf1d7ff948faabe090"/>
<project name="platform/hardware/qcom/wlan" path="hardware/qcom/wlan" revision="2e54754cc0529d26ccac37ed291600048adbf6c0"/>
<project name="platform/hardware/ril" path="hardware/ril" revision="71dfa8228ad0d6cdf6bac0426ac59404ab74b7f3"/>
<project name="platform_system_nfcd" path="system/nfcd" remote="b2g" revision="d19dad5844e8803d5e88d1577a2742b4f5cbc467"/>
<project name="platform_system_nfcd" path="system/nfcd" remote="b2g" revision="84395037a7a04546e8ef7cb81572eb516b85562b"/>
</manifest>

View File

@ -1577,6 +1577,9 @@ pref("devtools.browserconsole.filter.secwarn", true);
// Text size in the Web Console. Use 0 for the system default size.
pref("devtools.webconsole.fontSize", 0);
// Max number of inputs to store in web console history.
pref("devtools.webconsole.inputHistoryCount", 50);
// Persistent logging: |true| if you want the Web Console to keep all of the
// logged messages after reloading the page, |false| if you want the output to
// be cleared each time page navigation happens.

View File

@ -11,13 +11,19 @@ XPCOMUtils.defineLazyModuleGetter(this, "CloudSync",
let CloudSync = null;
#endif
XPCOMUtils.defineLazyModuleGetter(this, "ReadingListScheduler",
"resource:///modules/readinglist/Scheduler.jsm");
// gSyncUI handles updating the tools menu and displaying notifications.
let gSyncUI = {
_obs: ["weave:service:sync:start",
"weave:service:sync:finish",
"weave:service:sync:error",
"weave:service:quota:remaining",
"weave:service:setup-complete",
"weave:service:login:start",
"weave:service:login:finish",
"weave:service:login:error",
"weave:service:logout:finish",
"weave:service:start-over",
"weave:service:start-over:finish",
@ -25,9 +31,15 @@ let gSyncUI = {
"weave:ui:sync:error",
"weave:ui:sync:finish",
"weave:ui:clear-error",
"readinglist:sync:start",
"readinglist:sync:finish",
"readinglist:sync:error",
],
_unloaded: false,
// The number of "active" syncs - while this is non-zero, our button will spin
_numActiveSyncTasks: 0,
init: function () {
Cu.import("resource://services-common/stringbundle.js");
@ -95,21 +107,25 @@ let gSyncUI = {
}
},
_needsSetup: function SUI__needsSetup() {
_needsSetup() {
// We want to treat "account needs verification" as "needs setup". So
// "reach in" to Weave.Status._authManager to check whether we the signed-in
// user is verified.
// Referencing Weave.Status spins a nested event loop to initialize the
// authManager, so this should always return a value directly.
// This only applies to fxAccounts-based Sync.
if (Weave.Status._authManager._signedInUser) {
// If we have a signed in user already, and that user is not verified,
// revert to the "needs setup" state.
if (!Weave.Status._authManager._signedInUser.verified) {
return true;
}
if (Weave.Status._authManager._signedInUser !== undefined) {
// So we are using Firefox accounts - in this world, checking Sync isn't
// enough as reading list may be configured but not Sync.
// We consider ourselves setup if we have a verified user.
// XXX - later we should consider checking preferences to ensure at least
// one engine is enabled?
return !Weave.Status._authManager._signedInUser ||
!Weave.Status._authManager._signedInUser.verified;
}
// So we are using legacy sync, and reading-list isn't supported for such
// users, so check sync itself.
let firstSync = "";
try {
firstSync = Services.prefs.getCharPref("services.sync.firstSync");
@ -120,7 +136,8 @@ let gSyncUI = {
},
_loginFailed: function () {
return Weave.Status.login == Weave.LOGIN_FAILED_LOGIN_REJECTED;
return Weave.Status.login == Weave.LOGIN_FAILED_LOGIN_REJECTED ||
ReadingListScheduler.state == ReadingListScheduler.STATE_ERROR_AUTHENTICATION;
},
updateUI: function SUI_updateUI() {
@ -136,6 +153,7 @@ let gSyncUI = {
document.getElementById("sync-syncnow-state").hidden = false;
} else if (loginFailed) {
document.getElementById("sync-reauth-state").hidden = false;
this.showLoginError();
} else if (needsSetup) {
document.getElementById("sync-setup-state").hidden = false;
} else {
@ -146,14 +164,6 @@ let gSyncUI = {
return;
let syncButton = document.getElementById("sync-button");
if (syncButton) {
syncButton.removeAttribute("status");
}
let panelHorizontalButton = document.getElementById("PanelUI-fxa-status");
if (panelHorizontalButton) {
panelHorizontalButton.removeAttribute("syncstatus");
}
if (needsSetup && syncButton)
syncButton.removeAttribute("tooltiptext");
@ -162,17 +172,45 @@ let gSyncUI = {
// Functions called by observers
onActivityStart: function SUI_onActivityStart() {
onActivityStart() {
if (!gBrowser)
return;
let button = document.getElementById("sync-button");
if (button) {
button.setAttribute("status", "active");
this.log.debug("onActivityStart with numActive", this._numActiveSyncTasks);
if (++this._numActiveSyncTasks == 1) {
let button = document.getElementById("sync-button");
if (button) {
button.setAttribute("status", "active");
}
button = document.getElementById("PanelUI-fxa-status");
if (button) {
button.setAttribute("syncstatus", "active");
}
}
button = document.getElementById("PanelUI-fxa-status");
if (button) {
button.setAttribute("syncstatus", "active");
},
onActivityStop() {
if (!gBrowser)
return;
this.log.debug("onActivityStop with numActive", this._numActiveSyncTasks);
if (--this._numActiveSyncTasks) {
if (this._numActiveSyncTasks < 0) {
// This isn't particularly useful (it seems more likely we'll set a
// "start" without a "stop" meaning it forever remains > 0) but it
// might offer some value...
this.log.error("mismatched onActivityStart/Stop calls",
new Error("active=" + this._numActiveSyncTasks));
}
return; // active tasks are still ongoing...
}
let syncButton = document.getElementById("sync-button");
if (syncButton) {
syncButton.removeAttribute("status");
}
let panelHorizontalButton = document.getElementById("PanelUI-fxa-status");
if (panelHorizontalButton) {
panelHorizontalButton.removeAttribute("syncstatus");
}
},
@ -187,6 +225,7 @@ let gSyncUI = {
},
onLoginError: function SUI_onLoginError() {
// Note: This is used for *both* Sync and ReadingList login errors.
// if login fails, any other notifications are essentially moot
Weave.Notifications.removeAll();
@ -200,11 +239,18 @@ let gSyncUI = {
this.updateUI();
return;
}
this.showLoginError();
this.updateUI();
},
showLoginError() {
// Note: This is used for *both* Sync and ReadingList login errors.
let title = this._stringBundle.GetStringFromName("error.login.title");
let description;
if (Weave.Status.sync == Weave.PROLONGED_SYNC_FAILURE) {
if (Weave.Status.sync == Weave.PROLONGED_SYNC_FAILURE ||
this.isProlongedReadingListError()) {
this.log.debug("showLoginError has a prolonged login error");
// Convert to days
let lastSync =
Services.prefs.getIntPref("services.sync.errorhandler.networkFailureReportTimeout") / 86400;
@ -214,6 +260,7 @@ let gSyncUI = {
let reason = Weave.Utils.getErrorString(Weave.Status.login);
description =
this._stringBundle.formatStringFromName("error.sync.description", [reason], 1);
this.log.debug("showLoginError has a non-prolonged error", reason);
}
let buttons = [];
@ -226,7 +273,6 @@ let gSyncUI = {
let notification = new Weave.Notification(title, description, null,
Weave.Notifications.PRIORITY_WARNING, buttons);
Weave.Notifications.replaceTitle(notification);
this.updateUI();
},
onLogout: function SUI_onLogout() {
@ -271,6 +317,7 @@ let gSyncUI = {
}
Services.obs.notifyObservers(null, "cloudsync:user-sync", null);
Services.obs.notifyObservers(null, "readinglist:user-sync", null);
},
handleToolbarButton: function SUI_handleStatusbarButton() {
@ -367,7 +414,15 @@ let gSyncUI = {
let lastSync;
try {
lastSync = Services.prefs.getCharPref("services.sync.lastSync");
lastSync = new Date(Services.prefs.getCharPref("services.sync.lastSync"));
}
catch (e) { };
// and reading-list time - we want whatever one is the most recent.
try {
let lastRLSync = new Date(Services.prefs.getCharPref("readinglist.scheduler.lastSync"));
if (!lastSync || lastRLSync > lastSync) {
lastSync = lastRLSync;
}
}
catch (e) { };
if (!lastSync || this._needsSetup()) {
@ -376,9 +431,9 @@ let gSyncUI = {
}
// Show the day-of-week and time (HH:MM) of last sync
let lastSyncDate = new Date(lastSync).toLocaleFormat("%a %H:%M");
let lastSyncDateString = lastSync.toLocaleFormat("%a %H:%M");
let lastSyncLabel =
this._stringBundle.formatStringFromName("lastSync2.label", [lastSyncDate], 1);
this._stringBundle.formatStringFromName("lastSync2.label", [lastSyncDateString], 1);
syncButton.setAttribute("tooltiptext", lastSyncLabel);
},
@ -395,7 +450,69 @@ let gSyncUI = {
this.clearError(title);
},
// Return true if the reading-list is in a "prolonged" error state. That
// engine doesn't impose what that means, so calculate it here. For
// consistency, we just use the sync prefs.
isProlongedReadingListError() {
let lastSync, threshold, prolonged;
try {
lastSync = new Date(Services.prefs.getCharPref("readinglist.scheduler.lastSync"));
threshold = new Date(Date.now() - Services.prefs.getIntPref("services.sync.errorhandler.networkFailureReportTimeout"));
prolonged = lastSync <= threshold;
} catch (ex) {
// no pref, assume not prolonged.
prolonged = false;
}
this.log.debug("isProlongedReadingListError has last successful sync at ${lastSync}, threshold is ${threshold}, prolonged=${prolonged}",
{lastSync, threshold, prolonged});
return prolonged;
},
onRLSyncError() {
// Like onSyncError, but from the reading-list engine.
// However, the current UX around Sync is that error notifications should
// generally *not* be seen as they typically aren't actionable - so only
// authentication errors (which require user action) and "prolonged" errors
// (which technically aren't actionable, but user really should know anyway)
// are shown.
this.log.debug("onRLSyncError with readingList state", ReadingListScheduler.state);
if (ReadingListScheduler.state == ReadingListScheduler.STATE_ERROR_AUTHENTICATION) {
this.onLoginError();
return;
}
// If it's not prolonged there's nothing to do.
if (!this.isProlongedReadingListError()) {
this.log.debug("onRLSyncError has a non-authentication, non-prolonged error, so not showing any error UI");
return;
}
// So it's a prolonged error.
// Unfortunate duplication from below...
this.log.debug("onRLSyncError has a prolonged error");
let title = this._stringBundle.GetStringFromName("error.sync.title");
// XXX - this is somewhat wrong - we are reporting the threshold we consider
// to be prolonged, not how long it actually has been. (ie, lastSync below
// is effectively constant) - bit it too is copied from below.
let lastSync =
Services.prefs.getIntPref("services.sync.errorhandler.networkFailureReportTimeout") / 86400;
let description =
this._stringBundle.formatStringFromName("error.sync.prolonged_failure", [lastSync], 1);
let priority = Weave.Notifications.PRIORITY_INFO;
let buttons = [
new Weave.NotificationButton(
this._stringBundle.GetStringFromName("error.sync.tryAgainButton.label"),
this._stringBundle.GetStringFromName("error.sync.tryAgainButton.accesskey"),
function() { gSyncUI.doSync(); return true; }
),
];
let notification =
new Weave.Notification(title, description, null, priority, buttons);
Weave.Notifications.replaceTitle(notification);
this.updateUI();
},
onSyncError: function SUI_onSyncError() {
this.log.debug("onSyncError");
let title = this._stringBundle.GetStringFromName("error.sync.title");
if (Weave.Status.login != Weave.LOGIN_SUCCEEDED) {
@ -418,7 +535,9 @@ let gSyncUI = {
let priority = Weave.Notifications.PRIORITY_WARNING;
let buttons = [];
// Check if the client is outdated in some way
// Check if the client is outdated in some way (but note: we've never in the
// past, and probably never will, bump the relevent version numbers, so
// this is effectively dead code!)
let outdated = Weave.Status.sync == Weave.VERSION_OUT_OF_DATE;
for (let [engine, reason] in Iterator(Weave.Status.engines))
outdated = outdated || reason == Weave.VERSION_OUT_OF_DATE;
@ -468,6 +587,7 @@ let gSyncUI = {
},
observe: function SUI_observe(subject, topic, data) {
this.log.debug("observed", topic);
if (this._unloaded) {
Cu.reportError("SyncUI observer called after unload: " + topic);
return;
@ -480,10 +600,26 @@ let gSyncUI = {
subject = subject.wrappedJSObject.object;
}
// First handle "activity" only.
switch (topic) {
case "weave:service:sync:start":
case "weave:service:login:start":
case "readinglist:sync:start":
this.onActivityStart();
break;
case "weave:service:sync:finish":
case "weave:service:sync:error":
case "weave:service:login:finish":
case "weave:service:login:error":
case "readinglist:sync:finish":
case "readinglist:sync:error":
this.onActivityStop();
break;
}
// Now non-activity state (eg, enabled, errors, etc)
// Note that sync uses the ":ui:" notifications for errors because sync.
// ReadingList has no such concept (yet?; hopefully the :error is enough!)
switch (topic) {
case "weave:ui:sync:finish":
this.onSyncFinish();
break;
@ -496,9 +632,6 @@ let gSyncUI = {
case "weave:service:setup-complete":
this.onSetupComplete();
break;
case "weave:service:login:start":
this.onActivityStart();
break;
case "weave:service:login:finish":
this.onLoginFinish();
break;
@ -523,6 +656,13 @@ let gSyncUI = {
case "weave:ui:clear-error":
this.clearError();
break;
case "readinglist:sync:error":
this.onRLSyncError();
break;
case "readinglist:sync:finish":
this.clearError();
break;
}
},
@ -540,3 +680,6 @@ XPCOMUtils.defineLazyGetter(gSyncUI, "_stringBundle", function() {
createBundle("chrome://weave/locale/services/sync.properties");
});
XPCOMUtils.defineLazyGetter(gSyncUI, "log", function() {
return Log.repository.getLogger("browserwindow.syncui");
});

View File

@ -37,6 +37,8 @@ XPCOMUtils.defineLazyModuleGetter(this, "ContentSearch",
"resource:///modules/ContentSearch.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "AboutHome",
"resource:///modules/AboutHome.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Log",
"resource://gre/modules/Log.jsm");
XPCOMUtils.defineLazyServiceGetter(this, "Favicons",
"@mozilla.org/browser/favicon-service;1",
"mozIAsyncFavicons");

View File

@ -403,6 +403,8 @@ support-files =
[browser_ssl_error_reports.js]
[browser_star_hsts.js]
[browser_subframe_favicons_not_used.js]
[browser_syncui.js]
skip-if = e10s # Bug 1137087 - browser_tabopen_reflows.js fails if this was previously run with e10s
[browser_tabDrop.js]
skip-if = buildapp == 'mulet' || e10s
[browser_tabMatchesInAwesomebar.js]

View File

@ -0,0 +1,244 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
let {Log} = Cu.import("resource://gre/modules/Log.jsm", {});
let {Weave} = Cu.import("resource://services-sync/main.js", {});
let {Notifications} = Cu.import("resource://services-sync/notifications.js", {});
// The BackStagePass allows us to get this test-only non-exported function.
let {getInternalScheduler} = Cu.import("resource:///modules/readinglist/Scheduler.jsm", {});
let stringBundle = Cc["@mozilla.org/intl/stringbundle;1"]
.getService(Ci.nsIStringBundleService)
.createBundle("chrome://weave/locale/services/sync.properties");
// ensure test output sees log messages.
Log.repository.getLogger("browserwindow.syncui").addAppender(new Log.DumpAppender());
function promiseObserver(topic) {
return new Promise(resolve => {
let obs = (subject, topic, data) => {
Services.obs.removeObserver(obs, topic);
resolve(subject);
}
Services.obs.addObserver(obs, topic, false);
});
}
add_task(function* prepare() {
let xps = Components.classes["@mozilla.org/weave/service;1"]
.getService(Components.interfaces.nsISupports)
.wrappedJSObject;
yield xps.whenLoaded();
// mock out the "_needsSetup()" function so we don't short-circuit.
let oldNeedsSetup = window.gSyncUI._needsSetup;
window.gSyncUI._needsSetup = () => false;
registerCleanupFunction(() => {
window.gSyncUI._needsSetup = oldNeedsSetup;
});
});
add_task(function* testProlongedSyncError() {
let promiseNotificationAdded = promiseObserver("weave:notification:added");
Assert.equal(Notifications.notifications.length, 0, "start with no notifications");
// Pretend we are in the "prolonged error" state.
Weave.Status.sync = Weave.PROLONGED_SYNC_FAILURE;
Weave.Status.login = Weave.LOGIN_SUCCEEDED;
Services.obs.notifyObservers(null, "weave:ui:sync:error", null);
let subject = yield promiseNotificationAdded;
let notification = subject.wrappedJSObject.object; // sync's observer abstraction is abstract!
Assert.equal(notification.title, stringBundle.GetStringFromName("error.sync.title"));
Assert.equal(Notifications.notifications.length, 1, "exactly 1 notification");
// Now pretend we just had a successful sync - the error notification should go away.
let promiseNotificationRemoved = promiseObserver("weave:notification:removed");
Weave.Status.sync = Weave.STATUS_OK;
Services.obs.notifyObservers(null, "weave:ui:sync:finish", null);
yield promiseNotificationRemoved;
Assert.equal(Notifications.notifications.length, 0, "no notifications left");
});
add_task(function* testProlongedRLError() {
let promiseNotificationAdded = promiseObserver("weave:notification:added");
Assert.equal(Notifications.notifications.length, 0, "start with no notifications");
// Pretend the reading-list is in the "prolonged error" state.
let longAgo = new Date(Date.now() - 100 * 24 * 60 * 60 * 1000); // 100 days ago.
Services.prefs.setCharPref("readinglist.scheduler.lastSync", longAgo.toString());
getInternalScheduler().state = ReadingListScheduler.STATE_ERROR_OTHER;
Services.obs.notifyObservers(null, "readinglist:sync:start", null);
Services.obs.notifyObservers(null, "readinglist:sync:error", null);
let subject = yield promiseNotificationAdded;
let notification = subject.wrappedJSObject.object; // sync's observer abstraction is abstract!
Assert.equal(notification.title, stringBundle.GetStringFromName("error.sync.title"));
Assert.equal(Notifications.notifications.length, 1, "exactly 1 notification");
// Now pretend we just had a successful sync - the error notification should go away.
let promiseNotificationRemoved = promiseObserver("weave:notification:removed");
Services.prefs.setCharPref("readinglist.scheduler.lastSync", Date.now().toString());
Services.obs.notifyObservers(null, "readinglist:sync:start", null);
Services.obs.notifyObservers(null, "readinglist:sync:finish", null);
yield promiseNotificationRemoved;
Assert.equal(Notifications.notifications.length, 0, "no notifications left");
});
add_task(function* testSyncLoginError() {
let promiseNotificationAdded = promiseObserver("weave:notification:added");
Assert.equal(Notifications.notifications.length, 0, "start with no notifications");
// Pretend we are in the "prolonged error" state.
Weave.Status.sync = Weave.LOGIN_FAILED;
Weave.Status.login = Weave.LOGIN_FAILED_LOGIN_REJECTED;
Services.obs.notifyObservers(null, "weave:ui:sync:error", null);
let subject = yield promiseNotificationAdded;
let notification = subject.wrappedJSObject.object; // sync's observer abstraction is abstract!
Assert.equal(notification.title, stringBundle.GetStringFromName("error.login.title"));
Assert.equal(Notifications.notifications.length, 1, "exactly 1 notification");
// Now pretend we just had a successful login - the error notification should go away.
Weave.Status.sync = Weave.STATUS_OK;
Weave.Status.login = Weave.LOGIN_SUCCEEDED;
let promiseNotificationRemoved = promiseObserver("weave:notification:removed");
Services.obs.notifyObservers(null, "weave:service:login:start", null);
Services.obs.notifyObservers(null, "weave:service:login:finish", null);
yield promiseNotificationRemoved;
Assert.equal(Notifications.notifications.length, 0, "no notifications left");
});
add_task(function* testRLLoginError() {
let promiseNotificationAdded = promiseObserver("weave:notification:added");
Assert.equal(Notifications.notifications.length, 0, "start with no notifications");
// Pretend RL is in an auth error state
getInternalScheduler().state = ReadingListScheduler.STATE_ERROR_AUTHENTICATION;
Services.obs.notifyObservers(null, "readinglist:sync:start", null);
Services.obs.notifyObservers(null, "readinglist:sync:error", null);
let subject = yield promiseNotificationAdded;
let notification = subject.wrappedJSObject.object; // sync's observer abstraction is abstract!
Assert.equal(notification.title, stringBundle.GetStringFromName("error.login.title"));
Assert.equal(Notifications.notifications.length, 1, "exactly 1 notification");
// Now pretend we just had a successful sync - the error notification should go away.
getInternalScheduler().state = ReadingListScheduler.STATE_OK;
let promiseNotificationRemoved = promiseObserver("weave:notification:removed");
Services.obs.notifyObservers(null, "readinglist:sync:start", null);
Services.obs.notifyObservers(null, "readinglist:sync:finish", null);
yield promiseNotificationRemoved;
Assert.equal(Notifications.notifications.length, 0, "no notifications left");
});
// Here we put readinglist into an "authentication error" state (should see
// the error bar reflecting this), then report a prolonged error from Sync (an
// infobar to reflect the sync error should replace it), then resolve the sync
// error - the authentication error from readinglist should remain.
add_task(function* testRLLoginErrorRemains() {
let promiseNotificationAdded = promiseObserver("weave:notification:added");
Assert.equal(Notifications.notifications.length, 0, "start with no notifications");
// Pretend RL is in an auth error state
getInternalScheduler().state = ReadingListScheduler.STATE_ERROR_AUTHENTICATION;
Services.obs.notifyObservers(null, "readinglist:sync:start", null);
Services.obs.notifyObservers(null, "readinglist:sync:error", null);
let subject = yield promiseNotificationAdded;
let notification = subject.wrappedJSObject.object; // sync's observer abstraction is abstract!
Assert.equal(notification.title, stringBundle.GetStringFromName("error.login.title"));
Assert.equal(Notifications.notifications.length, 1, "exactly 1 notification");
// Now Sync into a prolonged auth error state.
promiseNotificationAdded = promiseObserver("weave:notification:added");
Weave.Status.sync = Weave.PROLONGED_SYNC_FAILURE;
Weave.Status.login = Weave.LOGIN_FAILED_LOGIN_REJECTED;
Services.obs.notifyObservers(null, "weave:ui:sync:error", null);
subject = yield promiseNotificationAdded;
// still exactly 1 notification with the "login" title.
notification = subject.wrappedJSObject.object;
Assert.equal(notification.title, stringBundle.GetStringFromName("error.login.title"));
Assert.equal(Notifications.notifications.length, 1, "exactly 1 notification");
// Resolve the sync problem.
promiseNotificationAdded = promiseObserver("weave:notification:added");
Weave.Status.sync = Weave.STATUS_OK;
Weave.Status.login = Weave.LOGIN_SUCCEEDED;
Services.obs.notifyObservers(null, "weave:ui:sync:finish", null);
// Expect one notification - the RL login problem.
subject = yield promiseNotificationAdded;
// still exactly 1 notification with the "login" title.
notification = subject.wrappedJSObject.object;
Assert.equal(notification.title, stringBundle.GetStringFromName("error.login.title"));
Assert.equal(Notifications.notifications.length, 1, "exactly 1 notification");
// and cleanup - resolve the readinglist error.
getInternalScheduler().state = ReadingListScheduler.STATE_OK;
let promiseNotificationRemoved = promiseObserver("weave:notification:removed");
Services.obs.notifyObservers(null, "readinglist:sync:start", null);
Services.obs.notifyObservers(null, "readinglist:sync:finish", null);
yield promiseNotificationRemoved;
Assert.equal(Notifications.notifications.length, 0, "no notifications left");
});
function checkButtonsStatus(shouldBeActive) {
let button = document.getElementById("sync-button");
let panelbutton = document.getElementById("PanelUI-fxa-status");
if (shouldBeActive) {
Assert.equal(button.getAttribute("status"), "active");
Assert.equal(panelbutton.getAttribute("syncstatus"), "active");
} else {
Assert.ok(!button.hasAttribute("status"));
Assert.ok(!panelbutton.hasAttribute("syncstatus"));
}
}
function testButtonActions(startNotification, endNotification) {
checkButtonsStatus(false);
// pretend a sync is starting.
Services.obs.notifyObservers(null, startNotification, null);
checkButtonsStatus(true);
// and has stopped
Services.obs.notifyObservers(null, endNotification, null);
checkButtonsStatus(false);
}
add_task(function* testButtonActivities() {
// add the Sync button to the panel so we can get it!
CustomizableUI.addWidgetToArea("sync-button", CustomizableUI.AREA_PANEL);
// check the button's functionality
yield PanelUI.show();
try {
testButtonActions("weave:service:login:start", "weave:service:login:finish");
testButtonActions("weave:service:login:start", "weave:service:login:error");
testButtonActions("weave:service:sync:start", "weave:service:sync:finish");
testButtonActions("weave:service:sync:start", "weave:service:sync:error");
testButtonActions("readinglist:sync:start", "readinglist:sync:finish");
testButtonActions("readinglist:sync:start", "readinglist:sync:error");
// and ensure the counters correctly handle multiple in-flight syncs
Services.obs.notifyObservers(null, "weave:service:sync:start", null);
checkButtonsStatus(true);
Services.obs.notifyObservers(null, "readinglist:sync:start", null);
checkButtonsStatus(true);
Services.obs.notifyObservers(null, "readinglist:sync:finish", null);
// sync is still going...
checkButtonsStatus(true);
// another reading list starts
Services.obs.notifyObservers(null, "readinglist:sync:start", null);
checkButtonsStatus(true);
// The initial sync stops.
Services.obs.notifyObservers(null, "weave:service:sync:finish", null);
// RL is still going...
checkButtonsStatus(true);
// RL finishes with an error, so no longer active.
Services.obs.notifyObservers(null, "readinglist:sync:error", null);
checkButtonsStatus(false);
} finally {
PanelUI.hide();
CustomizableUI.removeWidgetFromArea("sync-button");
}
});

View File

@ -4,6 +4,8 @@
let { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "DownloadUtils",
"resource://gre/modules/DownloadUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "DownloadsCommon",

View File

@ -66,6 +66,8 @@
let { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "DownloadsCommon",
"resource:///modules/DownloadsCommon.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "DownloadsViewUI",

View File

@ -0,0 +1,338 @@
/* 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 {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
XPCOMUtils.defineLazyModuleGetter(this, 'LogManager',
'resource://services-common/logmanager.js');
XPCOMUtils.defineLazyModuleGetter(this, 'Log',
'resource://gre/modules/Log.jsm');
XPCOMUtils.defineLazyModuleGetter(this, 'Preferences',
'resource://gre/modules/Preferences.jsm');
XPCOMUtils.defineLazyModuleGetter(this, 'setTimeout',
'resource://gre/modules/Timer.jsm');
XPCOMUtils.defineLazyModuleGetter(this, 'clearTimeout',
'resource://gre/modules/Timer.jsm');
Cu.import('resource://gre/modules/Task.jsm');
this.EXPORTED_SYMBOLS = ["ReadingListScheduler"];
// A list of "external" observer topics that may cause us to change when we
// sync.
const OBSERVERS = [
// We don't sync when offline and restart when online.
"network:offline-status-changed",
// FxA notifications also cause us to check if we should sync.
"fxaccounts:onverified",
// When something notices a local change to an item.
"readinglist:item-changed",
// some notifications the engine might send if we have been requested to backoff.
"readinglist:backoff-requested",
// request to sync now
"readinglist:user-sync",
];
///////// A temp object until we get our "engine"
let engine = {
ERROR_AUTHENTICATION: "authentication error",
sync: Task.async(function* () {
}),
}
let prefs = new Preferences("readinglist.scheduler.");
// A helper to manage our interval values.
let intervals = {
// Getters for our intervals.
_fixupIntervalPref(prefName, def) {
// All pref values are seconds, but we return ms.
return prefs.get(prefName, def) * 1000;
},
// How long after startup do we do an initial sync?
get initial() this._fixupIntervalPref("initial", 20), // 20 seconds.
// Every interval after the first.
get schedule() this._fixupIntervalPref("schedule", 2 * 60 * 60), // 2 hours
// After we've been told an item has changed
get dirty() this._fixupIntervalPref("dirty", 2 * 60), // 2 mins
// After an error
get retry() this._fixupIntervalPref("retry", 2 * 60), // 2 mins
};
// This is the implementation, but it's not exposed directly.
function InternalScheduler() {
// oh, I don't know what logs yet - let's guess!
let logs = ["readinglist", "FirefoxAccounts", "browserwindow.syncui"];
this._logManager = new LogManager("readinglist.", logs, "readinglist");
this.log = Log.repository.getLogger("readinglist.scheduler");
this.log.info("readinglist scheduler created.")
this.state = this.STATE_OK;
// don't this.init() here, but instead at the module level - tests want to
// add hooks before it is called.
}
InternalScheduler.prototype = {
// When the next scheduled sync should happen. If we can sync, there will
// be a timer set to fire then. If we can't sync there will not be a timer,
// but it will be set to fire then as soon as we can.
_nextScheduledSync: null,
// The time when the most-recent "backoff request" expires - we will never
// schedule a new timer before this.
_backoffUntil: 0,
// Our current timer.
_timer: null,
// Our timer fires a promise - _timerRunning is true until it resolves or
// rejects.
_timerRunning: false,
// Our sync engine - XXX - maybe just a callback?
_engine: engine,
// Our state variable and constants.
state: null,
STATE_OK: "ok",
STATE_ERROR_AUTHENTICATION: "authentication error",
STATE_ERROR_OTHER: "other error",
init() {
this.log.info("scheduler initialzing");
this._observe = this.observe.bind(this);
for (let notification of OBSERVERS) {
Services.obs.addObserver(this._observe, notification, false);
}
this._nextScheduledSync = Date.now() + intervals.initial;
this._setupTimer();
},
// Note: only called by tests.
finalize() {
this.log.info("scheduler finalizing");
this._clearTimer();
for (let notification of OBSERVERS) {
Services.obs.removeObserver(this._observe, notification);
}
this._observe = null;
},
observe(subject, topic, data) {
this.log.debug("observed ${}", topic);
switch (topic) {
case "readinglist:backoff-requested": {
// The subject comes in as a string, a number of seconds.
let interval = parseInt(data, 10);
if (isNaN(interval)) {
this.log.warn("Backoff request had non-numeric value", data);
return;
}
this.log.info("Received a request to backoff for ${} seconds", interval);
this._backoffUntil = Date.now() + interval * 1000;
this._maybeReschedule(0);
break;
}
case "readinglist:local:dirty":
this._maybeReschedule(intervals.dirty);
break;
case "readinglist:user-sync":
this._syncNow();
break;
case "fxaccounts:onverified":
// If we were in an authentication error state, reset that now.
if (this.state == this.STATE_ERROR_AUTHENTICATION) {
this.state = this.STATE_OK;
}
break;
// The rest just indicate that now is probably a good time to check if
// we can sync as normal using whatever schedule was previously set.
default:
break;
}
// When observers fire we ignore the current sync error state as the
// notification may indicate it's been resolved.
this._setupTimer(true);
},
// Is the current error state such that we shouldn't schedule a new sync.
_isBlockedOnError() {
// this needs more thought...
return this.state == this.STATE_ERROR_AUTHENTICATION;
},
// canSync indicates if we can currently sync.
_canSync(ignoreBlockingErrors = false) {
if (Services.io.offline) {
this.log.info("canSync=false - we are offline");
return false;
}
if (!ignoreBlockingErrors && this._isBlockedOnError()) {
this.log.info("canSync=false - we are in a blocked error state", this.state);
return false;
}
this.log.info("canSync=true");
return true;
},
// _setupTimer checks the current state and the environment to see when
// we should next sync and creates the timer with the appropriate delay.
_setupTimer(ignoreBlockingErrors = false) {
if (!this._canSync(ignoreBlockingErrors)) {
this._clearTimer();
return;
}
if (this._timer) {
let when = new Date(this._nextScheduledSync);
let delay = this._nextScheduledSync - Date.now();
this.log.info("checkStatus - already have a timer - will fire in ${delay}ms at ${when}",
{delay, when});
return;
}
if (this._timerRunning) {
this.log.info("checkStatus - currently syncing");
return;
}
// no timer and we can sync, so start a new one.
let now = Date.now();
let delay = Math.max(0, this._nextScheduledSync - now);
let when = new Date(now + delay);
this.log.info("next scheduled sync is in ${delay}ms (at ${when})", {delay, when})
this._timer = this._setTimeout(delay);
},
// Something (possibly naively) thinks the next sync should happen in
// delay-ms. If there's a backoff in progress, ignore the requested delay
// and use the back-off. If there's already a timer scheduled for earlier
// than delay, let the earlier timer remain. Otherwise, use the requested
// delay.
_maybeReschedule(delay) {
// If there's no delay specified and there's nothing currently scheduled,
// it means a backoff request while the sync is actually running - there's
// no need to do anything here - the next reschedule after the sync
// completes will take the backoff into account.
if (!delay && !this._nextScheduledSync) {
this.log.debug("_maybeReschedule ignoring a backoff request while running");
return;
}
let now = Date.now();
if (!this._nextScheduledSync) {
this._nextScheduledSync = now + delay;
}
// If there is something currently scheduled before the requested delay,
// keep the existing value (eg, if we have a timer firing in 1 second, and
// get a "dirty" notification that says we should sync in 2 seconds, we
// keep the 1 second value)
this._nextScheduledSync = Math.min(this._nextScheduledSync, now + delay);
// But we still need to honor a backoff.
this._nextScheduledSync = Math.max(this._nextScheduledSync, this._backoffUntil);
// And always create a new timer next time _setupTimer is called.
this._clearTimer();
},
// callback for when the timer fires.
_doSync() {
this.log.debug("starting sync");
this._timer = null;
this._timerRunning = true;
// flag that there's no new schedule yet, so a request coming in while
// we are running does the right thing.
this._nextScheduledSync = 0;
Services.obs.notifyObservers(null, "readinglist:sync:start", null);
this._engine.sync().then(() => {
this.log.info("Sync completed successfully");
// Write a pref in the same format used to services/sync to indicate
// the last success.
prefs.set("lastSync", new Date().toString());
this.state = this.STATE_OK;
this._logManager.resetFileLog(this._logManager.REASON_SUCCESS);
Services.obs.notifyObservers(null, "readinglist:sync:finish", null);
return intervals.schedule;
}).catch(err => {
this.log.error("Sync failed", err);
// XXX - how to detect an auth error?
this.state = err == this._engine.ERROR_AUTHENTICATION ?
this.STATE_ERROR_AUTHENTICATION : this.STATE_ERROR_OTHER;
this._logManager.resetFileLog(this._logManager.REASON_ERROR);
Services.obs.notifyObservers(null, "readinglist:sync:error", null);
return intervals.retry;
}).then(nextDelay => {
this._timerRunning = false;
// ensure a new timer is setup for the appropriate next time.
this._maybeReschedule(nextDelay);
this._setupTimer();
this._onAutoReschedule(); // just for tests...
}).catch(err => {
// We should never get here, but better safe than sorry...
this.log.error("Failed to reschedule after sync completed", err);
});
},
_clearTimer() {
if (this._timer) {
clearTimeout(this._timer);
this._timer = null;
}
},
// A function to "sync now", but not allowing it to start if one is
// already running, and rescheduling the timer.
// To call this, just send a "readinglist:user-sync" notification.
_syncNow() {
if (this._timerRunning) {
this.log.info("syncNow() but a sync is already in progress - ignoring");
return;
}
this._clearTimer();
this._doSync();
},
// A couple of hook-points for testing.
// xpcshell tests hook this so (a) it can check the expected delay is set
// and (b) to ignore the delay and set a timeout of 0 so the test is fast.
_setTimeout(delay) {
return setTimeout(() => this._doSync(), delay);
},
// xpcshell tests hook this to make sure that the correct state etc exist
// after a sync has been completed and a new timer created (or not).
_onAutoReschedule() {},
};
let internalScheduler = new InternalScheduler();
internalScheduler.init();
// The public interface into this module is tiny, so a simple object that
// delegates to the implementation.
let ReadingListScheduler = {
get STATE_OK() internalScheduler.STATE_OK,
get STATE_ERROR_AUTHENTICATION() internalScheduler.STATE_ERROR_AUTHENTICATION,
get STATE_ERROR_OTHER() internalScheduler.STATE_ERROR_OTHER,
get state() internalScheduler.state,
};
// These functions are exposed purely for tests, which manage to grab them
// via a BackstagePass.
function createTestableScheduler() {
// kill the "real" scheduler as we don't want it listening to notifications etc.
if (internalScheduler) {
internalScheduler.finalize();
internalScheduler = null;
}
// No .init() call - that's up to the tests after hooking.
return new InternalScheduler();
}
// mochitests want the internal state of the real scheduler for various things.
function getInternalScheduler() {
return internalScheduler;
}

View File

@ -13,3 +13,9 @@ TESTING_JS_MODULES += [
]
BROWSER_CHROME_MANIFESTS += ['test/browser/browser.ini']
XPCSHELL_TESTS_MANIFESTS += ['test/xpcshell/xpcshell.ini']
EXTRA_JS_MODULES.readinglist += [
'Scheduler.jsm',
]

View File

@ -0,0 +1,7 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");

View File

@ -0,0 +1,146 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
XPCOMUtils.defineLazyModuleGetter(this, 'setTimeout',
'resource://gre/modules/Timer.jsm');
// Setup logging prefs before importing the scheduler module.
Services.prefs.setCharPref("readinglist.log.appender.dump", "Trace");
let {createTestableScheduler} = Cu.import("resource:///modules/readinglist/Scheduler.jsm", {});
Cu.import("resource://gre/modules/Preferences.jsm");
Cu.import("resource://gre/modules/Timer.jsm");
// Log rotation needs a profile dir.
do_get_profile();
let prefs = new Preferences("readinglist.scheduler.");
function promiseObserver(topic) {
return new Promise(resolve => {
let obs = (subject, topic, data) => {
Services.obs.removeObserver(obs, topic);
resolve(data);
}
Services.obs.addObserver(obs, topic, false);
});
}
function createScheduler(options) {
// avoid typos in the test and other footguns in the options.
let allowedOptions = ["expectedDelay", "expectNewTimer", "syncFunction"];
for (let key of Object.keys(options)) {
if (!allowedOptions.includes(key)) {
throw new Error("Invalid option " + key);
}
}
let scheduler = createTestableScheduler();
// make our hooks
let syncFunction = options.syncFunction || Promise.resolve;
scheduler._engine.sync = syncFunction;
// we expect _setTimeout to be called *twice* - first is the initial sync,
// and there's no need to test the delay used for that. options.expectedDelay
// is to check the *subsequent* timer.
let numCalls = 0;
scheduler._setTimeout = function(delay) {
++numCalls;
print("Test scheduler _setTimeout call number " + numCalls + " with delay=" + delay);
switch (numCalls) {
case 1:
// this is the first and boring schedule as it initializes - do nothing
// other than return a timer that fires immediately.
return setTimeout(() => scheduler._doSync(), 0);
break;
case 2:
// This is the one we are interested in, so check things.
if (options.expectedDelay) {
// a little slop is OK as it takes a few ms to actually set the timer
ok(Math.abs(options.expectedDelay * 1000 - delay) < 500, [options.expectedDelay * 1000, delay]);
}
// and return a timeout that "never" fires
return setTimeout(() => scheduler._doSync(), 10000000);
break;
default:
// This is unexpected!
ok(false, numCalls);
}
};
// And a callback made once we've determined the next delay. This is always
// called even if _setTimeout isn't (due to no timer being created)
scheduler._onAutoReschedule = () => {
// Most tests expect a new timer, so this is "opt out"
let expectNewTimer = options.expectNewTimer === undefined ? true : options.expectNewTimer;
ok(expectNewTimer ? scheduler._timer : !scheduler._timer);
}
// calling .init fires things off...
scheduler.init();
return scheduler;
}
add_task(function* testSuccess() {
// promises which resolve once we've got all the expected notifications.
let allNotifications = [
promiseObserver("readinglist:sync:start"),
promiseObserver("readinglist:sync:finish"),
];
// New delay should be "as regularly scheduled".
prefs.set("schedule", 100);
let scheduler = createScheduler({expectedDelay: 100});
yield Promise.all(allNotifications);
scheduler.finalize();
});
add_task(function* testOffline() {
let scheduler = createScheduler({expectNewTimer: false});
Services.io.offline = true;
ok(!scheduler._canSync(), "_canSync is false when offline.")
ok(!scheduler._timer, "there is no current timer while offline.")
Services.io.offline = false;
ok(scheduler._canSync(), "_canSync is true when online.")
ok(scheduler._timer, "there is a new timer when back online.")
scheduler.finalize();
});
add_task(function* testRetryableError() {
let allNotifications = [
promiseObserver("readinglist:sync:start"),
promiseObserver("readinglist:sync:error"),
];
prefs.set("retry", 10);
let scheduler = createScheduler({
expectedDelay: 10,
syncFunction: () => Promise.reject("transient"),
});
yield Promise.all(allNotifications);
scheduler.finalize();
});
add_task(function* testAuthError() {
prefs.set("retry", 10);
// We expect an auth error to result in no new timer (as it's waiting for
// some indication it can proceed), but with the next delay being a normal
// "retry" interval (so when we can proceed it is probably already stale, so
// is effectively "immediate")
let scheduler = createScheduler({
expectedDelay: 10,
syncFunction: () => {
return Promise.reject(ReadingListScheduler._engine.ERROR_AUTHENTICATION);
},
expectNewTimer: false
});
// XXX - TODO - send an observer that "unblocks" us and ensure we actually
// do unblock.
scheduler.finalize();
});
add_task(function* testBackoff() {
let scheduler = createScheduler({expectedDelay: 1000});
Services.obs.notifyObservers(null, "readinglist:backoff-requested", 1000);
// XXX - this needs a little love as nothing checks createScheduler actually
// made the checks we think it does.
scheduler.finalize();
});
function run_test() {
run_next_test();
}

View File

@ -0,0 +1,5 @@
[DEFAULT]
head = head.js
firefox-appdir = browser
[test_scheduler.js]

View File

@ -48,7 +48,7 @@ function test() {
statusText: "OK",
type: "json",
fullMimeType: "application/json; charset=utf-8",
size: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.02),
size: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.03),
time: true
});
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(4),
@ -67,7 +67,7 @@ function test() {
statusText: "OK",
type: "png",
fullMimeType: "image/png",
size: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.75),
size: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.76),
time: true
});

View File

@ -228,8 +228,8 @@ function test() {
statusText: "Meh",
type: "2",
fullMimeType: "text/2",
transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.01),
size: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.01),
transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.02),
size: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.02),
time: true
});
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(c),
@ -239,8 +239,8 @@ function test() {
statusText: "Meh",
type: "3",
fullMimeType: "text/3",
transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.02),
size: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.02),
transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.03),
size: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.03),
time: true
});
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(d),
@ -250,8 +250,8 @@ function test() {
statusText: "Meh",
type: "4",
fullMimeType: "text/4",
transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.03),
size: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.03),
transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.04),
size: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.04),
time: true
});
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(e),
@ -261,8 +261,8 @@ function test() {
statusText: "Meh",
type: "5",
fullMimeType: "text/5",
transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.04),
size: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.04),
transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.05),
size: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.05),
time: true
});

View File

@ -143,8 +143,8 @@ function test() {
statusText: "Meh",
type: "2",
fullMimeType: "text/2",
transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.01),
size: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.01),
transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.02),
size: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.02),
time: true
});
}
@ -156,8 +156,8 @@ function test() {
statusText: "Meh",
type: "3",
fullMimeType: "text/3",
transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.02),
size: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.02),
transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.03),
size: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.03),
time: true
});
}
@ -169,8 +169,8 @@ function test() {
statusText: "Meh",
type: "4",
fullMimeType: "text/4",
transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.03),
size: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.03),
transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.04),
size: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.04),
time: true
});
}
@ -182,8 +182,8 @@ function test() {
statusText: "Meh",
type: "5",
fullMimeType: "text/5",
transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.04),
size: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.04),
transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.05),
size: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.05),
time: true
});
}

View File

@ -47,11 +47,6 @@ var Resource = Class({
this.uri = uri;
},
/**
* Return the trailing name component of this.uri.
*/
get basename() { return this.uri.path.replace(/\/+$/, '').replace(/\\/g,'/').replace( /.*\//, '' ); },
/**
* Is there more than 1 child Resource?
*/
@ -238,6 +233,13 @@ var FileResource = Class({
return this._refreshDeferred.promise;
},
/**
* Return the trailing name component of this Resource
*/
get basename() {
return this.path.replace(/\/+$/, '').replace(/\\/g,'/').replace( /.*\//, '' );
},
/**
* A string to be used when displaying this Resource in views
*/

View File

@ -13,7 +13,29 @@ add_task(function*() {
let root = [...projecteditor.project.allStores()][0].root;
is(root.path, TEMP_PATH, "The root store is set to the correct temp path.");
for (let child of root.children) {
yield renameWithContextMenu(projecteditor, projecteditor.projectTree.getViewContainer(child));
yield renameWithContextMenu(projecteditor,
projecteditor.projectTree.getViewContainer(child),
".renamed");
}
});
add_task(function*() {
let projecteditor = yield addProjectEditorTabForTempDirectory();
ok(true, "ProjectEditor has loaded");
let root = [...projecteditor.project.allStores()][0].root;
is(root.path, TEMP_PATH, "The root store is set to the correct temp path.");
let childrenList = new Array();
for (let child of root.children) {
yield renameWithContextMenu(projecteditor,
projecteditor.projectTree.getViewContainer(child),
".ren\u0061\u0308med");
childrenList.push(child.basename + ".ren\u0061\u0308med");
}
for (let child of root.children) {
is (childrenList.indexOf(child.basename) == -1, false,
"Failed to update tree with non-ascii character");
}
});
@ -25,7 +47,7 @@ function openContextMenuOn(node) {
);
}
function renameWithContextMenu(projecteditor, container) {
function renameWithContextMenu(projecteditor, container, newName) {
let defer = promise.defer();
let popup = projecteditor.contextMenuPopup;
let resource = container.resource;
@ -39,7 +61,7 @@ function renameWithContextMenu(projecteditor, container) {
projecteditor.project.on("refresh-complete", function refreshComplete() {
projecteditor.project.off("refresh-complete", refreshComplete);
OS.File.stat(resource.path + ".renamed").then(() => {
OS.File.stat(resource.path + newName).then(() => {
ok (true, "File is renamed");
defer.resolve();
}, (ex) => {
@ -50,7 +72,8 @@ function renameWithContextMenu(projecteditor, container) {
renameCommand.click();
popup.hidePopup();
EventUtils.sendString(resource.basename + ".renamed", projecteditor.window);
let input = container.elt.previousElementSibling;
input.value = resource.basename + newName;
EventUtils.synthesizeKey("VK_RETURN", {}, projecteditor.window);
});

View File

@ -54,7 +54,7 @@ function* testGraph(graph) {
is(graph._maxTooltip.querySelector("[text=value]").textContent, "60",
"The maximum tooltip displays the correct value.");
is(graph._avgTooltip.querySelector("[text=value]").textContent, "41.71",
is(graph._avgTooltip.querySelector("[text=value]").textContent, "41.72",
"The average tooltip displays the correct value.");
is(graph._minTooltip.querySelector("[text=value]").textContent, "10",
"The minimum tooltip displays the correct value.");

View File

@ -8,7 +8,7 @@ let {ViewHelpers} = Cu.import("resource:///modules/devtools/ViewHelpers.jsm", {}
function test() {
let l10n = new ViewHelpers.L10N();
is(l10n.numberWithDecimals(1234.56789, 2), "1,234.56",
is(l10n.numberWithDecimals(1234.56789, 2), "1,234.57",
"The first number was properly localized.");
is(l10n.numberWithDecimals(0.0001, 2), "0",
"The second number was properly localized.");

View File

@ -369,10 +369,6 @@ ViewHelpers.L10N.prototype = {
if (isNaN(aNumber) || aNumber == null) {
return "0";
}
// Remove {n} trailing decimals. Can't use toFixed(n) because
// toLocaleString converts the number to a string. Also can't use
// toLocaleString(, { maximumFractionDigits: n }) because it's not
// implemented on OS X (bug 368838). Gross.
let localized = aNumber.toLocaleString(); // localize
// If no grouping or decimal separators are available, bail out, because
@ -381,9 +377,10 @@ ViewHelpers.L10N.prototype = {
return localized;
}
let padded = localized + new Array(aDecimals).join("0"); // pad with zeros
let match = padded.match("([^]*?\\d{" + aDecimals + "})\\d*$");
return match.pop();
return aNumber.toLocaleString(undefined, {
maximumFractionDigits: aDecimals,
minimumFractionDigits: aDecimals
});
}
};

View File

@ -447,13 +447,15 @@ StyleEditorUI.prototype = {
* Editor to create UI for.
*/
_sourceLoaded: function(editor) {
let ordinal = editor.styleSheet.styleSheetIndex;
ordinal = ordinal == -1 ? Number.MAX_SAFE_INTEGER : ordinal;
// add new sidebar item and editor to the UI
this._view.appendTemplatedItem(STYLE_EDITOR_TEMPLATE, {
data: {
editor: editor
},
disableAnimations: this._alwaysDisableAnimations,
ordinal: editor.styleSheet.styleSheetIndex,
ordinal: ordinal,
onCreate: function(summary, details, data) {
let editor = data.editor;
editor.summary = summary;

View File

@ -15,6 +15,7 @@ const require = Cu.import("resource://gre/modules/devtools/Loader.jsm", {}).devt
const Editor = require("devtools/sourceeditor/editor");
const {Promise: promise} = Cu.import("resource://gre/modules/Promise.jsm", {});
const {CssLogic} = require("devtools/styleinspector/css-logic");
const {console} = require("resource://gre/modules/devtools/Console.jsm");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/FileUtils.jsm");
@ -251,11 +252,27 @@ StyleSheetEditor.prototype = {
callback(source);
}
return source;
}, e => {
if (this._isDestroyed) {
console.warn("Could not fetch the source for " +
this.styleSheet.href +
", the editor was destroyed");
Cu.reportError(e);
} else {
throw e;
}
});
}, e => {
this.emit("error", { key: LOAD_ERROR, append: this.styleSheet.href });
throw e;
})
if (this._isDestroyed) {
console.warn("Could not fetch the source for " +
this.styleSheet.href +
", the editor was destroyed");
Cu.reportError(e);
} else {
this.emit("error", { key: LOAD_ERROR, append: this.styleSheet.href });
throw e;
}
});
},
/**
@ -712,6 +729,7 @@ StyleSheetEditor.prototype = {
this.cssSheet.off("property-change", this._onPropertyChange);
this.cssSheet.off("media-rules-changed", this._onMediaRulesChanged);
this.styleSheet.off("error", this._onError);
this._isDestroyed = true;
}
}

View File

@ -9,7 +9,7 @@
//
thisTestLeaksUncaughtRejectionsAndShouldBeFixed("Error: Unknown sheet source");
const TESTCASE_URI = TEST_BASE + "autocomplete.html";
const TESTCASE_URI = TEST_BASE_HTTP + "autocomplete.html";
const MAX_SUGGESTIONS = 15;
// Pref which decides if CSS autocompletion is enabled in Style Editor or not.

View File

@ -8,7 +8,7 @@
//
thisTestLeaksUncaughtRejectionsAndShouldBeFixed("Error: Unknown sheet source");
const TESTCASE_URI = TEST_BASE + "four.html";
const TESTCASE_URI = TEST_BASE_HTTP + "four.html";
let gUI;

View File

@ -2,8 +2,8 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
const TESTCASE_URI_HTML = TEST_BASE + "simple.html";
const TESTCASE_URI_CSS = TEST_BASE + "simple.css";
const TESTCASE_URI_HTML = TEST_BASE_HTTP + "simple.html";
const TESTCASE_URI_CSS = TEST_BASE_HTTP + "simple.css";
const Cc = Components.classes;
const Ci = Components.interfaces;

View File

@ -9,7 +9,7 @@
//
thisTestLeaksUncaughtRejectionsAndShouldBeFixed("TypeError: summary is undefined");
const TESTCASE_URI = TEST_BASE + "simple.html";
const TESTCASE_URI = TEST_BASE_HTTP + "simple.html";
let gUI;

View File

@ -11,8 +11,8 @@ thisTestLeaksUncaughtRejectionsAndShouldBeFixed("Error: Unknown sheet source");
let gUI;
const FIRST_TEST_PAGE = TEST_BASE + "inline-1.html"
const SECOND_TEST_PAGE = TEST_BASE + "inline-2.html"
const FIRST_TEST_PAGE = TEST_BASE_HTTP + "inline-1.html"
const SECOND_TEST_PAGE = TEST_BASE_HTTP + "inline-2.html"
const SAVE_PATH = "test.css";
function test()

View File

@ -9,7 +9,7 @@
//
thisTestLeaksUncaughtRejectionsAndShouldBeFixed("Error: Unknown sheet source");
const TESTCASE_URI = TEST_BASE + "simple.html";
const TESTCASE_URI = TEST_BASE_HTTP + "simple.html";
let TESTCASE_CSS_SOURCE = "body{background-color:red;";

View File

@ -2,7 +2,7 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
const TESTCASE_URI = TEST_BASE + "nostyle.html";
const TESTCASE_URI = TEST_BASE_HTTP + "nostyle.html";
function test()

View File

@ -9,7 +9,7 @@
//
thisTestLeaksUncaughtRejectionsAndShouldBeFixed("Error: Unknown sheet source");
const TESTCASE_URI = TEST_BASE + "minified.html";
const TESTCASE_URI = TEST_BASE_HTTP + "minified.html";
let gUI;

View File

@ -6,11 +6,11 @@ Components.utils.import("resource://gre/modules/Task.jsm");
let {devtools} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
let {Promise: promise} = Cu.import("resource://gre/modules/Promise.jsm", {});
const TESTCASE_URI_HTML = TEST_BASE + "sourcemaps-watching.html";
const TESTCASE_URI_CSS = TEST_BASE + "sourcemap-css/sourcemaps.css";
const TESTCASE_URI_REG_CSS = TEST_BASE + "simple.css";
const TESTCASE_URI_SCSS = TEST_BASE + "sourcemap-sass/sourcemaps.scss";
const TESTCASE_URI_MAP = TEST_BASE + "sourcemap-css/sourcemaps.css.map";
const TESTCASE_URI_HTML = TEST_BASE_HTTP + "sourcemaps-watching.html";
const TESTCASE_URI_CSS = TEST_BASE_HTTP + "sourcemap-css/sourcemaps.css";
const TESTCASE_URI_REG_CSS = TEST_BASE_HTTP + "simple.css";
const TESTCASE_URI_SCSS = TEST_BASE_HTTP + "sourcemap-sass/sourcemaps.scss";
const TESTCASE_URI_MAP = TEST_BASE_HTTP + "sourcemap-css/sourcemaps.css.map";
const TESTCASE_SCSS_NAME = "sourcemaps.scss";
const TRANSITIONS_PREF = "devtools.styleeditor.transitions";

View File

@ -9,7 +9,7 @@
//
thisTestLeaksUncaughtRejectionsAndShouldBeFixed("Error: Unknown sheet source");
const TESTCASE_URI = TEST_BASE + "four.html";
const TESTCASE_URI = TEST_BASE_HTTP + "four.html";
let gUI;

View File

@ -2,7 +2,7 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
const TESTCASE_URI = TEST_BASE + "simple.html";
const TESTCASE_URI = TEST_BASE_HTTP + "simple.html";
let gOriginalWidth; // these are set by runTests()
let gOriginalHeight;

View File

@ -354,6 +354,7 @@ skip-if = e10s # Bug 1042253 - webconsole tests disabled with e10s
skip-if = e10s # Bug 1042253 - webconsole tests disabled with e10s
[browser_webconsole_autocomplete-properties-with-non-alphanumeric-names.js]
[browser_console_hide_jsterm_when_devtools_chrome_enabled_false.js]
[browser_console_history_persist.js]
[browser_webconsole_output_01.js]
skip-if = e10s # Bug 1042253 - webconsole e10s tests
[browser_webconsole_output_02.js]

View File

@ -0,0 +1,96 @@
/* vim:set ts=2 sw=2 sts=2 et: */
/* 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 that console command input is persisted across toolbox loads.
// See Bug 943306.
"use strict";
const TEST_URI = "data:text/html;charset=utf-8,Web Console test for persisting history - bug 943306";
const INPUT_HISTORY_COUNT = 10;
let test = asyncTest(function* () {
info ("Setting custom input history pref to " + INPUT_HISTORY_COUNT);
Services.prefs.setIntPref("devtools.webconsole.inputHistoryCount", INPUT_HISTORY_COUNT);
// First tab: run a bunch of commands and then make sure that you can
// navigate through their history.
yield loadTab(TEST_URI);
let hud1 = yield openConsole();
is (JSON.stringify(hud1.jsterm.history), "[]", "No history on first tab initially");
yield populateInputHistory(hud1);
is (JSON.stringify(hud1.jsterm.history), '["0","1","2","3","4","5","6","7","8","9"]',
"First tab has populated history");
// Second tab: Just make sure that you can navigate through the history
// generated by the first tab.
yield loadTab(TEST_URI);
let hud2 = yield openConsole();
is (JSON.stringify(hud2.jsterm.history), '["0","1","2","3","4","5","6","7","8","9"]',
"Second tab has populated history");
yield testNaviatingHistoryInUI(hud2);
is (JSON.stringify(hud2.jsterm.history), '["0","1","2","3","4","5","6","7","8","9",""]',
"An empty entry has been added in the second tab due to history perusal");
// Third tab: Should have the same history as first tab, but if we run a
// command, then the history of the first and second shouldn't be affected
yield loadTab(TEST_URI);
let hud3 = yield openConsole();
is (JSON.stringify(hud3.jsterm.history), '["0","1","2","3","4","5","6","7","8","9"]',
"Third tab has populated history");
// Set input value separately from execute so UP arrow accurately navigates history.
hud3.jsterm.setInputValue('"hello from third tab"');
hud3.jsterm.execute();
is (JSON.stringify(hud1.jsterm.history), '["0","1","2","3","4","5","6","7","8","9"]',
"First tab history hasn't changed due to command in third tab");
is (JSON.stringify(hud2.jsterm.history), '["0","1","2","3","4","5","6","7","8","9",""]',
"Second tab history hasn't changed due to command in third tab");
is (JSON.stringify(hud3.jsterm.history), '["1","2","3","4","5","6","7","8","9","\\"hello from third tab\\""]',
"Third tab has updated history (and purged the first result) after running a command");
// Fourth tab: Should have the latest command from the third tab, followed
// by the rest of the history from the first tab.
yield loadTab(TEST_URI);
let hud4 = yield openConsole();
is (JSON.stringify(hud4.jsterm.history), '["1","2","3","4","5","6","7","8","9","\\"hello from third tab\\""]',
"Fourth tab has most recent history");
info ("Clearing custom input history pref");
Services.prefs.clearUserPref("devtools.webconsole.inputHistoryCount");
});
/**
* Populate the history by running the following commands:
* [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
*/
function* populateInputHistory(hud) {
let jsterm = hud.jsterm;
let {inputNode} = jsterm;
for (let i = 0; i < INPUT_HISTORY_COUNT; i++) {
// Set input value separately from execute so UP arrow accurately navigates history.
jsterm.setInputValue(i);
jsterm.execute();
}
}
/**
* Check pressing up results in history traversal like:
* [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
*/
function* testNaviatingHistoryInUI(hud) {
let jsterm = hud.jsterm;
let {inputNode} = jsterm;
inputNode.focus();
// Count backwards from original input and make sure that pressing up
// restores this.
for (let i = INPUT_HISTORY_COUNT - 1; i >= 0; i--) {
EventUtils.synthesizeKey("VK_UP", {});
is(inputNode.value, i, "Pressing up restores last input");
}
}

View File

@ -13,6 +13,7 @@ let {devtools} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
let {require, TargetFactory} = devtools;
let {Utils: WebConsoleUtils} = require("devtools/toolkit/webconsole/utils");
let {Messages} = require("devtools/webconsole/console-output");
const asyncStorage = require("devtools/toolkit/shared/async-storage");
// promise._reportErrors = true; // please never leave me.
//Services.prefs.setBoolPref("devtools.debugger.log", true);
@ -322,6 +323,9 @@ let finishTest = Task.async(function* () {
registerCleanupFunction(function*() {
gDevTools.testing = false;
// Remove stored console commands in between tests
yield asyncStorage.removeItem("webConsoleHistory");
dumpConsoles();
if (HUDService.getBrowserConsole()) {

View File

@ -26,6 +26,8 @@ loader.lazyGetter(this, "ConsoleOutput",
() => require("devtools/webconsole/console-output").ConsoleOutput);
loader.lazyGetter(this, "Messages",
() => require("devtools/webconsole/console-output").Messages);
loader.lazyGetter(this, "asyncStorage",
() => require("devtools/toolkit/shared/async-storage"));
loader.lazyImporter(this, "EnvironmentClient", "resource://gre/modules/devtools/dbg-client.jsm");
loader.lazyImporter(this, "ObjectClient", "resource://gre/modules/devtools/dbg-client.jsm");
loader.lazyImporter(this, "VariablesView", "resource:///modules/devtools/VariablesView.jsm");
@ -176,6 +178,7 @@ const MIN_FONT_SIZE = 10;
const PREF_CONNECTION_TIMEOUT = "devtools.debugger.remote-timeout";
const PREF_PERSISTLOG = "devtools.webconsole.persistlog";
const PREF_MESSAGE_TIMESTAMP = "devtools.webconsole.timestampMessages";
const PREF_INPUT_HISTORY_COUNT = "devtools.webconsole.inputHistoryCount";
/**
* A WebConsoleFrame instance is an interactive console initialized *per target*
@ -443,12 +446,29 @@ WebConsoleFrame.prototype = {
/**
* Initialize the WebConsoleFrame instance.
* @return object
* A promise object for the initialization.
* A promise object that resolves once the frame is ready to use.
*/
init: function WCF_init()
init: function()
{
this._initUI();
return this._initConnection();
let connectionInited = this._initConnection();
// Don't reject if the history fails to load for some reason.
// This would be fine, the panel will just start with empty history.
let allReady = this.jsterm.historyLoaded.catch(() => {}).then(() => {
return connectionInited;
});
// This notification is only used in tests. Don't chain it onto
// the returned promise because the console panel needs to be attached
// to the toolbox before the web-console-created event is receieved.
let notifyObservers = () => {
let id = WebConsoleUtils.supportsString(this.hudId);
Services.obs.notifyObservers(id, "web-console-created", null);
};
allReady.then(notifyObservers, notifyObservers);
return allReady;
},
/**
@ -475,9 +495,6 @@ WebConsoleFrame.prototype = {
aReason.error + ": " + aReason.message);
this.outputMessage(CATEGORY_JS, node, [aReason]);
this._initDefer.reject(aReason);
}).then(() => {
let id = WebConsoleUtils.supportsString(this.hudId);
Services.obs.notifyObservers(id, "web-console-created", null);
});
return this._initDefer.promise;
@ -3054,17 +3071,11 @@ function JSTerm(aWebConsoleFrame)
{
this.hud = aWebConsoleFrame;
this.hudId = this.hud.hudId;
this.inputHistoryCount = Services.prefs.getIntPref(PREF_INPUT_HISTORY_COUNT);
this.lastCompletion = { value: null };
this.history = [];
this._loadHistory();
// Holds the number of entries in history. This value is incremented in
// this.execute().
this.historyIndex = 0; // incremented on this.execute()
// Holds the index of the history entry that the user is currently viewing.
// This is reset to this.history.length when this.execute() is invoked.
this.historyPlaceHolder = 0;
this._objectActorsInVariablesViews = new Map();
this._keyPress = this._keyPress.bind(this);
@ -3079,6 +3090,38 @@ function JSTerm(aWebConsoleFrame)
JSTerm.prototype = {
SELECTED_FRAME: -1,
/**
* Load the console history from previous sessions.
* @private
*/
_loadHistory: function() {
this.history = [];
this.historyIndex = this.historyPlaceHolder = 0;
this.historyLoaded = asyncStorage.getItem("webConsoleHistory").then(value => {
if (Array.isArray(value)) {
// Since it was gotten asynchronously, there could be items already in
// the history. It's not likely but stick them onto the end anyway.
this.history = value.concat(this.history);
// Holds the number of entries in history. This value is incremented in
// this.execute().
this.historyIndex = this.history.length;
// Holds the index of the history entry that the user is currently viewing.
// This is reset to this.history.length when this.execute() is invoked.
this.historyPlaceHolder = this.history.length;
}
}, console.error);
},
/**
* Stores the console history for future sessions.
*/
storeHistory: function() {
asyncStorage.setItem("webConsoleHistory", this.history);
},
/**
* Stores the data for the last completion.
* @type object
@ -3388,6 +3431,12 @@ JSTerm.prototype = {
// value that was not evaluated yet.
this.history[this.historyIndex++] = aExecuteString;
this.historyPlaceHolder = this.history.length;
if (this.history.length > this.inputHistoryCount) {
this.history.splice(0, this.history.length - this.inputHistoryCount);
this.historyIndex = this.historyPlaceHolder = this.history.length;
}
this.storeHistory();
WebConsoleUtils.usageCount++;
this.setInputValue("");
this.clearCompletion();

View File

@ -495,9 +495,14 @@ label.requests-menu-status-code {
}
.tabpanel-summary-value {
color: inherit;
-moz-padding-start: 3px;
}
.theme-dark .tabpanel-summary-value {
color: var(--theme-selection-color);
}
/* Headers tabpanel */
#headers-summary-status,

View File

@ -0,0 +1,28 @@
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
/* 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/. */
#include "BluetoothInterfaceHelpers.h"
BEGIN_BLUETOOTH_NAMESPACE
//
// Conversion
//
nsresult
Convert(nsresult aIn, BluetoothStatus& aOut)
{
if (NS_SUCCEEDED(aIn)) {
aOut = STATUS_SUCCESS;
} else if (aIn == NS_ERROR_OUT_OF_MEMORY) {
aOut = STATUS_NOMEM;
} else {
aOut = STATUS_FAIL;
}
return NS_OK;
}
END_BLUETOOTH_NAMESPACE

View File

@ -12,6 +12,13 @@
BEGIN_BLUETOOTH_NAMESPACE
//
// Conversion
//
nsresult
Convert(nsresult aIn, BluetoothStatus& aOut);
//
// Result handling
//
@ -49,6 +56,9 @@ public:
static void
Dispatch(Obj* aObj, Res (Obj::*aMethod)(), const InitOp& aInitOp)
{
if (!aObj) {
return; // silently return if no result runnable has been given
}
nsRefPtr<SelfType> runnable = Create(aObj, aMethod, aInitOp);
if (!runnable) {
BT_LOGR("BluetoothResultRunnable0::Create failed");
@ -108,6 +118,9 @@ public:
static void
Dispatch(Obj* aObj, Res (Obj::*aMethod)(Arg1), const InitOp& aInitOp)
{
if (!aObj) {
return; // silently return if no result runnable has been given
}
nsRefPtr<SelfType> runnable = Create(aObj, aMethod, aInitOp);
if (!runnable) {
BT_LOGR("BluetoothResultRunnable1::Create failed");
@ -174,6 +187,9 @@ public:
Dispatch(Obj* aObj, Res (Obj::*aMethod)(Arg1, Arg2, Arg3),
const InitOp& aInitOp)
{
if (!aObj) {
return; // silently return if no result runnable has been given
}
nsRefPtr<SelfType> runnable = Create(aObj, aMethod, aInitOp);
if (!runnable) {
BT_LOGR("BluetoothResultRunnable3::Create failed");

View File

@ -335,6 +335,9 @@ public:
{
BT_WARNING("BluetoothAvrcpInterface::Cleanup failed: %d",
(int)aStatus);
sBtAvrcpInterface = nullptr;
if (mRes) {
if (aStatus == STATUS_UNSUPPORTED) {
/* Not all versions of Bluedroid support AVRCP. So if the
@ -371,6 +374,9 @@ public:
{
BT_WARNING("BluetoothA2dpInterface::Cleanup failed: %d",
(int)aStatus);
sBtA2dpInterface = nullptr;
if (mRes) {
mRes->OnError(NS_ERROR_FAILURE);
}

View File

@ -395,7 +395,7 @@ BluetoothDaemonA2dpInterface::Init(
nsresult rv = mModule->RegisterModule(BluetoothDaemonA2dpModule::SERVICE_ID,
0x00, BluetoothDaemonA2dpModule::MAX_NUM_CLIENTS, res);
if (NS_FAILED(rv) && aRes) {
DispatchError(aRes, STATUS_FAIL);
DispatchError(aRes, rv);
}
}
@ -443,8 +443,12 @@ void
BluetoothDaemonA2dpInterface::Cleanup(
BluetoothA2dpResultHandler* aRes)
{
mModule->UnregisterModule(BluetoothDaemonA2dpModule::SERVICE_ID,
new CleanupResultHandler(mModule, aRes));
nsresult rv = mModule->UnregisterModule(
BluetoothDaemonA2dpModule::SERVICE_ID,
new CleanupResultHandler(mModule, aRes));
if (NS_FAILED(rv)) {
DispatchError(aRes, rv);
}
}
/* Connect / Disconnect */
@ -455,7 +459,10 @@ BluetoothDaemonA2dpInterface::Connect(
{
MOZ_ASSERT(mModule);
mModule->ConnectCmd(aBdAddr, aRes);
nsresult rv = mModule->ConnectCmd(aBdAddr, aRes);
if (NS_FAILED(rv)) {
DispatchError(aRes, rv);
}
}
void
@ -464,7 +471,10 @@ BluetoothDaemonA2dpInterface::Disconnect(
{
MOZ_ASSERT(mModule);
mModule->DisconnectCmd(aBdAddr, aRes);
nsresult rv = mModule->DisconnectCmd(aBdAddr, aRes);
if (NS_FAILED(rv)) {
DispatchError(aRes, rv);
}
}
void
@ -477,4 +487,16 @@ BluetoothDaemonA2dpInterface::DispatchError(
ConstantInitOp1<BluetoothStatus>(aStatus));
}
void
BluetoothDaemonA2dpInterface::DispatchError(
BluetoothA2dpResultHandler* aRes, nsresult aRv)
{
BluetoothStatus status;
if (NS_WARN_IF(NS_FAILED(Convert(aRv, status)))) {
status = STATUS_FAIL;
}
DispatchError(aRes, status);
}
END_BLUETOOTH_NAMESPACE

View File

@ -155,6 +155,7 @@ public:
private:
void DispatchError(BluetoothA2dpResultHandler* aRes,
BluetoothStatus aStatus);
void DispatchError(BluetoothA2dpResultHandler* aRes, nsresult aRv);
BluetoothDaemonA2dpModule* mModule;
};

View File

@ -893,7 +893,7 @@ BluetoothDaemonAvrcpInterface::Init(
BluetoothDaemonAvrcpModule::MAX_NUM_CLIENTS, 0x00, res);
if (NS_FAILED(rv) && aRes) {
DispatchError(aRes, STATUS_FAIL);
DispatchError(aRes, rv);
}
}
@ -943,8 +943,12 @@ BluetoothDaemonAvrcpInterface::Cleanup(
{
MOZ_ASSERT(mModule);
mModule->UnregisterModule(BluetoothDaemonAvrcpModule::SERVICE_ID,
new CleanupResultHandler(mModule, aRes));
nsresult rv = mModule->UnregisterModule(
BluetoothDaemonAvrcpModule::SERVICE_ID,
new CleanupResultHandler(mModule, aRes));
if (NS_FAILED(rv)) {
DispatchError(aRes, rv);
}
}
void
@ -954,7 +958,11 @@ BluetoothDaemonAvrcpInterface::GetPlayStatusRsp(
{
MOZ_ASSERT(mModule);
mModule->GetPlayStatusRspCmd(aPlayStatus, aSongLen, aSongPos, aRes);
nsresult rv = mModule->GetPlayStatusRspCmd(aPlayStatus, aSongLen,
aSongPos, aRes);
if (NS_FAILED(rv)) {
DispatchError(aRes, rv);
}
}
void
@ -964,7 +972,10 @@ BluetoothDaemonAvrcpInterface::ListPlayerAppAttrRsp(
{
MOZ_ASSERT(mModule);
mModule->ListPlayerAppAttrRspCmd(aNumAttr, aPAttrs, aRes);
nsresult rv = mModule->ListPlayerAppAttrRspCmd(aNumAttr, aPAttrs, aRes);
if (NS_FAILED(rv)) {
DispatchError(aRes, rv);
}
}
void
@ -973,7 +984,10 @@ BluetoothDaemonAvrcpInterface::ListPlayerAppValueRsp(
{
MOZ_ASSERT(mModule);
mModule->ListPlayerAppValueRspCmd(aNumVal, aPVals, aRes);
nsresult rv = mModule->ListPlayerAppValueRspCmd(aNumVal, aPVals, aRes);
if (NS_FAILED(rv)) {
DispatchError(aRes, rv);
}
}
void
@ -983,7 +997,11 @@ BluetoothDaemonAvrcpInterface::GetPlayerAppValueRsp(
{
MOZ_ASSERT(mModule);
mModule->GetPlayerAppValueRspCmd(aNumAttrs, aIds, aValues, aRes);
nsresult rv = mModule->GetPlayerAppValueRspCmd(aNumAttrs, aIds,
aValues, aRes);
if (NS_FAILED(rv)) {
DispatchError(aRes, rv);
}
}
void
@ -993,7 +1011,11 @@ BluetoothDaemonAvrcpInterface::GetPlayerAppAttrTextRsp(
{
MOZ_ASSERT(mModule);
mModule->GetPlayerAppAttrTextRspCmd(aNumAttr, aIds, aTexts, aRes);
nsresult rv = mModule->GetPlayerAppAttrTextRspCmd(aNumAttr, aIds,
aTexts, aRes);
if (NS_FAILED(rv)) {
DispatchError(aRes, rv);
}
}
void
@ -1003,7 +1025,11 @@ BluetoothDaemonAvrcpInterface::GetPlayerAppValueTextRsp(
{
MOZ_ASSERT(mModule);
mModule->GetPlayerAppValueTextRspCmd(aNumVal, aIds, aTexts, aRes);
nsresult rv = mModule->GetPlayerAppValueTextRspCmd(aNumVal, aIds,
aTexts, aRes);
if (NS_FAILED(rv)) {
DispatchError(aRes, rv);
}
}
void
@ -1013,7 +1039,10 @@ BluetoothDaemonAvrcpInterface::GetElementAttrRsp(
{
MOZ_ASSERT(mModule);
mModule->GetElementAttrRspCmd(aNumAttr, aAttr, aRes);
nsresult rv = mModule->GetElementAttrRspCmd(aNumAttr, aAttr, aRes);
if (NS_FAILED(rv)) {
DispatchError(aRes, rv);
}
}
void
@ -1022,7 +1051,10 @@ BluetoothDaemonAvrcpInterface::SetPlayerAppValueRsp(
{
MOZ_ASSERT(mModule);
mModule->SetPlayerAppValueRspCmd(aRspStatus, aRes);
nsresult rv = mModule->SetPlayerAppValueRspCmd(aRspStatus, aRes);
if (NS_FAILED(rv)) {
DispatchError(aRes, rv);
}
}
void
@ -1034,7 +1066,11 @@ BluetoothDaemonAvrcpInterface::RegisterNotificationRsp(
{
MOZ_ASSERT(mModule);
mModule->RegisterNotificationRspCmd(aEvent, aType, aParam, aRes);
nsresult rv = mModule->RegisterNotificationRspCmd(aEvent, aType,
aParam, aRes);
if (NS_FAILED(rv)) {
DispatchError(aRes, rv);
}
}
void
@ -1043,7 +1079,10 @@ BluetoothDaemonAvrcpInterface::SetVolume(
{
MOZ_ASSERT(mModule);
mModule->SetVolumeCmd(aVolume, aRes);
nsresult rv = mModule->SetVolumeCmd(aVolume, aRes);
if (NS_FAILED(rv)) {
DispatchError(aRes, rv);
}
}
void
@ -1056,4 +1095,16 @@ BluetoothDaemonAvrcpInterface::DispatchError(
ConstantInitOp1<BluetoothStatus>(aStatus));
}
void
BluetoothDaemonAvrcpInterface::DispatchError(
BluetoothAvrcpResultHandler* aRes, nsresult aRv)
{
BluetoothStatus status;
if (NS_WARN_IF(NS_FAILED(Convert(aRv, status)))) {
status = STATUS_FAIL;
}
DispatchError(aRes, status);
}
END_BLUETOOTH_NAMESPACE

View File

@ -346,6 +346,7 @@ public:
private:
void DispatchError(BluetoothAvrcpResultHandler* aRes,
BluetoothStatus aStatus);
void DispatchError(BluetoothAvrcpResultHandler* aRes, nsresult aRv);
BluetoothDaemonAvrcpModule* mModule;
};

View File

@ -1512,7 +1512,7 @@ BluetoothDaemonHandsfreeInterface::Init(
aMaxNumClients, res);
if (NS_FAILED(rv) && aRes) {
DispatchError(aRes, STATUS_FAIL);
DispatchError(aRes, rv);
}
}
@ -1532,6 +1532,7 @@ public:
{
MOZ_ASSERT(NS_IsMainThread());
BT_LOGR("%s:%d", __func__, __LINE__);
if (mRes) {
mRes->OnError(aStatus);
}
@ -1541,6 +1542,7 @@ public:
{
MOZ_ASSERT(NS_IsMainThread());
BT_LOGR("%s:%d", __func__, __LINE__);
// Clear notification handler _after_ module has been
// unregistered. While unregistering the module, we might
// still receive notifications.
@ -1560,8 +1562,14 @@ void
BluetoothDaemonHandsfreeInterface::Cleanup(
BluetoothHandsfreeResultHandler* aRes)
{
mModule->UnregisterModule(BluetoothDaemonHandsfreeModule::SERVICE_ID,
new CleanupResultHandler(mModule, aRes));
BT_LOGR("%s:%d", __func__, __LINE__);
nsresult rv = mModule->UnregisterModule(
BluetoothDaemonHandsfreeModule::SERVICE_ID,
new CleanupResultHandler(mModule, aRes));
BT_LOGR("%s:%d", __func__, __LINE__);
if (NS_FAILED(rv)) {
DispatchError(aRes, rv);
}
}
/* Connect / Disconnect */
@ -1572,7 +1580,10 @@ BluetoothDaemonHandsfreeInterface::Connect(
{
MOZ_ASSERT(mModule);
mModule->ConnectCmd(aBdAddr, aRes);
nsresult rv = mModule->ConnectCmd(aBdAddr, aRes);
if (NS_FAILED(rv)) {
DispatchError(aRes, rv);
}
}
void
@ -1581,7 +1592,10 @@ BluetoothDaemonHandsfreeInterface::Disconnect(
{
MOZ_ASSERT(mModule);
mModule->DisconnectCmd(aBdAddr, aRes);
nsresult rv = mModule->DisconnectCmd(aBdAddr, aRes);
if (NS_FAILED(rv)) {
DispatchError(aRes, rv);
}
}
void
@ -1590,7 +1604,10 @@ BluetoothDaemonHandsfreeInterface::ConnectAudio(
{
MOZ_ASSERT(mModule);
mModule->ConnectAudioCmd(aBdAddr, aRes);
nsresult rv = mModule->ConnectAudioCmd(aBdAddr, aRes);
if (NS_FAILED(rv)) {
DispatchError(aRes, rv);
}
}
void
@ -1599,7 +1616,10 @@ BluetoothDaemonHandsfreeInterface::DisconnectAudio(
{
MOZ_ASSERT(mModule);
mModule->DisconnectAudioCmd(aBdAddr, aRes);
nsresult rv = mModule->DisconnectAudioCmd(aBdAddr, aRes);
if (NS_FAILED(rv)) {
DispatchError(aRes, rv);
}
}
/* Voice Recognition */
@ -1610,7 +1630,10 @@ BluetoothDaemonHandsfreeInterface::StartVoiceRecognition(
{
MOZ_ASSERT(mModule);
mModule->StartVoiceRecognitionCmd(aBdAddr, aRes);
nsresult rv = mModule->StartVoiceRecognitionCmd(aBdAddr, aRes);
if (NS_FAILED(rv)) {
DispatchError(aRes, rv);
}
}
void
@ -1619,7 +1642,10 @@ BluetoothDaemonHandsfreeInterface::StopVoiceRecognition(
{
MOZ_ASSERT(mModule);
mModule->StopVoiceRecognitionCmd(aBdAddr, aRes);
nsresult rv = mModule->StopVoiceRecognitionCmd(aBdAddr, aRes);
if (NS_FAILED(rv)) {
DispatchError(aRes, rv);
}
}
/* Volume */
@ -1631,7 +1657,10 @@ BluetoothDaemonHandsfreeInterface::VolumeControl(
{
MOZ_ASSERT(mModule);
mModule->VolumeControlCmd(aType, aVolume, aBdAddr, aRes);
nsresult rv = mModule->VolumeControlCmd(aType, aVolume, aBdAddr, aRes);
if (NS_FAILED(rv)) {
DispatchError(aRes, rv);
}
}
/* Device status */
@ -1644,8 +1673,12 @@ BluetoothDaemonHandsfreeInterface::DeviceStatusNotification(
{
MOZ_ASSERT(mModule);
mModule->DeviceStatusNotificationCmd(aNtkState, aSvcType, aSignal,
aBattChg, aRes);
nsresult rv = mModule->DeviceStatusNotificationCmd(aNtkState, aSvcType,
aSignal, aBattChg,
aRes);
if (NS_FAILED(rv)) {
DispatchError(aRes, rv);
}
}
/* Responses */
@ -1657,7 +1690,10 @@ BluetoothDaemonHandsfreeInterface::CopsResponse(
{
MOZ_ASSERT(mModule);
mModule->CopsResponseCmd(aCops, aBdAddr, aRes);
nsresult rv = mModule->CopsResponseCmd(aCops, aBdAddr, aRes);
if (NS_FAILED(rv)) {
DispatchError(aRes, rv);
}
}
void
@ -1669,8 +1705,12 @@ BluetoothDaemonHandsfreeInterface::CindResponse(
{
MOZ_ASSERT(mModule);
mModule->CindResponseCmd(aSvc, aNumActive, aNumHeld, aCallSetupState,
aSignal, aRoam, aBattChg, aBdAddr, aRes);
nsresult rv = mModule->CindResponseCmd(aSvc, aNumActive, aNumHeld,
aCallSetupState, aSignal,
aRoam, aBattChg, aBdAddr, aRes);
if (NS_FAILED(rv)) {
DispatchError(aRes, rv);
}
}
void
@ -1680,7 +1720,10 @@ BluetoothDaemonHandsfreeInterface::FormattedAtResponse(
{
MOZ_ASSERT(mModule);
mModule->FormattedAtResponseCmd(aRsp, aBdAddr, aRes);
nsresult rv = mModule->FormattedAtResponseCmd(aRsp, aBdAddr, aRes);
if (NS_FAILED(rv)) {
DispatchError(aRes, rv);
}
}
void
@ -1690,7 +1733,11 @@ BluetoothDaemonHandsfreeInterface::AtResponse(
{
MOZ_ASSERT(mModule);
mModule->AtResponseCmd(aResponseCode, aErrorCode, aBdAddr, aRes);
nsresult rv = mModule->AtResponseCmd(aResponseCode, aErrorCode,
aBdAddr, aRes);
if (NS_FAILED(rv)) {
DispatchError(aRes, rv);
}
}
void
@ -1706,8 +1753,11 @@ BluetoothDaemonHandsfreeInterface::ClccResponse(
{
MOZ_ASSERT(mModule);
mModule->ClccResponseCmd(aIndex, aDir, aState, aMode, aMpty, aNumber,
aType, aBdAddr, aRes);
nsresult rv = mModule->ClccResponseCmd(aIndex, aDir, aState, aMode, aMpty,
aNumber, aType, aBdAddr, aRes);
if (NS_FAILED(rv)) {
DispatchError(aRes, rv);
}
}
/* Phone State */
@ -1722,8 +1772,12 @@ BluetoothDaemonHandsfreeInterface::PhoneStateChange(
{
MOZ_ASSERT(mModule);
mModule->PhoneStateChangeCmd(aNumActive, aNumHeld, aCallSetupState, aNumber,
aType, aRes);
nsresult rv = mModule->PhoneStateChangeCmd(aNumActive, aNumHeld,
aCallSetupState, aNumber,
aType, aRes);
if (NS_FAILED(rv)) {
DispatchError(aRes, rv);
}
}
/* Wide Band Speech */
@ -1735,7 +1789,10 @@ BluetoothDaemonHandsfreeInterface::ConfigureWbs(
{
MOZ_ASSERT(mModule);
mModule->ConfigureWbsCmd(aBdAddr, aConfig, aRes);
nsresult rv = mModule->ConfigureWbsCmd(aBdAddr, aConfig, aRes);
if (NS_FAILED(rv)) {
DispatchError(aRes, rv);
}
}
void
@ -1748,4 +1805,16 @@ BluetoothDaemonHandsfreeInterface::DispatchError(
ConstantInitOp1<BluetoothStatus>(aStatus));
}
void
BluetoothDaemonHandsfreeInterface::DispatchError(
BluetoothHandsfreeResultHandler* aRes, nsresult aRv)
{
BluetoothStatus status;
if (NS_WARN_IF(NS_FAILED(Convert(aRv, status)))) {
status = STATUS_FAIL;
}
DispatchError(aRes, status);
}
END_BLUETOOTH_NAMESPACE

View File

@ -472,6 +472,7 @@ public:
private:
void DispatchError(BluetoothHandsfreeResultHandler* aRes,
BluetoothStatus aStatus);
void DispatchError(BluetoothHandsfreeResultHandler* aRes, nsresult aRv);
BluetoothDaemonHandsfreeModule* mModule;
};

View File

@ -1893,7 +1893,15 @@ void
BluetoothDaemonInterface::OnDisconnect(enum Channel aChannel)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(!mResultHandlerQ.IsEmpty());
if (mResultHandlerQ.IsEmpty()) {
if (sNotificationHandler) {
// Bluetooth daemon crashed; clear state
sNotificationHandler->AdapterStateChangedNotification(false);
sNotificationHandler = nullptr;
}
return;
}
switch (aChannel) {
case CMD_CHANNEL:
@ -2194,19 +2202,31 @@ BluetoothDaemonInterface::Cleanup(BluetoothResultHandler* aRes)
mResultHandlerQ.AppendElement(aRes);
// Cleanup, step 1: Unregister Socket module
mProtocol->UnregisterModuleCmd(0x02, new CleanupResultHandler(this));
nsresult rv = mProtocol->UnregisterModuleCmd(
0x02, new CleanupResultHandler(this));
if (NS_FAILED(rv)) {
DispatchError(aRes, rv);
}
}
void
BluetoothDaemonInterface::Enable(BluetoothResultHandler* aRes)
{
static_cast<BluetoothDaemonCoreModule*>(mProtocol)->EnableCmd(aRes);
nsresult rv =
static_cast<BluetoothDaemonCoreModule*>(mProtocol)->EnableCmd(aRes);
if (NS_FAILED(rv)) {
DispatchError(aRes, rv);
}
}
void
BluetoothDaemonInterface::Disable(BluetoothResultHandler* aRes)
{
static_cast<BluetoothDaemonCoreModule*>(mProtocol)->DisableCmd(aRes);
nsresult rv =
static_cast<BluetoothDaemonCoreModule*>(mProtocol)->DisableCmd(aRes);
if (NS_FAILED(rv)) {
DispatchError(aRes, rv);
}
}
/* Adapter Properties */
@ -2214,24 +2234,33 @@ BluetoothDaemonInterface::Disable(BluetoothResultHandler* aRes)
void
BluetoothDaemonInterface::GetAdapterProperties(BluetoothResultHandler* aRes)
{
static_cast<BluetoothDaemonCoreModule*>
nsresult rv = static_cast<BluetoothDaemonCoreModule*>
(mProtocol)->GetAdapterPropertiesCmd(aRes);
if (NS_FAILED(rv)) {
DispatchError(aRes, rv);
}
}
void
BluetoothDaemonInterface::GetAdapterProperty(const nsAString& aName,
BluetoothResultHandler* aRes)
{
static_cast<BluetoothDaemonCoreModule*>
nsresult rv = static_cast<BluetoothDaemonCoreModule*>
(mProtocol)->GetAdapterPropertyCmd(aName, aRes);
if (NS_FAILED(rv)) {
DispatchError(aRes, rv);
}
}
void
BluetoothDaemonInterface::SetAdapterProperty(
const BluetoothNamedValue& aProperty, BluetoothResultHandler* aRes)
{
static_cast<BluetoothDaemonCoreModule*>
nsresult rv = static_cast<BluetoothDaemonCoreModule*>
(mProtocol)->SetAdapterPropertyCmd(aProperty, aRes);
if (NS_FAILED(rv)) {
DispatchError(aRes, rv);
}
}
/* Remote Device Properties */
@ -2240,8 +2269,11 @@ void
BluetoothDaemonInterface::GetRemoteDeviceProperties(
const nsAString& aRemoteAddr, BluetoothResultHandler* aRes)
{
static_cast<BluetoothDaemonCoreModule*>
nsresult rv = static_cast<BluetoothDaemonCoreModule*>
(mProtocol)->GetRemoteDevicePropertiesCmd(aRemoteAddr, aRes);
if (NS_FAILED(rv)) {
DispatchError(aRes, rv);
}
}
void
@ -2249,8 +2281,11 @@ BluetoothDaemonInterface::GetRemoteDeviceProperty(
const nsAString& aRemoteAddr, const nsAString& aName,
BluetoothResultHandler* aRes)
{
static_cast<BluetoothDaemonCoreModule*>
nsresult rv = static_cast<BluetoothDaemonCoreModule*>
(mProtocol)->GetRemoteDevicePropertyCmd(aRemoteAddr, aName, aRes);
if (NS_FAILED(rv)) {
DispatchError(aRes, rv);
}
}
void
@ -2258,8 +2293,11 @@ BluetoothDaemonInterface::SetRemoteDeviceProperty(
const nsAString& aRemoteAddr, const BluetoothNamedValue& aProperty,
BluetoothResultHandler* aRes)
{
static_cast<BluetoothDaemonCoreModule*>
nsresult rv = static_cast<BluetoothDaemonCoreModule*>
(mProtocol)->SetRemoteDevicePropertyCmd(aRemoteAddr, aProperty, aRes);
if (NS_FAILED(rv)) {
DispatchError(aRes, rv);
}
}
/* Remote Services */
@ -2269,16 +2307,22 @@ BluetoothDaemonInterface::GetRemoteServiceRecord(const nsAString& aRemoteAddr,
const uint8_t aUuid[16],
BluetoothResultHandler* aRes)
{
static_cast<BluetoothDaemonCoreModule*>(
mProtocol)->GetRemoteServiceRecordCmd(aRemoteAddr, aUuid, aRes);
nsresult rv = static_cast<BluetoothDaemonCoreModule*>
(mProtocol)->GetRemoteServiceRecordCmd(aRemoteAddr, aUuid, aRes);
if (NS_FAILED(rv)) {
DispatchError(aRes, rv);
}
}
void
BluetoothDaemonInterface::GetRemoteServices(const nsAString& aRemoteAddr,
BluetoothResultHandler* aRes)
{
static_cast<BluetoothDaemonCoreModule*>(
mProtocol)->GetRemoteServicesCmd(aRemoteAddr, aRes);
nsresult rv = static_cast<BluetoothDaemonCoreModule*>
(mProtocol)->GetRemoteServicesCmd(aRemoteAddr, aRes);
if (NS_FAILED(rv)) {
DispatchError(aRes, rv);
}
}
/* Discovery */
@ -2286,14 +2330,21 @@ BluetoothDaemonInterface::GetRemoteServices(const nsAString& aRemoteAddr,
void
BluetoothDaemonInterface::StartDiscovery(BluetoothResultHandler* aRes)
{
static_cast<BluetoothDaemonCoreModule*>(mProtocol)->StartDiscoveryCmd(aRes);
nsresult rv = static_cast<BluetoothDaemonCoreModule*>
(mProtocol)->StartDiscoveryCmd(aRes);
if (NS_FAILED(rv)) {
DispatchError(aRes, rv);
}
}
void
BluetoothDaemonInterface::CancelDiscovery(BluetoothResultHandler* aRes)
{
static_cast<BluetoothDaemonCoreModule*>
nsresult rv = static_cast<BluetoothDaemonCoreModule*>
(mProtocol)->CancelDiscoveryCmd(aRes);
if (NS_FAILED(rv)) {
DispatchError(aRes, rv);
}
}
/* Bonds */
@ -2303,24 +2354,33 @@ BluetoothDaemonInterface::CreateBond(const nsAString& aBdAddr,
BluetoothTransport aTransport,
BluetoothResultHandler* aRes)
{
static_cast<BluetoothDaemonCoreModule*>
nsresult rv = static_cast<BluetoothDaemonCoreModule*>
(mProtocol)->CreateBondCmd(aBdAddr, aTransport, aRes);
if (NS_FAILED(rv)) {
DispatchError(aRes, rv);
}
}
void
BluetoothDaemonInterface::RemoveBond(const nsAString& aBdAddr,
BluetoothResultHandler* aRes)
{
static_cast<BluetoothDaemonCoreModule*>
nsresult rv = static_cast<BluetoothDaemonCoreModule*>
(mProtocol)->RemoveBondCmd(aBdAddr, aRes);
if (NS_FAILED(rv)) {
DispatchError(aRes, rv);
}
}
void
BluetoothDaemonInterface::CancelBond(const nsAString& aBdAddr,
BluetoothResultHandler* aRes)
{
static_cast<BluetoothDaemonCoreModule*>
nsresult rv = static_cast<BluetoothDaemonCoreModule*>
(mProtocol)->CancelBondCmd(aBdAddr, aRes);
if (NS_FAILED(rv)) {
DispatchError(aRes, rv);
}
}
/* Connection */
@ -2339,8 +2399,11 @@ BluetoothDaemonInterface::PinReply(const nsAString& aBdAddr, bool aAccept,
const nsAString& aPinCode,
BluetoothResultHandler* aRes)
{
static_cast<BluetoothDaemonCoreModule*>
nsresult rv = static_cast<BluetoothDaemonCoreModule*>
(mProtocol)->PinReplyCmd(aBdAddr, aAccept, aPinCode, aRes);
if (NS_FAILED(rv)) {
DispatchError(aRes, rv);
}
}
void
@ -2349,8 +2412,11 @@ BluetoothDaemonInterface::SspReply(const nsAString& aBdAddr,
bool aAccept, uint32_t aPasskey,
BluetoothResultHandler* aRes)
{
static_cast<BluetoothDaemonCoreModule*>
nsresult rv = static_cast<BluetoothDaemonCoreModule*>
(mProtocol)->SspReplyCmd(aBdAddr, aVariant, aAccept, aPasskey, aRes);
if (NS_FAILED(rv)) {
DispatchError(aRes, rv);
}
}
/* DUT Mode */
@ -2359,8 +2425,11 @@ void
BluetoothDaemonInterface::DutModeConfigure(bool aEnable,
BluetoothResultHandler* aRes)
{
static_cast<BluetoothDaemonCoreModule*>
nsresult rv = static_cast<BluetoothDaemonCoreModule*>
(mProtocol)->DutModeConfigureCmd(aEnable, aRes);
if (NS_FAILED(rv)) {
DispatchError(aRes, rv);
}
}
void
@ -2368,8 +2437,11 @@ BluetoothDaemonInterface::DutModeSend(uint16_t aOpcode, uint8_t* aBuf,
uint8_t aLen,
BluetoothResultHandler* aRes)
{
static_cast<BluetoothDaemonCoreModule*>
nsresult rv = static_cast<BluetoothDaemonCoreModule*>
(mProtocol)->DutModeSendCmd(aOpcode, aBuf, aLen, aRes);
if (NS_FAILED(rv)) {
DispatchError(aRes, rv);
}
}
/* LE Mode */
@ -2379,8 +2451,11 @@ BluetoothDaemonInterface::LeTestMode(uint16_t aOpcode, uint8_t* aBuf,
uint8_t aLen,
BluetoothResultHandler* aRes)
{
static_cast<BluetoothDaemonCoreModule*>
nsresult rv = static_cast<BluetoothDaemonCoreModule*>
(mProtocol)->LeTestModeCmd(aOpcode, aBuf, aLen, aRes);
if (NS_FAILED(rv)) {
DispatchError(aRes, rv);
}
}
/* Energy Information */
@ -2401,6 +2476,18 @@ BluetoothDaemonInterface::DispatchError(BluetoothResultHandler* aRes,
ConstantInitOp1<BluetoothStatus>(aStatus));
}
void
BluetoothDaemonInterface::DispatchError(BluetoothResultHandler* aRes,
nsresult aRv)
{
BluetoothStatus status;
if (NS_WARN_IF(NS_FAILED(Convert(aRv, status)))) {
status = STATUS_FAIL;
}
DispatchError(aRes, status);
}
// Profile Interfaces
//

View File

@ -135,6 +135,7 @@ protected:
private:
void DispatchError(BluetoothResultHandler* aRes, BluetoothStatus aStatus);
void DispatchError(BluetoothResultHandler* aRes, nsresult aRv);
nsCString mListenSocketName;
nsRefPtr<BluetoothDaemonListenSocket> mListenSocket;

View File

@ -315,8 +315,11 @@ BluetoothDaemonSocketInterface::Listen(BluetoothSocketType aType,
{
MOZ_ASSERT(mModule);
mModule->ListenCmd(aType, aServiceName, aServiceUuid, aChannel,
aEncrypt, aAuth, aRes);
nsresult rv = mModule->ListenCmd(aType, aServiceName, aServiceUuid,
aChannel, aEncrypt, aAuth, aRes);
if (NS_FAILED(rv)) {
DispatchError(aRes, rv);
}
}
void
@ -329,7 +332,11 @@ BluetoothDaemonSocketInterface::Connect(const nsAString& aBdAddr,
{
MOZ_ASSERT(mModule);
mModule->ConnectCmd(aBdAddr, aType, aUuid, aChannel, aEncrypt, aAuth, aRes);
nsresult rv = mModule->ConnectCmd(aBdAddr, aType, aUuid, aChannel,
aEncrypt, aAuth, aRes);
if (NS_FAILED(rv)) {
DispatchError(aRes, rv);
}
}
void
@ -338,7 +345,10 @@ BluetoothDaemonSocketInterface::Accept(int aFd,
{
MOZ_ASSERT(mModule);
mModule->AcceptCmd(aFd, aRes);
nsresult rv = mModule->AcceptCmd(aFd, aRes);
if (NS_FAILED(rv)) {
DispatchError(aRes, rv);
}
}
void
@ -346,7 +356,32 @@ BluetoothDaemonSocketInterface::Close(BluetoothSocketResultHandler* aRes)
{
MOZ_ASSERT(mModule);
mModule->CloseCmd(aRes);
nsresult rv = mModule->CloseCmd(aRes);
if (NS_FAILED(rv)) {
DispatchError(aRes, rv);
}
}
void
BluetoothDaemonSocketInterface::DispatchError(
BluetoothSocketResultHandler* aRes, BluetoothStatus aStatus)
{
BluetoothResultRunnable1<BluetoothSocketResultHandler, void,
BluetoothStatus, BluetoothStatus>::Dispatch(
aRes, &BluetoothSocketResultHandler::OnError,
ConstantInitOp1<BluetoothStatus>(aStatus));
}
void
BluetoothDaemonSocketInterface::DispatchError(
BluetoothSocketResultHandler* aRes, nsresult aRv)
{
BluetoothStatus status;
if (NS_WARN_IF(NS_FAILED(Convert(aRv, status)))) {
status = STATUS_FAIL;
}
DispatchError(aRes, status);
}
END_BLUETOOTH_NAMESPACE

View File

@ -113,6 +113,10 @@ public:
void Close(BluetoothSocketResultHandler* aRes);
private:
void DispatchError(BluetoothSocketResultHandler* aRes,
BluetoothStatus aStatus);
void DispatchError(BluetoothSocketResultHandler* aRes, nsresult aRv);
BluetoothDaemonSocketModule* mModule;
};

View File

@ -310,7 +310,8 @@ public:
BT_LOGR("BluetoothInterface::Disable failed: %d", aStatus);
BluetoothService::AcknowledgeToggleBt(true);
// Always make progress; even on failures
BluetoothService::AcknowledgeToggleBt(false);
}
};

View File

@ -432,6 +432,9 @@ public:
void OnError(BluetoothStatus aStatus) MOZ_OVERRIDE
{
BT_WARNING("BluetoothHandsfreeInterface::Cleanup failed: %d", (int)aStatus);
sBluetoothHfpInterface = nullptr;
if (mRes) {
mRes->OnError(NS_ERROR_FAILURE);
}

View File

@ -10,6 +10,7 @@ if CONFIG['MOZ_B2G_BT']:
'BluetoothDevice.cpp',
'BluetoothHidManager.cpp',
'BluetoothInterface.cpp',
'BluetoothInterfaceHelpers.cpp',
'BluetoothManager.cpp',
'BluetoothProfileController.cpp',
'BluetoothPropertyContainer.cpp',

View File

@ -42,6 +42,8 @@ static const char *sEGLExtensionNames[] = {
static PRLibrary* LoadApitraceLibrary()
{
// Initialization of gfx prefs here is only needed during the unit tests...
gfxPrefs::GetSingleton();
if (!gfxPrefs::UseApitrace()) {
return nullptr;
}

View File

@ -391,7 +391,7 @@ pref("devtools.errorconsole.enabled", false);
// to communicate with a usb cable via adb forward.
pref("devtools.debugger.unix-domain-socket", "/data/data/@ANDROID_PACKAGE_NAME@/firefox-debugger-socket");
pref("font.size.inflation.minTwips", 120);
pref("font.size.inflation.minTwips", 0);
// When true, zooming will be enabled on all sites, even ones that declare user-scalable=no.
pref("browser.ui.zoom.force-user-scalable", false);

View File

@ -45,17 +45,19 @@ public class ReferrerReceiver extends BroadcastReceiver {
return;
}
// Track the referrer object for distribution handling.
ReferrerDescriptor referrer = new ReferrerDescriptor(intent.getStringExtra("referrer"));
// Track the referrer object for distribution handling.
if (!TextUtils.equals(referrer.source, MOZILLA_UTM_SOURCE)) {
return;
}
if (TextUtils.equals(referrer.campaign, DISTRIBUTION_UTM_CAMPAIGN)) {
Distribution.onReceivedReferrer(context, referrer);
} else {
Log.d(LOGTAG, "Not downloading distribution: non-matching campaign.");
}
// If this is a Mozilla campaign, pass the campaign along to Gecko.
if (TextUtils.equals(referrer.source, MOZILLA_UTM_SOURCE)) {
// If this is a Mozilla campaign, pass the campaign along to Gecko.
// It'll pretend to be a "playstore" distribution for BLP purposes.
propagateMozillaCampaign(referrer);
}

View File

@ -20,7 +20,7 @@ public class ActivityUtils {
// Hide/show the system notification bar
Window window = activity.getWindow();
if (Versions.feature11Plus) {
if (Versions.feature16Plus) {
final int newVis;
if (fullscreen) {
newVis = View.SYSTEM_UI_FLAG_FULLSCREEN |
@ -40,12 +40,12 @@ public class ActivityUtils {
public static boolean isFullScreen(final Activity activity) {
final Window window = activity.getWindow();
if (Versions.feature11Plus) {
if (Versions.feature16Plus) {
final int vis = window.getDecorView().getSystemUiVisibility();
return (vis & View.SYSTEM_UI_FLAG_FULLSCREEN) != 0;
} else {
final int flags = window.getAttributes().flags;
return ((flags & WindowManager.LayoutParams.FLAG_FULLSCREEN) != 0);
}
final int flags = window.getAttributes().flags;
return ((flags & WindowManager.LayoutParams.FLAG_FULLSCREEN) != 0);
}
}

View File

@ -626,6 +626,17 @@ var BrowserApp = {
});
});
NativeWindow.contextmenus.add(stringGetter("contextmenu.addToReadingList"),
NativeWindow.contextmenus.linkOpenableContext,
function(aTarget) {
let url = NativeWindow.contextmenus._getLinkURL(aTarget);
Messaging.sendRequestForResult({
type: "Reader:AddToList",
title: truncate(url, MAX_TITLE_LENGTH),
url: truncate(url, MAX_URI_LENGTH),
}).catch(Cu.reportError);
});
NativeWindow.contextmenus.add(stringGetter("contextmenu.copyLink"),
NativeWindow.contextmenus.linkCopyableContext,
function(aTarget) {

View File

@ -1,24 +1,24 @@
/* 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 {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, 'Services',
'resource://gre/modules/Services.jsm');
XPCOMUtils.defineLazyModuleGetter(this, 'Preferences',
'resource://gre/modules/Preferences.jsm');
XPCOMUtils.defineLazyModuleGetter(this, 'FileUtils',
'resource://gre/modules/FileUtils.jsm');
XPCOMUtils.defineLazyModuleGetter(this, 'Log',
'resource://gre/modules/Log.jsm');
XPCOMUtils.defineLazyModuleGetter(this, 'Task',
'resource://gre/modules/Task.jsm');
XPCOMUtils.defineLazyModuleGetter(this, 'OS',
'resource://gre/modules/osfile.jsm');
XPCOMUtils.defineLazyModuleGetter(this, 'CommonUtils',
'resource://services-common/utils.js');
XPCOMUtils.defineLazyModuleGetter(this, "Services",
"resource://gre/modules/Services.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "FileUtils",
"resource://gre/modules/FileUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Log",
"resource://gre/modules/Log.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "OS",
"resource://gre/modules/osfile.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "CommonUtils",
"resource://services-common/utils.js");
Cu.import("resource://gre/modules/Preferences.jsm");
Cu.import("resource://gre/modules/Task.jsm");
this.EXPORTED_SYMBOLS = [
"LogManager",
@ -28,7 +28,7 @@ const DEFAULT_MAX_ERROR_AGE = 20 * 24 * 60 * 60; // 20 days
// "shared" logs (ie, where the same log name is used by multiple LogManager
// instances) are a fact of life here - eg, FirefoxAccounts logs are used by
// both Sync and Reading-list.
// both Sync and Reading List.
// However, different instances have different pref branches, so we need to
// handle when one pref branch says "Debug" and the other says "Error"
// So we (a) keep singleton console and dump appenders and (b) keep track
@ -103,7 +103,7 @@ LogManager.prototype = {
// The file appender doesn't get the special singleton behaviour.
let fapp = this._fileAppender = new Log.StorageStreamAppender(formatter);
// the stream gets a default of Debug as the user must go out of there way
// the stream gets a default of Debug as the user must go out of their way
// to see the stuff spewed to it.
this._observeStreamPref = setupAppender(fapp, "log.appender.file.level", Log.Level.Debug);
@ -151,7 +151,7 @@ LogManager.prototype = {
const BUFFER_SIZE = 8192;
// get a binary stream
let binaryStream = Cc['@mozilla.org/binaryinputstream;1'].createInstance(Ci.nsIBinaryInputStream);
let binaryStream = Cc["@mozilla.org/binaryinputstream;1"].createInstance(Ci.nsIBinaryInputStream);
binaryStream.setInputStream(inputStream);
yield OS.File.makeDir(outputFile.parent.path, { ignoreExisting: true });
let output = yield OS.File.open(outputFile.path, { write: true} );
@ -165,12 +165,14 @@ LogManager.prototype = {
yield output.write(new Uint8Array(chunk));
}
} finally {
inputStream.close();
binaryStream.close();
yield output.close();
try {
binaryStream.close(); // inputStream is closed by the binaryStream
yield output.close();
} catch (ex) {
this._log.error("Failed to close the input stream", ex);
}
}
this._log.trace("finished copy to", outputFile.path);
return (yield OS.File.stat(outputFile.path)).lastModificationDate;
}),
/**
@ -179,70 +181,65 @@ LogManager.prototype = {
* Returns a promise that resolves on completion or rejects if the file could
* not be written.
*/
resetFileLog(reason) {
return new Promise((resolve, reject) => {
try {
let flushToFile;
let reasonPrefix;
switch (reason) {
case this.REASON_SUCCESS:
flushToFile = this._prefs.get("log.appender.file.logOnSuccess");
reasonPrefix = "success";
break;
case this.REASON_ERROR:
flushToFile = this._prefs.get("log.appender.file.logOnError");
reasonPrefix = "error";
break;
default:
return reject(new Error("Invalid reason"));
}
let inStream = this._fileAppender.getInputStream();
this._fileAppender.reset();
if (flushToFile && inStream) {
this._log.debug("Flushing file log");
let filename = this.logFilePrefix + "-" + reasonPrefix + "-" + Date.now() + ".txt";
let file = this._logFileDirectory;
file.append(filename);
this._log.trace("Beginning stream copy to " + file.leafName + ": " +
Date.now());
this._copyStreamToFile(inStream, file).then(
modDate => {
this._log.trace("onCopyComplete: " + Date.now());
this._log.trace("Output file timestamp: " + modDate + " (" + modDate.getTime() + ")");
},
err => {
this._log.error("Failed to copy log stream to file", err)
reject(err)
}
).then(
() => {
// It's not completely clear to markh why we only do log cleanups
// for errors, but for now the Sync semantics have been copied...
// (one theory is that only cleaning up on error makes it less
// likely old error logs would be removed, but that's not true if
// there are occasional errors - let's address this later!)
if (reason == this.REASON_ERROR &&
!this._cleaningUpFileLogs) {
this._log.trace("Scheduling cleanup.");
// Note we don't return or wait on this promise - it continues
// in the background
this.cleanupLogs().then(null, err => {
this._log.error("Failed to cleanup logs", err);
});
}
resolve();
}
);
} else {
resolve();
}
} catch (ex) {
this._log.error("Failed to resetFileLog", ex)
reject(ex);
resetFileLog: Task.async(function* (reason) {
try {
let flushToFile;
let reasonPrefix;
switch (reason) {
case this.REASON_SUCCESS:
flushToFile = this._prefs.get("log.appender.file.logOnSuccess", false);
reasonPrefix = "success";
break;
case this.REASON_ERROR:
flushToFile = this._prefs.get("log.appender.file.logOnError", true);
reasonPrefix = "error";
break;
default:
throw new Error("Invalid reason");
}
})
},
// might as well avoid creating an input stream if we aren't going to use it.
if (!flushToFile) {
this._fileAppender.reset();
return;
}
let inStream = this._fileAppender.getInputStream();
this._fileAppender.reset();
if (inStream) {
this._log.debug("Flushing file log");
// We have reasonPrefix at the start of the filename so all "error"
// logs are grouped in about:sync-log.
let filename = reasonPrefix + "-" + this.logFilePrefix + "-" + Date.now() + ".txt";
let file = this._logFileDirectory;
file.append(filename);
this._log.trace("Beginning stream copy to " + file.leafName + ": " +
Date.now());
try {
yield this._copyStreamToFile(inStream, file);
this._log.trace("onCopyComplete", Date.now());
} catch (ex) {
this._log.error("Failed to copy log stream to file", ex);
return;
}
// It's not completely clear to markh why we only do log cleanups
// for errors, but for now the Sync semantics have been copied...
// (one theory is that only cleaning up on error makes it less
// likely old error logs would be removed, but that's not true if
// there are occasional errors - let's address this later!)
if (reason == this.REASON_ERROR && !this._cleaningUpFileLogs) {
this._log.trace("Scheduling cleanup.");
// Note we don't return/yield or otherwise wait on this promise - it
// continues in the background
this.cleanupLogs().catch(err => {
this._log.error("Failed to cleanup logs", err);
});
}
}
} catch (ex) {
this._log.error("Failed to resetFileLog", ex)
}
}),
/**
* Finds all logs older than maxErrorAge and deletes them using async I/O.
@ -255,7 +252,8 @@ LogManager.prototype = {
this._log.debug("Log cleanup threshold time: " + threshold);
yield iterator.forEach(Task.async(function* (entry) {
if (!entry.name.startsWith(this.logFilePrefix + "-")) {
if (!entry.name.startsWith("error-" + this.logFilePrefix + "-") &&
!entry.name.startsWith("success-" + this.logFilePrefix + "-")) {
return;
}
try {

View File

@ -23,7 +23,7 @@ function getAppenders(log) {
}
// Test that the correct thing happens when no prefs exist for the log manager.
add_test(function test_noPrefs() {
add_task(function* test_noPrefs() {
// tell the log manager to init with a pref branch that doesn't exist.
let lm = new LogManager("no-such-branch.", ["TestLog"], "test");
@ -36,11 +36,10 @@ add_test(function test_noPrefs() {
equal(fapps.length, 1, "only 1 file appender");
equal(fapps[0].level, Log.Level.Debug);
lm.finalize();
run_next_test();
});
// Test that changes to the prefs used by the log manager are updated dynamically.
add_test(function test_PrefChanges() {
add_task(function* test_PrefChanges() {
Services.prefs.setCharPref("log-manager.test.log.appender.console", "Trace");
Services.prefs.setCharPref("log-manager.test.log.appender.dump", "Trace");
Services.prefs.setCharPref("log-manager.test.log.appender.file.level", "Trace");
@ -66,11 +65,10 @@ add_test(function test_PrefChanges() {
equal(dapp.level, Log.Level.Error);
equal(fapp.level, Log.Level.Debug);
lm.finalize();
run_next_test();
});
// Test that the same log used by multiple log managers does the right thing.
add_test(function test_SharedLogs() {
add_task(function* test_SharedLogs() {
// create the prefs for the first instance.
Services.prefs.setCharPref("log-manager-1.test.log.appender.console", "Trace");
Services.prefs.setCharPref("log-manager-1.test.log.appender.dump", "Trace");
@ -102,6 +100,4 @@ add_test(function test_SharedLogs() {
lm1.finalize();
lm2.finalize();
run_next_test();
});

View File

@ -617,7 +617,7 @@ this.BrowserIDManager.prototype = {
// that there is no authentication dance still under way.
this._shouldHaveSyncKeyBundle = true;
Weave.Status.login = this._authFailureReason;
Services.obs.notifyObservers(null, "weave:service:login:error", null);
Services.obs.notifyObservers(null, "weave:ui:login:error", null);
throw err;
});
},

View File

@ -1770,7 +1770,7 @@ add_task(function test_sync_engine_generic_fail() {
let entries = logsdir.directoryEntries;
do_check_true(entries.hasMoreElements());
let logfile = entries.getNext().QueryInterface(Ci.nsILocalFile);
do_check_true(logfile.leafName.startsWith("sync-error-"), logfile.leafName);
do_check_true(logfile.leafName.startsWith("error-sync-"), logfile.leafName);
clean();
server.stop(deferred.resolve);
@ -1801,7 +1801,7 @@ add_test(function test_logs_on_sync_error_despite_shouldReportError() {
let entries = logsdir.directoryEntries;
do_check_true(entries.hasMoreElements());
let logfile = entries.getNext().QueryInterface(Ci.nsILocalFile);
do_check_true(logfile.leafName.startsWith("sync-error-"), logfile.leafName);
do_check_true(logfile.leafName.startsWith("error-sync-"), logfile.leafName);
clean();
run_next_test();
@ -1828,7 +1828,7 @@ add_test(function test_logs_on_login_error_despite_shouldReportError() {
let entries = logsdir.directoryEntries;
do_check_true(entries.hasMoreElements());
let logfile = entries.getNext().QueryInterface(Ci.nsILocalFile);
do_check_true(logfile.leafName.startsWith("sync-error-"), logfile.leafName);
do_check_true(logfile.leafName.startsWith("error-sync-"), logfile.leafName);
clean();
run_next_test();
@ -1862,7 +1862,7 @@ add_task(function test_engine_applyFailed() {
let entries = logsdir.directoryEntries;
do_check_true(entries.hasMoreElements());
let logfile = entries.getNext().QueryInterface(Ci.nsILocalFile);
do_check_true(logfile.leafName.startsWith("sync-error-"), logfile.leafName);
do_check_true(logfile.leafName.startsWith("error-sync-"), logfile.leafName);
clean();
server.stop(deferred.resolve);

View File

@ -108,7 +108,7 @@ add_test(function test_logOnSuccess_true() {
do_check_true(entries.hasMoreElements());
let logfile = entries.getNext().QueryInterface(Ci.nsILocalFile);
do_check_eq(logfile.leafName.slice(-4), ".txt");
do_check_true(logfile.leafName.startsWith("sync-success-"), logfile.leafName);
do_check_true(logfile.leafName.startsWith("success-sync-"), logfile.leafName);
do_check_false(entries.hasMoreElements());
// Ensure the log message was actually written to file.
@ -175,7 +175,7 @@ add_test(function test_sync_error_logOnError_true() {
do_check_true(entries.hasMoreElements());
let logfile = entries.getNext().QueryInterface(Ci.nsILocalFile);
do_check_eq(logfile.leafName.slice(-4), ".txt");
do_check_true(logfile.leafName.startsWith("sync-error-"), logfile.leafName);
do_check_true(logfile.leafName.startsWith("error-sync-"), logfile.leafName);
do_check_false(entries.hasMoreElements());
// Ensure the log message was actually written to file.
@ -242,7 +242,7 @@ add_test(function test_login_error_logOnError_true() {
do_check_true(entries.hasMoreElements());
let logfile = entries.getNext().QueryInterface(Ci.nsILocalFile);
do_check_eq(logfile.leafName.slice(-4), ".txt");
do_check_true(logfile.leafName.startsWith("sync-error-"), logfile.leafName);
do_check_true(logfile.leafName.startsWith("error-sync-"), logfile.leafName);
do_check_false(entries.hasMoreElements());
// Ensure the log message was actually written to file.
@ -281,7 +281,7 @@ add_test(function test_logErrorCleanup_age() {
_("Making some files.");
for (let i = 0; i < numLogs; i++) {
let now = Date.now();
let filename = "sync-error-" + now + "" + i + ".txt";
let filename = "error-sync-" + now + "" + i + ".txt";
let newLog = FileUtils.getFile("ProfD", ["weave", "logs", filename]);
let foStream = FileUtils.openFileOutputStream(newLog);
foStream.write(errString, errString.length);

View File

@ -0,0 +1,35 @@
---
# For complete sample of all build and test jobs,
# see <gecko>/testing/taskcluster/tasks/job_flags.yml
$inherits:
from: tasks/branches/base_job_flags.yml
builds:
flame-kk:
platforms:
- b2g
types:
opt:
task: tasks/builds/b2g_flame_kk_opt.yml
flame-kk-eng:
platforms:
- b2g
types:
opt:
task: tasks/builds/b2g_flame_kk_eng.yml
tests:
gaia-build:
allowed_build_tasks:
tasks/builds/b2g_flame_kk_opt.yml:
task: tasks/tests/b2g_build_test.yml
tasks/builds/b2g_flame_kk_eng.yml:
task: tasks/tests/b2g_build_test.yml
gaia-linter:
allowed_build_tasks:
tasks/builds/b2g_flame_kk_opt.yml:
task: tasks/tests/b2g_linter.yml
tasks/builds/b2g_flame_kk_eng.yml:
task: tasks/tests/b2g_linter.yml

View File

@ -0,0 +1,35 @@
---
# For complete sample of all build and test jobs,
# see <gecko>/testing/taskcluster/tasks/job_flags.yml
$inherits:
from: tasks/branches/base_job_flags.yml
builds:
flame-kk:
platforms:
- b2g
types:
opt:
task: tasks/builds/b2g_flame_kk_opt.yml
flame-kk-eng:
platforms:
- b2g
types:
opt:
task: tasks/builds/b2g_flame_kk_eng.yml
tests:
gaia-build:
allowed_build_tasks:
tasks/builds/b2g_flame_kk_opt.yml:
task: tasks/tests/b2g_build_test.yml
tasks/builds/b2g_flame_kk_eng.yml:
task: tasks/tests/b2g_build_test.yml
gaia-linter:
allowed_build_tasks:
tasks/builds/b2g_flame_kk_opt.yml:
task: tasks/tests/b2g_linter.yml
tasks/builds/b2g_flame_kk_eng.yml:
task: tasks/tests/b2g_linter.yml

View File

@ -104,11 +104,11 @@ let StyleSheetsActor = exports.StyleSheetsActor = protocol.ActorClass({
let actors = [];
for (let doc of documents) {
let sheets = yield this._addStyleSheets(doc.styleSheets);
let sheets = yield this._addStyleSheets(doc);
actors = actors.concat(sheets);
// Recursively handle style sheets of the documents in iframes.
for (let iframe of doc.getElementsByTagName("iframe")) {
for (let iframe of doc.querySelectorAll("iframe, browser, frame")) {
if (iframe.contentDocument) {
// Sometimes, iframes don't have any document, like the
// one that are over deeply nested (bug 285395)
@ -121,25 +121,54 @@ let StyleSheetsActor = exports.StyleSheetsActor = protocol.ActorClass({
},
/**
* Add all the stylesheets to the map and create an actor for each one
* if not already created.
* Check if we should be showing this stylesheet.
*
* @param {[DOMStyleSheet]} styleSheets
* Stylesheets to add
* @param {Document} doc
* Document for which we're checking
* @param {DOMCSSStyleSheet} sheet
* Stylesheet we're interested in
*
* @return boolean
* Whether the stylesheet should be listed.
*/
_shouldListSheet: function(doc, sheet) {
// Special case about:PreferenceStyleSheet, as it is generated on the
// fly and the URI is not registered with the about: handler.
// https://bugzilla.mozilla.org/show_bug.cgi?id=935803#c37
if (sheet.href && sheet.href.toLowerCase() == "about:preferencestylesheet") {
return false;
}
return true;
},
/**
* Add all the stylesheets for this document to the map and create an actor
* for each one if not already created.
*
* @param {Document} doc
* Document for which to add stylesheets
*
* @return {Promise}
* Promise that resolves to an array of StyleSheetActors
*/
_addStyleSheets: function(styleSheets)
_addStyleSheets: function(doc)
{
return Task.spawn(function*() {
let isChrome = Services.scriptSecurityManager.isSystemPrincipal(doc.nodePrincipal);
let styleSheets = isChrome ? DOMUtils.getAllStyleSheets(doc) : doc.styleSheets;
let actors = [];
for (let i = 0; i < styleSheets.length; i++) {
let actor = this.parentActor.createStyleSheetActor(styleSheets[i]);
let sheet = styleSheets[i];
if (!this._shouldListSheet(doc, sheet)) {
continue;
}
let actor = this.parentActor.createStyleSheetActor(sheet);
actors.push(actor);
// Get all sheets, including imported ones
let imports = yield this._getImported(actor);
let imports = yield this._getImported(doc, actor);
actors = actors.concat(imports);
}
return actors;
@ -149,12 +178,14 @@ let StyleSheetsActor = exports.StyleSheetsActor = protocol.ActorClass({
/**
* Get all the stylesheets @imported from a stylesheet.
*
* @param {Document} doc
* The document including the stylesheet
* @param {DOMStyleSheet} styleSheet
* Style sheet to search
* @return {Promise}
* A promise that resolves with an array of StyleSheetActors
*/
_getImported: function(styleSheet) {
_getImported: function(doc, styleSheet) {
return Task.spawn(function*() {
let rules = yield styleSheet.getCSSRules();
let imported = [];
@ -164,14 +195,14 @@ let StyleSheetsActor = exports.StyleSheetsActor = protocol.ActorClass({
if (rule.type == Ci.nsIDOMCSSRule.IMPORT_RULE) {
// Associated styleSheet may be null if it has already been seen due
// to duplicate @imports for the same URL.
if (!rule.styleSheet) {
if (!rule.styleSheet || !this._shouldListSheet(doc, rule.styleSheet)) {
continue;
}
let actor = this.parentActor.createStyleSheetActor(rule.styleSheet);
imported.push(actor);
// recurse imports in this stylesheet as well
let children = yield this._getImported(actor);
let children = yield this._getImported(doc, actor);
imported = imported.concat(children);
}
else if (rule.type != Ci.nsIDOMCSSRule.CHARSET_RULE) {

View File

@ -154,7 +154,11 @@ WebConsoleClient.prototype = {
};
this._client.request(packet, response => {
this.pendingEvaluationResults.set(response.resultID, aOnResponse);
// Null check this in case the client has been detached while waiting
// for a response.
if (this.pendingEvaluationResults) {
this.pendingEvaluationResults.set(response.resultID, aOnResponse);
}
});
},

View File

@ -82,3 +82,17 @@ xul|*.inline-link:-moz-focusring {
color: #ff9500;
text-decoration: underline;
}
xul|button:-moz-focusring,
xul|menulist:-moz-focusring,
xul|checkbox:-moz-focusring > .checkbox-check,
xul|radio[focused="true"] > .radio-check,
xul|tab:-moz-focusring > .tab-middle > .tab-text {
outline: 2px solid rgba(0,149,221,0.5);
outline-offset: 1px;
-moz-outline-radius: 2px;
}
xul|radio[focused="true"] > .radio-check {
-moz-outline-radius: 100%;
}

View File

@ -94,9 +94,7 @@ XXX: apply styles to all themes until bug 509642 is fixed
-moz-appearance: none;
border-radius: 3px;
padding: 0;
%ifndef WINDOWS_AERO
background-color: rgba(250,250,250,.3);
%endif
background-color: rgba(250,250,250,.3)
}
.popup-notification-menubutton:hover:active {
@ -109,17 +107,25 @@ XXX: apply styles to all themes until bug 509642 is fixed
-moz-appearance: none;
margin: 0;
border: 1px solid rgba(0,0,0,.35);
%ifdef WINDOWS_AERO
background-image: linear-gradient(rgba(250,250,250,.6), rgba(175,175,175,.25) 49%, rgba(0,0,0,.12) 51%, rgba(0,0,0,.09) 60%, rgba(0,0,0,.05));
box-shadow: 0 0 1px 1px rgba(255,255,255,.75) inset;
%else
box-shadow: 0 1px 0 rgba(255,255,255,.5) inset,
0 2px 2px rgba(255,255,255,.35) inset,
0 -1px 2px rgba(0,0,0,.1) inset,
0 1px 0 rgba(255,255,255,.35);
%endif
}
%ifdef WINDOWS_AERO
@media (-moz-windows-glass) {
.popup-notification-menubutton:not([type="menu-button"]),
.popup-notification-menubutton > .button-menubutton-button,
.popup-notification-menubutton > .button-menubutton-dropmarker {
background-color: transparent;
background-image: linear-gradient(rgba(250,250,250,.6), rgba(175,175,175,.25) 49%, rgba(0,0,0,.12) 51%, rgba(0,0,0,.09) 60%, rgba(0,0,0,.05));
box-shadow: 0 0 1px 1px rgba(255,255,255,.75) inset;
}
}
%endif
.popup-notification-menubutton > .button-menubutton-button {
background-color: transparent;
padding: 1px;
@ -145,40 +151,51 @@ XXX: apply styles to all themes until bug 509642 is fixed
}
%ifdef WINDOWS_AERO
.popup-notification-menubutton > .button-menubutton-button:-moz-locale-dir(ltr),
.popup-notification-menubutton > .button-menubutton-dropmarker:-moz-locale-dir(rtl) {
border-radius: 2px 0 0 2px;
}
@media (-moz-windows-glass) {
.popup-notification-menubutton > .button-menubutton-button:-moz-locale-dir(ltr),
.popup-notification-menubutton > .button-menubutton-dropmarker:-moz-locale-dir(rtl) {
border-radius: 2px 0 0 2px;
}
.popup-notification-menubutton > .button-menubutton-button:-moz-locale-dir(rtl),
.popup-notification-menubutton > .button-menubutton-dropmarker:-moz-locale-dir(ltr) {
border-radius: 0 2px 2px 0;
.popup-notification-menubutton > .button-menubutton-button:-moz-locale-dir(rtl),
.popup-notification-menubutton > .button-menubutton-dropmarker:-moz-locale-dir(ltr) {
border-radius: 0 2px 2px 0;
}
}
%endif
.popup-notification-menubutton:not([type="menu-button"]):hover,
.popup-notification-menubutton > .button-menubutton-button:hover,
.popup-notification-menubutton > .button-menubutton-dropmarker:hover {
%ifdef WINDOWS_AERO
background-image: linear-gradient(rgba(250,250,250,.9), rgba(200,200,200,.6) 49%, rgba(0,0,0,.23) 51%, rgba(0,0,0,.17) 60%, rgba(0,0,0,.05));
box-shadow: 0 0 0 1px white inset,
0 0 2px 1px rgba(255,255,255,.75) inset;
%else
background-color: rgba(250,250,250,.6);
%endif
}
.popup-notification-menubutton:not([type="menu-button"]):hover:active,
.popup-notification-menubutton > .button-menubutton-button:hover:active,
.popup-notification-menubutton > .button-menubutton-dropmarker:hover:active,
.popup-notification-menubutton[open="true"] > .button-menubutton-dropmarker {
%ifdef WINDOWS_AERO
background-image: linear-gradient(rgba(250,250,250,.9), rgba(200,200,200,.6) 49%, rgba(0,0,0,.23) 51%, rgba(0,0,0,.17) 60%, rgba(0,0,0,.05));
%else
background-color: rgba(0,0,0,.05);
%endif
box-shadow: 0 0 2px rgba(0,0,0,.65) inset;
}
%ifdef WINDOWS_AERO
@media (-moz-windows-glass) {
.popup-notification-menubutton:not([type="menu-button"]):hover,
.popup-notification-menubutton > .button-menubutton-button:hover,
.popup-notification-menubutton > .button-menubutton-dropmarker:hover {
background-color: transparent;
background-image: linear-gradient(rgba(250,250,250,.9), rgba(200,200,200,.6) 49%, rgba(0,0,0,.23) 51%, rgba(0,0,0,.17) 60%, rgba(0,0,0,.05));
box-shadow: 0 0 0 1px white inset,
0 0 2px 1px rgba(255,255,255,.75) inset;
}
.popup-notification-menubutton:not([type="menu-button"]):hover:active,
.popup-notification-menubutton > .button-menubutton-button:hover:active,
.popup-notification-menubutton > .button-menubutton-dropmarker:hover:active,
.popup-notification-menubutton[open="true"] > .button-menubutton-dropmarker {
background-color: transparent;
background-image: linear-gradient(rgba(250,250,250,.9), rgba(200,200,200,.6) 49%, rgba(0,0,0,.23) 51%, rgba(0,0,0,.17) 60%, rgba(0,0,0,.05));
}
}
%endif
/*}*/
%endif

View File

@ -50,7 +50,7 @@ using namespace mozilla::gfx;
using namespace mozilla::jni;
using namespace mozilla::widget;
AndroidBridge* AndroidBridge::sBridge;
AndroidBridge* AndroidBridge::sBridge = nullptr;
pthread_t AndroidBridge::sJavaUiThread = -1;
static unsigned sJavaEnvThreadIndex = 0;
static jobject sGlobalContext = nullptr;
@ -161,14 +161,12 @@ AndroidBridge::ConstructBridge(JNIEnv *jEnv, Object::Param clsLoader)
PR_NewThreadPrivateIndex(&sJavaEnvThreadIndex, JavaThreadDetachFunc);
AndroidBridge *bridge = new AndroidBridge();
if (!bridge->Init(jEnv, clsLoader)) {
delete bridge;
}
sBridge = bridge;
MOZ_ASSERT(!sBridge);
sBridge = new AndroidBridge;
sBridge->Init(jEnv, clsLoader); // Success or crash
}
bool
void
AndroidBridge::Init(JNIEnv *jEnv, Object::Param clsLoader)
{
ALOG_BRIDGE("AndroidBridge::Init");
@ -244,8 +242,6 @@ AndroidBridge::Init(JNIEnv *jEnv, Object::Param clsLoader)
// jEnv should NOT be cached here by anything -- the jEnv here
// is not valid for the real gecko main thread, which is set
// at SetMainThread time.
return true;
}
bool

View File

@ -364,7 +364,7 @@ protected:
~AndroidBridge();
void InitStubs(JNIEnv *jEnv);
bool Init(JNIEnv *jEnv, jni::Object::Param clsLoader);
void Init(JNIEnv *jEnv, jni::Object::Param clsLoader);
bool mOpenedGraphicsLibraries;
void OpenGraphicsLibraries();

View File

@ -41,8 +41,9 @@ public:
return mVendor;
}
// This spoofed value wins, even if the environment variable
// MOZ_GFX_SPOOF_GL_VENDOR was set.
void SpoofVendor(const nsCString& s) {
EnsureInitialized();
mVendor = s;
}
@ -51,8 +52,9 @@ public:
return mRenderer;
}
// This spoofed value wins, even if the environment variable
// MOZ_GFX_SPOOF_GL_RENDERER was set.
void SpoofRenderer(const nsCString& s) {
EnsureInitialized();
mRenderer = s;
}
@ -61,8 +63,9 @@ public:
return mVersion;
}
// This spoofed value wins, even if the environment variable
// MOZ_GFX_SPOOF_GL_VERSION was set.
void SpoofVersion(const nsCString& s) {
EnsureInitialized();
mVersion = s;
}
@ -85,21 +88,32 @@ public:
gl->MakeCurrent();
const char *spoofedVendor = PR_GetEnv("MOZ_GFX_SPOOF_GL_VENDOR");
if (spoofedVendor)
if (mVendor.IsEmpty()) {
const char *spoofedVendor = PR_GetEnv("MOZ_GFX_SPOOF_GL_VENDOR");
if (spoofedVendor) {
mVendor.Assign(spoofedVendor);
else
} else {
mVendor.Assign((const char*)gl->fGetString(LOCAL_GL_VENDOR));
const char *spoofedRenderer = PR_GetEnv("MOZ_GFX_SPOOF_GL_RENDERER");
if (spoofedRenderer)
}
}
if (mRenderer.IsEmpty()) {
const char *spoofedRenderer = PR_GetEnv("MOZ_GFX_SPOOF_GL_RENDERER");
if (spoofedRenderer) {
mRenderer.Assign(spoofedRenderer);
else
} else {
mRenderer.Assign((const char*)gl->fGetString(LOCAL_GL_RENDERER));
const char *spoofedVersion = PR_GetEnv("MOZ_GFX_SPOOF_GL_VERSION");
if (spoofedVersion)
}
}
if (mVersion.IsEmpty()) {
const char *spoofedVersion = PR_GetEnv("MOZ_GFX_SPOOF_GL_VERSION");
if (spoofedVersion) {
mVersion.Assign(spoofedVersion);
else
} else {
mVersion.Assign((const char*)gl->fGetString(LOCAL_GL_VERSION));
}
}
mReady = true;
}
@ -153,9 +167,10 @@ GfxInfo::EnsureInitialized()
if (mInitialized)
return;
mGLStrings->EnsureInitialized();
MOZ_ASSERT(mozilla::AndroidBridge::Bridge());
if (!mozilla::AndroidBridge::Bridge()) {
gfxWarning() << "AndroidBridge missing during initialization";
return;
}
if (mozilla::AndroidBridge::Bridge()->GetStaticStringField("android/os/Build", "MODEL", mModel)) {
mAdapterDescription.AppendPrintf("Model: %s", NS_LossyConvertUTF16toASCII(mModel).get());
@ -601,7 +616,6 @@ GfxInfo::GetFeatureStatusImpl(int32_t aFeature,
/* void spoofVendorID (in DOMString aVendorID); */
NS_IMETHODIMP GfxInfo::SpoofVendorID(const nsAString & aVendorID)
{
EnsureInitialized();
mGLStrings->SpoofVendor(NS_LossyConvertUTF16toASCII(aVendorID));
return NS_OK;
}
@ -609,7 +623,6 @@ NS_IMETHODIMP GfxInfo::SpoofVendorID(const nsAString & aVendorID)
/* void spoofDeviceID (in unsigned long aDeviceID); */
NS_IMETHODIMP GfxInfo::SpoofDeviceID(const nsAString & aDeviceID)
{
EnsureInitialized();
mGLStrings->SpoofRenderer(NS_LossyConvertUTF16toASCII(aDeviceID));
return NS_OK;
}
@ -617,7 +630,6 @@ NS_IMETHODIMP GfxInfo::SpoofDeviceID(const nsAString & aDeviceID)
/* void spoofDriverVersion (in DOMString aDriverVersion); */
NS_IMETHODIMP GfxInfo::SpoofDriverVersion(const nsAString & aDriverVersion)
{
EnsureInitialized();
mGLStrings->SpoofVersion(NS_LossyConvertUTF16toASCII(aDriverVersion));
return NS_OK;
}