Merge m-c to inbound.
@ -12,7 +12,7 @@
|
|||||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||||
</project>
|
</project>
|
||||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="ade88673d00414c3177f7444543b2fa01324708e"/>
|
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="8ac5dc6b76709e742cb923c58d1f4b415b3e08fb"/>
|
||||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="78b908b493bfe0b477e3d4f6edec8c46a2c0d096"/>
|
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="78b908b493bfe0b477e3d4f6edec8c46a2c0d096"/>
|
||||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||||
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="eda08beb3ba9a159843c70ffde0f9660ec351eb9"/>
|
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="eda08beb3ba9a159843c70ffde0f9660ec351eb9"/>
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
</project>
|
</project>
|
||||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="ade88673d00414c3177f7444543b2fa01324708e"/>
|
<project name="gaia" path="gaia" remote="mozillaorg" revision="8ac5dc6b76709e742cb923c58d1f4b415b3e08fb"/>
|
||||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="78b908b493bfe0b477e3d4f6edec8c46a2c0d096"/>
|
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="78b908b493bfe0b477e3d4f6edec8c46a2c0d096"/>
|
||||||
<project name="moztt" path="external/moztt" remote="b2g" revision="3d5c964015967ca8c86abe6dbbebee3cb82b1609"/>
|
<project name="moztt" path="external/moztt" remote="b2g" revision="3d5c964015967ca8c86abe6dbbebee3cb82b1609"/>
|
||||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="a314508e397c8f1814228d36259ea8708034444e"/>
|
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="a314508e397c8f1814228d36259ea8708034444e"/>
|
||||||
|
@ -12,7 +12,7 @@
|
|||||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||||
</project>
|
</project>
|
||||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="ade88673d00414c3177f7444543b2fa01324708e"/>
|
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="8ac5dc6b76709e742cb923c58d1f4b415b3e08fb"/>
|
||||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="78b908b493bfe0b477e3d4f6edec8c46a2c0d096"/>
|
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="78b908b493bfe0b477e3d4f6edec8c46a2c0d096"/>
|
||||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||||
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="eda08beb3ba9a159843c70ffde0f9660ec351eb9"/>
|
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="eda08beb3ba9a159843c70ffde0f9660ec351eb9"/>
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
{
|
{
|
||||||
"revision": "3d73a644a8ecb4a4c426584956d0c7f6b05e4cdb",
|
"revision": "4f00231c5cc538139e63bee1a7ed8456f6cefed7",
|
||||||
"repo_path": "/integration/gaia-central"
|
"repo_path": "/integration/gaia-central"
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||||
</project>
|
</project>
|
||||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="ade88673d00414c3177f7444543b2fa01324708e"/>
|
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="8ac5dc6b76709e742cb923c58d1f4b415b3e08fb"/>
|
||||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="78b908b493bfe0b477e3d4f6edec8c46a2c0d096"/>
|
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="78b908b493bfe0b477e3d4f6edec8c46a2c0d096"/>
|
||||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||||
<project name="librecovery" path="librecovery" remote="b2g" revision="84f2f2fce22605e17d511ff1767e54770067b5b5"/>
|
<project name="librecovery" path="librecovery" remote="b2g" revision="84f2f2fce22605e17d511ff1767e54770067b5b5"/>
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||||
</project>
|
</project>
|
||||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="ade88673d00414c3177f7444543b2fa01324708e"/>
|
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="8ac5dc6b76709e742cb923c58d1f4b415b3e08fb"/>
|
||||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="78b908b493bfe0b477e3d4f6edec8c46a2c0d096"/>
|
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="78b908b493bfe0b477e3d4f6edec8c46a2c0d096"/>
|
||||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||||
<project name="librecovery" path="librecovery" remote="b2g" revision="84f2f2fce22605e17d511ff1767e54770067b5b5"/>
|
<project name="librecovery" path="librecovery" remote="b2g" revision="84f2f2fce22605e17d511ff1767e54770067b5b5"/>
|
||||||
|
@ -12,7 +12,7 @@
|
|||||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||||
</project>
|
</project>
|
||||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="ade88673d00414c3177f7444543b2fa01324708e"/>
|
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="8ac5dc6b76709e742cb923c58d1f4b415b3e08fb"/>
|
||||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="78b908b493bfe0b477e3d4f6edec8c46a2c0d096"/>
|
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="78b908b493bfe0b477e3d4f6edec8c46a2c0d096"/>
|
||||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||||
<project name="librecovery" path="librecovery" remote="b2g" revision="84f2f2fce22605e17d511ff1767e54770067b5b5"/>
|
<project name="librecovery" path="librecovery" remote="b2g" revision="84f2f2fce22605e17d511ff1767e54770067b5b5"/>
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||||
</project>
|
</project>
|
||||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="ade88673d00414c3177f7444543b2fa01324708e"/>
|
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="8ac5dc6b76709e742cb923c58d1f4b415b3e08fb"/>
|
||||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="78b908b493bfe0b477e3d4f6edec8c46a2c0d096"/>
|
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="78b908b493bfe0b477e3d4f6edec8c46a2c0d096"/>
|
||||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||||
<project name="librecovery" path="librecovery" remote="b2g" revision="84f2f2fce22605e17d511ff1767e54770067b5b5"/>
|
<project name="librecovery" path="librecovery" remote="b2g" revision="84f2f2fce22605e17d511ff1767e54770067b5b5"/>
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
</project>
|
</project>
|
||||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="ade88673d00414c3177f7444543b2fa01324708e"/>
|
<project name="gaia" path="gaia" remote="mozillaorg" revision="8ac5dc6b76709e742cb923c58d1f4b415b3e08fb"/>
|
||||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="78b908b493bfe0b477e3d4f6edec8c46a2c0d096"/>
|
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="78b908b493bfe0b477e3d4f6edec8c46a2c0d096"/>
|
||||||
<project name="moztt" path="external/moztt" remote="b2g" revision="3d5c964015967ca8c86abe6dbbebee3cb82b1609"/>
|
<project name="moztt" path="external/moztt" remote="b2g" revision="3d5c964015967ca8c86abe6dbbebee3cb82b1609"/>
|
||||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="a314508e397c8f1814228d36259ea8708034444e"/>
|
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="a314508e397c8f1814228d36259ea8708034444e"/>
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||||
</project>
|
</project>
|
||||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="ade88673d00414c3177f7444543b2fa01324708e"/>
|
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="8ac5dc6b76709e742cb923c58d1f4b415b3e08fb"/>
|
||||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="78b908b493bfe0b477e3d4f6edec8c46a2c0d096"/>
|
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="78b908b493bfe0b477e3d4f6edec8c46a2c0d096"/>
|
||||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||||
<project name="librecovery" path="librecovery" remote="b2g" revision="84f2f2fce22605e17d511ff1767e54770067b5b5"/>
|
<project name="librecovery" path="librecovery" remote="b2g" revision="84f2f2fce22605e17d511ff1767e54770067b5b5"/>
|
||||||
|
@ -226,8 +226,16 @@ let wrapper = {
|
|||||||
|
|
||||||
// Button onclick handlers
|
// Button onclick handlers
|
||||||
function handleOldSync() {
|
function handleOldSync() {
|
||||||
// we just want to navigate the current tab to the new location...
|
let chromeWin = window
|
||||||
window.location = Services.urlFormatter.formatURLPref("app.support.baseURL") + "old-sync";
|
.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||||
|
.getInterface(Ci.nsIWebNavigation)
|
||||||
|
.QueryInterface(Ci.nsIDocShellTreeItem)
|
||||||
|
.rootTreeItem
|
||||||
|
.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||||
|
.getInterface(Ci.nsIDOMWindow)
|
||||||
|
.QueryInterface(Ci.nsIDOMChromeWindow);
|
||||||
|
let url = Services.urlFormatter.formatURLPref("app.support.baseURL") + "old-sync";
|
||||||
|
chromeWin.switchToTabHavingURI(url, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getStarted() {
|
function getStarted() {
|
||||||
|
@ -104,14 +104,18 @@ let gSyncUI = {
|
|||||||
if (!gBrowser)
|
if (!gBrowser)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
let button = document.getElementById("sync-button");
|
let syncButton = document.getElementById("sync-button");
|
||||||
|
let panelHorizontalButton = document.getElementById("PanelUI-fxa-status");
|
||||||
|
[syncButton, panelHorizontalButton].forEach(function(button) {
|
||||||
if (!button)
|
if (!button)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
button.removeAttribute("status");
|
button.removeAttribute("status");
|
||||||
|
});
|
||||||
|
|
||||||
|
if (needsSetup && syncButton)
|
||||||
|
syncButton.removeAttribute("tooltiptext");
|
||||||
|
|
||||||
this._updateLastSyncTime();
|
this._updateLastSyncTime();
|
||||||
if (needsSetup)
|
|
||||||
button.removeAttribute("tooltiptext");
|
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
@ -120,11 +124,12 @@ let gSyncUI = {
|
|||||||
if (!gBrowser)
|
if (!gBrowser)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
let button = document.getElementById("sync-button");
|
["sync-button", "PanelUI-fxa-status"].forEach(function(id) {
|
||||||
|
let button = document.getElementById(id);
|
||||||
if (!button)
|
if (!button)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
button.setAttribute("status", "active");
|
button.setAttribute("status", "active");
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
onSyncDelay: function SUI_onSyncDelay() {
|
onSyncDelay: function SUI_onSyncDelay() {
|
||||||
@ -161,6 +166,11 @@ let gSyncUI = {
|
|||||||
this.updateUI();
|
this.updateUI();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
// if we are still waiting for the identity manager to initialize, don't show errors
|
||||||
|
if (Weave.Status.login == Weave.LOGIN_FAILED_NOT_READY) {
|
||||||
|
this.updateUI();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
let title = this._stringBundle.GetStringFromName("error.login.title");
|
let title = this._stringBundle.GetStringFromName("error.login.title");
|
||||||
|
|
||||||
|
@ -389,7 +389,7 @@
|
|||||||
|
|
||||||
<!-- Sync Panel -->
|
<!-- Sync Panel -->
|
||||||
<panel id="sync-start-panel" class="sync-panel" type="arrow" hidden="true"
|
<panel id="sync-start-panel" class="sync-panel" type="arrow" hidden="true"
|
||||||
noautofocus="true" level="top" onclick="this.hidePopup();"
|
noautofocus="true" onclick="this.hidePopup();"
|
||||||
flip="slide">
|
flip="slide">
|
||||||
<hbox class="sync-panel-outer">
|
<hbox class="sync-panel-outer">
|
||||||
<image class="sync-panel-icon"/>
|
<image class="sync-panel-icon"/>
|
||||||
@ -409,7 +409,7 @@
|
|||||||
|
|
||||||
<!-- Sync Error Panel -->
|
<!-- Sync Error Panel -->
|
||||||
<panel id="sync-error-panel" class="sync-panel" type="arrow" hidden="true"
|
<panel id="sync-error-panel" class="sync-panel" type="arrow" hidden="true"
|
||||||
noautofocus="true" level="top" onclick="this.hidePopup();"
|
noautofocus="true" onclick="this.hidePopup();"
|
||||||
flip="slide">
|
flip="slide">
|
||||||
<hbox class="sync-panel-outer">
|
<hbox class="sync-panel-outer">
|
||||||
<image class="sync-panel-icon"/>
|
<image class="sync-panel-icon"/>
|
||||||
|
@ -269,6 +269,8 @@ skip-if = true # browser_drag.js is disabled, as it needs to be updated for the
|
|||||||
[browser_locationBarCommand.js]
|
[browser_locationBarCommand.js]
|
||||||
skip-if = os == "linux" # Intermittent failures, bug 917535
|
skip-if = os == "linux" # Intermittent failures, bug 917535
|
||||||
[browser_locationBarExternalLoad.js]
|
[browser_locationBarExternalLoad.js]
|
||||||
|
[browser_menuButtonFitts.js]
|
||||||
|
skip-if = os != "win" # The Fitts Law menu button is only supported on Windows (bug 969376)
|
||||||
[browser_middleMouse_inherit.js]
|
[browser_middleMouse_inherit.js]
|
||||||
[browser_minimize.js]
|
[browser_minimize.js]
|
||||||
[browser_mixedcontent_securityflags.js]
|
[browser_mixedcontent_securityflags.js]
|
||||||
|
32
browser/base/content/test/general/browser_menuButtonFitts.js
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
/* Any copyright is dedicated to the Public Domain.
|
||||||
|
* http://creativecommons.org/publicdomain/zero/1.0/
|
||||||
|
*/
|
||||||
|
|
||||||
|
function test () {
|
||||||
|
waitForExplicitFinish();
|
||||||
|
window.maximize();
|
||||||
|
|
||||||
|
// Find where the nav-bar is vertically.
|
||||||
|
var navBar = document.getElementById("nav-bar");
|
||||||
|
var boundingRect = navBar.getBoundingClientRect();
|
||||||
|
var yPixel = boundingRect.top + Math.floor(boundingRect.height / 2);
|
||||||
|
var xPixel = boundingRect.width - 1; // Use the last pixel of the screen since it is maximized.
|
||||||
|
|
||||||
|
function onPopupHidden() {
|
||||||
|
PanelUI.panel.removeEventListener("popuphidden", onPopupHidden);
|
||||||
|
window.restore();
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
function onPopupShown() {
|
||||||
|
PanelUI.panel.removeEventListener("popupshown", onPopupShown);
|
||||||
|
ok(true, "Clicking at the far edge of the window opened the menu popup.");
|
||||||
|
PanelUI.panel.addEventListener("popuphidden", onPopupHidden);
|
||||||
|
PanelUI.hide();
|
||||||
|
}
|
||||||
|
registerCleanupFunction(function() {
|
||||||
|
PanelUI.panel.removeEventListener("popupshown", onPopupShown);
|
||||||
|
PanelUI.panel.removeEventListener("popuphidden", onPopupHidden);
|
||||||
|
});
|
||||||
|
PanelUI.panel.addEventListener("popupshown", onPopupShown);
|
||||||
|
EventUtils.synthesizeMouseAtPoint(xPixel, yPixel, {}, window);
|
||||||
|
}
|
@ -30,13 +30,11 @@
|
|||||||
<menupopup id="customization-toolbar-menu" onpopupshowing="onViewToolbarsPopupShowing(event)"/>
|
<menupopup id="customization-toolbar-menu" onpopupshowing="onViewToolbarsPopupShowing(event)"/>
|
||||||
</button>
|
</button>
|
||||||
<spacer flex="1"/>
|
<spacer flex="1"/>
|
||||||
<label id="customization-undo-reset"
|
<button id="customization-undo-reset"
|
||||||
|
class="customizationmode-button"
|
||||||
hidden="true"
|
hidden="true"
|
||||||
onclick="gCustomizeMode.undoReset();"
|
oncommand="gCustomizeMode.undoReset();"
|
||||||
onkeypress="gCustomizeMode.undoReset();"
|
label="&undoCmd.label;"/>
|
||||||
class="text-link">
|
|
||||||
&undoCmd.label;
|
|
||||||
</label>
|
|
||||||
<button id="customization-reset-button" oncommand="gCustomizeMode.reset();" label="&customizeMode.restoreDefaults;" class="customizationmode-button"/>
|
<button id="customization-reset-button" oncommand="gCustomizeMode.reset();" label="&customizeMode.restoreDefaults;" class="customizationmode-button"/>
|
||||||
</hbox>
|
</hbox>
|
||||||
</vbox>
|
</vbox>
|
||||||
|
@ -4,8 +4,6 @@
|
|||||||
|
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
Cu.import("resource://gre/modules/Promise.jsm");
|
|
||||||
|
|
||||||
const isOSX = (Services.appinfo.OS === "Darwin");
|
const isOSX = (Services.appinfo.OS === "Darwin");
|
||||||
|
|
||||||
// Right-click on the home button should
|
// Right-click on the home button should
|
||||||
|
@ -4,6 +4,19 @@
|
|||||||
|
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
|
let openUILinkInCalled = false;
|
||||||
|
let expectOpenUILinkInCall = false;
|
||||||
|
this.originalOpenUILinkIn = openUILinkIn;
|
||||||
|
openUILinkIn = (aUrl, aWhichTab) => {
|
||||||
|
is(aUrl, Services.search.defaultEngine.searchForm, "Search page should be requested to open.");
|
||||||
|
is(aWhichTab, "current", "Should use the current tab for the search page.");
|
||||||
|
openUILinkInCalled = true;
|
||||||
|
if (!expectOpenUILinkInCall) {
|
||||||
|
ok(false, "OpenUILink in was called when it shouldn't have been.");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
logActiveElement();
|
||||||
|
|
||||||
// Ctrl+K should open the menu panel and focus the search bar if the search bar is in the panel.
|
// Ctrl+K should open the menu panel and focus the search bar if the search bar is in the panel.
|
||||||
add_task(function() {
|
add_task(function() {
|
||||||
let searchbar = document.getElementById("searchbar");
|
let searchbar = document.getElementById("searchbar");
|
||||||
@ -88,28 +101,29 @@ add_task(function() {
|
|||||||
|
|
||||||
// Ctrl+K should open the search page if the search bar has been customized out.
|
// Ctrl+K should open the search page if the search bar has been customized out.
|
||||||
add_task(function() {
|
add_task(function() {
|
||||||
this.originalOpenUILinkIn = openUILinkIn;
|
|
||||||
try {
|
try {
|
||||||
|
expectOpenUILinkInCall = true;
|
||||||
CustomizableUI.removeWidgetFromArea("search-container");
|
CustomizableUI.removeWidgetFromArea("search-container");
|
||||||
let placement = CustomizableUI.getPlacementOfWidget("search-container");
|
let placement = CustomizableUI.getPlacementOfWidget("search-container");
|
||||||
is(placement, null, "Search container should be in palette");
|
is(placement, null, "Search container should be in palette");
|
||||||
|
|
||||||
let openUILinkInCalled = false;
|
openUILinkInCalled = false;
|
||||||
openUILinkIn = (aUrl, aWhichTab) => {
|
|
||||||
is(aUrl, Services.search.defaultEngine.searchForm, "Search page should be requested to open.");
|
|
||||||
is(aWhichTab, "current", "Should use the current tab for the search page.");
|
|
||||||
openUILinkInCalled = true;
|
|
||||||
};
|
|
||||||
sendWebSearchKeyCommand();
|
sendWebSearchKeyCommand();
|
||||||
yield waitForCondition(function() openUILinkInCalled);
|
yield waitForCondition(function() openUILinkInCalled);
|
||||||
ok(openUILinkInCalled, "The search page should have been opened.")
|
ok(openUILinkInCalled, "The search page should have been opened.")
|
||||||
|
expectOpenUILinkInCall = false;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
ok(false, e);
|
ok(false, e);
|
||||||
}
|
}
|
||||||
openUILinkIn = this.originalOpenUILinkIn;
|
|
||||||
CustomizableUI.reset();
|
CustomizableUI.reset();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
registerCleanupFunction(function() {
|
||||||
|
openUILinkIn = this.originalOpenUILinkIn;
|
||||||
|
delete this.originalOpenUILinkIn;
|
||||||
|
});
|
||||||
|
|
||||||
function sendWebSearchKeyCommand() {
|
function sendWebSearchKeyCommand() {
|
||||||
if (Services.appinfo.OS === "Darwin")
|
if (Services.appinfo.OS === "Darwin")
|
||||||
EventUtils.synthesizeKey("k", { accelKey: true });
|
EventUtils.synthesizeKey("k", { accelKey: true });
|
||||||
@ -119,7 +133,10 @@ function sendWebSearchKeyCommand() {
|
|||||||
|
|
||||||
function logActiveElement() {
|
function logActiveElement() {
|
||||||
let element = document.activeElement;
|
let element = document.activeElement;
|
||||||
info("Active element: " + element ?
|
let str = "";
|
||||||
element + " (" + element.localName + "#" + element.id + "." + [...element.classList].join(".") + ")" :
|
while (element && element.parentNode) {
|
||||||
"null");
|
str = " (" + element.localName + "#" + element.id + "." + [...element.classList].join(".") + ") >" + str;
|
||||||
|
element = element.parentNode;
|
||||||
|
}
|
||||||
|
info("Active element: " + element ? str : "null");
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,6 @@
|
|||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
let Preferences = Cu.import("resource://gre/modules/Preferences.jsm", {}).Preferences;
|
let Preferences = Cu.import("resource://gre/modules/Preferences.jsm", {}).Preferences;
|
||||||
Cu.import("resource://gre/modules/Promise.jsm");
|
|
||||||
|
|
||||||
let tmp = {};
|
let tmp = {};
|
||||||
Cu.import("resource://gre/modules/FxAccounts.jsm", tmp);
|
Cu.import("resource://gre/modules/FxAccounts.jsm", tmp);
|
||||||
|
@ -175,6 +175,8 @@ var BrowserUI = {
|
|||||||
Util.dumpLn("Exception in delay load module:", ex.message);
|
Util.dumpLn("Exception in delay load module:", ex.message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BrowserUI._initFirstRunContent();
|
||||||
|
|
||||||
// check for left over crash reports and submit them if found.
|
// check for left over crash reports and submit them if found.
|
||||||
BrowserUI.startupCrashCheck();
|
BrowserUI.startupCrashCheck();
|
||||||
|
|
||||||
@ -1216,6 +1218,24 @@ var BrowserUI = {
|
|||||||
|
|
||||||
prefsClearButton.disabled = false;
|
prefsClearButton.disabled = false;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_initFirstRunContent: function () {
|
||||||
|
let dismissed = Services.prefs.getBoolPref("browser.firstrun-content.dismissed");
|
||||||
|
let firstRunCount = Services.prefs.getIntPref("browser.firstrun.count");
|
||||||
|
|
||||||
|
if (!dismissed && firstRunCount > 0) {
|
||||||
|
document.loadOverlay("chrome://browser/content/FirstRunContentOverlay.xul", null);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
firstRunContentDismiss: function() {
|
||||||
|
let firstRunElements = Elements.stack.querySelectorAll(".firstrun-content");
|
||||||
|
for (let node of firstRunElements) {
|
||||||
|
node.parentNode.removeChild(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
Services.prefs.setBoolPref("browser.firstrun-content.dismissed", true);
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
var PanelUI = {
|
var PanelUI = {
|
||||||
|
@ -0,0 +1,68 @@
|
|||||||
|
<?xml version="1.0"?>
|
||||||
|
|
||||||
|
<?xml-stylesheet href="chrome://browser/skin/firstruncontent.css" type="text/css"?>
|
||||||
|
|
||||||
|
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||||
|
|
||||||
|
<!DOCTYPE window [
|
||||||
|
<!ENTITY % browserDTD SYSTEM "chrome://browser/locale/browser.dtd">
|
||||||
|
%browserDTD;
|
||||||
|
<!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd">
|
||||||
|
%brandDTD;
|
||||||
|
]>
|
||||||
|
|
||||||
|
<overlay id="firstruncontent"
|
||||||
|
xmlns:html="http://www.w3.org/1999/xhtml"
|
||||||
|
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
|
||||||
|
|
||||||
|
<stack id="stack">
|
||||||
|
<box id="firstrun-bg-pane" insertafter="page" class="firstrun-content"></box>
|
||||||
|
|
||||||
|
<box id="instruction-tabs" class="firstrun-content">
|
||||||
|
<vbox class="instruction-content-container" align="center">
|
||||||
|
<image class="instruction-arrow arrow-top" />
|
||||||
|
<label class="instruction-label">&firstRunTabs.label;</label>
|
||||||
|
</vbox>
|
||||||
|
</box>
|
||||||
|
|
||||||
|
<box id="instruction-back" class="firstrun-content">
|
||||||
|
<vbox class="instruction-content-container" align="start">
|
||||||
|
<image class="instruction-arrow arrow-back" />
|
||||||
|
<label class="instruction-label">&firstRunBack.label;</label>
|
||||||
|
</vbox>
|
||||||
|
</box>
|
||||||
|
|
||||||
|
<box id="instruction-plus" class="firstrun-content">
|
||||||
|
<vbox class="instruction-content-container" align="end">
|
||||||
|
<image class="instruction-arrow arrow-forward" />
|
||||||
|
<label class="instruction-label">&firstRunNewTab.label;</label>
|
||||||
|
</vbox>
|
||||||
|
</box>
|
||||||
|
|
||||||
|
<appbar id="navbar">
|
||||||
|
|
||||||
|
<box id="instruction-star" class="firstrun-content">
|
||||||
|
<hbox class="instruction-content-container">
|
||||||
|
<label class="instruction-label">&firstRunStar.label;</label>
|
||||||
|
<image class="instruction-arrow arrow-down" />
|
||||||
|
</hbox>
|
||||||
|
</box>
|
||||||
|
|
||||||
|
<box id="instruction-pin" class="firstrun-content">
|
||||||
|
<vbox class="instruction-content-container" align="end">
|
||||||
|
<label class="instruction-label">&firstRunPin.label;</label>
|
||||||
|
<image class="instruction-arrow arrow-down-reverse" />
|
||||||
|
</vbox>
|
||||||
|
</box>
|
||||||
|
|
||||||
|
<box id="firstrun-gotit" class="firstrun-content">
|
||||||
|
<button class="firstrun-button" label="&firstRunGotIt.label;" oncommand="BrowserUI.firstRunContentDismiss()" />
|
||||||
|
</box>
|
||||||
|
|
||||||
|
</appbar>
|
||||||
|
|
||||||
|
|
||||||
|
</stack>
|
||||||
|
</overlay>
|
@ -99,6 +99,7 @@ chrome.jar:
|
|||||||
content/HistoryView.js (content/startui/HistoryView.js)
|
content/HistoryView.js (content/startui/HistoryView.js)
|
||||||
content/TopSitesView.js (content/startui/TopSitesView.js)
|
content/TopSitesView.js (content/startui/TopSitesView.js)
|
||||||
content/FirstRunOverlay.xul (content/startui/FirstRunOverlay.xul)
|
content/FirstRunOverlay.xul (content/startui/FirstRunOverlay.xul)
|
||||||
|
content/FirstRunContentOverlay.xul (content/startui/FirstRunContentOverlay.xul)
|
||||||
#ifdef MOZ_SERVICES_SYNC
|
#ifdef MOZ_SERVICES_SYNC
|
||||||
content/RemoteTabsView.js (content/startui/RemoteTabsView.js)
|
content/RemoteTabsView.js (content/startui/RemoteTabsView.js)
|
||||||
#endif
|
#endif
|
||||||
|
@ -138,12 +138,9 @@
|
|||||||
firstRunStar.label,
|
firstRunStar.label,
|
||||||
firstRunPin.label,
|
firstRunPin.label,
|
||||||
firstRunGotIt.label )
|
firstRunGotIt.label )
|
||||||
These strings appear as a content overlay the first few times a page
|
These strings appear as a content overlay the first time a page
|
||||||
is visited. Each one has an arrow pointing toward the feature it
|
is visited. Each one has an arrow pointing toward the feature it
|
||||||
references. The code to display these strings is not enabled yet,
|
references.
|
||||||
but will be soon. For now, you can see this mockup for an example
|
|
||||||
of how they are used:
|
|
||||||
https://bug949213.bugzilla.mozilla.org/attachment.cgi?id=8363973
|
|
||||||
-->
|
-->
|
||||||
<!ENTITY firstRunBack.label "Tap to go back to the previous page">
|
<!ENTITY firstRunBack.label "Tap to go back to the previous page">
|
||||||
<!ENTITY firstRunNewTab.label "Add a new tab to explore a new site">
|
<!ENTITY firstRunNewTab.label "Add a new tab to explore a new site">
|
||||||
|
@ -136,6 +136,8 @@ pref("browser.display.startUI.maxresults", 16);
|
|||||||
|
|
||||||
// Number of times to display firstrun instructions on new tab page
|
// Number of times to display firstrun instructions on new tab page
|
||||||
pref("browser.firstrun.count", 3);
|
pref("browser.firstrun.count", 3);
|
||||||
|
// Has the content first run been dismissed
|
||||||
|
pref("browser.firstrun-content.dismissed", false);
|
||||||
|
|
||||||
// Backspace and Shift+Backspace behavior
|
// Backspace and Shift+Backspace behavior
|
||||||
// 0 goes Back/Forward
|
// 0 goes Back/Forward
|
||||||
|
205
browser/metro/theme/firstruncontent.css
Normal file
@ -0,0 +1,205 @@
|
|||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
%filter substitution
|
||||||
|
%include defines.inc
|
||||||
|
|
||||||
|
/* Disable firstrun in some cases */
|
||||||
|
#stack[startpage] .firstrun-content {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 900px) {
|
||||||
|
.firstrun-content {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* bg transparent pane --------------- */
|
||||||
|
|
||||||
|
#firstrun-bg-pane {
|
||||||
|
position: absolute;
|
||||||
|
background-color: rgba(0, 0, 0, .8);
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Got it button ---------------------- */
|
||||||
|
|
||||||
|
#firstrun-gotit {
|
||||||
|
position: fixed;
|
||||||
|
bottom: calc(@toolbar_height@ + 64px);
|
||||||
|
left: 64px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#firstrun-gotit:-moz-locale-dir(rtl) {
|
||||||
|
left: auto;
|
||||||
|
right: 64px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.firstrun-button {
|
||||||
|
padding: 15px 45px;
|
||||||
|
font-family: "Segoe UI", sans-serif;
|
||||||
|
font-size: 25px;
|
||||||
|
background-image: -moz-linear-gradient(0deg, rgb(255, 128, 0) 0%, rgb(255, 149, 0) 100%);
|
||||||
|
border: 0;
|
||||||
|
color: #FFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Instructions arrows ---------------- */
|
||||||
|
|
||||||
|
.instruction-arrow {
|
||||||
|
width: 76px;
|
||||||
|
height: 76px;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
}
|
||||||
|
|
||||||
|
.instruction-arrow.arrow-top,
|
||||||
|
.instruction-arrow.arrow-down,
|
||||||
|
.instruction-arrow.arrow-down-reverse {
|
||||||
|
background-image: url("chrome://browser/skin/images/arrow-top-light.png");
|
||||||
|
}
|
||||||
|
|
||||||
|
.instruction-arrow.arrow-down,
|
||||||
|
.instruction-arrow.arrow-down-reverse:-moz-locale-dir(rtl) {
|
||||||
|
transform: rotate(180deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.instruction-arrow.arrow-down-reverse,
|
||||||
|
.instruction-arrow.arrow-down:-moz-locale-dir(rtl) {
|
||||||
|
transform: rotate(180deg) scaleX(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.instruction-arrow.arrow-back,
|
||||||
|
.instruction-arrow.arrow-forward {
|
||||||
|
background-image: url("chrome://browser/skin/images/arrow-left-light.png");
|
||||||
|
}
|
||||||
|
|
||||||
|
.instruction-arrow.arrow-forward,
|
||||||
|
.instruction-arrow.arrow-back:-moz-locale-dir(rtl) {
|
||||||
|
transform: rotate(180deg) scaleY(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.instruction-arrow.arrow-forward:-moz-locale-dir(rtl) {
|
||||||
|
transform: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Instructions text ------------------ */
|
||||||
|
|
||||||
|
.instruction-content-container {
|
||||||
|
width: 380px;
|
||||||
|
vertical-align: bottom;
|
||||||
|
}
|
||||||
|
|
||||||
|
#instruction-back .instruction-content-container,
|
||||||
|
#instruction-plus .instruction-content-container {
|
||||||
|
width: 320px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.instruction-label {
|
||||||
|
font-size: 18px;
|
||||||
|
color: #BBB;
|
||||||
|
line-height: 22px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#instruction-tabs {
|
||||||
|
position: fixed;
|
||||||
|
top: 10px;
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
}
|
||||||
|
|
||||||
|
#instruction-back {
|
||||||
|
position: fixed;
|
||||||
|
top: 50%;
|
||||||
|
left: 100px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#instruction-back:-moz-locale-dir(rtl) {
|
||||||
|
left: auto;
|
||||||
|
right: 100px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#instruction-plus {
|
||||||
|
position: fixed;
|
||||||
|
top: 50%;
|
||||||
|
right: 100px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#instruction-plus:-moz-locale-dir(rtl) {
|
||||||
|
right: auto;
|
||||||
|
left: 100px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#instruction-star {
|
||||||
|
position: fixed;
|
||||||
|
bottom: @toolbar_height@;
|
||||||
|
right: 145px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#instruction-star:-moz-locale-dir(rtl) {
|
||||||
|
right: auto;
|
||||||
|
left: 145px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#instruction-star .instruction-label {
|
||||||
|
max-width: 250px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#instruction-star .instruction-content-container:-moz-locale-dir(rtl) {
|
||||||
|
-moz-box-align: start;
|
||||||
|
vertical-align: top;
|
||||||
|
}
|
||||||
|
|
||||||
|
#instruction-pin {
|
||||||
|
position: fixed;
|
||||||
|
bottom: @toolbar_height@;
|
||||||
|
right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#instruction-pin:-moz-locale-dir(rtl) {
|
||||||
|
right: auto;
|
||||||
|
left: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#instruction-pin .instruction-label {
|
||||||
|
max-width: 250px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#instruction-pin .instruction-arrow {
|
||||||
|
-moz-margin-end: 65px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Higher resolution images ---------------------- */
|
||||||
|
|
||||||
|
@media (min-resolution: @min_res_140pc@) {
|
||||||
|
/* Load 140% image when scaled by 140% */
|
||||||
|
.instruction-arrow.arrow-top,
|
||||||
|
.instruction-arrow.arrow-down,
|
||||||
|
.instruction-arrow.arrow-down-reverse {
|
||||||
|
background-image: url("chrome://browser/skin/images/arrow-top-light@1.4x.png");
|
||||||
|
}
|
||||||
|
|
||||||
|
.instruction-arrow.arrow-back,
|
||||||
|
.instruction-arrow.arrow-forward {
|
||||||
|
background-image: url("chrome://browser/skin/images/arrow-left-light@1.4x.png");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-resolution: @min_res_180pc@) {
|
||||||
|
/* Load 180% image when scaled by 180% */
|
||||||
|
.instruction-arrow.arrow-top,
|
||||||
|
.instruction-arrow.arrow-down,
|
||||||
|
.instruction-arrow.arrow-down-reverse {
|
||||||
|
background-image: url("chrome://browser/skin/images/arrow-top-light@1.8x.png");
|
||||||
|
}
|
||||||
|
|
||||||
|
.instruction-arrow.arrow-back,
|
||||||
|
.instruction-arrow.arrow-forward {
|
||||||
|
background-image: url("chrome://browser/skin/images/arrow-left-light@1.8x.png");
|
||||||
|
}
|
||||||
|
}
|
BIN
browser/metro/theme/images/arrow-left-light.png
Normal file
After Width: | Height: | Size: 2.3 KiB |
BIN
browser/metro/theme/images/arrow-left-light@1.4x.png
Normal file
After Width: | Height: | Size: 3.0 KiB |
BIN
browser/metro/theme/images/arrow-left-light@1.8x.png
Normal file
After Width: | Height: | Size: 3.9 KiB |
BIN
browser/metro/theme/images/arrow-top-light.png
Normal file
After Width: | Height: | Size: 2.3 KiB |
BIN
browser/metro/theme/images/arrow-top-light@1.4x.png
Normal file
After Width: | Height: | Size: 3.1 KiB |
BIN
browser/metro/theme/images/arrow-top-light@1.8x.png
Normal file
After Width: | Height: | Size: 4.0 KiB |
@ -23,6 +23,7 @@ chrome.jar:
|
|||||||
skin/touchcontrols.css (touchcontrols.css)
|
skin/touchcontrols.css (touchcontrols.css)
|
||||||
* skin/netError.css (netError.css)
|
* skin/netError.css (netError.css)
|
||||||
skin/firstrun.css (firstrun.css)
|
skin/firstrun.css (firstrun.css)
|
||||||
|
* skin/firstruncontent.css (firstruncontent.css)
|
||||||
* skin/start.css (start.css)
|
* skin/start.css (start.css)
|
||||||
% override chrome://global/skin/about.css chrome://browser/skin/about.css
|
% override chrome://global/skin/about.css chrome://browser/skin/about.css
|
||||||
% override chrome://global/skin/media/videocontrols.css chrome://browser/skin/touchcontrols.css
|
% override chrome://global/skin/media/videocontrols.css chrome://browser/skin/touchcontrols.css
|
||||||
@ -160,6 +161,12 @@ chrome.jar:
|
|||||||
skin/images/arrow-left.png (images/arrow-left.png)
|
skin/images/arrow-left.png (images/arrow-left.png)
|
||||||
skin/images/arrow-left@1.4x.png (images/arrow-left@1.4x.png)
|
skin/images/arrow-left@1.4x.png (images/arrow-left@1.4x.png)
|
||||||
skin/images/arrow-left@1.8x.png (images/arrow-left@1.8x.png)
|
skin/images/arrow-left@1.8x.png (images/arrow-left@1.8x.png)
|
||||||
|
skin/images/arrow-top-light.png (images/arrow-top-light.png)
|
||||||
|
skin/images/arrow-top-light@1.4x.png (images/arrow-top-light@1.4x.png)
|
||||||
|
skin/images/arrow-top-light@1.8x.png (images/arrow-top-light@1.8x.png)
|
||||||
|
skin/images/arrow-left-light.png (images/arrow-left-light.png)
|
||||||
|
skin/images/arrow-left-light@1.4x.png (images/arrow-left-light@1.4x.png)
|
||||||
|
skin/images/arrow-left-light@1.8x.png (images/arrow-left-light@1.8x.png)
|
||||||
|
|
||||||
# AboutConfig specific:
|
# AboutConfig specific:
|
||||||
skin/images/textfield.png (images/textfield.png)
|
skin/images/textfield.png (images/textfield.png)
|
||||||
|
@ -591,6 +591,10 @@ toolbarbutton[sdk-button="true"][cui-areatype="toolbar"] > .toolbarbutton-icon {
|
|||||||
width: 32px;
|
width: 32px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#nav-bar #PanelUI-menu-button {
|
||||||
|
-moz-padding-start: 7px;
|
||||||
|
-moz-padding-end: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
:-moz-any(#TabsToolbar, #nav-bar) .toolbarbutton-1 > .toolbarbutton-menubutton-button:not([disabled]):hover > .toolbarbutton-icon,
|
:-moz-any(#TabsToolbar, #nav-bar) .toolbarbutton-1 > .toolbarbutton-menubutton-button:not([disabled]):hover > .toolbarbutton-icon,
|
||||||
:-moz-any(#TabsToolbar, #nav-bar) .toolbarbutton-1:not([buttonover]):not([open]):hover > .toolbarbutton-menubutton-dropmarker > .dropmarker-icon,
|
:-moz-any(#TabsToolbar, #nav-bar) .toolbarbutton-1:not([buttonover]):not([open]):hover > .toolbarbutton-menubutton-dropmarker > .dropmarker-icon,
|
||||||
|
@ -274,6 +274,7 @@ browser.jar:
|
|||||||
skin/classic/browser/sync-bg.png
|
skin/classic/browser/sync-bg.png
|
||||||
skin/classic/browser/sync-128.png
|
skin/classic/browser/sync-128.png
|
||||||
skin/classic/browser/sync-desktopIcon.png
|
skin/classic/browser/sync-desktopIcon.png
|
||||||
|
skin/classic/browser/sync-horizontalbar.png
|
||||||
skin/classic/browser/sync-mobileIcon.png
|
skin/classic/browser/sync-mobileIcon.png
|
||||||
skin/classic/browser/sync-notification-24.png
|
skin/classic/browser/sync-notification-24.png
|
||||||
skin/classic/browser/syncProgress-menuPanel.png
|
skin/classic/browser/syncProgress-menuPanel.png
|
||||||
@ -282,6 +283,7 @@ browser.jar:
|
|||||||
skin/classic/browser/syncCommon.css
|
skin/classic/browser/syncCommon.css
|
||||||
skin/classic/browser/syncQuota.css
|
skin/classic/browser/syncQuota.css
|
||||||
skin/classic/browser/syncProgress.css
|
skin/classic/browser/syncProgress.css
|
||||||
|
skin/classic/browser/syncProgress-horizontalbar.png
|
||||||
#endif
|
#endif
|
||||||
skin/classic/browser/notification-pluginNormal.png (../shared/plugins/notification-pluginNormal.png)
|
skin/classic/browser/notification-pluginNormal.png (../shared/plugins/notification-pluginNormal.png)
|
||||||
skin/classic/browser/notification-pluginAlert.png (../shared/plugins/notification-pluginAlert.png)
|
skin/classic/browser/notification-pluginAlert.png (../shared/plugins/notification-pluginAlert.png)
|
||||||
|
BIN
browser/themes/linux/sync-horizontalbar.png
Normal file
After Width: | Height: | Size: 721 B |
BIN
browser/themes/linux/syncProgress-horizontalbar.png
Normal file
After Width: | Height: | Size: 535 B |
@ -507,6 +507,13 @@ toolbar .toolbarbutton-1:not([type="menu-button"]),
|
|||||||
-moz-margin-start: 10px;
|
-moz-margin-start: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#nav-bar #PanelUI-menu-button {
|
||||||
|
margin-top: 0;
|
||||||
|
margin-bottom: 0;
|
||||||
|
-moz-margin-start: 9px;
|
||||||
|
-moz-margin-end: 7px;
|
||||||
|
}
|
||||||
|
|
||||||
@media not all and (min-resolution: 2dppx) {
|
@media not all and (min-resolution: 2dppx) {
|
||||||
%include ../shared/toolbarbuttons.inc.css
|
%include ../shared/toolbarbuttons.inc.css
|
||||||
%include ../shared/menupanel.inc.css
|
%include ../shared/menupanel.inc.css
|
||||||
|
@ -11,6 +11,14 @@
|
|||||||
background-size: 16px, auto;
|
background-size: 16px, auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#PanelUI-fxa-status {
|
||||||
|
list-style-image: url(chrome://browser/skin/sync-horizontalbar@2x.png);
|
||||||
|
}
|
||||||
|
|
||||||
|
#PanelUI-fxa-status[status="active"] {
|
||||||
|
list-style-image: url(chrome://browser/skin/syncProgress-horizontalbar@2x.png);
|
||||||
|
}
|
||||||
|
|
||||||
#PanelUI-customize {
|
#PanelUI-customize {
|
||||||
list-style-image: url(chrome://browser/skin/menuPanel-customize@2x.png);
|
list-style-image: url(chrome://browser/skin/menuPanel-customize@2x.png);
|
||||||
}
|
}
|
||||||
@ -27,6 +35,7 @@
|
|||||||
list-style-image: url(chrome://browser/skin/menuPanel-exit@2x.png);
|
list-style-image: url(chrome://browser/skin/menuPanel-exit@2x.png);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#PanelUI-fxa-status,
|
||||||
#PanelUI-customize,
|
#PanelUI-customize,
|
||||||
#PanelUI-help,
|
#PanelUI-help,
|
||||||
#PanelUI-quit {
|
#PanelUI-quit {
|
||||||
|
@ -385,12 +385,16 @@ browser.jar:
|
|||||||
skin/classic/browser/sync-bg.png
|
skin/classic/browser/sync-bg.png
|
||||||
skin/classic/browser/sync-128.png
|
skin/classic/browser/sync-128.png
|
||||||
skin/classic/browser/sync-desktopIcon.png
|
skin/classic/browser/sync-desktopIcon.png
|
||||||
|
skin/classic/browser/sync-horizontalbar.png
|
||||||
|
skin/classic/browser/sync-horizontalbar@2x.png
|
||||||
skin/classic/browser/sync-mobileIcon.png
|
skin/classic/browser/sync-mobileIcon.png
|
||||||
skin/classic/browser/sync-notification-24.png
|
skin/classic/browser/sync-notification-24.png
|
||||||
skin/classic/browser/syncSetup.css
|
skin/classic/browser/syncSetup.css
|
||||||
skin/classic/browser/syncCommon.css
|
skin/classic/browser/syncCommon.css
|
||||||
skin/classic/browser/syncQuota.css
|
skin/classic/browser/syncQuota.css
|
||||||
skin/classic/browser/syncProgress.css
|
skin/classic/browser/syncProgress.css
|
||||||
|
skin/classic/browser/syncProgress-horizontalbar.png
|
||||||
|
skin/classic/browser/syncProgress-horizontalbar@2x.png
|
||||||
#endif
|
#endif
|
||||||
skin/classic/browser/lion/keyhole-circle.png (keyhole-circle-lion.png)
|
skin/classic/browser/lion/keyhole-circle.png (keyhole-circle-lion.png)
|
||||||
skin/classic/browser/keyhole-circle@2x.png (keyhole-circle-lion@2x.png)
|
skin/classic/browser/keyhole-circle@2x.png (keyhole-circle-lion@2x.png)
|
||||||
|
BIN
browser/themes/osx/sync-horizontalbar.png
Normal file
After Width: | Height: | Size: 707 B |
BIN
browser/themes/osx/sync-horizontalbar@2x.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
browser/themes/osx/syncProgress-horizontalbar.png
Normal file
After Width: | Height: | Size: 537 B |
BIN
browser/themes/osx/syncProgress-horizontalbar@2x.png
Normal file
After Width: | Height: | Size: 1.0 KiB |
@ -105,13 +105,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
#customization-undo-reset {
|
#customization-undo-reset {
|
||||||
padding-left: 12px;
|
-moz-margin-end: 10px;
|
||||||
padding-right: 12px;
|
|
||||||
%ifdef XP_MACOSX
|
|
||||||
padding-top: 6px;
|
|
||||||
%else
|
|
||||||
padding-top: 7px;
|
|
||||||
%endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#main-window[customize-entered] #customization-panel-container {
|
#main-window[customize-entered] #customization-panel-container {
|
||||||
|
@ -26,10 +26,6 @@
|
|||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
}
|
}
|
||||||
|
|
||||||
#PanelUI-menu-button {
|
|
||||||
margin: 0 7px 0 9px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.panel-subviews {
|
.panel-subviews {
|
||||||
padding: 4px;
|
padding: 4px;
|
||||||
background-color: hsla(0,0%,100%,.97);
|
background-color: hsla(0,0%,100%,.97);
|
||||||
@ -404,6 +400,17 @@ toolbarpaletteitem[place="palette"] > toolbaritem > toolbarbutton {
|
|||||||
flex: 1;
|
flex: 1;
|
||||||
-moz-padding-start: 15px;
|
-moz-padding-start: 15px;
|
||||||
-moz-border-start-style: none;
|
-moz-border-start-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#PanelUI-fxa-status {
|
||||||
|
list-style-image: url(chrome://browser/skin/sync-horizontalbar.png);
|
||||||
|
}
|
||||||
|
|
||||||
|
#PanelUI-fxa-status[status="active"] {
|
||||||
|
list-style-image: url(chrome://browser/skin/syncProgress-horizontalbar.png);
|
||||||
|
}
|
||||||
|
|
||||||
|
#PanelUI-customize {
|
||||||
list-style-image: url(chrome://browser/skin/menuPanel-customize.png);
|
list-style-image: url(chrome://browser/skin/menuPanel-customize.png);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -496,6 +496,11 @@ menuitem.bookmark-item {
|
|||||||
-moz-box-pack: center;
|
-moz-box-pack: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#nav-bar #PanelUI-menu-button {
|
||||||
|
-moz-padding-start: 7px;
|
||||||
|
-moz-padding-end: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
#nav-bar .toolbarbutton-1[type=menu]:not(#back-button):not(#forward-button):not(#feed-button):not(#social-provider-button):not(#PanelUI-menu-button) {
|
#nav-bar .toolbarbutton-1[type=menu]:not(#back-button):not(#forward-button):not(#feed-button):not(#social-provider-button):not(#PanelUI-menu-button) {
|
||||||
padding-left: 5px;
|
padding-left: 5px;
|
||||||
padding-right: 5px;
|
padding-right: 5px;
|
||||||
|
@ -303,12 +303,16 @@ browser.jar:
|
|||||||
skin/classic/browser/sync-128.png
|
skin/classic/browser/sync-128.png
|
||||||
skin/classic/browser/sync-bg.png
|
skin/classic/browser/sync-bg.png
|
||||||
skin/classic/browser/sync-desktopIcon.png
|
skin/classic/browser/sync-desktopIcon.png
|
||||||
|
skin/classic/browser/sync-horizontalbar.png
|
||||||
|
skin/classic/browser/sync-horizontalbar-XPVista7.png
|
||||||
skin/classic/browser/sync-mobileIcon.png
|
skin/classic/browser/sync-mobileIcon.png
|
||||||
skin/classic/browser/sync-notification-24.png
|
skin/classic/browser/sync-notification-24.png
|
||||||
skin/classic/browser/syncSetup.css
|
skin/classic/browser/syncSetup.css
|
||||||
skin/classic/browser/syncCommon.css
|
skin/classic/browser/syncCommon.css
|
||||||
skin/classic/browser/syncQuota.css
|
skin/classic/browser/syncQuota.css
|
||||||
skin/classic/browser/syncProgress.css
|
skin/classic/browser/syncProgress.css
|
||||||
|
skin/classic/browser/syncProgress-horizontalbar.png
|
||||||
|
skin/classic/browser/syncProgress-horizontalbar-XPVista7.png
|
||||||
#endif
|
#endif
|
||||||
skin/classic/browser/devtools/tooltip/arrow-horizontal-dark.png (../shared/devtools/tooltip/arrow-horizontal-dark.png)
|
skin/classic/browser/devtools/tooltip/arrow-horizontal-dark.png (../shared/devtools/tooltip/arrow-horizontal-dark.png)
|
||||||
skin/classic/browser/devtools/tooltip/arrow-horizontal-dark@2x.png (../shared/devtools/tooltip/arrow-horizontal-dark@2x.png)
|
skin/classic/browser/devtools/tooltip/arrow-horizontal-dark@2x.png (../shared/devtools/tooltip/arrow-horizontal-dark@2x.png)
|
||||||
@ -617,12 +621,16 @@ browser.jar:
|
|||||||
skin/classic/aero/browser/sync-128.png
|
skin/classic/aero/browser/sync-128.png
|
||||||
skin/classic/aero/browser/sync-bg.png
|
skin/classic/aero/browser/sync-bg.png
|
||||||
skin/classic/aero/browser/sync-desktopIcon.png
|
skin/classic/aero/browser/sync-desktopIcon.png
|
||||||
|
skin/classic/aero/browser/sync-horizontalbar.png
|
||||||
|
skin/classic/aero/browser/sync-horizontalbar-XPVista7.png
|
||||||
skin/classic/aero/browser/sync-mobileIcon.png
|
skin/classic/aero/browser/sync-mobileIcon.png
|
||||||
skin/classic/aero/browser/sync-notification-24.png
|
skin/classic/aero/browser/sync-notification-24.png
|
||||||
skin/classic/aero/browser/syncSetup.css
|
skin/classic/aero/browser/syncSetup.css
|
||||||
skin/classic/aero/browser/syncCommon.css
|
skin/classic/aero/browser/syncCommon.css
|
||||||
skin/classic/aero/browser/syncQuota.css
|
skin/classic/aero/browser/syncQuota.css
|
||||||
skin/classic/aero/browser/syncProgress.css
|
skin/classic/aero/browser/syncProgress.css
|
||||||
|
skin/classic/aero/browser/syncProgress-horizontalbar.png
|
||||||
|
skin/classic/aero/browser/syncProgress-horizontalbar-XPVista7.png
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
skin/classic/aero/browser/devtools/tooltip/arrow-horizontal-dark.png (../shared/devtools/tooltip/arrow-horizontal-dark.png)
|
skin/classic/aero/browser/devtools/tooltip/arrow-horizontal-dark.png (../shared/devtools/tooltip/arrow-horizontal-dark.png)
|
||||||
@ -633,3 +641,6 @@ browser.jar:
|
|||||||
skin/classic/aero/browser/devtools/tooltip/arrow-horizontal-light@2x.png (../shared/devtools/tooltip/arrow-horizontal-light@2x.png)
|
skin/classic/aero/browser/devtools/tooltip/arrow-horizontal-light@2x.png (../shared/devtools/tooltip/arrow-horizontal-light@2x.png)
|
||||||
skin/classic/aero/browser/devtools/tooltip/arrow-vertical-light.png (../shared/devtools/tooltip/arrow-vertical-light.png)
|
skin/classic/aero/browser/devtools/tooltip/arrow-vertical-light.png (../shared/devtools/tooltip/arrow-vertical-light.png)
|
||||||
skin/classic/aero/browser/devtools/tooltip/arrow-vertical-light@2x.png (../shared/devtools/tooltip/arrow-vertical-light@2x.png)
|
skin/classic/aero/browser/devtools/tooltip/arrow-vertical-light@2x.png (../shared/devtools/tooltip/arrow-vertical-light@2x.png)
|
||||||
|
|
||||||
|
% override chrome://browser/skin/sync-horizontalbar.png chrome://browser/skin/sync-horizontalbar-XPVista7.png os=WINNT osversion<6.2
|
||||||
|
% override chrome://browser/skin/syncProgress-horizontalbar.png chrome://browser/skin/syncProgress-horizontalbar-XPVista7.png os=WINNT osversion<6.2
|
||||||
|
BIN
browser/themes/windows/sync-horizontalbar-XPVista7.png
Normal file
After Width: | Height: | Size: 721 B |
BIN
browser/themes/windows/sync-horizontalbar.png
Normal file
After Width: | Height: | Size: 546 B |
BIN
browser/themes/windows/syncProgress-horizontalbar-XPVista7.png
Normal file
After Width: | Height: | Size: 535 B |
BIN
browser/themes/windows/syncProgress-horizontalbar.png
Normal file
After Width: | Height: | Size: 370 B |
@ -26,7 +26,6 @@ SimpleTest.waitForExplicitFinish();
|
|||||||
var index = -1;
|
var index = -1;
|
||||||
var todayDate = new Date();
|
var todayDate = new Date();
|
||||||
var baseServeURL = "http://mochi.test:8888/tests/dom/downloads/tests/";
|
var baseServeURL = "http://mochi.test:8888/tests/dom/downloads/tests/";
|
||||||
var baseDownloadPath = "/mnt/sdcard/downloads/";
|
|
||||||
var lastKnownCurrentBytes = 0;
|
var lastKnownCurrentBytes = 0;
|
||||||
|
|
||||||
function next() {
|
function next() {
|
||||||
@ -45,11 +44,12 @@ function next() {
|
|||||||
function checkConsistentDownloadAttributes(download) {
|
function checkConsistentDownloadAttributes(download) {
|
||||||
var href = document.getElementById("download1").getAttribute("href");
|
var href = document.getElementById("download1").getAttribute("href");
|
||||||
var expectedServeURL = baseServeURL + href;
|
var expectedServeURL = baseServeURL + href;
|
||||||
var expectedDownloadPath = baseDownloadPath + "test.bin";
|
var destinationRegEx = /test\(?[0-9]*\)?\.bin$/;
|
||||||
|
|
||||||
// bug 945323: Download path isn't honoring download attribute
|
// bug 945323: Download path isn't honoring download attribute
|
||||||
todo(download.path === expectedDownloadPath,
|
ok(destinationRegEx.test(download.path),
|
||||||
"Download path = " + expectedDownloadPath);
|
"Download path '" + download.path +
|
||||||
|
"' should match '" + destinationRegEx + "' regexp.");
|
||||||
|
|
||||||
ok(download.startTime >= todayDate,
|
ok(download.startTime >= todayDate,
|
||||||
"Download start time should be greater than or equal to today");
|
"Download start time should be greater than or equal to today");
|
||||||
|
@ -959,6 +959,8 @@ PExternalHelperAppChild*
|
|||||||
ContentChild::AllocPExternalHelperAppChild(const OptionalURIParams& uri,
|
ContentChild::AllocPExternalHelperAppChild(const OptionalURIParams& uri,
|
||||||
const nsCString& aMimeContentType,
|
const nsCString& aMimeContentType,
|
||||||
const nsCString& aContentDisposition,
|
const nsCString& aContentDisposition,
|
||||||
|
const uint32_t& aContentDispositionHint,
|
||||||
|
const nsString& aContentDispositionFilename,
|
||||||
const bool& aForceSave,
|
const bool& aForceSave,
|
||||||
const int64_t& aContentLength,
|
const int64_t& aContentLength,
|
||||||
const OptionalURIParams& aReferrer,
|
const OptionalURIParams& aReferrer,
|
||||||
|
@ -144,6 +144,8 @@ public:
|
|||||||
const OptionalURIParams& uri,
|
const OptionalURIParams& uri,
|
||||||
const nsCString& aMimeContentType,
|
const nsCString& aMimeContentType,
|
||||||
const nsCString& aContentDisposition,
|
const nsCString& aContentDisposition,
|
||||||
|
const uint32_t& aContentDispositionHint,
|
||||||
|
const nsString& aContentDispositionFilename,
|
||||||
const bool& aForceSave,
|
const bool& aForceSave,
|
||||||
const int64_t& aContentLength,
|
const int64_t& aContentLength,
|
||||||
const OptionalURIParams& aReferrer,
|
const OptionalURIParams& aReferrer,
|
||||||
|
@ -2393,6 +2393,8 @@ PExternalHelperAppParent*
|
|||||||
ContentParent::AllocPExternalHelperAppParent(const OptionalURIParams& uri,
|
ContentParent::AllocPExternalHelperAppParent(const OptionalURIParams& uri,
|
||||||
const nsCString& aMimeContentType,
|
const nsCString& aMimeContentType,
|
||||||
const nsCString& aContentDisposition,
|
const nsCString& aContentDisposition,
|
||||||
|
const uint32_t& aContentDispositionHint,
|
||||||
|
const nsString& aContentDispositionFilename,
|
||||||
const bool& aForceSave,
|
const bool& aForceSave,
|
||||||
const int64_t& aContentLength,
|
const int64_t& aContentLength,
|
||||||
const OptionalURIParams& aReferrer,
|
const OptionalURIParams& aReferrer,
|
||||||
@ -2400,7 +2402,14 @@ ContentParent::AllocPExternalHelperAppParent(const OptionalURIParams& uri,
|
|||||||
{
|
{
|
||||||
ExternalHelperAppParent *parent = new ExternalHelperAppParent(uri, aContentLength);
|
ExternalHelperAppParent *parent = new ExternalHelperAppParent(uri, aContentLength);
|
||||||
parent->AddRef();
|
parent->AddRef();
|
||||||
parent->Init(this, aMimeContentType, aContentDisposition, aForceSave, aReferrer, aBrowser);
|
parent->Init(this,
|
||||||
|
aMimeContentType,
|
||||||
|
aContentDisposition,
|
||||||
|
aContentDispositionHint,
|
||||||
|
aContentDispositionFilename,
|
||||||
|
aForceSave,
|
||||||
|
aReferrer,
|
||||||
|
aBrowser);
|
||||||
return parent;
|
return parent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -367,6 +367,8 @@ private:
|
|||||||
const OptionalURIParams& aUri,
|
const OptionalURIParams& aUri,
|
||||||
const nsCString& aMimeContentType,
|
const nsCString& aMimeContentType,
|
||||||
const nsCString& aContentDisposition,
|
const nsCString& aContentDisposition,
|
||||||
|
const uint32_t& aContentDispositionHint,
|
||||||
|
const nsString& aContentDispositionFilename,
|
||||||
const bool& aForceSave,
|
const bool& aForceSave,
|
||||||
const int64_t& aContentLength,
|
const int64_t& aContentLength,
|
||||||
const OptionalURIParams& aReferrer,
|
const OptionalURIParams& aReferrer,
|
||||||
|
@ -422,9 +422,14 @@ parent:
|
|||||||
|
|
||||||
CloseAlert(nsString name, Principal principal);
|
CloseAlert(nsString name, Principal principal);
|
||||||
|
|
||||||
PExternalHelperApp(OptionalURIParams uri, nsCString aMimeContentType,
|
PExternalHelperApp(OptionalURIParams uri,
|
||||||
nsCString aContentDisposition, bool aForceSave,
|
nsCString aMimeContentType,
|
||||||
int64_t aContentLength, OptionalURIParams aReferrer,
|
nsCString aContentDisposition,
|
||||||
|
uint32_t aContentDispositionHint,
|
||||||
|
nsString aContentDispositionFilename,
|
||||||
|
bool aForceSave,
|
||||||
|
int64_t aContentLength,
|
||||||
|
OptionalURIParams aReferrer,
|
||||||
nullable PBrowser aBrowser);
|
nullable PBrowser aBrowser);
|
||||||
|
|
||||||
AddGeolocationListener(Principal principal, bool highAccuracy);
|
AddGeolocationListener(Principal principal, bool highAccuracy);
|
||||||
|
@ -576,9 +576,12 @@ sync_java_files = [
|
|||||||
'fxa/login/StateFactory.java',
|
'fxa/login/StateFactory.java',
|
||||||
'fxa/login/TokensAndKeysState.java',
|
'fxa/login/TokensAndKeysState.java',
|
||||||
'fxa/sync/FxAccountGlobalSession.java',
|
'fxa/sync/FxAccountGlobalSession.java',
|
||||||
|
'fxa/sync/FxAccountSchedulePolicy.java',
|
||||||
'fxa/sync/FxAccountSyncAdapter.java',
|
'fxa/sync/FxAccountSyncAdapter.java',
|
||||||
'fxa/sync/FxAccountSyncService.java',
|
'fxa/sync/FxAccountSyncService.java',
|
||||||
|
'fxa/sync/SchedulePolicy.java',
|
||||||
'sync/AlreadySyncingException.java',
|
'sync/AlreadySyncingException.java',
|
||||||
|
'sync/BackoffHandler.java',
|
||||||
'sync/BadRequiredFieldJSONException.java',
|
'sync/BadRequiredFieldJSONException.java',
|
||||||
'sync/CollectionKeys.java',
|
'sync/CollectionKeys.java',
|
||||||
'sync/CommandProcessor.java',
|
'sync/CommandProcessor.java',
|
||||||
@ -677,6 +680,7 @@ sync_java_files = [
|
|||||||
'sync/NonObjectJSONException.java',
|
'sync/NonObjectJSONException.java',
|
||||||
'sync/NullClusterURLException.java',
|
'sync/NullClusterURLException.java',
|
||||||
'sync/PersistedMetaGlobal.java',
|
'sync/PersistedMetaGlobal.java',
|
||||||
|
'sync/PrefsBackoffHandler.java',
|
||||||
'sync/PrefsSource.java',
|
'sync/PrefsSource.java',
|
||||||
'sync/receivers/SyncAccountDeletedReceiver.java',
|
'sync/receivers/SyncAccountDeletedReceiver.java',
|
||||||
'sync/receivers/SyncAccountDeletedService.java',
|
'sync/receivers/SyncAccountDeletedService.java',
|
||||||
|
154
mobile/android/base/fxa/sync/FxAccountSchedulePolicy.java
Normal file
@ -0,0 +1,154 @@
|
|||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
package org.mozilla.gecko.fxa.sync;
|
||||||
|
|
||||||
|
import org.mozilla.gecko.background.common.log.Logger;
|
||||||
|
import org.mozilla.gecko.db.BrowserContract;
|
||||||
|
import org.mozilla.gecko.fxa.authenticator.AndroidFxAccount;
|
||||||
|
import org.mozilla.gecko.fxa.login.State.Action;
|
||||||
|
import org.mozilla.gecko.sync.BackoffHandler;
|
||||||
|
|
||||||
|
import android.accounts.Account;
|
||||||
|
import android.content.ContentResolver;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.os.Bundle;
|
||||||
|
|
||||||
|
public class FxAccountSchedulePolicy implements SchedulePolicy {
|
||||||
|
private static final String LOG_TAG = "FxAccountSchedulePolicy";
|
||||||
|
|
||||||
|
// Our poll intervals are used to trigger automatic background syncs
|
||||||
|
// in the absence of user activity.
|
||||||
|
|
||||||
|
// If we're waiting for the user to click on a verification link, we
|
||||||
|
// sync very often in order to detect a change in state.
|
||||||
|
//
|
||||||
|
// In the case of unverified -> unverified (no transition), this should be
|
||||||
|
// very close to a single HTTP request (with the SyncAdapter overhead, of
|
||||||
|
// course, but that's not wildly different from alarm manager overhead).
|
||||||
|
//
|
||||||
|
// The /account/status endpoint is HAWK authed by sessionToken, so we still
|
||||||
|
// have to do some crypto no matter what.
|
||||||
|
|
||||||
|
// TODO: only do this for a while...
|
||||||
|
public static final long POLL_INTERVAL_PENDING_VERIFICATION = 60; // 1 minute.
|
||||||
|
|
||||||
|
// If we're in some kind of error state, there's no point trying often.
|
||||||
|
// This is not the same as a server-imposed backoff, which will be
|
||||||
|
// reflected dynamically.
|
||||||
|
public static final long POLL_INTERVAL_ERROR_STATE = 24 * 60 * 60; // 24 hours.
|
||||||
|
|
||||||
|
// If we're the only device, just sync a few times a day in case that
|
||||||
|
// changes.
|
||||||
|
public static final long POLL_INTERVAL_SINGLE_DEVICE_SEC = 8 * 60 * 60; // 8 hours.
|
||||||
|
|
||||||
|
// And if we know there are other devices, let's sync often enough that
|
||||||
|
// we'll likely be caught up (even if not completely) by the time you
|
||||||
|
// next use this device.
|
||||||
|
public static final long POLL_INTERVAL_MULTI_DEVICE_SEC = 30 * 60; // 30 minutes.
|
||||||
|
|
||||||
|
// Never sync more frequently than this, unless forced.
|
||||||
|
public static final long POLL_INTERVAL_MINIMUM_SEC = 45; // 45 seconds.
|
||||||
|
|
||||||
|
// This is used solely as an optimization for backoff handling, so it's not
|
||||||
|
// persisted.
|
||||||
|
private static volatile long POLL_INTERVAL_CURRENT_SEC = POLL_INTERVAL_SINGLE_DEVICE_SEC;
|
||||||
|
|
||||||
|
private final AndroidFxAccount account;
|
||||||
|
private final Context context;
|
||||||
|
|
||||||
|
public FxAccountSchedulePolicy(Context context, AndroidFxAccount account) {
|
||||||
|
this.account = account;
|
||||||
|
this.context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a millisecond timestamp in the future, offset from the current
|
||||||
|
* time by the provided amount.
|
||||||
|
* @param millis the duration by which to delay
|
||||||
|
* @return a timestamp.
|
||||||
|
*/
|
||||||
|
private static long delay(long millis) {
|
||||||
|
return System.currentTimeMillis() + millis;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the existing system periodic sync interval to the specified duration.
|
||||||
|
*
|
||||||
|
* @param intervalSeconds the requested period, which Android will vary by up to 4%.
|
||||||
|
*/
|
||||||
|
protected void requestPeriodicSync(final long intervalSeconds) {
|
||||||
|
final String authority = BrowserContract.AUTHORITY;
|
||||||
|
final Account account = this.account.getAndroidAccount();
|
||||||
|
this.context.getContentResolver();
|
||||||
|
Logger.info(LOG_TAG, "Scheduling periodic sync for " + intervalSeconds + ".");
|
||||||
|
ContentResolver.addPeriodicSync(account, authority, Bundle.EMPTY, intervalSeconds);
|
||||||
|
POLL_INTERVAL_CURRENT_SEC = intervalSeconds;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSuccessfulSync(int otherClientsCount) {
|
||||||
|
// This undoes the change made in observeBackoffMillis -- once we hit backoff we'll
|
||||||
|
// periodically sync at the backoff duration, but as soon as we succeed we'll switch
|
||||||
|
// into the client-count-dependent interval.
|
||||||
|
long interval = (otherClientsCount > 0) ? POLL_INTERVAL_MULTI_DEVICE_SEC : POLL_INTERVAL_SINGLE_DEVICE_SEC;
|
||||||
|
requestPeriodicSync(interval);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onHandleFinal(Action needed) {
|
||||||
|
switch (needed) {
|
||||||
|
case NeedsPassword:
|
||||||
|
case NeedsUpgrade:
|
||||||
|
requestPeriodicSync(POLL_INTERVAL_ERROR_STATE);
|
||||||
|
break;
|
||||||
|
case NeedsVerification:
|
||||||
|
requestPeriodicSync(POLL_INTERVAL_PENDING_VERIFICATION);
|
||||||
|
break;
|
||||||
|
case None:
|
||||||
|
// No action needed: we'll set the periodic sync interval
|
||||||
|
// when the sync finishes, via the SessionCallback.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onUpgradeRequired() {
|
||||||
|
// TODO: this shouldn't occur in FxA, but when we upgrade we
|
||||||
|
// need to reduce the interval again.
|
||||||
|
requestPeriodicSync(POLL_INTERVAL_ERROR_STATE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onUnauthorized() {
|
||||||
|
// TODO: this shouldn't occur in FxA, but when we fix our credentials
|
||||||
|
// we need to reduce the interval again.
|
||||||
|
requestPeriodicSync(POLL_INTERVAL_ERROR_STATE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void configureBackoffMillisOnBackoff(BackoffHandler backoffHandler, long backoffMillis, boolean onlyExtend) {
|
||||||
|
if (onlyExtend) {
|
||||||
|
backoffHandler.extendEarliestNextRequest(delay(backoffMillis));
|
||||||
|
} else {
|
||||||
|
backoffHandler.setEarliestNextRequest(delay(backoffMillis));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Yes, we might be part-way through the interval, in which case the backoff
|
||||||
|
// code will do its job. But we certainly don't want to reduce the interval
|
||||||
|
// if we're given a small backoff instruction.
|
||||||
|
// We'll reset the poll interval next time we sync without a backoff instruction.
|
||||||
|
if (backoffMillis > (POLL_INTERVAL_CURRENT_SEC * 1000)) {
|
||||||
|
// Slightly inflate the backoff duration to ensure that a fuzzed
|
||||||
|
// periodic sync doesn't occur before our backoff has passed. Android
|
||||||
|
// 19+ default to a 4% fuzz factor.
|
||||||
|
requestPeriodicSync((long) Math.ceil((1.05 * backoffMillis) / 1000));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void configureBackoffMillisBeforeSyncing(BackoffHandler backoffHandler) {
|
||||||
|
backoffHandler.setEarliestNextRequest(delay(POLL_INTERVAL_MINIMUM_SEC * 1000));
|
||||||
|
}
|
||||||
|
}
|
@ -5,6 +5,7 @@
|
|||||||
package org.mozilla.gecko.fxa.sync;
|
package org.mozilla.gecko.fxa.sync;
|
||||||
|
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
|
import java.net.URISyntaxException;
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
import java.util.concurrent.CountDownLatch;
|
import java.util.concurrent.CountDownLatch;
|
||||||
import java.util.concurrent.ExecutorService;
|
import java.util.concurrent.ExecutorService;
|
||||||
@ -31,8 +32,10 @@ import org.mozilla.gecko.fxa.login.Married;
|
|||||||
import org.mozilla.gecko.fxa.login.State;
|
import org.mozilla.gecko.fxa.login.State;
|
||||||
import org.mozilla.gecko.fxa.login.State.Action;
|
import org.mozilla.gecko.fxa.login.State.Action;
|
||||||
import org.mozilla.gecko.fxa.login.State.StateLabel;
|
import org.mozilla.gecko.fxa.login.State.StateLabel;
|
||||||
|
import org.mozilla.gecko.sync.BackoffHandler;
|
||||||
import org.mozilla.gecko.sync.ExtendedJSONObject;
|
import org.mozilla.gecko.sync.ExtendedJSONObject;
|
||||||
import org.mozilla.gecko.sync.GlobalSession;
|
import org.mozilla.gecko.sync.GlobalSession;
|
||||||
|
import org.mozilla.gecko.sync.PrefsBackoffHandler;
|
||||||
import org.mozilla.gecko.sync.SharedPreferencesClientsDataDelegate;
|
import org.mozilla.gecko.sync.SharedPreferencesClientsDataDelegate;
|
||||||
import org.mozilla.gecko.sync.SyncConfiguration;
|
import org.mozilla.gecko.sync.SyncConfiguration;
|
||||||
import org.mozilla.gecko.sync.Utils;
|
import org.mozilla.gecko.sync.Utils;
|
||||||
@ -52,11 +55,13 @@ import android.app.NotificationManager;
|
|||||||
import android.app.PendingIntent;
|
import android.app.PendingIntent;
|
||||||
import android.content.AbstractThreadedSyncAdapter;
|
import android.content.AbstractThreadedSyncAdapter;
|
||||||
import android.content.ContentProviderClient;
|
import android.content.ContentProviderClient;
|
||||||
|
import android.content.ContentResolver;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.content.SyncResult;
|
import android.content.SyncResult;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.os.SystemClock;
|
||||||
import android.support.v4.app.NotificationCompat;
|
import android.support.v4.app.NotificationCompat;
|
||||||
import android.support.v4.app.NotificationCompat.Builder;
|
import android.support.v4.app.NotificationCompat.Builder;
|
||||||
|
|
||||||
@ -65,6 +70,13 @@ public class FxAccountSyncAdapter extends AbstractThreadedSyncAdapter {
|
|||||||
|
|
||||||
public static final int NOTIFICATION_ID = LOG_TAG.hashCode();
|
public static final int NOTIFICATION_ID = LOG_TAG.hashCode();
|
||||||
|
|
||||||
|
// Tracks the last seen storage hostname for backoff purposes.
|
||||||
|
private static final String PREF_BACKOFF_STORAGE_HOST = "backoffStorageHost";
|
||||||
|
|
||||||
|
// Used to do cheap in-memory rate limiting.
|
||||||
|
private static final int MINIMUM_SYNC_DELAY_MILLIS = 5000;
|
||||||
|
private volatile long lastSyncRealtimeMillis = 0L;
|
||||||
|
|
||||||
protected final ExecutorService executor;
|
protected final ExecutorService executor;
|
||||||
|
|
||||||
public FxAccountSyncAdapter(Context context, boolean autoInitialize) {
|
public FxAccountSyncAdapter(Context context, boolean autoInitialize) {
|
||||||
@ -163,34 +175,57 @@ public class FxAccountSyncAdapter extends AbstractThreadedSyncAdapter {
|
|||||||
setSyncResultSoftError();
|
setSyncResultSoftError();
|
||||||
latch.countDown();
|
latch.countDown();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void postponeSync(long millis) {
|
||||||
|
if (millis <= 0) {
|
||||||
|
Logger.debug(LOG_TAG, "Asked to postpone sync, but zero delay. Short-circuiting.");
|
||||||
|
} else {
|
||||||
|
// delayUntil is broken: https://code.google.com/p/android/issues/detail?id=65669
|
||||||
|
// So we don't bother doing this. Instead, we rely on the periodic sync
|
||||||
|
// we schedule, and the backoff handler for the rest.
|
||||||
|
/*
|
||||||
|
Logger.warn(LOG_TAG, "Postponing sync by " + millis + "ms.");
|
||||||
|
syncResult.delayUntil = millis / 1000;
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
setSyncResultSoftError();
|
||||||
|
latch.countDown();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* A trivial global session callback that ignores backoff requests, upgrades,
|
|
||||||
* and authorization errors. It simply waits until the sync completes.
|
|
||||||
*/
|
|
||||||
protected static class SessionCallback implements BaseGlobalSessionCallback {
|
protected static class SessionCallback implements BaseGlobalSessionCallback {
|
||||||
protected final SyncDelegate syncDelegate;
|
protected final SyncDelegate syncDelegate;
|
||||||
|
protected final SchedulePolicy schedulePolicy;
|
||||||
|
protected volatile BackoffHandler storageBackoffHandler;
|
||||||
|
|
||||||
public SessionCallback(SyncDelegate syncDelegate) {
|
public SessionCallback(SyncDelegate syncDelegate, SchedulePolicy schedulePolicy) {
|
||||||
this.syncDelegate = syncDelegate;
|
this.syncDelegate = syncDelegate;
|
||||||
|
this.schedulePolicy = schedulePolicy;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBackoffHandler(BackoffHandler backoffHandler) {
|
||||||
|
this.storageBackoffHandler = backoffHandler;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean shouldBackOff() {
|
public boolean shouldBackOffStorage() {
|
||||||
return false;
|
return storageBackoffHandler.delayMilliseconds() > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void requestBackoff(long backoff) {
|
public void requestBackoff(long backoffMillis) {
|
||||||
|
final boolean onlyExtend = true; // Because we trust what the storage server says.
|
||||||
|
schedulePolicy.configureBackoffMillisOnBackoff(storageBackoffHandler, backoffMillis, onlyExtend);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void informUpgradeRequiredResponse(GlobalSession session) {
|
public void informUpgradeRequiredResponse(GlobalSession session) {
|
||||||
|
schedulePolicy.onUpgradeRequired();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void informUnauthorizedResponse(GlobalSession globalSession, URI oldClusterURL) {
|
public void informUnauthorizedResponse(GlobalSession globalSession, URI oldClusterURL) {
|
||||||
|
schedulePolicy.onUnauthorized();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -200,35 +235,111 @@ public class FxAccountSyncAdapter extends AbstractThreadedSyncAdapter {
|
|||||||
@Override
|
@Override
|
||||||
public void handleSuccess(GlobalSession globalSession) {
|
public void handleSuccess(GlobalSession globalSession) {
|
||||||
Logger.info(LOG_TAG, "Global session succeeded.");
|
Logger.info(LOG_TAG, "Global session succeeded.");
|
||||||
|
|
||||||
|
// Get the number of clients, so we can schedule the sync interval accordingly.
|
||||||
|
try {
|
||||||
|
int otherClientsCount = globalSession.getClientsDelegate().getClientsCount();
|
||||||
|
Logger.debug(LOG_TAG, "" + otherClientsCount + " other client(s).");
|
||||||
|
this.schedulePolicy.onSuccessfulSync(otherClientsCount);
|
||||||
|
} finally {
|
||||||
|
// Continue with the usual success flow.
|
||||||
syncDelegate.handleSuccess();
|
syncDelegate.handleSuccess();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handleError(GlobalSession globalSession, Exception e) {
|
public void handleError(GlobalSession globalSession, Exception e) {
|
||||||
Logger.warn(LOG_TAG, "Global session failed."); // Exception will be dumped by delegate below.
|
Logger.warn(LOG_TAG, "Global session failed."); // Exception will be dumped by delegate below.
|
||||||
syncDelegate.handleError(e);
|
syncDelegate.handleError(e);
|
||||||
|
// TODO: should we reduce the periodic sync interval?
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handleAborted(GlobalSession globalSession, String reason) {
|
public void handleAborted(GlobalSession globalSession, String reason) {
|
||||||
Logger.warn(LOG_TAG, "Global session aborted: " + reason);
|
Logger.warn(LOG_TAG, "Global session aborted: " + reason);
|
||||||
syncDelegate.handleError(null);
|
syncDelegate.handleError(null);
|
||||||
|
// TODO: should we reduce the periodic sync interval?
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return true if the provided {@link BackoffHandler} isn't reporting that we're in
|
||||||
|
* a backoff state, or the provided {@link Bundle} contains flags that indicate
|
||||||
|
* we should force a sync.
|
||||||
|
*/
|
||||||
|
private boolean shouldPerformSync(final BackoffHandler backoffHandler, final String kind, final Bundle extras) {
|
||||||
|
final long delay = backoffHandler.delayMilliseconds();
|
||||||
|
if (delay <= 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (extras == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
final boolean forced = extras.getBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, false);
|
||||||
|
if (forced) {
|
||||||
|
Logger.info(LOG_TAG, "Forced sync (" + kind + "): overruling remaining backoff of " + delay + "ms.");
|
||||||
|
} else {
|
||||||
|
Logger.info(LOG_TAG, "Not syncing (" + kind + "): must wait another " + delay + "ms.");
|
||||||
|
}
|
||||||
|
return forced;
|
||||||
|
}
|
||||||
|
|
||||||
protected void syncWithAssertion(final String audience,
|
protected void syncWithAssertion(final String audience,
|
||||||
final String assertion,
|
final String assertion,
|
||||||
URI tokenServerEndpointURI,
|
final URI tokenServerEndpointURI,
|
||||||
|
final BackoffHandler tokenBackoffHandler,
|
||||||
final SharedPreferences sharedPrefs,
|
final SharedPreferences sharedPrefs,
|
||||||
final KeyBundle syncKeyBundle,
|
final KeyBundle syncKeyBundle,
|
||||||
final String clientState,
|
final String clientState,
|
||||||
final BaseGlobalSessionCallback callback) {
|
final SessionCallback callback,
|
||||||
TokenServerClient tokenServerclient = new TokenServerClient(tokenServerEndpointURI, executor);
|
final Bundle extras) {
|
||||||
tokenServerclient.getTokenFromBrowserIDAssertion(assertion, true, clientState, new TokenServerClientDelegate() {
|
final TokenServerClientDelegate delegate = new TokenServerClientDelegate() {
|
||||||
|
private boolean didReceiveBackoff = false;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handleSuccess(final TokenServerToken token) {
|
public void handleSuccess(final TokenServerToken token) {
|
||||||
FxAccountConstants.pii(LOG_TAG, "Got token! uid is " + token.uid + " and endpoint is " + token.endpoint + ".");
|
FxAccountConstants.pii(LOG_TAG, "Got token! uid is " + token.uid + " and endpoint is " + token.endpoint + ".");
|
||||||
|
|
||||||
|
if (!didReceiveBackoff) {
|
||||||
|
// We must be OK to touch this token server.
|
||||||
|
tokenBackoffHandler.setEarliestNextRequest(0L);
|
||||||
|
}
|
||||||
|
|
||||||
|
final URI storageServerURI;
|
||||||
|
try {
|
||||||
|
storageServerURI = new URI(token.endpoint);
|
||||||
|
} catch (URISyntaxException e) {
|
||||||
|
handleError(e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final String storageHostname = storageServerURI.getHost();
|
||||||
|
|
||||||
|
// We back off on a per-host basis. When we have an endpoint URI from a token, we
|
||||||
|
// can check on the backoff status for that host.
|
||||||
|
// If we're supposed to be backing off, we abort the not-yet-started session.
|
||||||
|
final BackoffHandler storageBackoffHandler = new PrefsBackoffHandler(sharedPrefs, "sync.storage");
|
||||||
|
callback.setBackoffHandler(storageBackoffHandler);
|
||||||
|
|
||||||
|
String lastStorageHost = sharedPrefs.getString(PREF_BACKOFF_STORAGE_HOST, null);
|
||||||
|
final boolean storageHostIsUnchanged = lastStorageHost != null &&
|
||||||
|
lastStorageHost.equalsIgnoreCase(storageHostname);
|
||||||
|
if (storageHostIsUnchanged) {
|
||||||
|
Logger.debug(LOG_TAG, "Storage host is unchanged.");
|
||||||
|
if (!shouldPerformSync(storageBackoffHandler, "storage", extras)) {
|
||||||
|
Logger.info(LOG_TAG, "Not syncing: storage server requested backoff.");
|
||||||
|
callback.handleAborted(null, "Storage backoff");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Logger.debug(LOG_TAG, "Received new storage host.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Invalidate the previous backoff, because our storage host has changed,
|
||||||
|
// or we never had one at all, or we're OK to sync.
|
||||||
|
storageBackoffHandler.setEarliestNextRequest(0L);
|
||||||
|
|
||||||
FxAccountGlobalSession globalSession = null;
|
FxAccountGlobalSession globalSession = null;
|
||||||
try {
|
try {
|
||||||
ClientsDataDelegate clientsDataDelegate = new SharedPreferencesClientsDataDelegate(sharedPrefs);
|
ClientsDataDelegate clientsDataDelegate = new SharedPreferencesClientsDataDelegate(sharedPrefs);
|
||||||
@ -237,15 +348,13 @@ public class FxAccountSyncAdapter extends AbstractThreadedSyncAdapter {
|
|||||||
// skew adjustment that the HawkAuthHeaderProvider uses to adjust its
|
// skew adjustment that the HawkAuthHeaderProvider uses to adjust its
|
||||||
// timestamps. Eventually we might want this to adapt within the scope of a
|
// timestamps. Eventually we might want this to adapt within the scope of a
|
||||||
// global session.
|
// global session.
|
||||||
final SkewHandler tokenServerSkewHandler = SkewHandler.getSkewHandlerFromEndpointString(token.endpoint);
|
final SkewHandler tokenServerSkewHandler = SkewHandler.getSkewHandlerForHostname(storageHostname);
|
||||||
final long tokenServerSkew = tokenServerSkewHandler.getSkewInSeconds();
|
final long tokenServerSkew = tokenServerSkewHandler.getSkewInSeconds();
|
||||||
AuthHeaderProvider authHeaderProvider = new HawkAuthHeaderProvider(token.id, token.key.getBytes("UTF-8"), false, tokenServerSkew);
|
final AuthHeaderProvider authHeaderProvider = new HawkAuthHeaderProvider(token.id, token.key.getBytes("UTF-8"), false, tokenServerSkew);
|
||||||
|
|
||||||
final Context context = getContext();
|
final Context context = getContext();
|
||||||
SyncConfiguration syncConfig = new SyncConfiguration(token.uid, authHeaderProvider, sharedPrefs, syncKeyBundle);
|
final SyncConfiguration syncConfig = new SyncConfiguration(token.uid, authHeaderProvider, sharedPrefs, syncKeyBundle);
|
||||||
|
|
||||||
// EXTRAS
|
|
||||||
final Bundle extras = Bundle.EMPTY;
|
|
||||||
globalSession = new FxAccountGlobalSession(token.endpoint, syncConfig, callback, context, extras, clientsDataDelegate);
|
globalSession = new FxAccountGlobalSession(token.endpoint, syncConfig, callback, context, extras, clientsDataDelegate);
|
||||||
globalSession.start();
|
globalSession.start();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
@ -268,8 +377,22 @@ public class FxAccountSyncAdapter extends AbstractThreadedSyncAdapter {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handleBackoff(int backoffSeconds) {
|
public void handleBackoff(int backoffSeconds) {
|
||||||
|
// This is the token server telling us to back off.
|
||||||
|
Logger.info(LOG_TAG, "Token server requesting backoff of " + backoffSeconds + "s. Backoff handler: " + tokenBackoffHandler);
|
||||||
|
didReceiveBackoff = true;
|
||||||
|
|
||||||
|
// If we've already stored a backoff, overrule it: we only use the server
|
||||||
|
// value for token server scheduling.
|
||||||
|
tokenBackoffHandler.setEarliestNextRequest(delay(backoffSeconds * 1000));
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
private long delay(long delay) {
|
||||||
|
return System.currentTimeMillis() + delay;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
TokenServerClient tokenServerclient = new TokenServerClient(tokenServerEndpointURI, executor);
|
||||||
|
tokenServerclient.getTokenFromBrowserIDAssertion(assertion, true, clientState, delegate);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static void showNotification(Context context, State state) {
|
protected static void showNotification(Context context, State state) {
|
||||||
@ -312,6 +435,13 @@ public class FxAccountSyncAdapter extends AbstractThreadedSyncAdapter {
|
|||||||
Logger.setThreadLogTag(FxAccountConstants.GLOBAL_LOG_TAG);
|
Logger.setThreadLogTag(FxAccountConstants.GLOBAL_LOG_TAG);
|
||||||
Logger.resetLogging();
|
Logger.resetLogging();
|
||||||
|
|
||||||
|
if (this.lastSyncRealtimeMillis > 0L &&
|
||||||
|
(this.lastSyncRealtimeMillis + MINIMUM_SYNC_DELAY_MILLIS) > SystemClock.elapsedRealtime()) {
|
||||||
|
Logger.info(LOG_TAG, "Not syncing FxAccount " + Utils.obfuscateEmail(account.name) +
|
||||||
|
": minimum interval not met.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
Logger.info(LOG_TAG, "Syncing FxAccount" +
|
Logger.info(LOG_TAG, "Syncing FxAccount" +
|
||||||
" account named like " + Utils.obfuscateEmail(account.name) +
|
" account named like " + Utils.obfuscateEmail(account.name) +
|
||||||
" for authority " + authority +
|
" for authority " + authority +
|
||||||
@ -324,10 +454,10 @@ public class FxAccountSyncAdapter extends AbstractThreadedSyncAdapter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
final CountDownLatch latch = new CountDownLatch(1);
|
final CountDownLatch latch = new CountDownLatch(1);
|
||||||
final SyncDelegate syncDelegate = new SyncDelegate(getContext(), latch, syncResult, fxAccount);
|
final SyncDelegate syncDelegate = new SyncDelegate(context, latch, syncResult, fxAccount);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
State state;
|
final State state;
|
||||||
try {
|
try {
|
||||||
state = fxAccount.getState();
|
state = fxAccount.getState();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
@ -335,9 +465,22 @@ public class FxAccountSyncAdapter extends AbstractThreadedSyncAdapter {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// This will be the same chunk of SharedPreferences that GlobalSession/SyncConfiguration will later create.
|
// This will be the same chunk of SharedPreferences that we pass through to GlobalSession/SyncConfiguration.
|
||||||
final SharedPreferences sharedPrefs = fxAccount.getSyncPrefs();
|
final SharedPreferences sharedPrefs = fxAccount.getSyncPrefs();
|
||||||
|
|
||||||
|
// Check for a backoff right here.
|
||||||
|
final BackoffHandler schedulerBackoffHandler = new PrefsBackoffHandler(sharedPrefs, "scheduler");
|
||||||
|
if (!shouldPerformSync(schedulerBackoffHandler, "scheduler", extras)) {
|
||||||
|
Logger.info(LOG_TAG, "Not syncing (scheduler).");
|
||||||
|
syncDelegate.postponeSync(schedulerBackoffHandler.delayMilliseconds());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final SchedulePolicy schedulePolicy = new FxAccountSchedulePolicy(context, fxAccount);
|
||||||
|
|
||||||
|
// Set a small scheduled 'backoff' to rate-limit the next sync.
|
||||||
|
schedulePolicy.configureBackoffMillisBeforeSyncing(schedulerBackoffHandler);
|
||||||
|
|
||||||
final String audience = fxAccount.getAudience();
|
final String audience = fxAccount.getAudience();
|
||||||
final String authServerEndpoint = fxAccount.getAccountServerURI();
|
final String authServerEndpoint = fxAccount.getAccountServerURI();
|
||||||
final String tokenServerEndpoint = fxAccount.getTokenServerURI();
|
final String tokenServerEndpoint = fxAccount.getTokenServerURI();
|
||||||
@ -372,25 +515,59 @@ public class FxAccountSyncAdapter extends AbstractThreadedSyncAdapter {
|
|||||||
Logger.info(LOG_TAG, "handleTransition: " + transition + " to " + state.getStateLabel());
|
Logger.info(LOG_TAG, "handleTransition: " + transition + " to " + state.getStateLabel());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean shouldRequestToken(final BackoffHandler tokenBackoffHandler, final Bundle extras) {
|
||||||
|
return shouldPerformSync(tokenBackoffHandler, "token", extras);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handleFinal(State state) {
|
public void handleFinal(State state) {
|
||||||
Logger.info(LOG_TAG, "handleFinal: in " + state.getStateLabel());
|
Logger.info(LOG_TAG, "handleFinal: in " + state.getStateLabel());
|
||||||
fxAccount.setState(state);
|
fxAccount.setState(state);
|
||||||
|
schedulePolicy.onHandleFinal(state.getNeededAction());
|
||||||
try {
|
try {
|
||||||
if (state.getStateLabel() != StateLabel.Married) {
|
if (state.getStateLabel() != StateLabel.Married) {
|
||||||
syncDelegate.handleCannotSync(state);
|
syncDelegate.handleCannotSync(state);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Married married = (Married) state;
|
final Married married = (Married) state;
|
||||||
final long now = System.currentTimeMillis();
|
|
||||||
SkewHandler skewHandler = SkewHandler.getSkewHandlerFromEndpointString(tokenServerEndpoint);
|
SkewHandler skewHandler = SkewHandler.getSkewHandlerFromEndpointString(tokenServerEndpoint);
|
||||||
String assertion = married.generateAssertion(audience, JSONWebTokenUtils.DEFAULT_ASSERTION_ISSUER,
|
final long now = System.currentTimeMillis();
|
||||||
now + skewHandler.getSkewInMillis(),
|
final long issuedAtMillis = now + skewHandler.getSkewInMillis();
|
||||||
this.getAssertionDurationInMilliseconds());
|
final long assertionDurationMillis = this.getAssertionDurationInMilliseconds();
|
||||||
final BaseGlobalSessionCallback sessionCallback = new SessionCallback(syncDelegate);
|
final String assertion = married.generateAssertion(audience, JSONWebTokenUtils.DEFAULT_ASSERTION_ISSUER, issuedAtMillis, assertionDurationMillis);
|
||||||
syncWithAssertion(audience, assertion, tokenServerEndpointURI, sharedPrefs, married.getSyncKeyBundle(), married.getClientState(), sessionCallback);
|
|
||||||
|
/*
|
||||||
|
* At this point we're in the correct state to sync, and we're ready to fetch
|
||||||
|
* a token and do some work.
|
||||||
|
*
|
||||||
|
* But first we need to do two things:
|
||||||
|
* 1. Check to see whether we're in a backoff situation for the token server.
|
||||||
|
* If we are, but we're not forcing a sync, then we go no further.
|
||||||
|
* 2. Clear an existing backoff (if we're syncing it doesn't matter, and if
|
||||||
|
* we're forcing we'll get a new backoff if things are still bad).
|
||||||
|
*
|
||||||
|
* Note that we don't check the storage backoff before the token dance: the token
|
||||||
|
* server tells us which server we're syncing to!
|
||||||
|
*
|
||||||
|
* That logic lives in the TokenServerClientDelegate elsewhere in this file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Strictly speaking this backoff check could be done prior to walking through
|
||||||
|
// the login state machine, allowing us to short-circuit sooner.
|
||||||
|
// We don't expect many token server backoffs, and most users will be sitting
|
||||||
|
// in the Married state, so instead we simply do this here, once.
|
||||||
|
final BackoffHandler tokenBackoffHandler = new PrefsBackoffHandler(sharedPrefs, "token");
|
||||||
|
if (!shouldRequestToken(tokenBackoffHandler, extras)) {
|
||||||
|
Logger.info(LOG_TAG, "Not syncing (token server).");
|
||||||
|
syncDelegate.postponeSync(tokenBackoffHandler.delayMilliseconds());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final SessionCallback sessionCallback = new SessionCallback(syncDelegate, schedulePolicy);
|
||||||
|
final KeyBundle syncKeyBundle = married.getSyncKeyBundle();
|
||||||
|
final String clientState = married.getClientState();
|
||||||
|
syncWithAssertion(audience, assertion, tokenServerEndpointURI, tokenBackoffHandler, sharedPrefs, syncKeyBundle, clientState, sessionCallback, extras);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
syncDelegate.handleError(e);
|
syncDelegate.handleError(e);
|
||||||
return;
|
return;
|
||||||
@ -404,7 +581,8 @@ public class FxAccountSyncAdapter extends AbstractThreadedSyncAdapter {
|
|||||||
syncDelegate.handleError(e);
|
syncDelegate.handleError(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
Logger.error(LOG_TAG, "Syncing done.");
|
Logger.info(LOG_TAG, "Syncing done.");
|
||||||
|
lastSyncRealtimeMillis = SystemClock.elapsedRealtime();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void debugAssertion(String audience, String assertion) {
|
protected void debugAssertion(String audience, String assertion) {
|
||||||
|
42
mobile/android/base/fxa/sync/SchedulePolicy.java
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
package org.mozilla.gecko.fxa.sync;
|
||||||
|
|
||||||
|
import org.mozilla.gecko.fxa.login.State.Action;
|
||||||
|
import org.mozilla.gecko.sync.BackoffHandler;
|
||||||
|
|
||||||
|
public interface SchedulePolicy {
|
||||||
|
/**
|
||||||
|
* Call this with the number of other clients syncing to the account.
|
||||||
|
*/
|
||||||
|
public abstract void onSuccessfulSync(int otherClientsCount);
|
||||||
|
public abstract void onHandleFinal(Action needed);
|
||||||
|
public abstract void onUpgradeRequired();
|
||||||
|
public abstract void onUnauthorized();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Before a sync we typically wish to adjust our backoff policy. This cleans
|
||||||
|
* the slate prior to encountering a new backoff, and also functions as a rate
|
||||||
|
* limiter.
|
||||||
|
*
|
||||||
|
* The {@link SchedulePolicy} acts as a controller for the {@link BackoffHandler}.
|
||||||
|
* As a result of calling these two methods, the {@link BackoffHandler} will be
|
||||||
|
* mutated, and additional side-effects (such as scheduling periodic syncs) can
|
||||||
|
* occur.
|
||||||
|
*
|
||||||
|
* @param backoffHandler the backoff handler to configure.
|
||||||
|
*/
|
||||||
|
public abstract void configureBackoffMillisBeforeSyncing(BackoffHandler backoffHandler);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* We received an explicit backoff instruction, typically from a server.
|
||||||
|
*
|
||||||
|
* @param onlyExtend
|
||||||
|
* if <code>true</code>, the backoff handler will be asked to update
|
||||||
|
* its backoff only if the provided value is greater than the current
|
||||||
|
* backoff.
|
||||||
|
*/
|
||||||
|
public abstract void configureBackoffMillisOnBackoff(BackoffHandler backoffHandler, long backoffMillis, boolean onlyExtend);
|
||||||
|
}
|
@ -244,18 +244,17 @@ abstract class HomeFragment extends Fragment {
|
|||||||
if (mInReadingList) {
|
if (mInReadingList) {
|
||||||
GeckoEvent e = GeckoEvent.createBroadcastEvent("Reader:Remove", mUrl);
|
GeckoEvent e = GeckoEvent.createBroadcastEvent("Reader:Remove", mUrl);
|
||||||
GeckoAppShell.sendEventToGecko(e);
|
GeckoAppShell.sendEventToGecko(e);
|
||||||
|
|
||||||
int count = BrowserDB.getReadingListCount(cr);
|
|
||||||
e = GeckoEvent.createBroadcastEvent("Reader:ListCountUpdated", Integer.toString(count));
|
|
||||||
GeckoAppShell.sendEventToGecko(e);
|
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onPostExecute(Void result) {
|
public void onPostExecute(Void result) {
|
||||||
int messageId = mInReadingList ? R.string.reading_list_removed : R.string.bookmark_removed;
|
// The remove from reading list toast is handled in Reader:Removed,
|
||||||
Toast.makeText(mContext, messageId, Toast.LENGTH_SHORT).show();
|
// so handle only the bookmark removed toast here.
|
||||||
|
if (!mInReadingList) {
|
||||||
|
Toast.makeText(mContext, R.string.bookmark_removed, Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
34
mobile/android/base/sync/BackoffHandler.java
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
package org.mozilla.gecko.sync;
|
||||||
|
|
||||||
|
|
||||||
|
public interface BackoffHandler {
|
||||||
|
public long getEarliestNextRequest();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provide a timestamp in millis before which we shouldn't sync again.
|
||||||
|
* Overrides any existing value.
|
||||||
|
*
|
||||||
|
* @param next
|
||||||
|
* a timestamp in milliseconds.
|
||||||
|
*/
|
||||||
|
public void setEarliestNextRequest(long next);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provide a timestamp in millis before which we shouldn't sync again. Only
|
||||||
|
* change our persisted value if it's later than the existing time.
|
||||||
|
*
|
||||||
|
* @param next
|
||||||
|
* a timestamp in milliseconds.
|
||||||
|
*/
|
||||||
|
public void extendEarliestNextRequest(long next);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the number of milliseconds until we're allowed to sync again,
|
||||||
|
* or 0 if now is fine.
|
||||||
|
*/
|
||||||
|
public long delayMilliseconds();
|
||||||
|
}
|
@ -316,7 +316,7 @@ public class GlobalSession implements PrefsSource, HttpResponseObserver {
|
|||||||
*/
|
*/
|
||||||
protected void restart() throws AlreadySyncingException {
|
protected void restart() throws AlreadySyncingException {
|
||||||
this.currentState = GlobalSyncStage.Stage.idle;
|
this.currentState = GlobalSyncStage.Stage.idle;
|
||||||
if (callback.shouldBackOff()) {
|
if (callback.shouldBackOffStorage()) {
|
||||||
this.callback.handleAborted(this, "Told to back off.");
|
this.callback.handleAborted(this, "Told to back off.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
67
mobile/android/base/sync/PrefsBackoffHandler.java
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
package org.mozilla.gecko.sync;
|
||||||
|
|
||||||
|
import org.mozilla.gecko.background.common.log.Logger;
|
||||||
|
|
||||||
|
import android.content.ContentResolver;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
|
import android.content.SharedPreferences.Editor;
|
||||||
|
import android.os.Bundle;
|
||||||
|
|
||||||
|
public class PrefsBackoffHandler implements BackoffHandler {
|
||||||
|
private static final String LOG_TAG = "BackoffHandler";
|
||||||
|
|
||||||
|
public static final String PREF_EARLIEST_NEXT = "earliestnext";
|
||||||
|
|
||||||
|
private final SharedPreferences prefs;
|
||||||
|
private final String prefSuffix;
|
||||||
|
private final String prefEarliest;
|
||||||
|
|
||||||
|
public PrefsBackoffHandler(final SharedPreferences prefs, final String prefSuffix) {
|
||||||
|
if (prefs == null) {
|
||||||
|
throw new IllegalArgumentException("prefs must not be null.");
|
||||||
|
}
|
||||||
|
this.prefs = prefs;
|
||||||
|
this.prefSuffix = prefSuffix;
|
||||||
|
this.prefEarliest = PREF_EARLIEST_NEXT + "." + prefSuffix;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized long getEarliestNextRequest() {
|
||||||
|
return prefs.getLong(prefEarliest, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized void setEarliestNextRequest(final long next) {
|
||||||
|
final Editor edit = prefs.edit();
|
||||||
|
edit.putLong(prefEarliest, next);
|
||||||
|
edit.commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized void extendEarliestNextRequest(final long next) {
|
||||||
|
if (prefs.getLong(prefEarliest, 0) >= next) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final Editor edit = prefs.edit();
|
||||||
|
edit.putLong(prefEarliest, next);
|
||||||
|
edit.commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the number of milliseconds until we're allowed to touch the server again,
|
||||||
|
* or 0 if now is fine.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public long delayMilliseconds() {
|
||||||
|
long earliestNextRequest = getEarliestNextRequest();
|
||||||
|
if (earliestNextRequest <= 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
long now = System.currentTimeMillis();
|
||||||
|
return Math.max(0, earliestNextRequest - now);
|
||||||
|
}
|
||||||
|
}
|
@ -251,7 +251,6 @@ public class SyncConfiguration {
|
|||||||
public static final String PREF_USER_SELECTED_ENGINES_TO_SYNC = "userSelectedEngines";
|
public static final String PREF_USER_SELECTED_ENGINES_TO_SYNC = "userSelectedEngines";
|
||||||
public static final String PREF_USER_SELECTED_ENGINES_TO_SYNC_TIMESTAMP = "userSelectedEnginesTimestamp";
|
public static final String PREF_USER_SELECTED_ENGINES_TO_SYNC_TIMESTAMP = "userSelectedEnginesTimestamp";
|
||||||
|
|
||||||
public static final String PREF_EARLIEST_NEXT_SYNC = "earliestnextsync";
|
|
||||||
public static final String PREF_CLUSTER_URL_IS_STALE = "clusterurlisstale";
|
public static final String PREF_CLUSTER_URL_IS_STALE = "clusterurlisstale";
|
||||||
|
|
||||||
public static final String PREF_ACCOUNT_GUID = "account.guid";
|
public static final String PREF_ACCOUNT_GUID = "account.guid";
|
||||||
|
@ -55,4 +55,7 @@ public class SyncConstants {
|
|||||||
public static final String PER_ACCOUNT_TYPE_PERMISSION = "@MOZ_ANDROID_SHARED_ACCOUNT_TYPE@.permission.PER_ACCOUNT_TYPE";
|
public static final String PER_ACCOUNT_TYPE_PERMISSION = "@MOZ_ANDROID_SHARED_ACCOUNT_TYPE@.permission.PER_ACCOUNT_TYPE";
|
||||||
|
|
||||||
public static final String DEFAULT_AUTH_SERVER = "https://auth.services.mozilla.com/";
|
public static final String DEFAULT_AUTH_SERVER = "https://auth.services.mozilla.com/";
|
||||||
|
|
||||||
|
// Used for BackoffHandler storage for Sync 1.1's SyncAdapter.
|
||||||
|
public static final String BACKOFF_PREF_SUFFIX_11 = "sync";
|
||||||
}
|
}
|
||||||
|
@ -32,5 +32,11 @@ public interface BaseGlobalSessionCallback {
|
|||||||
void handleSuccess(GlobalSession globalSession);
|
void handleSuccess(GlobalSession globalSession);
|
||||||
void handleStageCompleted(Stage currentState, GlobalSession globalSession);
|
void handleStageCompleted(Stage currentState, GlobalSession globalSession);
|
||||||
|
|
||||||
boolean shouldBackOff();
|
/**
|
||||||
|
* Called when a {@link GlobalSession} wants to know if it should continue
|
||||||
|
* to make storage requests.
|
||||||
|
*
|
||||||
|
* @return false if the session should make no further requests.
|
||||||
|
*/
|
||||||
|
boolean shouldBackOffStorage();
|
||||||
}
|
}
|
||||||
|
@ -96,7 +96,7 @@ public class EnsureClusterURLStage extends AbstractNonRepositorySyncStage {
|
|||||||
*
|
*
|
||||||
* 404: user not found | empty body
|
* 404: user not found | empty body
|
||||||
*
|
*
|
||||||
* {@link http://docs.services.mozilla.com/reg/apis.html}
|
* {@link "http://docs.services.mozilla.com/reg/apis.html"}
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void handleHttpResponse(HttpResponse response) {
|
public void handleHttpResponse(HttpResponse response) {
|
||||||
|
@ -14,9 +14,11 @@ import org.mozilla.gecko.background.common.GlobalConstants;
|
|||||||
import org.mozilla.gecko.background.common.log.Logger;
|
import org.mozilla.gecko.background.common.log.Logger;
|
||||||
import org.mozilla.gecko.db.BrowserContract;
|
import org.mozilla.gecko.db.BrowserContract;
|
||||||
import org.mozilla.gecko.sync.AlreadySyncingException;
|
import org.mozilla.gecko.sync.AlreadySyncingException;
|
||||||
|
import org.mozilla.gecko.sync.BackoffHandler;
|
||||||
import org.mozilla.gecko.sync.CredentialException;
|
import org.mozilla.gecko.sync.CredentialException;
|
||||||
import org.mozilla.gecko.sync.GlobalSession;
|
import org.mozilla.gecko.sync.GlobalSession;
|
||||||
import org.mozilla.gecko.sync.NonObjectJSONException;
|
import org.mozilla.gecko.sync.NonObjectJSONException;
|
||||||
|
import org.mozilla.gecko.sync.PrefsBackoffHandler;
|
||||||
import org.mozilla.gecko.sync.SharedPreferencesClientsDataDelegate;
|
import org.mozilla.gecko.sync.SharedPreferencesClientsDataDelegate;
|
||||||
import org.mozilla.gecko.sync.SharedPreferencesNodeAssignmentCallback;
|
import org.mozilla.gecko.sync.SharedPreferencesNodeAssignmentCallback;
|
||||||
import org.mozilla.gecko.sync.Sync11Configuration;
|
import org.mozilla.gecko.sync.Sync11Configuration;
|
||||||
@ -48,7 +50,6 @@ import android.content.ContentProviderClient;
|
|||||||
import android.content.ContentResolver;
|
import android.content.ContentResolver;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.content.SharedPreferences.Editor;
|
|
||||||
import android.content.SyncResult;
|
import android.content.SyncResult;
|
||||||
import android.database.sqlite.SQLiteConstraintException;
|
import android.database.sqlite.SQLiteConstraintException;
|
||||||
import android.database.sqlite.SQLiteException;
|
import android.database.sqlite.SQLiteException;
|
||||||
@ -65,33 +66,13 @@ public class SyncAdapter extends AbstractThreadedSyncAdapter implements BaseGlob
|
|||||||
|
|
||||||
protected long syncStartTimestamp;
|
protected long syncStartTimestamp;
|
||||||
|
|
||||||
|
protected volatile BackoffHandler backoffHandler;
|
||||||
|
|
||||||
public SyncAdapter(Context context, boolean autoInitialize) {
|
public SyncAdapter(Context context, boolean autoInitialize) {
|
||||||
super(context, autoInitialize);
|
super(context, autoInitialize);
|
||||||
mContext = context;
|
mContext = context;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Backoff.
|
|
||||||
*/
|
|
||||||
public synchronized long getEarliestNextSync() {
|
|
||||||
return accountSharedPreferences.getLong(SyncConfiguration.PREF_EARLIEST_NEXT_SYNC, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
public synchronized void setEarliestNextSync(long next) {
|
|
||||||
Editor edit = accountSharedPreferences.edit();
|
|
||||||
edit.putLong(SyncConfiguration.PREF_EARLIEST_NEXT_SYNC, next);
|
|
||||||
edit.commit();
|
|
||||||
}
|
|
||||||
|
|
||||||
public synchronized void extendEarliestNextSync(long next) {
|
|
||||||
if (accountSharedPreferences.getLong(SyncConfiguration.PREF_EARLIEST_NEXT_SYNC, 0) >= next) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Editor edit = accountSharedPreferences.edit();
|
|
||||||
edit.putLong(SyncConfiguration.PREF_EARLIEST_NEXT_SYNC, next);
|
|
||||||
edit.commit();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle an exception: update stats, log errors, etc.
|
* Handle an exception: update stats, log errors, etc.
|
||||||
* Wakes up sleeping threads by calling notifyMonitor().
|
* Wakes up sleeping threads by calling notifyMonitor().
|
||||||
@ -202,20 +183,27 @@ public class SyncAdapter extends AbstractThreadedSyncAdapter implements BaseGlob
|
|||||||
protected SharedPreferencesNodeAssignmentCallback nodeAssignmentDelegate;
|
protected SharedPreferencesNodeAssignmentCallback nodeAssignmentDelegate;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the number of milliseconds until we're allowed to sync again,
|
* Request that no sync start right away. A new sync won't start until
|
||||||
* or 0 if now is fine.
|
* at least <code>backoff</code> milliseconds from now.
|
||||||
|
*
|
||||||
|
* Don't call this unless you are inside `run`.
|
||||||
|
*
|
||||||
|
* @param backoff time to wait in milliseconds.
|
||||||
*/
|
*/
|
||||||
public long delayMilliseconds() {
|
@Override
|
||||||
long earliestNextSync = getEarliestNextSync();
|
public void requestBackoff(final long backoff) {
|
||||||
if (earliestNextSync <= 0) {
|
if (this.backoffHandler == null) {
|
||||||
return 0;
|
throw new IllegalStateException("No backoff handler: requesting backoff outside run()?");
|
||||||
|
}
|
||||||
|
if (backoff > 0) {
|
||||||
|
// Fuzz the backoff time (up to 25% more) to prevent client lock-stepping; agrees with desktop.
|
||||||
|
final long fuzzedBackoff = backoff + Math.round((double) backoff * 0.25d * Math.random());
|
||||||
|
this.backoffHandler.extendEarliestNextRequest(System.currentTimeMillis() + fuzzedBackoff);
|
||||||
}
|
}
|
||||||
long now = System.currentTimeMillis();
|
|
||||||
return Math.max(0, earliestNextSync - now);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean shouldBackOff() {
|
public boolean shouldBackOffStorage() {
|
||||||
if (thisSyncIsForced) {
|
if (thisSyncIsForced) {
|
||||||
/*
|
/*
|
||||||
* If the user asks us to sync, we should sync regardless. This path is
|
* If the user asks us to sync, we should sync regardless. This path is
|
||||||
@ -236,22 +224,10 @@ public class SyncAdapter extends AbstractThreadedSyncAdapter implements BaseGlob
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return delayMilliseconds() > 0;
|
if (this.backoffHandler == null) {
|
||||||
}
|
throw new IllegalStateException("No backoff handler: checking backoff outside run()?");
|
||||||
|
|
||||||
/**
|
|
||||||
* Request that no sync start right away. A new sync won't start until
|
|
||||||
* at least <code>backoff</code> milliseconds from now.
|
|
||||||
*
|
|
||||||
* @param backoff time to wait in milliseconds.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void requestBackoff(final long backoff) {
|
|
||||||
if (backoff > 0) {
|
|
||||||
// Fuzz the backoff time (up to 25% more) to prevent client lock-stepping; agrees with desktop.
|
|
||||||
final long fuzzedBackoff = backoff + Math.round((double) backoff * 0.25d * Math.random());
|
|
||||||
this.extendEarliestNextSync(System.currentTimeMillis() + fuzzedBackoff);
|
|
||||||
}
|
}
|
||||||
|
return this.backoffHandler.delayMilliseconds() > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -365,6 +341,7 @@ public class SyncAdapter extends AbstractThreadedSyncAdapter implements BaseGlob
|
|||||||
final long version = SyncConfiguration.CURRENT_PREFS_VERSION;
|
final long version = SyncConfiguration.CURRENT_PREFS_VERSION;
|
||||||
self.accountSharedPreferences = Utils.getSharedPreferences(mContext, product, username, serverURL, profile, version);
|
self.accountSharedPreferences = Utils.getSharedPreferences(mContext, product, username, serverURL, profile, version);
|
||||||
self.clientsDataDelegate = new SharedPreferencesClientsDataDelegate(accountSharedPreferences);
|
self.clientsDataDelegate = new SharedPreferencesClientsDataDelegate(accountSharedPreferences);
|
||||||
|
self.backoffHandler = new PrefsBackoffHandler(accountSharedPreferences, SyncConstants.BACKOFF_PREF_SUFFIX_11);
|
||||||
final String nodeWeaveURL = Utils.nodeWeaveURL(serverURL, username);
|
final String nodeWeaveURL = Utils.nodeWeaveURL(serverURL, username);
|
||||||
self.nodeAssignmentDelegate = new SharedPreferencesNodeAssignmentCallback(accountSharedPreferences, nodeWeaveURL);
|
self.nodeAssignmentDelegate = new SharedPreferencesNodeAssignmentCallback(accountSharedPreferences, nodeWeaveURL);
|
||||||
|
|
||||||
@ -373,20 +350,16 @@ public class SyncAdapter extends AbstractThreadedSyncAdapter implements BaseGlob
|
|||||||
", has client guid " + clientsDataDelegate.getAccountGUID() +
|
", has client guid " + clientsDataDelegate.getAccountGUID() +
|
||||||
", and has " + clientsDataDelegate.getClientsCount() + " clients.");
|
", and has " + clientsDataDelegate.getClientsCount() + " clients.");
|
||||||
|
|
||||||
thisSyncIsForced = (extras != null) && (extras.getBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, false));
|
final boolean thisSyncIsForced = (extras != null) && (extras.getBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, false));
|
||||||
long delay = delayMilliseconds();
|
final long delayMillis = backoffHandler.delayMilliseconds();
|
||||||
if (delay > 0) {
|
boolean shouldSync = thisSyncIsForced || (delayMillis <= 0L);
|
||||||
if (thisSyncIsForced) {
|
if (!shouldSync) {
|
||||||
Logger.info(LOG_TAG, "Forced sync: overruling remaining backoff of " + delay + "ms.");
|
long remainingSeconds = delayMillis / 1000;
|
||||||
} else {
|
|
||||||
Logger.info(LOG_TAG, "Not syncing: must wait another " + delay + "ms.");
|
|
||||||
long remainingSeconds = delay / 1000;
|
|
||||||
syncResult.delayUntil = remainingSeconds + BACKOFF_PAD_SECONDS;
|
syncResult.delayUntil = remainingSeconds + BACKOFF_PAD_SECONDS;
|
||||||
setNextSync.set(false);
|
setNextSync.set(false);
|
||||||
self.notifyMonitor();
|
self.notifyMonitor();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
final String prefsPath = Utils.getPrefsPath(product, username, serverURL, profile, version);
|
final String prefsPath = Utils.getPrefsPath(product, username, serverURL, profile, version);
|
||||||
self.performSync(account, extras, authority, provider, syncResult,
|
self.performSync(account, extras, authority, provider, syncResult,
|
||||||
@ -418,10 +391,10 @@ public class SyncAdapter extends AbstractThreadedSyncAdapter implements BaseGlob
|
|||||||
|
|
||||||
if (thisSyncIsForced) {
|
if (thisSyncIsForced) {
|
||||||
Logger.info(LOG_TAG, "Setting minimum next sync time to " + next + " (" + interval + "ms from now).");
|
Logger.info(LOG_TAG, "Setting minimum next sync time to " + next + " (" + interval + "ms from now).");
|
||||||
setEarliestNextSync(next);
|
self.backoffHandler.setEarliestNextRequest(next);
|
||||||
} else {
|
} else {
|
||||||
Logger.info(LOG_TAG, "Extending minimum next sync time to " + next + " (" + interval + "ms from now).");
|
Logger.info(LOG_TAG, "Extending minimum next sync time to " + next + " (" + interval + "ms from now).");
|
||||||
extendEarliestNextSync(next);
|
self.backoffHandler.extendEarliestNextRequest(next);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Logger.info(LOG_TAG, "Sync took " + Utils.formatDuration(syncStartTimestamp, System.currentTimeMillis()) + ".");
|
Logger.info(LOG_TAG, "Sync took " + Utils.formatDuration(syncStartTimestamp, System.currentTimeMillis()) + ".");
|
||||||
|
@ -146,6 +146,7 @@ public class TokenServerClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Responses should *always* be a valid JSON object.
|
// Responses should *always* be a valid JSON object.
|
||||||
|
// It turns out that right now they're not always, but that's a server bug...
|
||||||
ExtendedJSONObject result;
|
ExtendedJSONObject result;
|
||||||
try {
|
try {
|
||||||
result = res.jsonObjectBody();
|
result = res.jsonObjectBody();
|
||||||
|
@ -18,6 +18,8 @@
|
|||||||
|
|
||||||
package org.mozilla.gecko.util;
|
package org.mozilla.gecko.util;
|
||||||
|
|
||||||
|
import android.text.TextUtils;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
import java.net.Proxy;
|
import java.net.Proxy;
|
||||||
@ -79,7 +81,7 @@ public class ProxySelector {
|
|||||||
*/
|
*/
|
||||||
private Proxy lookupProxy(String hostKey, String portKey, Proxy.Type type, int defaultPort) {
|
private Proxy lookupProxy(String hostKey, String portKey, Proxy.Type type, int defaultPort) {
|
||||||
String host = System.getProperty(hostKey);
|
String host = System.getProperty(hostKey);
|
||||||
if (host == null || host.isEmpty()) {
|
if (TextUtils.isEmpty(host)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -352,16 +352,10 @@ AboutReader.prototype = {
|
|||||||
});
|
});
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
} else {
|
} else {
|
||||||
gChromeWin.Reader.removeArticleFromCache(this._article.url , function(success) {
|
// In addition to removing the article from the cache (handled in
|
||||||
dump("Reader:Remove (in reader) success=" + success);
|
// browser.js), sending this message will cause the toggle button to be
|
||||||
|
// updated (handled in this file).
|
||||||
Services.obs.notifyObservers(null, "Reader:Remove", this._article.url);
|
Services.obs.notifyObservers(null, "Reader:Remove", this._article.url);
|
||||||
|
|
||||||
gChromeWin.sendMessageToJava({
|
|
||||||
type: "Reader:Removed",
|
|
||||||
url: this._article.url
|
|
||||||
});
|
|
||||||
}.bind(this));
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -7571,6 +7571,13 @@ let Reader = {
|
|||||||
case "Reader:Remove": {
|
case "Reader:Remove": {
|
||||||
this.removeArticleFromCache(aData, function(success) {
|
this.removeArticleFromCache(aData, function(success) {
|
||||||
this.log("Reader:Remove success=" + success + ", url=" + aData);
|
this.log("Reader:Remove success=" + success + ", url=" + aData);
|
||||||
|
|
||||||
|
if (success) {
|
||||||
|
sendMessageToJava({
|
||||||
|
type: "Reader:Removed",
|
||||||
|
url: url
|
||||||
|
});
|
||||||
|
}
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,9 @@ import org.mozilla.gecko.background.common.GlobalConstants;
|
|||||||
import org.mozilla.gecko.background.helpers.AndroidSyncTestCase;
|
import org.mozilla.gecko.background.helpers.AndroidSyncTestCase;
|
||||||
import org.mozilla.gecko.background.testhelpers.MockSharedPreferences;
|
import org.mozilla.gecko.background.testhelpers.MockSharedPreferences;
|
||||||
import org.mozilla.gecko.sync.ExtendedJSONObject;
|
import org.mozilla.gecko.sync.ExtendedJSONObject;
|
||||||
|
import org.mozilla.gecko.sync.PrefsBackoffHandler;
|
||||||
import org.mozilla.gecko.sync.SyncConfiguration;
|
import org.mozilla.gecko.sync.SyncConfiguration;
|
||||||
|
import org.mozilla.gecko.sync.SyncConstants;
|
||||||
import org.mozilla.gecko.sync.Utils;
|
import org.mozilla.gecko.sync.Utils;
|
||||||
import org.mozilla.gecko.sync.config.ConfigurationMigrator;
|
import org.mozilla.gecko.sync.config.ConfigurationMigrator;
|
||||||
import org.mozilla.gecko.sync.setup.SyncAccounts;
|
import org.mozilla.gecko.sync.setup.SyncAccounts;
|
||||||
@ -243,7 +245,7 @@ public class TestConfigurationMigrator extends AndroidSyncTestCase {
|
|||||||
|
|
||||||
// Some global stuff.
|
// Some global stuff.
|
||||||
assertEquals(false, newPrefs.getBoolean(SyncConfiguration.PREF_CLUSTER_URL_IS_STALE, true));
|
assertEquals(false, newPrefs.getBoolean(SyncConfiguration.PREF_CLUSTER_URL_IS_STALE, true));
|
||||||
assertEquals(1340402318649L, newPrefs.getLong(SyncConfiguration.PREF_EARLIEST_NEXT_SYNC, 111));
|
assertEquals(1340402318649L, newPrefs.getLong(PrefsBackoffHandler.PREF_EARLIEST_NEXT + SyncConstants.BACKOFF_PREF_SUFFIX_11, 111));
|
||||||
// Some per-Sync account stuff.
|
// Some per-Sync account stuff.
|
||||||
assertEquals("{\"timestamp\":1340402003370}", newPrefs.getString("bookmarks.remote", null));
|
assertEquals("{\"timestamp\":1340402003370}", newPrefs.getString("bookmarks.remote", null));
|
||||||
assertEquals("{\"timestamp\":1340402008397}", newPrefs.getString("bookmarks.local", null));
|
assertEquals("{\"timestamp\":1340402008397}", newPrefs.getString("bookmarks.local", null));
|
||||||
|
@ -57,7 +57,7 @@ public class DefaultGlobalSessionCallback implements GlobalSessionCallback {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean shouldBackOff() {
|
public boolean shouldBackOffStorage() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -48,6 +48,8 @@ void
|
|||||||
ExternalHelperAppParent::Init(ContentParent *parent,
|
ExternalHelperAppParent::Init(ContentParent *parent,
|
||||||
const nsCString& aMimeContentType,
|
const nsCString& aMimeContentType,
|
||||||
const nsCString& aContentDispositionHeader,
|
const nsCString& aContentDispositionHeader,
|
||||||
|
const uint32_t& aContentDispositionHint,
|
||||||
|
const nsString& aContentDispositionFilename,
|
||||||
const bool& aForceSave,
|
const bool& aForceSave,
|
||||||
const OptionalURIParams& aReferrer,
|
const OptionalURIParams& aReferrer,
|
||||||
PBrowserParent* aBrowser)
|
PBrowserParent* aBrowser)
|
||||||
@ -61,8 +63,17 @@ ExternalHelperAppParent::Init(ContentParent *parent,
|
|||||||
SetPropertyAsInterface(NS_LITERAL_STRING("docshell.internalReferrer"), referrer);
|
SetPropertyAsInterface(NS_LITERAL_STRING("docshell.internalReferrer"), referrer);
|
||||||
|
|
||||||
mContentDispositionHeader = aContentDispositionHeader;
|
mContentDispositionHeader = aContentDispositionHeader;
|
||||||
NS_GetFilenameFromDisposition(mContentDispositionFilename, mContentDispositionHeader, mURI);
|
if (!mContentDispositionHeader.IsEmpty()) {
|
||||||
mContentDisposition = NS_GetContentDispositionFromHeader(mContentDispositionHeader, this);
|
NS_GetFilenameFromDisposition(mContentDispositionFilename,
|
||||||
|
mContentDispositionHeader,
|
||||||
|
mURI);
|
||||||
|
mContentDisposition =
|
||||||
|
NS_GetContentDispositionFromHeader(mContentDispositionHeader, this);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
mContentDisposition = aContentDispositionHint;
|
||||||
|
mContentDispositionFilename = aContentDispositionFilename;
|
||||||
|
}
|
||||||
|
|
||||||
nsCOMPtr<nsIInterfaceRequestor> window;
|
nsCOMPtr<nsIInterfaceRequestor> window;
|
||||||
if (aBrowser) {
|
if (aBrowser) {
|
||||||
@ -301,7 +312,8 @@ ExternalHelperAppParent::GetContentDisposition(uint32_t *aContentDisposition)
|
|||||||
NS_IMETHODIMP
|
NS_IMETHODIMP
|
||||||
ExternalHelperAppParent::SetContentDisposition(uint32_t aContentDisposition)
|
ExternalHelperAppParent::SetContentDisposition(uint32_t aContentDisposition)
|
||||||
{
|
{
|
||||||
return NS_ERROR_NOT_AVAILABLE;
|
mContentDisposition = aContentDisposition;
|
||||||
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
NS_IMETHODIMP
|
NS_IMETHODIMP
|
||||||
@ -317,7 +329,8 @@ ExternalHelperAppParent::GetContentDispositionFilename(nsAString& aContentDispos
|
|||||||
NS_IMETHODIMP
|
NS_IMETHODIMP
|
||||||
ExternalHelperAppParent::SetContentDispositionFilename(const nsAString& aContentDispositionFilename)
|
ExternalHelperAppParent::SetContentDispositionFilename(const nsAString& aContentDispositionFilename)
|
||||||
{
|
{
|
||||||
return NS_ERROR_NOT_AVAILABLE;
|
mContentDispositionFilename = aContentDispositionFilename;
|
||||||
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
NS_IMETHODIMP
|
NS_IMETHODIMP
|
||||||
|
@ -50,6 +50,8 @@ public:
|
|||||||
void Init(ContentParent *parent,
|
void Init(ContentParent *parent,
|
||||||
const nsCString& aMimeContentType,
|
const nsCString& aMimeContentType,
|
||||||
const nsCString& aContentDisposition,
|
const nsCString& aContentDisposition,
|
||||||
|
const uint32_t& aContentDispositionHint,
|
||||||
|
const nsString& aContentDispositionFilename,
|
||||||
const bool& aForceSave,
|
const bool& aForceSave,
|
||||||
const OptionalURIParams& aReferrer,
|
const OptionalURIParams& aReferrer,
|
||||||
PBrowserParent* aBrowser);
|
PBrowserParent* aBrowser);
|
||||||
|
@ -670,6 +670,8 @@ NS_IMETHODIMP nsExternalHelperAppService::DoContent(const nsACString& aMimeConte
|
|||||||
nsAutoString fileName;
|
nsAutoString fileName;
|
||||||
nsAutoCString fileExtension;
|
nsAutoCString fileExtension;
|
||||||
uint32_t reason = nsIHelperAppLauncherDialog::REASON_CANTHANDLE;
|
uint32_t reason = nsIHelperAppLauncherDialog::REASON_CANTHANDLE;
|
||||||
|
uint32_t contentDisposition = -1;
|
||||||
|
|
||||||
nsresult rv;
|
nsresult rv;
|
||||||
|
|
||||||
// Get the file extension and name that we will need later
|
// Get the file extension and name that we will need later
|
||||||
@ -679,7 +681,10 @@ NS_IMETHODIMP nsExternalHelperAppService::DoContent(const nsACString& aMimeConte
|
|||||||
if (channel) {
|
if (channel) {
|
||||||
channel->GetURI(getter_AddRefs(uri));
|
channel->GetURI(getter_AddRefs(uri));
|
||||||
channel->GetContentLength(&contentLength);
|
channel->GetContentLength(&contentLength);
|
||||||
|
channel->GetContentDisposition(&contentDisposition);
|
||||||
|
channel->GetContentDispositionFilename(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (XRE_GetProcessType() == GeckoProcessType_Content) {
|
if (XRE_GetProcessType() == GeckoProcessType_Content) {
|
||||||
nsCOMPtr<nsIDOMWindow> window = do_GetInterface(aWindowContext);
|
nsCOMPtr<nsIDOMWindow> window = do_GetInterface(aWindowContext);
|
||||||
NS_ENSURE_STATE(window);
|
NS_ENSURE_STATE(window);
|
||||||
@ -696,8 +701,9 @@ NS_IMETHODIMP nsExternalHelperAppService::DoContent(const nsACString& aMimeConte
|
|||||||
return NS_ERROR_FAILURE;
|
return NS_ERROR_FAILURE;
|
||||||
|
|
||||||
nsCString disp;
|
nsCString disp;
|
||||||
if (channel)
|
if (channel) {
|
||||||
channel->GetContentDispositionHeader(disp);
|
channel->GetContentDispositionHeader(disp);
|
||||||
|
}
|
||||||
|
|
||||||
nsCOMPtr<nsIURI> referrer;
|
nsCOMPtr<nsIURI> referrer;
|
||||||
rv = NS_GetReferrerFromChannel(channel, getter_AddRefs(referrer));
|
rv = NS_GetReferrerFromChannel(channel, getter_AddRefs(referrer));
|
||||||
@ -713,8 +719,9 @@ NS_IMETHODIMP nsExternalHelperAppService::DoContent(const nsACString& aMimeConte
|
|||||||
mozilla::dom::PExternalHelperAppChild *pc =
|
mozilla::dom::PExternalHelperAppChild *pc =
|
||||||
child->SendPExternalHelperAppConstructor(uriParams,
|
child->SendPExternalHelperAppConstructor(uriParams,
|
||||||
nsCString(aMimeContentType),
|
nsCString(aMimeContentType),
|
||||||
disp, aForceSave, contentLength,
|
disp, contentDisposition,
|
||||||
referrerParams,
|
fileName, aForceSave,
|
||||||
|
contentLength, referrerParams,
|
||||||
mozilla::dom::TabChild::GetFrom(window));
|
mozilla::dom::TabChild::GetFrom(window));
|
||||||
ExternalHelperAppChild *childListener = static_cast<ExternalHelperAppChild *>(pc);
|
ExternalHelperAppChild *childListener = static_cast<ExternalHelperAppChild *>(pc);
|
||||||
|
|
||||||
|