mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Backed out changeset 8673477f5fc2 (bug 862563) for Windows mochitest-5 failures
This commit is contained in:
parent
1346d5a2f8
commit
0c43bada3a
@ -62,6 +62,11 @@ let gDataNotificationInfoBar = {
|
||||
accessKey: gNavigatorBundle.getString("dataReportingNotification.button.accessKey"),
|
||||
popup: null,
|
||||
callback: function () {
|
||||
// Clicking the button to go to the preferences tab constitutes
|
||||
// acceptance of the data upload policy for Firefox Health Report.
|
||||
// This will ensure the checkbox is checked. The user has the option of
|
||||
// unchecking it.
|
||||
request.onUserAccept("info-bar-button-pressed");
|
||||
this._actionTaken = true;
|
||||
window.openAdvancedPreferences("dataChoicesTab");
|
||||
}.bind(this),
|
||||
@ -76,14 +81,16 @@ let gDataNotificationInfoBar = {
|
||||
buttons,
|
||||
function onEvent(event) {
|
||||
if (event == "removed") {
|
||||
if (!this._actionTaken) {
|
||||
request.onUserAccept("info-bar-dismissed");
|
||||
}
|
||||
|
||||
Services.obs.notifyObservers(null, "datareporting:notify-data-policy:close", null);
|
||||
}
|
||||
}.bind(this)
|
||||
);
|
||||
// It is important to defer calling onUserNotifyComplete() until we're
|
||||
// actually sure the notification was displayed. If we ever called
|
||||
// onUserNotifyComplete() without showing anything to the user, that
|
||||
// would be very good for user choice. It may also have legal impact.
|
||||
|
||||
// Tell the notification request we have displayed the notification.
|
||||
request.onUserNotifyComplete();
|
||||
},
|
||||
|
||||
@ -95,16 +102,18 @@ let gDataNotificationInfoBar = {
|
||||
}
|
||||
},
|
||||
|
||||
onNotifyDataPolicy: function (request) {
|
||||
try {
|
||||
this._displayDataPolicyInfoBar(request);
|
||||
} catch (ex) {
|
||||
request.onUserNotifyFailed(ex);
|
||||
}
|
||||
},
|
||||
|
||||
observe: function(subject, topic, data) {
|
||||
switch (topic) {
|
||||
case "datareporting:notify-data-policy:request":
|
||||
let request = subject.wrappedJSObject.object;
|
||||
try {
|
||||
this._displayDataPolicyInfoBar(request);
|
||||
} catch (ex) {
|
||||
request.onUserNotifyFailed(ex);
|
||||
return;
|
||||
}
|
||||
this.onNotifyDataPolicy(subject.wrappedJSObject.object);
|
||||
break;
|
||||
|
||||
case "datareporting:notify-data-policy:close":
|
||||
|
@ -2,49 +2,30 @@
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
let originalPolicy = null;
|
||||
|
||||
/**
|
||||
* Display a datareporting notification to the user.
|
||||
*
|
||||
* @param {String} name
|
||||
*/
|
||||
function sendNotifyRequest(name) {
|
||||
let ns = {};
|
||||
Cu.import("resource://gre/modules/services/datareporting/policy.jsm", ns);
|
||||
Cu.import("resource://gre/modules/Preferences.jsm", ns);
|
||||
Components.utils.import("resource://gre/modules/services/datareporting/policy.jsm", ns);
|
||||
Components.utils.import("resource://gre/modules/Preferences.jsm", ns);
|
||||
|
||||
let service = Cc["@mozilla.org/datareporting/service;1"]
|
||||
.getService(Ci.nsISupports)
|
||||
.wrappedJSObject;
|
||||
let service = Components.classes["@mozilla.org/datareporting/service;1"]
|
||||
.getService(Components.interfaces.nsISupports)
|
||||
.wrappedJSObject;
|
||||
ok(service.healthReporter, "Health Reporter instance is available.");
|
||||
|
||||
Cu.import("resource://gre/modules/Promise.jsm", ns);
|
||||
let deferred = ns.Promise.defer();
|
||||
|
||||
if (!originalPolicy) {
|
||||
originalPolicy = service.policy;
|
||||
}
|
||||
|
||||
let policyPrefs = new ns.Preferences("testing." + name + ".");
|
||||
ok(service._prefs, "Health Reporter prefs are available.");
|
||||
let hrPrefs = service._prefs;
|
||||
|
||||
let policy = new ns.DataReportingPolicy(policyPrefs, hrPrefs, service);
|
||||
policy.dataSubmissionPolicyBypassNotification = false;
|
||||
service.policy = policy;
|
||||
policy.firstRunDate = new Date(Date.now() - 24 * 60 * 60 * 1000);
|
||||
|
||||
service.healthReporter.onInit().then(function onSuccess () {
|
||||
is(policy.ensureUserNotified(), false, "User not notified about data policy on init.");
|
||||
ok(policy._userNotifyPromise, "_userNotifyPromise defined.");
|
||||
policy._userNotifyPromise.then(
|
||||
deferred.resolve.bind(deferred),
|
||||
deferred.reject.bind(deferred)
|
||||
);
|
||||
}.bind(this), deferred.reject.bind(deferred));
|
||||
is(policy.notifyState, policy.STATE_NOTIFY_UNNOTIFIED, "Policy is in unnotified state.");
|
||||
|
||||
return [policy, deferred.promise];
|
||||
service.healthReporter.onInit().then(function onInit() {
|
||||
is(policy.ensureNotifyResponse(new Date()), false, "User has not responded to policy.");
|
||||
});
|
||||
|
||||
return policy;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -74,7 +55,6 @@ function waitForNotificationClose(notification, cb) {
|
||||
let dumpAppender, rootLogger;
|
||||
|
||||
function test() {
|
||||
registerCleanupFunction(cleanup);
|
||||
waitForExplicitFinish();
|
||||
|
||||
let ns = {};
|
||||
@ -84,41 +64,29 @@ function test() {
|
||||
dumpAppender.level = ns.Log.Level.All;
|
||||
rootLogger.addAppender(dumpAppender);
|
||||
|
||||
closeAllNotifications().then(function onSuccess () {
|
||||
let notification = document.getElementById("global-notificationbox");
|
||||
let notification = document.getElementById("global-notificationbox");
|
||||
let policy;
|
||||
|
||||
notification.addEventListener("AlertActive", function active() {
|
||||
notification.removeEventListener("AlertActive", active, true);
|
||||
is(notification.allNotifications.length, 1, "Notification Displayed.");
|
||||
notification.addEventListener("AlertActive", function active() {
|
||||
notification.removeEventListener("AlertActive", active, true);
|
||||
|
||||
executeSoon(function afterNotification() {
|
||||
waitForNotificationClose(notification.currentNotification, function onClose() {
|
||||
is(notification.allNotifications.length, 0, "No notifications remain.");
|
||||
is(policy.dataSubmissionPolicyAcceptedVersion, 1, "Version pref set.");
|
||||
ok(policy.dataSubmissionPolicyNotifiedDate.getTime() > -1, "Date pref set.");
|
||||
test_multiple_windows();
|
||||
});
|
||||
notification.currentNotification.close();
|
||||
executeSoon(function afterNotification() {
|
||||
is(policy.notifyState, policy.STATE_NOTIFY_WAIT, "Policy is waiting for user response.");
|
||||
ok(!policy.dataSubmissionPolicyAccepted, "Data submission policy not yet accepted.");
|
||||
|
||||
waitForNotificationClose(notification.currentNotification, function onClose() {
|
||||
is(policy.notifyState, policy.STATE_NOTIFY_COMPLETE, "Closing info bar completes user notification.");
|
||||
ok(policy.dataSubmissionPolicyAccepted, "Data submission policy accepted.");
|
||||
is(policy.dataSubmissionPolicyResponseType, "accepted-info-bar-dismissed",
|
||||
"Reason for acceptance was info bar dismissal.");
|
||||
is(notification.allNotifications.length, 0, "No notifications remain.");
|
||||
test_multiple_windows();
|
||||
});
|
||||
}, true);
|
||||
|
||||
let [policy, promise] = sendNotifyRequest("single_window_notified");
|
||||
|
||||
is(policy.dataSubmissionPolicyAcceptedVersion, 0, "No version should be set on init.");
|
||||
is(policy.dataSubmissionPolicyNotifiedDate.getTime(), 0, "No date should be set on init.");
|
||||
is(policy.userNotifiedOfCurrentPolicy, false, "User not notified about datareporting policy.");
|
||||
|
||||
promise.then(function () {
|
||||
is(policy.dataSubmissionPolicyAcceptedVersion, 1, "Policy version set.");
|
||||
is(policy.dataSubmissionPolicyNotifiedDate.getTime() > 0, true, "Policy date set.");
|
||||
is(policy.userNotifiedOfCurrentPolicy, true, "User notified about datareporting policy.");
|
||||
}.bind(this), function (err) {
|
||||
throw err;
|
||||
notification.currentNotification.close();
|
||||
});
|
||||
}, true);
|
||||
|
||||
}.bind(this), function onError (err) {
|
||||
throw err;
|
||||
});
|
||||
policy = sendNotifyRequest("single_window_notified");
|
||||
}
|
||||
|
||||
function test_multiple_windows() {
|
||||
@ -130,7 +98,7 @@ function test_multiple_windows() {
|
||||
let notification2 = window2.document.getElementById("global-notificationbox");
|
||||
ok(notification2, "2nd window has a global notification box.");
|
||||
|
||||
let [policy, promise] = sendNotifyRequest("multiple_window_behavior");
|
||||
let policy;
|
||||
let displayCount = 0;
|
||||
let prefWindowClosed = false;
|
||||
let mutationObserversRemoved = false;
|
||||
@ -161,8 +129,8 @@ function test_multiple_windows() {
|
||||
|
||||
dump("Finishing multiple window test.\n");
|
||||
rootLogger.removeAppender(dumpAppender);
|
||||
dumpAppender = null;
|
||||
rootLogger = null;
|
||||
delete dumpAppender;
|
||||
delete rootLogger;
|
||||
finish();
|
||||
}
|
||||
let closeCount = 0;
|
||||
@ -175,8 +143,12 @@ function test_multiple_windows() {
|
||||
}
|
||||
|
||||
ok(true, "Closing info bar on one window closed them on all.");
|
||||
is(policy.userNotifiedOfCurrentPolicy, true, "Data submission policy accepted.");
|
||||
|
||||
is(policy.notifyState, policy.STATE_NOTIFY_COMPLETE,
|
||||
"Closing info bar with multiple windows completes notification.");
|
||||
ok(policy.dataSubmissionPolicyAccepted, "Data submission policy accepted.");
|
||||
is(policy.dataSubmissionPolicyResponseType, "accepted-info-bar-button-pressed",
|
||||
"Policy records reason for acceptance was button press.");
|
||||
is(notification1.allNotifications.length, 0, "No notifications remain on main window.");
|
||||
is(notification2.allNotifications.length, 0, "No notifications remain on 2nd window.");
|
||||
|
||||
@ -220,20 +192,7 @@ function test_multiple_windows() {
|
||||
executeSoon(onAlertDisplayed);
|
||||
}, true);
|
||||
|
||||
promise.then(null, function onError(err) {
|
||||
throw err;
|
||||
});
|
||||
policy = sendNotifyRequest("multiple_window_behavior");
|
||||
});
|
||||
}
|
||||
|
||||
function cleanup () {
|
||||
// In case some test fails.
|
||||
if (originalPolicy) {
|
||||
let service = Cc["@mozilla.org/datareporting/service;1"]
|
||||
.getService(Ci.nsISupports)
|
||||
.wrappedJSObject;
|
||||
service.policy = originalPolicy;
|
||||
}
|
||||
|
||||
return closeAllNotifications();
|
||||
}
|
||||
|
@ -7,26 +7,6 @@ XPCOMUtils.defineLazyModuleGetter(this, "Task",
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
|
||||
"resource://gre/modules/PlacesUtils.jsm");
|
||||
|
||||
function closeAllNotifications () {
|
||||
let notificationBox = document.getElementById("global-notificationbox");
|
||||
|
||||
if (!notificationBox || !notificationBox.currentNotification) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
let deferred = Promise.defer();
|
||||
for (let notification of notificationBox.allNotifications) {
|
||||
waitForNotificationClose(notification, function () {
|
||||
if (notificationBox.allNotifications.length === 0) {
|
||||
deferred.resolve();
|
||||
}
|
||||
});
|
||||
notification.close();
|
||||
}
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
function whenDelayedStartupFinished(aWindow, aCallback) {
|
||||
Services.obs.addObserver(function observer(aSubject, aTopic) {
|
||||
if (aWindow == aSubject) {
|
||||
|
@ -29,6 +29,7 @@ function test() {
|
||||
}
|
||||
|
||||
function testBasic(win, doc, policy) {
|
||||
is(policy.dataSubmissionPolicyAccepted, false, "Data submission policy not accepted.");
|
||||
is(policy.healthReportUploadEnabled, true, "Health Report upload enabled on app first run.");
|
||||
|
||||
let checkbox = doc.getElementById("submitHealthReportBox");
|
||||
|
@ -13,8 +13,6 @@ function runPaneTest(fn) {
|
||||
.policy;
|
||||
ok(policy, "Policy object defined");
|
||||
|
||||
resetPreferences();
|
||||
|
||||
fn(win, policy);
|
||||
}
|
||||
|
||||
@ -23,30 +21,11 @@ function runPaneTest(fn) {
|
||||
"chrome,titlebar,toolbar,centerscreen,dialog=no", "paneAdvanced");
|
||||
}
|
||||
|
||||
let logDetails = {
|
||||
dumpAppender: null,
|
||||
rootLogger: null,
|
||||
};
|
||||
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
resetPreferences();
|
||||
registerCleanupFunction(resetPreferences);
|
||||
|
||||
let ld = logDetails;
|
||||
registerCleanupFunction(() => {
|
||||
ld.rootLogger.removeAppender(ld.dumpAppender);
|
||||
delete ld.dumpAppender;
|
||||
delete ld.rootLogger;
|
||||
});
|
||||
|
||||
let ns = {};
|
||||
Cu.import("resource://gre/modules/Log.jsm", ns);
|
||||
ld.rootLogger = ns.Log.repository.rootLogger;
|
||||
ld.dumpAppender = new ns.Log.DumpAppender();
|
||||
ld.dumpAppender.level = ns.Log.Level.All;
|
||||
ld.rootLogger.addAppender(ld.dumpAppender);
|
||||
|
||||
Services.prefs.lockPref("datareporting.healthreport.uploadEnabled");
|
||||
runPaneTest(testUploadDisabled);
|
||||
}
|
||||
@ -64,8 +43,7 @@ function testUploadDisabled(win, policy) {
|
||||
function testBasic(win, policy) {
|
||||
let doc = win.document;
|
||||
|
||||
resetPreferences();
|
||||
|
||||
is(policy.dataSubmissionPolicyAccepted, false, "Data submission policy not accepted.");
|
||||
is(policy.healthReportUploadEnabled, true, "Health Report upload enabled on app first run.");
|
||||
|
||||
let checkbox = doc.getElementById("submitHealthReportBox");
|
||||
@ -85,10 +63,6 @@ function testBasic(win, policy) {
|
||||
}
|
||||
|
||||
function resetPreferences() {
|
||||
let service = Cc["@mozilla.org/datareporting/service;1"]
|
||||
.getService(Ci.nsISupports)
|
||||
.wrappedJSObject;
|
||||
service.policy._prefs.resetBranch("datareporting.policy.");
|
||||
service.policy.dataSubmissionPolicyBypassNotification = true;
|
||||
Services.prefs.clearUserPref("datareporting.healthreport.uploadEnabled");
|
||||
}
|
||||
|
||||
|
@ -6,7 +6,6 @@
|
||||
function test() {
|
||||
requestLongerTimeout(2);
|
||||
waitForExplicitFinish();
|
||||
resetPreferences();
|
||||
|
||||
try {
|
||||
let cm = Components.classes["@mozilla.org/categorymanager;1"]
|
||||
@ -102,10 +101,3 @@ function test() {
|
||||
|
||||
}
|
||||
|
||||
function resetPreferences() {
|
||||
let service = Components.classes["@mozilla.org/datareporting/service;1"]
|
||||
.getService(Components.interfaces.nsISupports)
|
||||
.wrappedJSObject;
|
||||
service.policy._prefs.resetBranch("datareporting.policy.");
|
||||
service.policy.dataSubmissionPolicyBypassNotification = true;
|
||||
}
|
@ -175,7 +175,6 @@ DataReportingService.prototype = Object.freeze({
|
||||
// The instance installs its own shutdown observers. So, we just
|
||||
// fire and forget: it will clean itself up.
|
||||
let reporter = this.healthReporter;
|
||||
this.policy.ensureUserNotified();
|
||||
}.bind(this),
|
||||
}, delayInterval, this.timer.TYPE_ONE_SHOT);
|
||||
|
||||
|
@ -3,10 +3,14 @@
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
pref("datareporting.policy.dataSubmissionEnabled", true);
|
||||
pref("datareporting.policy.firstRunTime", "0");
|
||||
pref("datareporting.policy.dataSubmissionPolicyAccepted", false);
|
||||
pref("datareporting.policy.dataSubmissionPolicyBypassAcceptance", false);
|
||||
pref("datareporting.policy.dataSubmissionPolicyNotifiedTime", "0");
|
||||
pref("datareporting.policy.dataSubmissionPolicyAcceptedVersion", 0);
|
||||
pref("datareporting.policy.dataSubmissionPolicyBypassNotification", false);
|
||||
pref("datareporting.policy.dataSubmissionPolicyResponseType", "");
|
||||
pref("datareporting.policy.dataSubmissionPolicyResponseTime", "0");
|
||||
pref("datareporting.policy.firstRunTime", "0");
|
||||
|
||||
pref("datareporting.policy.currentPolicyVersion", 2);
|
||||
pref("datareporting.policy.minimumPolicyVersion", 1);
|
||||
pref("datareporting.policy.minimumPolicyVersion.channel-beta", 2);
|
||||
|
||||
|
@ -26,27 +26,22 @@ this.MockPolicyListener = function MockPolicyListener() {
|
||||
}
|
||||
|
||||
MockPolicyListener.prototype = {
|
||||
onRequestDataUpload: function (request) {
|
||||
onRequestDataUpload: function onRequestDataUpload(request) {
|
||||
this._log.info("onRequestDataUpload invoked.");
|
||||
this.requestDataUploadCount++;
|
||||
this.lastDataRequest = request;
|
||||
},
|
||||
|
||||
onRequestRemoteDelete: function (request) {
|
||||
onRequestRemoteDelete: function onRequestRemoteDelete(request) {
|
||||
this._log.info("onRequestRemoteDelete invoked.");
|
||||
this.requestRemoteDeleteCount++;
|
||||
this.lastRemoteDeleteRequest = request;
|
||||
},
|
||||
|
||||
onNotifyDataPolicy: function (request, rejectMessage=null) {
|
||||
this._log.info("onNotifyDataPolicy invoked.");
|
||||
onNotifyDataPolicy: function onNotifyDataPolicy(request) {
|
||||
this._log.info("onNotifyUser invoked.");
|
||||
this.notifyUserCount++;
|
||||
this.lastNotifyRequest = request;
|
||||
if (rejectMessage) {
|
||||
request.onUserNotifyFailed(rejectMessage);
|
||||
} else {
|
||||
request.onUserNotifyComplete();
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -3,8 +3,14 @@
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
/**
|
||||
* This file is in transition. Most of its content needs to be moved under
|
||||
* /services/healthreport.
|
||||
* This file is in transition. It was originally conceived to fulfill the
|
||||
* needs of only Firefox Health Report. It is slowly being morphed into
|
||||
* fulfilling the needs of all data reporting facilities in Gecko applications.
|
||||
* As a result, some things feel a bit weird.
|
||||
*
|
||||
* DataReportingPolicy is both a driver for data reporting notification
|
||||
* (a true policy) and the driver for FHR data submission. The latter should
|
||||
* eventually be split into its own type and module.
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
@ -14,7 +20,6 @@
|
||||
this.EXPORTED_SYMBOLS = [
|
||||
"DataSubmissionRequest", // For test use only.
|
||||
"DataReportingPolicy",
|
||||
"DATAREPORTING_POLICY_VERSION",
|
||||
];
|
||||
|
||||
const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
|
||||
@ -27,11 +32,6 @@ Cu.import("resource://gre/modules/Log.jsm");
|
||||
Cu.import("resource://services-common/utils.js");
|
||||
Cu.import("resource://gre/modules/UpdateChannel.jsm");
|
||||
|
||||
// The current policy version number. If the version number stored in the prefs
|
||||
// is smaller than this, data upload will be disabled until the user is re-notified
|
||||
// about the policy changes.
|
||||
const DATAREPORTING_POLICY_VERSION = 1;
|
||||
|
||||
const MILLISECONDS_PER_DAY = 24 * 60 * 60 * 1000;
|
||||
|
||||
// Used as a sanity lower bound for dates stored in prefs. This module was
|
||||
@ -41,15 +41,33 @@ const OLDEST_ALLOWED_YEAR = 2012;
|
||||
/**
|
||||
* Represents a request to display data policy.
|
||||
*
|
||||
* Instances of this are created when the policy is requesting the user's
|
||||
* approval to agree to the data submission policy.
|
||||
*
|
||||
* Receivers of these instances are expected to call one or more of the on*
|
||||
* functions when events occur.
|
||||
*
|
||||
* When one of these requests is received, the first thing a callee should do
|
||||
* is present notification to the user of the data policy. When the notice
|
||||
* is displayed to the user, the callee should call `onUserNotifyComplete`.
|
||||
* This begins a countdown timer that upon completion will signal implicit
|
||||
* acceptance of the policy. If for whatever reason the callee could not
|
||||
* display a notice, it should call `onUserNotifyFailed`.
|
||||
*
|
||||
* If for whatever reason the callee could not display a notice,
|
||||
* it should call `onUserNotifyFailed`.
|
||||
* Once the user is notified of the policy, the callee has the option of
|
||||
* signaling explicit user acceptance or rejection of the policy. They do this
|
||||
* by calling `onUserAccept` or `onUserReject`, respectively. These functions
|
||||
* are essentially proxies to
|
||||
* DataReportingPolicy.{recordUserAcceptance,recordUserRejection}.
|
||||
*
|
||||
* If the user never explicitly accepts or rejects the policy, it will be
|
||||
* implicitly accepted after a specified duration of time. The notice is
|
||||
* expected to remain displayed even after implicit acceptance (in case the
|
||||
* user is away from the device). So, no event signaling implicit acceptance
|
||||
* is exposed.
|
||||
*
|
||||
* Receivers of instances of this type should treat it as a black box with
|
||||
* the exception of the on* functions.
|
||||
*
|
||||
* @param policy
|
||||
* (DataReportingPolicy) The policy instance this request came from.
|
||||
@ -60,13 +78,17 @@ function NotifyPolicyRequest(policy, deferred) {
|
||||
this.policy = policy;
|
||||
this.deferred = deferred;
|
||||
}
|
||||
NotifyPolicyRequest.prototype = Object.freeze({
|
||||
NotifyPolicyRequest.prototype = {
|
||||
/**
|
||||
* Called when the user is notified of the policy.
|
||||
*
|
||||
* This starts a countdown timer that will eventually signify implicit
|
||||
* acceptance of the data policy.
|
||||
*/
|
||||
onUserNotifyComplete: function () {
|
||||
return this.deferred.resolve();
|
||||
},
|
||||
onUserNotifyComplete: function onUserNotified() {
|
||||
this.deferred.resolve();
|
||||
return this.deferred.promise;
|
||||
},
|
||||
|
||||
/**
|
||||
* Called when there was an error notifying the user about the policy.
|
||||
@ -74,10 +96,32 @@ NotifyPolicyRequest.prototype = Object.freeze({
|
||||
* @param error
|
||||
* (Error) Explains what went wrong.
|
||||
*/
|
||||
onUserNotifyFailed: function (error) {
|
||||
return this.deferred.reject(error);
|
||||
onUserNotifyFailed: function onUserNotifyFailed(error) {
|
||||
this.deferred.reject(error);
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* Called when the user agreed to the data policy.
|
||||
*
|
||||
* @param reason
|
||||
* (string) How the user agreed to the policy.
|
||||
*/
|
||||
onUserAccept: function onUserAccept(reason) {
|
||||
this.policy.recordUserAcceptance(reason);
|
||||
},
|
||||
|
||||
/**
|
||||
* Called when the user rejected the data policy.
|
||||
*
|
||||
* @param reason
|
||||
* (string) How the user rejected the policy.
|
||||
*/
|
||||
onUserReject: function onUserReject(reason) {
|
||||
this.policy.recordUserRejection(reason);
|
||||
},
|
||||
};
|
||||
|
||||
Object.freeze(NotifyPolicyRequest.prototype);
|
||||
|
||||
/**
|
||||
* Represents a request to submit data.
|
||||
@ -194,7 +238,9 @@ this.DataSubmissionRequest.prototype = Object.freeze({
|
||||
* data collection practices.
|
||||
* 5. User should have opportunity to react to this notification before
|
||||
* data submission.
|
||||
* 6. If data submission fails, try at most 2 additional times before giving
|
||||
* 6. Display of notification without any explicit user action constitutes
|
||||
* implicit consent after a certain duration of time.
|
||||
* 7. If data submission fails, try at most 2 additional times before giving
|
||||
* up on that day's submission.
|
||||
*
|
||||
* The listener passed into the instance must have the following properties
|
||||
@ -243,9 +289,6 @@ this.DataReportingPolicy = function (prefs, healthReportPrefs, listener) {
|
||||
this._prefs = prefs;
|
||||
this._healthReportPrefs = healthReportPrefs;
|
||||
this._listener = listener;
|
||||
this._userNotifyPromise = null;
|
||||
|
||||
this._migratePrefs();
|
||||
|
||||
// If the policy version has changed, reset all preferences, so that
|
||||
// the notification reappears.
|
||||
@ -286,12 +329,30 @@ this.DataReportingPolicy = function (prefs, healthReportPrefs, listener) {
|
||||
this.nextDataSubmissionDate = this._futureDate(MILLISECONDS_PER_DAY);
|
||||
}
|
||||
|
||||
// Date at which we performed user notification of acceptance.
|
||||
// This is an instance variable because implicit acceptance should only
|
||||
// carry forward through a single application instance.
|
||||
this._dataSubmissionPolicyNotifiedDate = null;
|
||||
|
||||
// Record when we last requested for submitted data to be sent. This is
|
||||
// to avoid having multiple outstanding requests.
|
||||
this._inProgressSubmissionRequest = null;
|
||||
};
|
||||
|
||||
this.DataReportingPolicy.prototype = Object.freeze({
|
||||
/**
|
||||
* How long after first run we should notify about data submission.
|
||||
*/
|
||||
SUBMISSION_NOTIFY_INTERVAL_MSEC: 12 * 60 * 60 * 1000,
|
||||
|
||||
/**
|
||||
* Time that must elapse with no user action for implicit acceptance.
|
||||
*
|
||||
* THERE ARE POTENTIAL LEGAL IMPLICATIONS OF CHANGING THIS VALUE. Check with
|
||||
* Privacy and/or Legal before modifying.
|
||||
*/
|
||||
IMPLICIT_ACCEPTANCE_INTERVAL_MSEC: 8 * 60 * 60 * 1000,
|
||||
|
||||
/**
|
||||
* How often to poll to see if we need to do something.
|
||||
*
|
||||
@ -332,6 +393,13 @@ this.DataReportingPolicy.prototype = Object.freeze({
|
||||
60 * 60 * 1000,
|
||||
],
|
||||
|
||||
/**
|
||||
* State of user notification of data submission.
|
||||
*/
|
||||
STATE_NOTIFY_UNNOTIFIED: "not-notified",
|
||||
STATE_NOTIFY_WAIT: "waiting",
|
||||
STATE_NOTIFY_COMPLETE: "ok",
|
||||
|
||||
REQUIRED_LISTENERS: [
|
||||
"onRequestDataUpload",
|
||||
"onRequestRemoteDelete",
|
||||
@ -354,6 +422,22 @@ this.DataReportingPolicy.prototype = Object.freeze({
|
||||
OLDEST_ALLOWED_YEAR);
|
||||
},
|
||||
|
||||
/**
|
||||
* Short circuit policy checking and always assume acceptance.
|
||||
*
|
||||
* This shuld never be set by the user. Instead, it is a per-application or
|
||||
* per-deployment default pref.
|
||||
*/
|
||||
get dataSubmissionPolicyBypassAcceptance() {
|
||||
return this._prefs.get("dataSubmissionPolicyBypassAcceptance", false);
|
||||
},
|
||||
|
||||
/**
|
||||
* When the user was notified that data submission could occur.
|
||||
*
|
||||
* This is used for logging purposes. this._dataSubmissionPolicyNotifiedDate
|
||||
* is what's used internally.
|
||||
*/
|
||||
get dataSubmissionPolicyNotifiedDate() {
|
||||
return CommonUtils.getDatePref(this._prefs,
|
||||
"dataSubmissionPolicyNotifiedTime", 0,
|
||||
@ -366,12 +450,46 @@ this.DataReportingPolicy.prototype = Object.freeze({
|
||||
value, OLDEST_ALLOWED_YEAR);
|
||||
},
|
||||
|
||||
get dataSubmissionPolicyBypassNotification() {
|
||||
return this._prefs.get("dataSubmissionPolicyBypassNotification", false);
|
||||
/**
|
||||
* When the user accepted or rejected the data submission policy.
|
||||
*
|
||||
* If there was implicit acceptance, this will be set to the time of that.
|
||||
*/
|
||||
get dataSubmissionPolicyResponseDate() {
|
||||
return CommonUtils.getDatePref(this._prefs,
|
||||
"dataSubmissionPolicyResponseTime",
|
||||
0, this._log, OLDEST_ALLOWED_YEAR);
|
||||
},
|
||||
|
||||
set dataSubmissionPolicyBypassNotification(value) {
|
||||
return this._prefs.set("dataSubmissionPolicyBypassNotification", !!value);
|
||||
set dataSubmissionPolicyResponseDate(value) {
|
||||
this._log.debug("Setting user notified reaction date: " + value);
|
||||
CommonUtils.setDatePref(this._prefs,
|
||||
"dataSubmissionPolicyResponseTime",
|
||||
value, OLDEST_ALLOWED_YEAR);
|
||||
},
|
||||
|
||||
/**
|
||||
* Records the result of user notification of data submission policy.
|
||||
*
|
||||
* This is used for logging and diagnostics purposes. It can answer the
|
||||
* question "how was data submission agreed to on this profile?"
|
||||
*
|
||||
* Not all values are defined by this type and can come from other systems.
|
||||
*
|
||||
* The value must be a string and should be something machine readable. e.g.
|
||||
* "accept-user-clicked-ok-button-in-info-bar"
|
||||
*/
|
||||
get dataSubmissionPolicyResponseType() {
|
||||
return this._prefs.get("dataSubmissionPolicyResponseType",
|
||||
"none-recorded");
|
||||
},
|
||||
|
||||
set dataSubmissionPolicyResponseType(value) {
|
||||
if (typeof(value) != "string") {
|
||||
throw new Error("Value must be a string. Got " + typeof(value));
|
||||
}
|
||||
|
||||
this._prefs.set("dataSubmissionPolicyResponseType", value);
|
||||
},
|
||||
|
||||
/**
|
||||
@ -389,10 +507,6 @@ this.DataReportingPolicy.prototype = Object.freeze({
|
||||
this._prefs.set("dataSubmissionEnabled", !!value);
|
||||
},
|
||||
|
||||
get currentPolicyVersion() {
|
||||
return this._prefs.get("currentPolicyVersion", DATAREPORTING_POLICY_VERSION);
|
||||
},
|
||||
|
||||
/**
|
||||
* The minimum policy version which for dataSubmissionPolicyAccepted to
|
||||
* to be valid.
|
||||
@ -405,21 +519,48 @@ this.DataReportingPolicy.prototype = Object.freeze({
|
||||
channelPref : this._prefs.get("minimumPolicyVersion", 1);
|
||||
},
|
||||
|
||||
get dataSubmissionPolicyAcceptedVersion() {
|
||||
return this._prefs.get("dataSubmissionPolicyAcceptedVersion", 0);
|
||||
/**
|
||||
* Whether the user has accepted that data submission can occur.
|
||||
*
|
||||
* This overrides dataSubmissionEnabled.
|
||||
*/
|
||||
get dataSubmissionPolicyAccepted() {
|
||||
// Be conservative and default to false.
|
||||
return this._prefs.get("dataSubmissionPolicyAccepted", false);
|
||||
},
|
||||
|
||||
set dataSubmissionPolicyAcceptedVersion(value) {
|
||||
this._prefs.set("dataSubmissionPolicyAcceptedVersion", value);
|
||||
set dataSubmissionPolicyAccepted(value) {
|
||||
this._prefs.set("dataSubmissionPolicyAccepted", !!value);
|
||||
if (!!value) {
|
||||
let currentPolicyVersion = this._prefs.get("currentPolicyVersion", 1);
|
||||
this._prefs.set("dataSubmissionPolicyAcceptedVersion", currentPolicyVersion);
|
||||
} else {
|
||||
this._prefs.reset("dataSubmissionPolicyAcceptedVersion");
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Checks to see if the user has been notified about data submission
|
||||
* @return {bool}
|
||||
* The state of user notification of the data policy.
|
||||
*
|
||||
* This must be DataReportingPolicy.STATE_NOTIFY_COMPLETE before data
|
||||
* submission can occur.
|
||||
*
|
||||
* @return DataReportingPolicy.STATE_NOTIFY_* constant.
|
||||
*/
|
||||
get userNotifiedOfCurrentPolicy() {
|
||||
return this.dataSubmissionPolicyNotifiedDate.getTime() > 0 &&
|
||||
this.dataSubmissionPolicyAcceptedVersion >= this.currentPolicyVersion;
|
||||
get notifyState() {
|
||||
if (this.dataSubmissionPolicyResponseDate.getTime()) {
|
||||
return this.STATE_NOTIFY_COMPLETE;
|
||||
}
|
||||
|
||||
// We get the local state - not the state from prefs - because we don't want
|
||||
// a value from a previous application run to interfere. This prevents
|
||||
// a scenario where notification occurs just before application shutdown and
|
||||
// notification is displayed for shorter than the policy requires.
|
||||
if (!this._dataSubmissionPolicyNotifiedDate) {
|
||||
return this.STATE_NOTIFY_UNNOTIFIED;
|
||||
}
|
||||
|
||||
return this.STATE_NOTIFY_WAIT;
|
||||
},
|
||||
|
||||
/**
|
||||
@ -552,6 +693,43 @@ this.DataReportingPolicy.prototype = Object.freeze({
|
||||
return this._healthReportPrefs.locked("uploadEnabled");
|
||||
},
|
||||
|
||||
/**
|
||||
* Record user acceptance of data submission policy.
|
||||
*
|
||||
* Data submission will not be allowed to occur until this is called.
|
||||
*
|
||||
* This is typically called through the `onUserAccept` property attached to
|
||||
* the promise passed to `onUserNotify` in the policy listener. But, it can
|
||||
* be called through other interfaces at any time and the call will have
|
||||
* an impact on future data submissions.
|
||||
*
|
||||
* @param reason
|
||||
* (string) How the user accepted the data submission policy.
|
||||
*/
|
||||
recordUserAcceptance: function recordUserAcceptance(reason="no-reason") {
|
||||
this._log.info("User accepted data submission policy: " + reason);
|
||||
this.dataSubmissionPolicyResponseDate = this.now();
|
||||
this.dataSubmissionPolicyResponseType = "accepted-" + reason;
|
||||
this.dataSubmissionPolicyAccepted = true;
|
||||
},
|
||||
|
||||
/**
|
||||
* Record user rejection of submission policy.
|
||||
*
|
||||
* Data submission will not be allowed to occur if this is called.
|
||||
*
|
||||
* This is typically called through the `onUserReject` property attached to
|
||||
* the promise passed to `onUserNotify` in the policy listener. But, it can
|
||||
* be called through other interfaces at any time and the call will have an
|
||||
* impact on future data submissions.
|
||||
*/
|
||||
recordUserRejection: function recordUserRejection(reason="no-reason") {
|
||||
this._log.info("User rejected data submission policy: " + reason);
|
||||
this.dataSubmissionPolicyResponseDate = this.now();
|
||||
this.dataSubmissionPolicyResponseType = "rejected-" + reason;
|
||||
this.dataSubmissionPolicyAccepted = false;
|
||||
},
|
||||
|
||||
/**
|
||||
* Record the user's intent for whether FHR should upload data.
|
||||
*
|
||||
@ -704,13 +882,19 @@ this.DataReportingPolicy.prototype = Object.freeze({
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.ensureUserNotified()) {
|
||||
this._log.warn("The user has not been notified about the data submission " +
|
||||
"policy. Not attempting upload.");
|
||||
// If the user hasn't responded to the data policy, don't do anything.
|
||||
if (!this.ensureNotifyResponse(now)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Data submission is allowed to occur. Now comes the scheduling part.
|
||||
// User has opted out of data submission.
|
||||
if (!this.dataSubmissionPolicyAccepted && !this.dataSubmissionPolicyBypassAcceptance) {
|
||||
this._log.debug("Data submission has been disabled per user request.");
|
||||
return;
|
||||
}
|
||||
|
||||
// User has responded to data policy and data submission is enabled. Now
|
||||
// comes the scheduling part.
|
||||
|
||||
if (nowT < nextSubmissionDate.getTime()) {
|
||||
this._log.debug("Next data submission is scheduled in the future: " +
|
||||
@ -722,62 +906,82 @@ this.DataReportingPolicy.prototype = Object.freeze({
|
||||
},
|
||||
|
||||
/**
|
||||
* Ensure that the data policy notification has been displayed.
|
||||
* Ensure user has responded to data submission policy.
|
||||
*
|
||||
* This must be called before data submission. If the policy has not been
|
||||
* displayed, data submission must not occur.
|
||||
* responded to, data submission must not occur.
|
||||
*
|
||||
* @return bool Whether the notification has been displayed.
|
||||
* @return bool Whether user has responded to data policy.
|
||||
*/
|
||||
ensureUserNotified: function () {
|
||||
if (this.userNotifiedOfCurrentPolicy || this.dataSubmissionPolicyBypassNotification) {
|
||||
ensureNotifyResponse: function ensureNotifyResponse(now) {
|
||||
if (this.dataSubmissionPolicyBypassAcceptance) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// The user has not been notified yet, but is in the process of being notified.
|
||||
if (this._userNotifyPromise) {
|
||||
let notifyState = this.notifyState;
|
||||
|
||||
if (notifyState == this.STATE_NOTIFY_UNNOTIFIED) {
|
||||
let notifyAt = new Date(this.firstRunDate.getTime() +
|
||||
this.SUBMISSION_NOTIFY_INTERVAL_MSEC);
|
||||
|
||||
if (now.getTime() < notifyAt.getTime()) {
|
||||
this._log.debug("Don't have to notify about data submission yet.");
|
||||
return false;
|
||||
}
|
||||
|
||||
let onComplete = function onComplete() {
|
||||
this._log.info("Data submission notification presented.");
|
||||
let now = this.now();
|
||||
|
||||
this._dataSubmissionPolicyNotifiedDate = now;
|
||||
this.dataSubmissionPolicyNotifiedDate = now;
|
||||
}.bind(this);
|
||||
|
||||
let deferred = Promise.defer();
|
||||
|
||||
deferred.promise.then(onComplete, (error) => {
|
||||
this._log.warn("Data policy notification presentation failed: " +
|
||||
CommonUtils.exceptionStr(error));
|
||||
});
|
||||
|
||||
this._log.info("Requesting display of data policy.");
|
||||
let request = new NotifyPolicyRequest(this, deferred);
|
||||
|
||||
try {
|
||||
this._listener.onNotifyDataPolicy(request);
|
||||
} catch (ex) {
|
||||
this._log.warn("Exception when calling onNotifyDataPolicy: " +
|
||||
CommonUtils.exceptionStr(ex));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
let deferred = Promise.defer();
|
||||
deferred.promise.then((function onSuccess() {
|
||||
this._recordDataPolicyNotification(this.now(), this.currentPolicyVersion);
|
||||
this._userNotifyPromise = null;
|
||||
}).bind(this), ((error) => {
|
||||
this._log.warn("Data policy notification presentation failed: " +
|
||||
CommonUtils.exceptionStr(error));
|
||||
this._userNotifyPromise = null;
|
||||
}).bind(this));
|
||||
// We're waiting for user action or implicit acceptance after display.
|
||||
if (notifyState == this.STATE_NOTIFY_WAIT) {
|
||||
// Check for implicit acceptance.
|
||||
let implicitAcceptance =
|
||||
this._dataSubmissionPolicyNotifiedDate.getTime() +
|
||||
this.IMPLICIT_ACCEPTANCE_INTERVAL_MSEC;
|
||||
|
||||
this._log.info("Requesting display of data policy.");
|
||||
let request = new NotifyPolicyRequest(this, deferred);
|
||||
try {
|
||||
this._listener.onNotifyDataPolicy(request);
|
||||
} catch (ex) {
|
||||
this._log.warn("Exception when calling onNotifyDataPolicy: " +
|
||||
CommonUtils.exceptionStr(ex));
|
||||
this._log.debug("Now: " + now.getTime());
|
||||
this._log.debug("Will accept: " + implicitAcceptance);
|
||||
if (now.getTime() < implicitAcceptance) {
|
||||
this._log.debug("Still waiting for reaction or implicit acceptance. " +
|
||||
"Now: " + now.getTime() + " < " +
|
||||
"Accept: " + implicitAcceptance);
|
||||
return false;
|
||||
}
|
||||
|
||||
this.recordUserAcceptance("implicit-time-elapsed");
|
||||
return true;
|
||||
}
|
||||
|
||||
this._userNotifyPromise = deferred.promise;
|
||||
// If this happens, we have a coding error in this file.
|
||||
if (notifyState != this.STATE_NOTIFY_COMPLETE) {
|
||||
throw new Error("Unknown notification state: " + notifyState);
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
_recordDataPolicyNotification: function (date, version) {
|
||||
this._log.debug("Recording data policy notification to version " + version +
|
||||
" on date " + date);
|
||||
this.dataSubmissionPolicyNotifiedDate = date;
|
||||
this.dataSubmissionPolicyAcceptedVersion = version;
|
||||
},
|
||||
|
||||
_migratePrefs: function () {
|
||||
// Current prefs are mostly the same than the old ones, except for some deprecated ones.
|
||||
this._prefs.reset([
|
||||
"dataSubmissionPolicyAccepted",
|
||||
"dataSubmissionPolicyBypassAcceptance",
|
||||
"dataSubmissionPolicyResponseType",
|
||||
"dataSubmissionPolicyResponseTime"
|
||||
]);
|
||||
return true;
|
||||
},
|
||||
|
||||
_processInProgressSubmission: function _processInProgressSubmission() {
|
||||
|
@ -9,7 +9,6 @@ Cu.import("resource://gre/modules/Preferences.jsm");
|
||||
Cu.import("resource://gre/modules/services/datareporting/policy.jsm");
|
||||
Cu.import("resource://testing-common/services/datareporting/mocks.jsm");
|
||||
Cu.import("resource://gre/modules/UpdateChannel.jsm");
|
||||
Cu.import("resource://gre/modules/Task.jsm");
|
||||
|
||||
function getPolicy(name,
|
||||
aCurrentPolicyVersion = 1,
|
||||
@ -38,22 +37,6 @@ function getPolicy(name,
|
||||
return [policy, policyPrefs, healthReportPrefs, listener];
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure that the notification has been displayed to the user therefore having
|
||||
* policy.ensureUserNotified() === true, which will allow for a successful
|
||||
* data upload and afterwards does a call to policy.checkStateAndTrigger()
|
||||
* @param {Policy} policy
|
||||
* @return {Promise}
|
||||
*/
|
||||
function ensureUserNotifiedAndTrigger(policy) {
|
||||
return Task.spawn(function* ensureUserNotifiedAndTrigger () {
|
||||
policy.ensureUserNotified();
|
||||
yield policy._listener.lastNotifyRequest.deferred.promise;
|
||||
do_check_true(policy.userNotifiedOfCurrentPolicy);
|
||||
policy.checkStateAndTrigger();
|
||||
});
|
||||
}
|
||||
|
||||
function defineNow(policy, now) {
|
||||
print("Adjusting fake system clock to " + now);
|
||||
Object.defineProperty(policy, "now", {
|
||||
@ -83,8 +66,7 @@ add_test(function test_constructor() {
|
||||
let tomorrow = Date.now() + 24 * 60 * 60 * 1000;
|
||||
do_check_true(tomorrow - policy.nextDataSubmissionDate.getTime() < 1000);
|
||||
|
||||
do_check_eq(policy.dataSubmissionPolicyAcceptedVersion, 0);
|
||||
do_check_false(policy.userNotifiedOfCurrentPolicy);
|
||||
do_check_eq(policy.notifyState, policy.STATE_NOTIFY_UNNOTIFIED);
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
@ -99,23 +81,29 @@ add_test(function test_prefs() {
|
||||
do_check_eq(policyPrefs.get("firstRunTime"), nowT);
|
||||
do_check_eq(policy.firstRunDate.getTime(), nowT);
|
||||
|
||||
policy.dataSubmissionPolicyNotifiedDate = now;
|
||||
policy.dataSubmissionPolicyNotifiedDate= now;
|
||||
do_check_eq(policyPrefs.get("dataSubmissionPolicyNotifiedTime"), nowT);
|
||||
do_check_neq(policy.dataSubmissionPolicyNotifiedDate, null);
|
||||
do_check_eq(policy.dataSubmissionPolicyNotifiedDate.getTime(), nowT);
|
||||
|
||||
policy.dataSubmissionPolicyResponseDate = now;
|
||||
do_check_eq(policyPrefs.get("dataSubmissionPolicyResponseTime"), nowT);
|
||||
do_check_eq(policy.dataSubmissionPolicyResponseDate.getTime(), nowT);
|
||||
|
||||
policy.dataSubmissionPolicyResponseType = "type-1";
|
||||
do_check_eq(policyPrefs.get("dataSubmissionPolicyResponseType"), "type-1");
|
||||
do_check_eq(policy.dataSubmissionPolicyResponseType, "type-1");
|
||||
|
||||
policy.dataSubmissionEnabled = false;
|
||||
do_check_false(policyPrefs.get("dataSubmissionEnabled", true));
|
||||
do_check_false(policy.dataSubmissionEnabled);
|
||||
|
||||
let new_version = DATAREPORTING_POLICY_VERSION + 1;
|
||||
policy.dataSubmissionPolicyAcceptedVersion = new_version;
|
||||
do_check_eq(policyPrefs.get("dataSubmissionPolicyAcceptedVersion"), new_version);
|
||||
policy.dataSubmissionPolicyAccepted = false;
|
||||
do_check_false(policyPrefs.get("dataSubmissionPolicyAccepted", true));
|
||||
do_check_false(policy.dataSubmissionPolicyAccepted);
|
||||
|
||||
do_check_false(policy.dataSubmissionPolicyBypassNotification);
|
||||
policy.dataSubmissionPolicyBypassNotification = true;
|
||||
do_check_true(policy.dataSubmissionPolicyBypassNotification);
|
||||
do_check_true(policyPrefs.get("dataSubmissionPolicyBypassNotification"));
|
||||
do_check_false(policy.dataSubmissionPolicyBypassAcceptance);
|
||||
policyPrefs.set("dataSubmissionPolicyBypassAcceptance", true);
|
||||
do_check_true(policy.dataSubmissionPolicyBypassAcceptance);
|
||||
|
||||
policy.lastDataSubmissionRequestedDate = now;
|
||||
do_check_eq(hrPrefs.get("lastDataSubmissionRequestedTime"), nowT);
|
||||
@ -154,78 +142,153 @@ add_test(function test_prefs() {
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
add_task(function test_migratePrefs () {
|
||||
let [policy, policyPrefs, hrPrefs, listener] = getPolicy("migratePrefs");
|
||||
let outdated_prefs = {
|
||||
dataSubmissionPolicyAccepted: true,
|
||||
dataSubmissionPolicyBypassAcceptance: true,
|
||||
dataSubmissionPolicyResponseType: "something",
|
||||
dataSubmissionPolicyResponseTime: Date.now() + "",
|
||||
};
|
||||
add_test(function test_notify_state_prefs() {
|
||||
let [policy, policyPrefs, hrPrefs, listener] = getPolicy("notify_state_prefs");
|
||||
|
||||
// Test removal of old prefs.
|
||||
for (let name in outdated_prefs) {
|
||||
policyPrefs.set(name, outdated_prefs[name]);
|
||||
}
|
||||
policy._migratePrefs();
|
||||
for (let name in outdated_prefs) {
|
||||
do_check_false(policyPrefs.has(name));
|
||||
}
|
||||
do_check_eq(policy.notifyState, policy.STATE_NOTIFY_UNNOTIFIED);
|
||||
|
||||
policy._dataSubmissionPolicyNotifiedDate = new Date();
|
||||
do_check_eq(policy.notifyState, policy.STATE_NOTIFY_WAIT);
|
||||
|
||||
policy.dataSubmissionPolicyResponseDate = new Date();
|
||||
policy._dataSubmissionPolicyNotifiedDate = null;
|
||||
do_check_eq(policy.notifyState, policy.STATE_NOTIFY_COMPLETE);
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
add_task(function test_userNotifiedOfCurrentPolicy () {
|
||||
add_task(function test_initial_submission_notification() {
|
||||
let [policy, policyPrefs, hrPrefs, listener] = getPolicy("initial_submission_notification");
|
||||
|
||||
do_check_false(policy.userNotifiedOfCurrentPolicy,
|
||||
"The initial state should be unnotified.");
|
||||
do_check_eq(policy.dataSubmissionPolicyNotifiedDate.getTime(), 0);
|
||||
|
||||
policy.dataSubmissionPolicyAcceptedVersion = DATAREPORTING_POLICY_VERSION;
|
||||
do_check_false(policy.userNotifiedOfCurrentPolicy,
|
||||
"The default state of the date should have a time of 0 and it should therefore fail");
|
||||
do_check_eq(policy.dataSubmissionPolicyNotifiedDate.getTime(), 0,
|
||||
"Updating the accepted version should not set a notified date.");
|
||||
|
||||
policy._recordDataPolicyNotification(new Date(), DATAREPORTING_POLICY_VERSION);
|
||||
do_check_true(policy.userNotifiedOfCurrentPolicy,
|
||||
"Using the proper API causes user notification to report as true.");
|
||||
|
||||
// It is assumed that later versions of the policy will incorporate previous
|
||||
// ones, therefore this should also return true.
|
||||
policy._recordDataPolicyNotification(new Date(), DATAREPORTING_POLICY_VERSION);
|
||||
policy.dataSubmissionPolicyAcceptedVersion = DATAREPORTING_POLICY_VERSION + 1;
|
||||
do_check_true(policy.userNotifiedOfCurrentPolicy, 'A future version of the policy should pass.');
|
||||
|
||||
policy._recordDataPolicyNotification(new Date(), DATAREPORTING_POLICY_VERSION);
|
||||
policy.dataSubmissionPolicyAcceptedVersion = DATAREPORTING_POLICY_VERSION - 1;
|
||||
do_check_false(policy.userNotifiedOfCurrentPolicy, 'A previous version of the policy should fail.');
|
||||
});
|
||||
|
||||
add_task(function* test_notification_displayed () {
|
||||
let [policy, policyPrefs, hrPrefs, listener] = getPolicy("notification_accept_displayed");
|
||||
|
||||
do_check_eq(listener.requestDataUploadCount, 0);
|
||||
do_check_eq(listener.notifyUserCount, 0);
|
||||
|
||||
// Fresh instances should not do anything initially.
|
||||
policy.checkStateAndTrigger();
|
||||
do_check_eq(listener.notifyUserCount, 0);
|
||||
|
||||
// We still shouldn't notify up to the millisecond before the barrier.
|
||||
defineNow(policy, new Date(policy.firstRunDate.getTime() +
|
||||
policy.SUBMISSION_NOTIFY_INTERVAL_MSEC - 1));
|
||||
policy.checkStateAndTrigger();
|
||||
do_check_eq(listener.notifyUserCount, 0);
|
||||
do_check_null(policy._dataSubmissionPolicyNotifiedDate);
|
||||
do_check_eq(policy.dataSubmissionPolicyNotifiedDate.getTime(), 0);
|
||||
|
||||
// Uploads will trigger user notifications as needed.
|
||||
// We have crossed the threshold. We should see notification.
|
||||
defineNow(policy, new Date(policy.firstRunDate.getTime() +
|
||||
policy.SUBMISSION_NOTIFY_INTERVAL_MSEC));
|
||||
policy.checkStateAndTrigger();
|
||||
do_check_eq(listener.notifyUserCount, 1);
|
||||
do_check_eq(listener.requestDataUploadCount, 0);
|
||||
|
||||
yield ensureUserNotifiedAndTrigger(policy);
|
||||
|
||||
do_check_eq(listener.notifyUserCount, 1);
|
||||
yield listener.lastNotifyRequest.onUserNotifyComplete();
|
||||
do_check_true(policy._dataSubmissionPolicyNotifiedDate instanceof Date);
|
||||
do_check_true(policy.dataSubmissionPolicyNotifiedDate.getTime() > 0);
|
||||
do_check_true(policy.userNotifiedOfCurrentPolicy);
|
||||
do_check_eq(policy.dataSubmissionPolicyNotifiedDate.getTime(),
|
||||
policy._dataSubmissionPolicyNotifiedDate.getTime());
|
||||
do_check_eq(policy.notifyState, policy.STATE_NOTIFY_WAIT);
|
||||
});
|
||||
|
||||
add_task(function* test_submission_kill_switch() {
|
||||
let [policy, policyPrefs, hrPrefs, listener] = getPolicy("submission_kill_switch");
|
||||
policy.nextDataSubmissionDate = new Date(Date.now() - 24 * 60 * 60 * 1000);
|
||||
add_test(function test_bypass_acceptance() {
|
||||
let [policy, policyPrefs, hrPrefs, listener] = getPolicy("bypass_acceptance");
|
||||
|
||||
policyPrefs.set("dataSubmissionPolicyBypassAcceptance", true);
|
||||
do_check_false(policy.dataSubmissionPolicyAccepted);
|
||||
do_check_true(policy.dataSubmissionPolicyBypassAcceptance);
|
||||
defineNow(policy, new Date(policy.nextDataSubmissionDate.getTime()));
|
||||
policy.checkStateAndTrigger();
|
||||
do_check_eq(listener.requestDataUploadCount, 1);
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
add_task(function test_notification_implicit_acceptance() {
|
||||
let [policy, policyPrefs, hrPrefs, listener] = getPolicy("notification_implicit_acceptance");
|
||||
|
||||
let now = new Date(policy.nextDataSubmissionDate.getTime() -
|
||||
policy.SUBMISSION_NOTIFY_INTERVAL_MSEC + 1);
|
||||
defineNow(policy, now);
|
||||
policy.checkStateAndTrigger();
|
||||
do_check_eq(listener.notifyUserCount, 1);
|
||||
yield listener.lastNotifyRequest.onUserNotifyComplete();
|
||||
do_check_eq(policy.dataSubmissionPolicyResponseType, "none-recorded");
|
||||
|
||||
do_check_true(5000 < policy.IMPLICIT_ACCEPTANCE_INTERVAL_MSEC);
|
||||
defineNow(policy, new Date(now.getTime() + 5000));
|
||||
policy.checkStateAndTrigger();
|
||||
do_check_eq(listener.notifyUserCount, 1);
|
||||
do_check_eq(policy.notifyState, policy.STATE_NOTIFY_WAIT);
|
||||
do_check_eq(policy.dataSubmissionPolicyResponseDate.getTime(), 0);
|
||||
do_check_eq(policy.dataSubmissionPolicyResponseType, "none-recorded");
|
||||
|
||||
defineNow(policy, new Date(now.getTime() + policy.IMPLICIT_ACCEPTANCE_INTERVAL_MSEC + 1));
|
||||
policy.checkStateAndTrigger();
|
||||
do_check_eq(listener.notifyUserCount, 1);
|
||||
do_check_eq(policy.notifyState, policy.STATE_NOTIFY_COMPLETE);
|
||||
do_check_eq(policy.dataSubmissionPolicyResponseDate.getTime(), policy.now().getTime());
|
||||
do_check_eq(policy.dataSubmissionPolicyResponseType, "accepted-implicit-time-elapsed");
|
||||
});
|
||||
|
||||
add_task(function test_notification_rejected() {
|
||||
// User notification failed. We should not record it as being presented.
|
||||
let [policy, policyPrefs, hrPrefs, listener] = getPolicy("notification_failed");
|
||||
|
||||
let now = new Date(policy.nextDataSubmissionDate.getTime() -
|
||||
policy.SUBMISSION_NOTIFY_INTERVAL_MSEC + 1);
|
||||
defineNow(policy, now);
|
||||
policy.checkStateAndTrigger();
|
||||
do_check_eq(listener.notifyUserCount, 1);
|
||||
yield listener.lastNotifyRequest.onUserNotifyFailed(new Error("testing failed."));
|
||||
do_check_null(policy._dataSubmissionPolicyNotifiedDate);
|
||||
do_check_eq(policy.dataSubmissionPolicyNotifiedDate.getTime(), 0);
|
||||
do_check_eq(policy.notifyState, policy.STATE_NOTIFY_UNNOTIFIED);
|
||||
});
|
||||
|
||||
add_task(function test_notification_accepted() {
|
||||
let [policy, policyPrefs, hrPrefs, listener] = getPolicy("notification_accepted");
|
||||
|
||||
let now = new Date(policy.nextDataSubmissionDate.getTime() -
|
||||
policy.SUBMISSION_NOTIFY_INTERVAL_MSEC + 1);
|
||||
defineNow(policy, now);
|
||||
policy.checkStateAndTrigger();
|
||||
yield listener.lastNotifyRequest.onUserNotifyComplete();
|
||||
do_check_eq(policy.notifyState, policy.STATE_NOTIFY_WAIT);
|
||||
do_check_false(policy.dataSubmissionPolicyAccepted);
|
||||
listener.lastNotifyRequest.onUserNotifyComplete();
|
||||
listener.lastNotifyRequest.onUserAccept("foo-bar");
|
||||
do_check_eq(policy.notifyState, policy.STATE_NOTIFY_COMPLETE);
|
||||
do_check_eq(policy.dataSubmissionPolicyResponseType, "accepted-foo-bar");
|
||||
do_check_true(policy.dataSubmissionPolicyAccepted);
|
||||
do_check_eq(policy.dataSubmissionPolicyResponseDate.getTime(), now.getTime());
|
||||
});
|
||||
|
||||
add_task(function test_notification_rejected() {
|
||||
let [policy, policyPrefs, hrPrefs, listener] = getPolicy("notification_rejected");
|
||||
|
||||
let now = new Date(policy.nextDataSubmissionDate.getTime() -
|
||||
policy.SUBMISSION_NOTIFY_INTERVAL_MSEC + 1);
|
||||
defineNow(policy, now);
|
||||
policy.checkStateAndTrigger();
|
||||
yield listener.lastNotifyRequest.onUserNotifyComplete();
|
||||
do_check_eq(policy.notifyState, policy.STATE_NOTIFY_WAIT);
|
||||
do_check_false(policy.dataSubmissionPolicyAccepted);
|
||||
listener.lastNotifyRequest.onUserReject();
|
||||
do_check_eq(policy.notifyState, policy.STATE_NOTIFY_COMPLETE);
|
||||
do_check_eq(policy.dataSubmissionPolicyResponseType, "rejected-no-reason");
|
||||
do_check_false(policy.dataSubmissionPolicyAccepted);
|
||||
|
||||
// No requests for submission should occur if user has rejected.
|
||||
defineNow(policy, new Date(policy.nextDataSubmissionDate.getTime() + 10000));
|
||||
policy.checkStateAndTrigger();
|
||||
do_check_eq(listener.requestDataUploadCount, 0);
|
||||
yield ensureUserNotifiedAndTrigger(policy);
|
||||
});
|
||||
|
||||
add_test(function test_submission_kill_switch() {
|
||||
let [policy, policyPrefs, hrPrefs, listener] = getPolicy("submission_kill_switch");
|
||||
|
||||
policy.firstRunDate = new Date(Date.now() - 3 * 24 * 60 * 60 * 1000);
|
||||
policy.nextDataSubmissionDate = new Date(Date.now() - 24 * 60 * 60 * 1000);
|
||||
policy.recordUserAcceptance("accept-old-ack");
|
||||
do_check_true(policyPrefs.has("dataSubmissionPolicyAcceptedVersion"));
|
||||
policy.checkStateAndTrigger();
|
||||
do_check_eq(listener.requestDataUploadCount, 1);
|
||||
|
||||
defineNow(policy,
|
||||
@ -233,32 +296,39 @@ add_task(function* test_submission_kill_switch() {
|
||||
policy.dataSubmissionEnabled = false;
|
||||
policy.checkStateAndTrigger();
|
||||
do_check_eq(listener.requestDataUploadCount, 1);
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
add_task(function* test_upload_kill_switch() {
|
||||
let [policy, policyPrefs, hrPrefs, listener] = getPolicy("upload_kill_switch");
|
||||
add_test(function test_upload_kill_switch() {
|
||||
let [policy, policyPrefs, hrPrefs, listener] = getPolicy("upload_kill_switch");
|
||||
|
||||
yield ensureUserNotifiedAndTrigger(policy);
|
||||
defineNow(policy, policy._futureDate(-24 * 60 * 60 * 1000));
|
||||
policy.recordUserAcceptance();
|
||||
defineNow(policy, policy.nextDataSubmissionDate);
|
||||
|
||||
// So that we don't trigger deletions, which cause uploads to be delayed.
|
||||
hrPrefs.ignore("uploadEnabled", policy.uploadEnabledObserver);
|
||||
|
||||
policy.healthReportUploadEnabled = false;
|
||||
yield policy.checkStateAndTrigger();
|
||||
policy.checkStateAndTrigger();
|
||||
do_check_eq(listener.requestDataUploadCount, 0);
|
||||
policy.healthReportUploadEnabled = true;
|
||||
yield ensureUserNotifiedAndTrigger(policy);
|
||||
policy.checkStateAndTrigger();
|
||||
do_check_eq(listener.requestDataUploadCount, 1);
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
add_task(function* test_data_submission_no_data() {
|
||||
add_test(function test_data_submission_no_data() {
|
||||
let [policy, policyPrefs, hrPrefs, listener] = getPolicy("data_submission_no_data");
|
||||
|
||||
policy.dataSubmissionPolicyResponseDate = new Date(Date.now() - 24 * 60 * 60 * 1000);
|
||||
policy.dataSubmissionPolicyAccepted = true;
|
||||
let now = new Date(policy.nextDataSubmissionDate.getTime() + 1);
|
||||
defineNow(policy, now);
|
||||
do_check_eq(listener.requestDataUploadCount, 0);
|
||||
yield ensureUserNotifiedAndTrigger(policy);
|
||||
policy.checkStateAndTrigger();
|
||||
do_check_eq(listener.requestDataUploadCount, 1);
|
||||
listener.lastDataRequest.onNoDataAvailable();
|
||||
|
||||
@ -266,16 +336,20 @@ add_task(function* test_data_submission_no_data() {
|
||||
defineNow(policy, new Date(now.getTime() + 155 * 60 * 1000));
|
||||
policy.checkStateAndTrigger();
|
||||
do_check_eq(listener.requestDataUploadCount, 2);
|
||||
});
|
||||
|
||||
add_task(function* test_data_submission_submit_failure_hard() {
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
add_task(function test_data_submission_submit_failure_hard() {
|
||||
let [policy, policyPrefs, hrPrefs, listener] = getPolicy("data_submission_submit_failure_hard");
|
||||
|
||||
policy.dataSubmissionPolicyResponseDate = new Date(Date.now() - 24 * 60 * 60 * 1000);
|
||||
policy.dataSubmissionPolicyAccepted = true;
|
||||
let nextDataSubmissionDate = policy.nextDataSubmissionDate;
|
||||
let now = new Date(policy.nextDataSubmissionDate.getTime() + 1);
|
||||
defineNow(policy, now);
|
||||
|
||||
yield ensureUserNotifiedAndTrigger(policy);
|
||||
policy.checkStateAndTrigger();
|
||||
do_check_eq(listener.requestDataUploadCount, 1);
|
||||
yield listener.lastDataRequest.onSubmissionFailureHard();
|
||||
do_check_eq(listener.lastDataRequest.state,
|
||||
@ -289,27 +363,30 @@ add_task(function* test_data_submission_submit_failure_hard() {
|
||||
do_check_eq(listener.requestDataUploadCount, 1);
|
||||
});
|
||||
|
||||
add_task(function* test_data_submission_submit_try_again() {
|
||||
add_task(function test_data_submission_submit_try_again() {
|
||||
let [policy, policyPrefs, hrPrefs, listener] = getPolicy("data_submission_failure_soft");
|
||||
|
||||
policy.recordUserAcceptance();
|
||||
let nextDataSubmissionDate = policy.nextDataSubmissionDate;
|
||||
let now = new Date(policy.nextDataSubmissionDate.getTime());
|
||||
defineNow(policy, now);
|
||||
yield ensureUserNotifiedAndTrigger(policy);
|
||||
policy.checkStateAndTrigger();
|
||||
yield listener.lastDataRequest.onSubmissionFailureSoft();
|
||||
do_check_eq(policy.nextDataSubmissionDate.getTime(),
|
||||
nextDataSubmissionDate.getTime() + 15 * 60 * 1000);
|
||||
});
|
||||
|
||||
add_task(function* test_submission_daily_scheduling() {
|
||||
add_task(function test_submission_daily_scheduling() {
|
||||
let [policy, policyPrefs, hrPrefs, listener] = getPolicy("submission_daily_scheduling");
|
||||
|
||||
policy.dataSubmissionPolicyResponseDate = new Date(Date.now() - 24 * 60 * 60 * 1000);
|
||||
policy.dataSubmissionPolicyAccepted = true;
|
||||
let nextDataSubmissionDate = policy.nextDataSubmissionDate;
|
||||
|
||||
// Skip ahead to next submission date. We should get a submission request.
|
||||
let now = new Date(policy.nextDataSubmissionDate.getTime());
|
||||
defineNow(policy, now);
|
||||
yield ensureUserNotifiedAndTrigger(policy);
|
||||
policy.checkStateAndTrigger();
|
||||
do_check_eq(listener.requestDataUploadCount, 1);
|
||||
do_check_eq(policy.lastDataSubmissionRequestedDate.getTime(), now.getTime());
|
||||
|
||||
@ -337,17 +414,18 @@ add_task(function* test_submission_daily_scheduling() {
|
||||
new Date(nextScheduled.getTime() + 24 * 60 * 60 * 1000 + 200).getTime());
|
||||
});
|
||||
|
||||
add_task(function* test_submission_far_future_scheduling() {
|
||||
add_test(function test_submission_far_future_scheduling() {
|
||||
let [policy, policyPrefs, hrPrefs, listener] = getPolicy("submission_far_future_scheduling");
|
||||
|
||||
let now = new Date(Date.now() - 24 * 60 * 60 * 1000);
|
||||
defineNow(policy, now);
|
||||
yield ensureUserNotifiedAndTrigger(policy);
|
||||
policy.recordUserAcceptance();
|
||||
now = new Date();
|
||||
defineNow(policy, now);
|
||||
|
||||
let nextDate = policy._futureDate(3 * 24 * 60 * 60 * 1000 - 1);
|
||||
policy.nextDataSubmissionDate = nextDate;
|
||||
policy.checkStateAndTrigger();
|
||||
do_check_true(policy.dataSubmissionPolicyAcceptedVersion >= DATAREPORTING_POLICY_VERSION);
|
||||
do_check_eq(listener.requestDataUploadCount, 0);
|
||||
do_check_eq(policy.nextDataSubmissionDate.getTime(), nextDate.getTime());
|
||||
|
||||
@ -356,17 +434,21 @@ add_task(function* test_submission_far_future_scheduling() {
|
||||
do_check_eq(listener.requestDataUploadCount, 0);
|
||||
do_check_eq(policy.nextDataSubmissionDate.getTime(),
|
||||
policy._futureDate(24 * 60 * 60 * 1000).getTime());
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
add_task(function* test_submission_backoff() {
|
||||
add_task(function test_submission_backoff() {
|
||||
let [policy, policyPrefs, hrPrefs, listener] = getPolicy("submission_backoff");
|
||||
|
||||
do_check_eq(policy.FAILURE_BACKOFF_INTERVALS.length, 2);
|
||||
|
||||
policy.dataSubmissionPolicyResponseDate = new Date(Date.now() - 24 * 60 * 60 * 1000);
|
||||
policy.dataSubmissionPolicyAccepted = true;
|
||||
|
||||
let now = new Date(policy.nextDataSubmissionDate.getTime());
|
||||
defineNow(policy, now);
|
||||
yield ensureUserNotifiedAndTrigger(policy);
|
||||
policy.checkStateAndTrigger();
|
||||
do_check_eq(listener.requestDataUploadCount, 1);
|
||||
do_check_eq(policy.currentDaySubmissionFailureCount, 0);
|
||||
|
||||
@ -417,13 +499,15 @@ add_task(function* test_submission_backoff() {
|
||||
});
|
||||
|
||||
// Ensure that only one submission request can be active at a time.
|
||||
add_task(function* test_submission_expiring() {
|
||||
add_test(function test_submission_expiring() {
|
||||
let [policy, policyPrefs, hrPrefs, listener] = getPolicy("submission_expiring");
|
||||
|
||||
policy.dataSubmissionPolicyResponseDate = new Date(Date.now() - 24 * 60 * 60 * 1000);
|
||||
policy.dataSubmissionPolicyAccepted = true;
|
||||
let nextDataSubmission = policy.nextDataSubmissionDate;
|
||||
let now = new Date(policy.nextDataSubmissionDate.getTime());
|
||||
defineNow(policy, now);
|
||||
yield ensureUserNotifiedAndTrigger(policy);
|
||||
policy.checkStateAndTrigger();
|
||||
do_check_eq(listener.requestDataUploadCount, 1);
|
||||
defineNow(policy, new Date(now.getTime() + 500));
|
||||
policy.checkStateAndTrigger();
|
||||
@ -434,9 +518,11 @@ add_task(function* test_submission_expiring() {
|
||||
|
||||
policy.checkStateAndTrigger();
|
||||
do_check_eq(listener.requestDataUploadCount, 2);
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
add_task(function* test_delete_remote_data() {
|
||||
add_task(function test_delete_remote_data() {
|
||||
let [policy, policyPrefs, hrPrefs, listener] = getPolicy("delete_remote_data");
|
||||
|
||||
do_check_false(policy.pendingDeleteRemoteData);
|
||||
@ -460,13 +546,15 @@ add_task(function* test_delete_remote_data() {
|
||||
});
|
||||
|
||||
// Ensure that deletion requests take priority over regular data submission.
|
||||
add_task(function* test_delete_remote_data_priority() {
|
||||
add_test(function test_delete_remote_data_priority() {
|
||||
let [policy, policyPrefs, hrPrefs, listener] = getPolicy("delete_remote_data_priority");
|
||||
|
||||
let now = new Date();
|
||||
defineNow(policy, policy._futureDate(-24 * 60 * 60 * 1000));
|
||||
policy.recordUserAcceptance();
|
||||
defineNow(policy, new Date(now.getTime() + 3 * 24 * 60 * 60 * 1000));
|
||||
|
||||
yield ensureUserNotifiedAndTrigger(policy);
|
||||
policy.checkStateAndTrigger();
|
||||
do_check_eq(listener.requestDataUploadCount, 1);
|
||||
policy._inProgressSubmissionRequest = null;
|
||||
|
||||
@ -475,12 +563,16 @@ add_task(function* test_delete_remote_data_priority() {
|
||||
|
||||
do_check_eq(listener.requestRemoteDeleteCount, 1);
|
||||
do_check_eq(listener.requestDataUploadCount, 1);
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
add_test(function test_delete_remote_data_backoff() {
|
||||
let [policy, policyPrefs, hrPrefs, listener] = getPolicy("delete_remote_data_backoff");
|
||||
|
||||
let now = new Date();
|
||||
defineNow(policy, policy._futureDate(-24 * 60 * 60 * 1000));
|
||||
policy.recordUserAcceptance();
|
||||
defineNow(policy, now);
|
||||
policy.nextDataSubmissionDate = now;
|
||||
policy.deleteRemoteData();
|
||||
@ -508,12 +600,15 @@ add_test(function test_delete_remote_data_backoff() {
|
||||
|
||||
// If we request delete while an upload is in progress, delete should be
|
||||
// scheduled immediately after upload.
|
||||
add_task(function* test_delete_remote_data_in_progress_upload() {
|
||||
add_task(function test_delete_remote_data_in_progress_upload() {
|
||||
let [policy, policyPrefs, hrPrefs, listener] = getPolicy("delete_remote_data_in_progress_upload");
|
||||
|
||||
let now = new Date();
|
||||
defineNow(policy, policy._futureDate(-24 * 60 * 60 * 1000));
|
||||
policy.recordUserAcceptance();
|
||||
defineNow(policy, policy.nextDataSubmissionDate);
|
||||
|
||||
yield ensureUserNotifiedAndTrigger(policy);
|
||||
policy.checkStateAndTrigger();
|
||||
do_check_eq(listener.requestDataUploadCount, 1);
|
||||
defineNow(policy, policy._futureDate(50 * 1000));
|
||||
|
||||
@ -559,6 +654,7 @@ add_test(function test_polling() {
|
||||
if (count >= 2) {
|
||||
policy.stopPolling();
|
||||
|
||||
do_check_eq(listener.notifyUserCount, 0);
|
||||
do_check_eq(listener.requestDataUploadCount, 0);
|
||||
|
||||
run_next_test();
|
||||
@ -576,7 +672,79 @@ add_test(function test_polling() {
|
||||
policy.startPolling();
|
||||
});
|
||||
|
||||
add_task(function* test_record_health_report_upload_enabled() {
|
||||
// Ensure that implicit acceptance of policy is resolved through polling.
|
||||
//
|
||||
// This is probably covered by other tests. But, it's best to have explicit
|
||||
// coverage from a higher-level.
|
||||
add_test(function test_polling_implicit_acceptance() {
|
||||
let [policy, policyPrefs, hrPrefs, listener] = getPolicy("polling_implicit_acceptance");
|
||||
|
||||
// Redefine intervals with shorter, test-friendly values.
|
||||
Object.defineProperty(policy, "POLL_INTERVAL_MSEC", {
|
||||
value: 250,
|
||||
});
|
||||
|
||||
Object.defineProperty(policy, "IMPLICIT_ACCEPTANCE_INTERVAL_MSEC", {
|
||||
value: 700,
|
||||
});
|
||||
|
||||
let count = 0;
|
||||
|
||||
// Track JS elapsed time, so we can decide if we've waited for enough ticks.
|
||||
let start;
|
||||
Object.defineProperty(policy, "checkStateAndTrigger", {
|
||||
value: function CheckStateAndTriggerProxy() {
|
||||
count++;
|
||||
let now = Date.now();
|
||||
let delta = now - start;
|
||||
print("checkStateAndTrigger count: " + count + ", now " + now +
|
||||
", delta " + delta);
|
||||
|
||||
// Account for some slack.
|
||||
DataReportingPolicy.prototype.checkStateAndTrigger.call(policy);
|
||||
|
||||
// What should happen on different invocations:
|
||||
//
|
||||
// 1) We are inside the prompt interval so user gets prompted.
|
||||
// 2) still ~300ms away from implicit acceptance
|
||||
// 3) still ~50ms away from implicit acceptance
|
||||
// 4) Implicit acceptance recorded. Data submission requested.
|
||||
// 5) Request still pending. No new submission requested.
|
||||
//
|
||||
// Note that, due to the inaccuracy of timers, 4 might not happen until 5
|
||||
// firings have occurred. Yay. So we watch times, not just counts.
|
||||
|
||||
do_check_eq(listener.notifyUserCount, 1);
|
||||
|
||||
if (count == 1) {
|
||||
listener.lastNotifyRequest.onUserNotifyComplete();
|
||||
}
|
||||
|
||||
if (delta <= (policy.IMPLICIT_ACCEPTANCE_INTERVAL_MSEC + policy.POLL_INTERVAL_MSEC)) {
|
||||
do_check_false(policy.dataSubmissionPolicyAccepted);
|
||||
do_check_eq(listener.requestDataUploadCount, 0);
|
||||
} else if (count > 3) {
|
||||
do_check_true(policy.dataSubmissionPolicyAccepted);
|
||||
do_check_eq(policy.dataSubmissionPolicyResponseType,
|
||||
"accepted-implicit-time-elapsed");
|
||||
do_check_eq(listener.requestDataUploadCount, 1);
|
||||
}
|
||||
|
||||
if ((count > 4) && policy.dataSubmissionPolicyAccepted) {
|
||||
do_check_eq(listener.requestDataUploadCount, 1);
|
||||
policy.stopPolling();
|
||||
run_next_test();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
policy.firstRunDate = new Date(Date.now() - 4 * 24 * 60 * 60 * 1000);
|
||||
policy.nextDataSubmissionDate = new Date(Date.now());
|
||||
start = Date.now();
|
||||
policy.startPolling();
|
||||
});
|
||||
|
||||
add_task(function test_record_health_report_upload_enabled() {
|
||||
let [policy, policyPrefs, hrPrefs, listener] = getPolicy("record_health_report_upload_enabled");
|
||||
|
||||
// Preconditions.
|
||||
@ -623,10 +791,10 @@ add_test(function test_pref_change_initiates_deletion() {
|
||||
|
||||
hrPrefs.set("uploadEnabled", false);
|
||||
});
|
||||
|
||||
|
||||
add_task(function* test_policy_version() {
|
||||
let policy, policyPrefs, hrPrefs, listener, now, firstRunTime;
|
||||
function createPolicy(shouldBeNotified = false,
|
||||
function createPolicy(shouldBeAccepted = false,
|
||||
currentPolicyVersion = 1, minimumPolicyVersion = 1,
|
||||
branchMinimumVersionOverride) {
|
||||
[policy, policyPrefs, hrPrefs, listener] =
|
||||
@ -636,7 +804,8 @@ add_task(function* test_policy_version() {
|
||||
if (firstRun) {
|
||||
firstRunTime = policy.firstRunDate.getTime();
|
||||
do_check_true(firstRunTime > 0);
|
||||
now = new Date(policy.firstRunDate.getTime());
|
||||
now = new Date(policy.firstRunDate.getTime() +
|
||||
policy.SUBMISSION_NOTIFY_INTERVAL_MSEC);
|
||||
}
|
||||
else {
|
||||
// The first-run time should not be reset even after policy-version
|
||||
@ -644,18 +813,23 @@ add_task(function* test_policy_version() {
|
||||
do_check_eq(policy.firstRunDate.getTime(), firstRunTime);
|
||||
}
|
||||
defineNow(policy, now);
|
||||
do_check_eq(policy.userNotifiedOfCurrentPolicy, shouldBeNotified);
|
||||
do_check_eq(policy.dataSubmissionPolicyAccepted, shouldBeAccepted);
|
||||
}
|
||||
|
||||
function* triggerPolicyCheckAndEnsureNotified(notified = true) {
|
||||
function* triggerPolicyCheckAndEnsureNotified(notified = true, accept = true) {
|
||||
policy.checkStateAndTrigger();
|
||||
do_check_eq(listener.notifyUserCount, Number(notified));
|
||||
if (notified) {
|
||||
policy.ensureUserNotified();
|
||||
yield listener.lastNotifyRequest.deferred.promise;
|
||||
do_check_true(policy.userNotifiedOfCurrentPolicy);
|
||||
do_check_eq(policyPrefs.get("dataSubmissionPolicyAcceptedVersion"),
|
||||
policyPrefs.get("currentPolicyVersion"));
|
||||
yield listener.lastNotifyRequest.onUserNotifyComplete();
|
||||
if (accept) {
|
||||
listener.lastNotifyRequest.onUserAccept("because,");
|
||||
do_check_true(policy.dataSubmissionPolicyAccepted);
|
||||
do_check_eq(policyPrefs.get("dataSubmissionPolicyAcceptedVersion"),
|
||||
policyPrefs.get("currentPolicyVersion"));
|
||||
}
|
||||
else {
|
||||
do_check_false(policyPrefs.has("dataSubmissionPolicyAcceptedVersion"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -670,16 +844,16 @@ add_task(function* test_policy_version() {
|
||||
// version must be changed.
|
||||
let currentPolicyVersion = policyPrefs.get("currentPolicyVersion");
|
||||
let minimumPolicyVersion = policyPrefs.get("minimumPolicyVersion");
|
||||
createPolicy(false, ++currentPolicyVersion, minimumPolicyVersion);
|
||||
yield triggerPolicyCheckAndEnsureNotified(true);
|
||||
do_check_eq(policyPrefs.get("dataSubmissionPolicyAcceptedVersion"), currentPolicyVersion);
|
||||
createPolicy(true, ++currentPolicyVersion, minimumPolicyVersion);
|
||||
yield triggerPolicyCheckAndEnsureNotified(false);
|
||||
do_check_true(policy.dataSubmissionPolicyAccepted);
|
||||
do_check_eq(policyPrefs.get("dataSubmissionPolicyAcceptedVersion"),
|
||||
minimumPolicyVersion);
|
||||
|
||||
// Increase the minimum policy version and check if we're notified.
|
||||
|
||||
createPolicy(true, currentPolicyVersion, ++minimumPolicyVersion);
|
||||
do_check_true(policyPrefs.has("dataSubmissionPolicyAcceptedVersion"));
|
||||
yield triggerPolicyCheckAndEnsureNotified(false);
|
||||
|
||||
createPolicy(false, currentPolicyVersion, ++minimumPolicyVersion);
|
||||
do_check_false(policyPrefs.has("dataSubmissionPolicyAcceptedVersion"));
|
||||
yield triggerPolicyCheckAndEnsureNotified();
|
||||
|
||||
// Test increasing the minimum version just on the current channel.
|
||||
createPolicy(true, currentPolicyVersion, minimumPolicyVersion);
|
||||
|
@ -1260,8 +1260,8 @@ this.HealthReporter.prototype = Object.freeze({
|
||||
* Whether this instance will upload data to a server.
|
||||
*/
|
||||
get willUploadData() {
|
||||
return this._policy.userNotifiedOfCurrentPolicy &&
|
||||
this._policy.healthReportUploadEnabled;
|
||||
return this._policy.dataSubmissionPolicyAccepted &&
|
||||
this._policy.healthReportUploadEnabled;
|
||||
},
|
||||
|
||||
/**
|
||||
@ -1321,8 +1321,8 @@ this.HealthReporter.prototype = Object.freeze({
|
||||
// Need to capture this before we call the parent else it's always
|
||||
// set.
|
||||
let inShutdown = this._shutdownRequested;
|
||||
let result;
|
||||
|
||||
let result;
|
||||
try {
|
||||
result = AbstractHealthReporter.prototype._onInitError.call(this, error);
|
||||
} catch (ex) {
|
||||
@ -1335,8 +1335,8 @@ this.HealthReporter.prototype = Object.freeze({
|
||||
// startup errors is important. And, they should not occur with much
|
||||
// frequency in the wild. So, it shouldn't be too big of a deal.
|
||||
if (!inShutdown &&
|
||||
this._policy.healthReportUploadEnabled &&
|
||||
this._policy.ensureUserNotified()) {
|
||||
this._policy.ensureNotifyResponse(new Date()) &&
|
||||
this._policy.healthReportUploadEnabled) {
|
||||
// We don't care about what happens to this request. It's best
|
||||
// effort.
|
||||
let request = {
|
||||
|
@ -24,7 +24,6 @@ Cu.import("resource://gre/modules/services-common/utils.js");
|
||||
Cu.import("resource://gre/modules/services/datareporting/policy.jsm");
|
||||
Cu.import("resource://gre/modules/services/healthreport/healthreporter.jsm");
|
||||
Cu.import("resource://gre/modules/services/healthreport/providers.jsm");
|
||||
Cu.import("resource://testing-common/services/datareporting/mocks.jsm");
|
||||
|
||||
|
||||
let APP_INFO = {
|
||||
@ -192,16 +191,18 @@ this.getHealthReporter = function (name, uri=DUMMY_URI, inspected=false) {
|
||||
let reporter;
|
||||
|
||||
let policyPrefs = new Preferences(branch + "policy.");
|
||||
let listener = new MockPolicyListener();
|
||||
listener.onRequestDataUpload = function (request) {
|
||||
reporter.requestDataUpload(request);
|
||||
MockPolicyListener.prototype.onRequestDataUpload.call(this, request);
|
||||
}
|
||||
listener.onRequestRemoteDelete = function (request) {
|
||||
reporter.deleteRemoteData(request);
|
||||
MockPolicyListener.prototype.onRequestRemoteDelete.call(this, request);
|
||||
}
|
||||
let policy = new DataReportingPolicy(policyPrefs, prefs, listener);
|
||||
let policy = new DataReportingPolicy(policyPrefs, prefs, {
|
||||
onRequestDataUpload: function (request) {
|
||||
reporter.requestDataUpload(request);
|
||||
},
|
||||
|
||||
onNotifyDataPolicy: function (request) { },
|
||||
|
||||
onRequestRemoteDelete: function (request) {
|
||||
reporter.deleteRemoteData(request);
|
||||
},
|
||||
});
|
||||
|
||||
let type = inspected ? InspectedHealthReporter : HealthReporter;
|
||||
reporter = new type(branch + "healthreport.", policy, null,
|
||||
"state-" + name + ".json");
|
||||
|
@ -92,21 +92,6 @@ function getHealthReportProviderValues(reporter, day=null) {
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
* Ensure that the notification has been displayed to the user therefore having
|
||||
* reporter._policy.userNotifiedOfCurrentPolicy === true, which will allow for a
|
||||
* successful data upload.
|
||||
* @param {HealthReporter} reporter
|
||||
* @return {Promise}
|
||||
*/
|
||||
function ensureUserNotified (reporter) {
|
||||
return Task.spawn(function* ensureUserNotified () {
|
||||
reporter._policy.ensureUserNotified();
|
||||
yield reporter._policy._listener.lastNotifyRequest.deferred.promise;
|
||||
do_check_true(reporter._policy.userNotifiedOfCurrentPolicy);
|
||||
});
|
||||
}
|
||||
|
||||
function run_test() {
|
||||
run_next_test();
|
||||
}
|
||||
@ -688,8 +673,9 @@ add_task(function test_recurring_daily_pings() {
|
||||
|
||||
let policy = reporter._policy;
|
||||
|
||||
defineNow(policy, policy._futureDate(-24 * 60 * 68 * 1000));
|
||||
policy.recordUserAcceptance();
|
||||
defineNow(policy, policy.nextDataSubmissionDate);
|
||||
yield ensureUserNotified(reporter);
|
||||
let promise = policy.checkStateAndTrigger();
|
||||
do_check_neq(promise, null);
|
||||
yield promise;
|
||||
@ -726,8 +712,8 @@ add_task(function test_request_remote_data_deletion() {
|
||||
try {
|
||||
let policy = reporter._policy;
|
||||
defineNow(policy, policy._futureDate(-24 * 60 * 60 * 1000));
|
||||
policy.recordUserAcceptance();
|
||||
defineNow(policy, policy.nextDataSubmissionDate);
|
||||
yield ensureUserNotified(reporter);
|
||||
yield policy.checkStateAndTrigger();
|
||||
let id = reporter.lastSubmitID;
|
||||
do_check_neq(id, null);
|
||||
@ -814,12 +800,16 @@ add_task(function test_policy_accept_reject() {
|
||||
try {
|
||||
let policy = reporter._policy;
|
||||
|
||||
do_check_eq(policy.dataSubmissionPolicyNotifiedDate.getTime(), 0);
|
||||
do_check_true(policy.dataSubmissionPolicyAcceptedVersion < DATAREPORTING_POLICY_VERSION);
|
||||
do_check_false(policy.dataSubmissionPolicyAccepted);
|
||||
do_check_false(reporter.willUploadData);
|
||||
|
||||
yield ensureUserNotified(reporter);
|
||||
policy.recordUserAcceptance();
|
||||
do_check_true(policy.dataSubmissionPolicyAccepted);
|
||||
do_check_true(reporter.willUploadData);
|
||||
|
||||
policy.recordUserRejection();
|
||||
do_check_false(policy.dataSubmissionPolicyAccepted);
|
||||
do_check_false(reporter.willUploadData);
|
||||
} finally {
|
||||
yield reporter._shutdown();
|
||||
yield shutdownServer(server);
|
||||
@ -950,9 +940,9 @@ add_task(function test_upload_on_init_failure() {
|
||||
},
|
||||
});
|
||||
|
||||
reporter._policy.recordUserAcceptance();
|
||||
let error = false;
|
||||
try {
|
||||
yield ensureUserNotified(reporter);
|
||||
yield reporter.init();
|
||||
} catch (ex) {
|
||||
error = true;
|
||||
|
@ -117,10 +117,8 @@ user_pref("dom.use_xbl_scopes_for_remote_xul", true);
|
||||
// Get network events.
|
||||
user_pref("network.activity.blipIntervalMilliseconds", 250);
|
||||
|
||||
// We do not wish to display datareporting policy notifications as it might
|
||||
// cause other tests to fail. Tests that wish to test the notification functionality
|
||||
// should explicitly disable this pref.
|
||||
user_pref("datareporting.policy.dataSubmissionPolicyBypassNotification", true);
|
||||
// Don't allow the Data Reporting service to prompt for policy acceptance.
|
||||
user_pref("datareporting.policy.dataSubmissionPolicyBypassAcceptance", true);
|
||||
|
||||
// Point Firefox Health Report at a local server. We don't care if it actually
|
||||
// works. It just can't hit the default production endpoint.
|
||||
|
Loading…
Reference in New Issue
Block a user