Merge mozilla-central to mozilla-inbound
|
@ -369,6 +369,14 @@ body[dir=rtl] #restorePreviousSession::before {
|
|||
bottom: 2%;
|
||||
}
|
||||
|
||||
#syncLinksContainer {
|
||||
padding-top: 1em;
|
||||
}
|
||||
|
||||
.sync-link {
|
||||
padding: 1em;
|
||||
}
|
||||
|
||||
@media all and (max-height: 370px) {
|
||||
#bottomSection {
|
||||
visibility: hidden;
|
||||
|
|
|
@ -102,6 +102,10 @@
|
|||
<div id="aboutMozilla">
|
||||
<a href="http://www.mozilla.com/about/">&abouthome.aboutMozilla;</a>
|
||||
</div>
|
||||
<div id="syncLinksContainer">
|
||||
<a href="javascript:void(0);" class="sync-link" id="setupSyncLink">&abouthome.syncSetup.label;</a>
|
||||
<a href="javascript:void(0);" class="sync-link" id="pairDeviceLink">&abouthome.pairDevice.label;</a>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -211,6 +211,24 @@ let gSyncUI = {
|
|||
this.clearError(title);
|
||||
},
|
||||
|
||||
// Set visibility of "Setup Sync" link
|
||||
showSetupSyncAboutHome: function SUI_showSetupSyncAboutHome(toShow) {
|
||||
let browsers = gBrowser.browsers;
|
||||
for (let i = 0; i < browsers.length; i++) {
|
||||
let b = browsers[i];
|
||||
if ("about:home" == b.currentURI.spec) {
|
||||
b.contentDocument.getElementById("setupSyncLink").hidden = !toShow;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
onSetupComplete: function SUI_onSetupComplete() {
|
||||
// Remove "setup sync" link in about:home if it is open.
|
||||
this.showSetupSyncAboutHome(false);
|
||||
|
||||
onLoginFinish();
|
||||
},
|
||||
|
||||
onLoginError: function SUI_onLoginError() {
|
||||
// if login fails, any other notifications are essentially moot
|
||||
Weave.Notifications.removeAll();
|
||||
|
@ -255,6 +273,8 @@ let gSyncUI = {
|
|||
|
||||
onStartOver: function SUI_onStartOver() {
|
||||
this.clearError();
|
||||
// Make "setup sync" link visible in about:home if it is open.
|
||||
this.showSetupSyncAboutHome(true);
|
||||
},
|
||||
|
||||
onQuotaNotice: function onQuotaNotice(subject, data) {
|
||||
|
@ -291,16 +311,40 @@ let gSyncUI = {
|
|||
|
||||
//XXXzpao should be part of syncCommon.js - which we might want to make a module...
|
||||
// To be fixed in a followup (bug 583366)
|
||||
openSetup: function SUI_openSetup() {
|
||||
|
||||
/**
|
||||
* Invoke the Sync setup wizard.
|
||||
*
|
||||
* @param wizardType
|
||||
* Indicates type of wizard to launch:
|
||||
* null -- regular set up wizard
|
||||
* "pair" -- pair a device first
|
||||
* "reset" -- reset sync
|
||||
*/
|
||||
|
||||
openSetup: function SUI_openSetup(wizardType) {
|
||||
let win = Services.wm.getMostRecentWindow("Weave:AccountSetup");
|
||||
if (win)
|
||||
win.focus();
|
||||
else {
|
||||
window.openDialog("chrome://browser/content/syncSetup.xul",
|
||||
"weaveSetup", "centerscreen,chrome,resizable=no");
|
||||
"weaveSetup", "centerscreen,chrome,resizable=no",
|
||||
wizardType);
|
||||
}
|
||||
},
|
||||
|
||||
openAddDevice: function () {
|
||||
if (!Weave.Utils.ensureMPUnlocked())
|
||||
return;
|
||||
|
||||
let win = Services.wm.getMostRecentWindow("Sync:AddDevice");
|
||||
if (win)
|
||||
win.focus();
|
||||
else
|
||||
window.openDialog("chrome://browser/content/syncAddDevice.xul",
|
||||
"syncAddDevice", "centerscreen,chrome,resizable=no");
|
||||
},
|
||||
|
||||
openQuotaDialog: function SUI_openQuotaDialog() {
|
||||
let win = Services.wm.getMostRecentWindow("Sync:ViewQuota");
|
||||
if (win)
|
||||
|
@ -462,7 +506,7 @@ let gSyncUI = {
|
|||
this.onQuotaNotice();
|
||||
break;
|
||||
case "weave:service:setup-complete":
|
||||
this.onLoginFinish();
|
||||
this.onSetupComplete();
|
||||
break;
|
||||
case "weave:service:login:start":
|
||||
this.onActivityStart();
|
||||
|
|
|
@ -2675,6 +2675,10 @@ function BrowserOnAboutPageLoad(document) {
|
|||
getService(Components.interfaces.nsISessionStore);
|
||||
if (!ss.canRestoreLastSession)
|
||||
document.getElementById("sessionRestoreContainer").hidden = true;
|
||||
// Sync-related links
|
||||
if (Services.prefs.prefHasUserValue("services.sync.username")) {
|
||||
document.getElementById("setupSyncLink").hidden = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2683,7 +2687,9 @@ function BrowserOnAboutPageLoad(document) {
|
|||
*/
|
||||
function BrowserOnClick(event) {
|
||||
// Don't trust synthetic events
|
||||
if (!event.isTrusted || event.target.localName != "button")
|
||||
if (!event.isTrusted ||
|
||||
(event.target.localName != "button" &&
|
||||
event.target.className != "sync-link"))
|
||||
return;
|
||||
|
||||
var ot = event.originalTarget;
|
||||
|
@ -2813,6 +2819,16 @@ function BrowserOnClick(event) {
|
|||
ss.restoreLastSession();
|
||||
errorDoc.getElementById("sessionRestoreContainer").hidden = true;
|
||||
}
|
||||
else if (ot == errorDoc.getElementById("pairDeviceLink")) {
|
||||
if (Services.prefs.prefHasUserValue("services.sync.username")) {
|
||||
gSyncUI.openAddDevice();
|
||||
} else {
|
||||
gSyncUI.openSetup("pair");
|
||||
}
|
||||
}
|
||||
else if (ot == errorDoc.getElementById("setupSyncLink")) {
|
||||
gSyncUI.openSetup(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -44,12 +44,14 @@ let gProgressBar;
|
|||
let gCounter = 0;
|
||||
|
||||
function onLoad(event) {
|
||||
Services.obs.addObserver(increaseProgressBar, "weave:engine:sync:finish", false);
|
||||
Services.obs.addObserver(increaseProgressBar, "weave:engine:sync:error", false);
|
||||
Services.obs.addObserver(onEngineSync, "weave:engine:sync:finish", false);
|
||||
Services.obs.addObserver(onEngineSync, "weave:engine:sync:error", false);
|
||||
Services.obs.addObserver(onServiceSync, "weave:service:sync:finish", false);
|
||||
Services.obs.addObserver(onServiceSync, "weave:service:sync:error", false);
|
||||
|
||||
gProgressBar = document.getElementById('uploadProgressBar');
|
||||
|
||||
if (Services.prefs.getPrefType("services.sync.firstSync") != Ci.nsIPrefBranch.PREF_INVALID) {
|
||||
gProgressBar.max = Weave.Engines.getEnabled().length;
|
||||
gProgressBar.style.display = "inline";
|
||||
}
|
||||
else {
|
||||
|
@ -58,15 +60,46 @@ function onLoad(event) {
|
|||
}
|
||||
|
||||
function onUnload(event) {
|
||||
Services.obs.removeObserver(increaseProgressBar, "weave:engine:sync:finish");
|
||||
Services.obs.removeObserver(increaseProgressBar, "weave:engine:sync:error");
|
||||
cleanUpObservers();
|
||||
}
|
||||
|
||||
function increaseProgressBar(){
|
||||
function cleanUpObservers() {
|
||||
try {
|
||||
Services.obs.removeObserver(onEngineSync, "weave:engine:sync:finish", false);
|
||||
Services.obs.removeObserver(onEngineSync, "weave:engine:sync:error", false);
|
||||
Services.obs.removeObserver(onServiceSync, "weave:service:sync:finish", false);
|
||||
Services.obs.removeObserver(onServiceSync, "weave:service:sync:error", false);
|
||||
}
|
||||
catch (e) {
|
||||
// may be double called by unload & exit. Ignore.
|
||||
}
|
||||
}
|
||||
|
||||
function onEngineSync(subject, topic, data) {
|
||||
// The Clients engine syncs first. At this point we don't necessarily know
|
||||
// yet how many engines will be enabled, so we'll ignore the Clients engine
|
||||
// and evaluate how many engines are enabled when the first "real" engine
|
||||
// syncs.
|
||||
if (data == "clients") {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!gCounter &&
|
||||
Services.prefs.getPrefType("services.sync.firstSync") != Ci.nsIPrefBranch.PREF_INVALID) {
|
||||
gProgressBar.max = Weave.Engines.getEnabled().length;
|
||||
}
|
||||
|
||||
gCounter += 1;
|
||||
gProgressBar.setAttribute("value", gCounter);
|
||||
}
|
||||
|
||||
function onServiceSync(subject, topic, data) {
|
||||
// To address the case where 0 engines are synced, we will fill the
|
||||
// progress bar so the user knows that the sync has finished.
|
||||
gProgressBar.setAttribute("value", gProgressBar.max);
|
||||
cleanUpObservers();
|
||||
}
|
||||
|
||||
function closeTab() {
|
||||
window.close();
|
||||
}
|
||||
|
|
|
@ -10,6 +10,9 @@ registerCleanupFunction(function() {
|
|||
try {
|
||||
Services.prefs.clearUserPref("network.cookie.lifetimePolicy");
|
||||
} catch (ex) {}
|
||||
try {
|
||||
Services.prefs.clearUserPref("services.sync.username");
|
||||
} catch (ex) {}
|
||||
});
|
||||
|
||||
let gTests = [
|
||||
|
@ -114,6 +117,116 @@ let gTests = [
|
|||
}
|
||||
},
|
||||
|
||||
{
|
||||
desc: "Check sync links visibility before and after Sync setup",
|
||||
setup: function ()
|
||||
{
|
||||
try {
|
||||
Services.prefs.clearUserPref("services.sync.username");
|
||||
} catch (ex) {}
|
||||
Services.obs.notifyObservers(null, "weave:service:ready", null);
|
||||
},
|
||||
run: function ()
|
||||
{
|
||||
let doc = gBrowser.selectedTab.linkedBrowser.contentDocument;
|
||||
let pairLink = doc.getElementById("pairDeviceLink");
|
||||
let setupLink = doc.getElementById("setupSyncLink");
|
||||
|
||||
ok(pairLink, "Found 'Pair Device' link");
|
||||
ok(setupLink, "Found 'Set Up Sync' link");
|
||||
ok(!pairLink.hidden, "'Pair' link is visible before setup");
|
||||
ok(!setupLink.hidden, "'Set Up' link is visible before setup");
|
||||
|
||||
Services.obs.notifyObservers(null, "weave:service:setup-complete", null);
|
||||
|
||||
executeSoon(function () {
|
||||
setupLink = doc.getElementById("setupSyncLink");
|
||||
ok(setupLink.hidden, "'Set Up' link is hidden after setup");
|
||||
ok(!pairLink.hidden, "'Pair' link is visible after setup");
|
||||
|
||||
executeSoon(runNextTest);
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
desc: "Check sync links visibility before and after Sync unlink",
|
||||
setup: function ()
|
||||
{
|
||||
Services.prefs.setCharPref("services.sync.username", "someuser@domain.com");
|
||||
Services.obs.notifyObservers(null, "weave:service:ready", null);
|
||||
},
|
||||
run: function ()
|
||||
{
|
||||
let doc = gBrowser.selectedTab.linkedBrowser.contentDocument;
|
||||
let pairLink = doc.getElementById("pairDeviceLink");
|
||||
let setupLink = doc.getElementById("setupSyncLink");
|
||||
|
||||
ok(!pairLink.hidden, "'Pair' link is visible before unlink");
|
||||
ok(setupLink.hidden, "'Set Up' link is hidden before unlink");
|
||||
|
||||
Services.obs.notifyObservers(null, "weave:service:start-over", null);
|
||||
|
||||
executeSoon(function () {
|
||||
setupLink = doc.getElementById("setupSyncLink");
|
||||
ok(!setupLink.hidden, "'Set Up' link is visible after unlink");
|
||||
ok(!pairLink.hidden, "'Pair' link is visible after unlink");
|
||||
executeSoon(runNextTest);
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
desc: "Check Pair Device link opens correct dialog with Sync account ",
|
||||
setup: function ()
|
||||
{
|
||||
Services.prefs.setCharPref("services.sync.username", "someuser@domain.com");
|
||||
Services.obs.notifyObservers(null, "weave:service:ready", null);
|
||||
},
|
||||
run: function ()
|
||||
{
|
||||
expectDialogWindow("Sync:AddDevice");
|
||||
let browser = gBrowser.selectedTab.linkedBrowser;
|
||||
let button = browser.contentDocument.getElementById("pairDeviceLink");
|
||||
EventUtils.sendMouseEvent({type: "click"}, button, browser.contentWindow);
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
desc: "Check Pair Device link opens correct dialog without Sync account",
|
||||
setup: function ()
|
||||
{
|
||||
try {
|
||||
Services.prefs.clearUserPref("services.sync.username");
|
||||
} catch (ex) {}
|
||||
Services.obs.notifyObservers(null, "weave:service:ready", null);
|
||||
},
|
||||
run: function ()
|
||||
{
|
||||
expectDialogWindow("Weave:AccountSetup");
|
||||
let browser = gBrowser.selectedTab.linkedBrowser;
|
||||
let button = browser.contentDocument.getElementById("pairDeviceLink");
|
||||
EventUtils.sendMouseEvent({type: "click"}, button, browser.contentWindow);
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
desc: "Check Sync Setup link opens correct dialog (without Sync account)",
|
||||
setup: function ()
|
||||
{
|
||||
try {
|
||||
Services.prefs.clearUserPref("services.sync.username");
|
||||
} catch (ex) {}
|
||||
Services.obs.notifyObservers(null, "weave:service:ready", null);
|
||||
},
|
||||
run: function ()
|
||||
{
|
||||
expectDialogWindow("Weave:AccountSetup");
|
||||
let browser = gBrowser.selectedTab.linkedBrowser;
|
||||
let button = browser.contentDocument.getElementById("setupSyncLink");
|
||||
EventUtils.sendMouseEvent({type: "click"}, button, browser.contentWindow);
|
||||
}
|
||||
},
|
||||
];
|
||||
|
||||
function test()
|
||||
|
@ -159,6 +272,22 @@ function runNextTest()
|
|||
}
|
||||
}
|
||||
|
||||
function expectDialogWindow(expectedDialog) {
|
||||
Services.ww.registerNotification(function onWindow(subject, topic) {
|
||||
let win = subject.QueryInterface(Components.interfaces.nsIDOMWindow);
|
||||
win.addEventListener("load", function onLoad() {
|
||||
win.removeEventListener("load", onLoad, false);
|
||||
let wintype = win.document.documentElement.getAttribute("windowtype");
|
||||
if (topic == "domwindowopened" && wintype == expectedDialog) {
|
||||
Services.ww.unregisterNotification(onWindow);
|
||||
// Clean up dialog.
|
||||
win.close();
|
||||
executeSoon(runNextTest);
|
||||
}
|
||||
}, false);
|
||||
});
|
||||
}
|
||||
|
||||
function getStorage()
|
||||
{
|
||||
let aboutHomeURI = Services.io.newURI("moz-safe-about:home", null, null);
|
||||
|
|
|
@ -18,3 +18,6 @@
|
|||
text in <a/> will be linked to the featured add-ons on addons.mozilla.org
|
||||
-->
|
||||
<!ENTITY abouthome.defaultSnippet2.v1 "It's easy to customize your Firefox exactly the way you want it. <a>Choose from thousands of add-ons</a>.">
|
||||
|
||||
<!ENTITY abouthome.syncSetup.label "Set Up Sync">
|
||||
<!ENTITY abouthome.pairDevice.label "Pair a Device">
|
||||
|
|
|
@ -42,7 +42,6 @@ interface nsIChannel;
|
|||
interface nsIStreamListener;
|
||||
interface nsIURI;
|
||||
interface nsIDocument;
|
||||
interface nsIFrame;
|
||||
|
||||
/**
|
||||
* This interface represents a content node that loads images. The interface
|
||||
|
@ -66,7 +65,7 @@ interface nsIFrame;
|
|||
* sufficient, when combined with the imageBlockingStatus information.)
|
||||
*/
|
||||
|
||||
[scriptable, uuid(f7debb84-2854-4731-a57b-1bd752ad71f8)]
|
||||
[scriptable, uuid(95c74255-df9a-4060-b5a0-0d111fcafe08)]
|
||||
interface nsIImageLoadingContent : imgIDecoderObserver
|
||||
{
|
||||
/**
|
||||
|
@ -129,18 +128,6 @@ interface nsIImageLoadingContent : imgIDecoderObserver
|
|||
*/
|
||||
imgIRequest getRequest(in long aRequestType);
|
||||
|
||||
/**
|
||||
* Used to notify the image loading content node that a frame has been
|
||||
* created.
|
||||
*/
|
||||
[notxpcom] void frameCreated(in nsIFrame aFrame);
|
||||
|
||||
/**
|
||||
* Used to notify the image loading content node that a frame has been
|
||||
* destroyed.
|
||||
*/
|
||||
[notxpcom] void frameDestroyed(in nsIFrame aFrame);
|
||||
|
||||
/**
|
||||
* Used to find out what type of request one is dealing with (eg
|
||||
* which request got passed through to the imgIDecoderObserver
|
||||
|
|
|
@ -3225,7 +3225,6 @@ nsDocument::DeleteShell()
|
|||
if (IsEventHandlingEnabled()) {
|
||||
RevokeAnimationFrameNotifications();
|
||||
}
|
||||
|
||||
mPresShell = nsnull;
|
||||
}
|
||||
|
||||
|
|
|
@ -72,7 +72,6 @@
|
|||
#include "nsIDOMNode.h"
|
||||
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsLayoutUtils.h"
|
||||
#include "nsIContentPolicy.h"
|
||||
#include "nsContentPolicyUtils.h"
|
||||
#include "nsEventDispatcher.h"
|
||||
|
@ -116,9 +115,7 @@ nsImageLoadingContent::nsImageLoadingContent()
|
|||
mNewRequestsWillNeedAnimationReset(false),
|
||||
mPendingRequestNeedsResetAnimation(false),
|
||||
mCurrentRequestNeedsResetAnimation(false),
|
||||
mStateChangerDepth(0),
|
||||
mCurrentRequestRegistered(false),
|
||||
mPendingRequestRegistered(false)
|
||||
mStateChangerDepth(0)
|
||||
{
|
||||
if (!nsContentUtils::GetImgLoader()) {
|
||||
mLoadingEnabled = false;
|
||||
|
@ -199,16 +196,7 @@ nsImageLoadingContent::OnStartDecode(imgIRequest* aRequest)
|
|||
SetBlockingOnload(true);
|
||||
}
|
||||
}
|
||||
|
||||
bool* requestFlag = GetRegisteredFlagForRequest(aRequest);
|
||||
if (requestFlag) {
|
||||
nsLayoutUtils::RegisterImageRequest(GetFramePresContext(), aRequest,
|
||||
requestFlag);
|
||||
} else {
|
||||
NS_ERROR("Starting to decode an image other than our current/pending "
|
||||
"request?");
|
||||
}
|
||||
|
||||
|
||||
LOOP_OVER_OBSERVERS(OnStartDecode(aRequest));
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -341,13 +329,6 @@ nsImageLoadingContent::OnStopDecode(imgIRequest* aRequest,
|
|||
nsIPresShell* shell = doc ? doc->GetShell() : nsnull;
|
||||
if (shell) {
|
||||
|
||||
// Make sure that our image requests are deregistered from the refresh
|
||||
// driver if they aren't animated. Note that this must be mCurrentRequest,
|
||||
// or we would have aborted up above.
|
||||
nsLayoutUtils::DeregisterImageRequestIfNotAnimated(GetFramePresContext(),
|
||||
mCurrentRequest,
|
||||
&mCurrentRequestRegistered);
|
||||
|
||||
// We need to figure out whether to kick off decoding
|
||||
bool doRequestDecode = false;
|
||||
|
||||
|
@ -520,44 +501,6 @@ nsImageLoadingContent::GetRequest(PRInt32 aRequestType,
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP_(void)
|
||||
nsImageLoadingContent::FrameCreated(nsIFrame* aFrame)
|
||||
{
|
||||
NS_ASSERTION(aFrame, "aFrame is null");
|
||||
|
||||
// We need to make sure that our image request is registered.
|
||||
nsPresContext* presContext = aFrame->PresContext();
|
||||
|
||||
if (mCurrentRequest) {
|
||||
nsLayoutUtils::RegisterImageRequest(presContext, mCurrentRequest,
|
||||
&mCurrentRequestRegistered);
|
||||
nsLayoutUtils::DeregisterImageRequestIfNotAnimated(presContext,
|
||||
mCurrentRequest,
|
||||
&mCurrentRequestRegistered);
|
||||
} else if (mPendingRequest) {
|
||||
// We don't need to do the same check for animation, because this will be
|
||||
// done when decoding is finished.
|
||||
nsLayoutUtils::RegisterImageRequest(presContext, mPendingRequest,
|
||||
&mPendingRequestRegistered);
|
||||
}
|
||||
}
|
||||
|
||||
NS_IMETHODIMP_(void)
|
||||
nsImageLoadingContent::FrameDestroyed(nsIFrame* aFrame)
|
||||
{
|
||||
NS_ASSERTION(aFrame, "aFrame is null");
|
||||
|
||||
// We need to make sure that our image request is deregistered.
|
||||
if (mCurrentRequest) {
|
||||
nsLayoutUtils::DeregisterImageRequest(GetFramePresContext(),
|
||||
mCurrentRequest,
|
||||
&mCurrentRequestRegistered);
|
||||
} else if (mPendingRequest) {
|
||||
nsLayoutUtils::DeregisterImageRequest(GetFramePresContext(),
|
||||
mPendingRequest,
|
||||
&mPendingRequestRegistered);
|
||||
}
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsImageLoadingContent::GetRequestType(imgIRequest* aRequest,
|
||||
|
@ -924,23 +867,6 @@ nsImageLoadingContent::GetOurDocument()
|
|||
return thisContent->OwnerDoc();
|
||||
}
|
||||
|
||||
nsIFrame*
|
||||
nsImageLoadingContent::GetOurPrimaryFrame()
|
||||
{
|
||||
nsCOMPtr<nsIContent> thisContent = do_QueryInterface(this);
|
||||
return thisContent->GetPrimaryFrame();
|
||||
}
|
||||
|
||||
nsPresContext* nsImageLoadingContent::GetFramePresContext()
|
||||
{
|
||||
nsIFrame* frame = GetOurPrimaryFrame();
|
||||
if (!frame) {
|
||||
return nsnull;
|
||||
}
|
||||
|
||||
return frame->PresContext();
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsImageLoadingContent::StringToURI(const nsAString& aSpec,
|
||||
nsIDocument* aDocument,
|
||||
|
@ -1060,16 +986,6 @@ nsImageLoadingContent::ClearCurrentRequest(nsresult aReason)
|
|||
NS_ABORT_IF_FALSE(!mCurrentURI,
|
||||
"Shouldn't have both mCurrentRequest and mCurrentURI!");
|
||||
|
||||
// Deregister this image from the refresh driver so it no longer receives
|
||||
// notifications.
|
||||
nsLayoutUtils::DeregisterImageRequest(GetFramePresContext(), mCurrentRequest,
|
||||
&mCurrentRequestRegistered);
|
||||
|
||||
// Deregister this image from the refresh driver so it no longer receives
|
||||
// notifications.
|
||||
nsLayoutUtils::DeregisterImageRequest(GetFramePresContext(), mCurrentRequest,
|
||||
&mCurrentRequestRegistered);
|
||||
|
||||
// Clean up the request.
|
||||
UntrackImage(mCurrentRequest);
|
||||
mCurrentRequest->CancelAndForgetObserver(aReason);
|
||||
|
@ -1093,29 +1009,12 @@ nsImageLoadingContent::ClearPendingRequest(nsresult aReason)
|
|||
nsCxPusher pusher;
|
||||
pusher.PushNull();
|
||||
|
||||
// Deregister this image from the refresh driver so it no longer receives
|
||||
// notifications.
|
||||
nsLayoutUtils::DeregisterImageRequest(GetFramePresContext(), mPendingRequest,
|
||||
&mPendingRequestRegistered);
|
||||
|
||||
UntrackImage(mPendingRequest);
|
||||
mPendingRequest->CancelAndForgetObserver(aReason);
|
||||
mPendingRequest = nsnull;
|
||||
mPendingRequestNeedsResetAnimation = false;
|
||||
}
|
||||
|
||||
bool*
|
||||
nsImageLoadingContent::GetRegisteredFlagForRequest(imgIRequest* aRequest)
|
||||
{
|
||||
if (aRequest == mCurrentRequest) {
|
||||
return &mCurrentRequestRegistered;
|
||||
} else if (aRequest == mPendingRequest) {
|
||||
return &mPendingRequestRegistered;
|
||||
} else {
|
||||
return nsnull;
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
nsImageLoadingContent::HaveSize(imgIRequest *aImage)
|
||||
{
|
||||
|
|
|
@ -146,24 +146,6 @@ protected:
|
|||
*/
|
||||
nsIDocument* GetOurDocument();
|
||||
|
||||
/**
|
||||
* Helper function to get the frame associated with this content. Not named
|
||||
* GetPrimaryFrame to prevent ambiguous method names in subclasses.
|
||||
*
|
||||
* @return The frame which we belong to, or nsnull if it doesn't exist.
|
||||
*/
|
||||
nsIFrame* GetOurPrimaryFrame();
|
||||
|
||||
/**
|
||||
* Helper function to get the PresContext associated with this content's
|
||||
* frame. Not named GetPresContext to prevent ambiguous method names in
|
||||
* subclasses.
|
||||
*
|
||||
* @return The nsPresContext associated with our frame, or nsnull if either
|
||||
* the frame doesn't exist, or the frame's prescontext doesn't exist.
|
||||
*/
|
||||
nsPresContext* GetFramePresContext();
|
||||
|
||||
/**
|
||||
* CancelImageRequests is called by subclasses when they want to
|
||||
* cancel all image requests (for example when the subclass is
|
||||
|
@ -320,16 +302,6 @@ protected:
|
|||
void ClearCurrentRequest(nsresult aReason);
|
||||
void ClearPendingRequest(nsresult aReason);
|
||||
|
||||
/**
|
||||
* Retrieve a pointer to the 'registered with the refresh driver' flag for
|
||||
* which a particular image request corresponds.
|
||||
*
|
||||
* @returns A pointer to the boolean flag for a given image request, or
|
||||
* |nsnull| if the request is not either |mPendingRequest| or
|
||||
* |mCurrentRequest|.
|
||||
*/
|
||||
bool* GetRegisteredFlagForRequest(imgIRequest* aRequest);
|
||||
|
||||
/**
|
||||
* Static helper method to tell us if we have the size of a request. The
|
||||
* image may be null.
|
||||
|
@ -410,11 +382,6 @@ private:
|
|||
|
||||
/* The number of nested AutoStateChangers currently tracking our state. */
|
||||
PRUint8 mStateChangerDepth;
|
||||
|
||||
// Flags to indicate whether each of the current and pending requests are
|
||||
// registered with the refresh driver.
|
||||
bool mCurrentRequestRegistered;
|
||||
bool mPendingRequestRegistered;
|
||||
};
|
||||
|
||||
#endif // nsImageLoadingContent_h__
|
||||
|
|
|
@ -60,6 +60,7 @@
|
|||
#include "mozilla/ipc/XPCShellEnvironment.h"
|
||||
#include "mozilla/jsipc/PContextWrapperChild.h"
|
||||
#include "mozilla/net/NeckoChild.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
|
||||
#if defined(MOZ_SYDNEYAUDIO)
|
||||
#include "nsAudioStream.h"
|
||||
|
@ -68,7 +69,6 @@
|
|||
#include "nsIObserverService.h"
|
||||
#include "nsTObserverArray.h"
|
||||
#include "nsIObserver.h"
|
||||
#include "nsIPrefService.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
#include "nsXULAppAPI.h"
|
||||
#include "nsWeakReference.h"
|
||||
|
@ -638,24 +638,14 @@ ContentChild::AddRemoteAlertObserver(const nsString& aData,
|
|||
bool
|
||||
ContentChild::RecvPreferenceUpdate(const PrefTuple& aPref)
|
||||
{
|
||||
nsCOMPtr<nsIPrefServiceInternal> prefs = do_GetService("@mozilla.org/preferences-service;1");
|
||||
if (!prefs)
|
||||
return false;
|
||||
|
||||
prefs->SetPreference(&aPref);
|
||||
|
||||
Preferences::SetPreference(&aPref);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
ContentChild::RecvClearUserPreference(const nsCString& aPrefName)
|
||||
{
|
||||
nsCOMPtr<nsIPrefServiceInternal> prefs = do_GetService("@mozilla.org/preferences-service;1");
|
||||
if (!prefs)
|
||||
return false;
|
||||
|
||||
prefs->ClearContentPref(aPrefName);
|
||||
|
||||
Preferences::ClearContentPref(aPrefName.get());
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -51,7 +51,6 @@
|
|||
#include "nsIDOMWindow.h"
|
||||
#include "nsIPrefBranch.h"
|
||||
#include "nsIPrefBranch2.h"
|
||||
#include "nsIPrefService.h"
|
||||
#include "nsIPrefLocalizedString.h"
|
||||
#include "nsIObserverService.h"
|
||||
#include "nsContentUtils.h"
|
||||
|
@ -461,7 +460,7 @@ bool
|
|||
ContentParent::RecvReadPrefsArray(InfallibleTArray<PrefTuple> *prefs)
|
||||
{
|
||||
EnsurePrefService();
|
||||
mPrefService->MirrorPreferences(prefs);
|
||||
Preferences::MirrorPreferences(prefs);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -707,33 +706,9 @@ ContentParent::Observe(nsISupports* aSubject,
|
|||
// We know prefs are ASCII here.
|
||||
NS_LossyConvertUTF16toASCII strData(aData);
|
||||
|
||||
nsCOMPtr<nsIPrefServiceInternal> prefService =
|
||||
do_GetService("@mozilla.org/preferences-service;1");
|
||||
|
||||
bool prefNeedUpdate;
|
||||
prefService->PrefHasUserValue(strData, &prefNeedUpdate);
|
||||
|
||||
// If the pref does not have a user value, check if it exist on the
|
||||
// default branch or not
|
||||
if (!prefNeedUpdate) {
|
||||
nsCOMPtr<nsIPrefBranch> defaultBranch;
|
||||
nsCOMPtr<nsIPrefService> prefsService = do_QueryInterface(prefService);
|
||||
prefsService->GetDefaultBranch(nsnull, getter_AddRefs(defaultBranch));
|
||||
|
||||
PRInt32 prefType = nsIPrefBranch::PREF_INVALID;
|
||||
defaultBranch->GetPrefType(strData.get(), &prefType);
|
||||
prefNeedUpdate = (prefType != nsIPrefBranch::PREF_INVALID);
|
||||
}
|
||||
|
||||
PrefTuple pref;
|
||||
bool prefNeedUpdate = Preferences::MirrorPreference(strData.get(), &pref);
|
||||
if (prefNeedUpdate) {
|
||||
// Pref was created, or previously existed and its value
|
||||
// changed.
|
||||
PrefTuple pref;
|
||||
#ifdef DEBUG
|
||||
nsresult rv =
|
||||
#endif
|
||||
prefService->MirrorPreference(strData, &pref);
|
||||
NS_ASSERTION(NS_SUCCEEDED(rv), "Pref has value but can't mirror?");
|
||||
if (!SendPreferenceUpdate(pref)) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
|
|
@ -241,7 +241,7 @@ private:
|
|||
nsCOMArray<nsIMemoryReporter> mMemoryReporters;
|
||||
|
||||
bool mIsAlive;
|
||||
nsCOMPtr<nsIPrefServiceInternal> mPrefService;
|
||||
nsCOMPtr<nsIPrefService> mPrefService;
|
||||
|
||||
bool mSendPermissionUpdates;
|
||||
|
||||
|
|
|
@ -56,10 +56,6 @@ interface imgIDecoderObserver;
|
|||
#include "limits.h"
|
||||
|
||||
class nsIFrame;
|
||||
|
||||
namespace mozilla {
|
||||
class TimeStamp;
|
||||
}
|
||||
%}
|
||||
|
||||
[ptr] native gfxImageSurface(gfxImageSurface);
|
||||
|
@ -72,7 +68,6 @@ native gfxGraphicsFilter(gfxPattern::GraphicsFilter);
|
|||
[ref] native nsIntRect(nsIntRect);
|
||||
[ref] native nsIntSize(nsIntSize);
|
||||
[ptr] native nsIFrame(nsIFrame);
|
||||
[ref] native TimeStamp(mozilla::TimeStamp);
|
||||
|
||||
/**
|
||||
* imgIContainer is the interface that represents an image. It allows
|
||||
|
@ -266,13 +261,6 @@ interface imgIContainer : nsISupports
|
|||
*/
|
||||
void unlockImage();
|
||||
|
||||
/**
|
||||
* Indicates that this imgIContainer has been triggered to update
|
||||
* its internal animation state. Likely this should only be called
|
||||
* from within nsImageFrame or objects of similar type.
|
||||
*/
|
||||
[notxpcom] void requestRefresh([const] in TimeStamp aTime);
|
||||
|
||||
/**
|
||||
* Animation mode Constants
|
||||
* 0 = normal
|
||||
|
|
|
@ -173,10 +173,10 @@ namespace mozilla {
|
|||
namespace imagelib {
|
||||
|
||||
#ifndef DEBUG
|
||||
NS_IMPL_ISUPPORTS3(RasterImage, imgIContainer, nsIProperties,
|
||||
NS_IMPL_ISUPPORTS4(RasterImage, imgIContainer, nsITimerCallback, nsIProperties,
|
||||
nsISupportsWeakReference)
|
||||
#else
|
||||
NS_IMPL_ISUPPORTS4(RasterImage, imgIContainer, nsIProperties,
|
||||
NS_IMPL_ISUPPORTS5(RasterImage, imgIContainer, nsITimerCallback, nsIProperties,
|
||||
imgIContainerDebug, nsISupportsWeakReference)
|
||||
#endif
|
||||
|
||||
|
@ -313,169 +313,6 @@ RasterImage::Init(imgIDecoderObserver *aObserver,
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
bool
|
||||
RasterImage::AdvanceFrame(TimeStamp aTime, nsIntRect* aDirtyRect)
|
||||
{
|
||||
NS_ASSERTION(aTime <= TimeStamp::Now(),
|
||||
"Given time appears to be in the future");
|
||||
|
||||
imgFrame* nextFrame = nsnull;
|
||||
PRUint32 currentFrameIndex = mAnim->currentAnimationFrameIndex;
|
||||
PRUint32 nextFrameIndex = mAnim->currentAnimationFrameIndex + 1;
|
||||
PRUint32 timeout = 0;
|
||||
|
||||
// Figure out if we have the next full frame. This is more complicated than
|
||||
// just checking for mFrames.Length() because decoders append their frames
|
||||
// before they're filled in.
|
||||
NS_ABORT_IF_FALSE(mDecoder || nextFrameIndex <= mFrames.Length(),
|
||||
"How did we get 2 indices too far by incrementing?");
|
||||
|
||||
// If we don't have a decoder, we know we've got everything we're going to
|
||||
// get. If we do, we only display fully-downloaded frames; everything else
|
||||
// gets delayed.
|
||||
bool haveFullNextFrame = !mDecoder ||
|
||||
nextFrameIndex < mDecoder->GetCompleteFrameCount();
|
||||
|
||||
// If we're done decoding the next frame, go ahead and display it now and
|
||||
// reinit with the next frame's delay time.
|
||||
if (haveFullNextFrame) {
|
||||
if (mFrames.Length() == nextFrameIndex) {
|
||||
// End of Animation, unless we are looping forever
|
||||
|
||||
// If animation mode is "loop once", it's time to stop animating
|
||||
if (mAnimationMode == kLoopOnceAnimMode || mLoopCount == 0) {
|
||||
mAnimationFinished = PR_TRUE;
|
||||
EvaluateAnimation();
|
||||
}
|
||||
|
||||
// We may have used compositingFrame to build a frame, and then copied
|
||||
// it back into mFrames[..]. If so, delete composite to save memory
|
||||
if (mAnim->compositingFrame && mAnim->lastCompositedFrameIndex == -1) {
|
||||
mAnim->compositingFrame = nsnull;
|
||||
}
|
||||
|
||||
nextFrameIndex = 0;
|
||||
|
||||
if (mLoopCount > 0) {
|
||||
mLoopCount--;
|
||||
}
|
||||
|
||||
if (!mAnimating) {
|
||||
// break out early if we are actually done animating
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!(nextFrame = mFrames[nextFrameIndex])) {
|
||||
// something wrong with the next frame, skip it
|
||||
mAnim->currentAnimationFrameIndex = nextFrameIndex;
|
||||
return false;
|
||||
}
|
||||
|
||||
timeout = nextFrame->GetTimeout();
|
||||
|
||||
} else {
|
||||
// Uh oh, the frame we want to show is currently being decoded (partial)
|
||||
// Wait until the next refresh driver tick and try again
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!(timeout > 0)) {
|
||||
mAnimationFinished = PR_TRUE;
|
||||
EvaluateAnimation();
|
||||
}
|
||||
|
||||
imgFrame *frameToUse = nsnull;
|
||||
|
||||
if (nextFrameIndex == 0) {
|
||||
frameToUse = nextFrame;
|
||||
*aDirtyRect = mAnim->firstFrameRefreshArea;
|
||||
} else {
|
||||
imgFrame *curFrame = mFrames[currentFrameIndex];
|
||||
|
||||
if (!curFrame) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Change frame
|
||||
if (NS_FAILED(DoComposite(aDirtyRect, curFrame,
|
||||
nextFrame, nextFrameIndex))) {
|
||||
// something went wrong, move on to next
|
||||
NS_WARNING("RasterImage::AdvanceFrame(): Compositing of frame failed");
|
||||
nextFrame->SetCompositingFailed(PR_TRUE);
|
||||
mAnim->currentAnimationFrameIndex = nextFrameIndex;
|
||||
mAnim->currentAnimationFrameTime = aTime;
|
||||
return false;
|
||||
}
|
||||
|
||||
nextFrame->SetCompositingFailed(PR_FALSE);
|
||||
}
|
||||
|
||||
// Set currentAnimationFrameIndex at the last possible moment
|
||||
mAnim->currentAnimationFrameIndex = nextFrameIndex;
|
||||
mAnim->currentAnimationFrameTime = aTime;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//******************************************************************************
|
||||
// [notxpcom] void requestRefresh ([const] in TimeStamp aTime);
|
||||
NS_IMETHODIMP_(void)
|
||||
RasterImage::RequestRefresh(const mozilla::TimeStamp& aTime)
|
||||
{
|
||||
if (!mAnimating || !ShouldAnimate()) {
|
||||
return;
|
||||
}
|
||||
|
||||
EnsureAnimExists();
|
||||
|
||||
// only advance the frame if the current time is greater than or
|
||||
// equal to the current frame's end time.
|
||||
TimeStamp currentFrameEndTime = GetCurrentImgFrameEndTime();
|
||||
bool frameAdvanced = false;
|
||||
|
||||
// The dirtyRect variable will contain an accumulation of the sub-rectangles
|
||||
// that are dirty for each frame we advance in AdvanceFrame().
|
||||
nsIntRect dirtyRect;
|
||||
|
||||
while (currentFrameEndTime <= aTime) {
|
||||
TimeStamp oldFrameEndTime = currentFrameEndTime;
|
||||
nsIntRect frameDirtyRect;
|
||||
bool didAdvance = AdvanceFrame(aTime, &frameDirtyRect);
|
||||
frameAdvanced = frameAdvanced || didAdvance;
|
||||
currentFrameEndTime = GetCurrentImgFrameEndTime();
|
||||
|
||||
// Accumulate the dirty area.
|
||||
dirtyRect = dirtyRect.Union(frameDirtyRect);
|
||||
|
||||
// if we didn't advance a frame, and our frame end time didn't change,
|
||||
// then we need to break out of this loop & wait for the frame(s)
|
||||
// to finish downloading
|
||||
if (!frameAdvanced && (currentFrameEndTime == oldFrameEndTime)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (frameAdvanced) {
|
||||
nsCOMPtr<imgIContainerObserver> observer(do_QueryReferent(mObserver));
|
||||
|
||||
if (!observer) {
|
||||
NS_ERROR("Refreshing image after its imgRequest is gone");
|
||||
StopAnimation();
|
||||
return;
|
||||
}
|
||||
|
||||
// Notify listeners that our frame has actually changed, but do this only
|
||||
// once for all frames that we've now passed (if AdvanceFrame() was called
|
||||
// more than once).
|
||||
#ifdef DEBUG
|
||||
mFramesNotified++;
|
||||
#endif
|
||||
|
||||
observer->FrameChanged(this, &dirtyRect);
|
||||
}
|
||||
}
|
||||
|
||||
//******************************************************************************
|
||||
/* [noscript] imgIContainer extractFrame(PRUint32 aWhichFrame,
|
||||
* [const] in nsIntRect aRegion,
|
||||
|
@ -657,27 +494,6 @@ RasterImage::GetCurrentImgFrameIndex() const
|
|||
return 0;
|
||||
}
|
||||
|
||||
TimeStamp
|
||||
RasterImage::GetCurrentImgFrameEndTime() const
|
||||
{
|
||||
imgFrame* currentFrame = mFrames[mAnim->currentAnimationFrameIndex];
|
||||
TimeStamp currentFrameTime = mAnim->currentAnimationFrameTime;
|
||||
PRInt64 timeout = currentFrame->GetTimeout();
|
||||
|
||||
if (timeout < 0) {
|
||||
// We need to return a sentinel value in this case, because our logic
|
||||
// doesn't work correctly if we have a negative timeout value. The reason
|
||||
// this positive infinity was chosen was because it works with the loop in
|
||||
// RequestRefresh() above.
|
||||
return TimeStamp() + TimeDuration::FromMilliseconds(UINT64_MAX_VAL);
|
||||
}
|
||||
|
||||
TimeDuration durationOfTimeout = TimeDuration::FromMilliseconds(timeout);
|
||||
TimeStamp currentFrameEndTime = currentFrameTime + durationOfTimeout;
|
||||
|
||||
return currentFrameEndTime;
|
||||
}
|
||||
|
||||
imgFrame*
|
||||
RasterImage::GetCurrentImgFrame()
|
||||
{
|
||||
|
@ -1322,18 +1138,25 @@ RasterImage::StartAnimation()
|
|||
|
||||
EnsureAnimExists();
|
||||
|
||||
imgFrame* currentFrame = GetCurrentImgFrame();
|
||||
NS_ABORT_IF_FALSE(mAnim && !mAnim->timer, "Anim must exist and not have a timer yet");
|
||||
|
||||
// Default timeout to 100: the timer notify code will do the right
|
||||
// thing, so just get that started.
|
||||
PRInt32 timeout = 100;
|
||||
imgFrame *currentFrame = GetCurrentImgFrame();
|
||||
if (currentFrame) {
|
||||
if (currentFrame->GetTimeout() < 0) { // -1 means display this frame forever
|
||||
timeout = currentFrame->GetTimeout();
|
||||
if (timeout < 0) { // -1 means display this frame forever
|
||||
mAnimationFinished = true;
|
||||
return NS_ERROR_ABORT;
|
||||
}
|
||||
|
||||
// We need to set the time that this initial frame was first displayed, as
|
||||
// this is used in AdvanceFrame().
|
||||
mAnim->currentAnimationFrameTime = TimeStamp::Now();
|
||||
}
|
||||
|
||||
mAnim->timer = do_CreateInstance("@mozilla.org/timer;1");
|
||||
NS_ENSURE_TRUE(mAnim->timer, NS_ERROR_OUT_OF_MEMORY);
|
||||
mAnim->timer->InitWithCallback(static_cast<nsITimerCallback*>(this),
|
||||
timeout, nsITimer::TYPE_REPEATING_SLACK);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -1347,6 +1170,11 @@ RasterImage::StopAnimation()
|
|||
if (mError)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
if (mAnim->timer) {
|
||||
mAnim->timer->Cancel();
|
||||
mAnim->timer = nsnull;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -1602,6 +1430,127 @@ RasterImage::SetSourceSizeHint(PRUint32 sizeHint)
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
//******************************************************************************
|
||||
/* void notify(in nsITimer timer); */
|
||||
NS_IMETHODIMP
|
||||
RasterImage::Notify(nsITimer *timer)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
mFramesNotified++;
|
||||
#endif
|
||||
|
||||
// This should never happen since the timer is only set up in StartAnimation()
|
||||
// after mAnim is checked to exist.
|
||||
NS_ABORT_IF_FALSE(mAnim, "Need anim for Notify()");
|
||||
NS_ABORT_IF_FALSE(timer, "Need timer for Notify()");
|
||||
NS_ABORT_IF_FALSE(mAnim->timer == timer,
|
||||
"RasterImage::Notify() called with incorrect timer");
|
||||
|
||||
if (!mAnimating || !ShouldAnimate())
|
||||
return NS_OK;
|
||||
|
||||
nsCOMPtr<imgIContainerObserver> observer(do_QueryReferent(mObserver));
|
||||
if (!observer) {
|
||||
// the imgRequest that owns us is dead, we should die now too.
|
||||
NS_ABORT_IF_FALSE(mAnimationConsumers == 0,
|
||||
"If no observer, should have no consumers");
|
||||
if (mAnimating)
|
||||
StopAnimation();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (mFrames.Length() == 0)
|
||||
return NS_OK;
|
||||
|
||||
imgFrame *nextFrame = nsnull;
|
||||
PRInt32 previousFrameIndex = mAnim->currentAnimationFrameIndex;
|
||||
PRUint32 nextFrameIndex = mAnim->currentAnimationFrameIndex + 1;
|
||||
PRInt32 timeout = 0;
|
||||
|
||||
// Figure out if we have the next full frame. This is more complicated than
|
||||
// just checking for mFrames.Length() because decoders append their frames
|
||||
// before they're filled in.
|
||||
NS_ABORT_IF_FALSE(mDecoder || nextFrameIndex <= mFrames.Length(),
|
||||
"How did we get 2 indicies too far by incrementing?");
|
||||
|
||||
// If we don't have a decoder, we know we've got everything we're going to get.
|
||||
// If we do, we only display fully-downloaded frames; everything else gets delayed.
|
||||
bool haveFullNextFrame = !mDecoder || nextFrameIndex < mDecoder->GetCompleteFrameCount();
|
||||
|
||||
// If we're done decoding the next frame, go ahead and display it now and
|
||||
// reinit the timer with the next frame's delay time.
|
||||
if (haveFullNextFrame) {
|
||||
if (mFrames.Length() == nextFrameIndex) {
|
||||
// End of Animation
|
||||
|
||||
// If animation mode is "loop once", it's time to stop animating
|
||||
if (mAnimationMode == kLoopOnceAnimMode || mLoopCount == 0) {
|
||||
mAnimationFinished = true;
|
||||
EvaluateAnimation();
|
||||
return NS_OK;
|
||||
} else {
|
||||
// We may have used compositingFrame to build a frame, and then copied
|
||||
// it back into mFrames[..]. If so, delete composite to save memory
|
||||
if (mAnim->compositingFrame && mAnim->lastCompositedFrameIndex == -1)
|
||||
mAnim->compositingFrame = nsnull;
|
||||
}
|
||||
|
||||
nextFrameIndex = 0;
|
||||
if (mLoopCount > 0)
|
||||
mLoopCount--;
|
||||
}
|
||||
|
||||
if (!(nextFrame = mFrames[nextFrameIndex])) {
|
||||
// something wrong with the next frame, skip it
|
||||
mAnim->currentAnimationFrameIndex = nextFrameIndex;
|
||||
mAnim->timer->SetDelay(100);
|
||||
return NS_OK;
|
||||
}
|
||||
timeout = nextFrame->GetTimeout();
|
||||
|
||||
} else {
|
||||
// Uh oh, the frame we want to show is currently being decoded (partial)
|
||||
// Wait a bit and try again
|
||||
mAnim->timer->SetDelay(100);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (timeout > 0)
|
||||
mAnim->timer->SetDelay(timeout);
|
||||
else {
|
||||
mAnimationFinished = true;
|
||||
EvaluateAnimation();
|
||||
}
|
||||
|
||||
nsIntRect dirtyRect;
|
||||
|
||||
if (nextFrameIndex == 0) {
|
||||
dirtyRect = mAnim->firstFrameRefreshArea;
|
||||
} else {
|
||||
imgFrame *prevFrame = mFrames[previousFrameIndex];
|
||||
if (!prevFrame)
|
||||
return NS_OK;
|
||||
|
||||
// Change frame and announce it
|
||||
if (NS_FAILED(DoComposite(&dirtyRect, prevFrame,
|
||||
nextFrame, nextFrameIndex))) {
|
||||
// something went wrong, move on to next
|
||||
NS_WARNING("RasterImage::Notify(): Composing Frame Failed\n");
|
||||
nextFrame->SetCompositingFailed(true);
|
||||
mAnim->currentAnimationFrameIndex = nextFrameIndex;
|
||||
return NS_OK;
|
||||
} else {
|
||||
nextFrame->SetCompositingFailed(false);
|
||||
}
|
||||
}
|
||||
// Set currentAnimationFrameIndex at the last possible moment
|
||||
mAnim->currentAnimationFrameIndex = nextFrameIndex;
|
||||
// Refreshes the screen
|
||||
observer->FrameChanged(this, &dirtyRect);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
//******************************************************************************
|
||||
// DoComposite gets called when the timer for animation get fired and we have to
|
||||
// update the composited frame of the animation.
|
||||
|
|
|
@ -71,7 +71,6 @@
|
|||
#endif
|
||||
|
||||
class imgIDecoder;
|
||||
class imgIContainerObserver;
|
||||
class nsIInputStream;
|
||||
|
||||
#define NS_RASTERIMAGE_CID \
|
||||
|
@ -82,12 +81,6 @@ class nsIInputStream;
|
|||
{0xb1, 0x43, 0x33, 0x40, 0xc0, 0x01, 0x12, 0xf7} \
|
||||
}
|
||||
|
||||
/**
|
||||
* It would be nice if we had a macro for this in prtypes.h.
|
||||
* TODO: Place this macro in prtypes.h as PR_UINT64_MAX.
|
||||
*/
|
||||
#define UINT64_MAX_VAL PRUint64(-1)
|
||||
|
||||
/**
|
||||
* Handles static and animated image containers.
|
||||
*
|
||||
|
@ -95,27 +88,15 @@ class nsIInputStream;
|
|||
* @par A Quick Walk Through
|
||||
* The decoder initializes this class and calls AppendFrame() to add a frame.
|
||||
* Once RasterImage detects more than one frame, it starts the animation
|
||||
* with StartAnimation(). Note that the invalidation events for RasterImage are
|
||||
* generated automatically using nsRefreshDriver.
|
||||
* with StartAnimation().
|
||||
*
|
||||
* @par
|
||||
* StartAnimation() initializes the animation helper object and sets the time
|
||||
* the first frame was displayed to the current clock time.
|
||||
* StartAnimation() creates a timer. The timer calls Notify when the
|
||||
* specified frame delay time is up.
|
||||
*
|
||||
* @par
|
||||
* When the refresh driver corresponding to the imgIContainer that this image is
|
||||
* a part of notifies the RasterImage that it's time to invalidate,
|
||||
* RequestRefresh() is called with a given TimeStamp to advance to. As long as
|
||||
* the timeout of the given frame (the frame's "delay") plus the time that frame
|
||||
* was first displayed is less than or equal to the TimeStamp given,
|
||||
* RequestRefresh() calls AdvanceFrame().
|
||||
*
|
||||
* @par
|
||||
* AdvanceFrame() is responsible for advancing a single frame of the animation.
|
||||
* It can return true, meaning that the frame advanced, or false, meaning that
|
||||
* the frame failed to advance (usually because the next frame hasn't been
|
||||
* decoded yet). It is also responsible for performing the final animation stop
|
||||
* procedure if the final frame of a non-looping animation is reached.
|
||||
* Notify() moves on to the next frame, sets up the new timer delay, destroys
|
||||
* the old frame, and forces a redraw via observer->FrameChanged().
|
||||
*
|
||||
* @par
|
||||
* Each frame can have a different method of removing itself. These are
|
||||
|
@ -170,6 +151,7 @@ class imgDecodeWorker;
|
|||
class Decoder;
|
||||
|
||||
class RasterImage : public Image
|
||||
, public nsITimerCallback
|
||||
, public nsIProperties
|
||||
, public nsSupportsWeakReference
|
||||
#ifdef DEBUG
|
||||
|
@ -178,6 +160,7 @@ class RasterImage : public Image
|
|||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSITIMERCALLBACK
|
||||
NS_DECL_NSIPROPERTIES
|
||||
#ifdef DEBUG
|
||||
NS_DECL_IMGICONTAINERDEBUG
|
||||
|
@ -200,7 +183,6 @@ public:
|
|||
NS_SCRIPTABLE NS_IMETHOD LockImage(void);
|
||||
NS_SCRIPTABLE NS_IMETHOD UnlockImage(void);
|
||||
NS_SCRIPTABLE NS_IMETHOD ResetAnimation(void);
|
||||
NS_IMETHOD_(void) RequestRefresh(const mozilla::TimeStamp& aTime);
|
||||
// END NS_DECL_IMGICONTAINER
|
||||
|
||||
RasterImage(imgStatusTracker* aStatusTracker = nsnull);
|
||||
|
@ -352,10 +334,6 @@ private:
|
|||
//! Area of the first frame that needs to be redrawn on subsequent loops.
|
||||
nsIntRect firstFrameRefreshArea;
|
||||
PRUint32 currentAnimationFrameIndex; // 0 to numFrames-1
|
||||
|
||||
// the time that the animation advanced to the current frame
|
||||
TimeStamp currentAnimationFrameTime;
|
||||
|
||||
//! Track the last composited frame for Optimizations (See DoComposite code)
|
||||
PRInt32 lastCompositedFrameIndex;
|
||||
/** For managing blending of frames
|
||||
|
@ -374,33 +352,23 @@ private:
|
|||
* when it's done with the current frame.
|
||||
*/
|
||||
nsAutoPtr<imgFrame> compositingPrevFrame;
|
||||
//! Timer to animate multiframed images
|
||||
nsCOMPtr<nsITimer> timer;
|
||||
|
||||
Anim() :
|
||||
firstFrameRefreshArea(),
|
||||
currentAnimationFrameIndex(0),
|
||||
lastCompositedFrameIndex(-1) {}
|
||||
~Anim() {}
|
||||
lastCompositedFrameIndex(-1)
|
||||
{
|
||||
;
|
||||
}
|
||||
~Anim()
|
||||
{
|
||||
if (timer)
|
||||
timer->Cancel();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Advances the animation. Typically, this will advance a single frame, but it
|
||||
* may advance multiple frames. This may happen if we have infrequently
|
||||
* "ticking" refresh drivers (e.g. in background tabs), or extremely short-
|
||||
* lived animation frames.
|
||||
*
|
||||
* @param aTime the time that the animation should advance to. This will
|
||||
* typically be <= TimeStamp::Now().
|
||||
*
|
||||
* @param [out] aDirtyRect a pointer to an nsIntRect which encapsulates the
|
||||
* area to be repainted after the frame is advanced.
|
||||
*
|
||||
* @returns true, if the frame was successfully advanced, false if it was not
|
||||
* able to be advanced (e.g. the frame to which we want to advance is
|
||||
* still decoding). Note: If false is returned, then aDirtyRect will
|
||||
* remain unmodified.
|
||||
*/
|
||||
bool AdvanceFrame(mozilla::TimeStamp aTime, nsIntRect* aDirtyRect);
|
||||
|
||||
/**
|
||||
* Deletes and nulls out the frame in mFrames[framenum].
|
||||
*
|
||||
|
@ -417,7 +385,6 @@ private:
|
|||
imgFrame* GetCurrentImgFrame();
|
||||
imgFrame* GetCurrentDrawableImgFrame();
|
||||
PRUint32 GetCurrentImgFrameIndex() const;
|
||||
mozilla::TimeStamp GetCurrentImgFrameEndTime() const;
|
||||
|
||||
inline void EnsureAnimExists()
|
||||
{
|
||||
|
@ -438,7 +405,7 @@ private:
|
|||
LockImage();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** Function for doing the frame compositing of animations
|
||||
*
|
||||
* @param aDirtyRect Area that the display will need to update
|
||||
|
@ -450,7 +417,7 @@ private:
|
|||
imgFrame* aPrevFrame,
|
||||
imgFrame* aNextFrame,
|
||||
PRInt32 aNextFrameIndex);
|
||||
|
||||
|
||||
/** Clears an area of <aFrame> with transparent black.
|
||||
*
|
||||
* @param aFrame Target Frame
|
||||
|
@ -458,7 +425,7 @@ private:
|
|||
* @note Does also clears the transparancy mask
|
||||
*/
|
||||
static void ClearFrame(imgFrame* aFrame);
|
||||
|
||||
|
||||
//! @overload
|
||||
static void ClearFrame(imgFrame* aFrame, nsIntRect &aRect);
|
||||
|
||||
|
|
|
@ -172,6 +172,7 @@ SVGDrawingCallback::operator()(gfxContext* aContext,
|
|||
gfxContextMatrixAutoSaveRestore contextMatrixRestorer(aContext);
|
||||
aContext->Multiply(gfxMatrix(aTransform).Invert());
|
||||
|
||||
|
||||
nsPresContext* presContext = presShell->GetPresContext();
|
||||
NS_ABORT_IF_FALSE(presContext, "pres shell w/out pres context");
|
||||
|
||||
|
@ -329,14 +330,6 @@ VectorImage::GetWidth(PRInt32* aWidth)
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
//******************************************************************************
|
||||
/* [notxpcom] void requestRefresh ([const] in TimeStamp aTime); */
|
||||
NS_IMETHODIMP_(void)
|
||||
VectorImage::RequestRefresh(const mozilla::TimeStamp& aTime)
|
||||
{
|
||||
// TODO: Implement for b666446.
|
||||
}
|
||||
|
||||
//******************************************************************************
|
||||
/* readonly attribute PRInt32 height; */
|
||||
NS_IMETHODIMP
|
||||
|
|
|
@ -42,7 +42,6 @@
|
|||
#include "Image.h"
|
||||
#include "nsIStreamListener.h"
|
||||
#include "nsWeakReference.h"
|
||||
#include "mozilla/TimeStamp.h"
|
||||
|
||||
class imgIDecoderObserver;
|
||||
|
||||
|
@ -77,7 +76,6 @@ public:
|
|||
NS_SCRIPTABLE NS_IMETHOD LockImage(void);
|
||||
NS_SCRIPTABLE NS_IMETHOD UnlockImage(void);
|
||||
NS_SCRIPTABLE NS_IMETHOD ResetAnimation(void);
|
||||
NS_IMETHOD_(void) RequestRefresh(const mozilla::TimeStamp& aTime);
|
||||
// END NS_DECL_IMGICONTAINER
|
||||
|
||||
VectorImage(imgStatusTracker* aStatusTracker = nsnull);
|
||||
|
|
|
@ -93,27 +93,8 @@ _TEST_FILES = imgutils.js \
|
|||
# test_bug478398.html disabled - See bug 579139
|
||||
|
||||
_CHROME_FILES = imgutils.js \
|
||||
animationPolling.js \
|
||||
lime-anim-100x100.svg \
|
||||
animation.svg \
|
||||
test_animSVGImage.html \
|
||||
test_animation.html \
|
||||
animated-gif-finalframe.gif \
|
||||
animated-gif.gif \
|
||||
animated-gif2.gif \
|
||||
purple.gif \
|
||||
test_svg_animatedGIF.html \
|
||||
test_bullet_animation.html \
|
||||
test_background_image_anim.html \
|
||||
filter.svg \
|
||||
filter-final.svg \
|
||||
test_svg_filter_animation.html \
|
||||
test_xultree_animation.xhtml \
|
||||
test_changeOfSource.html \
|
||||
test_changeOfSource2.html \
|
||||
test_undisplayed_iframe.html \
|
||||
iframe.html \
|
||||
ref-iframe.html \
|
||||
$(NULL)
|
||||
|
||||
libs:: $(_TEST_FILES)
|
||||
|
|
Двоичные данные
image/test/mochitest/animated-gif-finalframe.gif
До Ширина: | Высота: | Размер: 72 B |
Двоичные данные
image/test/mochitest/animated-gif.gif
До Ширина: | Высота: | Размер: 146 B |
Двоичные данные
image/test/mochitest/animated-gif2.gif
До Ширина: | Высота: | Размер: 165 B |
|
@ -1,5 +0,0 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<image id="anim" xlink:href="animated-gif.gif" width="40" height="40"/>
|
||||
</svg>
|
||||
|
До Ширина: | Высота: | Размер: 170 B |
|
@ -1,395 +0,0 @@
|
|||
var currentTest;
|
||||
var gIsImageLoaded = false;
|
||||
var gIsRefImageLoaded = false;
|
||||
|
||||
function pollForSuccess ()
|
||||
{
|
||||
if (!currentTest.isTestFinished) {
|
||||
if (!currentTest.reusingReferenceImage || (currentTest.reusingReferenceImage
|
||||
&& gRefImageLoaded)) {
|
||||
currentTest.checkImage();
|
||||
}
|
||||
|
||||
setTimeout(pollForSuccess, currentTest.pollFreq);
|
||||
}
|
||||
};
|
||||
|
||||
function imageLoadCallback()
|
||||
{
|
||||
gIsImageLoaded = true;
|
||||
}
|
||||
|
||||
function referencePoller()
|
||||
{
|
||||
currentTest.takeReferenceSnapshot();
|
||||
}
|
||||
|
||||
function reuseImageCallback()
|
||||
{
|
||||
gIsRefImageLoaded = true;
|
||||
}
|
||||
|
||||
function failTest ()
|
||||
{
|
||||
if (currentTest.isTestFinished || currentTest.closeFunc) {
|
||||
return;
|
||||
}
|
||||
|
||||
ok(false, "timing out after " + currentTest.timeout + "ms. "
|
||||
+ "Animated image still doesn't look correct, " + "after call #"
|
||||
+ currentTest.onStopFrameCounter + " to onStopFrame");
|
||||
currentTest.wereFailures = true;
|
||||
|
||||
currentTest.enableDisplay(document.getElementById(currentTest.debugElementId));
|
||||
|
||||
currentTest.cleanUpAndFinish();
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a new AnimationTest object.
|
||||
*
|
||||
* @param pollFreq The amount of time (in ms) to wait between consecutive
|
||||
* snapshots if the reference image and the test image don't match.
|
||||
* @param timeout The total amount of time (in ms) to wait before declaring the
|
||||
* test as failed.
|
||||
* @param referenceElementId The id attribute of the reference image element, or
|
||||
* the source of the image to change to, once the reference snapshot has
|
||||
* been successfully taken. This latter option could be used if you don't
|
||||
* want the image to become invisible at any time during the test.
|
||||
* @param imageElementId The id attribute of the test image element.
|
||||
* @param debugElementId The id attribute of the div where links should be
|
||||
* appended if the test fails.
|
||||
* @param cleanId The id attribute of the div or element to use as the 'clean'
|
||||
* test. This element is only enabled when we are testing to verify that
|
||||
* the reference image has been loaded. It can be undefined.
|
||||
* @param srcAttr The location of the source of the image, for preloading. This
|
||||
* is usually not required, but it useful for preloading reference
|
||||
* images.
|
||||
* @param xulTest A boolean value indicating whether or not this is a XUL test
|
||||
* (uses hidden=true/false rather than display: none to hide/show
|
||||
* elements).
|
||||
* @param closeFunc A function that should be called when this test is finished.
|
||||
* If null, then cleanUpAndFinish() will be called. This can be used to
|
||||
* chain tests together, so they are all finished exactly once.
|
||||
* @returns {AnimationTest}
|
||||
*/
|
||||
function AnimationTest(pollFreq, timeout, referenceElementId, imageElementId,
|
||||
debugElementId, cleanId, srcAttr, xulTest, closeFunc)
|
||||
{
|
||||
// We want to test the cold loading behavior, so clear cache in case an
|
||||
// earlier test got our image in there already.
|
||||
clearImageCache();
|
||||
|
||||
this.wereFailures = false;
|
||||
this.pollFreq = pollFreq;
|
||||
this.timeout = timeout;
|
||||
this.imageElementId = imageElementId;
|
||||
this.referenceElementId = referenceElementId;
|
||||
|
||||
if (!document.getElementById(referenceElementId)) {
|
||||
// In this case, we're assuming the user passed in a string that
|
||||
// indicates the source of the image they want to change to,
|
||||
// after the reference image has been taken.
|
||||
this.reusingImageAsReference = true;
|
||||
}
|
||||
|
||||
this.srcAttr = srcAttr;
|
||||
this.debugElementId = debugElementId;
|
||||
this.referenceSnapshot = ""; // value will be set in takeReferenceSnapshot()
|
||||
this.onStopFrameCounter = 0;
|
||||
this.isTestFinished = false;
|
||||
this.numRefsTaken = 0;
|
||||
this.blankWaitTime = 0;
|
||||
|
||||
this.cleanId = cleanId ? cleanId : '';
|
||||
this.xulTest = xulTest ? xulTest : '';
|
||||
this.closeFunc = closeFunc ? closeFunc : '';
|
||||
|
||||
if (this.srcAttr) {
|
||||
this.myImage = new Image();
|
||||
this.myImage.onload = imageLoadCallback;
|
||||
this.myImage.src = this.srcAttr;
|
||||
} else {
|
||||
gIsImageLoaded = true;
|
||||
}
|
||||
}
|
||||
|
||||
AnimationTest.prototype.outputDebugInfo = function(message, id, dataUri)
|
||||
{
|
||||
var debugElement = document.getElementById(this.debugElementId);
|
||||
var newDataUriElement = document.createElement("a");
|
||||
newDataUriElement.setAttribute("id", id);
|
||||
newDataUriElement.setAttribute("href", dataUri);
|
||||
newDataUriElement.appendChild(document.createTextNode(message));
|
||||
debugElement.appendChild(newDataUriElement);
|
||||
var brElement = document.createElement("br");
|
||||
debugElement.appendChild(brElement);
|
||||
};
|
||||
|
||||
AnimationTest.prototype.isFinished = function()
|
||||
{
|
||||
return this.isTestFinished;
|
||||
};
|
||||
|
||||
AnimationTest.prototype.takeCleanSnapshot = function()
|
||||
{
|
||||
var cleanElement;
|
||||
if (this.cleanId) {
|
||||
cleanElement = document.getElementById(this.cleanId);
|
||||
}
|
||||
|
||||
// Enable clean page comparison element
|
||||
if (cleanElement) {
|
||||
this.enableDisplay(cleanElement);
|
||||
}
|
||||
|
||||
// Take a snapshot of the initial (clean) page
|
||||
this.cleanSnapshot = snapshotWindow(window, false);
|
||||
|
||||
// Disable the clean page comparison element
|
||||
if (cleanElement) {
|
||||
this.disableDisplay(cleanElement);
|
||||
}
|
||||
|
||||
var dataString1 = "Clean Snapshot";
|
||||
this.outputDebugInfo(dataString1, 'cleanSnap',
|
||||
this.cleanSnapshot.toDataURL());
|
||||
};
|
||||
|
||||
AnimationTest.prototype.takeBlankSnapshot = function()
|
||||
{
|
||||
// Take a snapshot of the initial (essentially blank) page
|
||||
this.blankSnapshot = snapshotWindow(window, false);
|
||||
|
||||
var dataString1 = "Initial Blank Snapshot";
|
||||
this.outputDebugInfo(dataString1, 'blank1Snap',
|
||||
this.blankSnapshot.toDataURL());
|
||||
};
|
||||
|
||||
/**
|
||||
* Begin the AnimationTest. This will utilize the information provided in the
|
||||
* constructor to invoke a mochitest on animated images. It will automatically
|
||||
* fail if allowed to run past the timeout.
|
||||
*/
|
||||
AnimationTest.prototype.beginTest = function ()
|
||||
{
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
currentTest = this;
|
||||
|
||||
this.takeReferenceSnapshot();
|
||||
|
||||
// In case something goes wrong, fail earlier than mochitest timeout,
|
||||
// and with more information.
|
||||
setTimeout(failTest, this.timeout);
|
||||
|
||||
if (!this.reusingImageAsReference) {
|
||||
this.disableDisplay(document.getElementById(this.imageElementId));
|
||||
}
|
||||
|
||||
this.setupPolledImage();
|
||||
setTimeout(pollForSuccess, 10);
|
||||
};
|
||||
|
||||
AnimationTest.prototype.setupPolledImage = function ()
|
||||
{
|
||||
// Make sure the image is visible
|
||||
if (!this.reusingImageAsReference) {
|
||||
this.enableDisplay(document.getElementById(this.imageElementId));
|
||||
var currentSnapshot = snapshotWindow(window, false);
|
||||
var result = compareSnapshots(currentSnapshot, this.referenceSnapshot, true);
|
||||
|
||||
var dataString = "Snapshot #" + this.onStopFrameCounter;
|
||||
this.outputDebugInfo(dataString, 'snap' + this.onStopFrameCounter,
|
||||
currentSnapshot.toDataURL());
|
||||
|
||||
if (result[0]) {
|
||||
// SUCCESS!
|
||||
ok(true, "Animated image looks correct, " + "at call #"
|
||||
+ this.onStopFrameCounter + " to onStopFrame");
|
||||
|
||||
this.cleanUpAndFinish();
|
||||
}
|
||||
} else {
|
||||
if (!gIsRefImageLoaded) {
|
||||
this.myImage = new Image();
|
||||
this.myImage.onload = reuseImageCallback;
|
||||
document.getElementById(this.imageElementId).setAttribute('src',
|
||||
this.referenceElementId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AnimationTest.prototype.checkImage = function ()
|
||||
{
|
||||
if (this.isTestFinished) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.onStopFrameCounter++;
|
||||
|
||||
// We need this for some tests, because we need to force the
|
||||
// test image to be visible.
|
||||
if (!this.reusingImageAsReference) {
|
||||
this.enableDisplay(document.getElementById(this.imageElementId));
|
||||
}
|
||||
|
||||
var currentSnapshot = snapshotWindow(window, false);
|
||||
var result = compareSnapshots(currentSnapshot, this.referenceSnapshot, true);
|
||||
|
||||
var dataString = "Snapshot #" + this.onStopFrameCounter;
|
||||
this.outputDebugInfo(dataString, 'snap' + this.onStopFrameCounter,
|
||||
currentSnapshot.toDataURL());
|
||||
|
||||
if (result[0]) {
|
||||
// SUCCESS!
|
||||
ok(true, "Animated image looks correct, " + "at call #"
|
||||
+ this.onStopFrameCounter + " to onStopFrame");
|
||||
|
||||
this.cleanUpAndFinish();
|
||||
}
|
||||
};
|
||||
|
||||
AnimationTest.prototype.takeReferenceSnapshot = function ()
|
||||
{
|
||||
this.numRefsTaken++;
|
||||
|
||||
// Test to make sure the reference image doesn't match a clean snapshot
|
||||
if (!this.cleanSnapshot) {
|
||||
this.takeCleanSnapshot();
|
||||
}
|
||||
|
||||
// Used later to verify that the reference div disappeared
|
||||
if (!this.blankSnapshot) {
|
||||
this.takeBlankSnapshot();
|
||||
}
|
||||
|
||||
if (this.reusingImageAsReference) {
|
||||
// Show reference div, & take a snapshot
|
||||
var referenceDiv = document.getElementById(this.imageElementId);
|
||||
this.enableDisplay(referenceDiv);
|
||||
|
||||
this.referenceSnapshot = snapshotWindow(window, false);
|
||||
var snapResult = compareSnapshots(this.cleanSnapshot, this.referenceSnapshot,
|
||||
false);
|
||||
if (!snapResult[0]) {
|
||||
if (this.blankWaitTime > 2000) {
|
||||
// if it took longer than two seconds to load the image, we probably
|
||||
// have a problem.
|
||||
this.wereFailures = true;
|
||||
ok(snapResult[0],
|
||||
"Reference snapshot shouldn't match clean (non-image) snapshot");
|
||||
} else {
|
||||
this.blankWaitTime += 20;
|
||||
// let's wait a bit and see if it clears up
|
||||
setTimeout(referencePoller, 20);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
ok(snapResult[0],
|
||||
"Reference snapshot shouldn't match clean (non-image) snapshot");
|
||||
|
||||
var dataString = "Reference Snapshot #" + this.numRefsTaken;
|
||||
this.outputDebugInfo(dataString, 'refSnapId',
|
||||
this.referenceSnapshot.toDataURL());
|
||||
} else {
|
||||
// Make sure the animation section is hidden
|
||||
this.disableDisplay(document.getElementById(this.imageElementId));
|
||||
|
||||
// Show reference div, & take a snapshot
|
||||
var referenceDiv = document.getElementById(this.referenceElementId);
|
||||
this.enableDisplay(referenceDiv);
|
||||
|
||||
this.referenceSnapshot = snapshotWindow(window, false);
|
||||
var snapResult = compareSnapshots(this.cleanSnapshot, this.referenceSnapshot, false);
|
||||
if (!snapResult[0]) {
|
||||
if (this.blankWaitTime > 2000) {
|
||||
// if it took longer than two seconds to load the image, we probably
|
||||
// have a problem.
|
||||
this.wereFailures = true;
|
||||
ok(snapResult[0],
|
||||
"Reference snapshot shouldn't match clean (non-image) snapshot");
|
||||
} else {
|
||||
this.blankWaitTime += 20;
|
||||
// let's wait a bit and see if it clears up
|
||||
setTimeout(referencePoller, 20);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
ok(snapResult[0],
|
||||
"Reference snapshot shouldn't match clean (non-image) snapshot");
|
||||
|
||||
var dataString = "Reference Snapshot #" + this.numRefsTaken;
|
||||
this.outputDebugInfo(dataString, 'refSnapId',
|
||||
this.referenceSnapshot.toDataURL());
|
||||
|
||||
// Re-hide reference div, and take another snapshot to be sure it's gone
|
||||
this.disableDisplay(referenceDiv);
|
||||
this.testBlankCameBack();
|
||||
}
|
||||
};
|
||||
|
||||
AnimationTest.prototype.enableDisplay = function(element)
|
||||
{
|
||||
if (!element) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.xulTest) {
|
||||
element.style.display = '';
|
||||
} else {
|
||||
element.setAttribute('hidden', 'false');
|
||||
}
|
||||
};
|
||||
|
||||
AnimationTest.prototype.disableDisplay = function(element)
|
||||
{
|
||||
if (!element) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.xulTest) {
|
||||
element.style.display = 'none';
|
||||
} else {
|
||||
element.setAttribute('hidden', 'true');
|
||||
}
|
||||
};
|
||||
|
||||
AnimationTest.prototype.testBlankCameBack = function()
|
||||
{
|
||||
var blankSnapshot2 = snapshotWindow(window, false);
|
||||
var result = compareSnapshots(this.blankSnapshot, blankSnapshot2, true);
|
||||
ok(result[0], "Reference image should disappear when it becomes display:none");
|
||||
|
||||
if (!result[0]) {
|
||||
this.wereFailures = true;
|
||||
var dataString = "Second Blank Snapshot";
|
||||
this.outputDebugInfo(dataString, 'blank2SnapId', result[2]);
|
||||
}
|
||||
};
|
||||
|
||||
AnimationTest.prototype.cleanUpAndFinish = function ()
|
||||
{
|
||||
// On the off chance that failTest and checkImage are triggered
|
||||
// back-to-back, use a flag to prevent multiple calls to SimpleTest.finish.
|
||||
if (this.isTestFinished) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.isTestFinished = true;
|
||||
|
||||
// Call our closing function, if one exists
|
||||
if (this.closeFunc) {
|
||||
this.closeFunc();
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.wereFailures) {
|
||||
document.getElementById(this.debugElementId).style.display = 'block';
|
||||
}
|
||||
|
||||
SimpleTest.finish();
|
||||
document.getElementById(this.debugElementId).style.display = "";
|
||||
};
|
|
@ -1,9 +0,0 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
|
||||
<filter id="filter1" x="0%" y="0%" width="100%" height="100%">
|
||||
<feImage xlink:href="animated-gif-finalframe.gif"/>
|
||||
</filter>
|
||||
<g>
|
||||
<rect x="0" y="0" width="100%" height="100%" filter="url(#filter1)"/>
|
||||
</g>
|
||||
</svg>
|
До Ширина: | Высота: | Размер: 299 B |
|
@ -1,9 +0,0 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
|
||||
<filter id="filter1" x="0%" y="0%" width="100%" height="100%">
|
||||
<feImage xlink:href="animated-gif.gif"/>
|
||||
</filter>
|
||||
<g>
|
||||
<rect x="0" y="0" width="100%" height="100%" filter="url(#filter1)"/>
|
||||
</g>
|
||||
</svg>
|
До Ширина: | Высота: | Размер: 288 B |
|
@ -1,5 +0,0 @@
|
|||
<html>
|
||||
<body bgcolor="gray">
|
||||
<img src="animated-gif.gif">
|
||||
</body>
|
||||
</html>
|
Двоичные данные
image/test/mochitest/purple.gif
До Ширина: | Высота: | Размер: 86 B |
|
@ -1,6 +0,0 @@
|
|||
<html>
|
||||
<body bgcolor="gray">
|
||||
<div id="referenceImage"
|
||||
style="height: 40px; width: 40px; background: #2aff00"></div>
|
||||
</body>
|
||||
</html>
|
|
@ -1,46 +0,0 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=666446
|
||||
-->
|
||||
<head>
|
||||
<title>Test for Bug 666446 - General Animated GIF Test</title>
|
||||
<script type="application/javascript" src="chrome://mochikit/content/MochiKit/packed.js"></script>
|
||||
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/WindowSnapshot.js"></script>
|
||||
<script type="application/javascript" src="imgutils.js"></script>
|
||||
<script type="application/javascript" src="animationPolling.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=666446">
|
||||
Mozilla Bug 666446: lots of animated gifs swamp us with paint events
|
||||
</a>
|
||||
<p id="display"></p>
|
||||
|
||||
<div id="content">
|
||||
<div id="referenceDiv" style="height: 40px; width: 40px;
|
||||
display: none; background: #2aff00"></div>
|
||||
<div id="animatedImage">
|
||||
<img id="animatedGif" src="animated-gif.gif" style="display: none;">
|
||||
<div id="text-descr"></div>
|
||||
</div>
|
||||
<div id="debug" style="display:none">
|
||||
</div>
|
||||
</div>
|
||||
<pre id="test">
|
||||
<script type="text/javascript;version=1.8">
|
||||
const FAILURE_TIMEOUT = 120000; // Fail early after 120 seconds (2 minutes)
|
||||
|
||||
function main()
|
||||
{
|
||||
var animTest = new AnimationTest(20, FAILURE_TIMEOUT, 'referenceDiv',
|
||||
'animatedGif', 'debug');
|
||||
animTest.beginTest();
|
||||
}
|
||||
|
||||
window.onload = main;
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
|
@ -1,44 +0,0 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=666446
|
||||
-->
|
||||
<head>
|
||||
<title>Test for Bug 666446 - Animated Background Images</title>
|
||||
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/WindowSnapshot.js"></script>
|
||||
<script type="application/javascript" src="imgutils.js"></script>
|
||||
<script type="application/javascript" src="animationPolling.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=666446">
|
||||
Mozilla Bug 666446: lots of animated gifs swamp us with paint events
|
||||
</a>
|
||||
<p id="display"></p>
|
||||
<div id="content">
|
||||
<div id="referenceDiv" style="height: 140px; width: 140px;
|
||||
display: none; background: #2aff00"></div>
|
||||
<div id="bgImage" style="height: 140px; width: 140px; background-image: url(animated-gif.gif); display: none;">
|
||||
</div>
|
||||
</div>
|
||||
<div id="debug" style="display:none"></div>
|
||||
<pre id="test">
|
||||
<script type="text/javascript;version=1.8">
|
||||
|
||||
/** Test for Bug 666446 nsImageLoader/RasterImage**/
|
||||
|
||||
const FAILURE_TIMEOUT = 120000; // Fail early after 120 seconds (2 minutes)
|
||||
|
||||
function main() {
|
||||
var animTest = new AnimationTest(20, FAILURE_TIMEOUT, 'referenceDiv',
|
||||
'bgImage', 'debug');
|
||||
animTest.beginTest();
|
||||
}
|
||||
|
||||
window.onload = main;
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
|
@ -1,57 +0,0 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=666446
|
||||
-->
|
||||
<head>
|
||||
<title>Test for Bug 666446 - Animated Bullets</title>
|
||||
<script type="application/javascript" src="chrome://mochikit/content/MochiKit/packed.js"></script>
|
||||
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/WindowSnapshot.js"></script>
|
||||
<script type="application/javascript" src="imgutils.js"></script>
|
||||
<script type="application/javascript" src="animationPolling.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=666446">
|
||||
Mozilla Bug 666446: lots of animated gifs swamp us with paint events
|
||||
</a>
|
||||
<p id="display"></p>
|
||||
|
||||
<div id="content">
|
||||
<div id="cleanDiv" style="display: none;">
|
||||
<ul>
|
||||
<li>Test 1</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div id="referenceDiv" style="display: none;">
|
||||
<ul>
|
||||
<li style="list-style-image: url(animated-gif-finalframe.gif);">Test 1</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div id="animatedImage" style="display: none;">
|
||||
<ul>
|
||||
<li style="list-style-image: url(animated-gif.gif);">Test 1</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div id="text-descr"></div>
|
||||
<div id="debug" style="display:none">
|
||||
</div>
|
||||
</div>
|
||||
<pre id="test">
|
||||
<script type="text/javascript;version=1.8">
|
||||
const FAILURE_TIMEOUT = 120000; // Fail early after 120 seconds (2 minutes)
|
||||
|
||||
function main()
|
||||
{
|
||||
var animTest = new AnimationTest(20, FAILURE_TIMEOUT, 'referenceDiv',
|
||||
'animatedImage', 'debug', 'cleanDiv',
|
||||
'animated-gif-finalframe.gif');
|
||||
animTest.beginTest();
|
||||
}
|
||||
|
||||
window.onload = main;
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
|
@ -1,63 +0,0 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=666446
|
||||
-->
|
||||
<head>
|
||||
<title>Test for Bug 666446 - Change of Source (1st Version)</title>
|
||||
<script type="application/javascript" src="chrome://mochikit/content/MochiKit/packed.js"></script>
|
||||
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/WindowSnapshot.js"></script>
|
||||
<script type="application/javascript" src="imgutils.js"></script>
|
||||
<script type="application/javascript" src="animationPolling.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=666446">
|
||||
Mozilla Bug 666446: lots of animated gifs swamp us with paint events
|
||||
</a>
|
||||
<p id="display"></p>
|
||||
|
||||
<div id="content">
|
||||
<div id="referenceDiv" style="height: 40px; width: 40px;
|
||||
display: none; background: #2aff00;">
|
||||
</div>
|
||||
<div id="animatedImage">
|
||||
<img id='animatedGif' src="animated-gif.gif" style="display: none;">
|
||||
</div>
|
||||
<div id="text-descr"></div>
|
||||
<div id="debug" style="display:none">
|
||||
</div>
|
||||
</div>
|
||||
<pre id="test">
|
||||
<script type="text/javascript;version=1.8">
|
||||
const FAILURE_TIMEOUT = 120000; // Fail early after 120 seconds (2 minutes)
|
||||
|
||||
var gAnimTest;
|
||||
var gIntervalId;
|
||||
|
||||
function initSecondTest() {
|
||||
document.getElementById('debug').style.display = 'none';
|
||||
document.getElementById('referenceDiv').style.background = "#9600ff";
|
||||
document.getElementById('animatedGif').setAttribute('src',
|
||||
'animated-gif2.gif');
|
||||
document.getElementById('animatedGif').style.display = 'none';
|
||||
var secondTest = new AnimationTest(20, FAILURE_TIMEOUT, 'referenceDiv',
|
||||
'animatedGif', 'debug', '', '', false);
|
||||
secondTest.beginTest();
|
||||
}
|
||||
|
||||
function main()
|
||||
{
|
||||
gAnimTest = new AnimationTest(20, FAILURE_TIMEOUT, 'referenceDiv',
|
||||
'animatedGif', 'debug', '', '', false,
|
||||
initSecondTest);
|
||||
gAnimTest.beginTest();
|
||||
|
||||
}
|
||||
|
||||
window.onload = main;
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
|
@ -1,48 +0,0 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=666446
|
||||
-->
|
||||
<head>
|
||||
<title>Test for Bug 691792 - Change of Source (2nd Version)</title>
|
||||
<script type="application/javascript" src="chrome://mochikit/content/MochiKit/packed.js"></script>
|
||||
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/WindowSnapshot.js"></script>
|
||||
<script type="application/javascript" src="imgutils.js"></script>
|
||||
<script type="application/javascript" src="animationPolling.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=691792">
|
||||
Mozilla Bug 691792: Change of src attribute for animated gifs no longer works as expected
|
||||
</a>
|
||||
<p id="display"></p>
|
||||
|
||||
<div id="content">
|
||||
<div id="animatedImage">
|
||||
<img id='animatedGif' src="purple.gif" style="display: none;">
|
||||
</div>
|
||||
<div id="text-descr"></div>
|
||||
<div id="debug" style="display:none">
|
||||
</div>
|
||||
</div>
|
||||
<pre id="test">
|
||||
<script type="text/javascript;version=1.8">
|
||||
const FAILURE_TIMEOUT = 120000; // Fail early after 120 seconds (2 minutes)
|
||||
|
||||
var gAnimTest;
|
||||
var gIntervalId;
|
||||
|
||||
function main()
|
||||
{
|
||||
gAnimTest = new AnimationTest(20, FAILURE_TIMEOUT, 'animated-gif2.gif',
|
||||
'animatedGif', 'debug', '', 'animated-gif2.gif',
|
||||
false);
|
||||
gAnimTest.beginTest();
|
||||
}
|
||||
|
||||
window.onload = main;
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
|
@ -1,52 +0,0 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=666446
|
||||
-->
|
||||
<head>
|
||||
<title>Test for Bug 666446 - Animated Raster Images inside of SVG Frames</title>
|
||||
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/WindowSnapshot.js"></script>
|
||||
<script type="application/javascript" src="imgutils.js"></script>
|
||||
<script type="application/javascript" src="animationPolling.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
|
||||
<!-- Make sure embed element is snapped to an exact pixel. -->
|
||||
<div class="bug-header" style="height: 100px;">
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=666446">
|
||||
Mozilla Bug 666446: lots of animated gifs swamp us with paint events
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<p id="display"></p>
|
||||
<div id="content">
|
||||
<div id="referenceDiv" style="height: 40px; width: 40px;
|
||||
display: none; background: #2aff00"></div>
|
||||
<!--
|
||||
We use <embed> here instead of <img> because the <img> tag utilizes
|
||||
the VectorImage class for SVG, whereas in this test, we are testing
|
||||
RasterImage.
|
||||
-->
|
||||
<embed id="embeddedSVG" src="animation.svg" type="image/svg+xml" style="display: none;"/>
|
||||
</div>
|
||||
<div id="debug" style="display:none"></div>
|
||||
<pre id="test">
|
||||
<script type="text/javascript;version=1.8">
|
||||
|
||||
/** Test for Bug 666446 nsSVGImageFrame/RasterImage**/
|
||||
|
||||
const FAILURE_TIMEOUT = 120000; // Fail early after 120 seconds (2 minutes)
|
||||
|
||||
function main() {
|
||||
var animTest = new AnimationTest(20, FAILURE_TIMEOUT, 'referenceDiv',
|
||||
'embeddedSVG', 'debug', '', 'src');
|
||||
animTest.beginTest();
|
||||
}
|
||||
|
||||
window.onload = main;
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
|
@ -1,42 +0,0 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=666446
|
||||
-->
|
||||
<head>
|
||||
<title>Test for Bug 666446 - Animated Images within SVG Filters</title>
|
||||
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/WindowSnapshot.js"></script>
|
||||
<script type="application/javascript" src="imgutils.js"></script>
|
||||
<script type="application/javascript" src="animationPolling.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=666446">
|
||||
Mozilla Bug 666446: lots of animated gifs swamp us with paint events
|
||||
</a>
|
||||
<p id="display"></p>
|
||||
<div id="content">
|
||||
<embed id="referenceImage" src="filter-final.svg" type="image/svg+xml" style="display: none;"/>
|
||||
<embed id="embeddedSVGFilt" src="filter.svg" type="image/svg+xml" style="display: none;"/>
|
||||
</div>
|
||||
<div id="debug" style="display:none"></div>
|
||||
<pre id="test">
|
||||
<script type="text/javascript;version=1.8">
|
||||
|
||||
/** Test for Bug 666446 nsSVGFEImageElement/RasterImage**/
|
||||
|
||||
const FAILURE_TIMEOUT = 120000; // Fail early after 120 seconds (2 minutes)
|
||||
|
||||
function main() {
|
||||
var animTest = new AnimationTest(20, FAILURE_TIMEOUT, 'referenceImage',
|
||||
'embeddedSVGFilt', 'debug', '', 'src');
|
||||
animTest.beginTest();
|
||||
}
|
||||
|
||||
window.onload = main;
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
|
@ -1,48 +0,0 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=666446
|
||||
-->
|
||||
<head>
|
||||
<title>Test for Bug 666446 - Test for Animated Gif within IFRAME</title>
|
||||
<script type="application/javascript" src="chrome://mochikit/content/MochiKit/packed.js"></script>
|
||||
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/WindowSnapshot.js"></script>
|
||||
<script type="application/javascript" src="imgutils.js"></script>
|
||||
<script type="application/javascript" src="animationPolling.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=666446">
|
||||
Mozilla Bug 666446: lots of animated gifs swamp us with paint events</a>
|
||||
<p id="display"></p>
|
||||
|
||||
<div id="content">
|
||||
<div id="referenceDiv" style="display:none;">
|
||||
<iframe id="referenceIFrame" src="ref-iframe.html" width="50%" height="100">
|
||||
Browser does not support iframes.
|
||||
</iframe>
|
||||
</div>
|
||||
<div id="animatedImage">
|
||||
<iframe id="imageIFrame" src="iframe.html" width="50%" height="100" style="display: none;">
|
||||
Browser does not support iframes.
|
||||
</iframe>
|
||||
</div>
|
||||
<div id="debug" style="display: none"></div>
|
||||
</div>
|
||||
<pre id="test">
|
||||
<script type="text/javascript;version=1.8">
|
||||
const FAILURE_TIMEOUT = 120000; // Fail early after 120 seconds (2 minutes)
|
||||
|
||||
function main()
|
||||
{
|
||||
var animTest = new AnimationTest(20, FAILURE_TIMEOUT, 'referenceDiv',
|
||||
'imageIFrame', 'debug');
|
||||
animTest.beginTest();
|
||||
}
|
||||
|
||||
window.onload = main;
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
|
@ -1,67 +0,0 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html
|
||||
xmlns="http://www.w3.org/1999/xhtml"
|
||||
xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||
xml:lang="en" lang="en">
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=666446
|
||||
-->
|
||||
<head>
|
||||
<title>Test for Bug 666446 - Animated Images within SVG Filters</title>
|
||||
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/WindowSnapshot.js"></script>
|
||||
<script type="application/javascript" src="imgutils.js"></script>
|
||||
<script type="application/javascript" src="animationPolling.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=666446">
|
||||
Mozilla Bug 666446: lots of animated gifs swamp us with paint events
|
||||
</a>
|
||||
<p id="display"></p>
|
||||
<div id="content">
|
||||
<xul:caption label="Bug 666446 - XULTree Test" />
|
||||
<xul:separator />
|
||||
<br />
|
||||
<xul:window id="main" title="Bug 666446: XUL Tree Testing" width="100" height="100">
|
||||
<xul:tree flex="1">
|
||||
<xul:treecols>
|
||||
<xul:treecol id="icon" label="Icon" flex="1" />
|
||||
</xul:treecols>
|
||||
|
||||
<xul:treechildren>
|
||||
<xul:treeitem id="referenceItem" hidden="true">
|
||||
<xul:treerow>
|
||||
<xul:treecell src="animated-gif-finalframe.gif" width="40" height="40" />
|
||||
</xul:treerow>
|
||||
</xul:treeitem>
|
||||
<xul:treeitem id="imageItem" hidden="true">
|
||||
<xul:treerow>
|
||||
<xul:treecell src="animated-gif.gif" width="40" height="40" />
|
||||
</xul:treerow>
|
||||
</xul:treeitem>
|
||||
</xul:treechildren>
|
||||
</xul:tree>
|
||||
</xul:window>
|
||||
</div>
|
||||
<div id="debug" style="display:none"></div>
|
||||
<pre id="test">
|
||||
<script type="text/javascript;version=1.8">
|
||||
|
||||
/** Test for Bug 666446 nsSVGFEImageElement/RasterImage**/
|
||||
|
||||
const FAILURE_TIMEOUT = 5000; // Fail early after 120 seconds (2 minutes)
|
||||
|
||||
function main() {
|
||||
var animTest = new AnimationTest(20, FAILURE_TIMEOUT, 'referenceItem',
|
||||
'imageItem', 'debug', '',
|
||||
'animated-gif-finalframe.gif', true);
|
||||
animTest.beginTest();
|
||||
}
|
||||
|
||||
window.onload = main;
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
|
@ -57,7 +57,6 @@
|
|||
|
||||
#include "nsStyleContext.h"
|
||||
#include "nsGkAtoms.h"
|
||||
#include "nsLayoutUtils.h"
|
||||
|
||||
// Paint forcing
|
||||
#include "prenv.h"
|
||||
|
@ -68,14 +67,17 @@ nsImageLoader::nsImageLoader(nsIFrame *aFrame, PRUint32 aActions,
|
|||
nsImageLoader *aNextLoader)
|
||||
: mFrame(aFrame),
|
||||
mActions(aActions),
|
||||
mNextLoader(aNextLoader),
|
||||
mRequestRegistered(false)
|
||||
mNextLoader(aNextLoader)
|
||||
{
|
||||
}
|
||||
|
||||
nsImageLoader::~nsImageLoader()
|
||||
{
|
||||
Destroy();
|
||||
mFrame = nsnull;
|
||||
|
||||
if (mRequest) {
|
||||
mRequest->CancelAndForgetObserver(NS_ERROR_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
/* static */ already_AddRefed<nsImageLoader>
|
||||
|
@ -103,15 +105,12 @@ nsImageLoader::Destroy()
|
|||
todestroy->Destroy();
|
||||
}
|
||||
|
||||
if (mRequest && mFrame) {
|
||||
nsPresContext* presContext = mFrame->PresContext();
|
||||
mFrame = nsnull;
|
||||
|
||||
nsLayoutUtils::DeregisterImageRequest(presContext, mRequest,
|
||||
&mRequestRegistered);
|
||||
if (mRequest) {
|
||||
mRequest->CancelAndForgetObserver(NS_ERROR_FAILURE);
|
||||
}
|
||||
|
||||
mFrame = nsnull;
|
||||
mRequest = nsnull;
|
||||
}
|
||||
|
||||
|
@ -128,31 +127,17 @@ nsImageLoader::Load(imgIRequest *aImage)
|
|||
if (!aImage)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
// Deregister mRequest from the refresh driver, since it is no longer
|
||||
// going to be managed by this nsImageLoader.
|
||||
nsPresContext* presContext = mFrame->PresContext();
|
||||
|
||||
nsLayoutUtils::DeregisterImageRequest(presContext, mRequest,
|
||||
&mRequestRegistered);
|
||||
|
||||
// Make sure to clone into a temporary, then set mRequest, since
|
||||
// cloning may notify and we don't want to trigger paints from this
|
||||
// code.
|
||||
nsCOMPtr<imgIRequest> newRequest;
|
||||
nsresult rv = aImage->Clone(this, getter_AddRefs(newRequest));
|
||||
mRequest.swap(newRequest);
|
||||
|
||||
// Re-register mRequest with the refresh driver, but immediately deregister
|
||||
// if it isn't animated.
|
||||
nsLayoutUtils::RegisterImageRequest(presContext, mRequest,
|
||||
&mRequestRegistered);
|
||||
|
||||
nsLayoutUtils::DeregisterImageRequestIfNotAnimated(presContext, mRequest,
|
||||
&mRequestRegistered);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
||||
|
||||
NS_IMETHODIMP nsImageLoader::OnStartContainer(imgIRequest *aRequest,
|
||||
imgIContainer *aImage)
|
||||
{
|
||||
|
@ -284,35 +269,3 @@ nsImageLoader::DoRedraw(const nsRect* aDamageRect)
|
|||
mFrame->Invalidate(bounds);
|
||||
}
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsImageLoader::OnStartDecode(imgIRequest *aRequest)
|
||||
{
|
||||
// Register our image request with the refresh driver.
|
||||
nsPresContext* presContext = mFrame->PresContext();
|
||||
if (!presContext) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (mRequest == aRequest) {
|
||||
nsLayoutUtils::RegisterImageRequest(presContext, mRequest,
|
||||
&mRequestRegistered);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsImageLoader::OnStopDecode(imgIRequest *aRequest, nsresult status,
|
||||
const PRUnichar *statusArg)
|
||||
{
|
||||
if (mRequest == aRequest) {
|
||||
// Deregister the imgIRequest with the refresh driver if the
|
||||
// image is not animated.
|
||||
nsLayoutUtils::DeregisterImageRequestIfNotAnimated(mFrame->PresContext(),
|
||||
mRequest,
|
||||
&mRequestRegistered);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
|
|
@ -82,10 +82,6 @@ public:
|
|||
|
||||
// imgIDecoderObserver (override nsStubImageDecoderObserver)
|
||||
NS_IMETHOD OnStartContainer(imgIRequest *aRequest, imgIContainer *aImage);
|
||||
NS_IMETHOD OnStartDecode(imgIRequest *aRequest);
|
||||
NS_IMETHOD OnStopDecode(imgIRequest *aRequest,
|
||||
nsresult status,
|
||||
const PRUnichar *statusArg);
|
||||
NS_IMETHOD OnStopFrame(imgIRequest *aRequest, PRUint32 aFrame);
|
||||
NS_IMETHOD OnStopRequest(imgIRequest *aRequest, bool aLastPart);
|
||||
// Do not override OnDataAvailable since background images are not
|
||||
|
@ -113,8 +109,4 @@ private:
|
|||
nsCOMPtr<imgIRequest> mRequest;
|
||||
PRUint32 mActions;
|
||||
nsRefPtr<nsImageLoader> mNextLoader;
|
||||
|
||||
// This is a boolean flag indicating whether or not the current image request
|
||||
// has been registered with the refresh driver.
|
||||
bool mRequestRegistered;
|
||||
};
|
||||
|
|
|
@ -4327,102 +4327,6 @@ nsLayoutUtils::Shutdown()
|
|||
}
|
||||
}
|
||||
|
||||
/* static */
|
||||
void
|
||||
nsLayoutUtils::RegisterImageRequest(nsPresContext* aPresContext,
|
||||
imgIRequest* aRequest,
|
||||
bool* aRequestRegistered)
|
||||
{
|
||||
if (!aPresContext) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (aRequestRegistered && *aRequestRegistered) {
|
||||
// Our request is already registered with the refresh driver, so
|
||||
// no need to register it again.
|
||||
return;
|
||||
}
|
||||
|
||||
if (aRequest) {
|
||||
nsCOMPtr<imgIContainer> image;
|
||||
aRequest->GetImage(getter_AddRefs(image));
|
||||
if (image) {
|
||||
if (!aPresContext->RefreshDriver()->AddImageRequest(aRequest)) {
|
||||
NS_WARNING("Unable to add image request");
|
||||
return;
|
||||
}
|
||||
|
||||
if (aRequestRegistered) {
|
||||
*aRequestRegistered = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* static */
|
||||
void
|
||||
nsLayoutUtils::DeregisterImageRequest(nsPresContext* aPresContext,
|
||||
imgIRequest* aRequest,
|
||||
bool* aRequestRegistered)
|
||||
{
|
||||
if (!aPresContext) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Deregister our imgIRequest with the refresh driver to
|
||||
// complete tear-down, but only if it has been registered
|
||||
if (aRequestRegistered && !*aRequestRegistered) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (aRequest) {
|
||||
nsCOMPtr<imgIContainer> image;
|
||||
aRequest->GetImage(getter_AddRefs(image));
|
||||
if (image) {
|
||||
aPresContext->RefreshDriver()->RemoveImageRequest(aRequest);
|
||||
|
||||
if (aRequestRegistered) {
|
||||
*aRequestRegistered = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* static */
|
||||
void
|
||||
nsLayoutUtils::DeregisterImageRequestIfNotAnimated(nsPresContext* aPresContext,
|
||||
imgIRequest* aRequest,
|
||||
bool* aRequestRegistered)
|
||||
{
|
||||
if (!aPresContext) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (aRequestRegistered && !*aRequestRegistered) {
|
||||
// Image request isn't registered with the refresh driver - no need
|
||||
// to try and deregister it.
|
||||
return;
|
||||
}
|
||||
|
||||
// Deregister the imgIRequest with the refresh driver if the
|
||||
// image is not animated
|
||||
nsCOMPtr<imgIContainer> imageContainer;
|
||||
if (aRequest) {
|
||||
aRequest->GetImage(getter_AddRefs(imageContainer));
|
||||
bool animated;
|
||||
|
||||
if (!imageContainer) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsresult rv = imageContainer->GetAnimated(&animated);
|
||||
if (NS_SUCCEEDED(rv) && !animated) {
|
||||
nsLayoutUtils::DeregisterImageRequest(aPresContext, aRequest,
|
||||
aRequestRegistered);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nsSetAttrRunnable::nsSetAttrRunnable(nsIContent* aContent, nsIAtom* aAttrName,
|
||||
const nsAString& aValue)
|
||||
: mContent(aContent),
|
||||
|
|
|
@ -1454,63 +1454,6 @@ public:
|
|||
|
||||
static void Shutdown();
|
||||
|
||||
/**
|
||||
* Register an imgIRequest object with a refresh driver.
|
||||
*
|
||||
* @param aPresContext The nsPresContext whose refresh driver we want to
|
||||
* register with.
|
||||
* @param aRequest A pointer to the imgIRequest object which the client wants
|
||||
* to register with the refresh driver.
|
||||
* @param aRequestRegistered A pointer to a boolean value which indicates
|
||||
* whether the given image request is registered. If
|
||||
* *aRequestRegistered is true, then this request will not be
|
||||
* registered again. If the request is registered by this function,
|
||||
* then *aRequestRegistered will be set to true upon the completion of
|
||||
* this function.
|
||||
*
|
||||
*/
|
||||
static void RegisterImageRequest(nsPresContext* aPresContext,
|
||||
imgIRequest* aRequest,
|
||||
bool* aRequestRegistered);
|
||||
/**
|
||||
* Deregister an imgIRequest object from a refresh driver.
|
||||
*
|
||||
* @param aPresContext The nsPresContext whose refresh driver we want to
|
||||
* deregister from.
|
||||
* @param aRequest A pointer to the imgIRequest object with which the client
|
||||
* previously registered and now wants to deregister from the refresh
|
||||
* driver.
|
||||
* @param aRequestRegistered A pointer to a boolean value which indicates
|
||||
* whether the given image request is registered. If
|
||||
* *aRequestRegistered is false, then this request will not be
|
||||
* deregistered. If the request is deregistered by this function,
|
||||
* then *aRequestRegistered will be set to false upon the completion of
|
||||
* this function.
|
||||
*/
|
||||
static void DeregisterImageRequest(nsPresContext* aPresContext,
|
||||
imgIRequest* aRequest,
|
||||
bool* aRequestRegistered);
|
||||
/**
|
||||
* Deregister an imgIRequest object from a refresh driver, if the
|
||||
* imgIRequest object represents a static (i.e. not animated) image.
|
||||
*
|
||||
* @param aPresContext The nsPresContext whose refresh driver we want to
|
||||
* deregister from.
|
||||
* @param aRequest A pointer to the imgIRequest object with which the client
|
||||
* previously registered and now wants to deregister from the refresh
|
||||
* driver.
|
||||
* @param aRequestRegistered A pointer to a boolean value which indicates
|
||||
* whether the given image request is registered. If
|
||||
* *aRequestRegistered is false, then this request will not be
|
||||
* deregistered. If the request is deregistered by this function,
|
||||
* then *aRequestRegistered will be set to false upon the completion of
|
||||
* this function.
|
||||
*/
|
||||
|
||||
static void DeregisterImageRequestIfNotAnimated(nsPresContext* aPresContext,
|
||||
imgIRequest* aRequest,
|
||||
bool* aRequestRegistered);
|
||||
|
||||
#ifdef DEBUG
|
||||
/**
|
||||
* Assert that there are no duplicate continuations of the same frame
|
||||
|
|
|
@ -114,7 +114,6 @@ nsRefreshDriver::nsRefreshDriver(nsPresContext *aPresContext)
|
|||
mTimerIsPrecise(false),
|
||||
mLastTimerInterval(0)
|
||||
{
|
||||
mRequests.Init();
|
||||
}
|
||||
|
||||
nsRefreshDriver::~nsRefreshDriver()
|
||||
|
@ -186,29 +185,6 @@ nsRefreshDriver::RemoveRefreshObserver(nsARefreshObserver *aObserver,
|
|||
return array.RemoveElement(aObserver);
|
||||
}
|
||||
|
||||
bool
|
||||
nsRefreshDriver::AddImageRequest(imgIRequest* aRequest)
|
||||
{
|
||||
if (!mRequests.PutEntry(aRequest)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
EnsureTimerStarted(false);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
nsRefreshDriver::RemoveImageRequest(imgIRequest* aRequest)
|
||||
{
|
||||
mRequests.RemoveEntry(aRequest);
|
||||
}
|
||||
|
||||
void nsRefreshDriver::ClearAllImageRequests()
|
||||
{
|
||||
mRequests.Clear();
|
||||
}
|
||||
|
||||
void
|
||||
nsRefreshDriver::EnsureTimerStarted(bool aAdjustingTimer)
|
||||
{
|
||||
|
@ -262,7 +238,6 @@ nsRefreshDriver::ObserverCount() const
|
|||
for (PRUint32 i = 0; i < ArrayLength(mObservers); ++i) {
|
||||
sum += mObservers[i].Length();
|
||||
}
|
||||
|
||||
// Even while throttled, we need to process layout and style changes. Style
|
||||
// changes can trigger transitions which fire events when they complete, and
|
||||
// layout changes can affect media queries on child documents, triggering
|
||||
|
@ -274,12 +249,6 @@ nsRefreshDriver::ObserverCount() const
|
|||
return sum;
|
||||
}
|
||||
|
||||
PRUint32
|
||||
nsRefreshDriver::ImageRequestCount() const
|
||||
{
|
||||
return mRequests.Count();
|
||||
}
|
||||
|
||||
void
|
||||
nsRefreshDriver::UpdateMostRecentRefresh()
|
||||
{
|
||||
|
@ -334,7 +303,7 @@ nsRefreshDriver::Notify(nsITimer *aTimer)
|
|||
UpdateMostRecentRefresh();
|
||||
|
||||
nsCOMPtr<nsIPresShell> presShell = mPresContext->GetPresShell();
|
||||
if (!presShell || (ObserverCount() == 0 && ImageRequestCount() == 0)) {
|
||||
if (!presShell || ObserverCount() == 0) {
|
||||
// Things are being destroyed, or we no longer have any observers.
|
||||
// We don't want to stop the timer when observers are initially
|
||||
// removed, because sometimes observers can be added and removed
|
||||
|
@ -363,7 +332,6 @@ nsRefreshDriver::Notify(nsITimer *aTimer)
|
|||
return NS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
if (i == 0) {
|
||||
// Don't just loop while we have things in mBeforePaintTargets,
|
||||
// the whole point is that event handlers should readd the
|
||||
|
@ -436,17 +404,6 @@ nsRefreshDriver::Notify(nsITimer *aTimer)
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Perform notification to imgIRequests subscribed to listen
|
||||
* for refresh events.
|
||||
*/
|
||||
|
||||
ImageRequestParameters parms = {mMostRecentRefresh};
|
||||
if (mRequests.Count()) {
|
||||
mRequests.EnumerateEntries(nsRefreshDriver::ImageRequestEnumerator, &parms);
|
||||
EnsureTimerStarted(false);
|
||||
}
|
||||
|
||||
if (mThrottled ||
|
||||
(mTimerIsPrecise !=
|
||||
(GetRefreshTimerType() == nsITimer::TYPE_REPEATING_PRECISE_CAN_SKIP))) {
|
||||
|
@ -467,24 +424,6 @@ nsRefreshDriver::Notify(nsITimer *aTimer)
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
PLDHashOperator
|
||||
nsRefreshDriver::ImageRequestEnumerator(nsISupportsHashKey* aEntry,
|
||||
void* aUserArg)
|
||||
{
|
||||
ImageRequestParameters* parms =
|
||||
static_cast<ImageRequestParameters*> (aUserArg);
|
||||
mozilla::TimeStamp mostRecentRefresh = parms->ts;
|
||||
imgIRequest* req = static_cast<imgIRequest*>(aEntry->GetKey());
|
||||
NS_ABORT_IF_FALSE(req, "Unable to retrieve the image request");
|
||||
nsCOMPtr<imgIContainer> image;
|
||||
req->GetImage(getter_AddRefs(image));
|
||||
if (image) {
|
||||
image->RequestRefresh(mostRecentRefresh);
|
||||
}
|
||||
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
void
|
||||
nsRefreshDriver::Freeze()
|
||||
{
|
||||
|
@ -498,7 +437,7 @@ nsRefreshDriver::Thaw()
|
|||
{
|
||||
NS_ASSERTION(mFrozen, "Thaw called on an unfrozen refresh driver");
|
||||
mFrozen = false;
|
||||
if (ObserverCount() || ImageRequestCount()) {
|
||||
if (ObserverCount()) {
|
||||
// FIXME: This isn't quite right, since our EnsureTimerStarted call
|
||||
// updates our mMostRecentRefresh, but the DoRefresh call won't run
|
||||
// and notify our observers until we get back to the event loop.
|
||||
|
|
|
@ -50,13 +50,10 @@
|
|||
#include "nsTObserverArray.h"
|
||||
#include "nsTArray.h"
|
||||
#include "nsAutoPtr.h"
|
||||
#include "nsTHashtable.h"
|
||||
#include "nsHashKeys.h"
|
||||
|
||||
class nsPresContext;
|
||||
class nsIPresShell;
|
||||
class nsIDocument;
|
||||
class imgIRequest;
|
||||
|
||||
/**
|
||||
* An abstract base class to be implemented by callers wanting to be
|
||||
|
@ -131,24 +128,6 @@ public:
|
|||
bool RemoveRefreshObserver(nsARefreshObserver *aObserver,
|
||||
mozFlushType aFlushType);
|
||||
|
||||
/**
|
||||
* Add/Remove imgIRequest versions of observers.
|
||||
*
|
||||
* These are used for hooking into the refresh driver for
|
||||
* controlling animated images.
|
||||
*
|
||||
* @note The refresh driver owns a reference to these listeners.
|
||||
*
|
||||
* @note Technically, imgIRequest objects are not nsARefreshObservers, but
|
||||
* for controlling animated image repaint events, we subscribe the
|
||||
* imgIRequests to the nsRefreshDriver for notification of paint events.
|
||||
*
|
||||
* @returns whether the operation succeeded, or void in the case of removal.
|
||||
*/
|
||||
bool AddImageRequest(imgIRequest* aRequest);
|
||||
void RemoveImageRequest(imgIRequest* aRequest);
|
||||
void ClearAllImageRequests();
|
||||
|
||||
/**
|
||||
* Add / remove presshells that we should flush style and layout on
|
||||
*/
|
||||
|
@ -239,15 +218,10 @@ public:
|
|||
|
||||
private:
|
||||
typedef nsTObserverArray<nsARefreshObserver*> ObserverArray;
|
||||
typedef nsTHashtable<nsISupportsHashKey> RequestTable;
|
||||
|
||||
void EnsureTimerStarted(bool aAdjustingTimer);
|
||||
void StopTimer();
|
||||
|
||||
PRUint32 ObserverCount() const;
|
||||
PRUint32 ImageRequestCount() const;
|
||||
static PLDHashOperator ImageRequestEnumerator(nsISupportsHashKey* aEntry,
|
||||
void* aUserArg);
|
||||
void UpdateMostRecentRefresh();
|
||||
ObserverArray& ArrayFor(mozFlushType aFlushType);
|
||||
// Trigger a refresh immediately, if haven't been disconnected or frozen.
|
||||
|
@ -278,8 +252,6 @@ private:
|
|||
|
||||
// separate arrays for each flush type we support
|
||||
ObserverArray mObservers[3];
|
||||
RequestTable mRequests;
|
||||
|
||||
nsAutoTArray<nsIPresShell*, 16> mStyleFlushObservers;
|
||||
nsAutoTArray<nsIPresShell*, 16> mLayoutFlushObservers;
|
||||
// nsTArray on purpose, because we want to be able to swap.
|
||||
|
@ -290,11 +262,6 @@ private:
|
|||
// This is the last interval we used for our timer. May be 0 if we
|
||||
// haven't computed a timer interval yet.
|
||||
mutable PRInt32 mLastTimerInterval;
|
||||
|
||||
// Helper struct for processing image requests
|
||||
struct ImageRequestParameters {
|
||||
mozilla::TimeStamp ts;
|
||||
};
|
||||
};
|
||||
|
||||
#endif /* !defined(nsRefreshDriver_h_) */
|
||||
|
|
|
@ -78,7 +78,6 @@ public:
|
|||
NS_IMETHOD OnStartContainer(imgIRequest *aRequest, imgIContainer *aImage);
|
||||
NS_IMETHOD OnDataAvailable(imgIRequest *aRequest, bool aCurrentFrame,
|
||||
const nsIntRect *aRect);
|
||||
NS_IMETHOD OnStartDecode(imgIRequest *aRequest);
|
||||
NS_IMETHOD OnStopDecode(imgIRequest *aRequest, nsresult status,
|
||||
const PRUnichar *statusArg);
|
||||
// imgIContainerObserver (override nsStubImageDecoderObserver)
|
||||
|
@ -102,10 +101,6 @@ nsBulletFrame::DestroyFrom(nsIFrame* aDestructRoot)
|
|||
{
|
||||
// Stop image loading first
|
||||
if (mImageRequest) {
|
||||
// Deregister our image request from the refresh driver
|
||||
nsLayoutUtils::DeregisterImageRequest(PresContext(),
|
||||
mImageRequest,
|
||||
&mRequestRegistered);
|
||||
mImageRequest->CancelAndForgetObserver(NS_ERROR_FAILURE);
|
||||
mImageRequest = nsnull;
|
||||
}
|
||||
|
@ -175,8 +170,6 @@ nsBulletFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext)
|
|||
if (same) {
|
||||
needNewRequest = false;
|
||||
} else {
|
||||
nsLayoutUtils::DeregisterImageRequest(PresContext(), mImageRequest,
|
||||
&mRequestRegistered);
|
||||
mImageRequest->Cancel(NS_ERROR_FAILURE);
|
||||
mImageRequest = nsnull;
|
||||
}
|
||||
|
@ -185,15 +178,10 @@ nsBulletFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext)
|
|||
|
||||
if (needNewRequest) {
|
||||
newRequest->Clone(mListener, getter_AddRefs(mImageRequest));
|
||||
nsLayoutUtils::RegisterImageRequest(PresContext(), mImageRequest,
|
||||
&mRequestRegistered);
|
||||
}
|
||||
} else {
|
||||
// No image request on the new style context
|
||||
if (mImageRequest) {
|
||||
nsLayoutUtils::DeregisterImageRequest(PresContext(), mImageRequest,
|
||||
&mRequestRegistered);
|
||||
|
||||
mImageRequest->Cancel(NS_ERROR_FAILURE);
|
||||
mImageRequest = nsnull;
|
||||
}
|
||||
|
@ -1533,17 +1521,6 @@ NS_IMETHODIMP nsBulletFrame::OnDataAvailable(imgIRequest *aRequest,
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsBulletFrame::OnStartDecode(imgIRequest* aRequest)
|
||||
{
|
||||
// Register the image request with the refresh driver.
|
||||
if (aRequest == mImageRequest) {
|
||||
nsLayoutUtils::RegisterImageRequest(PresContext(), mImageRequest,
|
||||
&mRequestRegistered);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsBulletFrame::OnStopDecode(imgIRequest *aRequest,
|
||||
nsresult aStatus,
|
||||
const PRUnichar *aStatusArg)
|
||||
|
@ -1560,14 +1537,6 @@ NS_IMETHODIMP nsBulletFrame::OnStopDecode(imgIRequest *aRequest,
|
|||
}
|
||||
#endif
|
||||
|
||||
// Deregister the imgIRequest with the refresh driver if the
|
||||
// image is not animated.
|
||||
if (aRequest == mImageRequest) {
|
||||
nsLayoutUtils::DeregisterImageRequestIfNotAnimated(PresContext(),
|
||||
mImageRequest,
|
||||
&mRequestRegistered);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -1665,25 +1634,17 @@ NS_IMETHODIMP nsBulletListener::OnDataAvailable(imgIRequest *aRequest,
|
|||
const nsIntRect *aRect)
|
||||
{
|
||||
if (!mFrame)
|
||||
return NS_OK;
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
return mFrame->OnDataAvailable(aRequest, aCurrentFrame, aRect);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsBulletListener::OnStartDecode(imgIRequest *aRequest)
|
||||
{
|
||||
if (!mFrame)
|
||||
return NS_OK;
|
||||
|
||||
return mFrame->OnStartDecode(aRequest);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsBulletListener::OnStopDecode(imgIRequest *aRequest,
|
||||
nsresult status,
|
||||
const PRUnichar *statusArg)
|
||||
{
|
||||
if (!mFrame)
|
||||
return NS_OK;
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
return mFrame->OnStopDecode(aRequest, status, statusArg);
|
||||
}
|
||||
|
@ -1692,7 +1653,7 @@ NS_IMETHODIMP nsBulletListener::FrameChanged(imgIContainer *aContainer,
|
|||
const nsIntRect *aDirtyRect)
|
||||
{
|
||||
if (!mFrame)
|
||||
return NS_OK;
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
return mFrame->FrameChanged(aContainer, aDirtyRect);
|
||||
}
|
||||
|
|
|
@ -84,7 +84,6 @@ public:
|
|||
NS_IMETHOD OnDataAvailable(imgIRequest *aRequest,
|
||||
bool aCurrentFrame,
|
||||
const nsIntRect *aRect);
|
||||
NS_IMETHOD OnStartDecode(imgIRequest *aRequest);
|
||||
NS_IMETHOD OnStopDecode(imgIRequest *aRequest,
|
||||
nsresult aStatus,
|
||||
const PRUnichar *aStatusArg);
|
||||
|
@ -122,12 +121,6 @@ protected:
|
|||
nsSize mComputedSize;
|
||||
PRInt32 mOrdinal;
|
||||
bool mTextIsRTL;
|
||||
|
||||
private:
|
||||
|
||||
// This is a boolean flag indicating whether or not the current image request
|
||||
// has been registered with the refresh driver.
|
||||
bool mRequestRegistered;
|
||||
};
|
||||
|
||||
#endif /* nsBulletFrame_h___ */
|
||||
|
|
|
@ -579,10 +579,6 @@ protected:
|
|||
/**
|
||||
* Implements Destroy(). Do not call this directly except from within a
|
||||
* DestroyFrom() implementation.
|
||||
*
|
||||
* @note This will always be called, so it is not necessary to override
|
||||
* Destroy() in subclasses of nsFrame, just DestroyFrom().
|
||||
*
|
||||
* @param aDestructRoot is the root of the subtree being destroyed
|
||||
*/
|
||||
virtual void DestroyFrom(nsIFrame* aDestructRoot) = 0;
|
||||
|
|
|
@ -231,10 +231,6 @@ nsImageFrame::DestroyFrom(nsIFrame* aDestructRoot)
|
|||
nsCxPusher pusher;
|
||||
pusher.PushNull();
|
||||
|
||||
// Notify our image loading content that we are going away so it can
|
||||
// deregister with our refresh driver.
|
||||
imageLoader->FrameDestroyed(this);
|
||||
|
||||
imageLoader->RemoveObserver(mListener);
|
||||
}
|
||||
|
||||
|
@ -280,10 +276,6 @@ nsImageFrame::Init(nsIContent* aContent,
|
|||
if (!gIconLoad)
|
||||
LoadIcons(aPresContext);
|
||||
|
||||
// We have a PresContext now, so we need to notify the image content node
|
||||
// that it can register images.
|
||||
imageLoader->FrameCreated(this);
|
||||
|
||||
// Give image loads associated with an image frame a small priority boost!
|
||||
nsCOMPtr<imgIRequest> currentRequest;
|
||||
imageLoader->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
|
||||
|
|
|
@ -103,7 +103,6 @@ public:
|
|||
NS_IMETHOD Init(nsIContent* aContent,
|
||||
nsIFrame* aParent,
|
||||
nsIFrame* aPrevInFlow);
|
||||
virtual void DestroyFrom(nsIFrame* aDestructRoot);
|
||||
|
||||
/**
|
||||
* Get the "type" of the frame
|
||||
|
@ -180,10 +179,6 @@ nsSVGImageFrame::Init(nsIContent* aContent,
|
|||
nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(mContent);
|
||||
NS_ENSURE_TRUE(imageLoader, NS_ERROR_UNEXPECTED);
|
||||
|
||||
// We should have a PresContext now, so let's notify our image loader that
|
||||
// we need to register any image animations with the refresh driver.
|
||||
imageLoader->FrameCreated(this);
|
||||
|
||||
// Push a null JSContext on the stack so that code that runs within
|
||||
// the below code doesn't think it's being called by JS. See bug
|
||||
// 604262.
|
||||
|
@ -195,19 +190,6 @@ nsSVGImageFrame::Init(nsIContent* aContent,
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
/* virtual */ void
|
||||
nsSVGImageFrame::DestroyFrom(nsIFrame* aDestructRoot)
|
||||
{
|
||||
nsCOMPtr<nsIImageLoadingContent> imageLoader =
|
||||
do_QueryInterface(nsFrame::mContent);
|
||||
|
||||
if (imageLoader) {
|
||||
imageLoader->FrameDestroyed(this);
|
||||
}
|
||||
|
||||
nsFrame::DestroyFrom(aDestructRoot);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// nsIFrame methods:
|
||||
|
||||
|
|
|
@ -36,7 +36,6 @@
|
|||
|
||||
#include "nsFrame.h"
|
||||
#include "nsSVGEffects.h"
|
||||
#include "nsImageLoadingContent.h"
|
||||
|
||||
class nsSVGLeafFrame : public nsFrame
|
||||
{
|
||||
|
@ -48,12 +47,6 @@ protected:
|
|||
public:
|
||||
NS_DECL_FRAMEARENA_HELPERS
|
||||
|
||||
NS_IMETHOD Init(nsIContent* aContent,
|
||||
nsIFrame* aParent,
|
||||
nsIFrame* asPrevInFlow);
|
||||
|
||||
virtual void DestroyFrom(nsIFrame* aDestructRoot);
|
||||
|
||||
virtual bool IsFrameOfType(PRUint32 aFlags) const
|
||||
{
|
||||
return nsFrame::IsFrameOfType(aFlags & ~(nsIFrame::eSVG));
|
||||
|
@ -77,33 +70,6 @@ NS_NewSVGLeafFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
|
|||
|
||||
NS_IMPL_FRAMEARENA_HELPERS(nsSVGLeafFrame)
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsSVGLeafFrame::Init(nsIContent* aContent, nsIFrame* aParent, nsIFrame* asPrevInFlow)
|
||||
{
|
||||
nsFrame::Init(aContent, aParent, asPrevInFlow);
|
||||
nsCOMPtr<nsIImageLoadingContent> imageLoader =
|
||||
do_QueryInterface(nsFrame::mContent);
|
||||
|
||||
if (imageLoader) {
|
||||
imageLoader->FrameCreated(this);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/* virtual */ void
|
||||
nsSVGLeafFrame::DestroyFrom(nsIFrame* aDestructRoot)
|
||||
{
|
||||
nsCOMPtr<nsIImageLoadingContent> imageLoader =
|
||||
do_QueryInterface(nsFrame::mContent);
|
||||
|
||||
if (imageLoader) {
|
||||
imageLoader->FrameDestroyed(this);
|
||||
}
|
||||
|
||||
nsFrame::DestroyFrom(aDestructRoot);
|
||||
}
|
||||
|
||||
/* virtual */ void
|
||||
nsSVGLeafFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext)
|
||||
{
|
||||
|
|
|
@ -72,7 +72,6 @@
|
|||
#include "nsIDOMDocument.h"
|
||||
#include "nsTransform2D.h"
|
||||
#include "nsITheme.h"
|
||||
#include "nsIImageLoadingContent.h"
|
||||
|
||||
#include "nsIServiceManager.h"
|
||||
#include "nsIURI.h"
|
||||
|
@ -174,7 +173,6 @@ nsImageBoxFrame::AttributeChanged(PRInt32 aNameSpaceID,
|
|||
nsImageBoxFrame::nsImageBoxFrame(nsIPresShell* aShell, nsStyleContext* aContext):
|
||||
nsLeafBoxFrame(aShell, aContext),
|
||||
mIntrinsicSize(0,0),
|
||||
mRequestRegistered(false),
|
||||
mLoadFlags(nsIRequest::LOAD_NORMAL),
|
||||
mUseSrcAttr(false),
|
||||
mSuppressStyleCheck(false)
|
||||
|
@ -197,16 +195,9 @@ nsImageBoxFrame::MarkIntrinsicWidthsDirty()
|
|||
void
|
||||
nsImageBoxFrame::DestroyFrom(nsIFrame* aDestructRoot)
|
||||
{
|
||||
if (mImageRequest) {
|
||||
nsPresContext* presContext = PresContext();
|
||||
NS_ASSERTION(presContext, "No PresContext");
|
||||
nsLayoutUtils::DeregisterImageRequest(presContext,
|
||||
mImageRequest,
|
||||
&mRequestRegistered);
|
||||
|
||||
// Release image loader first so that it's refcnt can go to zero
|
||||
// Release image loader first so that it's refcnt can go to zero
|
||||
if (mImageRequest)
|
||||
mImageRequest->CancelAndForgetObserver(NS_ERROR_FAILURE);
|
||||
}
|
||||
|
||||
if (mListener)
|
||||
reinterpret_cast<nsImageBoxListener*>(mListener.get())->SetFrame(nsnull); // set the frame to null so we don't send messages to a dead object.
|
||||
|
@ -241,12 +232,7 @@ nsImageBoxFrame::Init(nsIContent* aContent,
|
|||
void
|
||||
nsImageBoxFrame::UpdateImage()
|
||||
{
|
||||
nsPresContext* presContext = PresContext();
|
||||
NS_ASSERTION(presContext, "No PresContext");
|
||||
|
||||
if (mImageRequest) {
|
||||
nsLayoutUtils::DeregisterImageRequest(presContext, mImageRequest,
|
||||
&mRequestRegistered);
|
||||
mImageRequest->CancelAndForgetObserver(NS_ERROR_FAILURE);
|
||||
mImageRequest = nsnull;
|
||||
}
|
||||
|
@ -273,12 +259,6 @@ nsImageBoxFrame::UpdateImage()
|
|||
nsContentUtils::LoadImage(uri, doc, mContent->NodePrincipal(),
|
||||
doc->GetDocumentURI(), mListener, mLoadFlags,
|
||||
getter_AddRefs(mImageRequest));
|
||||
|
||||
// Register our imgIRequest with the refresh driver
|
||||
nsLayoutUtils::RegisterImageRequest(presContext,
|
||||
mImageRequest,
|
||||
&mRequestRegistered);
|
||||
|
||||
}
|
||||
} else {
|
||||
// Only get the list-style-image if we aren't being drawn
|
||||
|
@ -603,31 +583,10 @@ NS_IMETHODIMP nsImageBoxFrame::OnStopContainer(imgIRequest *request,
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsImageBoxFrame::OnStartDecode(imgIRequest* aRequest)
|
||||
{
|
||||
nsPresContext* presContext = PresContext();
|
||||
NS_ASSERTION(presContext, "No PresContext");
|
||||
|
||||
nsLayoutUtils::RegisterImageRequest(presContext,
|
||||
mImageRequest,
|
||||
&mRequestRegistered);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsImageBoxFrame::OnStopDecode(imgIRequest *request,
|
||||
nsresult aStatus,
|
||||
const PRUnichar *statusArg)
|
||||
{
|
||||
// If the imgIRequest does not represent an animated image, then we should
|
||||
// deregister it from our refresh driver.
|
||||
nsPresContext* presContext = PresContext();
|
||||
NS_ASSERTION(presContext, "No PresContext");
|
||||
|
||||
nsLayoutUtils::DeregisterImageRequestIfNotAnimated(presContext,
|
||||
mImageRequest,
|
||||
&mRequestRegistered);
|
||||
|
||||
if (NS_SUCCEEDED(aStatus))
|
||||
// Fire an onload DOM event.
|
||||
FireImageDOMEvent(mContent, NS_LOAD);
|
||||
|
@ -665,7 +624,7 @@ NS_IMETHODIMP nsImageBoxListener::OnStartContainer(imgIRequest *request,
|
|||
imgIContainer *image)
|
||||
{
|
||||
if (!mFrame)
|
||||
return NS_OK;
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
return mFrame->OnStartContainer(request, image);
|
||||
}
|
||||
|
@ -674,26 +633,17 @@ NS_IMETHODIMP nsImageBoxListener::OnStopContainer(imgIRequest *request,
|
|||
imgIContainer *image)
|
||||
{
|
||||
if (!mFrame)
|
||||
return NS_OK;
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
return mFrame->OnStopContainer(request, image);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsImageBoxListener::OnStartDecode(imgIRequest *aRequest)
|
||||
{
|
||||
if (!mFrame) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
return mFrame->OnStartDecode(aRequest);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsImageBoxListener::OnStopDecode(imgIRequest *request,
|
||||
nsresult status,
|
||||
const PRUnichar *statusArg)
|
||||
{
|
||||
if (!mFrame)
|
||||
return NS_OK;
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
return mFrame->OnStopDecode(request, status, statusArg);
|
||||
}
|
||||
|
|
|
@ -56,7 +56,6 @@ public:
|
|||
// imgIDecoderObserver (override nsStubImageDecoderObserver)
|
||||
NS_IMETHOD OnStartContainer(imgIRequest *request, imgIContainer *image);
|
||||
NS_IMETHOD OnStopContainer(imgIRequest *request, imgIContainer *image);
|
||||
NS_IMETHOD OnStartDecode(imgIRequest *aRequest);
|
||||
NS_IMETHOD OnStopDecode(imgIRequest *request, nsresult status,
|
||||
const PRUnichar *statusArg);
|
||||
// imgIContainerObserver (override nsStubImageDecoderObserver)
|
||||
|
@ -120,7 +119,6 @@ public:
|
|||
|
||||
NS_IMETHOD OnStartContainer(imgIRequest *request, imgIContainer *image);
|
||||
NS_IMETHOD OnStopContainer(imgIRequest *request, imgIContainer *image);
|
||||
NS_IMETHOD OnStartDecode(imgIRequest *aRequest);
|
||||
NS_IMETHOD OnStopDecode(imgIRequest *request,
|
||||
nsresult status,
|
||||
const PRUnichar *statusArg);
|
||||
|
@ -144,10 +142,6 @@ private:
|
|||
nsSize mIntrinsicSize;
|
||||
nsSize mImageSize;
|
||||
|
||||
// Boolean variable to determine if the current image request has been
|
||||
// registered with the refresh driver.
|
||||
bool mRequestRegistered;
|
||||
|
||||
nsCOMPtr<imgIRequest> mImageRequest;
|
||||
nsCOMPtr<imgIDecoderObserver> mListener;
|
||||
|
||||
|
|
|
@ -1,64 +0,0 @@
|
|||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is mozilla.org code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Netscape Communications Corporation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2011
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Dave Hyatt <hyatt@mozilla.org> (Original Author)
|
||||
* Jan Varga <varga@ku.sk>
|
||||
* Scott Johnson <sjohnson@mozilla.com>, Mozilla Corporation
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#ifndef nsITreeImageListener_h__
|
||||
#define nsITreeImageListener_h__
|
||||
|
||||
// The interface for our image listener.
|
||||
// {90586540-2D50-403e-8DCE-981CAA778444}
|
||||
#define NS_ITREEIMAGELISTENER_IID \
|
||||
{ 0x90586540, 0x2d50, 0x403e, { 0x8d, 0xce, 0x98, 0x1c, 0xaa, 0x77, 0x84, 0x44 } }
|
||||
|
||||
class nsITreeImageListener : public nsISupports
|
||||
{
|
||||
public:
|
||||
NS_DECLARE_STATIC_IID_ACCESSOR(NS_ITREEIMAGELISTENER_IID)
|
||||
|
||||
NS_IMETHOD AddCell(PRInt32 aIndex, nsITreeColumn* aCol) = 0;
|
||||
|
||||
/**
|
||||
* Clear the internal frame pointer to prevent dereferencing an object
|
||||
* that no longer exists.
|
||||
*/
|
||||
NS_IMETHOD ClearFrame() = 0;
|
||||
};
|
||||
|
||||
NS_DEFINE_STATIC_IID_ACCESSOR(nsITreeImageListener, NS_ITREEIMAGELISTENER_IID)
|
||||
|
||||
#endif
|
|
@ -54,7 +54,6 @@
|
|||
|
||||
#include "nsTreeBodyFrame.h"
|
||||
#include "nsTreeSelection.h"
|
||||
#include "nsTreeImageListener.h"
|
||||
|
||||
#include "nsGkAtoms.h"
|
||||
#include "nsCSSAnonBoxes.h"
|
||||
|
@ -115,14 +114,6 @@ static PLDHashOperator
|
|||
CancelImageRequest(const nsAString& aKey,
|
||||
nsTreeImageCacheEntry aEntry, void* aData)
|
||||
{
|
||||
|
||||
// If our imgIRequest object was registered with the refresh driver,
|
||||
// then we need to deregister it.
|
||||
nsTreeBodyFrame* frame = static_cast<nsTreeBodyFrame*>(aData);
|
||||
|
||||
nsLayoutUtils::DeregisterImageRequest(frame->PresContext(), aEntry.request,
|
||||
nsnull);
|
||||
|
||||
aEntry.request->CancelAndForgetObserver(NS_BINDING_ABORTED);
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
@ -173,8 +164,7 @@ nsTreeBodyFrame::nsTreeBodyFrame(nsIPresShell* aPresShell, nsStyleContext* aCont
|
|||
// Destructor
|
||||
nsTreeBodyFrame::~nsTreeBodyFrame()
|
||||
{
|
||||
mImageCache.EnumerateRead(CancelImageRequest, this);
|
||||
DetachImageListeners();
|
||||
mImageCache.EnumerateRead(CancelImageRequest, nsnull);
|
||||
delete mSlots;
|
||||
}
|
||||
|
||||
|
@ -207,11 +197,8 @@ nsTreeBodyFrame::Init(nsIContent* aContent,
|
|||
mIndentation = GetIndentation();
|
||||
mRowHeight = GetRowHeight();
|
||||
|
||||
NS_ENSURE_TRUE(mCreatedListeners.Init(), NS_ERROR_OUT_OF_MEMORY);
|
||||
|
||||
NS_ENSURE_TRUE(mImageCache.Init(16), NS_ERROR_OUT_OF_MEMORY);
|
||||
EnsureBoxObject();
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
@ -2167,14 +2154,10 @@ nsTreeBodyFrame::GetImage(PRInt32 aRowIndex, nsTreeColumn* aCol, bool aUseContex
|
|||
if (!*aResult) {
|
||||
// Create a new nsTreeImageListener object and pass it our row and column
|
||||
// information.
|
||||
nsTreeImageListener* listener = new nsTreeImageListener(this);
|
||||
nsTreeImageListener* listener = new nsTreeImageListener(mTreeBoxObject);
|
||||
if (!listener)
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
|
||||
if (!mCreatedListeners.PutEntry(listener)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
listener->AddCell(aRowIndex, aCol);
|
||||
nsCOMPtr<imgIDecoderObserver> imgDecoderObserver = listener;
|
||||
|
||||
|
@ -4253,7 +4236,7 @@ nsresult
|
|||
nsTreeBodyFrame::ClearStyleAndImageCaches()
|
||||
{
|
||||
mStyleCache.Clear();
|
||||
mImageCache.EnumerateRead(CancelImageRequest, this);
|
||||
mImageCache.EnumerateRead(CancelImageRequest, nsnull);
|
||||
mImageCache.Clear();
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -4485,20 +4468,6 @@ nsTreeBodyFrame::PostScrollEvent()
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsTreeBodyFrame::DetachImageListeners()
|
||||
{
|
||||
mCreatedListeners.Clear();
|
||||
}
|
||||
|
||||
void
|
||||
nsTreeBodyFrame::RemoveTreeImageListener(nsTreeImageListener* aListener)
|
||||
{
|
||||
if (aListener) {
|
||||
mCreatedListeners.RemoveEntry(aListener);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef ACCESSIBILITY
|
||||
void
|
||||
nsTreeBodyFrame::FireRowCountChangedEvent(PRInt32 aIndex, PRInt32 aCount)
|
||||
|
@ -4672,19 +4641,3 @@ nsTreeBodyFrame::FullScrollbarsUpdate(bool aNeedsFullInvalidation)
|
|||
nsContentUtils::AddScriptRunner(new nsOverflowChecker(this));
|
||||
return weakFrame.IsAlive();
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsTreeBodyFrame::OnStartDecode(imgIRequest* aRequest)
|
||||
{
|
||||
nsLayoutUtils::RegisterImageRequest(PresContext(), aRequest, nsnull);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsTreeBodyFrame::OnStopDecode(imgIRequest* aRequest, nsresult aStatus,
|
||||
const PRUnichar* aStatusArg)
|
||||
{
|
||||
nsLayoutUtils::DeregisterImageRequestIfNotAnimated(PresContext(), aRequest,
|
||||
nsnull);
|
||||
return NS_OK;
|
||||
}
|
||||
|
|
|
@ -54,6 +54,7 @@
|
|||
#include "nsTArray.h"
|
||||
#include "nsTreeStyleCache.h"
|
||||
#include "nsTreeColumns.h"
|
||||
#include "nsTreeImageListener.h"
|
||||
#include "nsAutoPtr.h"
|
||||
#include "nsDataHashtable.h"
|
||||
#include "imgIRequest.h"
|
||||
|
@ -61,10 +62,8 @@
|
|||
#include "nsScrollbarFrame.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "mozilla/LookAndFeel.h"
|
||||
#include "nsITreeImageListener.h"
|
||||
|
||||
class nsOverflowChecker;
|
||||
class nsTreeImageListener;
|
||||
|
||||
// An entry in the tree's image cache
|
||||
struct nsTreeImageCacheEntry
|
||||
|
@ -92,13 +91,6 @@ public:
|
|||
NS_DECL_QUERYFRAME
|
||||
NS_DECL_FRAMEARENA_HELPERS
|
||||
|
||||
// Callback handler methods for refresh driver based animations.
|
||||
// Calls to these functions are forwarded from nsTreeImageListener. These
|
||||
// mirror how nsImageFrame works.
|
||||
nsresult OnStartDecode(imgIRequest* aRequest);
|
||||
nsresult OnStopDecode(imgIRequest* aRequest, nsresult aStatus,
|
||||
const PRUnichar* aStatusArg);
|
||||
|
||||
// non-virtual signatures like nsITreeBodyFrame
|
||||
nsresult GetColumns(nsITreeColumns **aColumns);
|
||||
nsresult GetView(nsITreeView **aView);
|
||||
|
@ -443,15 +435,6 @@ public:
|
|||
return col;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove an nsITreeImageListener from being tracked by this frame. Only tree
|
||||
* image listeners that are created by this frame are tracked.
|
||||
*
|
||||
* @param aListener A pointer to an nsTreeImageListener to no longer
|
||||
* track.
|
||||
*/
|
||||
void RemoveTreeImageListener(nsTreeImageListener* aListener);
|
||||
|
||||
protected:
|
||||
|
||||
// Create a new timer. This method is used to delay various actions like
|
||||
|
@ -482,12 +465,6 @@ protected:
|
|||
void PostScrollEvent();
|
||||
void FireScrollEvent();
|
||||
|
||||
/**
|
||||
* Clear the pointer to this frame for all nsTreeImageListeners that were
|
||||
* created by this frame.
|
||||
*/
|
||||
void DetachImageListeners();
|
||||
|
||||
#ifdef ACCESSIBILITY
|
||||
/**
|
||||
* Fires 'treeRowCountChanged' event asynchronously. The event supports
|
||||
|
@ -625,11 +602,6 @@ protected: // Data Members
|
|||
bool mHorizontalOverflow;
|
||||
|
||||
bool mReflowCallbackPosted;
|
||||
|
||||
// Hash table to keep track of which listeners we created and thus
|
||||
// have pointers to us.
|
||||
nsTHashtable<nsPtrHashKey<nsTreeImageListener> > mCreatedListeners;
|
||||
|
||||
}; // class nsTreeBodyFrame
|
||||
|
||||
#endif
|
||||
|
|
|
@ -44,8 +44,8 @@
|
|||
|
||||
NS_IMPL_ISUPPORTS3(nsTreeImageListener, imgIDecoderObserver, imgIContainerObserver, nsITreeImageListener)
|
||||
|
||||
nsTreeImageListener::nsTreeImageListener(nsTreeBodyFrame* aTreeFrame)
|
||||
: mTreeFrame(aTreeFrame),
|
||||
nsTreeImageListener::nsTreeImageListener(nsITreeBoxObject* aTree)
|
||||
: mTree(aTree),
|
||||
mInvalidationSuppressed(true),
|
||||
mInvalidationArea(nsnull)
|
||||
{
|
||||
|
@ -56,29 +56,6 @@ nsTreeImageListener::~nsTreeImageListener()
|
|||
delete mInvalidationArea;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsTreeImageListener::OnStartDecode(imgIRequest *aRequest)
|
||||
{
|
||||
if (!mTreeFrame) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// grab the frame we want to use
|
||||
return mTreeFrame->OnStartDecode(aRequest);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsTreeImageListener::OnStopDecode(imgIRequest *aRequest,
|
||||
nsresult aStatus,
|
||||
const PRUnichar *aStatusArg)
|
||||
{
|
||||
if (!mTreeFrame) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
return mTreeFrame->OnStopDecode(aRequest, aStatus, aStatusArg);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsTreeImageListener::OnStartContainer(imgIRequest *aRequest,
|
||||
imgIContainer *aImage)
|
||||
{
|
||||
|
@ -136,16 +113,10 @@ void
|
|||
nsTreeImageListener::Invalidate()
|
||||
{
|
||||
if (!mInvalidationSuppressed) {
|
||||
for (InvalidationArea* currArea = mInvalidationArea; currArea;
|
||||
currArea = currArea->GetNext()) {
|
||||
for (InvalidationArea* currArea = mInvalidationArea; currArea; currArea = currArea->GetNext()) {
|
||||
// Loop from min to max, invalidating each cell that was listening for this image.
|
||||
for (PRInt32 i = currArea->GetMin(); i <= currArea->GetMax(); ++i) {
|
||||
if (mTreeFrame) {
|
||||
nsITreeBoxObject* tree = mTreeFrame->GetTreeBoxObject();
|
||||
if (tree) {
|
||||
tree->InvalidateCell(i, currArea->GetCol());
|
||||
}
|
||||
}
|
||||
mTree->InvalidateCell(i, currArea->GetCol());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -169,10 +140,3 @@ nsTreeImageListener::InvalidationArea::AddRow(PRInt32 aIndex)
|
|||
else if (aIndex > mMax)
|
||||
mMax = aIndex;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsTreeImageListener::ClearFrame()
|
||||
{
|
||||
mTreeFrame = nsnull;
|
||||
return NS_OK;
|
||||
}
|
||||
|
|
|
@ -44,21 +44,33 @@
|
|||
#include "nsCOMPtr.h"
|
||||
#include "nsITreeColumns.h"
|
||||
#include "nsStubImageDecoderObserver.h"
|
||||
#include "nsTreeBodyFrame.h"
|
||||
#include "nsITreeImageListener.h"
|
||||
|
||||
class nsITreeBoxObject;
|
||||
|
||||
// The interface for our image listener.
|
||||
// {90586540-2D50-403e-8DCE-981CAA778444}
|
||||
#define NS_ITREEIMAGELISTENER_IID \
|
||||
{ 0x90586540, 0x2d50, 0x403e, { 0x8d, 0xce, 0x98, 0x1c, 0xaa, 0x77, 0x84, 0x44 } }
|
||||
|
||||
class nsITreeImageListener : public nsISupports
|
||||
{
|
||||
public:
|
||||
NS_DECLARE_STATIC_IID_ACCESSOR(NS_ITREEIMAGELISTENER_IID)
|
||||
|
||||
NS_IMETHOD AddCell(PRInt32 aIndex, nsITreeColumn* aCol) = 0;
|
||||
};
|
||||
|
||||
NS_DEFINE_STATIC_IID_ACCESSOR(nsITreeImageListener, NS_ITREEIMAGELISTENER_IID)
|
||||
|
||||
// This class handles image load observation.
|
||||
class nsTreeImageListener : public nsStubImageDecoderObserver, public nsITreeImageListener
|
||||
{
|
||||
public:
|
||||
nsTreeImageListener(nsTreeBodyFrame *aTreeFrame);
|
||||
nsTreeImageListener(nsITreeBoxObject* aTree);
|
||||
~nsTreeImageListener();
|
||||
|
||||
NS_DECL_ISUPPORTS
|
||||
// imgIDecoderObserver (override nsStubImageDecoderObserver)
|
||||
NS_IMETHOD OnStartDecode(imgIRequest *aRequest);
|
||||
NS_IMETHOD OnStopDecode(imgIRequest *aRequest,
|
||||
nsresult aStatus, const PRUnichar *aStatusArg);
|
||||
NS_IMETHOD OnStartContainer(imgIRequest *aRequest, imgIContainer *aImage);
|
||||
NS_IMETHOD OnDataAvailable(imgIRequest *aRequest, bool aCurrentFrame,
|
||||
const nsIntRect *aRect);
|
||||
|
@ -67,8 +79,7 @@ public:
|
|||
const nsIntRect *aDirtyRect);
|
||||
|
||||
NS_IMETHOD AddCell(PRInt32 aIndex, nsITreeColumn* aCol);
|
||||
NS_IMETHOD ClearFrame();
|
||||
|
||||
|
||||
friend class nsTreeBodyFrame;
|
||||
|
||||
protected:
|
||||
|
@ -76,7 +87,7 @@ protected:
|
|||
void Invalidate();
|
||||
|
||||
private:
|
||||
nsTreeBodyFrame* mTreeFrame;
|
||||
nsITreeBoxObject* mTree;
|
||||
|
||||
// A guard that prevents us from recursive painting.
|
||||
bool mInvalidationSuppressed;
|
||||
|
|
|
@ -96,6 +96,7 @@
|
|||
</div>
|
||||
<div id="sync-setup">
|
||||
<a id="syncSetupSync" href="#" onclick="openSetupSyncWizard();">&aboutHome.setupSync;</a>
|
||||
<a id="syncPairDevice" href="#" onclick="openPairDeviceWizard();">&aboutHome.syncPairDevice;</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -433,8 +434,10 @@
|
|||
function initSetupSync() {
|
||||
let services = getChromeWin().Services;
|
||||
if (services.prefs.prefHasUserValue("services.sync.username")) {
|
||||
document.getElementById("syncSetupSync").style.display = "none";
|
||||
}
|
||||
syncConnected();
|
||||
} else {
|
||||
syncDisconnected();
|
||||
}
|
||||
services.obs.addObserver(syncConnected, "weave:service:setup-complete", false);
|
||||
services.obs.addObserver(syncDisconnected, "weave:service:start-over", false);
|
||||
}
|
||||
|
@ -447,10 +450,12 @@
|
|||
|
||||
function syncConnected() {
|
||||
document.getElementById("syncSetupSync").style.display = "none";
|
||||
document.getElementById("syncPairDevice").style.display = "inline";
|
||||
}
|
||||
|
||||
function syncDisconnected() {
|
||||
document.getElementById("syncSetupSync").style.display = "inline";
|
||||
document.getElementById("syncPairDevice").style.display = "none";
|
||||
}
|
||||
|
||||
function openSetupSyncWizard() {
|
||||
|
@ -458,6 +463,11 @@
|
|||
chromeWin.WeaveGlue.open();
|
||||
}
|
||||
|
||||
function openPairDeviceWizard() {
|
||||
let chromeWin = getChromeWin();
|
||||
chromeWin.SyncPairDevice.open();
|
||||
}
|
||||
|
||||
function initLightbox() {
|
||||
let prefs = getChromeWin().Services.prefs;
|
||||
let channel = prefs.getCharPref("app.update.channel");
|
||||
|
|
|
@ -113,6 +113,7 @@ XPCOMUtils.defineLazyGetter(this, "CommonUI", function() {
|
|||
["MasterPasswordUI", "chrome://browser/content/MasterPasswordUI.js"],
|
||||
#ifdef MOZ_SERVICES_SYNC
|
||||
["WeaveGlue", "chrome://browser/content/sync.js"],
|
||||
["SyncPairDevice", "chrome://browser/content/sync.js"],
|
||||
#endif
|
||||
["WebappsUI", "chrome://browser/content/WebappsUI.js"],
|
||||
["SSLExceptions", "chrome://browser/content/exceptions.js"],
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
- Brad Lassey <blassey@mozilla.com>
|
||||
- Mark Finkle <mfinkle@mozila.com>
|
||||
- Matt Brubeck <mbrubeck@mozila.com>
|
||||
- Allison Naaktgeboren <ally@mozilla.com>
|
||||
-
|
||||
- Alternatively, the contents of this file may be used under the terms of
|
||||
- either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
|
@ -487,6 +488,7 @@
|
|||
<button label="&sync.connect;" oncommand="WeaveGlue.tryConnect();" />
|
||||
</setting>
|
||||
<setting id="sync-connected" class="setting-group" title="&sync.connected;" type="control" collapsed="true">
|
||||
<button id="sync-pairdevice" label="&sync.pair.title;" oncommand="SyncPairDevice.open();" />
|
||||
<button id="sync-details" label="&sync.details;" type="checkbox" autocheck="false" checked="false" oncommand="WeaveGlue.showDetails();" />
|
||||
</setting>
|
||||
<setting id="sync-sync" class="setting-subgroup" type="control" collapsed="true">
|
||||
|
@ -593,7 +595,15 @@
|
|||
</hbox>
|
||||
</vbox>
|
||||
<vbox id="syncsetup-waiting" class="syncsetup-page" flex="1" hidden="true">
|
||||
<description class="syncsetup-desc syncsetup-center" flex="1">&sync.setup.waiting;</description>
|
||||
<progressmeter id="syncsetup-progressbar" mode="undetermined"/>
|
||||
<vbox id="syncsetup-waiting-top" align="center" flex="1">
|
||||
<description id="syncsetup-waiting-desc" class="syncsetup-desc syncsetup-center" flex="1">&sync.setup.waiting2;</description>
|
||||
<description id="syncsetup-waitingdownload-desc" class="syncsetup-desc syncsetup-center" hidden="true" flex="1">&sync.setup.waitingdownload;</description>
|
||||
</vbox>
|
||||
<hbox class="prompt-buttons" pack="center" align="end">
|
||||
<button id="syncsetup-waiting-cancel" class="prompt-button" oncommand="WeaveGlue.close();">&sync.setup.cancel;</button>
|
||||
<button id="syncsetup-waiting-close" class="prompt-button" hidden="true" oncommand="WeaveGlue.close();">&sync.setup.close;</button>
|
||||
</hbox>
|
||||
</vbox>
|
||||
<vbox id="syncsetup-fallback" class="syncsetup-page" flex="1" hidden="true">
|
||||
<scrollbox class="prompt-message" orient="vertical" flex="1">
|
||||
|
@ -618,6 +628,32 @@
|
|||
</vbox>
|
||||
</dialog>
|
||||
</box>
|
||||
|
||||
<box id="syncpair-container" class="perm-modal-block" hidden="true">
|
||||
<dialog id="syncpair-dialog" class="content-dialog" flex="1">
|
||||
<hbox class="prompt-title">
|
||||
<description>&sync.pair.title;</description>
|
||||
</hbox>
|
||||
<separator class="prompt-line"/>
|
||||
<vbox id="syncpair-simple" class="syncsetup-page" flex="1">
|
||||
<scrollbox id="sync-message" class="prompt-message" orient="vertical" flex="1">
|
||||
<description class="syncsetup-desc syncsetup-center" flex="1">&sync.pair.description;</description>
|
||||
<description class="syncsetup-center link" flex="1" onclick="SyncPairDevice.close(); WeaveGlue.openTutorial();">&sync.setup.tutorial;</description>
|
||||
<separator/>
|
||||
<vbox align="center" flex="1">
|
||||
<textbox id="syncpair-code1" class="syncsetup-code" oninput="SyncPairDevice.onTextBoxInput(this);"/>
|
||||
<textbox id="syncpair-code2" class="syncsetup-code" oninput="SyncPairDevice.onTextBoxInput(this);"/>
|
||||
<textbox id="syncpair-code3" class="syncsetup-code" oninput="SyncPairDevice.onTextBoxInput(this);"/>
|
||||
</vbox>
|
||||
<separator flex="1"/>
|
||||
</scrollbox>
|
||||
<hbox class="prompt-buttons" pack="center">
|
||||
<button class="prompt-button" oncommand="SyncPairDevice.close();">&sync.setup.cancel;</button>
|
||||
<button id="syncpair-connectbutton" class="prompt-button" disabled="true" oncommand="SyncPairDevice.connect();">&sync.setup.connect;</button>
|
||||
</hbox>
|
||||
</vbox>
|
||||
</dialog>
|
||||
</box>
|
||||
#endif
|
||||
|
||||
<arrowbox id="search-engines-popup" hidden="true" offset="18" flex="1" type="dialog">
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
* Mark Finkle <mfinkle@mozila.com>
|
||||
* Matt Brubeck <mbrubeck@mozila.com>
|
||||
* Jono DiCarlo <jdicarlo@mozilla.com>
|
||||
* Allison Naaktgeboren <ally@mozilla.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
|
@ -38,9 +39,14 @@
|
|||
|
||||
let WeaveGlue = {
|
||||
setupData: null,
|
||||
_boundOnEngineSync: null, // Needed to unhook the observers in close().
|
||||
_boundOnServiceSync: null,
|
||||
jpake: null,
|
||||
_bundle: null,
|
||||
_loginError: false,
|
||||
_progressBar: null,
|
||||
_progressValue: 0,
|
||||
_progressMax: null,
|
||||
|
||||
init: function init() {
|
||||
if (this._bundle)
|
||||
|
@ -64,6 +70,9 @@ let WeaveGlue = {
|
|||
} else if (Weave.Status.login != Weave.LOGIN_FAILED_NO_USERNAME) {
|
||||
this.loadSetupData();
|
||||
}
|
||||
this._boundOnEngineSync = this.onEngineSync.bind(this);
|
||||
this._boundOnServiceSync = this.onServiceSync.bind(this);
|
||||
this._progressBar = document.getElementById("syncsetup-progressbar");
|
||||
},
|
||||
|
||||
abortEasySetup: function abortEasySetup() {
|
||||
|
@ -128,7 +137,16 @@ let WeaveGlue = {
|
|||
|
||||
onComplete: function onComplete(aCredentials) {
|
||||
self.jpake = null;
|
||||
self.close();
|
||||
|
||||
self._progressBar.mode = "determined";
|
||||
document.getElementById("syncsetup-waiting-desc").hidden = true;
|
||||
document.getElementById("syncsetup-waiting-cancel").hidden = true;
|
||||
document.getElementById("syncsetup-waitingdownload-desc").hidden = false;
|
||||
document.getElementById("syncsetup-waiting-close").hidden = false;
|
||||
Services.obs.addObserver(self._boundOnEngineSync, "weave:engine:sync:finish", false);
|
||||
Services.obs.addObserver(self._boundOnEngineSync, "weave:engine:sync:error", false);
|
||||
Services.obs.addObserver(self._boundOnServiceSync, "weave:service:sync:finish", false);
|
||||
Services.obs.addObserver(self._boundOnServiceSync, "weave:service:sync:error", false);
|
||||
self.setupData = aCredentials;
|
||||
self.connect();
|
||||
},
|
||||
|
@ -180,6 +198,7 @@ let WeaveGlue = {
|
|||
this._resetScrollPosition();
|
||||
|
||||
document.getElementById("syncsetup-simple").hidden = true;
|
||||
document.getElementById("syncsetup-waiting").hidden = true;
|
||||
document.getElementById("syncsetup-fallback").hidden = false;
|
||||
|
||||
// Push the current setup data into the UI
|
||||
|
@ -204,7 +223,37 @@ let WeaveGlue = {
|
|||
this.canConnect();
|
||||
},
|
||||
|
||||
onEngineSync: function onEngineSync(subject, topic, data) {
|
||||
// The Clients engine syncs first. At this point we don't necessarily know
|
||||
// yet how many engines will be enabled, so we'll ignore the Clients engine
|
||||
// and evaluate how many engines are enabled when the first "real" engine
|
||||
// syncs.
|
||||
if (data == 'clients') {
|
||||
return;
|
||||
}
|
||||
if (this._progressMax == null) {
|
||||
this._progressMax = Weave.Engines.getEnabled().length;
|
||||
this._progressBar.max = this._progressMax;
|
||||
}
|
||||
this._progressValue += 1;
|
||||
this._progressBar.setAttribute("value", this._progressValue);
|
||||
},
|
||||
|
||||
onServiceSync: function onServiceSync() {
|
||||
this.close();
|
||||
},
|
||||
|
||||
close: function close() {
|
||||
try {
|
||||
Services.obs.removeObserver(this._boundOnEngineSync, "weave:engine:sync:finish");
|
||||
Services.obs.removeObserver(this._boundOnEngineSync, "weave:engine:sync:error");
|
||||
Services.obs.removeObserver(this._boundOnServiceSync, "weave:service:sync:finish");
|
||||
Services.obs.removeObserver(this._boundOnServiceSync, "weave:service:sync:error");
|
||||
}
|
||||
catch(e) {
|
||||
// Observers weren't registered because we never got as far as onComplete.
|
||||
}
|
||||
|
||||
if (this.jpake)
|
||||
this.abortEasySetup();
|
||||
|
||||
|
@ -226,6 +275,15 @@ let WeaveGlue = {
|
|||
this._elements.usecustomserver.checked = false;
|
||||
this._elements.customserver.disabled = true;
|
||||
this._elements.customserver.value = "";
|
||||
document.getElementById("syncsetup-waiting-desc").hidden = false;
|
||||
document.getElementById("syncsetup-waiting-cancel").hidden = false;
|
||||
document.getElementById("syncsetup-waitingdownload-desc").hidden = true;
|
||||
document.getElementById("syncsetup-waiting-close").hidden = true;
|
||||
this._progressMax = null;
|
||||
this._progressValue = 0;
|
||||
this._progressBar.max = 0;
|
||||
this._progressBar.value = 0;
|
||||
this._progressBar.mode = "undetermined";
|
||||
|
||||
// Close the connect UI
|
||||
document.getElementById("syncsetup-container").hidden = true;
|
||||
|
@ -372,7 +430,7 @@ let WeaveGlue = {
|
|||
elements[id] = document.getElementById("syncsetup-" + id);
|
||||
});
|
||||
|
||||
let settingids = ["device", "connect", "connected", "disconnect", "sync", "details"];
|
||||
let settingids = ["device", "connect", "connected", "disconnect", "sync", "details", "pairdevice"];
|
||||
settingids.forEach(function(id) {
|
||||
elements[id] = document.getElementById("sync-" + id);
|
||||
});
|
||||
|
@ -397,6 +455,7 @@ let WeaveGlue = {
|
|||
let device = this._elements.device;
|
||||
let disconnect = this._elements.disconnect;
|
||||
let sync = this._elements.sync;
|
||||
let pairdevice = this._elements.pairdevice;
|
||||
|
||||
// Show what went wrong with login if necessary
|
||||
if (aTopic == "weave:ui:login:error") {
|
||||
|
@ -541,3 +600,95 @@ let WeaveGlue = {
|
|||
this.setupData.serverURL = serverURL;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
const PIN_PART_LENGTH = 4;
|
||||
|
||||
let SyncPairDevice = {
|
||||
jpake: null,
|
||||
|
||||
open: function open() {
|
||||
this.code1.setAttribute("maxlength", PIN_PART_LENGTH);
|
||||
this.code2.setAttribute("maxlength", PIN_PART_LENGTH);
|
||||
this.code3.setAttribute("maxlength", PIN_PART_LENGTH);
|
||||
this.nextFocusEl = {code1: this.code2,
|
||||
code2: this.code3,
|
||||
code3: this.connectbutton};
|
||||
|
||||
document.getElementById("syncpair-container").hidden = false;
|
||||
BrowserUI.pushDialog(this);
|
||||
this.code1.focus();
|
||||
|
||||
// Kick off a sync. That way the server will have the most recent data from
|
||||
// this computer and it will show up immediately on the new device.
|
||||
Weave.SyncScheduler.scheduleNextSync(0);
|
||||
},
|
||||
|
||||
close: function close() {
|
||||
this.code1.value = this.code2.value = this.code3.value = "";
|
||||
this.code1.disabled = this.code2.disabled = this.code3.disabled = false;
|
||||
this.connectbutton.disabled = true;
|
||||
if (this.jpake) {
|
||||
this.jpake.abort();
|
||||
this.jpake = null;
|
||||
}
|
||||
document.getElementById("syncpair-container").hidden = true;
|
||||
BrowserUI.popDialog();
|
||||
},
|
||||
|
||||
onTextBoxInput: function onTextBoxInput(textbox) {
|
||||
if (textbox && textbox.value.length == PIN_PART_LENGTH) {
|
||||
let name = textbox.id.split("-")[1];
|
||||
this.nextFocusEl[name].focus();
|
||||
}
|
||||
|
||||
this.connectbutton.disabled =
|
||||
!(this.code1.value.length == PIN_PART_LENGTH &&
|
||||
this.code2.value.length == PIN_PART_LENGTH &&
|
||||
this.code3.value.length == PIN_PART_LENGTH);
|
||||
},
|
||||
|
||||
connect: function connect() {
|
||||
let self = this;
|
||||
let jpake = this.jpake = new Weave.JPAKEClient({
|
||||
onPaired: function onPaired() {
|
||||
let credentials = {account: Weave.Service.account,
|
||||
password: Weave.Service.password,
|
||||
synckey: Weave.Service.passphrase,
|
||||
serverURL: Weave.Service.serverURL};
|
||||
jpake.sendAndComplete(credentials);
|
||||
},
|
||||
onComplete: function onComplete() {
|
||||
self.jpake = null;
|
||||
self.close();
|
||||
|
||||
// Schedule a Sync for soonish to fetch the data uploaded by the
|
||||
// device with which we just paired.
|
||||
Weave.SyncScheduler.scheduleNextSync(Weave.SyncScheduler.activeInterval);
|
||||
},
|
||||
onAbort: function onAbort(error) {
|
||||
self.jpake = null;
|
||||
|
||||
// Aborted by user, ignore.
|
||||
if (error == Weave.JPAKE_ERROR_USERABORT) {
|
||||
return;
|
||||
}
|
||||
|
||||
self.code1.value = self.code2.value = self.code3.value = "";
|
||||
self.code1.disabled = self.code2.disabled = self.code3.disabled = false;
|
||||
self.code1.focus();
|
||||
}
|
||||
});
|
||||
this.code1.disabled = this.code2.disabled = this.code3.disabled = true;
|
||||
this.connectbutton.disabled = true;
|
||||
|
||||
let pin = this.code1.value + this.code2.value + this.code3.value;
|
||||
let expectDelay = false;
|
||||
jpake.pairWithPIN(pin, expectDelay);
|
||||
}
|
||||
};
|
||||
["code1", "code2", "code3", "connectbutton"].forEach(function (id) {
|
||||
XPCOMUtils.defineLazyGetter(SyncPairDevice, id, function() {
|
||||
return document.getElementById("syncpair-" + id);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -19,4 +19,13 @@
|
|||
(aboutHome.forAndroid): Second line of a multi-line button. Treat as a subtitle.
|
||||
-->
|
||||
<!ENTITY aboutHome.forAndroid "for Android">
|
||||
<!ENTITY aboutHome.setupSync "Set Up Sync">
|
||||
<!-- LOCALIZATION NOTE:
|
||||
(aboutHome.setupSync): This string should match the desktop
|
||||
equivalent, in particular concerning syncBrand.fullName.label.
|
||||
-->
|
||||
<!ENTITY aboutHome.setupSync "Set Up Sync">
|
||||
<!-- LOCALIZATION NOTE:
|
||||
(aboutHome.syncPairDevice): This string should match the desktop
|
||||
equivalent, in particular concerning syncBrand.fullName.label.
|
||||
-->
|
||||
<!ENTITY aboutHome.syncPairDevice "Pair a Device">
|
||||
|
|
|
@ -19,4 +19,9 @@
|
|||
<!ENTITY sync.setup.connect "Connect">
|
||||
<!ENTITY sync.setup.cancel "Cancel">
|
||||
<!ENTITY sync.setup.tutorial "Show me how">
|
||||
<!ENTITY sync.setup.waiting "Pairing in progress…">
|
||||
<!ENTITY sync.setup.waiting2 "Waiting for other device…">
|
||||
|
||||
<!ENTITY sync.pair.title "Pair a Device">
|
||||
<!ENTITY sync.pair.description "To activate your new device, select "Set Up Sync" on the device.">
|
||||
<!ENTITY sync.setup.close "Close">
|
||||
<!ENTITY sync.setup.waitingdownload "Your data is now being downloaded in the background. You can close this window at any time.">
|
||||
|
|
|
@ -267,7 +267,8 @@ body[dir="rtl"] {
|
|||
text-align: center;
|
||||
}
|
||||
|
||||
#syncSetupSync {
|
||||
#syncSetupSync,
|
||||
#syncPairDevice {
|
||||
text-decoration: underline;
|
||||
color: blue;
|
||||
}
|
||||
|
|
|
@ -1534,7 +1534,11 @@ setting {
|
|||
}
|
||||
|
||||
#syncsetup-waiting {
|
||||
padding: 10em 0;
|
||||
padding: 2em 0 0 0;
|
||||
}
|
||||
|
||||
#syncsetup-waiting-top {
|
||||
padding: 1em;
|
||||
}
|
||||
|
||||
/* content scrollbars */
|
||||
|
|
|
@ -65,7 +65,6 @@ typedef int (*PR_CALLBACK PrefChangedFunc)(const char *, void *);
|
|||
namespace mozilla {
|
||||
|
||||
class Preferences : public nsIPrefService,
|
||||
public nsIPrefServiceInternal,
|
||||
public nsIObserver,
|
||||
public nsIPrefBranchInternal,
|
||||
public nsSupportsWeakReference
|
||||
|
@ -73,7 +72,6 @@ class Preferences : public nsIPrefService,
|
|||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIPREFSERVICE
|
||||
NS_DECL_NSIPREFSERVICEINTERNAL
|
||||
NS_FORWARD_NSIPREFBRANCH(sRootBranch->)
|
||||
NS_FORWARD_NSIPREFBRANCH2(sRootBranch->)
|
||||
NS_DECL_NSIOBSERVER
|
||||
|
@ -341,6 +339,14 @@ public:
|
|||
static nsresult GetDefaultComplex(const char* aPref, const nsIID &aType,
|
||||
void** aResult);
|
||||
|
||||
// Used to synchronise preferences between chrome and content processes.
|
||||
static nsresult ReadExtensionPrefs(nsIFile *aFile);
|
||||
static void MirrorPreferences(nsTArray<PrefTuple,
|
||||
nsTArrayInfallibleAllocator> *aArray);
|
||||
static bool MirrorPreference(const char *aPref, PrefTuple *aTuple);
|
||||
static void ClearContentPref(const char *aPref);
|
||||
static void SetPreference(const PrefTuple *aTuple);
|
||||
|
||||
protected:
|
||||
nsresult NotifyServiceObservers(const char *aSubject);
|
||||
nsresult UseDefaultPrefFile();
|
||||
|
|
|
@ -163,29 +163,6 @@ interface nsIPrefService : nsISupports
|
|||
|
||||
};
|
||||
|
||||
[scriptable, uuid(08c8cd2f-8345-45ee-938d-37ee6d3661b2)]
|
||||
interface nsIPrefServiceInternal : nsISupports
|
||||
{
|
||||
/**
|
||||
* Called to read the preferences in the defaults/preferences/
|
||||
* directory of a zip file
|
||||
*
|
||||
* @param aFile The zip file to be read.
|
||||
*
|
||||
* @return NS_OK The file was read and processed.
|
||||
* @return Other The file failed to read or contained invalid data.
|
||||
*
|
||||
* @see readUserPrefs
|
||||
*/
|
||||
void readExtensionPrefs(in nsILocalFile aFile);
|
||||
|
||||
[noscript] void mirrorPreferences(in nsPreferencesArrayPtr aArray);
|
||||
[noscript] void mirrorPreference(in ACString aPrefName, in nsPreferencePtr aPref);
|
||||
[noscript] boolean prefHasUserValue(in ACString aPrefName);
|
||||
[noscript] void setPreference(in nsPreferencePtrConst aPref);
|
||||
[noscript] void clearContentPref(in ACString aPrefName);
|
||||
};
|
||||
|
||||
%{C++
|
||||
|
||||
#define NS_PREFSERVICE_CID \
|
||||
|
|
|
@ -303,7 +303,6 @@ NS_IMPL_THREADSAFE_RELEASE(Preferences)
|
|||
NS_INTERFACE_MAP_BEGIN(Preferences)
|
||||
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIPrefService)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIPrefService)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIPrefServiceInternal)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIObserver)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIPrefBranch)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIPrefBranch2)
|
||||
|
@ -463,9 +462,8 @@ Preferences::SavePrefFile(nsIFile *aFile)
|
|||
return SavePrefFileInternal(aFile);
|
||||
}
|
||||
|
||||
/* part of nsIPrefServiceInternal */
|
||||
NS_IMETHODIMP
|
||||
Preferences::ReadExtensionPrefs(nsILocalFile *aFile)
|
||||
nsresult
|
||||
Preferences::ReadExtensionPrefs(nsIFile *aFile)
|
||||
{
|
||||
nsresult rv;
|
||||
nsCOMPtr<nsIZipReader> reader = do_CreateInstance(kZipReaderCID, &rv);
|
||||
|
@ -513,47 +511,35 @@ Preferences::ReadExtensionPrefs(nsILocalFile *aFile)
|
|||
return rv;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
Preferences::PrefHasUserValue(const nsACString& aPrefName, bool* aHasValue)
|
||||
{
|
||||
*aHasValue = PREF_HasUserPref(aPrefName.BeginReading());
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
void
|
||||
Preferences::SetPreference(const PrefTuple *aPref)
|
||||
{
|
||||
return pref_SetPrefTuple(*aPref, true);
|
||||
pref_SetPrefTuple(*aPref, true);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
Preferences::ClearContentPref(const nsACString& aPrefName)
|
||||
void
|
||||
Preferences::ClearContentPref(const char *aPref)
|
||||
{
|
||||
return PREF_ClearUserPref(aPrefName.BeginReading());
|
||||
PREF_ClearUserPref(aPref);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
Preferences::MirrorPreference(const nsACString& aPrefName, PrefTuple *aPref)
|
||||
bool
|
||||
Preferences::MirrorPreference(const char *aPref, PrefTuple *aTuple)
|
||||
{
|
||||
PrefHashEntry *pref = pref_HashTableLookup(PromiseFlatCString(aPrefName).get());
|
||||
PrefHashEntry *entry = pref_HashTableLookup(aPref);
|
||||
if (!entry)
|
||||
return false;
|
||||
|
||||
if (!pref)
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
|
||||
pref_GetTupleFromEntry(pref, aPref);
|
||||
|
||||
return NS_OK;
|
||||
pref_GetTupleFromEntry(entry, aTuple);
|
||||
return true;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
void
|
||||
Preferences::MirrorPreferences(nsTArray<PrefTuple,
|
||||
nsTArrayInfallibleAllocator> *aArray)
|
||||
{
|
||||
aArray->SetCapacity(PL_DHASH_TABLE_SIZE(&gHashTable));
|
||||
|
||||
PL_DHashTableEnumerate(&gHashTable, pref_MirrorPrefs, aArray);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
|
|
|
@ -724,7 +724,7 @@ SyncEngine.prototype = {
|
|||
// Delete any existing data and reupload on bad version or missing meta.
|
||||
// No crypto component here...? We could regenerate per-collection keys...
|
||||
if (needsWipe) {
|
||||
this.wipeServer(true);
|
||||
this.wipeServer();
|
||||
}
|
||||
|
||||
// Save objects that need to be uploaded in this._modified. We also save
|
||||
|
@ -1248,7 +1248,10 @@ SyncEngine.prototype = {
|
|||
},
|
||||
|
||||
wipeServer: function wipeServer() {
|
||||
new Resource(this.engineURL).delete();
|
||||
let response = new Resource(this.engineURL).delete();
|
||||
if (response.status != 200 && response.status != 404) {
|
||||
throw response;
|
||||
}
|
||||
this._resetClient();
|
||||
},
|
||||
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
* Marina Samuel <msamuel@mozilla.com>
|
||||
* Philipp von Weitershausen <philipp@weitershausen.de>
|
||||
* Chenxia Liu <liuche@mozilla.com>
|
||||
* Richard Newman <rnewman@mozilla.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
|
@ -55,6 +56,12 @@ Cu.import("resource://services-sync/main.js"); // So we can get to Service fo
|
|||
let SyncScheduler = {
|
||||
_log: Log4Moz.repository.getLogger("Sync.SyncScheduler"),
|
||||
|
||||
_fatalLoginStatus: [LOGIN_FAILED_NO_USERNAME,
|
||||
LOGIN_FAILED_NO_PASSWORD,
|
||||
LOGIN_FAILED_NO_PASSPHRASE,
|
||||
LOGIN_FAILED_INVALID_PASSPHRASE,
|
||||
LOGIN_FAILED_LOGIN_REJECTED],
|
||||
|
||||
/**
|
||||
* The nsITimer object that schedules the next sync. See scheduleNextSync().
|
||||
*/
|
||||
|
@ -116,6 +123,7 @@ let SyncScheduler = {
|
|||
},
|
||||
|
||||
observe: function observe(subject, topic, data) {
|
||||
this._log.trace("Handling " + topic);
|
||||
switch(topic) {
|
||||
case "weave:engine:score:updated":
|
||||
if (Status.login == LOGIN_SUCCEEDED) {
|
||||
|
@ -163,7 +171,7 @@ let SyncScheduler = {
|
|||
}
|
||||
break;
|
||||
case "weave:engine:sync:error":
|
||||
// subject is the exception thrown by an engine's sync() method
|
||||
// `subject` is the exception thrown by an engine's sync() method.
|
||||
let exception = subject;
|
||||
if (exception.status >= 500 && exception.status <= 504) {
|
||||
this.requiresBackoff = true;
|
||||
|
@ -172,12 +180,16 @@ let SyncScheduler = {
|
|||
case "weave:service:login:error":
|
||||
this.clearSyncTriggers();
|
||||
|
||||
// Try again later, just as if we threw an error... only without the
|
||||
// error count.
|
||||
if (Status.login == MASTER_PASSWORD_LOCKED) {
|
||||
// Try again later, just as if we threw an error... only without the
|
||||
// error count.
|
||||
this._log.debug("Couldn't log in: master password is locked.");
|
||||
this._log.trace("Scheduling a sync at MASTER_PASSWORD_LOCKED_RETRY_INTERVAL");
|
||||
this.scheduleAtInterval(MASTER_PASSWORD_LOCKED_RETRY_INTERVAL);
|
||||
} else if (this._fatalLoginStatus.indexOf(Status.login) == -1) {
|
||||
// Not a fatal login error, just an intermittent network or server
|
||||
// issue. Keep on syncin'.
|
||||
this.checkSyncStatus();
|
||||
}
|
||||
break;
|
||||
case "weave:service:logout:finish":
|
||||
|
@ -449,6 +461,7 @@ let SyncScheduler = {
|
|||
* Deal with sync errors appropriately
|
||||
*/
|
||||
handleSyncError: function handleSyncError() {
|
||||
this._log.trace("In handleSyncError. Error count: " + this._syncErrors);
|
||||
this._syncErrors++;
|
||||
|
||||
// Do nothing on the first couple of failures, if we're not in
|
||||
|
@ -458,6 +471,8 @@ let SyncScheduler = {
|
|||
this.scheduleNextSync();
|
||||
return;
|
||||
}
|
||||
this._log.debug("Sync error count has exceeded " +
|
||||
MAX_ERROR_COUNT_BEFORE_BACKOFF + "; enforcing backoff.");
|
||||
Status.enforceBackoff = true;
|
||||
}
|
||||
|
||||
|
@ -469,7 +484,8 @@ let SyncScheduler = {
|
|||
* Remove any timers/observers that might trigger a sync
|
||||
*/
|
||||
clearSyncTriggers: function clearSyncTriggers() {
|
||||
this._log.debug("Clearing sync triggers.");
|
||||
this._log.debug("Clearing sync triggers and the global score.");
|
||||
this.globalScore = this.nextSync = 0;
|
||||
|
||||
// Clear out any scheduled syncs
|
||||
if (this.syncTimer)
|
||||
|
@ -521,6 +537,7 @@ let ErrorHandler = {
|
|||
},
|
||||
|
||||
observe: function observe(subject, topic, data) {
|
||||
this._log.trace("Handling " + topic);
|
||||
switch(topic) {
|
||||
case "weave:engine:sync:applied":
|
||||
if (subject.newFailed) {
|
||||
|
@ -569,6 +586,19 @@ let ErrorHandler = {
|
|||
this.dontIgnoreErrors = false;
|
||||
break;
|
||||
case "weave:service:sync:finish":
|
||||
this._log.trace("Status.service is " + Status.service);
|
||||
|
||||
// Check both of these status codes: in the event of a failure in one
|
||||
// engine, Status.service will be SYNC_FAILED_PARTIAL despite
|
||||
// Status.sync being SYNC_SUCCEEDED.
|
||||
// *facepalm*
|
||||
if (Status.sync == SYNC_SUCCEEDED &&
|
||||
Status.service == STATUS_OK) {
|
||||
// Great. Let's clear our mid-sync 401 note.
|
||||
this._log.trace("Clearing lastSyncReassigned.");
|
||||
Svc.Prefs.reset("lastSyncReassigned");
|
||||
}
|
||||
|
||||
if (Status.service == SYNC_FAILED_PARTIAL) {
|
||||
this._log.debug("Some engines did not sync correctly.");
|
||||
this.resetFileLog(Svc.Prefs.get("log.appender.file.logOnError"),
|
||||
|
@ -590,7 +620,12 @@ let ErrorHandler = {
|
|||
},
|
||||
|
||||
notifyOnNextTick: function notifyOnNextTick(topic) {
|
||||
Utils.nextTick(function() Svc.Obs.notify(topic));
|
||||
Utils.nextTick(function() {
|
||||
this._log.trace("Notifying " + topic +
|
||||
". Status.login is " + Status.login +
|
||||
". Status.sync is " + Status.sync);
|
||||
Svc.Obs.notify(topic);
|
||||
}, this);
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -710,6 +745,7 @@ let ErrorHandler = {
|
|||
|
||||
shouldReportError: function shouldReportError() {
|
||||
if (Status.login == MASTER_PASSWORD_LOCKED) {
|
||||
this._log.trace("shouldReportError: false (master password locked).");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -721,8 +757,18 @@ let ErrorHandler = {
|
|||
if (lastSync && ((Date.now() - Date.parse(lastSync)) >
|
||||
Svc.Prefs.get("errorhandler.networkFailureReportTimeout") * 1000)) {
|
||||
Status.sync = PROLONGED_SYNC_FAILURE;
|
||||
this._log.trace("shouldReportError: true (prolonged sync failure).");
|
||||
return true;
|
||||
}
|
||||
|
||||
// We got a 401 mid-sync. Wait for the next sync before actually handling
|
||||
// an error. This assumes that we'll get a 401 again on a login fetch in
|
||||
// order to report the error.
|
||||
if (!Weave.Service.clusterURL) {
|
||||
this._log.trace("shouldReportError: false (no cluster URL; " +
|
||||
"possible node reassignment).");
|
||||
return false;
|
||||
}
|
||||
|
||||
return ([Status.login, Status.sync].indexOf(SERVER_MAINTENANCE) == -1 &&
|
||||
[Status.login, Status.sync].indexOf(LOGIN_FAILED_NETWORK_ERROR) == -1);
|
||||
|
@ -742,7 +788,24 @@ let ErrorHandler = {
|
|||
|
||||
case 401:
|
||||
Weave.Service.logout();
|
||||
Status.login = LOGIN_FAILED_LOGIN_REJECTED;
|
||||
this._log.info("Got 401 response; resetting clusterURL.");
|
||||
Svc.Prefs.reset("clusterURL");
|
||||
|
||||
let delay = 0;
|
||||
if (Svc.Prefs.get("lastSyncReassigned")) {
|
||||
// We got a 401 in the middle of the previous sync, and we just got
|
||||
// another. Login must have succeeded in order for us to get here, so
|
||||
// the password should be correct.
|
||||
// This is likely to be an intermittent server issue, so back off and
|
||||
// give it time to recover.
|
||||
this._log.warn("Last sync also failed for 401. Delaying next sync.");
|
||||
delay = MINIMUM_BACKOFF_INTERVAL;
|
||||
} else {
|
||||
this._log.debug("New mid-sync 401 failure. Making a note.");
|
||||
Svc.Prefs.set("lastSyncReassigned", true);
|
||||
}
|
||||
this._log.info("Attempting to schedule another sync.");
|
||||
SyncScheduler.scheduleNextSync(delay);
|
||||
break;
|
||||
|
||||
case 500:
|
||||
|
|
|
@ -270,7 +270,7 @@ AsyncResource.prototype = {
|
|||
_doRequest: function _doRequest(action, data, callback) {
|
||||
this._log.trace("In _doRequest.");
|
||||
this._callback = callback;
|
||||
let channel = this._channel = this._createRequest();
|
||||
let channel = this._createRequest();
|
||||
|
||||
if ("undefined" != typeof(data))
|
||||
this._data = data;
|
||||
|
@ -303,7 +303,7 @@ AsyncResource.prototype = {
|
|||
channel.asyncOpen(listener, null);
|
||||
},
|
||||
|
||||
_onComplete: function _onComplete(error, data) {
|
||||
_onComplete: function _onComplete(error, data, channel) {
|
||||
this._log.trace("In _onComplete. Error is " + error + ".");
|
||||
|
||||
if (error) {
|
||||
|
@ -312,7 +312,6 @@ AsyncResource.prototype = {
|
|||
}
|
||||
|
||||
this._data = data;
|
||||
let channel = this._channel;
|
||||
let action = channel.requestMethod;
|
||||
|
||||
this._log.trace("Channel: " + channel);
|
||||
|
@ -539,69 +538,29 @@ ChannelListener.prototype = {
|
|||
// Clear the abort timer now that the channel is done.
|
||||
this.abortTimer.clear();
|
||||
|
||||
/**
|
||||
* Shim to help investigate Bug 672878.
|
||||
* We seem to be seeing a situation in which onStopRequest is being called
|
||||
* with a success code, but no mResponseHead yet set (a failure condition).
|
||||
* One possibility, according to bzbarsky, is that the channel status and
|
||||
* the status argument don't match up. This block will log that situation.
|
||||
*
|
||||
* Fetching channel.status should not throw, but if it does requestStatus
|
||||
* will be NS_ERROR_UNEXPECTED, which will cause this method to report
|
||||
* failure.
|
||||
*/
|
||||
let requestStatus = Cr.NS_ERROR_UNEXPECTED;
|
||||
let statusSuccess = Components.isSuccessCode(status);
|
||||
try {
|
||||
// From nsIRequest.
|
||||
requestStatus = channel.status;
|
||||
this._log.trace("Request status is " + requestStatus);
|
||||
} catch (ex) {
|
||||
this._log.warn("Got exception " + Utils.exceptionStr(ex) +
|
||||
" fetching channel.status.");
|
||||
}
|
||||
if (statusSuccess && (status != requestStatus)) {
|
||||
this._log.error("Request status " + requestStatus +
|
||||
" does not match status arg " + status);
|
||||
try {
|
||||
channel.responseStatus;
|
||||
} catch (ex) {
|
||||
this._log.error("... and we got " + Utils.exceptionStr(ex) +
|
||||
" retrieving responseStatus.");
|
||||
}
|
||||
}
|
||||
|
||||
let requestStatusSuccess = Components.isSuccessCode(requestStatus);
|
||||
|
||||
let uri = channel && channel.URI && channel.URI.spec || "<unknown>";
|
||||
this._log.trace("Channel for " + channel.requestMethod + " " + uri + ": " +
|
||||
"isSuccessCode(" + status + ")? " + statusSuccess + ", " +
|
||||
"isSuccessCode(" + requestStatus + ")? " +
|
||||
requestStatusSuccess);
|
||||
"isSuccessCode(" + status + ")? " + statusSuccess);
|
||||
|
||||
if (this._data == '') {
|
||||
this._data = null;
|
||||
}
|
||||
|
||||
// Check both the nsIRequest status and the status we were provided.
|
||||
// If either is unsuccessful, don't take the success branch!
|
||||
//
|
||||
// Pass back the failure code and stop execution. Use Components.Exception()
|
||||
// instead of Error() so the exception is QI-able and can be passed across
|
||||
// XPCOM borders while preserving the status code.
|
||||
if (!statusSuccess || !requestStatusSuccess) {
|
||||
// Pick the code that caused us to fail.
|
||||
let code = statusSuccess ? requestStatus : status;
|
||||
let message = Components.Exception("", code).name;
|
||||
let error = Components.Exception(message, code);
|
||||
this._onComplete(error);
|
||||
if (!statusSuccess) {
|
||||
let message = Components.Exception("", status).name;
|
||||
let error = Components.Exception(message, status);
|
||||
this._onComplete(error, undefined, channel);
|
||||
return;
|
||||
}
|
||||
|
||||
this._log.trace("Channel: flags = " + channel.loadFlags +
|
||||
", URI = " + uri +
|
||||
", HTTP success? " + channel.requestSucceeded);
|
||||
this._onComplete(null, this._data);
|
||||
this._onComplete(null, this._data, channel);
|
||||
},
|
||||
|
||||
onDataAvailable: function Channel_onDataAvail(req, cb, stream, off, count) {
|
||||
|
|
|
@ -370,6 +370,9 @@ RESTRequest.prototype = {
|
|||
/*** nsIStreamListener ***/
|
||||
|
||||
onStartRequest: function onStartRequest(channel) {
|
||||
// Update the channel in case we got redirected.
|
||||
this.channel = channel;
|
||||
|
||||
if (this.status == this.ABORTED) {
|
||||
this._log.trace("Not proceeding with onStartRequest, request was aborted.");
|
||||
return;
|
||||
|
@ -394,6 +397,9 @@ RESTRequest.prototype = {
|
|||
},
|
||||
|
||||
onStopRequest: function onStopRequest(channel, context, statusCode) {
|
||||
// Update the channel in case we got redirected.
|
||||
this.channel = channel;
|
||||
|
||||
if (this.timeoutTimer) {
|
||||
// Clear the abort timer now that the channel is done.
|
||||
this.timeoutTimer.clear();
|
||||
|
@ -406,42 +412,7 @@ RESTRequest.prototype = {
|
|||
}
|
||||
this.status = this.COMPLETED;
|
||||
|
||||
/**
|
||||
* Shim to help investigate Bug 672878.
|
||||
* We seem to be seeing a situation in which onStopRequest is being called
|
||||
* with a success code, but no mResponseHead yet set (a failure condition).
|
||||
* One possibility, according to bzbarsky, is that the channel status and
|
||||
* the status argument don't match up. This block will log that situation.
|
||||
*
|
||||
* Fetching channel.status should not throw, but if it does requestStatus
|
||||
* will be NS_ERROR_UNEXPECTED, which will cause this method to report
|
||||
* failure.
|
||||
*
|
||||
* This code parallels nearly identical shimming in resource.js.
|
||||
*/
|
||||
let requestStatus = Cr.NS_ERROR_UNEXPECTED;
|
||||
let statusSuccess = Components.isSuccessCode(statusCode);
|
||||
try {
|
||||
// From nsIRequest.
|
||||
requestStatus = channel.status;
|
||||
this._log.trace("Request status is " + requestStatus);
|
||||
} catch (ex) {
|
||||
this._log.warn("Got exception " + Utils.exceptionStr(ex) +
|
||||
" fetching channel.status.");
|
||||
}
|
||||
if (statusSuccess && (statusCode != requestStatus)) {
|
||||
this._log.error("Request status " + requestStatus +
|
||||
" does not match status arg " + statusCode);
|
||||
try {
|
||||
channel.responseStatus;
|
||||
} catch (ex) {
|
||||
this._log.error("... and we got " + Utils.exceptionStr(ex) +
|
||||
" retrieving responseStatus.");
|
||||
}
|
||||
}
|
||||
|
||||
let requestStatusSuccess = Components.isSuccessCode(requestStatus);
|
||||
|
||||
let uri = channel && channel.URI && channel.URI.spec || "<unknown>";
|
||||
this._log.trace("Channel for " + channel.requestMethod + " " + uri +
|
||||
" returned status code " + statusCode);
|
||||
|
@ -449,7 +420,7 @@ RESTRequest.prototype = {
|
|||
// Throw the failure code and stop execution. Use Components.Exception()
|
||||
// instead of Error() so the exception is QI-able and can be passed across
|
||||
// XPCOM borders while preserving the status code.
|
||||
if (!statusSuccess || !requestStatusSuccess) {
|
||||
if (!statusSuccess) {
|
||||
let message = Components.Exception("", statusCode).name;
|
||||
let error = Components.Exception(message, statusCode);
|
||||
this.onComplete(error);
|
||||
|
|
|
@ -523,8 +523,10 @@ WeaveSvc.prototype = {
|
|||
return this.serverURL;
|
||||
case 0:
|
||||
case 200:
|
||||
if (node == "null")
|
||||
if (node == "null") {
|
||||
node = null;
|
||||
}
|
||||
this._log.trace("_findCluster successfully returning " + node);
|
||||
return node;
|
||||
default:
|
||||
ErrorHandler.checkServerError(node);
|
||||
|
@ -702,8 +704,11 @@ WeaveSvc.prototype = {
|
|||
return true;
|
||||
}
|
||||
|
||||
} catch (e) {
|
||||
} catch (ex) {
|
||||
// This means no keys are present, or there's a network error.
|
||||
this._log.debug("Failed to fetch and verify keys: "
|
||||
+ Utils.exceptionStr(ex));
|
||||
ErrorHandler.checkServerError(ex);
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
@ -731,9 +736,9 @@ WeaveSvc.prototype = {
|
|||
}
|
||||
|
||||
try {
|
||||
// Make sure we have a cluster to verify against
|
||||
// this is a little weird, if we don't get a node we pretend
|
||||
// to succeed, since that probably means we just don't have storage
|
||||
// Make sure we have a cluster to verify against.
|
||||
// This is a little weird, if we don't get a node we pretend
|
||||
// to succeed, since that probably means we just don't have storage.
|
||||
if (this.clusterURL == "" && !this._setCluster()) {
|
||||
Status.sync = NO_SYNC_NODE_FOUND;
|
||||
Svc.Obs.notify("weave:service:sync:delayed");
|
||||
|
@ -826,6 +831,7 @@ WeaveSvc.prototype = {
|
|||
let uploadRes = wbo.upload(this.cryptoKeysURL);
|
||||
if (uploadRes.status != 200) {
|
||||
this._log.warn("Got status " + uploadRes.status + " uploading new keys. What to do? Throw!");
|
||||
ErrorHandler.checkServerError(uploadRes);
|
||||
throw new Error("Unable to upload symmetric keys.");
|
||||
}
|
||||
this._log.info("Got status " + uploadRes.status + " uploading keys.");
|
||||
|
@ -915,6 +921,7 @@ WeaveSvc.prototype = {
|
|||
}))(),
|
||||
|
||||
startOver: function() {
|
||||
this._log.trace("Invoking Service.startOver.");
|
||||
Svc.Obs.notify("weave:engine:stop-tracking");
|
||||
Status.resetSync();
|
||||
|
||||
|
@ -1367,7 +1374,14 @@ WeaveSvc.prototype = {
|
|||
}
|
||||
|
||||
// Update engines because it might change what we sync.
|
||||
this._updateEnabledEngines();
|
||||
try {
|
||||
this._updateEnabledEngines();
|
||||
} catch (ex) {
|
||||
this._log.debug("Updating enabled engines failed: " +
|
||||
Utils.exceptionStr(ex));
|
||||
ErrorHandler.checkServerError(ex);
|
||||
throw ex;
|
||||
}
|
||||
|
||||
try {
|
||||
for each (let engine in Engines.getEnabled()) {
|
||||
|
@ -1480,22 +1494,18 @@ WeaveSvc.prototype = {
|
|||
_syncEngine: function WeaveSvc__syncEngine(engine) {
|
||||
try {
|
||||
engine.sync();
|
||||
return true;
|
||||
}
|
||||
catch(e) {
|
||||
// Maybe a 401, cluster update perhaps needed?
|
||||
if (e.status == 401) {
|
||||
// Log out and clear the cluster URL pref. That will make us perform
|
||||
// cluster detection and password check on next sync, which handles
|
||||
// both causes of 401s; in either case, we won't proceed with this
|
||||
// sync so return false, but kick off a sync for next time.
|
||||
this.logout();
|
||||
Svc.Prefs.reset("clusterURL");
|
||||
Utils.nextTick(this.sync, this);
|
||||
// Maybe a 401, cluster update perhaps needed?
|
||||
// We rely on ErrorHandler observing the sync failure notification to
|
||||
// schedule another sync and clear node assignment values.
|
||||
// Here we simply want to muffle the exception and return an
|
||||
// appropriate value.
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -1555,22 +1565,34 @@ WeaveSvc.prototype = {
|
|||
CollectionKeys.clear();
|
||||
this.upgradeSyncKey(this.syncID);
|
||||
|
||||
// Wipe the server.
|
||||
let wipeTimestamp = this.wipeServer();
|
||||
|
||||
// Upload a new meta/global record.
|
||||
let meta = new WBORecord("meta", "global");
|
||||
meta.payload.syncID = this.syncID;
|
||||
meta.payload.storageVersion = STORAGE_VERSION;
|
||||
meta.isNew = true;
|
||||
|
||||
this._log.debug("New metadata record: " + JSON.stringify(meta.payload));
|
||||
let resp = new Resource(this.metaURL).put(meta);
|
||||
if (!resp.success)
|
||||
let res = new Resource(this.metaURL);
|
||||
// It would be good to set the X-If-Unmodified-Since header to `timestamp`
|
||||
// for this PUT to ensure at least some level of transactionality.
|
||||
// Unfortunately, the servers don't support it after a wipe right now
|
||||
// (bug 693893), so we're going to defer this until bug 692700.
|
||||
let resp = res.put(meta);
|
||||
if (!resp.success) {
|
||||
// If we got into a race condition, we'll abort the sync this way, too.
|
||||
// That's fine. We'll just wait till the next sync. The client that we're
|
||||
// racing is probably busy uploading stuff right now anyway.
|
||||
throw resp;
|
||||
}
|
||||
Records.set(this.metaURL, meta);
|
||||
|
||||
// Wipe everything we know about except meta because we just uploaded it
|
||||
let collections = [Clients].concat(Engines.getAll()).map(function(engine) {
|
||||
return engine.name;
|
||||
});
|
||||
this.wipeServer(collections);
|
||||
|
||||
// Generate, upload, and download new keys. Do this last so we don't wipe
|
||||
// them...
|
||||
|
@ -1581,36 +1603,51 @@ WeaveSvc.prototype = {
|
|||
* Wipe user data from the server.
|
||||
*
|
||||
* @param collections [optional]
|
||||
* Array of collections to wipe. If not given, all collections are wiped.
|
||||
* Array of collections to wipe. If not given, all collections are
|
||||
* wiped by issuing a DELETE request for `storageURL`.
|
||||
*
|
||||
* @param includeKeys [optional]
|
||||
* If true, keys/pubkey and keys/privkey are deleted from the server.
|
||||
* This is false by default, which will cause the usual upgrade paths
|
||||
* to leave those keys on the server. This is to solve Bug 614737: old
|
||||
* clients check for keys *before* checking storage versions.
|
||||
*
|
||||
* Note that this parameter only has an effect if `collections` is not
|
||||
* passed. If you explicitly pass a list of collections, they will be
|
||||
* processed regardless of the value of `includeKeys`.
|
||||
* @return the server's timestamp of the (last) DELETE.
|
||||
*/
|
||||
wipeServer: function wipeServer(collections, includeKeyPairs)
|
||||
wipeServer: function wipeServer(collections)
|
||||
this._notify("wipe-server", "", function() {
|
||||
let response;
|
||||
if (!collections) {
|
||||
collections = [];
|
||||
let info = new Resource(this.infoURL).get();
|
||||
for (let name in info.obj) {
|
||||
if (includeKeyPairs || (name != "keys"))
|
||||
collections.push(name);
|
||||
// Strip the trailing slash.
|
||||
let res = new Resource(this.storageURL.slice(0, -1));
|
||||
res.setHeader("X-Confirm-Delete", "1");
|
||||
try {
|
||||
response = res.delete();
|
||||
} catch (ex) {
|
||||
this._log.debug("Failed to wipe server: " + Utils.exceptionStr(ex));
|
||||
throw ex;
|
||||
}
|
||||
if (response.status != 200 && response.status != 404) {
|
||||
this._log.debug("Aborting wipeServer. Server responded with " +
|
||||
response.status + " response for " + this.storageURL);
|
||||
throw response;
|
||||
}
|
||||
return response.headers["x-weave-timestamp"];
|
||||
}
|
||||
let timestamp;
|
||||
for each (let name in collections) {
|
||||
let url = this.storageURL + name;
|
||||
let response = new Resource(url).delete();
|
||||
try {
|
||||
response = new Resource(url).delete();
|
||||
} catch (ex) {
|
||||
this._log.debug("Failed to wipe '" + name + "' collection: " +
|
||||
Utils.exceptionStr(ex));
|
||||
throw ex;
|
||||
}
|
||||
if (response.status != 200 && response.status != 404) {
|
||||
throw "Aborting wipeServer. Server responded with "
|
||||
+ response.status + " response for " + url;
|
||||
this._log.debug("Aborting wipeServer. Server responded with " +
|
||||
response.status + " response for " + url);
|
||||
throw response;
|
||||
}
|
||||
if ("x-weave-timestamp" in response.headers) {
|
||||
timestamp = response.headers["x-weave-timestamp"];
|
||||
}
|
||||
}
|
||||
return timestamp;
|
||||
})(),
|
||||
|
||||
/**
|
||||
|
@ -1620,7 +1657,7 @@ WeaveSvc.prototype = {
|
|||
* Array of engine names to wipe. If not given, all engines are used.
|
||||
*/
|
||||
wipeClient: function WeaveSvc_wipeClient(engines)
|
||||
this._catch(this._notify("wipe-client", "", function() {
|
||||
this._notify("wipe-client", "", function() {
|
||||
// If we don't have any engines, reset the service and wipe all engines
|
||||
if (!engines) {
|
||||
// Clear out any service data
|
||||
|
@ -1639,7 +1676,7 @@ WeaveSvc.prototype = {
|
|||
|
||||
// Save the password/passphrase just in-case they aren't restored by sync
|
||||
this.persistLogin();
|
||||
}))(),
|
||||
})(),
|
||||
|
||||
/**
|
||||
* Wipe all remote user data by wiping the server then telling each remote
|
||||
|
@ -1648,8 +1685,8 @@ WeaveSvc.prototype = {
|
|||
* @param engines [optional]
|
||||
* Array of engine names to wipe. If not given, all engines are used.
|
||||
*/
|
||||
wipeRemote: function WeaveSvc_wipeRemote(engines)
|
||||
this._catch(this._notify("wipe-remote", "", function() {
|
||||
wipeRemote: function wipeRemote(engines) {
|
||||
try {
|
||||
// Make sure stuff gets uploaded.
|
||||
this.resetClient(engines);
|
||||
|
||||
|
@ -1667,7 +1704,11 @@ WeaveSvc.prototype = {
|
|||
|
||||
// Make sure the changed clients get updated.
|
||||
Clients.sync();
|
||||
}))(),
|
||||
} catch (ex) {
|
||||
ErrorHandler.checkServerError(ex);
|
||||
throw ex;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Reset local service information like logs, sync times, caches.
|
||||
|
@ -1678,7 +1719,6 @@ WeaveSvc.prototype = {
|
|||
|
||||
// Pretend we've never synced to the server and drop cached data
|
||||
this.syncID = "";
|
||||
Svc.Prefs.reset("lastSync");
|
||||
Records.clearCache();
|
||||
}))(),
|
||||
|
||||
|
|
|
@ -1092,6 +1092,9 @@ let Utils = {
|
|||
// If Master Password is enabled and locked, present a dialog to unlock it.
|
||||
// Return whether the system is unlocked.
|
||||
ensureMPUnlocked: function ensureMPUnlocked() {
|
||||
if (!Utils.mpLocked()) {
|
||||
return true;
|
||||
}
|
||||
let sdr = Cc["@mozilla.org/security/sdr;1"]
|
||||
.getService(Ci.nsISecretDecoderRing);
|
||||
try {
|
||||
|
|
|
@ -17,6 +17,23 @@ let provider = {
|
|||
};
|
||||
Services.dirsvc.QueryInterface(Ci.nsIDirectoryService).registerProvider(provider);
|
||||
|
||||
let timer;
|
||||
function waitForZeroTimer(callback) {
|
||||
// First wait >100ms (nsITimers can take up to that much time to fire, so
|
||||
// we can account for the timer in delayedAutoconnect) and then two event
|
||||
// loop ticks (to account for the Utils.nextTick() in autoConnect).
|
||||
let ticks = 2;
|
||||
function wait() {
|
||||
if (ticks) {
|
||||
ticks -= 1;
|
||||
Utils.nextTick(wait);
|
||||
return;
|
||||
}
|
||||
callback();
|
||||
}
|
||||
timer = Utils.namedTimer(wait, 150, {}, "timer");
|
||||
}
|
||||
|
||||
btoa = Cu.import("resource://services-sync/log4moz.js").btoa;
|
||||
function getTestLogger(component) {
|
||||
return Log4Moz.repository.getLogger("Testing");
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
const Cm = Components.manager;
|
||||
|
||||
// Shared logging for all HTTP server functions.
|
||||
Cu.import("resource://services-sync/log4moz.js");
|
||||
const SYNC_HTTP_LOGGER = "Sync.Test.Server";
|
||||
|
@ -10,6 +12,17 @@ function new_timestamp() {
|
|||
return Math.round(Date.now() / 10) / 100;
|
||||
}
|
||||
|
||||
function return_timestamp(request, response, timestamp) {
|
||||
if (!timestamp) {
|
||||
timestamp = new_timestamp();
|
||||
}
|
||||
let body = "" + timestamp;
|
||||
response.setHeader("X-Weave-Timestamp", body);
|
||||
response.setStatusLine(request.httpVersion, 200, "OK");
|
||||
response.bodyOutputStream.write(body, body.length);
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
function httpd_setup (handlers) {
|
||||
let server = new nsHttpServer();
|
||||
let port = 8080;
|
||||
|
@ -608,7 +621,7 @@ SyncServer.prototype = {
|
|||
* subject to change: see Bug 650435.
|
||||
*/
|
||||
timestamp: function timestamp() {
|
||||
return Math.round(Date.now() / 10) / 100;
|
||||
return new_timestamp();
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -988,3 +1001,51 @@ function serverForUsers(users, contents, callback) {
|
|||
server.start();
|
||||
return server;
|
||||
}
|
||||
|
||||
/**
|
||||
* Proxy auth helpers.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Fake a PAC to prompt a channel replacement.
|
||||
*/
|
||||
let PACSystemSettings = {
|
||||
CID: Components.ID("{5645d2c1-d6d8-4091-b117-fe7ee4027db7}"),
|
||||
contractID: "@mozilla.org/system-proxy-settings;1",
|
||||
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIFactory,
|
||||
Ci.nsISystemProxySettings]),
|
||||
|
||||
createInstance: function createInstance(outer, iid) {
|
||||
if (outer) {
|
||||
throw Cr.NS_ERROR_NO_AGGREGATION;
|
||||
}
|
||||
return this.QueryInterface(iid);
|
||||
},
|
||||
|
||||
lockFactory: function lockFactory(lock) {
|
||||
throw Cr.NS_ERROR_NOT_IMPLEMENTED;
|
||||
},
|
||||
|
||||
// Replace this URI for each test to avoid caching. We want to ensure that
|
||||
// each test gets a completely fresh setup.
|
||||
PACURI: null,
|
||||
getProxyForURI: function getProxyForURI(aURI) {
|
||||
throw Cr.NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
};
|
||||
|
||||
function installFakePAC() {
|
||||
_("Installing fake PAC.");
|
||||
Cm.nsIComponentRegistrar
|
||||
.registerFactory(PACSystemSettings.CID,
|
||||
"Fake system proxy-settings",
|
||||
PACSystemSettings.contractID,
|
||||
PACSystemSettings);
|
||||
}
|
||||
|
||||
function uninstallFakePAC() {
|
||||
_("Uninstalling fake PAC.");
|
||||
let CID = PACSystemSettings.CID;
|
||||
Cm.nsIComponentRegistrar.unregisterFactory(CID, PACSystemSettings);
|
||||
}
|
||||
|
|
|
@ -20,8 +20,7 @@ const NON_PROLONGED_ERROR_DURATION =
|
|||
(Svc.Prefs.get('errorhandler.networkFailureReportTimeout') / 2) * 1000;
|
||||
|
||||
function setLastSync(lastSyncValue) {
|
||||
Svc.Prefs.set("lastSync", (new Date(Date.now() -
|
||||
lastSyncValue)).toString());
|
||||
Svc.Prefs.set("lastSync", (new Date(Date.now() - lastSyncValue)).toString());
|
||||
}
|
||||
|
||||
function CatapultEngine() {
|
||||
|
@ -31,7 +30,9 @@ CatapultEngine.prototype = {
|
|||
__proto__: SyncEngine.prototype,
|
||||
exception: null, // tests fill this in
|
||||
_sync: function _sync() {
|
||||
throw this.exception;
|
||||
if (this.exception) {
|
||||
throw this.exception;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -69,7 +70,9 @@ function sync_httpd_setup() {
|
|||
syncID: Service.syncID,
|
||||
storageVersion: STORAGE_VERSION,
|
||||
engines: {clients: {version: Clients.version,
|
||||
syncID: Clients.syncID}}
|
||||
syncID: Clients.syncID},
|
||||
catapult: {version: Engines.get("catapult").version,
|
||||
syncID: Engines.get("catapult").syncID}}
|
||||
});
|
||||
let clientsColl = new ServerCollection({}, true);
|
||||
|
||||
|
@ -79,23 +82,37 @@ function sync_httpd_setup() {
|
|||
|
||||
let handler_401 = httpd_handler(401, "Unauthorized");
|
||||
return httpd_setup({
|
||||
// Normal server behaviour.
|
||||
"/1.1/johndoe/storage/meta/global": upd("meta", global.handler()),
|
||||
"/1.1/johndoe/info/collections": collectionsHelper.handler,
|
||||
"/1.1/johndoe/storage/crypto/keys":
|
||||
upd("crypto", (new ServerWBO("keys")).handler()),
|
||||
"/1.1/johndoe/storage/clients": upd("clients", clientsColl.handler()),
|
||||
|
||||
// Credentials are wrong or node reallocated.
|
||||
"/1.1/janedoe/storage/meta/global": handler_401,
|
||||
"/1.1/janedoe/info/collections": handler_401,
|
||||
|
||||
"/maintenance/1.1/johnsmith/info/collections": service_unavailable,
|
||||
// Maintenance or overloaded (503 + Retry-After) at info/collections.
|
||||
"/maintenance/1.1/broken.info/info/collections": service_unavailable,
|
||||
|
||||
"/maintenance/1.1/janesmith/storage/meta/global": service_unavailable,
|
||||
"/maintenance/1.1/janesmith/info/collections": collectionsHelper.handler,
|
||||
// Maintenance or overloaded (503 + Retry-After) at meta/global.
|
||||
"/maintenance/1.1/broken.meta/storage/meta/global": service_unavailable,
|
||||
"/maintenance/1.1/broken.meta/info/collections": collectionsHelper.handler,
|
||||
|
||||
"/maintenance/1.1/foo/storage/meta/global": upd("meta", global.handler()),
|
||||
"/maintenance/1.1/foo/info/collections": collectionsHelper.handler,
|
||||
"/maintenance/1.1/foo/storage/crypto/keys": service_unavailable,
|
||||
// Maintenance or overloaded (503 + Retry-After) at crypto/keys.
|
||||
"/maintenance/1.1/broken.keys/storage/meta/global": upd("meta", global.handler()),
|
||||
"/maintenance/1.1/broken.keys/info/collections": collectionsHelper.handler,
|
||||
"/maintenance/1.1/broken.keys/storage/crypto/keys": service_unavailable,
|
||||
|
||||
// Maintenance or overloaded (503 + Retry-After) at wiping collection.
|
||||
"/maintenance/1.1/broken.wipe/info/collections": collectionsHelper.handler,
|
||||
"/maintenance/1.1/broken.wipe/storage/meta/global": upd("meta", global.handler()),
|
||||
"/maintenance/1.1/broken.wipe/storage/crypto/keys":
|
||||
upd("crypto", (new ServerWBO("keys")).handler()),
|
||||
"/maintenance/1.1/broken.wipe/storage": service_unavailable,
|
||||
"/maintenance/1.1/broken.wipe/storage/clients": upd("clients", clientsColl.handler()),
|
||||
"/maintenance/1.1/broken.wipe/storage/catapult": service_unavailable
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -104,7 +121,10 @@ function setUp() {
|
|||
Service.password = "ilovejane";
|
||||
Service.passphrase = "abcdeabcdeabcdeabcdeabcdea";
|
||||
Service.clusterURL = "http://localhost:8080/";
|
||||
return generateAndUploadKeys();
|
||||
}
|
||||
|
||||
function generateAndUploadKeys() {
|
||||
generateNewKeys();
|
||||
let serverKeys = CollectionKeys.asWBO("crypto", "keys");
|
||||
serverKeys.encrypt(Service.syncKeyBundle);
|
||||
|
@ -126,16 +146,34 @@ add_test(function test_401_logout() {
|
|||
do_check_eq(Status.sync, SYNC_SUCCEEDED);
|
||||
do_check_true(Service.isLoggedIn);
|
||||
|
||||
Svc.Obs.add("weave:service:sync:error", onSyncError);
|
||||
function onSyncError() {
|
||||
_("Got weave:service:sync:error in first sync.");
|
||||
Svc.Obs.remove("weave:service:sync:error", onSyncError);
|
||||
|
||||
// Wait for the automatic next sync.
|
||||
function onLoginError() {
|
||||
_("Got weave:service:login:error in second sync.");
|
||||
Svc.Obs.remove("weave:service:login:error", onLoginError);
|
||||
|
||||
do_check_eq(Status.login, LOGIN_FAILED_LOGIN_REJECTED);
|
||||
do_check_false(Service.isLoggedIn);
|
||||
|
||||
// Clean up.
|
||||
Utils.nextTick(function () {
|
||||
Service.startOver();
|
||||
server.stop(run_next_test);
|
||||
});
|
||||
}
|
||||
Svc.Obs.add("weave:service:login:error", onLoginError);
|
||||
}
|
||||
|
||||
// Make sync fail due to login rejected.
|
||||
Service.username = "janedoe";
|
||||
|
||||
_("Starting first sync.");
|
||||
Service.sync();
|
||||
|
||||
do_check_eq(Status.login, LOGIN_FAILED_LOGIN_REJECTED);
|
||||
do_check_false(Service.isLoggedIn);
|
||||
|
||||
// Clean up.
|
||||
Service.startOver();
|
||||
server.stop(run_next_test);
|
||||
_("First sync done.");
|
||||
});
|
||||
|
||||
add_test(function test_credentials_changed_logout() {
|
||||
|
@ -178,6 +216,10 @@ add_test(function test_shouldReportError() {
|
|||
Status.login = MASTER_PASSWORD_LOCKED;
|
||||
do_check_false(ErrorHandler.shouldReportError());
|
||||
|
||||
// Give ourselves a clusterURL so that the temporary 401 no-error situation
|
||||
// doesn't come into play.
|
||||
Service.clusterURL = "http://localhost:8080/";
|
||||
|
||||
// Test dontIgnoreErrors, non-network, non-prolonged, login error reported
|
||||
Status.resetSync();
|
||||
setLastSync(NON_PROLONGED_ERROR_DURATION);
|
||||
|
@ -733,9 +775,9 @@ add_test(function test_info_collections_login_server_maintenance_error() {
|
|||
let server = sync_httpd_setup();
|
||||
setUp();
|
||||
|
||||
Service.username = "broken.info";
|
||||
Service.clusterURL = "http://localhost:8080/maintenance/";
|
||||
|
||||
Service.username = "johnsmith";
|
||||
let backoffInterval;
|
||||
Svc.Obs.add("weave:service:backoff:interval", function observe(subject, data) {
|
||||
Svc.Obs.remove("weave:service:backoff:interval", observe);
|
||||
|
@ -772,9 +814,9 @@ add_test(function test_meta_global_login_server_maintenance_error() {
|
|||
let server = sync_httpd_setup();
|
||||
setUp();
|
||||
|
||||
Service.username = "broken.meta";
|
||||
Service.clusterURL = "http://localhost:8080/maintenance/";
|
||||
|
||||
Service.username = "janesmith";
|
||||
let backoffInterval;
|
||||
Svc.Obs.add("weave:service:backoff:interval", function observe(subject, data) {
|
||||
Svc.Obs.remove("weave:service:backoff:interval", observe);
|
||||
|
@ -811,8 +853,8 @@ add_test(function test_crypto_keys_login_server_maintenance_error() {
|
|||
let server = sync_httpd_setup();
|
||||
setUp();
|
||||
|
||||
Service.username = "broken.keys";
|
||||
Service.clusterURL = "http://localhost:8080/maintenance/";
|
||||
Service.username = "foo";
|
||||
// Force re-download of keys
|
||||
CollectionKeys.clear();
|
||||
|
||||
|
@ -878,9 +920,9 @@ add_test(function test_info_collections_login_prolonged_server_maintenance_error
|
|||
let server = sync_httpd_setup();
|
||||
setUp();
|
||||
|
||||
Service.username = "broken.info";
|
||||
Service.clusterURL = "http://localhost:8080/maintenance/";
|
||||
|
||||
Service.username = "johnsmith";
|
||||
let backoffInterval;
|
||||
Svc.Obs.add("weave:service:backoff:interval", function observe(subject, data) {
|
||||
Svc.Obs.remove("weave:service:backoff:interval", observe);
|
||||
|
@ -910,9 +952,9 @@ add_test(function test_meta_global_login_prolonged_server_maintenance_error(){
|
|||
let server = sync_httpd_setup();
|
||||
setUp();
|
||||
|
||||
Service.username = "broken.meta";
|
||||
Service.clusterURL = "http://localhost:8080/maintenance/";
|
||||
|
||||
Service.username = "janesmith";
|
||||
let backoffInterval;
|
||||
Svc.Obs.add("weave:service:backoff:interval", function observe(subject, data) {
|
||||
Svc.Obs.remove("weave:service:backoff:interval", observe);
|
||||
|
@ -937,13 +979,13 @@ add_test(function test_meta_global_login_prolonged_server_maintenance_error(){
|
|||
Service.sync();
|
||||
});
|
||||
|
||||
add_test(function test_crypto_keys_login_prolonged_server_maintenance_error(){
|
||||
add_test(function test_download_crypto_keys_login_prolonged_server_maintenance_error(){
|
||||
// Test crypto/keys prolonged server maintenance errors are reported.
|
||||
let server = sync_httpd_setup();
|
||||
setUp();
|
||||
|
||||
Service.username = "broken.keys";
|
||||
Service.clusterURL = "http://localhost:8080/maintenance/";
|
||||
Service.username = "foo";
|
||||
// Force re-download of keys
|
||||
CollectionKeys.clear();
|
||||
|
||||
|
@ -971,6 +1013,116 @@ add_test(function test_crypto_keys_login_prolonged_server_maintenance_error(){
|
|||
Service.sync();
|
||||
});
|
||||
|
||||
add_test(function test_upload_crypto_keys_login_prolonged_server_maintenance_error(){
|
||||
// Test crypto/keys prolonged server maintenance errors are reported.
|
||||
let server = sync_httpd_setup();
|
||||
|
||||
// Start off with an empty account, do not upload a key.
|
||||
Service.username = "broken.keys";
|
||||
Service.password = "ilovejane";
|
||||
Service.passphrase = "abcdeabcdeabcdeabcdeabcdea";
|
||||
Service.clusterURL = "http://localhost:8080/maintenance/";
|
||||
|
||||
let backoffInterval;
|
||||
Svc.Obs.add("weave:service:backoff:interval", function observe(subject, data) {
|
||||
Svc.Obs.remove("weave:service:backoff:interval", observe);
|
||||
backoffInterval = subject;
|
||||
});
|
||||
|
||||
Svc.Obs.add("weave:ui:login:error", function onUIUpdate() {
|
||||
Svc.Obs.remove("weave:ui:login:error", onUIUpdate);
|
||||
do_check_true(Status.enforceBackoff);
|
||||
do_check_eq(backoffInterval, 42);
|
||||
do_check_eq(Status.service, SYNC_FAILED);
|
||||
do_check_eq(Status.sync, PROLONGED_SYNC_FAILURE);
|
||||
|
||||
clean();
|
||||
server.stop(run_next_test);
|
||||
});
|
||||
|
||||
do_check_false(Status.enforceBackoff);
|
||||
do_check_eq(Status.service, STATUS_OK);
|
||||
|
||||
setLastSync(PROLONGED_ERROR_DURATION);
|
||||
Service.sync();
|
||||
});
|
||||
|
||||
add_test(function test_wipeServer_login_prolonged_server_maintenance_error(){
|
||||
// Test that we report prolonged server maintenance errors that occur whilst
|
||||
// wiping the server.
|
||||
let server = sync_httpd_setup();
|
||||
|
||||
// Start off with an empty account, do not upload a key.
|
||||
Service.username = "broken.wipe";
|
||||
Service.password = "ilovejane";
|
||||
Service.passphrase = "abcdeabcdeabcdeabcdeabcdea";
|
||||
Service.clusterURL = "http://localhost:8080/maintenance/";
|
||||
|
||||
let backoffInterval;
|
||||
Svc.Obs.add("weave:service:backoff:interval", function observe(subject, data) {
|
||||
Svc.Obs.remove("weave:service:backoff:interval", observe);
|
||||
backoffInterval = subject;
|
||||
});
|
||||
|
||||
Svc.Obs.add("weave:ui:login:error", function onUIUpdate() {
|
||||
Svc.Obs.remove("weave:ui:login:error", onUIUpdate);
|
||||
do_check_true(Status.enforceBackoff);
|
||||
do_check_eq(backoffInterval, 42);
|
||||
do_check_eq(Status.service, SYNC_FAILED);
|
||||
do_check_eq(Status.sync, PROLONGED_SYNC_FAILURE);
|
||||
|
||||
clean();
|
||||
server.stop(run_next_test);
|
||||
});
|
||||
|
||||
do_check_false(Status.enforceBackoff);
|
||||
do_check_eq(Status.service, STATUS_OK);
|
||||
|
||||
setLastSync(PROLONGED_ERROR_DURATION);
|
||||
Service.sync();
|
||||
});
|
||||
|
||||
add_test(function test_wipeRemote_prolonged_server_maintenance_error(){
|
||||
// Test that we report prolonged server maintenance errors that occur whilst
|
||||
// wiping all remote devices.
|
||||
let server = sync_httpd_setup();
|
||||
|
||||
Service.username = "broken.wipe";
|
||||
Service.password = "ilovejane";
|
||||
Service.passphrase = "abcdeabcdeabcdeabcdeabcdea";
|
||||
Service.clusterURL = "http://localhost:8080/maintenance/";
|
||||
generateAndUploadKeys();
|
||||
|
||||
let engine = Engines.get("catapult");
|
||||
engine.exception = null;
|
||||
engine.enabled = true;
|
||||
|
||||
let backoffInterval;
|
||||
Svc.Obs.add("weave:service:backoff:interval", function observe(subject, data) {
|
||||
Svc.Obs.remove("weave:service:backoff:interval", observe);
|
||||
backoffInterval = subject;
|
||||
});
|
||||
|
||||
Svc.Obs.add("weave:ui:sync:error", function onUIUpdate() {
|
||||
Svc.Obs.remove("weave:ui:sync:error", onUIUpdate);
|
||||
do_check_true(Status.enforceBackoff);
|
||||
do_check_eq(backoffInterval, 42);
|
||||
do_check_eq(Status.service, SYNC_FAILED);
|
||||
do_check_eq(Status.sync, PROLONGED_SYNC_FAILURE);
|
||||
do_check_eq(Svc.Prefs.get("firstSync"), "wipeRemote");
|
||||
|
||||
clean();
|
||||
server.stop(run_next_test);
|
||||
});
|
||||
|
||||
do_check_false(Status.enforceBackoff);
|
||||
do_check_eq(Status.service, STATUS_OK);
|
||||
|
||||
Svc.Prefs.set("firstSync", "wipeRemote");
|
||||
setLastSync(PROLONGED_ERROR_DURATION);
|
||||
Service.sync();
|
||||
});
|
||||
|
||||
add_test(function test_sync_syncAndReportErrors_server_maintenance_error() {
|
||||
// Test server maintenance errors are reported
|
||||
// when calling syncAndReportErrors.
|
||||
|
@ -1004,9 +1156,9 @@ add_test(function test_info_collections_login_syncAndReportErrors_server_mainten
|
|||
let server = sync_httpd_setup();
|
||||
setUp();
|
||||
|
||||
Service.username = "broken.info";
|
||||
Service.clusterURL = "http://localhost:8080/maintenance/";
|
||||
|
||||
Service.username = "johnsmith";
|
||||
let backoffInterval;
|
||||
Svc.Obs.add("weave:service:backoff:interval", function observe(subject, data) {
|
||||
Svc.Obs.remove("weave:service:backoff:interval", observe);
|
||||
|
@ -1037,9 +1189,9 @@ add_test(function test_meta_global_login_syncAndReportErrors_server_maintenance_
|
|||
let server = sync_httpd_setup();
|
||||
setUp();
|
||||
|
||||
Service.username = "broken.meta";
|
||||
Service.clusterURL = "http://localhost:8080/maintenance/";
|
||||
|
||||
Service.username = "janesmith";
|
||||
let backoffInterval;
|
||||
Svc.Obs.add("weave:service:backoff:interval", function observe(subject, data) {
|
||||
Svc.Obs.remove("weave:service:backoff:interval", observe);
|
||||
|
@ -1064,14 +1216,14 @@ add_test(function test_meta_global_login_syncAndReportErrors_server_maintenance_
|
|||
ErrorHandler.syncAndReportErrors();
|
||||
});
|
||||
|
||||
add_test(function test_crypto_keys_login_syncAndReportErrors_server_maintenance_error() {
|
||||
add_test(function test_download_crypto_keys_login_syncAndReportErrors_server_maintenance_error() {
|
||||
// Test crypto/keys server maintenance errors are reported
|
||||
// when calling syncAndReportErrors.
|
||||
let server = sync_httpd_setup();
|
||||
setUp();
|
||||
|
||||
Service.username = "broken.keys";
|
||||
Service.clusterURL = "http://localhost:8080/maintenance/";
|
||||
Service.username = "foo";
|
||||
// Force re-download of keys
|
||||
CollectionKeys.clear();
|
||||
|
||||
|
@ -1099,6 +1251,117 @@ add_test(function test_crypto_keys_login_syncAndReportErrors_server_maintenance_
|
|||
ErrorHandler.syncAndReportErrors();
|
||||
});
|
||||
|
||||
add_test(function test_upload_crypto_keys_login_syncAndReportErrors_server_maintenance_error() {
|
||||
// Test crypto/keys server maintenance errors are reported
|
||||
// when calling syncAndReportErrors.
|
||||
let server = sync_httpd_setup();
|
||||
|
||||
// Start off with an empty account, do not upload a key.
|
||||
Service.username = "broken.keys";
|
||||
Service.password = "ilovejane";
|
||||
Service.passphrase = "abcdeabcdeabcdeabcdeabcdea";
|
||||
Service.clusterURL = "http://localhost:8080/maintenance/";
|
||||
|
||||
let backoffInterval;
|
||||
Svc.Obs.add("weave:service:backoff:interval", function observe(subject, data) {
|
||||
Svc.Obs.remove("weave:service:backoff:interval", observe);
|
||||
backoffInterval = subject;
|
||||
});
|
||||
|
||||
Svc.Obs.add("weave:ui:login:error", function onUIUpdate() {
|
||||
Svc.Obs.remove("weave:ui:login:error", onUIUpdate);
|
||||
do_check_true(Status.enforceBackoff);
|
||||
do_check_eq(backoffInterval, 42);
|
||||
do_check_eq(Status.service, LOGIN_FAILED);
|
||||
do_check_eq(Status.login, SERVER_MAINTENANCE);
|
||||
|
||||
clean();
|
||||
server.stop(run_next_test);
|
||||
});
|
||||
|
||||
do_check_false(Status.enforceBackoff);
|
||||
do_check_eq(Status.service, STATUS_OK);
|
||||
|
||||
setLastSync(NON_PROLONGED_ERROR_DURATION);
|
||||
ErrorHandler.syncAndReportErrors();
|
||||
});
|
||||
|
||||
add_test(function test_wipeServer_login_syncAndReportErrors_server_maintenance_error() {
|
||||
// Test crypto/keys server maintenance errors are reported
|
||||
// when calling syncAndReportErrors.
|
||||
let server = sync_httpd_setup();
|
||||
|
||||
// Start off with an empty account, do not upload a key.
|
||||
Service.username = "broken.wipe";
|
||||
Service.password = "ilovejane";
|
||||
Service.passphrase = "abcdeabcdeabcdeabcdeabcdea";
|
||||
Service.clusterURL = "http://localhost:8080/maintenance/";
|
||||
|
||||
let backoffInterval;
|
||||
Svc.Obs.add("weave:service:backoff:interval", function observe(subject, data) {
|
||||
Svc.Obs.remove("weave:service:backoff:interval", observe);
|
||||
backoffInterval = subject;
|
||||
});
|
||||
|
||||
Svc.Obs.add("weave:ui:login:error", function onUIUpdate() {
|
||||
Svc.Obs.remove("weave:ui:login:error", onUIUpdate);
|
||||
do_check_true(Status.enforceBackoff);
|
||||
do_check_eq(backoffInterval, 42);
|
||||
do_check_eq(Status.service, LOGIN_FAILED);
|
||||
do_check_eq(Status.login, SERVER_MAINTENANCE);
|
||||
|
||||
clean();
|
||||
server.stop(run_next_test);
|
||||
});
|
||||
|
||||
do_check_false(Status.enforceBackoff);
|
||||
do_check_eq(Status.service, STATUS_OK);
|
||||
|
||||
setLastSync(NON_PROLONGED_ERROR_DURATION);
|
||||
ErrorHandler.syncAndReportErrors();
|
||||
});
|
||||
|
||||
add_test(function test_wipeRemote_syncAndReportErrors_server_maintenance_error(){
|
||||
// Test that we report prolonged server maintenance errors that occur whilst
|
||||
// wiping all remote devices.
|
||||
let server = sync_httpd_setup();
|
||||
|
||||
Service.username = "broken.wipe";
|
||||
Service.password = "ilovejane";
|
||||
Service.passphrase = "abcdeabcdeabcdeabcdeabcdea";
|
||||
Service.clusterURL = "http://localhost:8080/maintenance/";
|
||||
generateAndUploadKeys();
|
||||
|
||||
let engine = Engines.get("catapult");
|
||||
engine.exception = null;
|
||||
engine.enabled = true;
|
||||
|
||||
let backoffInterval;
|
||||
Svc.Obs.add("weave:service:backoff:interval", function observe(subject, data) {
|
||||
Svc.Obs.remove("weave:service:backoff:interval", observe);
|
||||
backoffInterval = subject;
|
||||
});
|
||||
|
||||
Svc.Obs.add("weave:ui:sync:error", function onUIUpdate() {
|
||||
Svc.Obs.remove("weave:ui:sync:error", onUIUpdate);
|
||||
do_check_true(Status.enforceBackoff);
|
||||
do_check_eq(backoffInterval, 42);
|
||||
do_check_eq(Status.service, SYNC_FAILED);
|
||||
do_check_eq(Status.sync, SERVER_MAINTENANCE);
|
||||
do_check_eq(Svc.Prefs.get("firstSync"), "wipeRemote");
|
||||
|
||||
clean();
|
||||
server.stop(run_next_test);
|
||||
});
|
||||
|
||||
do_check_false(Status.enforceBackoff);
|
||||
do_check_eq(Status.service, STATUS_OK);
|
||||
|
||||
Svc.Prefs.set("firstSync", "wipeRemote");
|
||||
setLastSync(NON_PROLONGED_ERROR_DURATION);
|
||||
ErrorHandler.syncAndReportErrors();
|
||||
});
|
||||
|
||||
add_test(function test_sync_syncAndReportErrors_prolonged_server_maintenance_error() {
|
||||
// Test prolonged server maintenance errors are
|
||||
// reported when calling syncAndReportErrors.
|
||||
|
@ -1132,9 +1395,9 @@ add_test(function test_info_collections_login_syncAndReportErrors_prolonged_serv
|
|||
let server = sync_httpd_setup();
|
||||
setUp();
|
||||
|
||||
Service.username = "broken.info";
|
||||
Service.clusterURL = "http://localhost:8080/maintenance/";
|
||||
|
||||
Service.username = "johnsmith";
|
||||
let backoffInterval;
|
||||
Svc.Obs.add("weave:service:backoff:interval", function observe(subject, data) {
|
||||
Svc.Obs.remove("weave:service:backoff:interval", observe);
|
||||
|
@ -1165,9 +1428,9 @@ add_test(function test_meta_global_login_syncAndReportErrors_prolonged_server_ma
|
|||
let server = sync_httpd_setup();
|
||||
setUp();
|
||||
|
||||
Service.username = "broken.meta";
|
||||
Service.clusterURL = "http://localhost:8080/maintenance/";
|
||||
|
||||
Service.username = "janesmith";
|
||||
let backoffInterval;
|
||||
Svc.Obs.add("weave:service:backoff:interval", function observe(subject, data) {
|
||||
Svc.Obs.remove("weave:service:backoff:interval", observe);
|
||||
|
@ -1192,14 +1455,14 @@ add_test(function test_meta_global_login_syncAndReportErrors_prolonged_server_ma
|
|||
ErrorHandler.syncAndReportErrors();
|
||||
});
|
||||
|
||||
add_test(function test_crypto_keys_login_syncAndReportErrors_prolonged_server_maintenance_error() {
|
||||
add_test(function test_download_crypto_keys_login_syncAndReportErrors_prolonged_server_maintenance_error() {
|
||||
// Test crypto/keys server maintenance errors are reported
|
||||
// when calling syncAndReportErrors.
|
||||
let server = sync_httpd_setup();
|
||||
setUp();
|
||||
|
||||
Service.username = "broken.keys";
|
||||
Service.clusterURL = "http://localhost:8080/maintenance/";
|
||||
Service.username = "foo";
|
||||
// Force re-download of keys
|
||||
CollectionKeys.clear();
|
||||
|
||||
|
@ -1227,6 +1490,76 @@ add_test(function test_crypto_keys_login_syncAndReportErrors_prolonged_server_ma
|
|||
ErrorHandler.syncAndReportErrors();
|
||||
});
|
||||
|
||||
add_test(function test_upload_crypto_keys_login_syncAndReportErrors_prolonged_server_maintenance_error() {
|
||||
// Test crypto/keys server maintenance errors are reported
|
||||
// when calling syncAndReportErrors.
|
||||
let server = sync_httpd_setup();
|
||||
|
||||
// Start off with an empty account, do not upload a key.
|
||||
Service.username = "broken.keys";
|
||||
Service.password = "ilovejane";
|
||||
Service.passphrase = "abcdeabcdeabcdeabcdeabcdea";
|
||||
Service.clusterURL = "http://localhost:8080/maintenance/";
|
||||
|
||||
let backoffInterval;
|
||||
Svc.Obs.add("weave:service:backoff:interval", function observe(subject, data) {
|
||||
Svc.Obs.remove("weave:service:backoff:interval", observe);
|
||||
backoffInterval = subject;
|
||||
});
|
||||
|
||||
Svc.Obs.add("weave:ui:login:error", function onUIUpdate() {
|
||||
Svc.Obs.remove("weave:ui:login:error", onUIUpdate);
|
||||
do_check_true(Status.enforceBackoff);
|
||||
do_check_eq(backoffInterval, 42);
|
||||
do_check_eq(Status.service, LOGIN_FAILED);
|
||||
do_check_eq(Status.login, SERVER_MAINTENANCE);
|
||||
|
||||
clean();
|
||||
server.stop(run_next_test);
|
||||
});
|
||||
|
||||
do_check_false(Status.enforceBackoff);
|
||||
do_check_eq(Status.service, STATUS_OK);
|
||||
|
||||
setLastSync(PROLONGED_ERROR_DURATION);
|
||||
ErrorHandler.syncAndReportErrors();
|
||||
});
|
||||
|
||||
add_test(function test_wipeServer_login_syncAndReportErrors_prolonged_server_maintenance_error() {
|
||||
// Test crypto/keys server maintenance errors are reported
|
||||
// when calling syncAndReportErrors.
|
||||
let server = sync_httpd_setup();
|
||||
|
||||
// Start off with an empty account, do not upload a key.
|
||||
Service.username = "broken.wipe";
|
||||
Service.password = "ilovejane";
|
||||
Service.passphrase = "abcdeabcdeabcdeabcdeabcdea";
|
||||
Service.clusterURL = "http://localhost:8080/maintenance/";
|
||||
|
||||
let backoffInterval;
|
||||
Svc.Obs.add("weave:service:backoff:interval", function observe(subject, data) {
|
||||
Svc.Obs.remove("weave:service:backoff:interval", observe);
|
||||
backoffInterval = subject;
|
||||
});
|
||||
|
||||
Svc.Obs.add("weave:ui:login:error", function onUIUpdate() {
|
||||
Svc.Obs.remove("weave:ui:login:error", onUIUpdate);
|
||||
do_check_true(Status.enforceBackoff);
|
||||
do_check_eq(backoffInterval, 42);
|
||||
do_check_eq(Status.service, LOGIN_FAILED);
|
||||
do_check_eq(Status.login, SERVER_MAINTENANCE);
|
||||
|
||||
clean();
|
||||
server.stop(run_next_test);
|
||||
});
|
||||
|
||||
do_check_false(Status.enforceBackoff);
|
||||
do_check_eq(Status.service, STATUS_OK);
|
||||
|
||||
setLastSync(PROLONGED_ERROR_DURATION);
|
||||
ErrorHandler.syncAndReportErrors();
|
||||
});
|
||||
|
||||
add_test(function test_sync_engine_generic_fail() {
|
||||
let server = sync_httpd_setup();
|
||||
|
||||
|
|
|
@ -0,0 +1,487 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
_("Test that node reassignment responses are respected on all kinds of " +
|
||||
"requests.");
|
||||
|
||||
// Don't sync any engines by default.
|
||||
Svc.DefaultPrefs.set("registerEngines", "")
|
||||
|
||||
Cu.import("resource://services-sync/constants.js");
|
||||
Cu.import("resource://services-sync/policies.js");
|
||||
Cu.import("resource://services-sync/rest.js");
|
||||
Cu.import("resource://services-sync/service.js");
|
||||
Cu.import("resource://services-sync/status.js");
|
||||
Cu.import("resource://services-sync/log4moz.js");
|
||||
|
||||
function run_test() {
|
||||
Log4Moz.repository.getLogger("Sync.AsyncResource").level = Log4Moz.Level.Trace;
|
||||
Log4Moz.repository.getLogger("Sync.ErrorHandler").level = Log4Moz.Level.Trace;
|
||||
Log4Moz.repository.getLogger("Sync.Resource").level = Log4Moz.Level.Trace;
|
||||
Log4Moz.repository.getLogger("Sync.RESTRequest").level = Log4Moz.Level.Trace;
|
||||
Log4Moz.repository.getLogger("Sync.Service").level = Log4Moz.Level.Trace;
|
||||
Log4Moz.repository.getLogger("Sync.SyncScheduler").level = Log4Moz.Level.Trace;
|
||||
initTestLogging();
|
||||
|
||||
Engines.register(RotaryEngine);
|
||||
|
||||
// None of the failures in this file should result in a UI error.
|
||||
function onUIError() {
|
||||
do_throw("Errors should not be presented in the UI.");
|
||||
}
|
||||
Svc.Obs.add("weave:ui:login:error", onUIError);
|
||||
Svc.Obs.add("weave:ui:sync:error", onUIError);
|
||||
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
/**
|
||||
* Emulate the following Zeus config:
|
||||
* $draining = data.get($prefix . $host . " draining");
|
||||
* if ($draining == "drain.") {
|
||||
* log.warn($log_host_db_status . " migrating=1 (node-reassignment)" .
|
||||
* $log_suffix);
|
||||
* http.sendResponse("401 Node reassignment", $content_type,
|
||||
* '"server request: node reassignment"', "");
|
||||
* }
|
||||
*/
|
||||
const reassignBody = "\"server request: node reassignment\"";
|
||||
|
||||
// API-compatible with SyncServer handler. Bind `handler` to something to use
|
||||
// as a ServerCollection handler.
|
||||
function handleReassign(handler, req, resp) {
|
||||
resp.setStatusLine(req.httpVersion, 401, "Node reassignment");
|
||||
resp.setHeader("Content-Type", "application/json");
|
||||
resp.bodyOutputStream.write(reassignBody, reassignBody.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* A node assignment handler.
|
||||
*/
|
||||
const newNodeBody = "http://localhost:8080/";
|
||||
function installNodeHandler(server, next) {
|
||||
function handleNodeRequest(req, resp) {
|
||||
_("Client made a request for a node reassignment.");
|
||||
resp.setStatusLine(req.httpVersion, 200, "OK");
|
||||
resp.setHeader("Content-Type", "text/plain");
|
||||
resp.bodyOutputStream.write(newNodeBody, newNodeBody.length);
|
||||
Utils.nextTick(next);
|
||||
}
|
||||
let nodePath = "/user/1.0/johndoe/node/weave";
|
||||
server.server.registerPathHandler(nodePath, handleNodeRequest);
|
||||
_("Registered node handler at " + nodePath);
|
||||
}
|
||||
|
||||
function prepareServer() {
|
||||
Service.username = "johndoe";
|
||||
Service.passphrase = "abcdeabcdeabcdeabcdeabcdea";
|
||||
Service.password = "ilovejane";
|
||||
Service.serverURL = "http://localhost:8080/";
|
||||
Service.clusterURL = "http://localhost:8080/";
|
||||
|
||||
do_check_eq(Service.userAPI, "http://localhost:8080/user/1.0/");
|
||||
let server = new SyncServer();
|
||||
server.registerUser("johndoe");
|
||||
server.start();
|
||||
return server;
|
||||
}
|
||||
|
||||
function getReassigned() {
|
||||
try {
|
||||
return Services.prefs.getBoolPref("services.sync.lastSyncReassigned");
|
||||
} catch (ex if (ex.result == Cr.NS_ERROR_UNEXPECTED)) {
|
||||
return false;
|
||||
} catch (ex) {
|
||||
do_throw("Got exception retrieving lastSyncReassigned: " +
|
||||
Utils.exceptionStr(ex));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a test request to `url`, then watch the result of two syncs
|
||||
* to ensure that a node request was made.
|
||||
* Runs `between` between the two. This can be used to undo deliberate failure
|
||||
* setup, detach observers, etc.
|
||||
*/
|
||||
function syncAndExpectNodeReassignment(server, firstNotification, between,
|
||||
secondNotification, url) {
|
||||
function onwards() {
|
||||
let nodeFetched = false;
|
||||
function onFirstSync() {
|
||||
_("First sync completed.");
|
||||
Svc.Obs.remove(firstNotification, onFirstSync);
|
||||
Svc.Obs.add(secondNotification, onSecondSync);
|
||||
|
||||
do_check_eq(Service.clusterURL, "");
|
||||
|
||||
// Track whether we fetched node/weave. We want to wait for the second
|
||||
// sync to finish so that we're cleaned up for the next test, so don't
|
||||
// run_next_test in the node handler.
|
||||
nodeFetched = false;
|
||||
|
||||
// Verify that the client requests a node reassignment.
|
||||
// Install a node handler to watch for these requests.
|
||||
installNodeHandler(server, function () {
|
||||
nodeFetched = true;
|
||||
});
|
||||
|
||||
// Allow for tests to clean up error conditions.
|
||||
between();
|
||||
}
|
||||
function onSecondSync() {
|
||||
_("Second sync completed.");
|
||||
Svc.Obs.remove(secondNotification, onSecondSync);
|
||||
SyncScheduler.clearSyncTriggers();
|
||||
|
||||
// Make absolutely sure that any event listeners are done with their work
|
||||
// before we proceed.
|
||||
waitForZeroTimer(function () {
|
||||
_("Second sync nextTick.");
|
||||
do_check_true(nodeFetched);
|
||||
Service.startOver();
|
||||
server.stop(run_next_test);
|
||||
});
|
||||
}
|
||||
|
||||
Svc.Obs.add(firstNotification, onFirstSync);
|
||||
Service.sync();
|
||||
}
|
||||
|
||||
// Make sure that it works!
|
||||
let request = new RESTRequest(url);
|
||||
request.get(function () {
|
||||
do_check_eq(request.response.status, 401);
|
||||
Utils.nextTick(onwards);
|
||||
});
|
||||
}
|
||||
|
||||
add_test(function test_momentary_401_engine() {
|
||||
_("Test a failure for engine URLs that's resolved by reassignment.");
|
||||
let server = prepareServer();
|
||||
let john = server.user("johndoe");
|
||||
|
||||
_("Enabling the Rotary engine.");
|
||||
let engine = Engines.get("rotary");
|
||||
engine.enabled = true;
|
||||
|
||||
// We need the server to be correctly set up prior to experimenting. Do this
|
||||
// through a sync.
|
||||
let global = {syncID: Service.syncID,
|
||||
storageVersion: STORAGE_VERSION,
|
||||
rotary: {version: engine.version,
|
||||
syncID: engine.syncID}}
|
||||
john.createCollection("meta").insert("global", global);
|
||||
|
||||
_("First sync to prepare server contents.");
|
||||
Service.sync();
|
||||
|
||||
_("Setting up Rotary collection to 401.");
|
||||
let rotary = john.createCollection("rotary");
|
||||
let oldHandler = rotary.collectionHandler;
|
||||
rotary.collectionHandler = handleReassign.bind(this, undefined);
|
||||
|
||||
// We want to verify that the clusterURL pref has been cleared after a 401
|
||||
// inside a sync. Flag the Rotary engine to need syncing.
|
||||
john.collection("rotary").timestamp += 1000;
|
||||
|
||||
function between() {
|
||||
_("Undoing test changes.");
|
||||
rotary.collectionHandler = oldHandler;
|
||||
|
||||
function onLoginStart() {
|
||||
// lastSyncReassigned shouldn't be cleared until a sync has succeeded.
|
||||
_("Ensuring that lastSyncReassigned is still set at next sync start.");
|
||||
Svc.Obs.remove("weave:service:login:start", onLoginStart);
|
||||
do_check_true(getReassigned());
|
||||
}
|
||||
|
||||
_("Adding observer that lastSyncReassigned is still set on login.");
|
||||
Svc.Obs.add("weave:service:login:start", onLoginStart);
|
||||
}
|
||||
|
||||
syncAndExpectNodeReassignment(server,
|
||||
"weave:service:sync:finish",
|
||||
between,
|
||||
"weave:service:sync:finish",
|
||||
Service.storageURL + "rotary");
|
||||
});
|
||||
|
||||
// This test ends up being a failing fetch *after we're already logged in*.
|
||||
add_test(function test_momentary_401_info_collections() {
|
||||
_("Test a failure for info/collections that's resolved by reassignment.");
|
||||
let server = prepareServer();
|
||||
|
||||
_("First sync to prepare server contents.");
|
||||
Service.sync();
|
||||
|
||||
// Return a 401 for info requests, particularly info/collections.
|
||||
let oldHandler = server.toplevelHandlers.info;
|
||||
server.toplevelHandlers.info = handleReassign;
|
||||
|
||||
function undo() {
|
||||
_("Undoing test changes.");
|
||||
server.toplevelHandlers.info = oldHandler;
|
||||
}
|
||||
|
||||
syncAndExpectNodeReassignment(server,
|
||||
"weave:service:sync:error",
|
||||
undo,
|
||||
"weave:service:sync:finish",
|
||||
Service.infoURL);
|
||||
});
|
||||
|
||||
add_test(function test_momentary_401_storage() {
|
||||
_("Test a failure for any storage URL, not just engine parts. " +
|
||||
"Resolved by reassignment.");
|
||||
let server = prepareServer();
|
||||
|
||||
// Return a 401 for all storage requests.
|
||||
let oldHandler = server.toplevelHandlers.storage;
|
||||
server.toplevelHandlers.storage = handleReassign;
|
||||
|
||||
function undo() {
|
||||
_("Undoing test changes.");
|
||||
server.toplevelHandlers.storage = oldHandler;
|
||||
}
|
||||
|
||||
syncAndExpectNodeReassignment(server,
|
||||
"weave:service:login:error",
|
||||
undo,
|
||||
"weave:service:sync:finish",
|
||||
Service.storageURL + "meta/global");
|
||||
});
|
||||
|
||||
add_test(function test_loop_avoidance_storage() {
|
||||
_("Test that a repeated failure doesn't result in a sync loop " +
|
||||
"if node reassignment cannot resolve the failure.");
|
||||
|
||||
let server = prepareServer();
|
||||
|
||||
// Return a 401 for all storage requests.
|
||||
let oldHandler = server.toplevelHandlers.storage;
|
||||
server.toplevelHandlers.storage = handleReassign;
|
||||
|
||||
let firstNotification = "weave:service:login:error";
|
||||
let secondNotification = "weave:service:login:error";
|
||||
let thirdNotification = "weave:service:sync:finish";
|
||||
|
||||
let nodeFetched = false;
|
||||
|
||||
// Track the time. We want to make sure the duration between the first and
|
||||
// second sync is small, and then that the duration between second and third
|
||||
// is set to be large.
|
||||
let now;
|
||||
|
||||
function onFirstSync() {
|
||||
_("First sync completed.");
|
||||
Svc.Obs.remove(firstNotification, onFirstSync);
|
||||
Svc.Obs.add(secondNotification, onSecondSync);
|
||||
|
||||
do_check_eq(Service.clusterURL, "");
|
||||
|
||||
// We got a 401 mid-sync, and set the pref accordingly.
|
||||
do_check_true(Services.prefs.getBoolPref("services.sync.lastSyncReassigned"));
|
||||
|
||||
// Track whether we fetched node/weave. We want to wait for the second
|
||||
// sync to finish so that we're cleaned up for the next test, so don't
|
||||
// run_next_test in the node handler.
|
||||
nodeFetched = false;
|
||||
|
||||
// Verify that the client requests a node reassignment.
|
||||
// Install a node handler to watch for these requests.
|
||||
installNodeHandler(server, function () {
|
||||
nodeFetched = true;
|
||||
});
|
||||
|
||||
// Update the timestamp.
|
||||
now = Date.now();
|
||||
}
|
||||
|
||||
function onSecondSync() {
|
||||
_("Second sync completed.");
|
||||
Svc.Obs.remove(secondNotification, onSecondSync);
|
||||
Svc.Obs.add(thirdNotification, onThirdSync);
|
||||
|
||||
// This sync occurred within the backoff interval.
|
||||
let elapsedTime = Date.now() - now;
|
||||
do_check_true(elapsedTime < MINIMUM_BACKOFF_INTERVAL);
|
||||
|
||||
// This pref will be true until a sync completes successfully.
|
||||
do_check_true(getReassigned());
|
||||
|
||||
// The timer will be set for some distant time.
|
||||
// We store nextSync in prefs, which offers us only limited resolution.
|
||||
// Include that logic here.
|
||||
let expectedNextSync = 1000 * Math.floor((now + MINIMUM_BACKOFF_INTERVAL) / 1000);
|
||||
_("Next sync scheduled for " + SyncScheduler.nextSync);
|
||||
_("Expected to be slightly greater than " + expectedNextSync);
|
||||
|
||||
do_check_true(SyncScheduler.nextSync >= expectedNextSync);
|
||||
do_check_true(!!SyncScheduler.syncTimer);
|
||||
|
||||
// Undo our evil scheme.
|
||||
server.toplevelHandlers.storage = oldHandler;
|
||||
|
||||
// Bring the timer forward to kick off a successful sync, so we can watch
|
||||
// the pref get cleared.
|
||||
SyncScheduler.scheduleNextSync(0);
|
||||
}
|
||||
function onThirdSync() {
|
||||
Svc.Obs.remove(thirdNotification, onThirdSync);
|
||||
|
||||
// That'll do for now; no more syncs.
|
||||
SyncScheduler.clearSyncTriggers();
|
||||
|
||||
// Make absolutely sure that any event listeners are done with their work
|
||||
// before we proceed.
|
||||
waitForZeroTimer(function () {
|
||||
_("Third sync nextTick.");
|
||||
do_check_false(getReassigned());
|
||||
do_check_true(nodeFetched);
|
||||
Service.startOver();
|
||||
server.stop(run_next_test);
|
||||
});
|
||||
}
|
||||
|
||||
Svc.Obs.add(firstNotification, onFirstSync);
|
||||
|
||||
now = Date.now();
|
||||
Service.sync();
|
||||
});
|
||||
|
||||
add_test(function test_loop_avoidance_engine() {
|
||||
_("Test that a repeated 401 in an engine doesn't result in a sync loop " +
|
||||
"if node reassignment cannot resolve the failure.");
|
||||
let server = prepareServer();
|
||||
let john = server.user("johndoe");
|
||||
|
||||
_("Enabling the Rotary engine.");
|
||||
let engine = Engines.get("rotary");
|
||||
engine.enabled = true;
|
||||
|
||||
// We need the server to be correctly set up prior to experimenting. Do this
|
||||
// through a sync.
|
||||
let global = {syncID: Service.syncID,
|
||||
storageVersion: STORAGE_VERSION,
|
||||
rotary: {version: engine.version,
|
||||
syncID: engine.syncID}}
|
||||
john.createCollection("meta").insert("global", global);
|
||||
|
||||
_("First sync to prepare server contents.");
|
||||
Service.sync();
|
||||
|
||||
_("Setting up Rotary collection to 401.");
|
||||
let rotary = john.createCollection("rotary");
|
||||
let oldHandler = rotary.collectionHandler;
|
||||
rotary.collectionHandler = handleReassign.bind(this, undefined);
|
||||
|
||||
// Flag the Rotary engine to need syncing.
|
||||
john.collection("rotary").timestamp += 1000;
|
||||
|
||||
function onLoginStart() {
|
||||
// lastSyncReassigned shouldn't be cleared until a sync has succeeded.
|
||||
_("Ensuring that lastSyncReassigned is still set at next sync start.");
|
||||
do_check_true(getReassigned());
|
||||
}
|
||||
|
||||
function beforeSuccessfulSync() {
|
||||
_("Undoing test changes.");
|
||||
rotary.collectionHandler = oldHandler;
|
||||
}
|
||||
|
||||
function afterSuccessfulSync() {
|
||||
Svc.Obs.remove("weave:service:login:start", onLoginStart);
|
||||
Service.startOver();
|
||||
server.stop(run_next_test);
|
||||
}
|
||||
|
||||
let firstNotification = "weave:service:sync:finish";
|
||||
let secondNotification = "weave:service:sync:finish";
|
||||
let thirdNotification = "weave:service:sync:finish";
|
||||
|
||||
let nodeFetched = false;
|
||||
|
||||
// Track the time. We want to make sure the duration between the first and
|
||||
// second sync is small, and then that the duration between second and third
|
||||
// is set to be large.
|
||||
let now;
|
||||
|
||||
function onFirstSync() {
|
||||
_("First sync completed.");
|
||||
Svc.Obs.remove(firstNotification, onFirstSync);
|
||||
Svc.Obs.add(secondNotification, onSecondSync);
|
||||
|
||||
do_check_eq(Service.clusterURL, "");
|
||||
|
||||
_("Adding observer that lastSyncReassigned is still set on login.");
|
||||
Svc.Obs.add("weave:service:login:start", onLoginStart);
|
||||
|
||||
// We got a 401 mid-sync, and set the pref accordingly.
|
||||
do_check_true(Services.prefs.getBoolPref("services.sync.lastSyncReassigned"));
|
||||
|
||||
// Track whether we fetched node/weave. We want to wait for the second
|
||||
// sync to finish so that we're cleaned up for the next test, so don't
|
||||
// run_next_test in the node handler.
|
||||
nodeFetched = false;
|
||||
|
||||
// Verify that the client requests a node reassignment.
|
||||
// Install a node handler to watch for these requests.
|
||||
installNodeHandler(server, function () {
|
||||
nodeFetched = true;
|
||||
});
|
||||
|
||||
// Update the timestamp.
|
||||
now = Date.now();
|
||||
}
|
||||
|
||||
function onSecondSync() {
|
||||
_("Second sync completed.");
|
||||
Svc.Obs.remove(secondNotification, onSecondSync);
|
||||
Svc.Obs.add(thirdNotification, onThirdSync);
|
||||
|
||||
// This sync occurred within the backoff interval.
|
||||
let elapsedTime = Date.now() - now;
|
||||
do_check_true(elapsedTime < MINIMUM_BACKOFF_INTERVAL);
|
||||
|
||||
// This pref will be true until a sync completes successfully.
|
||||
do_check_true(getReassigned());
|
||||
|
||||
// The timer will be set for some distant time.
|
||||
// We store nextSync in prefs, which offers us only limited resolution.
|
||||
// Include that logic here.
|
||||
let expectedNextSync = 1000 * Math.floor((now + MINIMUM_BACKOFF_INTERVAL) / 1000);
|
||||
_("Next sync scheduled for " + SyncScheduler.nextSync);
|
||||
_("Expected to be slightly greater than " + expectedNextSync);
|
||||
|
||||
do_check_true(SyncScheduler.nextSync >= expectedNextSync);
|
||||
do_check_true(!!SyncScheduler.syncTimer);
|
||||
|
||||
// Undo our evil scheme.
|
||||
beforeSuccessfulSync();
|
||||
|
||||
// Bring the timer forward to kick off a successful sync, so we can watch
|
||||
// the pref get cleared.
|
||||
SyncScheduler.scheduleNextSync(0);
|
||||
}
|
||||
|
||||
function onThirdSync() {
|
||||
Svc.Obs.remove(thirdNotification, onThirdSync);
|
||||
|
||||
// That'll do for now; no more syncs.
|
||||
SyncScheduler.clearSyncTriggers();
|
||||
|
||||
// Make absolutely sure that any event listeners are done with their work
|
||||
// before we proceed.
|
||||
waitForZeroTimer(function () {
|
||||
_("Third sync nextTick.");
|
||||
do_check_false(getReassigned());
|
||||
do_check_true(nodeFetched);
|
||||
afterSuccessfulSync();
|
||||
});
|
||||
}
|
||||
|
||||
Svc.Obs.add(firstNotification, onFirstSync);
|
||||
|
||||
now = Date.now();
|
||||
Service.sync();
|
||||
});
|
|
@ -6,9 +6,11 @@ Cu.import("resource://services-sync/util.js");
|
|||
|
||||
let logger;
|
||||
|
||||
let fetched = false;
|
||||
function server_open(metadata, response) {
|
||||
let body;
|
||||
if (metadata.method == "GET") {
|
||||
fetched = true;
|
||||
body = "This path exists";
|
||||
response.setStatusLine(metadata.httpVersion, 200, "OK");
|
||||
} else {
|
||||
|
@ -40,6 +42,15 @@ function server_404(metadata, response) {
|
|||
response.bodyOutputStream.write(body, body.length);
|
||||
}
|
||||
|
||||
let pacFetched = false;
|
||||
function server_pac(metadata, response) {
|
||||
pacFetched = true;
|
||||
let body = 'function FindProxyForURL(url, host) { return "DIRECT"; }';
|
||||
response.setStatusLine(metadata.httpVersion, 200, "OK");
|
||||
response.setHeader("Content-Type", "application/x-ns-proxy-autoconfig", false);
|
||||
response.bodyOutputStream.write(body, body.length);
|
||||
}
|
||||
|
||||
|
||||
let sample_data = {
|
||||
some: "sample_data",
|
||||
|
@ -150,12 +161,26 @@ function run_test() {
|
|||
"/timestamp": server_timestamp,
|
||||
"/headers": server_headers,
|
||||
"/backoff": server_backoff,
|
||||
"/pac1": server_pac,
|
||||
"/quota-notice": server_quota_notice,
|
||||
"/quota-error": server_quota_error
|
||||
});
|
||||
|
||||
Svc.Prefs.set("network.numRetries", 1); // speed up test
|
||||
|
||||
// This apparently has to come first in order for our PAC URL to be hit.
|
||||
// Don't put any other HTTP requests earlier in the file!
|
||||
_("Testing handling of proxy auth redirection.");
|
||||
PACSystemSettings.PACURI = "http://localhost:8080/pac1";
|
||||
installFakePAC();
|
||||
let proxiedRes = new Resource("http://localhost:8080/open");
|
||||
let content = proxiedRes.get();
|
||||
do_check_true(pacFetched);
|
||||
do_check_true(fetched);
|
||||
do_check_eq(content, "This path exists");
|
||||
pacFetched = fetched = false;
|
||||
uninstallFakePAC();
|
||||
|
||||
_("Resource object members");
|
||||
let res = new Resource("http://localhost:8080/open");
|
||||
do_check_true(res.uri instanceof Ci.nsIURI);
|
||||
|
@ -168,7 +193,7 @@ function run_test() {
|
|||
do_check_eq(res.data, null);
|
||||
|
||||
_("GET a non-password-protected resource");
|
||||
let content = res.get();
|
||||
content = res.get();
|
||||
do_check_eq(content, "This path exists");
|
||||
do_check_eq(content.status, 200);
|
||||
do_check_true(content.success);
|
||||
|
@ -474,6 +499,5 @@ function run_test() {
|
|||
let uri2 = Utils.makeURL("http://foo/");
|
||||
uri2.query = query;
|
||||
do_check_eq(uri1.query, uri2.query);
|
||||
|
||||
server.stop(do_test_finished);
|
||||
}
|
||||
|
|
|
@ -6,9 +6,11 @@ Cu.import("resource://services-sync/util.js");
|
|||
|
||||
let logger;
|
||||
|
||||
let fetched = false;
|
||||
function server_open(metadata, response) {
|
||||
let body;
|
||||
if (metadata.method == "GET") {
|
||||
fetched = true;
|
||||
body = "This path exists";
|
||||
response.setStatusLine(metadata.httpVersion, 200, "OK");
|
||||
} else {
|
||||
|
@ -40,6 +42,15 @@ function server_404(metadata, response) {
|
|||
response.bodyOutputStream.write(body, body.length);
|
||||
}
|
||||
|
||||
let pacFetched = false;
|
||||
function server_pac(metadata, response) {
|
||||
_("Invoked PAC handler.");
|
||||
pacFetched = true;
|
||||
let body = 'function FindProxyForURL(url, host) { return "DIRECT"; }';
|
||||
response.setStatusLine(metadata.httpVersion, 200, "OK");
|
||||
response.setHeader("Content-Type", "application/x-ns-proxy-autoconfig", false);
|
||||
response.bodyOutputStream.write(body, body.length);
|
||||
}
|
||||
|
||||
let sample_data = {
|
||||
some: "sample_data",
|
||||
|
@ -154,6 +165,7 @@ function run_test() {
|
|||
"/timestamp": server_timestamp,
|
||||
"/headers": server_headers,
|
||||
"/backoff": server_backoff,
|
||||
"/pac2": server_pac,
|
||||
"/quota-notice": server_quota_notice,
|
||||
"/quota-error": server_quota_error
|
||||
});
|
||||
|
@ -162,9 +174,27 @@ function run_test() {
|
|||
run_next_test();
|
||||
}
|
||||
|
||||
// This apparently has to come first in order for our PAC URL to be hit.
|
||||
// Don't put any other HTTP requests earlier in the file!
|
||||
add_test(function test_proxy_auth_redirect() {
|
||||
_("Ensure that a proxy auth redirect (which switches out our channel) " +
|
||||
"doesn't break AsyncResource.");
|
||||
PACSystemSettings.PACURI = "http://localhost:8080/pac2";
|
||||
installFakePAC();
|
||||
let res = new AsyncResource("http://localhost:8080/open");
|
||||
res.get(function (error, result) {
|
||||
do_check_true(!error);
|
||||
do_check_true(pacFetched);
|
||||
do_check_true(fetched);
|
||||
do_check_eq("This path exists", result);
|
||||
pacFetched = fetched = false;
|
||||
uninstallFakePAC();
|
||||
run_next_test();
|
||||
});
|
||||
});
|
||||
|
||||
add_test(function test_members() {
|
||||
_("Resource object memebers");
|
||||
_("Resource object members");
|
||||
let res = new AsyncResource("http://localhost:8080/open");
|
||||
do_check_true(res.uri instanceof Ci.nsIURI);
|
||||
do_check_eq(res.uri.spec, "http://localhost:8080/open");
|
||||
|
|
|
@ -41,6 +41,47 @@ add_test(function test_attributes() {
|
|||
run_next_test();
|
||||
});
|
||||
|
||||
/**
|
||||
* Verify that a proxy auth redirect doesn't break us. This has to be the first
|
||||
* request made in the file!
|
||||
*/
|
||||
add_test(function test_proxy_auth_redirect() {
|
||||
let pacFetched = false;
|
||||
function pacHandler(metadata, response) {
|
||||
pacFetched = true;
|
||||
let body = 'function FindProxyForURL(url, host) { return "DIRECT"; }';
|
||||
response.setStatusLine(metadata.httpVersion, 200, "OK");
|
||||
response.setHeader("Content-Type", "application/x-ns-proxy-autoconfig", false);
|
||||
response.bodyOutputStream.write(body, body.length);
|
||||
}
|
||||
|
||||
let fetched = false;
|
||||
function original(metadata, response) {
|
||||
fetched = true;
|
||||
let body = "TADA!";
|
||||
response.setStatusLine(metadata.httpVersion, 200, "OK");
|
||||
response.bodyOutputStream.write(body, body.length);
|
||||
}
|
||||
|
||||
let server = httpd_setup({
|
||||
"/original": original,
|
||||
"/pac3": pacHandler
|
||||
});
|
||||
PACSystemSettings.PACURI = "http://localhost:8080/pac3";
|
||||
installFakePAC();
|
||||
|
||||
let res = new RESTRequest("http://localhost:8080/original");
|
||||
res.get(function (error) {
|
||||
do_check_true(pacFetched);
|
||||
do_check_true(fetched);
|
||||
do_check_true(!error);
|
||||
do_check_true(this.response.success);
|
||||
do_check_eq("TADA!", this.response.body);
|
||||
uninstallFakePAC();
|
||||
server.stop(run_next_test);
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Demonstrate API short-hand: create a request and dispatch it immediately.
|
||||
*/
|
||||
|
|
|
@ -2,8 +2,21 @@ Cu.import("resource://services-sync/main.js");
|
|||
Cu.import("resource://services-sync/util.js");
|
||||
Cu.import("resource://services-sync/constants.js");
|
||||
|
||||
Cu.import("resource://services-sync/log4moz.js");
|
||||
|
||||
function run_test() {
|
||||
var requestBody;
|
||||
initTestLogging("Trace");
|
||||
Log4Moz.repository.getLogger("Sync.AsyncResource").level = Log4Moz.Level.Trace;
|
||||
Log4Moz.repository.getLogger("Sync.Resource").level = Log4Moz.Level.Trace;
|
||||
Log4Moz.repository.getLogger("Sync.Service").level = Log4Moz.Level.Trace;
|
||||
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_test(function test_change_password() {
|
||||
let requestBody;
|
||||
let server;
|
||||
|
||||
function send(statusCode, status, body) {
|
||||
return function(request, response) {
|
||||
requestBody = readBytesFromInputStream(request.bodyInputStream);
|
||||
|
@ -12,10 +25,7 @@ function run_test() {
|
|||
};
|
||||
}
|
||||
|
||||
let server;
|
||||
|
||||
try {
|
||||
|
||||
Weave.Service.serverURL = "http://localhost:8080/";
|
||||
Weave.Service.username = "johndoe";
|
||||
Weave.Service.password = "ilovejane";
|
||||
|
@ -26,11 +36,10 @@ function run_test() {
|
|||
do_check_eq(Weave.Service.password, "ilovejane");
|
||||
|
||||
_("Let's fire up the server and actually change the password.");
|
||||
server = httpd_setup({
|
||||
server = httpd_setup({
|
||||
"/user/1.0/johndoe/password": send(200, "OK", ""),
|
||||
"/user/1.0/janedoe/password": send(401, "Unauthorized", "Forbidden!")
|
||||
});
|
||||
do_test_pending();
|
||||
|
||||
res = Weave.Service.changePassword("ILoveJane83");
|
||||
do_check_true(res);
|
||||
|
@ -60,8 +69,6 @@ function run_test() {
|
|||
} finally {
|
||||
Weave.Svc.Prefs.resetBranch("");
|
||||
Services.logins.removeAllLogins();
|
||||
if (server) {
|
||||
server.stop(do_test_finished);
|
||||
}
|
||||
server.stop(run_next_test);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
@ -51,7 +51,7 @@ function run_test() {
|
|||
do_check_true(Weave.Service.isLoggedIn);
|
||||
do_check_eq(Weave.Status.login, Weave.LOGIN_SUCCEEDED);
|
||||
|
||||
_("Simulate having changed the password somehwere else.");
|
||||
_("Simulate having changed the password somewhere else.");
|
||||
Weave.Service.password = "ilovejosephine";
|
||||
|
||||
_("Let's try to sync.");
|
||||
|
@ -63,12 +63,18 @@ function run_test() {
|
|||
_("We're no longer logged in.");
|
||||
do_check_false(Weave.Service.isLoggedIn);
|
||||
|
||||
_("Sync status.");
|
||||
do_check_eq(Weave.Status.login, Weave.LOGIN_FAILED_LOGIN_REJECTED);
|
||||
_("Sync status won't have changed yet, because we haven't tried again.");
|
||||
|
||||
_("globalScore is reset upon starting a sync.");
|
||||
do_check_eq(SyncScheduler.globalScore, 0);
|
||||
|
||||
_("Our next sync will fail appropriately.");
|
||||
try {
|
||||
Weave.Service.sync();
|
||||
} catch (ex) {
|
||||
}
|
||||
do_check_eq(Weave.Status.login, Weave.LOGIN_FAILED_LOGIN_REJECTED);
|
||||
|
||||
} finally {
|
||||
Weave.Svc.Prefs.resetBranch("");
|
||||
server.stop(do_test_finished);
|
||||
|
|
|
@ -29,7 +29,28 @@ function run_test() {
|
|||
let cryptoColl = new ServerCollection({keys: keysWBO});
|
||||
let metaColl = new ServerCollection({global: meta_global});
|
||||
do_test_pending();
|
||||
|
||||
/**
|
||||
* Handle the bulk DELETE request sent by wipeServer.
|
||||
*/
|
||||
function storageHandler(request, response) {
|
||||
do_check_eq("DELETE", request.method);
|
||||
do_check_true(request.hasHeader("X-Confirm-Delete"));
|
||||
|
||||
_("Wiping out all collections.");
|
||||
cryptoColl.delete({});
|
||||
clients.delete({});
|
||||
metaColl.delete({});
|
||||
|
||||
let ts = new_timestamp();
|
||||
collectionsHelper.update_collection("crypto", ts);
|
||||
collectionsHelper.update_collection("clients", ts);
|
||||
collectionsHelper.update_collection("meta", ts);
|
||||
return_timestamp(request, response, ts);
|
||||
}
|
||||
|
||||
let server = httpd_setup({
|
||||
"/1.1/johndoe/storage": storageHandler,
|
||||
"/1.1/johndoe/storage/crypto/keys": upd("crypto", keysWBO.handler()),
|
||||
"/1.1/johndoe/storage/crypto": upd("crypto", cryptoColl.handler()),
|
||||
"/1.1/johndoe/storage/clients": upd("clients", clients.handler()),
|
||||
|
|
|
@ -3,9 +3,11 @@ Cu.import("resource://services-sync/engines/clients.js");
|
|||
Cu.import("resource://services-sync/util.js");
|
||||
Cu.import("resource://services-sync/constants.js");
|
||||
Cu.import("resource://services-sync/record.js");
|
||||
Cu.import("resource://services-sync/status.js");
|
||||
|
||||
Svc.DefaultPrefs.set("registerEngines", "");
|
||||
Cu.import("resource://services-sync/service.js");
|
||||
Cu.import("resource://services-sync/policies.js");
|
||||
|
||||
initTestLogging();
|
||||
|
||||
|
@ -66,13 +68,30 @@ function sync_httpd_setup(handlers) {
|
|||
function setUp() {
|
||||
Service.username = "johndoe";
|
||||
Service.password = "ilovejane";
|
||||
Service.passphrase = "sekrit";
|
||||
Service.passphrase = "abcdeabcdeabcdeabcdeabcdea";
|
||||
Service.clusterURL = "http://localhost:8080/";
|
||||
// So that we can poke at meta/global.
|
||||
new FakeCryptoService();
|
||||
|
||||
// Ensure that the server has valid keys so that logging in will work and not
|
||||
// result in a server wipe, rendering many of these tests useless.
|
||||
generateNewKeys();
|
||||
let serverKeys = CollectionKeys.asWBO("crypto", "keys");
|
||||
serverKeys.encrypt(Service.syncKeyBundle);
|
||||
return serverKeys.upload(Service.cryptoKeysURL).success;
|
||||
}
|
||||
|
||||
const PAYLOAD = 42;
|
||||
|
||||
|
||||
function run_test() {
|
||||
initTestLogging("Trace");
|
||||
Log4Moz.repository.getLogger("Sync.Service").level = Log4Moz.Level.Trace;
|
||||
Log4Moz.repository.getLogger("Sync.ErrorHandler").level = Log4Moz.Level.Trace;
|
||||
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_test(function test_newAccount() {
|
||||
_("Test: New account does not disable locally enabled engines.");
|
||||
let engine = Engines.get("steam");
|
||||
|
@ -89,7 +108,6 @@ add_test(function test_newAccount() {
|
|||
Service._ignorePrefObserver = false;
|
||||
|
||||
_("Sync.");
|
||||
Weave.Service.login();
|
||||
Weave.Service.sync();
|
||||
|
||||
_("Engine continues to be enabled.");
|
||||
|
@ -118,7 +136,6 @@ add_test(function test_enabledLocally() {
|
|||
engine.enabled = true;
|
||||
|
||||
_("Sync.");
|
||||
Weave.Service.login();
|
||||
Weave.Service.sync();
|
||||
|
||||
_("Meta record now contains the new engine.");
|
||||
|
@ -143,6 +160,7 @@ add_test(function test_disabledLocally() {
|
|||
version: engine.version}}
|
||||
});
|
||||
let steamCollection = new ServerWBO("steam", PAYLOAD);
|
||||
|
||||
let server = sync_httpd_setup({
|
||||
"/1.1/johndoe/storage/meta/global": metaWBO.handler(),
|
||||
"/1.1/johndoe/storage/steam": steamCollection.handler()
|
||||
|
@ -157,7 +175,6 @@ add_test(function test_disabledLocally() {
|
|||
engine.enabled = false;
|
||||
|
||||
_("Sync.");
|
||||
Weave.Service.login();
|
||||
Weave.Service.sync();
|
||||
|
||||
_("Meta record no longer contains engine.");
|
||||
|
@ -174,6 +191,50 @@ add_test(function test_disabledLocally() {
|
|||
}
|
||||
});
|
||||
|
||||
add_test(function test_disabledLocally_wipe503() {
|
||||
_("Test: Engine is enabled on remote clients and disabled locally");
|
||||
Service.syncID = "abcdefghij";
|
||||
let engine = Engines.get("steam");
|
||||
let metaWBO = new ServerWBO("global", {
|
||||
syncID: Service.syncID,
|
||||
storageVersion: STORAGE_VERSION,
|
||||
engines: {steam: {syncID: engine.syncID,
|
||||
version: engine.version}}
|
||||
});
|
||||
let steamCollection = new ServerWBO("steam", PAYLOAD);
|
||||
|
||||
function service_unavailable(request, response) {
|
||||
let body = "Service Unavailable";
|
||||
response.setStatusLine(request.httpVersion, 503, "Service Unavailable");
|
||||
response.setHeader("Retry-After", "23");
|
||||
response.bodyOutputStream.write(body, body.length);
|
||||
}
|
||||
|
||||
let server = sync_httpd_setup({
|
||||
"/1.1/johndoe/storage/meta/global": metaWBO.handler(),
|
||||
"/1.1/johndoe/storage/steam": service_unavailable
|
||||
});
|
||||
setUp();
|
||||
|
||||
_("Disable engine locally.");
|
||||
Service._ignorePrefObserver = true;
|
||||
engine.enabled = true;
|
||||
Service._ignorePrefObserver = false;
|
||||
engine.enabled = false;
|
||||
|
||||
Svc.Obs.add("weave:ui:sync:error", function onSyncError() {
|
||||
Svc.Obs.remove("weave:ui:sync:error", onSyncError);
|
||||
|
||||
do_check_eq(Status.sync, SERVER_MAINTENANCE);
|
||||
|
||||
Service.startOver();
|
||||
server.stop(run_next_test);
|
||||
});
|
||||
|
||||
_("Sync.");
|
||||
ErrorHandler.syncAndReportErrors();
|
||||
});
|
||||
|
||||
add_test(function test_enabledRemotely() {
|
||||
_("Test: Engine is disabled locally and enabled on a remote client");
|
||||
Service.syncID = "abcdefghij";
|
||||
|
@ -205,7 +266,6 @@ add_test(function test_enabledRemotely() {
|
|||
do_check_false(engine.enabled);
|
||||
|
||||
_("Sync.");
|
||||
Weave.Service.login();
|
||||
Weave.Service.sync();
|
||||
|
||||
_("Engine is enabled.");
|
||||
|
@ -242,7 +302,6 @@ add_test(function test_disabledRemotelyTwoClients() {
|
|||
Service._ignorePrefObserver = false;
|
||||
|
||||
_("Sync.");
|
||||
Weave.Service.login();
|
||||
Weave.Service.sync();
|
||||
|
||||
_("Disable engine by deleting from meta/global.");
|
||||
|
@ -284,7 +343,6 @@ add_test(function test_disabledRemotely() {
|
|||
Service._ignorePrefObserver = false;
|
||||
|
||||
_("Sync.");
|
||||
Weave.Service.login();
|
||||
Weave.Service.sync();
|
||||
|
||||
_("Engine is not disabled: only one client.");
|
||||
|
@ -316,7 +374,6 @@ add_test(function test_dependentEnginesEnabledLocally() {
|
|||
steamEngine.enabled = true;
|
||||
|
||||
_("Sync.");
|
||||
Weave.Service.login();
|
||||
Weave.Service.sync();
|
||||
|
||||
_("Meta record now contains the new engines.");
|
||||
|
@ -348,6 +405,7 @@ add_test(function test_dependentEnginesDisabledLocally() {
|
|||
|
||||
let steamCollection = new ServerWBO("steam", PAYLOAD);
|
||||
let stirlingCollection = new ServerWBO("stirling", PAYLOAD);
|
||||
|
||||
let server = sync_httpd_setup({
|
||||
"/1.1/johndoe/storage/meta/global": metaWBO.handler(),
|
||||
"/1.1/johndoe/storage/steam": steamCollection.handler(),
|
||||
|
@ -365,7 +423,6 @@ add_test(function test_dependentEnginesDisabledLocally() {
|
|||
do_check_false(stirlingEngine.enabled);
|
||||
|
||||
_("Sync.");
|
||||
Weave.Service.login();
|
||||
Weave.Service.sync();
|
||||
|
||||
_("Meta record no longer contains engines.");
|
||||
|
@ -384,7 +441,3 @@ add_test(function test_dependentEnginesDisabledLocally() {
|
|||
server.stop(run_next_test);
|
||||
}
|
||||
});
|
||||
|
||||
function run_test() {
|
||||
run_next_test();
|
||||
}
|
||||
|
|
|
@ -13,22 +13,19 @@ FakeCollection.prototype = {
|
|||
let self = this;
|
||||
return function(request, response) {
|
||||
let body = "";
|
||||
self.timestamp = new_timestamp();
|
||||
let timestamp = "" + self.timestamp;
|
||||
if (request.method == "DELETE") {
|
||||
body = JSON.stringify(Date.now() / 1000);
|
||||
body = timestamp;
|
||||
self.deleted = true;
|
||||
}
|
||||
response.setHeader("X-Weave-Timestamp", timestamp);
|
||||
response.setStatusLine(request.httpVersion, 200, "OK");
|
||||
response.bodyOutputStream.write(body, body.length);
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
function serviceUnavailable(request, response) {
|
||||
let body = "Service Unavailable";
|
||||
response.setStatusLine(request.httpVersion, 503, "Service Unavailable");
|
||||
response.bodyOutputStream.write(body, body.length);
|
||||
}
|
||||
|
||||
function setUpTestFixtures() {
|
||||
let cryptoService = new FakeCryptoService();
|
||||
|
||||
|
@ -37,7 +34,13 @@ function setUpTestFixtures() {
|
|||
Service.passphrase = "aabcdeabcdeabcdeabcdeabcde";
|
||||
}
|
||||
|
||||
function test_withCollectionList_fail() {
|
||||
|
||||
function run_test() {
|
||||
initTestLogging("Trace");
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_test(function test_wipeServer_list_success() {
|
||||
_("Service.wipeServer() deletes collections given as argument.");
|
||||
|
||||
let steam_coll = new FakeCollection();
|
||||
|
@ -45,10 +48,42 @@ function test_withCollectionList_fail() {
|
|||
|
||||
let server = httpd_setup({
|
||||
"/1.1/johndoe/storage/steam": steam_coll.handler(),
|
||||
"/1.1/johndoe/storage/petrol": serviceUnavailable,
|
||||
"/1.1/johndoe/storage/diesel": diesel_coll.handler(),
|
||||
"/1.1/johndoe/storage/petrol": httpd_handler(404, "Not Found")
|
||||
});
|
||||
|
||||
try {
|
||||
setUpTestFixtures();
|
||||
|
||||
_("Confirm initial environment.");
|
||||
do_check_false(steam_coll.deleted);
|
||||
do_check_false(diesel_coll.deleted);
|
||||
|
||||
_("wipeServer() will happily ignore the non-existent collection and use the timestamp of the last DELETE that was successful.");
|
||||
let timestamp = Service.wipeServer(["steam", "diesel", "petrol"]);
|
||||
do_check_eq(timestamp, diesel_coll.timestamp);
|
||||
|
||||
_("wipeServer stopped deleting after encountering an error with the 'petrol' collection, thus only 'steam' has been deleted.");
|
||||
do_check_true(steam_coll.deleted);
|
||||
do_check_true(diesel_coll.deleted);
|
||||
|
||||
} finally {
|
||||
server.stop(run_next_test);
|
||||
Svc.Prefs.resetBranch("");
|
||||
}
|
||||
});
|
||||
|
||||
add_test(function test_wipeServer_list_503() {
|
||||
_("Service.wipeServer() deletes collections given as argument.");
|
||||
|
||||
let steam_coll = new FakeCollection();
|
||||
let diesel_coll = new FakeCollection();
|
||||
|
||||
let server = httpd_setup({
|
||||
"/1.1/johndoe/storage/steam": steam_coll.handler(),
|
||||
"/1.1/johndoe/storage/petrol": httpd_handler(503, "Service Unavailable"),
|
||||
"/1.1/johndoe/storage/diesel": diesel_coll.handler()
|
||||
});
|
||||
do_test_pending();
|
||||
|
||||
try {
|
||||
setUpTestFixtures();
|
||||
|
@ -61,84 +96,126 @@ function test_withCollectionList_fail() {
|
|||
let error;
|
||||
try {
|
||||
Service.wipeServer(["non-existent", "steam", "petrol", "diesel"]);
|
||||
do_throw("Should have thrown!");
|
||||
} catch(ex) {
|
||||
error = ex;
|
||||
}
|
||||
_("wipeServer() threw this exception: " + error);
|
||||
do_check_true(error != undefined);
|
||||
do_check_eq(error.status, 503);
|
||||
|
||||
_("wipeServer stopped deleting after encountering an error with the 'petrol' collection, thus only 'steam' has been deleted.");
|
||||
do_check_true(steam_coll.deleted);
|
||||
do_check_false(diesel_coll.deleted);
|
||||
|
||||
} finally {
|
||||
server.stop(do_test_finished);
|
||||
server.stop(run_next_test);
|
||||
Svc.Prefs.resetBranch("");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
function test_wipeServer_leaves_collections() {
|
||||
_("Service.wipeServer() deletes everything but keys.");
|
||||
add_test(function test_wipeServer_all_success() {
|
||||
_("Service.wipeServer() deletes all the things.");
|
||||
|
||||
let steam_coll = new FakeCollection();
|
||||
let diesel_coll = new FakeCollection();
|
||||
let keys_coll = new FakeCollection();
|
||||
|
||||
function info_collections(request, response) {
|
||||
let collections = {};
|
||||
let timestamp = Date.now() / 1000;
|
||||
if (!steam_coll.deleted)
|
||||
collections.steam = timestamp
|
||||
if (!diesel_coll.deleted)
|
||||
collections.diesel = timestamp;
|
||||
if (!keys_coll.deleted)
|
||||
collections.keys = timestamp;
|
||||
let body = JSON.stringify(collections);
|
||||
response.setStatusLine(request.httpVersion, 200, "OK");
|
||||
response.bodyOutputStream.write(body, body.length);
|
||||
/**
|
||||
* Handle the bulk DELETE request sent by wipeServer.
|
||||
*/
|
||||
let deleted = false;
|
||||
let serverTimestamp;
|
||||
function storageHandler(request, response) {
|
||||
do_check_eq("DELETE", request.method);
|
||||
do_check_true(request.hasHeader("X-Confirm-Delete"));
|
||||
deleted = true;
|
||||
serverTimestamp = return_timestamp(request, response);
|
||||
}
|
||||
|
||||
let server = httpd_setup({
|
||||
"/1.1/johndoe/storage/steam": steam_coll.handler(),
|
||||
"/1.1/johndoe/storage/diesel": diesel_coll.handler(),
|
||||
"/1.1/johndoe/storage/keys": keys_coll.handler(),
|
||||
"/1.1/johndoe/info/collections": info_collections
|
||||
"/1.1/johndoe/storage": storageHandler
|
||||
});
|
||||
do_test_pending();
|
||||
setUpTestFixtures();
|
||||
|
||||
try {
|
||||
setUpTestFixtures();
|
||||
_("Info URL: " + Service.infoURL);
|
||||
_("Try deletion.");
|
||||
let returnedTimestamp = Service.wipeServer();
|
||||
do_check_true(deleted);
|
||||
do_check_eq(returnedTimestamp, serverTimestamp);
|
||||
|
||||
_("Confirm initial environment.");
|
||||
do_check_false(steam_coll.deleted);
|
||||
do_check_false(diesel_coll.deleted);
|
||||
do_check_false(keys_coll.deleted);
|
||||
|
||||
_("Collections: " + new Resource(Service.infoURL).get());
|
||||
_("Try deletion.");
|
||||
Service.wipeServer();
|
||||
_("Collections: " + new Resource(Service.infoURL).get());
|
||||
|
||||
_("Make sure keys is still present.");
|
||||
do_check_true(steam_coll.deleted);
|
||||
do_check_true(diesel_coll.deleted);
|
||||
do_check_false(keys_coll.deleted);
|
||||
|
||||
_("Delete everything.");
|
||||
Service.wipeServer(null, true);
|
||||
do_check_true(steam_coll.deleted);
|
||||
do_check_true(diesel_coll.deleted);
|
||||
do_check_true(keys_coll.deleted);
|
||||
|
||||
} finally {
|
||||
server.stop(do_test_finished);
|
||||
Svc.Prefs.resetBranch("");
|
||||
server.stop(run_next_test);
|
||||
Svc.Prefs.resetBranch("");
|
||||
});
|
||||
|
||||
add_test(function test_wipeServer_all_404() {
|
||||
_("Service.wipeServer() accepts a 404.");
|
||||
|
||||
/**
|
||||
* Handle the bulk DELETE request sent by wipeServer. Returns a 404.
|
||||
*/
|
||||
let deleted = false;
|
||||
let serverTimestamp;
|
||||
function storageHandler(request, response) {
|
||||
do_check_eq("DELETE", request.method);
|
||||
do_check_true(request.hasHeader("X-Confirm-Delete"));
|
||||
deleted = true;
|
||||
serverTimestamp = new_timestamp();
|
||||
response.setHeader("X-Weave-Timestamp", "" + serverTimestamp);
|
||||
response.setStatusLine(request.httpVersion, 404, "Not Found");
|
||||
}
|
||||
}
|
||||
|
||||
function run_test() {
|
||||
initTestLogging("Trace");
|
||||
test_withCollectionList_fail();
|
||||
test_wipeServer_leaves_collections();
|
||||
}
|
||||
let server = httpd_setup({
|
||||
"/1.1/johndoe/storage": storageHandler
|
||||
});
|
||||
setUpTestFixtures();
|
||||
|
||||
_("Try deletion.");
|
||||
let returnedTimestamp = Service.wipeServer();
|
||||
do_check_true(deleted);
|
||||
do_check_eq(returnedTimestamp, serverTimestamp);
|
||||
|
||||
server.stop(run_next_test);
|
||||
Svc.Prefs.resetBranch("");
|
||||
});
|
||||
|
||||
add_test(function test_wipeServer_all_503() {
|
||||
_("Service.wipeServer() throws if it encounters a non-200/404 response.");
|
||||
|
||||
/**
|
||||
* Handle the bulk DELETE request sent by wipeServer. Returns a 503.
|
||||
*/
|
||||
function storageHandler(request, response) {
|
||||
do_check_eq("DELETE", request.method);
|
||||
do_check_true(request.hasHeader("X-Confirm-Delete"));
|
||||
response.setStatusLine(request.httpVersion, 503, "Service Unavailable");
|
||||
}
|
||||
|
||||
let server = httpd_setup({
|
||||
"/1.1/johndoe/storage": storageHandler
|
||||
});
|
||||
setUpTestFixtures();
|
||||
|
||||
_("Try deletion.");
|
||||
let error;
|
||||
try {
|
||||
Service.wipeServer();
|
||||
do_throw("Should have thrown!");
|
||||
} catch (ex) {
|
||||
error = ex;
|
||||
}
|
||||
do_check_eq(error.status, 503);
|
||||
|
||||
server.stop(run_next_test);
|
||||
Svc.Prefs.resetBranch("");
|
||||
});
|
||||
|
||||
add_test(function test_wipeServer_all_connectionRefused() {
|
||||
_("Service.wipeServer() throws if it encounters a network problem.");
|
||||
setUpTestFixtures();
|
||||
|
||||
_("Try deletion.");
|
||||
try {
|
||||
Service.wipeServer();
|
||||
do_throw("Should have thrown!");
|
||||
} catch (ex) {
|
||||
do_check_eq(ex.result, Cr.NS_ERROR_CONNECTION_REFUSED);
|
||||
}
|
||||
|
||||
run_next_test();
|
||||
Svc.Prefs.resetBranch("");
|
||||
});
|
||||
|
|
|
@ -71,23 +71,6 @@ function cleanUpAndGo(server) {
|
|||
});
|
||||
}
|
||||
|
||||
let timer;
|
||||
function waitForZeroTimer(callback) {
|
||||
// First wait >100ms (nsITimers can take up to that much time to fire, so
|
||||
// we can account for the timer in delayedAutoconnect) and then two event
|
||||
// loop ticks (to account for the Utils.nextTick() in autoConnect).
|
||||
let ticks = 2;
|
||||
function wait() {
|
||||
if (ticks) {
|
||||
ticks -= 1;
|
||||
Utils.nextTick(wait);
|
||||
return;
|
||||
}
|
||||
callback();
|
||||
}
|
||||
timer = Utils.namedTimer(wait, 150, {}, "timer");
|
||||
}
|
||||
|
||||
function run_test() {
|
||||
initTestLogging("Trace");
|
||||
|
||||
|
@ -126,7 +109,7 @@ add_test(function test_prefAttributes() {
|
|||
do_check_eq(SyncScheduler.syncThreshold, THRESHOLD);
|
||||
|
||||
_("'globalScore' corresponds to preference, defaults to zero.");
|
||||
do_check_eq(Svc.Prefs.get('globalScore'), undefined);
|
||||
do_check_eq(Svc.Prefs.get('globalScore'), 0);
|
||||
do_check_eq(SyncScheduler.globalScore, 0);
|
||||
SyncScheduler.globalScore = SCORE;
|
||||
do_check_eq(SyncScheduler.globalScore, SCORE);
|
||||
|
@ -863,3 +846,80 @@ add_test(function test_sync_503_Retry_After() {
|
|||
|
||||
cleanUpAndGo(server);
|
||||
});
|
||||
|
||||
add_test(function test_loginError_recoverable_reschedules() {
|
||||
_("Verify that a recoverable login error schedules a new sync.");
|
||||
Service.username = "johndoe";
|
||||
Service.password = "ilovejane";
|
||||
Service.passphrase = "abcdeabcdeabcdeabcdeabcdea";
|
||||
Service.clusterURL = "http://localhost:8080/";
|
||||
Service.persistLogin();
|
||||
Status.resetSync(); // reset Status.login
|
||||
|
||||
Svc.Obs.add("weave:service:login:error", function onLoginError() {
|
||||
Svc.Obs.remove("weave:service:login:error", onLoginError);
|
||||
Utils.nextTick(function aLittleBitAfterLoginError() {
|
||||
do_check_eq(Status.login, LOGIN_FAILED_NETWORK_ERROR);
|
||||
|
||||
let expectedNextSync = Date.now() + SyncScheduler.syncInterval;
|
||||
do_check_true(SyncScheduler.nextSync > Date.now());
|
||||
do_check_true(SyncScheduler.nextSync <= expectedNextSync);
|
||||
do_check_true(SyncScheduler.syncTimer.delay > 0);
|
||||
do_check_true(SyncScheduler.syncTimer.delay <= SyncScheduler.syncInterval);
|
||||
|
||||
Svc.Obs.remove("weave:service:sync:start", onSyncStart);
|
||||
cleanUpAndGo();
|
||||
});
|
||||
});
|
||||
|
||||
// Let's set it up so that a sync is overdue, both in terms of previously
|
||||
// scheduled syncs and the global score. We still do not expect an immediate
|
||||
// sync because we just tried (duh).
|
||||
SyncScheduler.nextSync = Date.now() - 100000;
|
||||
SyncScheduler.globalScore = SINGLE_USER_THRESHOLD + 1;
|
||||
function onSyncStart() {
|
||||
do_throw("Shouldn't have started a sync!");
|
||||
}
|
||||
Svc.Obs.add("weave:service:sync:start", onSyncStart);
|
||||
|
||||
// Sanity check.
|
||||
do_check_eq(SyncScheduler.syncTimer, null);
|
||||
do_check_eq(Status.checkSetup(), STATUS_OK);
|
||||
do_check_eq(Status.login, LOGIN_SUCCEEDED);
|
||||
|
||||
SyncScheduler.scheduleNextSync(0);
|
||||
});
|
||||
|
||||
add_test(function test_loginError_fatal_clearsTriggers() {
|
||||
_("Verify that a fatal login error clears sync triggers.");
|
||||
Service.username = "johndoe";
|
||||
Service.password = "ilovejane";
|
||||
Service.passphrase = "abcdeabcdeabcdeabcdeabcdea";
|
||||
Service.clusterURL = "http://localhost:8080/";
|
||||
Service.persistLogin();
|
||||
Status.resetSync(); // reset Status.login
|
||||
|
||||
let server = httpd_setup({
|
||||
"/1.1/johndoe/info/collections": httpd_handler(401, "Unauthorized")
|
||||
});
|
||||
|
||||
Svc.Obs.add("weave:service:login:error", function onLoginError() {
|
||||
Svc.Obs.remove("weave:service:login:error", onLoginError);
|
||||
Utils.nextTick(function aLittleBitAfterLoginError() {
|
||||
do_check_eq(Status.login, LOGIN_FAILED_LOGIN_REJECTED);
|
||||
|
||||
do_check_eq(SyncScheduler.nextSync, 0);
|
||||
do_check_eq(SyncScheduler.syncTimer, null);
|
||||
|
||||
cleanUpAndGo(server);
|
||||
});
|
||||
});
|
||||
|
||||
// Sanity check.
|
||||
do_check_eq(SyncScheduler.nextSync, 0);
|
||||
do_check_eq(SyncScheduler.syncTimer, null);
|
||||
do_check_eq(Status.checkSetup(), STATUS_OK);
|
||||
do_check_eq(Status.login, LOGIN_SUCCEEDED);
|
||||
|
||||
SyncScheduler.scheduleNextSync(0);
|
||||
});
|
||||
|
|
|
@ -48,6 +48,7 @@ skip-if = os == "win" || os == "android"
|
|||
[test_keys.js]
|
||||
[test_load_modules.js]
|
||||
[test_log4moz.js]
|
||||
[test_node_reassignment.js]
|
||||
[test_notifications.js]
|
||||
[test_password_store.js]
|
||||
[test_password_tracker.js]
|
||||
|
|
|
@ -126,7 +126,7 @@ var Logger =
|
|||
var now = new Date()
|
||||
this.write(now.getFullYear() + "-" + (now.getMonth() < 9 ? '0' : '') +
|
||||
(now.getMonth() + 1) + "-" +
|
||||
(now.getDay() < 9 ? '0' : '') + (now.getDay() + 1) + " " +
|
||||
(now.getDate() < 9 ? '0' : '') + (now.getDate() + 1) + " " +
|
||||
(now.getHours() < 10 ? '0' : '') + now.getHours() + ":" +
|
||||
(now.getMinutes() < 10 ? '0' : '') + now.getMinutes() + ":" +
|
||||
(now.getSeconds() < 10 ? '0' : '') + now.getSeconds() + " " +
|
||||
|
|
|
@ -140,7 +140,6 @@ var TPS =
|
|||
Logger.logInfo("sync error; retrying...");
|
||||
this._syncErrors++;
|
||||
this._waitingForSync = false;
|
||||
Weave.Service.logout();
|
||||
Utils.nextTick(this.RunNextTestAction, this);
|
||||
}
|
||||
else if (this._waitingForSync) {
|
||||
|
@ -156,7 +155,6 @@ var TPS =
|
|||
// Wait a second before continuing, otherwise we can get
|
||||
// 'sync not complete' errors.
|
||||
Utils.namedTimer(function() {
|
||||
Weave.Service.logout();
|
||||
this.FinishAsyncOperation();
|
||||
}, 1000, this, "postsync");
|
||||
}
|
||||
|
|
|
@ -41,7 +41,7 @@ from setuptools import setup, find_packages
|
|||
version = '0.2.40'
|
||||
|
||||
deps = ['pulsebuildmonitor >= 0.2', 'MozillaPulse == .4',
|
||||
'mozinfo == 0.3.1', 'mozprofile == 0.1a',
|
||||
'mozinfo == 0.3.1', 'mozprofile == 0.1t',
|
||||
'mozprocess == 0.1a', 'mozrunner == 3.0a', 'mozregression == 0.3',
|
||||
'mozautolog >= 0.2.1']
|
||||
|
||||
|
|
|
@ -37,7 +37,7 @@
|
|||
|
||||
import datetime
|
||||
|
||||
def GenerateEmailBody(data, numpassed, numfailed, serverUrl):
|
||||
def GenerateEmailBody(data, numpassed, numfailed, serverUrl, buildUrl):
|
||||
|
||||
now = datetime.datetime.now()
|
||||
builddate = datetime.datetime.strptime(data['productversion']['buildid'],
|
||||
|
@ -46,7 +46,7 @@ def GenerateEmailBody(data, numpassed, numfailed, serverUrl):
|
|||
|
||||
row = """
|
||||
<tr>
|
||||
<td><a href="http://hg.mozilla.org/services/services-central/file/tip/services/sync/tests/tps/{name}">{name}</a></td>
|
||||
<td><a href="http://hg.mozilla.org/services/services-central/file/default/services/sync/tests/tps/{name}">{name}</a></td>
|
||||
<td>{state}</td>
|
||||
<td>{message}</td>
|
||||
</tr>
|
||||
|
@ -54,7 +54,7 @@ def GenerateEmailBody(data, numpassed, numfailed, serverUrl):
|
|||
|
||||
rowWithLog = """
|
||||
<tr>
|
||||
<td><a href="http://hg.mozilla.org/services/services-central/services/sync/tests/tps/file/tip/{name}">{name}</a></td>
|
||||
<td><a href="http://hg.mozilla.org/services/services-central/file/default/services/sync/tests/tps/{name}">{name}</a></td>
|
||||
<td>{state}</td>
|
||||
<td>{message} [<a href="{logurl}">view log</a>]</td>
|
||||
</tr>
|
||||
|
@ -72,6 +72,9 @@ def GenerateEmailBody(data, numpassed, numfailed, serverUrl):
|
|||
state=test['state'],
|
||||
message=test['message'] if test['message'] else 'None')
|
||||
|
||||
firefox_version = data['productversion']['version']
|
||||
if buildUrl is not None:
|
||||
firefox_version = "<a href='%s'>%s</a>" % (buildUrl, firefox_version)
|
||||
body = """
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
||||
<head>
|
||||
|
@ -169,7 +172,7 @@ def GenerateEmailBody(data, numpassed, numfailed, serverUrl):
|
|||
</html>
|
||||
|
||||
""".format(date=now.ctime(),
|
||||
firefox_version=data['productversion']['version'],
|
||||
firefox_version=firefox_version,
|
||||
firefox_date=builddate.ctime(),
|
||||
sync_version=data['addonversion']['version'],
|
||||
sync_type=data['synctype'],
|
||||
|
|
|
@ -433,7 +433,14 @@ class TPSTestRunner(object):
|
|||
from tps.emailtemplate import GenerateEmailBody
|
||||
|
||||
if body is None:
|
||||
body = GenerateEmailBody(self.postdata, self.numpassed, self.numfailed, self.config['account']['serverURL'])
|
||||
buildUrl = None
|
||||
if self.firefoxRunner and self.firefoxRunner.url:
|
||||
buildUrl = self.firefoxRunner.url
|
||||
body = GenerateEmailBody(self.postdata,
|
||||
self.numpassed,
|
||||
self.numfailed,
|
||||
self.config['account']['serverURL'],
|
||||
self.buildUrl)
|
||||
|
||||
subj = "TPS Report: "
|
||||
if self.numfailed == 0 and self.numpassed > 0:
|
||||
|
|
|
@ -49,7 +49,6 @@
|
|||
#include "nsILocalFile.h"
|
||||
#include "nsIObserver.h"
|
||||
#include "nsIObserverService.h"
|
||||
#include "nsIPrefService.h"
|
||||
#include "nsIProfileChangeStatus.h"
|
||||
#include "nsISimpleEnumerator.h"
|
||||
#include "nsIToolkitChromeRegistry.h"
|
||||
|
@ -68,6 +67,7 @@
|
|||
#include "nsReadableUtils.h"
|
||||
#include "mozilla/Services.h"
|
||||
#include "mozilla/Omnijar.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
|
@ -508,7 +508,7 @@ LoadExtensionDirectories(nsINIParser &parser,
|
|||
{
|
||||
nsresult rv;
|
||||
PRInt32 i = 0;
|
||||
nsCOMPtr<nsIPrefServiceInternal> prefs =
|
||||
nsCOMPtr<nsIPrefService> prefs =
|
||||
do_GetService("@mozilla.org/preferences-service;1");
|
||||
do {
|
||||
nsCAutoString buf("Extension");
|
||||
|
@ -531,7 +531,7 @@ LoadExtensionDirectories(nsINIParser &parser,
|
|||
XRE_AddJarManifestLocation(aType, dir);
|
||||
if (!prefs)
|
||||
continue;
|
||||
prefs->ReadExtensionPrefs(dir);
|
||||
mozilla::Preferences::ReadExtensionPrefs(dir);
|
||||
}
|
||||
else {
|
||||
aDirectories.AppendObject(dir);
|
||||
|
|