Merge m-c to inbound, a=merge

This commit is contained in:
Wes Kocher 2015-06-04 18:29:42 -07:00
commit 38f28af2e3
89 changed files with 1052 additions and 1661 deletions

View File

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="e862ab9177af664f00b4522e2350f4cb13866d73">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="e0fbadeb78a96137f071d9be7a47ef9fe882d17f"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="65369b217faac7d70c1a29100c4208c6d1db16e3"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="5deaf27fd266316f27e68206cc3be0e6f47ded54"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
@ -110,7 +110,7 @@
<project name="platform_prebuilts_qemu-kernel" path="prebuilts/qemu-kernel" remote="b2g" revision="6a1bb59af65b6485b1090522f66fac95c3f9e22c"/>
<project name="platform/prebuilts/sdk" path="prebuilts/sdk" revision="69d524e80cdf3981006627c65ac85f3a871238a3"/>
<project name="platform/prebuilts/tools" path="prebuilts/tools" revision="5a48c04c4bb5f079bc757e29864a42427378e051"/>
<project name="platform_system_bluetoothd" path="system/bluetoothd" remote="b2g" revision="4132c66e4bc78e119eb1cf34b6cf77db8e714e9a"/>
<project name="platform_system_bluetoothd" path="system/bluetoothd" remote="b2g" revision="9afc4e0e3e883f3a22a5eb94470d50f4b1cfe5c5"/>
<project name="platform/system/extras" path="system/extras" revision="576f57b6510de59c08568b53c0fb60588be8689e"/>
<project name="platform_system_libfdio" path="system/libfdio" remote="b2g" revision="3c5405863d2002f665ef2b901abb3853c420129b"/>
<project name="platform/system/netd" path="system/netd" revision="a6531f7befb49b1c81bc0de7e51c5482b308e1c5"/>

View File

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="e862ab9177af664f00b4522e2350f4cb13866d73">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="e0fbadeb78a96137f071d9be7a47ef9fe882d17f"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="65369b217faac7d70c1a29100c4208c6d1db16e3"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="5deaf27fd266316f27e68206cc3be0e6f47ded54"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
@ -115,7 +115,7 @@
<project name="platform_prebuilts_qemu-kernel" path="prebuilts/qemu-kernel" remote="b2g" revision="6a1bb59af65b6485b1090522f66fac95c3f9e22c"/>
<project name="platform/prebuilts/sdk" path="prebuilts/sdk" revision="cfcef469537869947abb9aa1d656774cc2678d4c"/>
<project name="platform/prebuilts/tools" path="prebuilts/tools" revision="5a48c04c4bb5f079bc757e29864a42427378e051"/>
<project name="platform_system_bluetoothd" path="system/bluetoothd" remote="b2g" revision="4132c66e4bc78e119eb1cf34b6cf77db8e714e9a"/>
<project name="platform_system_bluetoothd" path="system/bluetoothd" remote="b2g" revision="9afc4e0e3e883f3a22a5eb94470d50f4b1cfe5c5"/>
<project name="platform/system/extras" path="system/extras" revision="10e78a05252b3de785f88c2d0b9ea8a428009c50"/>
<project name="platform/system/media" path="system/media" revision="7ff72c2ea2496fa50b5e8a915e56e901c3ccd240"/>
<project name="platform_system_libfdio" path="system/libfdio" remote="b2g" revision="3c5405863d2002f665ef2b901abb3853c420129b"/>

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="e0fbadeb78a96137f071d9be7a47ef9fe882d17f"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="65369b217faac7d70c1a29100c4208c6d1db16e3"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="5deaf27fd266316f27e68206cc3be0e6f47ded54"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="aac9cc4bb94cf720baf8f7ee419b4d76ac86b1ac"/>

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="e0fbadeb78a96137f071d9be7a47ef9fe882d17f"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="65369b217faac7d70c1a29100c4208c6d1db16e3"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="5deaf27fd266316f27e68206cc3be0e6f47ded54"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="46da1a05ac04157669685246d70ac59d48699c9e"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="5c487ecd9afbcea1d507b9e4dfd09b3a8787fa65"/>
@ -117,7 +117,7 @@
<project name="platform/prebuilts/sdk" path="prebuilts/sdk" revision="842e33e43a55ea44833b9e23e4d180fa17c843af"/>
<project name="platform/prebuilts/tools" path="prebuilts/tools" revision="5db24726f0f42124304195a6bdea129039eeeaeb"/>
<project name="platform/system/bluetooth" path="system/bluetooth" revision="930ae098543881f47eac054677726ee4b998b2f8"/>
<project name="platform_system_bluetoothd" path="system/bluetoothd" remote="b2g" revision="4132c66e4bc78e119eb1cf34b6cf77db8e714e9a"/>
<project name="platform_system_bluetoothd" path="system/bluetoothd" remote="b2g" revision="9afc4e0e3e883f3a22a5eb94470d50f4b1cfe5c5"/>
<project name="platform_system_core" path="system/core" remote="b2g" revision="542d1f59dc331b472307e5bd043101d14d5a3a3e"/>
<project name="platform/system/extras" path="system/extras" revision="18c1180e848e7ab8691940481f5c1c8d22c37b3e"/>
<project name="platform_system_libfdio" path="system/libfdio" remote="b2g" revision="3c5405863d2002f665ef2b901abb3853c420129b"/>

View File

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="e862ab9177af664f00b4522e2350f4cb13866d73">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="e0fbadeb78a96137f071d9be7a47ef9fe882d17f"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="65369b217faac7d70c1a29100c4208c6d1db16e3"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="5deaf27fd266316f27e68206cc3be0e6f47ded54"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
@ -115,7 +115,7 @@
<project name="platform_prebuilts_qemu-kernel" path="prebuilts/qemu-kernel" remote="b2g" revision="6a1bb59af65b6485b1090522f66fac95c3f9e22c"/>
<project name="platform/prebuilts/sdk" path="prebuilts/sdk" revision="b562b01c93de9578d5db537b6a602a38e1aaa0ce"/>
<project name="platform/prebuilts/tools" path="prebuilts/tools" revision="387f03e815f57d536dd922706db1622bddba8d81"/>
<project name="platform_system_bluetoothd" path="system/bluetoothd" remote="b2g" revision="4132c66e4bc78e119eb1cf34b6cf77db8e714e9a"/>
<project name="platform_system_bluetoothd" path="system/bluetoothd" remote="b2g" revision="9afc4e0e3e883f3a22a5eb94470d50f4b1cfe5c5"/>
<project name="platform/system/extras" path="system/extras" revision="5356165f67f4a81c2ef28671c13697f1657590df"/>
<project name="platform/system/media" path="system/media" revision="be0e2fe59a8043fa5200f75697df9220a99abe9d"/>
<project name="platform_system_libfdio" path="system/libfdio" remote="b2g" revision="3c5405863d2002f665ef2b901abb3853c420129b"/>

View File

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="61e82f99bb8bc78d52b5717e9a2481ec7267fa33">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="e0fbadeb78a96137f071d9be7a47ef9fe882d17f"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="65369b217faac7d70c1a29100c4208c6d1db16e3"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="5deaf27fd266316f27e68206cc3be0e6f47ded54"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
@ -127,7 +127,7 @@
<project name="platform_prebuilts_qemu-kernel" path="prebuilts/qemu-kernel" remote="b2g" revision="2c0d193349c55337e37196a7f2d5cef37753ed3e"/>
<project name="platform/prebuilts/sdk" path="prebuilts/sdk" revision="61a10cbd19d6b7fc052a8cb92dfa1b37b93754f3"/>
<project name="platform/prebuilts/tools" path="prebuilts/tools" revision="9e892a67a01671f312c76b0880dedaa6ba478148"/>
<project name="platform_system_bluetoothd" path="system/bluetoothd" remote="b2g" revision="4132c66e4bc78e119eb1cf34b6cf77db8e714e9a"/>
<project name="platform_system_bluetoothd" path="system/bluetoothd" remote="b2g" revision="9afc4e0e3e883f3a22a5eb94470d50f4b1cfe5c5"/>
<project name="platform/system/extras" path="system/extras" revision="47fa016e2248b80aebd5928402c7409f8e0ca64e"/>
<project name="platform_system_libfdio" path="system/libfdio" remote="b2g" revision="3c5405863d2002f665ef2b901abb3853c420129b"/>
<project name="platform/system/media" path="system/media" revision="70bfebc66d9c6a4c614a8c7efde90e8e7e1d8641"/>

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="e0fbadeb78a96137f071d9be7a47ef9fe882d17f"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="65369b217faac7d70c1a29100c4208c6d1db16e3"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="5deaf27fd266316f27e68206cc3be0e6f47ded54"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="aac9cc4bb94cf720baf8f7ee419b4d76ac86b1ac"/>

View File

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="e862ab9177af664f00b4522e2350f4cb13866d73">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="e0fbadeb78a96137f071d9be7a47ef9fe882d17f"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="65369b217faac7d70c1a29100c4208c6d1db16e3"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="5deaf27fd266316f27e68206cc3be0e6f47ded54"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
@ -109,7 +109,7 @@
<project name="platform_prebuilts_qemu-kernel" path="prebuilts/qemu-kernel" remote="b2g" revision="6a1bb59af65b6485b1090522f66fac95c3f9e22c"/>
<project name="platform/prebuilts/sdk" path="prebuilts/sdk" revision="69d524e80cdf3981006627c65ac85f3a871238a3"/>
<project name="platform/prebuilts/tools" path="prebuilts/tools" revision="5a48c04c4bb5f079bc757e29864a42427378e051"/>
<project name="platform_system_bluetoothd" path="system/bluetoothd" remote="b2g" revision="4132c66e4bc78e119eb1cf34b6cf77db8e714e9a"/>
<project name="platform_system_bluetoothd" path="system/bluetoothd" remote="b2g" revision="9afc4e0e3e883f3a22a5eb94470d50f4b1cfe5c5"/>
<project name="platform/system/extras" path="system/extras" revision="576f57b6510de59c08568b53c0fb60588be8689e"/>
<project name="platform_system_libfdio" path="system/libfdio" remote="b2g" revision="3c5405863d2002f665ef2b901abb3853c420129b"/>
<project name="platform/system/netd" path="system/netd" revision="a6531f7befb49b1c81bc0de7e51c5482b308e1c5"/>

View File

@ -1,9 +1,9 @@
{
"git": {
"git_revision": "e0fbadeb78a96137f071d9be7a47ef9fe882d17f",
"git_revision": "65369b217faac7d70c1a29100c4208c6d1db16e3",
"remote": "https://git.mozilla.org/releases/gaia.git",
"branch": ""
},
"revision": "aa2355f8f650e320a73584dfd2c5a4932577f6ea",
"revision": "268edde8a1fcf00d4f4fbf9296f72d89ee754916",
"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="e0fbadeb78a96137f071d9be7a47ef9fe882d17f"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="65369b217faac7d70c1a29100c4208c6d1db16e3"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="5deaf27fd266316f27e68206cc3be0e6f47ded54"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="46da1a05ac04157669685246d70ac59d48699c9e"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="5c487ecd9afbcea1d507b9e4dfd09b3a8787fa65"/>
@ -117,7 +117,7 @@
<project name="platform/prebuilts/sdk" path="prebuilts/sdk" revision="842e33e43a55ea44833b9e23e4d180fa17c843af"/>
<project name="platform/prebuilts/tools" path="prebuilts/tools" revision="5db24726f0f42124304195a6bdea129039eeeaeb"/>
<project name="platform/system/bluetooth" path="system/bluetooth" revision="930ae098543881f47eac054677726ee4b998b2f8"/>
<project name="platform_system_bluetoothd" path="system/bluetoothd" remote="b2g" revision="4132c66e4bc78e119eb1cf34b6cf77db8e714e9a"/>
<project name="platform_system_bluetoothd" path="system/bluetoothd" remote="b2g" revision="9afc4e0e3e883f3a22a5eb94470d50f4b1cfe5c5"/>
<project name="platform_system_core" path="system/core" remote="b2g" revision="542d1f59dc331b472307e5bd043101d14d5a3a3e"/>
<project name="platform/system/extras" path="system/extras" revision="18c1180e848e7ab8691940481f5c1c8d22c37b3e"/>
<project name="platform_system_libfdio" path="system/libfdio" remote="b2g" revision="3c5405863d2002f665ef2b901abb3853c420129b"/>

View File

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="61e82f99bb8bc78d52b5717e9a2481ec7267fa33">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="e0fbadeb78a96137f071d9be7a47ef9fe882d17f"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="65369b217faac7d70c1a29100c4208c6d1db16e3"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="5deaf27fd266316f27e68206cc3be0e6f47ded54"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
@ -127,7 +127,7 @@
<project name="platform_prebuilts_qemu-kernel" path="prebuilts/qemu-kernel" remote="b2g" revision="2c0d193349c55337e37196a7f2d5cef37753ed3e"/>
<project name="platform/prebuilts/sdk" path="prebuilts/sdk" revision="61a10cbd19d6b7fc052a8cb92dfa1b37b93754f3"/>
<project name="platform/prebuilts/tools" path="prebuilts/tools" revision="9e892a67a01671f312c76b0880dedaa6ba478148"/>
<project name="platform_system_bluetoothd" path="system/bluetoothd" remote="b2g" revision="4132c66e4bc78e119eb1cf34b6cf77db8e714e9a"/>
<project name="platform_system_bluetoothd" path="system/bluetoothd" remote="b2g" revision="9afc4e0e3e883f3a22a5eb94470d50f4b1cfe5c5"/>
<project name="platform/system/extras" path="system/extras" revision="47fa016e2248b80aebd5928402c7409f8e0ca64e"/>
<project name="platform_system_libfdio" path="system/libfdio" remote="b2g" revision="3c5405863d2002f665ef2b901abb3853c420129b"/>
<project name="platform/system/media" path="system/media" revision="70bfebc66d9c6a4c614a8c7efde90e8e7e1d8641"/>

View File

@ -1747,13 +1747,9 @@ pref("loop.contextInConversations.enabled", true);
pref("social.sidebar.unload_timeout_ms", 10000);
// activation from inside of share panel is possible if activationPanelEnabled
// Activation from inside of share panel is possible if activationPanelEnabled
// is true. Pref'd off for release while usage testing is done through beta.
#ifdef EARLY_BETA_OR_EARLIER
pref("social.share.activationPanelEnabled", true);
#else
pref("social.share.activationPanelEnabled", false);
#endif
pref("social.shareDirectory", "https://activations.cdn.mozilla.net/sharePanel.html");
pref("dom.identity.enabled", false);

View File

@ -304,9 +304,12 @@ SocialActivationListener = {
// and has never been visible, so we check the widget directly. If
// there is no area for the widget we move it into the toolbar.
let widget = CustomizableUI.getWidget("social-share-button");
if (!widget.areaType) {
// If the panel is already open, we can be sure that the provider can
// already be accessed, possibly anchored to another toolbar button.
// In that case we don't move the widget.
if (!widget.areaType && SocialShare.panel.state != "open") {
CustomizableUI.addWidgetToArea("social-share-button", CustomizableUI.AREA_NAVBAR);
// ensure correct state
// Ensure correct state.
SocialUI.onCustomizeEnd(window);
}
@ -473,6 +476,9 @@ SocialShare = {
let widget = CustomizableUI.getWidget("social-share-button");
return widget.forWindow(window).anchor;
},
// Holds the anchor node in use whilst the panel is open, because it may vary.
_currentAnchor: null,
get panel() {
return document.getElementById("social-share-panel");
},
@ -578,12 +584,13 @@ SocialShare = {
},
onShowing: function() {
this.anchor.setAttribute("open", "true");
(this._currentAnchor || this.anchor).setAttribute("open", "true");
this.iframe.addEventListener("click", this._onclick, true);
},
onHidden: function() {
this.anchor.removeAttribute("open");
(this._currentAnchor || this.anchor).removeAttribute("open");
this._currentAnchor = null;
this.iframe.removeEventListener("click", this._onclick, true);
this.iframe.setAttribute("src", "data:text/plain;charset=utf8,");
// make sure that the frame is unloaded after it is hidden
@ -597,7 +604,7 @@ SocialShare = {
}
},
sharePage: function(providerOrigin, graphData, target) {
sharePage: function(providerOrigin, graphData, target, anchor) {
// if providerOrigin is undefined, we use the last-used provider, or the
// current/default provider. The provider selection in the share panel
// will call sharePage with an origin for us to switch to.
@ -630,7 +637,7 @@ SocialShare = {
pageData[p] = graphData[p];
}
}
this.sharePage(providerOrigin, pageData, target);
this.sharePage(providerOrigin, pageData, target, anchor);
});
gBrowser.selectedBrowser.messageManager.sendAsyncMessage("PageMetadata:GetPageData");
return;
@ -640,7 +647,7 @@ SocialShare = {
messageManager.addMessageListener("PageMetadata:MicrodataResult", _dataFn = (msg) => {
messageManager.removeMessageListener("PageMetadata:MicrodataResult", _dataFn);
pageData.microdata = msg.data;
this.sharePage(providerOrigin, pageData, target);
this.sharePage(providerOrigin, pageData, target, anchor);
});
gBrowser.selectedBrowser.messageManager.sendAsyncMessage("PageMetadata:GetMicrodata", null, { target });
return;
@ -653,7 +660,7 @@ SocialShare = {
else
provider = this.getSelectedProvider();
if (!provider || !provider.shareURL) {
this.showDirectory();
this.showDirectory(anchor);
return;
}
// check the menu button
@ -713,10 +720,10 @@ SocialShare = {
let uri = Services.io.newURI(shareEndpoint, null, null);
iframe.setAttribute("origin", provider.origin);
iframe.setAttribute("src", shareEndpoint);
this._openPanel();
this._openPanel(anchor);
},
showDirectory: function() {
showDirectory: function(anchor) {
this._createFrame();
let iframe = this.iframe;
if (iframe.getAttribute("src") == "about:providerdirectory")
@ -740,11 +747,12 @@ SocialShare = {
}, true);
}, true);
iframe.setAttribute("src", "about:providerdirectory");
this._openPanel();
this._openPanel(anchor);
},
_openPanel: function() {
let anchor = document.getAnonymousElementByAttribute(this.anchor, "class", "toolbarbutton-icon");
_openPanel: function(anchor) {
this._currentAnchor = anchor || this.anchor;
anchor = document.getAnonymousElementByAttribute(this._currentAnchor, "class", "toolbarbutton-icon");
this.panel.openPopup(anchor, "bottomcenter topright", 0, 0, false, false);
Services.telemetry.getHistogramById("SOCIAL_TOOLBAR_BUTTONS").add(0);
}

View File

@ -77,7 +77,6 @@ loop.store = loop.store || {};
* @type {Array}
*/
actions: [
"addSocialShareButton",
"addSocialShareProvider",
"createRoom",
"createdRoom",
@ -375,15 +374,6 @@ loop.store = loop.store || {};
this._mozLoop.notifyUITour("Loop:RoomURLShared");
},
/**
* Add the Social Share button to the browser toolbar.
*
* @param {sharedActions.AddSocialShareButton} actionData The action data.
*/
addSocialShareButton: function(actionData) {
this._mozLoop.addSocialShareButton();
},
/**
* Open the share panel to add a Social share provider.
*

View File

@ -79,16 +79,9 @@ loop.roomViews = (function(mozL10n) {
dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired,
roomUrl: React.PropTypes.string,
show: React.PropTypes.bool.isRequired,
socialShareButtonAvailable: React.PropTypes.bool,
socialShareProviders: React.PropTypes.array
},
handleToolbarAddButtonClick: function(event) {
event.preventDefault();
this.props.dispatcher.dispatch(new sharedActions.AddSocialShareButton());
},
handleAddServiceClick: function(event) {
event.preventDefault();
@ -121,34 +114,9 @@ loop.roomViews = (function(mozL10n) {
"share-service-dropdown": true,
"dropdown-menu": true,
"visually-hidden": true,
"share-button-unavailable": !this.props.socialShareButtonAvailable,
"hide": !this.props.show
});
// When the button is not yet available, we offer to put it in the navbar
// for the user.
if (!this.props.socialShareButtonAvailable) {
return (
React.createElement("div", {className: shareDropdown},
React.createElement("div", {className: "share-panel-header"},
mozL10n.get("share_panel_header")
),
React.createElement("div", {className: "share-panel-body"},
mozL10n.get("share_panel_body", {
brandShortname: mozL10n.get("brandShortname"),
clientSuperShortname: mozL10n.get("clientSuperShortname")
})
),
React.createElement("button", {className: "btn btn-info btn-toolbar-add",
onClick: this.handleToolbarAddButtonClick},
mozL10n.get("add_to_toolbar_button")
)
)
);
}
return (
React.createElement("ul", {className: shareDropdown},
React.createElement("li", {className: "dropdown-menu-item", onClick: this.handleAddServiceClick},
@ -188,7 +156,8 @@ loop.roomViews = (function(mozL10n) {
roomData: React.PropTypes.object.isRequired,
savingContext: React.PropTypes.bool,
show: React.PropTypes.bool.isRequired,
showContext: React.PropTypes.bool.isRequired
showContext: React.PropTypes.bool.isRequired,
socialShareProviders: React.PropTypes.array
},
getInitialState: function() {
@ -223,6 +192,14 @@ loop.roomViews = (function(mozL10n) {
handleShareButtonClick: function(event) {
event.preventDefault();
var providers = this.props.socialShareProviders;
// If there are no providers available currently, save a click by dispatching
// the 'AddSocialShareProvider' right away.
if (!providers || !providers.length) {
this.props.dispatcher.dispatch(new sharedActions.AddSocialShareProvider());
return;
}
this.toggleDropdownMenu();
},
@ -280,7 +257,6 @@ loop.roomViews = (function(mozL10n) {
dispatcher: this.props.dispatcher,
roomUrl: this.props.roomData.roomUrl,
show: this.state.showMenu,
socialShareButtonAvailable: this.props.socialShareButtonAvailable,
socialShareProviders: this.props.socialShareProviders,
ref: "menu"}),
React.createElement(DesktopRoomContextView, {
@ -709,7 +685,6 @@ loop.roomViews = (function(mozL10n) {
savingContext: this.state.savingContext,
show: shouldRenderInvitationOverlay,
showContext: shouldRenderContextView,
socialShareButtonAvailable: this.state.socialShareButtonAvailable,
socialShareProviders: this.state.socialShareProviders}),
React.createElement("div", {className: "video-layout-wrapper"},
React.createElement("div", {className: "conversation room-conversation"},

View File

@ -79,16 +79,9 @@ loop.roomViews = (function(mozL10n) {
dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired,
roomUrl: React.PropTypes.string,
show: React.PropTypes.bool.isRequired,
socialShareButtonAvailable: React.PropTypes.bool,
socialShareProviders: React.PropTypes.array
},
handleToolbarAddButtonClick: function(event) {
event.preventDefault();
this.props.dispatcher.dispatch(new sharedActions.AddSocialShareButton());
},
handleAddServiceClick: function(event) {
event.preventDefault();
@ -121,34 +114,9 @@ loop.roomViews = (function(mozL10n) {
"share-service-dropdown": true,
"dropdown-menu": true,
"visually-hidden": true,
"share-button-unavailable": !this.props.socialShareButtonAvailable,
"hide": !this.props.show
});
// When the button is not yet available, we offer to put it in the navbar
// for the user.
if (!this.props.socialShareButtonAvailable) {
return (
<div className={shareDropdown}>
<div className="share-panel-header">
{mozL10n.get("share_panel_header")}
</div>
<div className="share-panel-body">
{
mozL10n.get("share_panel_body", {
brandShortname: mozL10n.get("brandShortname"),
clientSuperShortname: mozL10n.get("clientSuperShortname")
})
}
</div>
<button className="btn btn-info btn-toolbar-add"
onClick={this.handleToolbarAddButtonClick}>
{mozL10n.get("add_to_toolbar_button")}
</button>
</div>
);
}
return (
<ul className={shareDropdown}>
<li className="dropdown-menu-item" onClick={this.handleAddServiceClick}>
@ -188,7 +156,8 @@ loop.roomViews = (function(mozL10n) {
roomData: React.PropTypes.object.isRequired,
savingContext: React.PropTypes.bool,
show: React.PropTypes.bool.isRequired,
showContext: React.PropTypes.bool.isRequired
showContext: React.PropTypes.bool.isRequired,
socialShareProviders: React.PropTypes.array
},
getInitialState: function() {
@ -223,6 +192,14 @@ loop.roomViews = (function(mozL10n) {
handleShareButtonClick: function(event) {
event.preventDefault();
var providers = this.props.socialShareProviders;
// If there are no providers available currently, save a click by dispatching
// the 'AddSocialShareProvider' right away.
if (!providers || !providers.length) {
this.props.dispatcher.dispatch(new sharedActions.AddSocialShareProvider());
return;
}
this.toggleDropdownMenu();
},
@ -280,7 +257,6 @@ loop.roomViews = (function(mozL10n) {
dispatcher={this.props.dispatcher}
roomUrl={this.props.roomData.roomUrl}
show={this.state.showMenu}
socialShareButtonAvailable={this.props.socialShareButtonAvailable}
socialShareProviders={this.props.socialShareProviders}
ref="menu" />
<DesktopRoomContextView
@ -709,7 +685,6 @@ loop.roomViews = (function(mozL10n) {
savingContext={this.state.savingContext}
show={shouldRenderInvitationOverlay}
showContext={shouldRenderContextView}
socialShareButtonAvailable={this.state.socialShareButtonAvailable}
socialShareProviders={this.state.socialShareProviders} />
<div className="video-layout-wrapper">
<div className="conversation room-conversation">

View File

@ -944,40 +944,12 @@ body[platform="win"] .share-service-dropdown.overflow > .dropdown-menu-item {
-moz-padding-end: 20px;
}
.share-service-dropdown.share-button-unavailable {
width: 230px;
padding: 8px;
}
.share-service-dropdown > .dropdown-menu-item > .icon {
width: 14px;
height: 14px;
margin-right: 4px;
}
.share-service-dropdown .share-panel-header {
background-image: url("../img/icons-16x16.svg#share-darkgrey");
background-size: 3em 3em;
background-repeat: no-repeat;
min-height: 3em;
font-weight: bold;
margin-bottom: 1em;
padding-left: 4.5em;
}
body[dir=rtl] .share-service-dropdown .share-panel-header {
background-position: top right;
padding-left: 0;
padding-right: 4.5em;
}
.share-service-dropdown .btn-toolbar-add {
padding: 4px 2px;
border-radius: 2px;
margin-top: 1em;
width: 100%;
}
.dropdown-menu-item > .icon-add-share-service {
background-image: url("../img/icons-16x16.svg#add");
background-repeat: no-repeat;

View File

@ -400,13 +400,6 @@ loop.shared.actions = (function() {
roomUrl: String
}),
/**
* Add the Social Share button to the browser toolbar.
* XXX: should move to some roomActions module - refs bug 1079284
*/
AddSocialShareButton: Action.define("addSocialShareButton", {
}),
/**
* Open the share panel to add a Social share provider.
* XXX: should move to some roomActions module - refs bug 1079284
@ -436,7 +429,6 @@ loop.shared.actions = (function() {
roomOwner: String,
roomToken: String,
roomUrl: String,
socialShareButtonAvailable: Boolean,
socialShareProviders: Array
}),
@ -460,7 +452,6 @@ loop.shared.actions = (function() {
* XXX: should move to some roomActions module - refs bug 1079284
*/
UpdateSocialShareInfo: Action.define("updateSocialShareInfo", {
socialShareButtonAvailable: Boolean,
socialShareProviders: Array
}),

View File

@ -115,8 +115,6 @@ loop.store.ActiveRoomStore = (function() {
roomInfoFailure: null,
// The name of the room.
roomName: null,
// Social API state.
socialShareButtonAvailable: false,
socialShareProviders: null
};
},
@ -218,7 +216,6 @@ loop.store.ActiveRoomStore = (function() {
roomName: roomData.decryptedContext.roomName,
roomOwner: roomData.roomOwner,
roomUrl: roomData.roomUrl,
socialShareButtonAvailable: this._mozLoop.isSocialShareButtonAvailable(),
socialShareProviders: this._mozLoop.getSocialShareProviders()
}));
@ -337,7 +334,6 @@ loop.store.ActiveRoomStore = (function() {
roomState: ROOM_STATES.READY,
roomToken: actionData.roomToken,
roomUrl: actionData.roomUrl,
socialShareButtonAvailable: actionData.socialShareButtonAvailable,
socialShareProviders: actionData.socialShareProviders
});
@ -379,7 +375,6 @@ loop.store.ActiveRoomStore = (function() {
*/
updateSocialShareInfo: function(actionData) {
this.setStoreState({
socialShareButtonAvailable: actionData.socialShareButtonAvailable,
socialShareProviders: actionData.socialShareProviders
});
},
@ -418,7 +413,6 @@ loop.store.ActiveRoomStore = (function() {
*/
_handleSocialShareUpdate: function() {
this.dispatchAction(new sharedActions.UpdateSocialShareInfo({
socialShareButtonAvailable: this._mozLoop.isSocialShareButtonAvailable(),
socialShareProviders: this._mozLoop.getSocialShareProviders()
}));
},

View File

@ -969,58 +969,6 @@ function injectLoopAPI(targetWindow) {
}
},
/**
* Checks if the Social Share widget is available in any of the registered
* widget areas (navbar, MenuPanel, etc).
*
* @return {Boolean} `true` if the widget is available and `false` when it's
* still in the Customization palette.
*/
isSocialShareButtonAvailable: {
enumerable: true,
writable: true,
value: function() {
let win = Services.wm.getMostRecentWindow("navigator:browser");
if (!win || !win.CustomizableUI) {
return false;
}
let widget = win.CustomizableUI.getWidget(kShareWidgetId);
if (widget) {
if (!socialShareButtonListenersAdded) {
let eventName = "social:" + kShareWidgetId;
Services.obs.addObserver(onShareWidgetChanged, eventName + "-added", false);
Services.obs.addObserver(onShareWidgetChanged, eventName + "-removed", false);
socialShareButtonListenersAdded = true;
}
return !!widget.areaType;
}
return false;
}
},
/**
* Add the Social Share widget to the navbar area, but only when it's not
* located anywhere else than the Customization palette.
*/
addSocialShareButton: {
enumerable: true,
writable: true,
value: function() {
// Don't do anything if the button is already available.
if (api.isSocialShareButtonAvailable.value()) {
return;
}
let win = Services.wm.getMostRecentWindow("navigator:browser");
if (!win || !win.CustomizableUI) {
return;
}
win.CustomizableUI.addWidgetToArea(kShareWidgetId, win.CustomizableUI.AREA_NAVBAR);
}
},
/**
* Activates the Social Share panel with the Social Provider panel opened
* when the popup open.
@ -1029,23 +977,18 @@ function injectLoopAPI(targetWindow) {
enumerable: true,
writable: true,
value: function() {
// Don't do anything if the button is _not_ available.
if (!api.isSocialShareButtonAvailable.value()) {
return;
}
let win = Services.wm.getMostRecentWindow("navigator:browser");
if (!win || !win.SocialShare) {
return;
}
win.SocialShare.showDirectory();
win.SocialShare.showDirectory(win.LoopUI.toolbarButton.anchor);
}
},
/**
* Returns a sorted list of Social Providers that can share URLs. See
* `updateSocialProvidersCache()` for more information.
*
*
* @return {Array} Sorted list of share-capable Social Providers.
*/
getSocialShareProviders: {
@ -1087,7 +1030,8 @@ function injectLoopAPI(targetWindow) {
if (body) {
graphData.body = body;
}
win.SocialShare.sharePage(providerOrigin, graphData);
win.SocialShare.sharePage(providerOrigin, graphData, null,
win.LoopUI.toolbarButton.anchor);
}
}
};

View File

@ -447,16 +447,6 @@ describe("loop.store.RoomStore", function () {
});
});
describe("#addSocialShareButton", function() {
it("should invoke to the correct mozLoop function", function() {
fakeMozLoop.addSocialShareButton = sinon.stub();
store.addSocialShareButton(new sharedActions.AddSocialShareButton());
sinon.assert.calledOnce(fakeMozLoop.addSocialShareButton);
});
});
describe("#addSocialShareProvider", function() {
it("should invoke to the correct mozLoop function", function() {
fakeMozLoop.addSocialShareProvider = sinon.stub();

View File

@ -28,7 +28,6 @@ describe("loop.roomViews", function () {
previews: [],
title: ""
}),
isSocialShareButtonAvailable: sinon.stub(),
rooms: {
get: sinon.stub().callsArgWith(1, null, {
roomToken: "fakeToken",
@ -211,11 +210,27 @@ describe("loop.roomViews", function () {
});
describe("Share button", function() {
beforeEach(function() {
it("should dispatch a AddSocialShareProvider action when the share button is clicked", function() {
view = mountTestComponent();
var shareBtn = view.getDOMNode().querySelector(".btn-share");
React.addons.TestUtils.Simulate.click(shareBtn);
sinon.assert.calledOnce(dispatcher.dispatch);
sinon.assert.calledWith(dispatcher.dispatch,
new sharedActions.AddSocialShareProvider());
});
it("should toggle the share dropdown when the share button is clicked", function() {
view = mountTestComponent({
socialShareProviders: [{
name: "foo",
origin: "https://foo",
iconURL: "http://example.com/foo.png"
}]
});
var shareBtn = view.getDOMNode().querySelector(".btn-share");
React.addons.TestUtils.Simulate.click(shareBtn);
@ -552,18 +567,8 @@ describe("loop.roomViews", function () {
expect(view.getDOMNode()).to.eql(null);
});
it("should show different contents when the Share XUL button is not available", function() {
view = mountTestComponent({
socialShareProviders: []
});
var node = view.getDOMNode();
expect(node.querySelector(".share-panel-header")).to.not.eql(null);
});
it("should show an empty list when no Social Providers are available", function() {
view = mountTestComponent({
socialShareButtonAvailable: true,
socialShareProviders: []
});
@ -574,7 +579,6 @@ describe("loop.roomViews", function () {
it("should show a list of available Social Providers", function() {
view = mountTestComponent({
socialShareButtonAvailable: true,
socialShareProviders: [fakeProvider]
});
@ -590,26 +594,10 @@ describe("loop.roomViews", function () {
});
});
describe("#handleToolbarAddButtonClick", function() {
it("should dispatch an action when the 'add to toolbar' button is clicked", function() {
view = mountTestComponent({
socialShareProviders: []
});
var addButton = view.getDOMNode().querySelector(".btn-toolbar-add");
React.addons.TestUtils.Simulate.click(addButton);
sinon.assert.calledOnce(dispatcher.dispatch);
sinon.assert.calledWithExactly(dispatcher.dispatch,
new sharedActions.AddSocialShareButton());
});
});
describe("#handleAddServiceClick", function() {
it("should dispatch an action when the 'add provider' item is clicked", function() {
view = mountTestComponent({
socialShareProviders: [],
socialShareButtonAvailable: true
socialShareProviders: []
});
var addItem = view.getDOMNode().querySelector(".dropdown-menu-item:first-child");
@ -625,7 +613,6 @@ describe("loop.roomViews", function () {
it("should dispatch an action when a provider item is clicked", function() {
view = mountTestComponent({
roomUrl: "http://example.com",
socialShareButtonAvailable: true,
socialShareProviders: [fakeProvider]
});

View File

@ -13,7 +13,6 @@ const {SocialService} = Cu.import("resource://gre/modules/SocialService.jsm", {}
add_task(loadLoopPanel);
const kShareWidgetId = "social-share-button";
const kShareProvider = {
name: "provider 1",
origin: "https://example.com",
@ -32,51 +31,7 @@ registerCleanupFunction(function* () {
SocialShare.uninit();
});
add_task(function* test_mozLoop_isSocialShareButtonAvailable() {
Assert.ok(gMozLoopAPI, "mozLoop should exist");
// First make sure the Social Share button is not available. This is probably
// already the case, but make it explicit here.
CustomizableUI.removeWidgetFromArea(kShareWidgetId);
Assert.ok(!gMozLoopAPI.isSocialShareButtonAvailable(),
"Social Share button should not be available");
// Add the widget to the navbar.
CustomizableUI.addWidgetToArea(kShareWidgetId, CustomizableUI.AREA_NAVBAR);
Assert.ok(gMozLoopAPI.isSocialShareButtonAvailable(),
"Social Share button should be available");
// Add the widget to the MenuPanel.
CustomizableUI.addWidgetToArea(kShareWidgetId, CustomizableUI.AREA_PANEL);
Assert.ok(gMozLoopAPI.isSocialShareButtonAvailable(),
"Social Share button should still be available");
// Test button removal during the same session.
CustomizableUI.removeWidgetFromArea(kShareWidgetId);
Assert.ok(!gMozLoopAPI.isSocialShareButtonAvailable(),
"Social Share button should not be available");
});
add_task(function* test_mozLoop_addSocialShareButton() {
gMozLoopAPI.addSocialShareButton();
Assert.ok(gMozLoopAPI.isSocialShareButtonAvailable(),
"Social Share button should be available");
let widget = CustomizableUI.getWidget(kShareWidgetId);
Assert.strictEqual(widget.areaType, CustomizableUI.TYPE_TOOLBAR,
"Social Share button should be placed in the navbar");
CustomizableUI.removeWidgetFromArea(kShareWidgetId);
});
add_task(function* test_mozLoop_addSocialShareProvider() {
gMozLoopAPI.addSocialShareButton();
gMozLoopAPI.addSocialShareProvider();
yield promiseWaitForCondition(() => SocialShare.panel.state == "open");
@ -85,7 +40,6 @@ add_task(function* test_mozLoop_addSocialShareProvider() {
"Provider directory page should be visible");
SocialShare.panel.hidePopup();
CustomizableUI.removeWidgetFromArea(kShareWidgetId);
});
add_task(function* test_mozLoop_getSocialShareProviders() {
@ -131,8 +85,6 @@ add_task(function* test_mozLoop_getSocialShareProviders() {
});
add_task(function* test_mozLoop_socialShareRoom() {
gMozLoopAPI.addSocialShareButton();
gMozLoopAPI.socialShareRoom(kShareProvider.origin, "https://someroom.com", "Some Title");
yield promiseWaitForCondition(() => SocialShare.panel.state == "open");
@ -143,5 +95,4 @@ add_task(function* test_mozLoop_socialShareRoom() {
"Provider's share page should be displayed");
SocialShare.panel.hidePopup();
CustomizableUI.removeWidgetFromArea(kShareWidgetId);
});

View File

@ -37,7 +37,6 @@ describe("loop.store.ActiveRoomStore", function () {
},
setScreenShareState: sinon.stub(),
getActiveTabWindowId: sandbox.stub().callsArgWith(0, null, 42),
isSocialShareButtonAvailable: sinon.stub().returns(false),
getSocialShareProviders: sinon.stub().returns([])
};
@ -289,7 +288,6 @@ describe("loop.store.ActiveRoomStore", function () {
roomName: fakeRoomData.decryptedContext.roomName,
roomOwner: fakeRoomData.roomOwner,
roomUrl: fakeRoomData.roomUrl,
socialShareButtonAvailable: false,
socialShareProviders: []
}));
});
@ -550,7 +548,6 @@ describe("loop.store.ActiveRoomStore", function () {
roomOwner: "Me",
roomToken: "fakeToken",
roomUrl: "http://invalid",
socialShareButtonAvailable: false,
socialShareProviders: []
};
});
@ -569,7 +566,6 @@ describe("loop.store.ActiveRoomStore", function () {
expect(state.roomOwner).eql(fakeRoomInfo.roomOwner);
expect(state.roomToken).eql(fakeRoomInfo.roomToken);
expect(state.roomUrl).eql(fakeRoomInfo.roomUrl);
expect(state.socialShareButtonAvailable).eql(false);
expect(state.socialShareProviders).eql([]);
});
});
@ -606,7 +602,6 @@ describe("loop.store.ActiveRoomStore", function () {
beforeEach(function() {
fakeSocialShareInfo = {
socialShareButtonAvailable: true,
socialShareProviders: [{
name: "foo",
origin: "https://example.com",
@ -619,8 +614,6 @@ describe("loop.store.ActiveRoomStore", function () {
store.updateSocialShareInfo(new sharedActions.UpdateSocialShareInfo(fakeSocialShareInfo));
var state = store.getStoreState();
expect(state.socialShareButtonAvailable)
.eql(fakeSocialShareInfo.socialShareButtonAvailable);
expect(state.socialShareProviders)
.eql(fakeSocialShareInfo.socialShareProviders);
});
@ -1368,7 +1361,6 @@ describe("loop.store.ActiveRoomStore", function () {
sinon.assert.calledOnce(dispatcher.dispatch);
sinon.assert.calledWithExactly(dispatcher.dispatch,
new sharedActions.UpdateSocialShareInfo({
socialShareButtonAvailable: false,
socialShareProviders: []
}));
});
@ -1376,7 +1368,6 @@ describe("loop.store.ActiveRoomStore", function () {
it("should call respective mozLoop methods", function() {
store._handleSocialShareUpdate();
sinon.assert.calledOnce(fakeMozLoop.isSocialShareButtonAvailable);
sinon.assert.calledOnce(fakeMozLoop.getSocialShareProviders);
});
});
@ -1389,7 +1380,6 @@ describe("loop.store.ActiveRoomStore", function () {
roomOwner: "Me",
roomToken: "fakeToken",
roomUrl: "http://invalid",
socialShareButtonAvailable: false,
socialShareProviders: []
}));
});
@ -1438,7 +1428,6 @@ describe("loop.store.ActiveRoomStore", function () {
beforeEach(function() {
store.setupRoomInfo(new sharedActions.SetupRoomInfo(
_.extend(fakeRoomData, {
socialShareButtonAvailable: false,
socialShareProviders: []
})
));

View File

@ -32,8 +32,9 @@
"brace-style": [2, "1tbs", {"allowSingleLine": false}],
// Require camel case names
"camelcase": 2,
// Disallow trailing commas. Not valid JSON notation.
"comma-dangle": 1,
// Allow trailing commas for easy list extension. Having them does not
// impair readability, but also not required either.
"comma-dangle": 0,
// Enforce spacing before and after comma
"comma-spacing": [2, {"before": false, "after": true}],
// Enforce one true comma style.

View File

@ -1910,16 +1910,6 @@ Breakpoints.prototype = {
// editor itself.
let breakpointClient = yield this.addBreakpoint(location, { noEditorUpdate: true });
// If the breakpoint client has a "requestedLocation" attached, then
// the original requested placement for the breakpoint wasn't accepted.
// In this case, we need to update the editor with the new location.
if (breakpointClient.requestedLocation) {
DebuggerView.editor.moveBreakpoint(
breakpointClient.requestedLocation.line - 1,
breakpointClient.location.line - 1
);
}
// Notify that we've shown a breakpoint in the source editor.
window.emit(EVENTS.BREAKPOINT_SHOWN_IN_EDITOR);
}),
@ -2030,11 +2020,40 @@ Breakpoints.prototype = {
source.setBreakpoint(aLocation, Task.async(function*(aResponse, aBreakpointClient) {
// If the breakpoint response has an "actualLocation" attached, then
// the original requested placement for the breakpoint wasn't accepted.
if (aResponse.actualLocation) {
// Remember the initialization promise for the new location instead.
let actualLocation = aResponse.actualLocation;
if (actualLocation) {
// Update the editor to reflect the new location of the breakpoint. We
// always need to do this, even when we already have a breakpoint for
// the actual location, because the editor already as already shown the
// breakpoint at the original location at this point. Calling
// moveBreakpoint will hide the breakpoint at the original location, and
// show it at the actual location, if necessary.
//
// FIXME: The call to moveBreakpoint triggers another call to remove-
// and addBreakpoint, respectively. These calls do not have any effect,
// because there is no breakpoint to remove at the old location, and
// the breakpoint is already being added at the new location, but they
// are redundant and confusing.
DebuggerView.editor.moveBreakpoint(
aBreakpointClient.location.line - 1,
actualLocation.line - 1
);
aBreakpointClient.location = actualLocation;
aBreakpointClient.location.actor = actualLocation.source
? actualLocation.source.actor
: null;
let oldIdentifier = identifier;
let newIdentifier = identifier = this.getIdentifier(aResponse.actualLocation);
this._added.delete(oldIdentifier);
if ((addedPromise = this._getAdded(actualLocation))) {
deferred.resolve(addedPromise);
return;
}
// Remember the initialization promise for the new location instead.
let newIdentifier = identifier = this.getIdentifier(actualLocation);
this._added.set(newIdentifier, deferred.promise);
}
@ -2055,15 +2074,6 @@ Breakpoints.prototype = {
}
}
if (aResponse.actualLocation) {
// Store the originally requested location in case it's ever needed
// and update the breakpoint client with the actual location.
let actualLoc = aResponse.actualLocation;
aBreakpointClient.requestedLocation = aLocation;
aBreakpointClient.location = actualLoc;
aBreakpointClient.location.actor = actualLoc.source ? actualLoc.source.actor : null;
}
// Preserve information about the breakpoint's line text, to display it
// in the sources pane without requiring fetching the source (for example,
// after the target navigated). Note that this will get out of sync
@ -2071,8 +2081,7 @@ Breakpoints.prototype = {
let line = aBreakpointClient.location.line - 1;
aBreakpointClient.text = DebuggerView.editor.getText(line).trim();
// Show the breakpoint in the editor and breakpoints pane, and
// resolve.
// Show the breakpoint in the breakpoints pane, and resolve.
yield this._showBreakpoint(aBreakpointClient, aOptions);
// Notify that we've added a breakpoint.

View File

@ -49,11 +49,6 @@ function test() {
is(aBreakpointClient.location.line, 6,
"Breakpoint client line is new.");
is(aBreakpointClient.requestedLocation.actor, gSources.selectedValue,
"Requested location url is correct");
is(aBreakpointClient.requestedLocation.line, 4,
"Requested location line is correct");
onBpDebuggerAdd = true;
maybeFinish();
}

View File

@ -49,6 +49,7 @@ function test() {
actor: gSources.selectedValue,
line: 17
});
testMovedLocation(movedBpClient);
yield resumeAndTestBreakpoint(19);
@ -93,10 +94,5 @@ function test() {
"Breakpoint client url is the same.");
is(breakpointClient.location.line, 19,
"Breakpoint client line is new.");
is(breakpointClient.requestedLocation.actor, gSources.selectedValue,
"Requested location url is correct");
is(breakpointClient.requestedLocation.line, 17,
"Requested location line is correct");
}
}

View File

@ -28,6 +28,7 @@ FontInspector.prototype = {
init: function() {
this.update = this.update.bind(this);
this.onNewNode = this.onNewNode.bind(this);
this.onThemeChanged = this.onThemeChanged.bind(this);
this.inspector.selection.on("new-node", this.onNewNode);
this.inspector.sidebar.on("fontinspector-selected", this.onNewNode);
this.showAll = this.showAll.bind(this);
@ -36,6 +37,10 @@ FontInspector.prototype = {
this.previewTextChanged = this.previewTextChanged.bind(this);
this.previewInput = this.chromeDoc.getElementById("preview-text-input");
this.previewInput.addEventListener("input", this.previewTextChanged);
// Listen for theme changes as the color of the previews depend on the theme
gDevTools.on("theme-switched", this.onThemeChanged);
this.update();
},
@ -57,6 +62,8 @@ FontInspector.prototype = {
this.showAllButton.removeEventListener("click", this.showAll);
this.previewInput.removeEventListener("input", this.previewTextChanged);
gDevTools.off("theme-switched", this.onThemeChanged);
if (this._previewUpdateTimeout) {
clearTimeout(this._previewUpdateTimeout);
}
@ -103,6 +110,15 @@ FontInspector.prototype = {
}, PREVIEW_UPDATE_DELAY);
},
/**
* Callback for the theme-switched event.
*/
onThemeChanged: function(event, frame) {
if (frame === this.chromeDoc.defaultView) {
this.update(this._lastUpdateShowedAllFonts);
}
},
/**
* Hide the font list. No node are selected.
*/

View File

@ -11,3 +11,4 @@ support-files =
[browser_fontinspector.js]
[browser_fontinspector_edit-previews.js]
[browser_fontinspector_edit-previews-show-all.js]
[browser_fontinspector_theme-change.js]

View File

@ -0,0 +1,53 @@
/* vim: set ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Test that the preview images are updated when the theme changes.
const { getTheme, setTheme } = devtools.require("devtools/shared/theme");
const TEST_URI = BASE_URI + "browser_fontinspector.html";
const originalTheme = getTheme();
registerCleanupFunction(() => {
info(`Restoring theme to '${originalTheme}.`);
setTheme(originalTheme);
});
add_task(function* () {
let { inspector, fontInspector } = yield openFontInspectorForURL(TEST_URI);
let { chromeDoc: doc } = fontInspector;
yield selectNode(".normal-text", inspector);
// Store the original preview URI for later comparison.
let originalURI = doc.querySelector("#all-fonts .font-preview").src;
let newTheme = originalTheme === "light" ? "dark" : "light";
info(`Original theme was '${originalTheme}'.`);
yield setThemeAndWaitForUpdate(newTheme, inspector);
isnot(doc.querySelector("#all-fonts .font-preview").src, originalURI,
"The preview image changed with the theme.");
yield setThemeAndWaitForUpdate(originalTheme, inspector);
is(doc.querySelector("#all-fonts .font-preview").src, originalURI,
"The preview image is correct after the original theme was restored.");
});
/**
* Sets the current theme and waits for fontinspector-updated event.
*
* @param {String} theme - the new theme
* @param {Object} inspector - the inspector panel
*/
function* setThemeAndWaitForUpdate(theme, inspector) {
let onUpdated = inspector.once("fontinspector-updated");
info(`Setting theme to '${theme}'.`);
setTheme(theme);
info("Waiting for font-inspector to update.");
yield onUpdated;
}

View File

@ -6,6 +6,8 @@
* theme on load, and rerenders when changed.
*/
const { setTheme } = devtools.require("devtools/shared/theme");
const LIGHT_BG = "#fcfcfc";
const DARK_BG = "#14171a";
@ -70,19 +72,3 @@ function* spawnTest() {
yield teardown(panel);
finish();
}
/**
* Mimics selecting the theme selector in the toolbox;
* sets the preference and emits an event on gDevTools to trigger
* the themeing.
*/
function setTheme (newTheme) {
let oldTheme = Services.prefs.getCharPref("devtools.theme");
info("Setting `devtools.theme` to \"" + newTheme + "\"");
Services.prefs.setCharPref("devtools.theme", newTheme);
gDevTools.emit("pref-changed", {
pref: "devtools.theme",
newValue: newTheme,
oldValue: oldTheme
});
}

View File

@ -27,6 +27,14 @@ function testGetTheme () {
function testSetTheme () {
let originalTheme = getTheme();
gDevTools.once("pref-changed", (_, { pref, oldValue, newValue }) => {
is(pref, "devtools.theme",
"The 'pref-changed' event triggered by setTheme has correct pref.");
is(oldValue, originalTheme,
"The 'pref-changed' event triggered by setTheme has correct oldValue.");
is(newValue, "dark",
"The 'pref-changed' event triggered by setTheme has correct newValue.");
});
setTheme("dark");
is(Services.prefs.getCharPref("devtools.theme"), "dark", "setTheme() correctly sets dark theme.");
setTheme("light");

View File

@ -85,10 +85,12 @@ const getColor = exports.getColor = (type, theme) => {
* the themeing.
*/
const setTheme = exports.setTheme = (newTheme) => {
let oldTheme = getTheme();
Services.prefs.setCharPref("devtools.theme", newTheme);
gDevTools.emit("pref-changed", {
pref: "devtools.theme",
newValue: newTheme,
oldValue: getTheme()
oldValue: oldTheme
});
};

View File

@ -52,7 +52,6 @@ support-files =
[browser_styleeditor_autocomplete.js]
[browser_styleeditor_autocomplete-disabled.js]
[browser_styleeditor_bug_740541_iframes.js]
skip-if = os == "linux" || "mac" # bug 949355
[browser_styleeditor_bug_851132_middle_click.js]
[browser_styleeditor_bug_870339.js]
[browser_styleeditor_cmd_edit.js]

View File

@ -5,6 +5,8 @@
* Tests that the SVG marker styling is updated when devtools theme changes.
*/
const { setTheme } = devtools.require("devtools/shared/theme");
add_task(function*() {
let { target, panel } = yield initWebAudioEditor(SIMPLE_CONTEXT_URL);
let { panelWin } = panel;
@ -57,19 +59,3 @@ add_task(function*() {
function getFill (el) {
return el.getAttribute("style").match(/(#.*)$/)[1];
}
/**
* Mimics selecting the theme selector in the toolbox;
* sets the preference and emits an event on gDevTools to trigger
* the themeing.
*/
function setTheme (newTheme) {
let oldTheme = Services.prefs.getCharPref("devtools.theme");
info("Setting `devtools.theme` to \"" + newTheme + "\"");
Services.prefs.setCharPref("devtools.theme", newTheme);
gDevTools.emit("pref-changed", {
pref: "devtools.theme",
newValue: newTheme,
oldValue: oldTheme
});
}

View File

@ -74,11 +74,6 @@ share_tweet=Join me for a video conversation on {{clientShortname2}}!
share_button3=Share Link
share_add_service_button=Add a Service
add_to_toolbar_button=Add the Share panel to my toolbar
share_panel_header=Share the web with your friends!
## LOCALIZATION NOTE (share_panel_body): In this item, don't translate the part
## between {{..}}.
share_panel_body={{brandShortname}}'s new Share panel allows you to quickly share links or {{clientSuperShortname}} invitations with your favourite social networks.
copy_url_button2=Copy Link
copied_url_button=Copied!

View File

@ -10,12 +10,17 @@
color: black;
direction: ltr;
-moz-control-character-visibility: visible;
height: 100%;
}
#viewsource {
font-family: -moz-fixed;
font-weight: normal;
white-space: pre;
counter-reset: line;
height: 100%;
box-sizing: border-box;
margin: 0;
padding: 8px;
}
#viewsource.wrap {
white-space: pre-wrap;
@ -24,7 +29,7 @@
pre {
font: inherit;
color: inherit;
white-space: inherit;
white-space: inherit;
margin: 0 0 0 5ch;
}
pre[id]:before,

View File

@ -5,10 +5,10 @@
package org.mozilla.gecko.background.common;
import org.mozilla.gecko.AppConstants;
import org.mozilla.gecko.AppConstants.Versions;
/**
* Preprocessed class for storing preprocessed values common to all
* Android services.
* Constant values common to all Android services.
*/
public class GlobalConstants {
public static final String BROWSER_INTENT_PACKAGE = AppConstants.ANDROID_PACKAGE_NAME;
@ -35,4 +35,80 @@ public class GlobalConstants {
// Common time values.
public static final long MILLISECONDS_PER_DAY = 24 * 60 * 60 * 1000;
public static final long MILLISECONDS_PER_SIX_MONTHS = 180 * MILLISECONDS_PER_DAY;
// Acceptable cipher suites.
/**
* We support only a very limited range of strong cipher suites and protocols:
* no SSLv3 or TLSv1.0 (if we can), no DHE ciphers that might be vulnerable to Logjam
* (https://weakdh.org/), no RC4.
*
* Backstory: Bug 717691 (we no longer support Android 2.2, so the name
* workaround is unnecessary), Bug 1081953, Bug 1061273, Bug 1166839.
*
* See <http://developer.android.com/reference/javax/net/ssl/SSLSocket.html> for
* supported Android versions for each set of protocols and cipher suites.
*
* Note that currently we need to support connections to Sync 1.1 on Mozilla-hosted infra,
* as well as connections to FxA and Sync 1.5 on AWS.
*
* ELB cipher suites:
* <http://docs.aws.amazon.com/ElasticLoadBalancing/latest/DeveloperGuide/elb-security-policy-table.html>
*/
public static final String[] DEFAULT_CIPHER_SUITES;
public static final String[] DEFAULT_PROTOCOLS;
static {
// Prioritize 128 over 256 as a tradeoff between device CPU/battery and the minor
// increase in strength.
if (Versions.feature20Plus) {
DEFAULT_CIPHER_SUITES = new String[]
{
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", // 20+
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", // 20+
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256", // 20+
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA", // 11+
"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", // 20+
"TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384", // 20+
"TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA", // 11+
};
} else if (Versions.feature11Plus) {
DEFAULT_CIPHER_SUITES = new String[]
{
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA", // 11+
"TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA", // 11+
"TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA", // 11+
"TLS_RSA_WITH_AES_256_CBC_SHA", // 9+
};
} else { // 9+
// Fall back to the only half-decent cipher suites supported on Gingerbread.
// N.B., there appears to be *no overlap* between the ELB 2015-05 default
// suites and Gingerbread. A custom configuration is needed if moving beyond
// the 2015-03 defaults.
DEFAULT_CIPHER_SUITES = new String[]
{
// This is for Sync 1.5 on ELB 2015-03.
"TLS_DHE_RSA_WITH_AES_128_CBC_SHA",
"TLS_DHE_DSS_WITH_AES_128_CBC_SHA",
// This is for Sync 1.1.
"TLS_DHE_RSA_WITH_AES_256_CBC_SHA", // 9+
"TLS_RSA_WITH_AES_256_CBC_SHA", // 9+
};
}
if (Versions.feature16Plus) {
DEFAULT_PROTOCOLS = new String[]
{
"TLSv1.2",
"TLSv1.1",
"TLSv1", // We would like to remove this, and will do so when we can.
};
} else {
// Fall back to TLSv1 if there's nothing better.
DEFAULT_PROTOCOLS = new String[]
{
"TLSv1",
};
}
}
}

View File

@ -13,7 +13,6 @@ import org.json.JSONArray;
import android.graphics.PointF;
import android.graphics.RectF;
import android.util.FloatMath;
import android.util.Log;
import java.util.HashMap;
@ -184,10 +183,10 @@ final class DisplayPortCalculator {
float top = metrics.viewportRectTop - margins.top;
float right = metrics.viewportRectRight + margins.right;
float bottom = metrics.viewportRectBottom + margins.bottom;
left = Math.max(metrics.pageRectLeft, TILE_SIZE * FloatMath.floor(left / TILE_SIZE));
top = Math.max(metrics.pageRectTop, TILE_SIZE * FloatMath.floor(top / TILE_SIZE));
right = Math.min(metrics.pageRectRight, TILE_SIZE * FloatMath.ceil(right / TILE_SIZE));
bottom = Math.min(metrics.pageRectBottom, TILE_SIZE * FloatMath.ceil(bottom / TILE_SIZE));
left = (float) Math.max(metrics.pageRectLeft, TILE_SIZE * Math.floor(left / TILE_SIZE));
top = (float) Math.max(metrics.pageRectTop, TILE_SIZE * Math.floor(top / TILE_SIZE));
right = (float) Math.min(metrics.pageRectRight, TILE_SIZE * Math.ceil(right / TILE_SIZE));
bottom = (float) Math.min(metrics.pageRectBottom, TILE_SIZE * Math.ceil(bottom / TILE_SIZE));
return new DisplayPortMetrics(left, top, right, bottom, zoom);
}
@ -747,7 +746,7 @@ final class DisplayPortCalculator {
public boolean drawTimeUpdate(long millis, int pixels) {
// calculate the number of frames it took to draw a viewport-sized area
float normalizedTime = (float)mPixelArea * millis / pixels;
int normalizedFrames = (int)FloatMath.ceil(normalizedTime * 60f / 1000f);
int normalizedFrames = (int) Math.ceil(normalizedTime * 60f / 1000f);
// broaden our range on how long it takes to draw if the draw falls outside
// the range. this allows it to grow gradually. this heuristic may need to
// be tweaked into more of a floating window average or something.

View File

@ -8,8 +8,6 @@ package org.mozilla.gecko.gfx;
import org.json.JSONException;
import org.json.JSONObject;
import android.util.FloatMath;
public class IntSize {
public final int width, height;
@ -75,7 +73,7 @@ public class IntSize {
}
public static int largestPowerOfTwoLessThan(float value) {
int val = (int)FloatMath.floor(value);
int val = (int) Math.floor(value);
if (val <= 0) {
throw new IllegalArgumentException("Error: value must be > 0");
}

View File

@ -21,7 +21,6 @@ import org.mozilla.gecko.util.ThreadUtils;
import android.graphics.PointF;
import android.graphics.RectF;
import android.util.FloatMath;
import android.util.Log;
import android.view.GestureDetector;
import android.view.InputDevice;
@ -668,7 +667,7 @@ class JavaPanZoomController
private float panDistance(MotionEvent move) {
float dx = mX.panDistance(move.getX(0));
float dy = mY.panDistance(move.getY(0));
return FloatMath.sqrt(dx * dx + dy * dy);
return (float) Math.sqrt(dx * dx + dy * dy);
}
private void track(float x, float y, long time) {
@ -801,7 +800,7 @@ class JavaPanZoomController
private float getVelocity() {
float xvel = mX.getRealVelocity();
float yvel = mY.getRealVelocity();
return FloatMath.sqrt(xvel * xvel + yvel * yvel);
return (float) Math.sqrt(xvel * xvel + yvel * yvel);
}
@Override

View File

@ -6,9 +6,6 @@ package org.mozilla.gecko.sync;
import org.mozilla.gecko.AppConstants;
/**
* Preprocessed class for storing preprocessed values specific to Android Sync.
*/
public class SyncConstants {
public static final String GLOBAL_LOG_TAG = "FxSync";
public static final String SYNC_MAJOR_VERSION = "1";

View File

@ -20,6 +20,7 @@ import javax.net.ssl.SSLContext;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.mozilla.gecko.background.common.GlobalConstants;
import org.mozilla.gecko.background.common.log.Logger;
import org.mozilla.gecko.sync.ExtendedJSONObject;
@ -215,7 +216,9 @@ public class BaseResource implements Resource {
private static ClientConnectionManager enableTLSConnectionManager() throws KeyManagementException, NoSuchAlgorithmException {
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, null, new SecureRandom());
SSLSocketFactory sf = new TLSSocketFactory(sslContext);
Logger.debug(LOG_TAG, "Using protocols and cipher suites for Android API " + android.os.Build.VERSION.SDK_INT);
SSLSocketFactory sf = new SSLSocketFactory(sslContext, GlobalConstants.DEFAULT_PROTOCOLS, GlobalConstants.DEFAULT_CIPHER_SUITES, null);
SchemeRegistry schemeRegistry = new SchemeRegistry();
schemeRegistry.register(new Scheme("https", 443, sf));
schemeRegistry.register(new Scheme("http", 80, new PlainSocketFactory()));

View File

@ -10,6 +10,7 @@ import java.net.Socket;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocket;
import org.mozilla.gecko.background.common.GlobalConstants;
import org.mozilla.gecko.background.common.log.Logger;
import ch.boye.httpclientandroidlib.conn.ssl.SSLSocketFactory;
@ -17,18 +18,9 @@ import ch.boye.httpclientandroidlib.params.HttpParams;
public class TLSSocketFactory extends SSLSocketFactory {
private static final String LOG_TAG = "TLSSocketFactory";
private static final String[] DEFAULT_CIPHER_SUITES = new String[] {
"TLS_DHE_RSA_WITH_AES_256_CBC_SHA",
"TLS_DHE_RSA_WITH_AES_128_CBC_SHA",
"SSL_RSA_WITH_RC4_128_SHA", // "RC4_SHA"
};
private static final String[] DEFAULT_PROTOCOLS = new String[] {
"SSLv3",
"TLSv1"
};
// Guarded by `this`.
private static String[] cipherSuites = DEFAULT_CIPHER_SUITES;
private static String[] cipherSuites = GlobalConstants.DEFAULT_CIPHER_SUITES;
public TLSSocketFactory(SSLContext sslContext) {
super(sslContext);
@ -63,7 +55,7 @@ public class TLSSocketFactory extends SSLSocketFactory {
@Override
public Socket createSocket(HttpParams params) throws IOException {
SSLSocket socket = (SSLSocket) super.createSocket(params);
socket.setEnabledProtocols(DEFAULT_PROTOCOLS);
socket.setEnabledProtocols(GlobalConstants.DEFAULT_PROTOCOLS);
setEnabledCipherSuites(socket);
return socket;
}

View File

@ -9,7 +9,6 @@ import org.mozilla.gecko.gfx.LayerView;
import android.app.Instrumentation;
import android.os.SystemClock;
import android.util.FloatMath;
import android.util.Log;
import android.view.MotionEvent;
@ -133,7 +132,7 @@ class MotionEventHelper {
// will trigger the fling.
final float dx = (endX - startX) / 2;
final float dy = (endY - startY) / 2;
float distance = FloatMath.sqrt((dx * dx) + (dy * dy));
float distance = (float) Math.sqrt((dx * dx) + (dy * dy));
final long time = (long)(distance / velocity);
if (time <= 0) {
throw new IllegalArgumentException( "Fling parameters require too small a time period" );

View File

@ -12,18 +12,26 @@ const { AddonManager } = Cu.import("resource://gre/modules/AddonManager.jsm", {}
const { AddonWatcher } = Cu.import("resource://gre/modules/AddonWatcher.jsm", {});
const { PerformanceStats } = Cu.import("resource://gre/modules/PerformanceStats.jsm", {});
const { Services } = Cu.import("resource://gre/modules/Services.jsm", {});
const { Task } = Cu.import("resource://gre/modules/Task.jsm", {});
const UPDATE_TOPIC = "about:performance-update-immediately";
// about:performance observes notifications on this topic.
// if a notification is sent, this causes the page to be updated immediately,
// regardless of whether the page is on pause.
const UPDATE_IMMEDIATELY_TOPIC = "about:performance-update-immediately";
// about:performance posts notifications on this topic whenever the page
// is updated.
const UPDATE_COMPLETE_TOPIC = "about:performance-update-complete";
/**
* The various measures we display.
*/
const MEASURES = [
{key: "longestDuration", percentOfDeltaT: false, label: "Jank level"},
{key: "totalUserTime", percentOfDeltaT: true, label: "User (%)"},
{key: "totalSystemTime", percentOfDeltaT: true, label: "System (%)"},
{key: "totalCPOWTime", percentOfDeltaT: true, label: "Cross-Process (%)"},
{key: "ticks", percentOfDeltaT: false, label: "Activations"},
{probe: "jank", key: "longestDuration", percentOfDeltaT: false, label: "Jank level"},
{probe: "jank", key: "totalUserTime", percentOfDeltaT: true, label: "User (%)"},
{probe: "jank", key: "totalSystemTime", percentOfDeltaT: true, label: "System (%)"},
{probe: "cpow", key: "totalCPOWTime", percentOfDeltaT: true, label: "Cross-Process (%)"},
{probe: "ticks",key: "ticks", percentOfDeltaT: false, label: "Activations"},
];
/**
@ -78,6 +86,8 @@ let AutoUpdate = {
};
let State = {
_monitor: PerformanceStats.getMonitor(["jank", "cpow", "ticks"]),
/**
* @type{PerformanceData}
*/
@ -97,29 +107,31 @@ let State = {
/**
* Fetch the latest information, compute diffs.
*
* @return {object} An object with the following fields:
* @return {Promise}
* @resolve An object with the following fields:
* - `components`: an array of `PerformanceDiff` representing
* the components, sorted by `longestDuration`, then by `totalUserTime`
* - `process`: a `PerformanceDiff` representing the entire process;
* - `deltaT`: the number of milliseconds elapsed since the data
* was last displayed.
*/
update: function () {
let snapshot = PerformanceStats.getSnapshot();
update: Task.async(function*() {
let snapshot = yield this._monitor.promiseSnapshot();
let newData = new Map();
let deltas = [];
for (let componentNew of snapshot.componentsData) {
let {name, addonId, isSystem} = componentNew;
let key = JSON.stringify({name, addonId, isSystem});
let componentOld = State._componentsData.get(key);
deltas.push(componentNew.substract(componentOld));
deltas.push(componentNew.subtract(componentOld));
newData.set(key, componentNew);
}
State._componentsData = newData;
let now = window.performance.now();
let process = snapshot.processData.subtract(State._processData);
let result = {
components: deltas.filter(x => x.ticks > 0),
process: snapshot.processData.substract(State._processData),
components: deltas.filter(x => x.ticks.ticks > 0),
process: snapshot.processData.subtract(State._processData),
deltaT: now - State._date
};
result.components.sort((a, b) => {
@ -134,19 +146,20 @@ let State = {
State._processData = snapshot.processData;
State._date = now;
return result;
}
})
};
function update() {
updateLiveData();
updateSlowAddons();
}
let update = Task.async(function*() {
yield updateLiveData();
yield updateSlowAddons();
Services.obs.notifyObservers(null, UPDATE_COMPLETE_TOPIC, "");
});
/**
* Update the list of slow addons
*/
function updateSlowAddons() {
let updateSlowAddons = Task.async(function*() {
try {
let data = AddonWatcher.alerts;
if (data.size == 0) {
@ -218,12 +231,12 @@ function updateSlowAddons() {
} catch (ex) {
console.error(ex);
}
}
});
/**
* Update the table of live data.
*/
function updateLiveData() {
let updateLiveData = Task.async(function*() {
try {
let dataElt = document.getElementById("liveData");
dataElt.innerHTML = "";
@ -239,9 +252,9 @@ function updateLiveData() {
headerElt.appendChild(el);
}
let deltas = State.update();
let deltas = yield State.update();
for (let item of deltas.components) {
for (let item of [deltas.process, ...deltas.components]) {
let row = document.createElement("tr");
if (item.addonId) {
row.classList.add("addon");
@ -253,13 +266,14 @@ function updateLiveData() {
dataElt.appendChild(row);
// Measures
for (let {key, percentOfDeltaT} of MEASURES) {
for (let {probe, key, percentOfDeltaT} of MEASURES) {
let el = document.createElement("td");
el.classList.add(key);
el.classList.add("contents");
row.appendChild(el);
let value = percentOfDeltaT ? Math.round(item[key] / deltas.deltaT) : item[key];
let rawValue = item[probe][key];
let value = percentOfDeltaT ? Math.round(rawValue / deltas.deltaT) : rawValue;
if (key == "longestDuration") {
value += 1;
el.classList.add("jank" + value);
@ -286,19 +300,21 @@ function updateLiveData() {
} catch (ex) {
console.error(ex);
}
}
});
function go() {
// Compute initial state immediately, then wait a little
// before we start computing diffs and refreshing.
document.getElementById("playButton").addEventListener("click", () => AutoUpdate.start());
document.getElementById("pauseButton").addEventListener("click", () => AutoUpdate.stop());
document.getElementById("intervalDropdown").addEventListener("change", () => AutoUpdate.updateRefreshRate());
// Compute initial state immediately, then wait a little
// before we start computing diffs and refreshing.
State.update();
window.setTimeout(update, 500);
let observer = update;
Services.obs.addObserver(update, UPDATE_TOPIC, false);
window.addEventListener("unload", () => Services.obs.removeObserver(update, UPDATE_TOPIC));
Services.obs.addObserver(update, UPDATE_IMMEDIATELY_TOPIC, false);
window.addEventListener("unload", () => Services.obs.removeObserver(update, UPDATE_IMMEDIATELY_TOPIC));
}

View File

@ -21,29 +21,33 @@ function frameScript() {
});
addMessageListener("aboutperformance-test:hasItems", ({data: title}) => {
Services.obs.notifyObservers(null, "about:performance-update-immediately", "");
let hasPlatform = false;
let hasTitle = false;
let observer = function() {
Services.obs.removeObserver(observer, "about:performance-update-complete");
let hasPlatform = false;
let hasTitle = false;
try {
let eltData = content.document.getElementById("liveData");
if (!eltData) {
return;
try {
let eltData = content.document.getElementById("liveData");
if (!eltData) {
return;
}
// Find if we have a row for "platform"
hasPlatform = eltData.querySelector("tr.platform") != null;
// Find if we have a row for our content page
let titles = [for (eltContent of eltData.querySelectorAll("td.contents.name")) eltContent.textContent];
hasTitle = titles.includes(title);
} catch (ex) {
Cu.reportError("Error in content: " + ex);
Cu.reportError(ex.stack);
} finally {
sendAsyncMessage("aboutperformance-test:hasItems", {hasPlatform, hasTitle});
}
// Find if we have a row for "platform"
hasPlatform = eltData.querySelector("tr.platform") != null;
// Find if we have a row for our content page
let titles = [for (eltContent of eltData.querySelectorAll("td.contents.name")) eltContent.textContent];
hasTitle = titles.includes(title);
} catch (ex) {
Cu.reportError("Error in content: " + ex);
Cu.reportError(ex.stack);
} finally {
sendAsyncMessage("aboutperformance-test:hasItems", {hasPlatform, hasTitle});
}
Services.obs.addObserver(observer, "about:performance-update-complete", false);
Services.obs.notifyObservers(null, "about:performance-update-immediately", "");
});
}

View File

@ -13,6 +13,8 @@ Cu.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Preferences",
"resource://gre/modules/Preferences.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Task",
"resource://gre/modules/Task.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "console",
"resource://gre/modules/devtools/Console.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "PerformanceStats",
@ -23,7 +25,10 @@ XPCOMUtils.defineLazyServiceGetter(this, "Telemetry",
XPCOMUtils.defineLazyModuleGetter(this, "Services",
"resource://gre/modules/Services.jsm");
const FILTERS = ["longestDuration", "totalCPOWTime"];
const FILTERS = [
{probe: "jank", field: "longestDuration"},
{probe: "cpow", field: "totalCPOWTime"},
];
let AddonWatcher = {
_previousPerformanceIndicators: {},
@ -35,6 +40,12 @@ let AddonWatcher = {
_stats: new Map(),
_timer: Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer),
_callback: null,
/**
* A performance monitor used to pull data from SpiderMonkey.
*
* @type {PerformanceStats Monitor}
*/
_monitor: null,
/**
* The interval at which we poll the available performance information
* to find out about possibly slow add-ons, in milliseconds.
@ -103,8 +114,13 @@ let AddonWatcher = {
}
if (isPaused) {
this._timer.cancel();
if (this._monitor) {
// We don't need the probes anymore, release them.
this._monitor.dispose();
}
this._monitor = null;
} else {
PerformanceStats.init();
this._monitor = PerformanceStats.getMonitor([for (filter of FILTERS) filter.probe]);
this._timer.initWithCallback(this._checkAddons.bind(this), this._interval, Ci.nsITimer.TYPE_REPEATING_SLACK);
}
this._isPaused = isPaused;
@ -123,94 +139,96 @@ let AddonWatcher = {
* slice.
*/
_checkAddons: function() {
try {
let snapshot = PerformanceStats.getSnapshot();
return Task.spawn(function*() {
try {
let snapshot = yield this._monitor.promiseSnapshot();
let limits = {
// By default, warn if we have a total time of 1s of CPOW per 15 seconds
totalCPOWTime: Math.round(Preferences.get("browser.addon-watch.limits.totalCPOWTime", 1000000) * this._interval / 15000),
// By default, warn if we have skipped 4 consecutive frames
// at least once during the latest slice.
longestDuration: Math.round(Math.log2(Preferences.get("browser.addon-watch.limits.longestDuration", 128))),
};
let limits = {
// By default, warn if we have a total time of 1s of CPOW per 15 seconds
totalCPOWTime: Math.round(Preferences.get("browser.addon-watch.limits.totalCPOWTime", 1000000) * this._interval / 15000),
// By default, warn if we have skipped 4 consecutive frames
// at least once during the latest slice.
longestDuration: Math.round(Math.log2(Preferences.get("browser.addon-watch.limits.longestDuration", 128))),
};
// By default, warn only after an add-on has been spotted misbehaving 3 times.
let tolerance = Preferences.get("browser.addon-watch.tolerance", 3);
// By default, warn only after an add-on has been spotted misbehaving 3 times.
let tolerance = Preferences.get("browser.addon-watch.tolerance", 3);
for (let item of snapshot.componentsData) {
let addonId = item.addonId;
if (!item.isSystem || !addonId) {
// We are only interested in add-ons.
continue;
}
if (this._ignoreList.has(addonId)) {
// This add-on has been explicitly put in the ignore list
// by the user. Don't waste time with it.
continue;
}
let previous = this._previousPerformanceIndicators[addonId];
this._previousPerformanceIndicators[addonId] = item;
for (let item of snapshot.componentsData) {
let addonId = item.addonId;
if (!item.isSystem || !addonId) {
// We are only interested in add-ons.
continue;
}
if (this._ignoreList.has(addonId)) {
// This add-on has been explicitly put in the ignore list
// by the user. Don't waste time with it.
continue;
}
let previous = this._previousPerformanceIndicators[addonId];
this._previousPerformanceIndicators[addonId] = item;
if (!previous) {
// This is the first time we see the addon, so we are probably
// executed right during/after startup. Performance is always
// weird during startup, with the JIT warming up, competition
// in disk access, etc. so we do not take this as a reason to
// display the slow addon warning.
continue;
}
// Report misbehaviors to Telemetry
let diff = item.substract(previous);
if (diff.longestDuration > 5) {
Telemetry.getKeyedHistogramById("MISBEHAVING_ADDONS_JANK_LEVEL").
add(addonId, diff.longestDuration);
}
if (diff.totalCPOWTime > 0) {
Telemetry.getKeyedHistogramById("MISBEHAVING_ADDONS_CPOW_TIME_MS").
add(addonId, diff.totalCPOWTime / 1000);
}
// Store misbehaviors for about:performance and other clients
let stats = this._stats.get(addonId);
if (!stats) {
stats = {
peaks: {},
alerts: {},
};
this._stats.set(addonId, stats);
}
// Report misbehaviors to the user.
for (let filter of FILTERS) {
let peak = stats.peaks[filter] || 0;
stats.peaks[filter] = Math.max(diff[filter], peak);
if (limits[filter] <= 0 || diff[filter] <= limits[filter]) {
if (!previous) {
// This is the first time we see the addon, so we are probably
// executed right during/after startup. Performance is always
// weird during startup, with the JIT warming up, competition
// in disk access, etc. so we do not take this as a reason to
// display the slow addon warning.
continue;
}
stats.alerts[filter] = (stats.alerts[filter] || 0) + 1;
// Report misbehaviors to Telemetry
if (stats.alerts[filter] % tolerance != 0) {
continue;
let diff = item.subtract(previous);
if ("jank" in diff && diff.jank.longestDuration > 5) {
Telemetry.getKeyedHistogramById("MISBEHAVING_ADDONS_JANK_LEVEL").
add(addonId, diff.jank.longestDuration);
}
if ("cpow" in diff && diff.cpow.totalCPOWTime > 0) {
Telemetry.getKeyedHistogramById("MISBEHAVING_ADDONS_CPOW_TIME_MS").
add(addonId, diff.cpow.totalCPOWTime / 1000);
}
try {
this._callback(addonId, filter);
} catch (ex) {
Cu.reportError("Error in AddonWatcher._checkAddons callback " + ex);
Cu.reportError(ex.stack);
// Store misbehaviors for about:performance and other clients
let stats = this._stats.get(addonId);
if (!stats) {
stats = {
peaks: {},
alerts: {},
};
this._stats.set(addonId, stats);
}
// Report misbehaviors to the user.
for (let {probe, field: filter} of FILTERS) {
let peak = stats.peaks[filter] || 0;
let value = diff[probe][filter];
stats.peaks[filter] = Math.max(value, peak);
if (limits[filter] <= 0 || value <= limits[filter]) {
continue;
}
stats.alerts[filter] = (stats.alerts[filter] || 0) + 1;
if (stats.alerts[filter] % tolerance != 0) {
continue;
}
try {
this._callback(addonId, filter);
} catch (ex) {
Cu.reportError("Error in AddonWatcher._checkAddons callback " + ex);
Cu.reportError(ex.stack);
}
}
}
} catch (ex) {
Cu.reportError("Error in AddonWatcher._checkAddons " + ex);
Cu.reportError(Task.Debugging.generateReadableStack(ex.stack));
}
} catch (ex) {
Cu.reportError("Error in AddonWatcher._checkAddons " + ex);
Cu.reportError(ex.stack);
}
}.bind(this));
},
ignoreAddonForSession: function(addonid) {
this._ignoreList.add(addonid);

View File

@ -12,29 +12,425 @@ const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
/**
* API for querying and examining performance data.
*
* The data exposed by this API is computed internally by the JavaScript VM.
* See `PerformanceData` for the detail of the information provided by this
* API.
* This API exposes data from several probes implemented by the JavaScript VM.
* See `PerformanceStats.getMonitor()` for information on how to monitor data
* from one or more probes and `PerformanceData` for the information obtained
* from the probes.
*
* Data is collected by "Performance Group". Typically, a Performance Group
* is an add-on, or a frame, or the internals of the application.
*/
Cu.import("resource://gre/modules/XPCOMUtils.jsm", this);
Cu.import("resource://gre/modules/Services.jsm", this);
let performanceStatsService =
Cc["@mozilla.org/toolkit/performance-stats-service;1"].
getService(Ci.nsIPerformanceStatsService);
// The nsIPerformanceStatsService provides lower-level
// access to SpiderMonkey and the probes.
XPCOMUtils.defineLazyServiceGetter(this, "performanceStatsService",
"@mozilla.org/toolkit/performance-stats-service;1",
Ci.nsIPerformanceStatsService);
// The finalizer lets us automatically release (and when possible deactivate)
// probes when a monitor is garbage-collected.
XPCOMUtils.defineLazyServiceGetter(this, "finalizer",
"@mozilla.org/toolkit/finalizationwitness;1",
Ci.nsIFinalizationWitnessService
);
const PROPERTIES_NUMBERED = ["totalUserTime", "totalSystemTime", "totalCPOWTime", "ticks"];
// The topic used to notify that a PerformanceMonitor has been garbage-collected
// and that we can release/close the probes it holds.
const FINALIZATION_TOPIC = "performancemonitor-finalize";
const PROPERTIES_META_IMMUTABLE = ["name", "addonId", "isSystem"];
const PROPERTIES_META = [...PROPERTIES_META_IMMUTABLE, "windowId", "title"];
const PROPERTIES_FLAT = [...PROPERTIES_NUMBERED, ...PROPERTIES_META];
/**
* Information on a single component.
* Access to a low-level performance probe.
*
* Each probe is dedicated to some form of performance monitoring.
* As each probe may have a performance impact, a probe is activated
* only when a client has requested a PerformanceMonitor for this probe,
* and deactivated once all clients are disposed of.
*/
function Probe(name, impl) {
this._name = name;
this._counter = 0;
this._impl = impl;
}
Probe.prototype = {
/**
* Acquire the probe on behalf of a client.
*
* If the probe was inactive, activate it. Note that activating a probe
* can incur a memory or performance cost.
*/
acquire: function() {
if (this._counter == 0) {
this._impl.isActive = true;
}
this._counter++;
},
/**
* Release the probe on behalf of a client.
*
* If this was the last client for this probe, deactivate it.
*/
release: function() {
this._counter--;
if (this._counter == 0) {
this._impl.isActive = false;
}
},
/**
* Obtain data from this probe, once it is available.
*
* @param {nsIPerformanceStats} xpcom A xpcom object obtained from
* SpiderMonkey. Only the fields updated by the low-level probe
* are in a specified state.
* @return {object} An object containing the data extracted from this
* probe. Actual format depends on the probe.
*/
extract: function(xpcom) {
if (!this._impl.isActive) {
throw new Error(`Probe is inactive: ${this._name}`);
}
return this._impl.extract(xpcom);
},
/**
* @param {object} a An object returned by `this.extract()`.
* @param {object} b An object returned by `this.extract()`.
*
* @return {true} If `a` and `b` hold identical values.
*/
isEqual: function(a, b) {
if (a == null && b == null) {
return true;
}
if (a != null && b != null) {
return this._impl.isEqual(a, b);
}
return false;
},
/**
* @param {object} a An object returned by `this.extract()`. May
* NOT be `null`.
* @param {object} b An object returned by `this.extract()`. May
* be `null`.
*
* @return {object} An object representing `a - b`. If `b` is
* `null`, this is `a`.
*/
substract: function(a, b) {
if (a == null) {
throw new TypeError();
}
if (b == null) {
return a;
}
return this._impl.substract(a, b);
},
/**
* The name of the probe.
*/
get name() {
return this._name;
}
};
// Utility function. Return the position of the last non-0 item in an
// array, or -1 if there isn't any such item.
function lastNonZero(array) {
for (let i = array.length - 1; i >= 0; --i) {
if (array[i] != 0) {
return i;
}
}
return -1;
}
/**
* The actual Probes implemented by SpiderMonkey.
*/
let Probes = {
/**
* A probe measuring jank.
*
* Data provided by this probe uses the following format:
*
* @field {number} totalCPUTime The total amount of time spent using the
* CPU for this performance group, in µs.
* @field {number} totalSystemTime The total amount of time spent in the
* kernel for this performance group, in µs.
* @field {Array<number>} durations An array containing at each position `i`
* the number of times execution of this component has lasted at least `2^i`
* milliseconds.
* @field {number} longestDuration The index of the highest non-0 value in
* `durations`.
*/
jank: new Probe("jank", {
set isActive(x) { /* always active in the current implementation */ },
get isActive() { return true; },
extract: function(xpcom) {
let durations = xpcom.getDurations();
return {
totalUserTime: xpcom.totalUserTime,
totalSystemTime: xpcom.totalSystemTime,
durations: durations,
longestDuration: lastNonZero(durations)
}
},
isEqual: function(a, b) {
// invariant: `a` and `b` are both non-null
if (a.totalUserTime != b.totalUserTime) {
return false;
}
if (a.totalSystemTime != b.totalSystemTime) {
return false;
}
for (let i = 0; i < a.durations.length; ++i) {
if (a.durations[i] != b.durations[i]) {
return false;
}
}
return true;
},
substract: function(a, b) {
// invariant: `a` and `b` are both non-null
let result = {
totalUserTime: a.totalUserTime - b.totalUserTime,
totalSystemTime: a.totalSystemTime - b.totalSystemTime,
durations: [],
longestDuration: -1,
};
for (let i = 0; i < a.durations.length; ++i) {
result.durations[i] = a.durations[i] - b.durations[i];
}
result.longestDuration = lastNonZero(result.durations);
return result;
}
}),
/**
* A probe measuring CPOW activity.
*
* Data provided by this probe uses the following format:
*
* @field {number} totalCPOWTime The amount of wallclock time
* spent executing blocking cross-process calls, in µs.
*/
cpow: new Probe("cpow", {
set isActive(x) { /* always active in the current implementation */ },
get isActive() { return true; },
extract: function(xpcom) {
return {
totalCPOWTime: xpcom.totalCPOWTime
};
},
isEqual: function(a, b) {
return a.totalCPOWTime == b.totalCPOWTime;
},
substract: function(a, b) {
return {
totalCPOWTime: a.totalCPOWTime - b.totalCPOWTime
};
}
}),
/**
* A probe measuring activations, i.e. the number
* of times code execution has entered a given
* PerformanceGroup.
*
* Note that this probe is always active.
*
* Data provided by this probe uses the following format:
* @type {number} ticks The number of times execution has entered
* this performance group.
*/
ticks: new Probe("ticks", {
set isActive(x) { /* this probe cannot be deactivated */ },
get isActive() { return true; },
extract: function(xpcom) {
return {
ticks: xpcom.ticks
};
},
isEqual: function(a, b) {
return a.ticks == b.ticks;
},
substract: function(a, b) {
return {
ticks: a.ticks - b.ticks
};
}
}),
};
/**
* A monitor for a set of probes.
*
* Keeping probes active when they are unused is often a bad
* idea for performance reasons. Upon destruction, or whenever
* a client calls `dispose`, this monitor releases the probes,
* which may let the system deactivate them.
*/
function PerformanceMonitor(probes) {
this._probes = probes;
// Activate low-level features as needed
for (let probe of probes) {
probe.acquire();
}
// A finalization witness. At some point after the garbage-collection of
// `this` object, a notification of `FINALIZATION_TOPIC` will be triggered
// with `id` as message.
this._id = PerformanceMonitor.makeId();
this._finalizer = finalizer.make(FINALIZATION_TOPIC, this._id)
PerformanceMonitor._monitors.set(this._id, probes);
}
PerformanceMonitor.prototype = {
/**
* Return asynchronously a snapshot with the data
* for each probe monitored by this PerformanceMonitor.
*
* All numeric values are non-negative and can only increase. Depending on
* the probe and the underlying operating system, probes may not be available
* immediately and may miss some activity.
*
* Clients should NOT expect that the first call to `promiseSnapshot()`
* will return a `Snapshot` in which all values are 0. For most uses,
* the appropriate scenario is to perform a first call to `promiseSnapshot()`
* to obtain a baseline, and then watch evolution of the values by calling
* `promiseSnapshot()` and `substract()`.
*
* On the other hand, numeric values are also monotonic across several instances
* of a PerformanceMonitor with the same probes.
* let a = PerformanceStats.getMonitor(someProbes);
* let snapshot1 = yield a.promiseSnapshot();
*
* // ...
* let b = PerformanceStats.getMonitor(someProbes); // Same list of probes
* let snapshot2 = yield b.promiseSnapshot();
*
* // all values of `snapshot2` are greater or equal to values of `snapshot1`.
*
* @return {Promise}
* @resolve {Snapshot}
*/
promiseSnapshot: function() {
if (!this._finalizer) {
throw new Error("dispose() has already been called, this PerformanceMonitor is not usable anymore");
}
// Current implementation is actually synchronous.
return Promise.resolve().then(() => new Snapshot({
xpcom: performanceStatsService.getSnapshot(),
probes: this._probes
}));
},
/**
* Release the probes used by this monitor.
*
* Releasing probes as soon as they are unused is a good idea, as some probes
* cost CPU and/or memory.
*/
dispose: function() {
if (!this._finalizer) {
return;
}
this._finalizer.forget();
PerformanceMonitor.dispose(this._id);
// As a safeguard against double-release, reset everything to `null`
this._probes = null;
this._id = null;
this._finalizer = null;
}
};
/**
* @type {Map<string, Array<string>>} A map from id (as produced by `makeId`)
* to list of probes. Used to deallocate a list of probes during finalization.
*/
PerformanceMonitor._monitors = new Map();
/**
* Create a `PerformanceMonitor` for a list of probes, register it for
* finalization.
*/
PerformanceMonitor.make = function(probeNames) {
// Sanity checks
if (!Array.isArray(probeNames)) {
throw new TypeError("Expected an array, got " + probes);
}
let probes = [];
for (let probeName of probeNames) {
if (!(probeName in Probes)) {
throw new TypeError("Probe not implemented: " + k);
}
probes.push(Probes[probeName]);
}
return new PerformanceMonitor(probes);
};
/**
* Implementation of `dispose`.
*
* The actual implementation of `dispose` is as a method of `PerformanceMonitor`,
* rather than `PerformanceMonitor.prototype`, to avoid needing a strong reference
* to instances of `PerformanceMonitor`, which would defeat the purpose of
* finalization.
*/
PerformanceMonitor.dispose = function(id) {
let probes = PerformanceMonitor._monitors.get(id);
if (!probes) {
throw new TypeError("`dispose()` has already been called on this monitor");
}
PerformanceMonitor._monitors.delete(id);
for (let probe of probes) {
probe.release();
}
}
// Generate a unique id for each PerformanceMonitor. Used during
// finalization.
PerformanceMonitor._counter = 0;
PerformanceMonitor.makeId = function() {
return "PerformanceMonitor-" + (this._counter++);
}
// Once a `PerformanceMonitor` has been garbage-collected,
// release the probes unless `dispose()` has already been called.
Services.obs.addObserver(function(subject, topic, value) {
PerformanceMonitor.dispose(value);
}, FINALIZATION_TOPIC, false);
// Public API
this.PerformanceStats = {
/**
* Create a monitor for observing a set of performance probes.
*/
getMonitor: function(probes) {
return PerformanceMonitor.make(probes);
}
};
/**
* Information on a single performance group.
*
* This offers the following fields:
*
* @field {string} name The name of the component:
* @field {string} name The name of the performance group:
* - for the process itself, "<process>";
* - for platform code, "<platform>";
* - for an add-on, the identifier of the addon (e.g. "myaddon@foo.bar");
@ -54,28 +450,22 @@ const PROPERTIES_FLAT = [...PROPERTIES_NUMBERED, ...PROPERTIES_META];
* @field {boolean} isSystem `true` if the component is a system component (i.e.
* an add-on or platform-code), `false` otherwise (i.e. a webpage).
*
* @field {number} totalUserTime The total amount of time spent executing code.
* @field {object|undefined} activations See the documentation of probe "ticks".
* `undefined` if this probe is not active.
*
* @field {number} totalSystemTime The total amount of time spent executing
* system calls.
* @field {object|undefined} jank See the documentation of probe "jank".
* `undefined` if this probe is not active.
*
* @field {number} totalCPOWTime The total amount of time spent waiting for
* blocking cross-process communications
*
* @field {number} ticks The number of times the JavaScript VM entered the code
* of this component to execute it.
*
* @field {Array<number>} durations An array containing at each position `i`
* the number of times execution of this component has lasted at least `2^i`
* milliseconds.
*
* All numeric values are non-negative and can only increase.
* @field {object|undefined} cpow See the documentation of probe "cpow".
* `undefined` if this probe is not active.
*/
function PerformanceData(xpcom) {
for (let k of PROPERTIES_FLAT) {
function PerformanceData({xpcom, probes}) {
for (let k of PROPERTIES_META) {
this[k] = xpcom[k];
}
this.durations = xpcom.getDurations();
for (let probe of probes) {
this[probe.name] = probe.extract(xpcom);
}
}
PerformanceData.prototype = {
/**
@ -87,17 +477,12 @@ PerformanceData.prototype = {
if (!(to instanceof PerformanceData)) {
throw new TypeError();
}
for (let k of PROPERTIES_FLAT) {
if (this[k] != to[k]) {
for (let probeName of Object.keys(Probes)) {
let probe = Probes[probeName];
if (!probe.isEqual(this[probeName], to[probeName])) {
return false;
}
}
for (let i = 0; i < this.durations.length; ++i) {
if (to.durations[i] != this.durations[i]) {
return false;
}
}
return true;
},
/**
@ -108,7 +493,7 @@ PerformanceData.prototype = {
*
* @return {PerformanceDiff} The performance usage between `to` and `this`.
*/
substract: function(to = null) {
subtract: function(to = null) {
return new PerformanceDiff(this, to);
}
};
@ -117,57 +502,16 @@ PerformanceData.prototype = {
* The delta between two instances of `PerformanceData`.
*
* Used to monitor resource usage between two timestamps.
*
* @field {number} longestDuration An indication of the longest
* execution duration between two timestamps:
* - -1 == less than 1ms
* - 0 == [1, 2[ ms
* - 1 == [2, 4[ ms
* - 3 == [4, 8[ ms
* - 4 == [8, 16[ ms
* - ...
* - 7 == [128, ...] ms
*/
function PerformanceDiff(current, old = null) {
for (let k of PROPERTIES_META) {
this[k] = current[k];
}
if (old) {
if (!(old instanceof PerformanceData)) {
throw new TypeError();
}
if (current.durations.length != old.durations.length) {
throw new TypeError("Internal error: mismatched length for `durations`.");
}
this.durations = [];
this.longestDuration = -1;
for (let i = 0; i < current.durations.length; ++i) {
let delta = current.durations[i] - old.durations[i];
this.durations[i] = delta;
if (delta > 0) {
this.longestDuration = i;
}
}
for (let k of PROPERTIES_NUMBERED) {
this[k] = current[k] - old[k];
}
} else {
this.durations = current.durations.slice(0);
for (let k of PROPERTIES_NUMBERED) {
this[k] = current[k];
}
this.longestDuration = -1;
for (let i = this.durations.length - 1; i >= 0; --i) {
if (this.durations[i] > 0) {
this.longestDuration = i;
break;
}
for (let probeName of Object.keys(Probes)) {
let other = old ? old[probeName] : null;
if (probeName in current) {
this[probeName] = Probes[probeName].substract(current[probeName], other);
}
}
}
@ -175,41 +519,16 @@ function PerformanceDiff(current, old = null) {
/**
* A snapshot of the performance usage of the process.
*/
function Snapshot(xpcom) {
function Snapshot({xpcom, probes}) {
this.componentsData = [];
let enumeration = xpcom.getComponentsData().enumerate();
while (enumeration.hasMoreElements()) {
let stat = enumeration.getNext().QueryInterface(Ci.nsIPerformanceStats);
this.componentsData.push(new PerformanceData(stat));
this.componentsData.push(new PerformanceData({xpcom: stat, probes}));
}
this.processData = new PerformanceData(xpcom.getProcessData());
this.processData = new PerformanceData({xpcom: xpcom.getProcessData(), probes});
}
this.PerformanceStats = {
/**
* Activate monitoring.
*/
init() {
//
// The implementation actually does nothing, as monitoring is
// initiated when loading the module.
//
// This function is actually provided as a gentle way to ensure
// that client code that imports `PerformanceStats` lazily
// does not forget to force the import, hence triggering
// actual load of the module.
//
},
/**
* Get a snapshot of the performance usage of the current process.
*
* @type {Snapshot}
*/
getSnapshot() {
return new Snapshot(performanceStatsService.getSnapshot());
},
};
// In the current implementation, all probes are always active.
performanceStatsService.isStopwatchActive = true;

View File

@ -29,9 +29,13 @@ function frameScript() {
// Make sure that the stopwatch is now active.
performanceStatsService.isStopwatchActive = true;
let monitor = PerformanceStats.getMonitor(["jank", "cpow", "ticks"]);
addMessageListener("compartments-test:getStatistics", () => {
try {
sendAsyncMessage("compartments-test:getStatistics", PerformanceStats.getSnapshot());
monitor.promiseSnapshot().then(snapshot => {
sendAsyncMessage("compartments-test:getStatistics", snapshot);
});
} catch (ex) {
Cu.reportError("Error in content: " + ex);
Cu.reportError(ex.stack);
@ -99,20 +103,25 @@ function monotinicity_tester(source, testName) {
for (let k of ["name", "addonId", "isSystem"]) {
SilentAssert.equal(prev[k], next[k], `Sanity check (${testName}): ${k} hasn't changed.`);
}
for (let k of ["totalUserTime", "totalSystemTime", "totalCPOWTime", "ticks"]) {
SilentAssert.equal(typeof next[k], "number", `Sanity check (${testName}): ${k} is a number.`);
SilentAssert.leq(prev[k], next[k], `Sanity check (${testName}): ${k} is monotonic.`);
SilentAssert.leq(0, next[k], `Sanity check (${testName}): ${k} is >= 0.`)
for (let [probe, k] of [
["jank", "totalUserTime"],
["jank", "totalSystemTime"],
["cpow", "totalCPOWTime"],
["ticks", "ticks"]
]) {
SilentAssert.equal(typeof next[probe][k], "number", `Sanity check (${testName}): ${k} is a number.`);
SilentAssert.leq(prev[probe][k], next[probe][k], `Sanity check (${testName}): ${k} is monotonic.`);
SilentAssert.leq(0, next[probe][k], `Sanity check (${testName}): ${k} is >= 0.`)
}
SilentAssert.equal(prev.durations.length, next.durations.length);
for (let i = 0; i < next.durations.length; ++i) {
SilentAssert.ok(typeof next.durations[i] == "number" && next.durations[i] >= 0,
SilentAssert.equal(prev.jank.durations.length, next.jank.durations.length);
for (let i = 0; i < next.jank.durations.length; ++i) {
SilentAssert.ok(typeof next.jank.durations[i] == "number" && next.jank.durations[i] >= 0,
`Sanity check (${testName}): durations[${i}] is a non-negative number.`);
SilentAssert.leq(prev.durations[i], next.durations[i],
`Sanity check (${testName}): durations[${i}] is monotonic.`)
SilentAssert.leq(prev.jank.durations[i], next.jank.durations[i],
`Sanity check (${testName}): durations[${i}] is monotonic.`);
}
for (let i = 0; i < next.durations.length - 1; ++i) {
SilentAssert.leq(next.durations[i + 1], next.durations[i],
for (let i = 0; i < next.jank.durations.length - 1; ++i) {
SilentAssert.leq(next.jank.durations[i + 1], next.jank.durations[i],
`Sanity check (${testName}): durations[${i}] >= durations[${i + 1}].`)
}
};
@ -138,8 +147,12 @@ function monotinicity_tester(source, testName) {
let set = new Set();
let map = new Map();
for (let item of snapshot.componentsData) {
for (let k of ["totalUserTime", "totalSystemTime", "totalCPOWTime"]) {
SilentAssert.leq(item[k], snapshot.processData[k],
for (let [probe, k] of [
["jank", "totalUserTime"],
["jank", "totalSystemTime"],
["cpow", "totalCPOWTime"]
]) {
SilentAssert.leq(item[probe][k], snapshot.processData[probe][k],
`Sanity check (${testName}): component has a lower ${k} than process`);
}
@ -170,8 +183,10 @@ function monotinicity_tester(source, testName) {
}
add_task(function* test() {
let monitor = PerformanceStats.getMonitor(["jank", "cpow", "ticks"]);
info("Extracting initial state");
let stats0 = PerformanceStats.getSnapshot();
let stats0 = yield monitor.promiseSnapshot();
Assert.notEqual(stats0.componentsData.length, 0, "There is more than one component");
Assert.ok(!stats0.componentsData.find(stat => stat.name.indexOf(URL) != -1),
"The url doesn't appear yet");
@ -189,7 +204,7 @@ add_task(function* test() {
info("Deactivating sanity checks under Windows (bug 1151240)");
} else {
info("Setting up sanity checks");
monotinicity_tester(() => PerformanceStats.getSnapshot(), "parent process");
monotinicity_tester(() => monitor.promiseSnapshot(), "parent process");
monotinicity_tester(() => promiseContentResponseOrNull(browser, "compartments-test:getStatistics", null), "content process" );
}
@ -230,11 +245,11 @@ add_task(function* test() {
info("Searching by title, we didn't find the main frame");
continue;
}
info("Searching by title, we found the main frame");
info(`Total user time: ${parent.totalUserTime}`);
if (skipTotalUserTime || parent.totalUserTime > 1000) {
if (skipTotalUserTime || parent.jank.totalUserTime > 1000) {
break;
} else {
info(`Not enough CPU time detected: ${parent.jank.totalUserTime}`)
}
}

View File

@ -10,14 +10,12 @@ function run_test() {
run_next_test();
}
let monitor = PerformanceStats.getMonitor(["ticks", "jank", "cpow"]);
let promiseStatistics = Task.async(function*(name) {
yield Promise.resolve(); // Make sure that we wait until
// statistics have been updated.
let snapshot = PerformanceStats.getSnapshot();
do_print("Statistics: " + name);
do_print(JSON.stringify(snapshot.processData, null, "\t"));
do_print(JSON.stringify(snapshot.componentsData, null, "\t"));
return snapshot;
return monitor.promiseSnapshot();
});
let promiseSetMonitoring = Task.async(function*(to) {
@ -105,13 +103,13 @@ add_task(function* test_measure() {
if (skipPrecisionTests) {
do_print("Skipping totalUserTime check under Windows XP, as timer is not always updated by the OS.")
} else {
Assert.ok(process2.totalUserTime - process1.totalUserTime >= 10000, `At least 10ms counted for process time (${process2.totalUserTime - process1.totalUserTime})`);
Assert.ok(process2.jank.totalUserTime - process1.jank.totalUserTime >= 10000, `At least 10ms counted for process time (${process2.jank.totalUserTime - process1.jank.totalUserTime})`);
}
Assert.equal(process2.totalCPOWTime, process1.totalCPOWTime, "We haven't used any CPOW time during the first burn");
Assert.equal(process4.totalUserTime, process3.totalUserTime, "After deactivating the stopwatch, we didn't count any time");
Assert.equal(process4.totalCPOWTime, process3.totalCPOWTime, "After deactivating the stopwatch, we didn't count any CPOW time");
Assert.equal(process2.jank.totalCPOWTime, process1.jank.totalCPOWTime, "We haven't used any CPOW time during the first burn");
Assert.equal(process4.jank.totalUserTime, process3.jank.totalUserTime, "After deactivating the stopwatch, we didn't count any time");
Assert.equal(process4.jank.totalCPOWTime, process3.jank.totalCPOWTime, "After deactivating the stopwatch, we didn't count any CPOW time");
let builtin1 = getBuiltinStatistics(stats1) || { totalUserTime: 0, totalCPOWTime: 0 };
let builtin1 = getBuiltinStatistics(stats1) || { jank: { totalUserTime: 0 }, cpow: { totalCPOWTime: 0 } };
let builtin2 = getBuiltinStatistics(stats2);
let builtin3 = getBuiltinStatistics(stats3);
let builtin4 = getBuiltinStatistics(stats4);
@ -122,10 +120,10 @@ add_task(function* test_measure() {
if (skipPrecisionTests) {
do_print("Skipping totalUserTime check under Windows XP, as timer is not always updated by the OS.")
} else {
Assert.ok(builtin2.totalUserTime - builtin1.totalUserTime >= 10000, `At least 10ms counted for built-in statistics (${builtin2.totalUserTime - builtin1.totalUserTime})`);
Assert.ok(builtin2.jank.totalUserTime - builtin1.jank.totalUserTime >= 10000, `At least 10ms counted for built-in statistics (${builtin2.jank.totalUserTime - builtin1.jank.totalUserTime})`);
}
Assert.equal(builtin2.totalCPOWTime, builtin1.totalCPOWTime, "We haven't used any CPOW time during the first burn for the built-in");
Assert.equal(builtin2.totalCPOWTime, builtin1.totalCPOWTime, "No CPOW for built-in statistics");
Assert.equal(builtin4.totalUserTime, builtin3.totalUserTime, "After deactivating the stopwatch, we didn't count any time for the built-in");
Assert.equal(builtin4.totalCPOWTime, builtin3.totalCPOWTime, "After deactivating the stopwatch, we didn't count any CPOW time for the built-in");
Assert.equal(builtin2.jank.totalCPOWTime, builtin1.jank.totalCPOWTime, "We haven't used any CPOW time during the first burn for the built-in");
Assert.equal(builtin2.jank.totalCPOWTime, builtin1.jank.totalCPOWTime, "No CPOW for built-in statistics");
Assert.equal(builtin4.jank.totalUserTime, builtin3.jank.totalUserTime, "After deactivating the stopwatch, we didn't count any time for the built-in");
Assert.equal(builtin4.jank.totalCPOWTime, builtin3.jank.totalCPOWTime, "After deactivating the stopwatch, we didn't count any CPOW time for the built-in");
});

View File

@ -461,7 +461,7 @@ let Impl = {
creationDate: (Policy.now()).toISOString(),
version: PING_FORMAT_VERSION,
application: this._getApplicationSection(),
payload: aPayload,
payload: payload,
};
if (aOptions.addClientId) {

View File

@ -19,6 +19,7 @@ const protocol = require("devtools/server/protocol");
const {Arg, Option, method, RetVal, types} = protocol;
const {LongStringActor, ShortLongString} = require("devtools/server/actors/string");
const {fetch} = require("devtools/toolkit/DevToolsUtils");
const {listenOnce} = require("devtools/async-utils");
loader.lazyGetter(this, "CssLogic", () => require("devtools/styleinspector/css-logic").CssLogic);
@ -75,56 +76,29 @@ let StyleSheetsActor = exports.StyleSheetsActor = protocol.ActorClass({
* Protocol method for getting a list of StyleSheetActors representing
* all the style sheets in this document.
*/
getStyleSheets: method(function() {
let deferred = promise.defer();
getStyleSheets: method(Task.async(function* () {
let documents = [this.document];
let actors = [];
let window = this.window;
var domReady = () => {
window.removeEventListener("DOMContentLoaded", domReady, true);
this._addAllStyleSheets().then(deferred.resolve, Cu.reportError);
};
for (let doc of documents) {
let sheets = yield this._addStyleSheets(doc);
actors = actors.concat(sheets);
if (window.document.readyState === "loading") {
window.addEventListener("DOMContentLoaded", domReady, true);
} else {
domReady();
// Recursively handle style sheets of the documents in iframes.
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)
documents.push(iframe.contentDocument);
}
}
}
return deferred.promise;
}, {
return actors;
}), {
request: {},
response: { styleSheets: RetVal("array:stylesheet") }
}),
/**
* Add all the stylesheets in this document and its subframes.
* Assumes the document is loaded.
*
* @return {Promise}
* Promise that resolves with an array of StyleSheetActors
*/
_addAllStyleSheets: function() {
return Task.spawn(function*() {
let documents = [this.document];
let actors = [];
for (let doc of documents) {
let sheets = yield this._addStyleSheets(doc);
actors = actors.concat(sheets);
// Recursively handle style sheets of the documents in iframes.
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)
documents.push(iframe.contentDocument);
}
}
}
return actors;
}.bind(this));
},
/**
* Check if we should be showing this stylesheet.
*
@ -160,6 +134,11 @@ let StyleSheetsActor = exports.StyleSheetsActor = protocol.ActorClass({
_addStyleSheets: function(doc)
{
return Task.spawn(function*() {
if (doc.readyState === "loading") {
// Wait for the document to load first.
yield listenOnce(doc.defaultView, "DOMContentLoaded", true);
}
let isChrome = Services.scriptSecurityManager.isSystemPrincipal(doc.nodePrincipal);
let styleSheets = isChrome ? DOMUtils.getAllStyleSheets(doc) : doc.styleSheets;
let actors = [];

View File

@ -74,6 +74,7 @@ const LOG_SWITCH_SUCCESS = "rename_file: proceeding to rename the directory\n" +
const ERR_RENAME_FILE = "rename_file: failed to rename file";
const ERR_UNABLE_OPEN_DEST = "unable to open destination file";
const ERR_BACKUP_DISCARD = "backup_discard: unable to remove";
const ERR_MOVE_DESTDIR_7 = "Moving destDir to tmpDir failed, err: 7";
const LOG_SVC_SUCCESSFUL_LAUNCH = "Process was started... waiting on result.";
@ -144,7 +145,6 @@ var gCallbackArgs = ["./", "callback.log", "Test Arg 2", "Test Arg 3"];
var gPostUpdateBinFile = "postup_app" + BIN_SUFFIX;
var gStageUpdate = false;
var gSwitchApp = false;
var gDisableReplaceFallback = false;
var gUseTestAppDir = true;
var gTimeoutRuns = 0;
@ -1584,21 +1584,11 @@ function runUpdate(aExpectedExitValue, aExpectedStatus, aCallback) {
}
debugDump("running the updater: " + updateBin.path + " " + args.join(" "));
let env = Cc["@mozilla.org/process/environment;1"].
getService(Ci.nsIEnvironment);
if (gDisableReplaceFallback) {
env.set("MOZ_NO_REPLACE_FALLBACK", "1");
}
let process = Cc["@mozilla.org/process/util;1"].
createInstance(Ci.nsIProcess);
process.init(updateBin);
process.run(true, args, args.length);
if (gDisableReplaceFallback) {
env.set("MOZ_NO_REPLACE_FALLBACK", "");
}
let status = readStatusFile();
if (process.exitValue != aExpectedExitValue || status != aExpectedStatus) {
if (process.exitValue != aExpectedExitValue) {
@ -2126,7 +2116,7 @@ function runUpdateUsingService(aInitialStatus, aExpectedStatus, aCheckSvcLog) {
gServiceLaunchedCallbackArgs = [
"-no-remote",
"-process-updates",
"-test-process-updates",
"-dump-args",
appArgsLogPath
];
@ -2343,7 +2333,6 @@ function setupUpdaterTest(aMarFile) {
helperBin.copyToFollowingLinks(afterApplyBinDir, gCallbackBinFile);
helperBin.copyToFollowingLinks(afterApplyBinDir, gPostUpdateBinFile);
let applyToDir = getApplyDirFile(null, true);
gTestFiles.forEach(function SUT_TF_FE(aTestFile) {
if (aTestFile.originalFile || aTestFile.originalContents) {
let testDir = getApplyDirFile(aTestFile.relPathDir, true);
@ -3386,8 +3375,8 @@ function createAppInfo(aID, aName, aVersion, aPlatformVersion) {
* Command line arguments used when launching the application:
* -no-remote prevents shell integration from being affected by an existing
* application process.
* -process-updates makes the application exits after being relaunched by the
* updater.
* -test-process-updates makes the application exit after being relaunched by
* the updater.
* the platform specific string defined by PIPE_TO_NULL to output both stdout
* and stderr to null. This is needed to prevent output from the application
* from ending up in the xpchsell log.
@ -3409,14 +3398,14 @@ function getProcessArgs(aExtraArgs) {
launchScript.create(Ci.nsILocalFile.NORMAL_FILE_TYPE, PERMS_DIRECTORY);
let scriptContents = "#! /bin/sh\n";
scriptContents += appBinPath + " -no-remote -process-updates " +
scriptContents += appBinPath + " -no-remote -test-process-updates " +
aExtraArgs.join(" ") + " " + PIPE_TO_NULL;
writeFile(launchScript, scriptContents);
debugDump("created " + launchScript.path + " containing:\n" +
scriptContents);
args = [launchScript.path];
} else {
args = ["/D", "/Q", "/C", appBinPath, "-no-remote", "-process-updates"].
args = ["/D", "/Q", "/C", appBinPath, "-no-remote", "-test-process-updates"].
concat(aExtraArgs).concat([PIPE_TO_NULL]);
}
return args;

View File

@ -1,48 +0,0 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
/* Application in use complete MAR file staged patch apply failure fallback test */
const START_STATE = STATE_APPLIED;
const END_STATE = STATE_PENDING;
function run_test() {
gStageUpdate = true;
setupTestCommon();
gTestFiles = gTestFilesCompleteSuccess;
gTestDirs = gTestDirsCompleteSuccess;
setTestFilesAndDirsForFailure();
setupUpdaterTest(FILE_COMPLETE_MAR);
// Launch the callback helper application so it is in use during the update.
let callbackApp = getApplyDirFile(DIR_RESOURCES + gCallbackBinFile);
let args = [getApplyDirPath() + DIR_RESOURCES, "input", "output", "-s",
HELPER_SLEEP_TIMEOUT];
let callbackAppProcess = Cc["@mozilla.org/process/util;1"].
createInstance(Ci.nsIProcess);
callbackAppProcess.init(callbackApp);
callbackAppProcess.run(false, args, args.length);
do_timeout(TEST_HELPER_TIMEOUT, waitForHelperSleep);
}
function doUpdate() {
runUpdate(0, START_STATE, null);
// Switch the application to the staged application that was updated.
gStageUpdate = false;
gSwitchApp = true;
runUpdate(1, END_STATE, checkUpdateApplied);
}
function checkUpdateApplied() {
setupHelperFinish();
}
function checkUpdate() {
checkFilesAfterUpdateFailure(getApplyDirFile, false, false);
checkUpdateLogContains(ERR_RENAME_FILE);
standardInit();
checkCallbackAppLog();
}

View File

@ -5,7 +5,7 @@
/* Application in use complete MAR file staged patch apply failure test */
const START_STATE = STATE_APPLIED;
const END_STATE = STATE_FAILED_WRITE_ERROR;
const END_STATE = STATE_PENDING;
function run_test() {
gStageUpdate = true;
@ -33,7 +33,6 @@ function doUpdate() {
// Switch the application to the staged application that was updated.
gStageUpdate = false;
gSwitchApp = true;
gDisableReplaceFallback = true;
runUpdate(1, END_STATE, checkUpdateApplied);
}
@ -42,8 +41,9 @@ function checkUpdateApplied() {
}
function checkUpdate() {
checkFilesAfterUpdateFailure(getApplyDirFile, true, false);
checkFilesAfterUpdateFailure(getApplyDirFile, false, false);
checkUpdateLogContains(ERR_RENAME_FILE);
checkUpdateLogContains(ERR_MOVE_DESTDIR_7);
standardInit();
checkCallbackAppLog();
}

View File

@ -1,46 +0,0 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
/* File in use complete MAR file staged patch apply failure fallback test */
function run_test() {
gStageUpdate = true;
setupTestCommon();
gTestFiles = gTestFilesCompleteSuccess;
gTestDirs = gTestDirsCompleteSuccess;
setTestFilesAndDirsForFailure();
setupUpdaterTest(FILE_COMPLETE_MAR);
// Launch an existing file so it is in use during the update.
let fileInUseBin = getApplyDirFile(gTestFiles[13].relPathDir +
gTestFiles[13].fileName);
let args = [getApplyDirPath() + DIR_RESOURCES, "input", "output", "-s",
HELPER_SLEEP_TIMEOUT];
let fileInUseProcess = Cc["@mozilla.org/process/util;1"].
createInstance(Ci.nsIProcess);
fileInUseProcess.init(fileInUseBin);
fileInUseProcess.run(false, args, args.length);
do_timeout(TEST_HELPER_TIMEOUT, waitForHelperSleep);
}
function doUpdate() {
runUpdate(0, STATE_APPLIED, null);
// Switch the application to the staged application that was updated.
gStageUpdate = false;
gSwitchApp = true;
runUpdate(1, STATE_PENDING, checkUpdateApplied);
}
function checkUpdateApplied() {
setupHelperFinish();
}
function checkUpdate() {
checkFilesAfterUpdateFailure(getApplyDirFile, false, false);
checkUpdateLogContains(ERR_RENAME_FILE);
standardInit();
checkCallbackAppLog();
}

View File

@ -1,46 +0,0 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
/* File in use partial MAR file staged patch apply failure fallback test */
function run_test() {
gStageUpdate = true;
setupTestCommon();
gTestFiles = gTestFilesPartialSuccess;
gTestDirs = gTestDirsPartialSuccess;
setTestFilesAndDirsForFailure();
setupUpdaterTest(FILE_PARTIAL_MAR);
// Launch an existing file so it is in use during the update.
let fileInUseBin = getApplyDirFile(gTestFiles[11].relPathDir +
gTestFiles[11].fileName);
let args = [getApplyDirPath() + DIR_RESOURCES, "input", "output", "-s",
HELPER_SLEEP_TIMEOUT];
let fileInUseProcess = Cc["@mozilla.org/process/util;1"].
createInstance(Ci.nsIProcess);
fileInUseProcess.init(fileInUseBin);
fileInUseProcess.run(false, args, args.length);
do_timeout(TEST_HELPER_TIMEOUT, waitForHelperSleep);
}
function doUpdate() {
runUpdate(0, STATE_APPLIED, null);
// Switch the application to the staged application that was updated.
gStageUpdate = false;
gSwitchApp = true;
runUpdate(1, STATE_PENDING, checkUpdateApplied);
}
function checkUpdateApplied() {
setupHelperFinish();
}
function checkUpdate() {
checkFilesAfterUpdateFailure(getApplyDirFile, false, false);
checkUpdateLogContains(ERR_RENAME_FILE);
standardInit();
checkCallbackAppLog();
}

View File

@ -31,8 +31,7 @@ function doUpdate() {
// Switch the application to the staged application that was updated.
gStageUpdate = false;
gSwitchApp = true;
gDisableReplaceFallback = true;
runUpdate(1, STATE_FAILED_WRITE_ERROR, checkUpdateApplied);
runUpdate(1, STATE_PENDING, checkUpdateApplied);
}
function checkUpdateApplied() {
@ -40,8 +39,9 @@ function checkUpdateApplied() {
}
function checkUpdate() {
checkFilesAfterUpdateFailure(getApplyDirFile, true, false);
checkFilesAfterUpdateFailure(getApplyDirFile, false, false);
checkUpdateLogContains(ERR_RENAME_FILE);
checkUpdateLogContains(ERR_MOVE_DESTDIR_7);
standardInit();
checkCallbackAppLog();
}

View File

@ -31,8 +31,7 @@ function doUpdate() {
// Switch the application to the staged application that was updated.
gStageUpdate = false;
gSwitchApp = true;
gDisableReplaceFallback = true;
runUpdate(1, STATE_FAILED_WRITE_ERROR, checkUpdateApplied);
runUpdate(1, STATE_PENDING, checkUpdateApplied);
}
function checkUpdateApplied() {
@ -40,8 +39,9 @@ function checkUpdateApplied() {
}
function checkUpdate() {
checkFilesAfterUpdateFailure(getApplyDirFile, true, false);
checkFilesAfterUpdateFailure(getApplyDirFile, false, false);
checkUpdateLogContains(ERR_RENAME_FILE);
checkUpdateLogContains(ERR_MOVE_DESTDIR_7);
standardInit();
checkCallbackAppLog();
}

View File

@ -1,55 +0,0 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
/* File locked complete MAR file staged patch apply failure fallback test */
function run_test() {
gStageUpdate = true;
setupTestCommon();
gTestFiles = gTestFilesCompleteSuccess;
gTestDirs = gTestDirsCompleteSuccess;
setTestFilesAndDirsForFailure();
setupUpdaterTest(FILE_COMPLETE_MAR);
// Exclusively lock an existing file so it is in use during the update.
let helperBin = getTestDirFile(FILE_HELPER_BIN);
let helperDestDir = getApplyDirFile(DIR_RESOURCES);
helperBin.copyTo(helperDestDir, FILE_HELPER_BIN);
helperBin = getApplyDirFile(DIR_RESOURCES + FILE_HELPER_BIN);
// Strip off the first two directories so the path has to be from the helper's
// working directory.
let lockFileRelPath = gTestFiles[3].relPathDir.split("/");
if (IS_MACOSX) {
lockFileRelPath = lockFileRelPath.slice(2);
}
lockFileRelPath = lockFileRelPath.join("/") + "/" + gTestFiles[3].fileName;
let args = [getApplyDirPath() + DIR_RESOURCES, "input", "output", "-s",
HELPER_SLEEP_TIMEOUT, lockFileRelPath];
let lockFileProcess = Cc["@mozilla.org/process/util;1"].
createInstance(Ci.nsIProcess);
lockFileProcess.init(helperBin);
lockFileProcess.run(false, args, args.length);
do_timeout(TEST_HELPER_TIMEOUT, waitForHelperSleep);
}
function doUpdate() {
runUpdate(1, STATE_FAILED_WRITE_ERROR_FILE_COPY, null);
// Switch the application to the staged application that was updated.
gStageUpdate = false;
gSwitchApp = true;
runUpdate(1, STATE_PENDING, checkUpdateApplied);
}
function checkUpdateApplied() {
setupHelperFinish();
}
function checkUpdate() {
checkFilesAfterUpdateFailure(getApplyDirFile, false, false);
checkUpdateLogContains(ERR_RENAME_FILE);
standardInit();
checkCallbackAppLog();
}

View File

@ -1,55 +0,0 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
/* File locked partial MAR file staged patch apply failure fallback test */
function run_test() {
gStageUpdate = true;
setupTestCommon();
gTestFiles = gTestFilesPartialSuccess;
gTestDirs = gTestDirsPartialSuccess;
setTestFilesAndDirsForFailure();
setupUpdaterTest(FILE_PARTIAL_MAR);
// Exclusively lock an existing file so it is in use during the update.
let helperBin = getTestDirFile(FILE_HELPER_BIN);
let helperDestDir = getApplyDirFile(DIR_RESOURCES);
helperBin.copyTo(helperDestDir, FILE_HELPER_BIN);
helperBin = getApplyDirFile(DIR_RESOURCES + FILE_HELPER_BIN);
// Strip off the first two directories so the path has to be from the helper's
// working directory.
let lockFileRelPath = gTestFiles[2].relPathDir.split("/");
if (IS_MACOSX) {
lockFileRelPath = lockFileRelPath.slice(2);
}
lockFileRelPath = lockFileRelPath.join("/") + "/" + gTestFiles[2].fileName;
let args = [getApplyDirPath() + DIR_RESOURCES, "input", "output", "-s",
HELPER_SLEEP_TIMEOUT, lockFileRelPath];
let lockFileProcess = Cc["@mozilla.org/process/util;1"].
createInstance(Ci.nsIProcess);
lockFileProcess.init(helperBin);
lockFileProcess.run(false, args, args.length);
do_timeout(TEST_HELPER_TIMEOUT, waitForHelperSleep);
}
function doUpdate() {
runUpdate(1, STATE_FAILED_WRITE_ERROR_FILE_COPY, null);
// Switch the application to the staged application that was updated.
gStageUpdate = false;
gSwitchApp = true;
runUpdate(1, STATE_PENDING, checkUpdateApplied);
}
function checkUpdateApplied() {
setupHelperFinish();
}
function checkUpdate() {
checkFilesAfterUpdateFailure(getApplyDirFile, false, false);
checkUpdateLogContains(ERR_RENAME_FILE);
standardInit();
checkCallbackAppLog();
}

View File

@ -40,8 +40,7 @@ function doUpdate() {
// Switch the application to the staged application that was updated.
gStageUpdate = false;
gSwitchApp = true;
gDisableReplaceFallback = true;
runUpdate(1, STATE_FAILED_WRITE_ERROR, checkUpdateApplied);
runUpdate(1, STATE_PENDING, checkUpdateApplied);
}
function checkUpdateApplied() {
@ -49,8 +48,9 @@ function checkUpdateApplied() {
}
function checkUpdate() {
checkFilesAfterUpdateFailure(getApplyDirFile, true, false);
checkFilesAfterUpdateFailure(getApplyDirFile, false, false);
checkUpdateLogContains(ERR_RENAME_FILE);
checkUpdateLogContains(ERR_MOVE_DESTDIR_7);
standardInit();
checkCallbackAppLog();
}

View File

@ -40,8 +40,7 @@ function doUpdate() {
// Switch the application to the staged application that was updated.
gStageUpdate = false;
gSwitchApp = true;
gDisableReplaceFallback = true;
runUpdate(1, STATE_FAILED_WRITE_ERROR, checkUpdateApplied);
runUpdate(1, STATE_PENDING, checkUpdateApplied);
}
function checkUpdateApplied() {
@ -49,8 +48,9 @@ function checkUpdateApplied() {
}
function checkUpdate() {
checkFilesAfterUpdateFailure(getApplyDirFile, true, false);
checkFilesAfterUpdateFailure(getApplyDirFile, false, false);
checkUpdateLogContains(ERR_RENAME_FILE);
checkUpdateLogContains(ERR_MOVE_DESTDIR_7);
standardInit();
checkCallbackAppLog();
}

View File

@ -1,57 +0,0 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
/* File in use inside removed dir complete MAR file staged patch apply failure
fallback test */
function run_test() {
gStageUpdate = true;
setupTestCommon();
gTestFiles = gTestFilesCompleteSuccess;
gTestDirs = gTestDirsCompleteSuccess;
setTestFilesAndDirsForFailure();
setupUpdaterTest(FILE_COMPLETE_MAR);
let fileInUseBin = getApplyDirFile(gTestDirs[4].relPathDir +
gTestDirs[4].subDirs[0] +
gTestDirs[4].subDirFiles[0]);
// Remove the empty file created for the test so the helper application can
// replace it.
fileInUseBin.remove(false);
let helperBin = getTestDirFile(FILE_HELPER_BIN);
let fileInUseDir = getApplyDirFile(gTestDirs[4].relPathDir +
gTestDirs[4].subDirs[0]);
helperBin.copyTo(fileInUseDir, gTestDirs[4].subDirFiles[0]);
// Launch an existing file so it is in use during the update.
let args = [getApplyDirPath() + DIR_RESOURCES, "input", "output", "-s",
HELPER_SLEEP_TIMEOUT];
let fileInUseProcess = Cc["@mozilla.org/process/util;1"].
createInstance(Ci.nsIProcess);
fileInUseProcess.init(fileInUseBin);
fileInUseProcess.run(false, args, args.length);
do_timeout(TEST_HELPER_TIMEOUT, waitForHelperSleep);
}
function doUpdate() {
runUpdate(0, STATE_APPLIED, null);
// Switch the application to the staged application that was updated.
gStageUpdate = false;
gSwitchApp = true;
runUpdate(1, STATE_PENDING, checkUpdateApplied);
}
function checkUpdateApplied() {
setupHelperFinish();
}
function checkUpdate() {
checkFilesAfterUpdateFailure(getApplyDirFile, false, false);
checkUpdateLogContains(ERR_RENAME_FILE);
standardInit();
checkCallbackAppLog();
}

View File

@ -1,55 +0,0 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
/* File in use inside removed dir partial MAR file staged patch apply failure
fallback test */
function run_test() {
gStageUpdate = true;
setupTestCommon();
gTestFiles = gTestFilesPartialSuccess;
gTestDirs = gTestDirsPartialSuccess;
setTestFilesAndDirsForFailure();
setupUpdaterTest(FILE_PARTIAL_MAR);
let fileInUseBin = getApplyDirFile(gTestDirs[2].relPathDir +
gTestDirs[2].files[0]);
// Remove the empty file created for the test so the helper application can
// replace it.
fileInUseBin.remove(false);
let helperBin = getTestDirFile(FILE_HELPER_BIN);
let fileInUseDir = getApplyDirFile(gTestDirs[2].relPathDir);
helperBin.copyTo(fileInUseDir, gTestDirs[2].files[0]);
// Launch an existing file so it is in use during the update.
let args = [getApplyDirPath() + DIR_RESOURCES, "input", "output", "-s",
HELPER_SLEEP_TIMEOUT];
let fileInUseProcess = Cc["@mozilla.org/process/util;1"].
createInstance(Ci.nsIProcess);
fileInUseProcess.init(fileInUseBin);
fileInUseProcess.run(false, args, args.length);
do_timeout(TEST_HELPER_TIMEOUT, waitForHelperSleep);
}
function doUpdate() {
runUpdate(0, STATE_APPLIED, null);
// Switch the application to the staged application that was updated.
gStageUpdate = false;
gSwitchApp = true;
runUpdate(1, STATE_PENDING, checkUpdateApplied);
}
function checkUpdateApplied() {
setupHelperFinish();
}
function checkUpdate() {
checkFilesAfterUpdateFailure(getApplyDirFile, false, false);
checkUpdateLogContains(ERR_RENAME_FILE);
standardInit();
checkCallbackAppLog();
}

View File

@ -35,15 +35,13 @@ function run_test() {
do_timeout(TEST_HELPER_TIMEOUT, waitForHelperSleep);
}
function doUpdate() {
runUpdate(0, STATE_APPLIED, null);
// Switch the application to the staged application that was updated.
gStageUpdate = false;
gSwitchApp = true;
gDisableReplaceFallback = true;
runUpdate(1, STATE_FAILED_WRITE_ERROR, checkUpdateApplied);
runUpdate(1, STATE_PENDING, checkUpdateApplied);
}
function checkUpdateApplied() {
@ -51,8 +49,9 @@ function checkUpdateApplied() {
}
function checkUpdate() {
checkFilesAfterUpdateFailure(getApplyDirFile, true, false);
checkFilesAfterUpdateFailure(getApplyDirFile, false, false);
checkUpdateLogContains(ERR_RENAME_FILE);
checkUpdateLogContains(ERR_MOVE_DESTDIR_7);
standardInit();
checkCallbackAppLog();
}

View File

@ -40,8 +40,7 @@ function doUpdate() {
// Switch the application to the staged application that was updated.
gStageUpdate = false;
gSwitchApp = true;
gDisableReplaceFallback = true;
runUpdate(1, STATE_FAILED_WRITE_ERROR, checkUpdateApplied);
runUpdate(1, STATE_PENDING, checkUpdateApplied);
}
function checkUpdateApplied() {
@ -49,8 +48,9 @@ function checkUpdateApplied() {
}
function checkUpdate() {
checkFilesAfterUpdateFailure(getApplyDirFile, true, false);
checkFilesAfterUpdateFailure(getApplyDirFile, false, false);
checkUpdateLogContains(ERR_RENAME_FILE);
checkUpdateLogContains(ERR_MOVE_DESTDIR_7);
standardInit();
checkCallbackAppLog();
}

View File

@ -39,8 +39,6 @@ skip-if = os == 'win' || toolkit == 'gonk'
reason = bug 1164150
[marAppInUseStageFailureComplete_win.js]
skip-if = os != 'win'
[marAppInUseFallbackStageFailureComplete_win.js]
skip-if = os != 'win'
[marFileLockedFailureComplete_win.js]
skip-if = os != 'win'
[marFileLockedFailurePartial_win.js]
@ -50,10 +48,6 @@ skip-if = os != 'win'
run-sequentially = Bug 1156446
[marFileLockedStageFailurePartial_win.js]
skip-if = os != 'win'
[marFileLockedFallbackStageFailureComplete_win.js]
skip-if = os != 'win'
[marFileLockedFallbackStageFailurePartial_win.js]
skip-if = os != 'win'
[marFileInUseSuccessComplete_win.js]
skip-if = os != 'win'
[marFileInUseSuccessPartial_win.js]
@ -70,14 +64,6 @@ skip-if = os != 'win'
skip-if = os != 'win'
[marRMRFDirFileInUseStageFailurePartial_win.js]
skip-if = os != 'win'
[marFileInUseFallbackStageFailureComplete_win.js]
skip-if = os != 'win'
[marFileInUseFallbackStageFailurePartial_win.js]
skip-if = os != 'win'
[marRMRFDirFileInUseFallbackStageFailureComplete_win.js]
skip-if = os != 'win'
[marRMRFDirFileInUseFallbackStageFailurePartial_win.js]
skip-if = os != 'win'
[marAppApplyDirLockedStageFailure_win.js]
skip-if = os != 'win'
[marAppApplyUpdateAppBinInUseStageSuccess_win.js]

View File

@ -1,58 +0,0 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
/* Application in use complete MAR file staged patch apply failure fallback test */
const START_STATE = STATE_PENDING_SVC;
const END_STATE = STATE_PENDING;
function run_test() {
if (!shouldRunServiceTest()) {
return;
}
gStageUpdate = true;
setupTestCommon();
gTestFiles = gTestFilesCompleteSuccess;
gTestDirs = gTestDirsCompleteSuccess;
setTestFilesAndDirsForFailure();
setupUpdaterTest(FILE_COMPLETE_MAR);
// Launch the callback helper application so it is in use during the update.
let callbackApp = getApplyDirFile(DIR_RESOURCES + gCallbackBinFile);
let args = [getApplyDirPath() + DIR_RESOURCES, "input", "output", "-s",
HELPER_SLEEP_TIMEOUT];
let callbackAppProcess = Cc["@mozilla.org/process/util;1"].
createInstance(Ci.nsIProcess);
callbackAppProcess.init(callbackApp);
callbackAppProcess.run(false, args, args.length);
setupAppFilesAsync();
}
function setupAppFilesFinished() {
do_timeout(TEST_HELPER_TIMEOUT, waitForHelperSleep);
}
function doUpdate() {
runUpdateUsingService(START_STATE, STATE_APPLIED);
}
function checkUpdateFinished() {
// Switch the application to the staged application that was updated.
gStageUpdate = false;
gSwitchApp = true;
runUpdate(1, END_STATE, checkUpdateApplied);
}
function checkUpdateApplied() {
setupHelperFinish();
}
function checkUpdate() {
checkFilesAfterUpdateFailure(getApplyDirFile, false, false);
checkUpdateLogContains(ERR_RENAME_FILE);
standardInit();
checkCallbackAppLog();
}

View File

@ -5,7 +5,7 @@
/* Application in use complete MAR file staged patch apply failure test */
const START_STATE = STATE_PENDING_SVC;
const END_STATE = STATE_FAILED_WRITE_ERROR;
const END_STATE = STATE_PENDING;
function run_test() {
if (!shouldRunServiceTest()) {
@ -43,7 +43,6 @@ function checkUpdateFinished() {
// Switch the application to the staged application that was updated.
gStageUpdate = false;
gSwitchApp = true;
gDisableReplaceFallback = true;
runUpdate(1, END_STATE, checkUpdateApplied);
}
@ -52,8 +51,9 @@ function checkUpdateApplied() {
}
function checkUpdate() {
checkFilesAfterUpdateFailure(getApplyDirFile, true, false);
checkFilesAfterUpdateFailure(getApplyDirFile, false, false);
checkUpdateLogContains(ERR_RENAME_FILE);
checkUpdateLogContains(ERR_MOVE_DESTDIR_7);
standardInit();
checkCallbackAppLog();
}

View File

@ -1,56 +0,0 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
/* File in use complete MAR file staged patch apply failure fallback test */
function run_test() {
if (!shouldRunServiceTest()) {
return;
}
gStageUpdate = true;
setupTestCommon();
gTestFiles = gTestFilesCompleteSuccess;
gTestDirs = gTestDirsCompleteSuccess;
setTestFilesAndDirsForFailure();
setupUpdaterTest(FILE_COMPLETE_MAR);
// Launch an existing file so it is in use during the update.
let fileInUseBin = getApplyDirFile(gTestFiles[13].relPathDir +
gTestFiles[13].fileName);
let args = [getApplyDirPath() + DIR_RESOURCES, "input", "output", "-s",
HELPER_SLEEP_TIMEOUT];
let fileInUseProcess = Cc["@mozilla.org/process/util;1"].
createInstance(Ci.nsIProcess);
fileInUseProcess.init(fileInUseBin);
fileInUseProcess.run(false, args, args.length);
setupAppFilesAsync();
}
function setupAppFilesFinished() {
do_timeout(TEST_HELPER_TIMEOUT, waitForHelperSleep);
}
function doUpdate() {
runUpdateUsingService(STATE_PENDING_SVC, STATE_APPLIED);
}
function checkUpdateFinished() {
// Switch the application to the staged application that was updated.
gStageUpdate = false;
gSwitchApp = true;
runUpdate(1, STATE_PENDING, checkUpdateApplied);
}
function checkUpdateApplied() {
setupHelperFinish();
}
function checkUpdate() {
checkFilesAfterUpdateFailure(getApplyDirFile, false, false);
checkUpdateLogContains(ERR_RENAME_FILE);
standardInit();
checkCallbackAppLog();
}

View File

@ -1,56 +0,0 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
/* File in use partial MAR file staged patch apply failure fallback test */
function run_test() {
if (!shouldRunServiceTest()) {
return;
}
gStageUpdate = true;
setupTestCommon();
gTestFiles = gTestFilesPartialSuccess;
gTestDirs = gTestDirsPartialSuccess;
setTestFilesAndDirsForFailure();
setupUpdaterTest(FILE_PARTIAL_MAR);
// Launch an existing file so it is in use during the update.
let fileInUseBin = getApplyDirFile(gTestFiles[11].relPathDir +
gTestFiles[11].fileName);
let args = [getApplyDirPath() + DIR_RESOURCES, "input", "output", "-s",
HELPER_SLEEP_TIMEOUT];
let fileInUseProcess = Cc["@mozilla.org/process/util;1"].
createInstance(Ci.nsIProcess);
fileInUseProcess.init(fileInUseBin);
fileInUseProcess.run(false, args, args.length);
setupAppFilesAsync();
}
function setupAppFilesFinished() {
do_timeout(TEST_HELPER_TIMEOUT, waitForHelperSleep);
}
function doUpdate() {
runUpdateUsingService(STATE_PENDING_SVC, STATE_APPLIED);
}
function checkUpdateFinished() {
// Switch the application to the staged application that was updated.
gStageUpdate = false;
gSwitchApp = true;
runUpdate(1, STATE_PENDING, checkUpdateApplied);
}
function checkUpdateApplied() {
setupHelperFinish();
}
function checkUpdate() {
checkFilesAfterUpdateFailure(getApplyDirFile, false, false);
checkUpdateLogContains(ERR_RENAME_FILE);
standardInit();
checkCallbackAppLog();
}

View File

@ -41,8 +41,7 @@ function checkUpdateFinished() {
// Switch the application to the staged application that was updated.
gStageUpdate = false;
gSwitchApp = true;
gDisableReplaceFallback = true;
runUpdate(1, STATE_FAILED_WRITE_ERROR, checkUpdateApplied);
runUpdate(1, STATE_PENDING, checkUpdateApplied);
}
function checkUpdateApplied() {
@ -50,8 +49,9 @@ function checkUpdateApplied() {
}
function checkUpdate() {
checkFilesAfterUpdateFailure(getApplyDirFile, true, false);
checkFilesAfterUpdateFailure(getApplyDirFile, false, false);
checkUpdateLogContains(ERR_RENAME_FILE);
checkUpdateLogContains(ERR_MOVE_DESTDIR_7);
standardInit();
checkCallbackAppLog();
}

View File

@ -41,8 +41,7 @@ function checkUpdateFinished() {
// Switch the application to the staged application that was updated.
gStageUpdate = false;
gSwitchApp = true;
gDisableReplaceFallback = true;
runUpdate(1, STATE_FAILED_WRITE_ERROR, checkUpdateApplied);
runUpdate(1, STATE_PENDING, checkUpdateApplied);
}
function checkUpdateApplied() {
@ -50,8 +49,9 @@ function checkUpdateApplied() {
}
function checkUpdate() {
checkFilesAfterUpdateFailure(getApplyDirFile, true, false);
checkFilesAfterUpdateFailure(getApplyDirFile, false, false);
checkUpdateLogContains(ERR_RENAME_FILE);
checkUpdateLogContains(ERR_MOVE_DESTDIR_7);
standardInit();
checkCallbackAppLog();
}

View File

@ -1,65 +0,0 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
/* File locked complete MAR file staged patch apply failure fallback test */
function run_test() {
if (!shouldRunServiceTest()) {
return;
}
gStageUpdate = true;
setupTestCommon();
gTestFiles = gTestFilesCompleteSuccess;
gTestDirs = gTestDirsCompleteSuccess;
setTestFilesAndDirsForFailure();
setupUpdaterTest(FILE_COMPLETE_MAR);
// Exclusively lock an existing file so it is in use during the update.
let helperBin = getTestDirFile(FILE_HELPER_BIN);
let helperDestDir = getApplyDirFile(DIR_RESOURCES);
helperBin.copyTo(helperDestDir, FILE_HELPER_BIN);
helperBin = getApplyDirFile(DIR_RESOURCES + FILE_HELPER_BIN);
// Strip off the first two directories so the path has to be from the helper's
// working directory.
let lockFileRelPath = gTestFiles[3].relPathDir.split("/");
if (IS_MACOSX) {
lockFileRelPath = lockFileRelPath.slice(2);
}
lockFileRelPath = lockFileRelPath.join("/") + "/" + gTestFiles[3].fileName;
let args = [getApplyDirPath() + DIR_RESOURCES, "input", "output", "-s",
HELPER_SLEEP_TIMEOUT, lockFileRelPath];
let lockFileProcess = Cc["@mozilla.org/process/util;1"].
createInstance(Ci.nsIProcess);
lockFileProcess.init(helperBin);
lockFileProcess.run(false, args, args.length);
setupAppFilesAsync();
}
function setupAppFilesFinished() {
do_timeout(TEST_HELPER_TIMEOUT, waitForHelperSleep);
}
function doUpdate() {
runUpdateUsingService(STATE_PENDING_SVC, STATE_FAILED_WRITE_ERROR_FILE_COPY);
}
function checkUpdateFinished() {
// Switch the application to the staged application that was updated.
gStageUpdate = false;
gSwitchApp = true;
runUpdate(1, STATE_PENDING, checkUpdateApplied);
}
function checkUpdateApplied() {
setupHelperFinish();
}
function checkUpdate() {
checkFilesAfterUpdateFailure(getApplyDirFile, false, false);
checkUpdateLogContains(ERR_RENAME_FILE);
standardInit();
checkCallbackAppLog();
}

View File

@ -1,65 +0,0 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
/* File locked partial MAR file staged patch apply failure fallback test */
function run_test() {
if (!shouldRunServiceTest()) {
return;
}
gStageUpdate = true;
setupTestCommon();
gTestFiles = gTestFilesPartialSuccess;
gTestDirs = gTestDirsPartialSuccess;
setTestFilesAndDirsForFailure();
setupUpdaterTest(FILE_PARTIAL_MAR);
// Exclusively lock an existing file so it is in use during the update.
let helperBin = getTestDirFile(FILE_HELPER_BIN);
let helperDestDir = getApplyDirFile(DIR_RESOURCES);
helperBin.copyTo(helperDestDir, FILE_HELPER_BIN);
helperBin = getApplyDirFile(DIR_RESOURCES + FILE_HELPER_BIN);
// Strip off the first two directories so the path has to be from the helper's
// working directory.
let lockFileRelPath = gTestFiles[2].relPathDir.split("/");
if (IS_MACOSX) {
lockFileRelPath = lockFileRelPath.slice(2);
}
lockFileRelPath = lockFileRelPath.join("/") + "/" + gTestFiles[2].fileName;
let args = [getApplyDirPath() + DIR_RESOURCES, "input", "output", "-s",
HELPER_SLEEP_TIMEOUT, lockFileRelPath];
let lockFileProcess = Cc["@mozilla.org/process/util;1"].
createInstance(Ci.nsIProcess);
lockFileProcess.init(helperBin);
lockFileProcess.run(false, args, args.length);
setupAppFilesAsync();
}
function setupAppFilesFinished() {
do_timeout(TEST_HELPER_TIMEOUT, waitForHelperSleep);
}
function doUpdate() {
runUpdateUsingService(STATE_PENDING_SVC, STATE_FAILED_WRITE_ERROR_FILE_COPY);
}
function checkUpdateFinished() {
// Switch the application to the staged application that was updated.
gStageUpdate = false;
gSwitchApp = true;
runUpdate(1, STATE_PENDING, checkUpdateApplied);
}
function checkUpdateApplied() {
setupHelperFinish();
}
function checkUpdate() {
checkFilesAfterUpdateFailure(getApplyDirFile, false, false);
checkUpdateLogContains(ERR_RENAME_FILE);
standardInit();
checkCallbackAppLog();
}

View File

@ -50,8 +50,7 @@ function checkUpdateFinished() {
// Switch the application to the staged application that was updated.
gStageUpdate = false;
gSwitchApp = true;
gDisableReplaceFallback = true;
runUpdate(1, STATE_FAILED_WRITE_ERROR, checkUpdateApplied);
runUpdate(1, STATE_PENDING, checkUpdateApplied);
}
function checkUpdateApplied() {
@ -59,8 +58,9 @@ function checkUpdateApplied() {
}
function checkUpdate() {
checkFilesAfterUpdateFailure(getApplyDirFile, true, false);
checkFilesAfterUpdateFailure(getApplyDirFile, false, false);
checkUpdateLogContains(ERR_RENAME_FILE);
checkUpdateLogContains(ERR_MOVE_DESTDIR_7);
standardInit();
checkCallbackAppLog();
}

View File

@ -50,8 +50,7 @@ function checkUpdateFinished() {
// Switch the application to the staged application that was updated.
gStageUpdate = false;
gSwitchApp = true;
gDisableReplaceFallback = true;
runUpdate(1, STATE_FAILED_WRITE_ERROR, checkUpdateApplied);
runUpdate(1, STATE_PENDING, checkUpdateApplied);
}
function checkUpdateApplied() {
@ -59,8 +58,9 @@ function checkUpdateApplied() {
}
function checkUpdate() {
checkFilesAfterUpdateFailure(getApplyDirFile, true, false);
checkFilesAfterUpdateFailure(getApplyDirFile, false, false);
checkUpdateLogContains(ERR_RENAME_FILE);
checkUpdateLogContains(ERR_MOVE_DESTDIR_7);
standardInit();
checkCallbackAppLog();
}

View File

@ -1,67 +0,0 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
/* File in use inside removed dir complete MAR file staged patch apply failure
fallback test */
function run_test() {
if (!shouldRunServiceTest()) {
return;
}
gStageUpdate = true;
setupTestCommon();
gTestFiles = gTestFilesCompleteSuccess;
gTestDirs = gTestDirsCompleteSuccess;
setTestFilesAndDirsForFailure();
setupUpdaterTest(FILE_COMPLETE_MAR);
let fileInUseBin = getApplyDirFile(gTestDirs[4].relPathDir +
gTestDirs[4].subDirs[0] +
gTestDirs[4].subDirFiles[0]);
// Remove the empty file created for the test so the helper application can
// replace it.
fileInUseBin.remove(false);
let helperBin = getTestDirFile(FILE_HELPER_BIN);
let fileInUseDir = getApplyDirFile(gTestDirs[4].relPathDir +
gTestDirs[4].subDirs[0]);
helperBin.copyTo(fileInUseDir, gTestDirs[4].subDirFiles[0]);
// Launch an existing file so it is in use during the update.
let args = [getApplyDirPath() + DIR_RESOURCES, "input", "output", "-s",
HELPER_SLEEP_TIMEOUT];
let fileInUseProcess = Cc["@mozilla.org/process/util;1"].
createInstance(Ci.nsIProcess);
fileInUseProcess.init(fileInUseBin);
fileInUseProcess.run(false, args, args.length);
setupAppFilesAsync();
}
function setupAppFilesFinished() {
do_timeout(TEST_HELPER_TIMEOUT, waitForHelperSleep);
}
function doUpdate() {
runUpdateUsingService(STATE_PENDING_SVC, STATE_APPLIED);
}
function checkUpdateFinished() {
// Switch the application to the staged application that was updated.
gStageUpdate = false;
gSwitchApp = true;
runUpdate(1, STATE_PENDING, checkUpdateApplied);
}
function checkUpdateApplied() {
setupHelperFinish();
}
function checkUpdate() {
checkFilesAfterUpdateFailure(getApplyDirFile, false, false);
checkUpdateLogContains(ERR_RENAME_FILE);
standardInit();
checkCallbackAppLog();
}

View File

@ -1,65 +0,0 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
/* File in use inside removed dir partial MAR file staged patch apply failure
fallback test */
function run_test() {
if (!shouldRunServiceTest()) {
return;
}
gStageUpdate = true;
setupTestCommon();
gTestFiles = gTestFilesPartialSuccess;
gTestDirs = gTestDirsPartialSuccess;
setTestFilesAndDirsForFailure();
setupUpdaterTest(FILE_PARTIAL_MAR);
let fileInUseBin = getApplyDirFile(gTestDirs[2].relPathDir +
gTestDirs[2].files[0]);
// Remove the empty file created for the test so the helper application can
// replace it.
fileInUseBin.remove(false);
let helperBin = getTestDirFile(FILE_HELPER_BIN);
let fileInUseDir = getApplyDirFile(gTestDirs[2].relPathDir);
helperBin.copyTo(fileInUseDir, gTestDirs[2].files[0]);
// Launch an existing file so it is in use during the update.
let args = [getApplyDirPath() + DIR_RESOURCES, "input", "output", "-s",
HELPER_SLEEP_TIMEOUT];
let fileInUseProcess = Cc["@mozilla.org/process/util;1"].
createInstance(Ci.nsIProcess);
fileInUseProcess.init(fileInUseBin);
fileInUseProcess.run(false, args, args.length);
setupAppFilesAsync();
}
function setupAppFilesFinished() {
do_timeout(TEST_HELPER_TIMEOUT, waitForHelperSleep);
}
function doUpdate() {
runUpdateUsingService(STATE_PENDING_SVC, STATE_APPLIED);
}
function checkUpdateFinished() {
// Switch the application to the staged application that was updated.
gStageUpdate = false;
gSwitchApp = true;
runUpdate(1, STATE_PENDING, checkUpdateApplied);
}
function checkUpdateApplied() {
setupHelperFinish();
}
function checkUpdate() {
checkFilesAfterUpdateFailure(getApplyDirFile, false, false);
checkUpdateLogContains(ERR_RENAME_FILE);
standardInit();
checkCallbackAppLog();
}

View File

@ -52,8 +52,7 @@ function checkUpdateFinished() {
// Switch the application to the staged application that was updated.
gStageUpdate = false;
gSwitchApp = true;
gDisableReplaceFallback = true;
runUpdate(1, STATE_FAILED_WRITE_ERROR, checkUpdateApplied);
runUpdate(1, STATE_PENDING, checkUpdateApplied);
}
function checkUpdateApplied() {
@ -61,8 +60,9 @@ function checkUpdateApplied() {
}
function checkUpdate() {
checkFilesAfterUpdateFailure(getApplyDirFile, true, false);
checkFilesAfterUpdateFailure(getApplyDirFile, false, false);
checkUpdateLogContains(ERR_RENAME_FILE);
checkUpdateLogContains(ERR_MOVE_DESTDIR_7);
standardInit();
checkCallbackAppLog();
}

View File

@ -50,8 +50,7 @@ function checkUpdateFinished() {
// Switch the application to the staged application that was updated.
gStageUpdate = false;
gSwitchApp = true;
gDisableReplaceFallback = true;
runUpdate(1, STATE_FAILED_WRITE_ERROR, checkUpdateApplied);
runUpdate(1, STATE_PENDING, checkUpdateApplied);
}
function checkUpdateApplied() {
@ -59,8 +58,9 @@ function checkUpdateApplied() {
}
function checkUpdate() {
checkFilesAfterUpdateFailure(getApplyDirFile, true, false);
checkFilesAfterUpdateFailure(getApplyDirFile, false, false);
checkUpdateLogContains(ERR_RENAME_FILE);
checkUpdateLogContains(ERR_MOVE_DESTDIR_7);
standardInit();
checkCallbackAppLog();
}

View File

@ -33,7 +33,8 @@ function run_test() {
// Don't test symlinks on Mac OS X in this test since it tends to timeout.
// It is tested on Mac OS X in marAppInUseStageSuccessComplete_unix.js
if (IS_UNIX && !IS_MACOSX) {
// The tests don't support symlinks on gonk.
if (IS_UNIX && !IS_MACOSX && !IS_TOOLKIT_GONK) {
removeSymlink();
createSymlink();
do_register_cleanup(removeSymlink);
@ -97,7 +98,8 @@ function finishCheckUpdateApplied() {
checkUpdateLogContains("removing old distribution directory");
}
if (IS_UNIX && !IS_MACOSX) {
// The tests don't support symlinks on gonk.
if (IS_UNIX && !IS_MACOSX && !IS_TOOLKIT_GONK) {
checkSymlink();
}
checkAppBundleModTime();

View File

@ -35,8 +35,6 @@ run-sequentially = Uses the Mozilla Maintenance Service.
run-sequentially = Uses the Mozilla Maintenance Service.
[marAppInUseStageFailureCompleteSvc_win.js]
run-sequentially = Uses the Mozilla Maintenance Service.
[marAppInUseFallbackStageFailureCompleteSvc_win.js]
run-sequentially = Uses the Mozilla Maintenance Service.
[marFileLockedFailureCompleteSvc_win.js]
run-sequentially = Uses the Mozilla Maintenance Service.
[marFileLockedFailurePartialSvc_win.js]
@ -45,10 +43,6 @@ run-sequentially = Uses the Mozilla Maintenance Service.
run-sequentially = Uses the Mozilla Maintenance Service.
[marFileLockedStageFailurePartialSvc_win.js]
run-sequentially = Uses the Mozilla Maintenance Service.
[marFileLockedFallbackStageFailureCompleteSvc_win.js]
run-sequentially = Uses the Mozilla Maintenance Service.
[marFileLockedFallbackStageFailurePartialSvc_win.js]
run-sequentially = Uses the Mozilla Maintenance Service.
[marFileInUseSuccessCompleteSvc_win.js]
run-sequentially = Uses the Mozilla Maintenance Service.
[marFileInUseSuccessPartialSvc_win.js]
@ -65,14 +59,6 @@ run-sequentially = Uses the Mozilla Maintenance Service.
run-sequentially = Uses the Mozilla Maintenance Service.
[marRMRFDirFileInUseStageFailurePartialSvc_win.js]
run-sequentially = Uses the Mozilla Maintenance Service.
[marFileInUseFallbackStageFailureCompleteSvc_win.js]
run-sequentially = Uses the Mozilla Maintenance Service.
[marFileInUseFallbackStageFailurePartialSvc_win.js]
run-sequentially = Uses the Mozilla Maintenance Service.
[marRMRFDirFileInUseFallbackStageFailureCompleteSvc_win.js]
run-sequentially = Uses the Mozilla Maintenance Service.
[marRMRFDirFileInUseFallbackStageFailurePartialSvc_win.js]
run-sequentially = Uses the Mozilla Maintenance Service.
[marAppApplyDirLockedStageFailureSvc_win.js]
run-sequentially = Uses the Mozilla Maintenance Service.
[marAppApplyUpdateAppBinInUseStageSuccessSvc_win.js]

View File

@ -345,6 +345,13 @@ mstrtok(const NS_tchar *delims, NS_tchar **str)
return ret;
}
static bool
EnvHasValue(const char *name)
{
const char *val = getenv(name);
return (val && *val);
}
#ifdef XP_WIN
/**
* Coverts a relative update path to a full path for Windows.
@ -2266,7 +2273,7 @@ UpdateThreadFunc(void *param)
// The MOZ_TEST_SKIP_UPDATE_STAGE environment variable prevents copying
// the files in dist/bin in the test updater when staging an update since
// this can cause tests to timeout.
if (getenv("MOZ_TEST_SKIP_UPDATE_STAGE")) {
if (EnvHasValue("MOZ_TEST_SKIP_UPDATE_STAGE")) {
rv = OK;
} else {
rv = CopyInstallDirToDestDir();
@ -2286,8 +2293,7 @@ UpdateThreadFunc(void *param)
}
}
bool reportRealResults = true;
if (sReplaceRequest && rv && !getenv("MOZ_NO_REPLACE_FALLBACK")) {
if (sReplaceRequest && rv) {
// When attempting to replace the application, we should fall back
// to non-staged updates in case of a failure. We do this by
// setting the status to pending, exiting the updater, and
@ -2295,22 +2301,16 @@ UpdateThreadFunc(void *param)
// startup path will see the pending status, and will start the
// updater application again in order to apply the update without
// staging.
// The MOZ_NO_REPLACE_FALLBACK environment variable is used to
// bypass this fallback, and is used in the updater tests.
// The only special thing which we should do here is to remove the
// staged directory as it won't be useful any more.
ensure_remove_recursive(gWorkingDirPath);
WriteStatusFile(sUsingService ? "pending-service" : "pending");
// We need to use --process-updates again in the tests
putenv(const_cast<char*>("MOZ_PROCESS_UPDATES="));
reportRealResults = false; // pretend success
}
if (reportRealResults) {
#ifdef TEST_UPDATER
// Some tests need to use --test-process-updates again.
putenv(const_cast<char*>("MOZ_TEST_PROCESS_UPDATES="));
#endif
} else {
if (rv) {
LOG(("failed: %d", rv));
}
else {
} else {
#ifdef XP_MACOSX
// If the update was successful we need to update the timestamp on the
// top-level Mac OS X bundle directory so that Mac OS X's Launch Services
@ -2332,7 +2332,7 @@ UpdateThreadFunc(void *param)
int NS_main(int argc, NS_tchar **argv)
{
#if defined(MOZ_WIDGET_GONK)
if (getenv("LD_PRELOAD")) {
if (EnvHasValue("LD_PRELOAD")) {
// If the updater is launched with LD_PRELOAD set, then we wind up
// preloading libmozglue.so. Under some circumstances, this can cause
// the remount of /system to fail when going from rw to ro, so if we
@ -2399,7 +2399,7 @@ int NS_main(int argc, NS_tchar **argv)
#ifdef XP_WIN
bool useService = false;
bool testOnlyFallbackKeyExists = false;
bool noServiceFallback = getenv("MOZ_NO_SERVICE_FALLBACK") != nullptr;
bool noServiceFallback = EnvHasValue("MOZ_NO_SERVICE_FALLBACK");
putenv(const_cast<char*>("MOZ_NO_SERVICE_FALLBACK="));
// We never want the service to be used unless we build with
@ -2465,7 +2465,7 @@ int NS_main(int argc, NS_tchar **argv)
*slash = NS_T('\0');
}
if (getenv("MOZ_OS_UPDATE")) {
if (EnvHasValue("MOZ_OS_UPDATE")) {
sIsOSUpdate = true;
putenv(const_cast<char*>("MOZ_OS_UPDATE="));
}
@ -2572,7 +2572,7 @@ int NS_main(int argc, NS_tchar **argv)
const int callbackIndex = 6;
#if defined(XP_WIN)
sUsingService = getenv("MOZ_USING_SERVICE") != nullptr;
sUsingService = EnvHasValue("MOZ_USING_SERVICE");
putenv(const_cast<char*>("MOZ_USING_SERVICE="));
// lastFallbackError keeps track of the last error for the service not being
// used, in case of an error when fallback is not enabled we write the
@ -3217,13 +3217,10 @@ int NS_main(int argc, NS_tchar **argv)
LaunchMacPostProcess(gInstallDirPath);
}
#endif /* XP_MACOSX */
if (getenv("MOZ_PROCESS_UPDATES") == nullptr) {
LaunchCallbackApp(argv[5],
argc - callbackIndex,
argv + callbackIndex,
sUsingService);
}
LaunchCallbackApp(argv[5],
argc - callbackIndex,
argv + callbackIndex,
sUsingService);
}
return gSucceeded ? 0 : 1;

View File

@ -3811,9 +3811,9 @@ XREMain::XRE_mainStartup(bool* aExitFlag)
if (NS_FAILED(rv))
updRoot = mDirProvider.GetAppDir();
// If the MOZ_PROCESS_UPDATES environment variable already exists, then
// If the MOZ_TEST_PROCESS_UPDATES environment variable already exists, then
// we are being called from the callback application.
if (EnvHasValue("MOZ_PROCESS_UPDATES")) {
if (EnvHasValue("MOZ_TEST_PROCESS_UPDATES")) {
// If the caller has asked us to log our arguments, do so. This is used
// to make sure that the maintenance service successfully launches the
// callback application.
@ -3831,13 +3831,13 @@ XREMain::XRE_mainStartup(bool* aExitFlag)
return 0;
}
// Support for processing an update and exiting. The MOZ_PROCESS_UPDATES
// Support for processing an update and exiting. The MOZ_TEST_PROCESS_UPDATES
// environment variable will be part of the updater's environment and the
// application that is relaunched by the updater. When the application is
// relaunched by the updater it will be removed below and the application
// will exit.
if (CheckArg("process-updates")) {
SaveToEnv("MOZ_PROCESS_UPDATES=1");
if (CheckArg("test-process-updates")) {
SaveToEnv("MOZ_TEST_PROCESS_UPDATES=1");
}
nsCOMPtr<nsIFile> exeFile, exeDir;
rv = mDirProvider.GetFile(XRE_EXECUTABLE_FILE, &persistent,
@ -3851,8 +3851,8 @@ XREMain::XRE_mainStartup(bool* aExitFlag)
gRestartArgc,
gRestartArgv,
mAppData->version);
if (EnvHasValue("MOZ_PROCESS_UPDATES")) {
SaveToEnv("MOZ_PROCESS_UPDATES=");
if (EnvHasValue("MOZ_TEST_PROCESS_UPDATES")) {
SaveToEnv("MOZ_TEST_PROCESS_UPDATES=");
*aExitFlag = true;
return 0;
}

View File

@ -1012,7 +1012,7 @@ ProcessUpdates(nsIFile *greDir, nsIFile *appDir, nsIFile *updRootDir,
return rv;
ProcessType dummyPID; // this will only be used for MOZ_UPDATE_STAGING
const char *processingUpdates = PR_GetEnv("MOZ_PROCESS_UPDATES");
const char *processingUpdates = PR_GetEnv("MOZ_TEST_PROCESS_UPDATES");
if (processingUpdates && *processingUpdates) {
// Enable the tests to request an update to be staged.
const char *stagingUpdate = PR_GetEnv("MOZ_UPDATE_STAGING");