mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 862563 - Remove implicit acceptance for data reporting notification and notify on first run. r=gps
The data reporting notification was over-complicated. It wasn't displayed for +24hr after first run and it had a weird, non-required policy around what constituted acceptance of the policy. The notification is now shown shortly after first startup. The logic around "notification accepted" has been greatly simplified by rolling it into "notification shown." Where we once were checking whether the notification has been "accepted," we now check whether it has been displayed. The overly complicated logic around the implicit acceptance of the policy has also been removed. The end result is the code for managing the state of the notification is greatly simplified.
This commit is contained in:
parent
752707192e
commit
3ec162d8ae
@ -62,11 +62,6 @@ 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),
|
||||
@ -81,16 +76,14 @@ 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)
|
||||
);
|
||||
|
||||
// Tell the notification request we have displayed the notification.
|
||||
// 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.
|
||||
request.onUserNotifyComplete();
|
||||
},
|
||||
|
||||
@ -102,18 +95,16 @@ 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":
|
||||
this.onNotifyDataPolicy(subject.wrappedJSObject.object);
|
||||
let request = subject.wrappedJSObject.object;
|
||||
try {
|
||||
this._displayDataPolicyInfoBar(request);
|
||||
} catch (ex) {
|
||||
request.onUserNotifyFailed(ex);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
||||
case "datareporting:notify-data-policy:close":
|
||||
|
@ -2,30 +2,49 @@
|
||||
* 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 = {};
|
||||
Components.utils.import("resource://gre/modules/services/datareporting/policy.jsm", ns);
|
||||
Components.utils.import("resource://gre/modules/Preferences.jsm", ns);
|
||||
Cu.import("resource://gre/modules/services/datareporting/policy.jsm", ns);
|
||||
Cu.import("resource://gre/modules/Preferences.jsm", ns);
|
||||
|
||||
let service = Components.classes["@mozilla.org/datareporting/service;1"]
|
||||
.getService(Components.interfaces.nsISupports)
|
||||
.wrappedJSObject;
|
||||
let service = Cc["@mozilla.org/datareporting/service;1"]
|
||||
.getService(Ci.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);
|
||||
|
||||
is(policy.notifyState, policy.STATE_NOTIFY_UNNOTIFIED, "Policy is in unnotified state.");
|
||||
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));
|
||||
|
||||
service.healthReporter.onInit().then(function onInit() {
|
||||
is(policy.ensureNotifyResponse(new Date()), false, "User has not responded to policy.");
|
||||
});
|
||||
|
||||
return policy;
|
||||
return [policy, deferred.promise];
|
||||
}
|
||||
|
||||
/**
|
||||
@ -55,6 +74,7 @@ function waitForNotificationClose(notification, cb) {
|
||||
let dumpAppender, rootLogger;
|
||||
|
||||
function test() {
|
||||
registerCleanupFunction(cleanup);
|
||||
waitForExplicitFinish();
|
||||
|
||||
let ns = {};
|
||||
@ -64,29 +84,41 @@ function test() {
|
||||
dumpAppender.level = ns.Log.Level.All;
|
||||
rootLogger.addAppender(dumpAppender);
|
||||
|
||||
let notification = document.getElementById("global-notificationbox");
|
||||
let policy;
|
||||
closeAllNotifications().then(function onSuccess () {
|
||||
let notification = document.getElementById("global-notificationbox");
|
||||
|
||||
notification.addEventListener("AlertActive", function active() {
|
||||
notification.removeEventListener("AlertActive", active, true);
|
||||
notification.addEventListener("AlertActive", function active() {
|
||||
notification.removeEventListener("AlertActive", active, true);
|
||||
is(notification.allNotifications.length, 1, "Notification Displayed.");
|
||||
|
||||
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();
|
||||
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();
|
||||
});
|
||||
notification.currentNotification.close();
|
||||
});
|
||||
}, true);
|
||||
}, true);
|
||||
|
||||
policy = sendNotifyRequest("single_window_notified");
|
||||
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;
|
||||
});
|
||||
|
||||
}.bind(this), function onError (err) {
|
||||
throw err;
|
||||
});
|
||||
}
|
||||
|
||||
function test_multiple_windows() {
|
||||
@ -98,7 +130,7 @@ function test_multiple_windows() {
|
||||
let notification2 = window2.document.getElementById("global-notificationbox");
|
||||
ok(notification2, "2nd window has a global notification box.");
|
||||
|
||||
let policy;
|
||||
let [policy, promise] = sendNotifyRequest("multiple_window_behavior");
|
||||
let displayCount = 0;
|
||||
let prefWindowClosed = false;
|
||||
let mutationObserversRemoved = false;
|
||||
@ -129,8 +161,8 @@ function test_multiple_windows() {
|
||||
|
||||
dump("Finishing multiple window test.\n");
|
||||
rootLogger.removeAppender(dumpAppender);
|
||||
delete dumpAppender;
|
||||
delete rootLogger;
|
||||
dumpAppender = null;
|
||||
rootLogger = null;
|
||||
finish();
|
||||
}
|
||||
let closeCount = 0;
|
||||
@ -143,12 +175,8 @@ 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.");
|
||||
|
||||
@ -192,7 +220,20 @@ function test_multiple_windows() {
|
||||
executeSoon(onAlertDisplayed);
|
||||
}, true);
|
||||
|
||||
policy = sendNotifyRequest("multiple_window_behavior");
|
||||
promise.then(null, function onError(err) {
|
||||
throw err;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
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,6 +7,26 @@ 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,7 +29,6 @@ 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,6 +13,8 @@ function runPaneTest(fn) {
|
||||
.policy;
|
||||
ok(policy, "Policy object defined");
|
||||
|
||||
resetPreferences();
|
||||
|
||||
fn(win, policy);
|
||||
}
|
||||
|
||||
@ -21,11 +23,30 @@ 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);
|
||||
}
|
||||
@ -43,7 +64,8 @@ function testUploadDisabled(win, policy) {
|
||||
function testBasic(win, policy) {
|
||||
let doc = win.document;
|
||||
|
||||
is(policy.dataSubmissionPolicyAccepted, false, "Data submission policy not accepted.");
|
||||
resetPreferences();
|
||||
|
||||
is(policy.healthReportUploadEnabled, true, "Health Report upload enabled on app first run.");
|
||||
|
||||
let checkbox = doc.getElementById("submitHealthReportBox");
|
||||
@ -63,6 +85,10 @@ function testBasic(win, policy) {
|
||||
}
|
||||
|
||||
function resetPreferences() {
|
||||
Services.prefs.clearUserPref("datareporting.healthreport.uploadEnabled");
|
||||
let service = Cc["@mozilla.org/datareporting/service;1"]
|
||||
.getService(Ci.nsISupports)
|
||||
.wrappedJSObject;
|
||||
service.policy._prefs.resetBranch("datareporting.policy.");
|
||||
service.policy.dataSubmissionPolicyBypassNotification = true;
|
||||
}
|
||||
|
||||
|
@ -6,6 +6,7 @@
|
||||
function test() {
|
||||
requestLongerTimeout(2);
|
||||
waitForExplicitFinish();
|
||||
resetPreferences();
|
||||
|
||||
try {
|
||||
let cm = Components.classes["@mozilla.org/categorymanager;1"]
|
||||
@ -101,3 +102,10 @@ 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,6 +175,7 @@ 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,14 +3,10 @@
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
pref("datareporting.policy.dataSubmissionEnabled", true);
|
||||
pref("datareporting.policy.dataSubmissionPolicyAccepted", false);
|
||||
pref("datareporting.policy.dataSubmissionPolicyBypassAcceptance", false);
|
||||
pref("datareporting.policy.dataSubmissionPolicyNotifiedTime", "0");
|
||||
pref("datareporting.policy.dataSubmissionPolicyResponseType", "");
|
||||
pref("datareporting.policy.dataSubmissionPolicyResponseTime", "0");
|
||||
pref("datareporting.policy.firstRunTime", "0");
|
||||
|
||||
pref("datareporting.policy.dataSubmissionPolicyNotifiedTime", "0");
|
||||
pref("datareporting.policy.dataSubmissionPolicyAcceptedVersion", 0);
|
||||
pref("datareporting.policy.dataSubmissionPolicyBypassNotification", false);
|
||||
pref("datareporting.policy.currentPolicyVersion", 2);
|
||||
pref("datareporting.policy.minimumPolicyVersion", 1);
|
||||
pref("datareporting.policy.minimumPolicyVersion.channel-beta", 2);
|
||||
|
||||
|
@ -26,22 +26,27 @@ this.MockPolicyListener = function MockPolicyListener() {
|
||||
}
|
||||
|
||||
MockPolicyListener.prototype = {
|
||||
onRequestDataUpload: function onRequestDataUpload(request) {
|
||||
onRequestDataUpload: function (request) {
|
||||
this._log.info("onRequestDataUpload invoked.");
|
||||
this.requestDataUploadCount++;
|
||||
this.lastDataRequest = request;
|
||||
},
|
||||
|
||||
onRequestRemoteDelete: function onRequestRemoteDelete(request) {
|
||||
onRequestRemoteDelete: function (request) {
|
||||
this._log.info("onRequestRemoteDelete invoked.");
|
||||
this.requestRemoteDeleteCount++;
|
||||
this.lastRemoteDeleteRequest = request;
|
||||
},
|
||||
|
||||
onNotifyDataPolicy: function onNotifyDataPolicy(request) {
|
||||
this._log.info("onNotifyUser invoked.");
|
||||
onNotifyDataPolicy: function (request, rejectMessage=null) {
|
||||
this._log.info("onNotifyDataPolicy invoked.");
|
||||
this.notifyUserCount++;
|
||||
this.lastNotifyRequest = request;
|
||||
if (rejectMessage) {
|
||||
request.onUserNotifyFailed(rejectMessage);
|
||||
} else {
|
||||
request.onUserNotifyComplete();
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -3,14 +3,8 @@
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* This file is in transition. Most of its content needs to be moved under
|
||||
* /services/healthreport.
|
||||
*/
|
||||
|
||||
#ifndef MERGED_COMPARTMENT
|
||||
@ -20,6 +14,7 @@
|
||||
this.EXPORTED_SYMBOLS = [
|
||||
"DataSubmissionRequest", // For test use only.
|
||||
"DataReportingPolicy",
|
||||
"DATAREPORTING_POLICY_VERSION",
|
||||
];
|
||||
|
||||
const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
|
||||
@ -32,6 +27,11 @@ 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,33 +41,15 @@ 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`.
|
||||
*
|
||||
* 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.
|
||||
* If for whatever reason the callee could not display a notice,
|
||||
* it should call `onUserNotifyFailed`.
|
||||
*
|
||||
* @param policy
|
||||
* (DataReportingPolicy) The policy instance this request came from.
|
||||
@ -78,17 +60,13 @@ function NotifyPolicyRequest(policy, deferred) {
|
||||
this.policy = policy;
|
||||
this.deferred = deferred;
|
||||
}
|
||||
NotifyPolicyRequest.prototype = {
|
||||
NotifyPolicyRequest.prototype = Object.freeze({
|
||||
/**
|
||||
* 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 onUserNotified() {
|
||||
this.deferred.resolve();
|
||||
return this.deferred.promise;
|
||||
},
|
||||
onUserNotifyComplete: function () {
|
||||
return this.deferred.resolve();
|
||||
},
|
||||
|
||||
/**
|
||||
* Called when there was an error notifying the user about the policy.
|
||||
@ -96,32 +74,10 @@ NotifyPolicyRequest.prototype = {
|
||||
* @param error
|
||||
* (Error) Explains what went wrong.
|
||||
*/
|
||||
onUserNotifyFailed: function onUserNotifyFailed(error) {
|
||||
this.deferred.reject(error);
|
||||
onUserNotifyFailed: function (error) {
|
||||
return 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.
|
||||
@ -238,9 +194,7 @@ this.DataSubmissionRequest.prototype = Object.freeze({
|
||||
* data collection practices.
|
||||
* 5. User should have opportunity to react to this notification before
|
||||
* data submission.
|
||||
* 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
|
||||
* 6. 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
|
||||
@ -289,19 +243,11 @@ this.DataReportingPolicy = function (prefs, healthReportPrefs, listener) {
|
||||
this._prefs = prefs;
|
||||
this._healthReportPrefs = healthReportPrefs;
|
||||
this._listener = listener;
|
||||
this._userNotifyPromise = null;
|
||||
|
||||
// If the policy version has changed, reset all preferences, so that
|
||||
// the notification reappears.
|
||||
let acceptedVersion = this._prefs.get("dataSubmissionPolicyAcceptedVersion");
|
||||
if (typeof(acceptedVersion) == "number" &&
|
||||
acceptedVersion < this.minimumPolicyVersion) {
|
||||
this._log.info("policy version has changed - resetting all prefs");
|
||||
// We don't want to delay the notification in this case.
|
||||
let firstRunToRestore = this.firstRunDate;
|
||||
this._prefs.resetBranch();
|
||||
this.firstRunDate = firstRunToRestore.getTime() ?
|
||||
firstRunToRestore : this.now();
|
||||
} else if (!this.firstRunDate.getTime()) {
|
||||
this._migratePrefs();
|
||||
|
||||
if (!this.firstRunDate.getTime()) {
|
||||
// If we've never run before, record the current time.
|
||||
this.firstRunDate = this.now();
|
||||
}
|
||||
@ -329,30 +275,12 @@ 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.
|
||||
*
|
||||
@ -393,13 +321,6 @@ 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",
|
||||
@ -422,22 +343,6 @@ 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,
|
||||
@ -450,46 +355,12 @@ this.DataReportingPolicy.prototype = Object.freeze({
|
||||
value, OLDEST_ALLOWED_YEAR);
|
||||
},
|
||||
|
||||
/**
|
||||
* 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);
|
||||
get dataSubmissionPolicyBypassNotification() {
|
||||
return this._prefs.get("dataSubmissionPolicyBypassNotification", false);
|
||||
},
|
||||
|
||||
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);
|
||||
set dataSubmissionPolicyBypassNotification(value) {
|
||||
return this._prefs.set("dataSubmissionPolicyBypassNotification", !!value);
|
||||
},
|
||||
|
||||
/**
|
||||
@ -507,6 +378,10 @@ 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.
|
||||
@ -519,48 +394,21 @@ this.DataReportingPolicy.prototype = Object.freeze({
|
||||
channelPref : this._prefs.get("minimumPolicyVersion", 1);
|
||||
},
|
||||
|
||||
/**
|
||||
* 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);
|
||||
get dataSubmissionPolicyAcceptedVersion() {
|
||||
return this._prefs.get("dataSubmissionPolicyAcceptedVersion", 0);
|
||||
},
|
||||
|
||||
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");
|
||||
}
|
||||
set dataSubmissionPolicyAcceptedVersion(value) {
|
||||
this._prefs.set("dataSubmissionPolicyAcceptedVersion", value);
|
||||
},
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* Checks to see if the user has been notified about data submission
|
||||
* @return {bool}
|
||||
*/
|
||||
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;
|
||||
get userNotifiedOfCurrentPolicy() {
|
||||
return this.dataSubmissionPolicyNotifiedDate.getTime() > 0 &&
|
||||
this.dataSubmissionPolicyAcceptedVersion >= this.currentPolicyVersion;
|
||||
},
|
||||
|
||||
/**
|
||||
@ -693,43 +541,6 @@ 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.
|
||||
*
|
||||
@ -882,19 +693,13 @@ this.DataReportingPolicy.prototype = Object.freeze({
|
||||
return;
|
||||
}
|
||||
|
||||
// If the user hasn't responded to the data policy, don't do anything.
|
||||
if (!this.ensureNotifyResponse(now)) {
|
||||
if (!this.ensureUserNotified()) {
|
||||
this._log.warn("The user has not been notified about the data submission " +
|
||||
"policy. Not attempting upload.");
|
||||
return;
|
||||
}
|
||||
|
||||
// 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.
|
||||
// Data submission is allowed to occur. Now comes the scheduling part.
|
||||
|
||||
if (nowT < nextSubmissionDate.getTime()) {
|
||||
this._log.debug("Next data submission is scheduled in the future: " +
|
||||
@ -906,82 +711,62 @@ this.DataReportingPolicy.prototype = Object.freeze({
|
||||
},
|
||||
|
||||
/**
|
||||
* Ensure user has responded to data submission policy.
|
||||
* Ensure that the data policy notification has been displayed.
|
||||
*
|
||||
* This must be called before data submission. If the policy has not been
|
||||
* responded to, data submission must not occur.
|
||||
* displayed, data submission must not occur.
|
||||
*
|
||||
* @return bool Whether user has responded to data policy.
|
||||
* @return bool Whether the notification has been displayed.
|
||||
*/
|
||||
ensureNotifyResponse: function ensureNotifyResponse(now) {
|
||||
if (this.dataSubmissionPolicyBypassAcceptance) {
|
||||
ensureUserNotified: function () {
|
||||
if (this.userNotifiedOfCurrentPolicy || this.dataSubmissionPolicyBypassNotification) {
|
||||
return true;
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
// The user has not been notified yet, but is in the process of being notified.
|
||||
if (this._userNotifyPromise) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 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;
|
||||
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));
|
||||
|
||||
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._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));
|
||||
}
|
||||
|
||||
// If this happens, we have a coding error in this file.
|
||||
if (notifyState != this.STATE_NOTIFY_COMPLETE) {
|
||||
throw new Error("Unknown notification state: " + notifyState);
|
||||
}
|
||||
this._userNotifyPromise = deferred.promise;
|
||||
|
||||
return true;
|
||||
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"
|
||||
]);
|
||||
},
|
||||
|
||||
_processInProgressSubmission: function _processInProgressSubmission() {
|
||||
|
@ -9,6 +9,7 @@ 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,
|
||||
@ -37,6 +38,22 @@ 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", {
|
||||
@ -66,7 +83,8 @@ 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.notifyState, policy.STATE_NOTIFY_UNNOTIFIED);
|
||||
do_check_eq(policy.dataSubmissionPolicyAcceptedVersion, 0);
|
||||
do_check_false(policy.userNotifiedOfCurrentPolicy);
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
@ -81,29 +99,23 @@ 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);
|
||||
|
||||
policy.dataSubmissionPolicyAccepted = false;
|
||||
do_check_false(policyPrefs.get("dataSubmissionPolicyAccepted", true));
|
||||
do_check_false(policy.dataSubmissionPolicyAccepted);
|
||||
let new_version = DATAREPORTING_POLICY_VERSION + 1;
|
||||
policy.dataSubmissionPolicyAcceptedVersion = new_version;
|
||||
do_check_eq(policyPrefs.get("dataSubmissionPolicyAcceptedVersion"), new_version);
|
||||
|
||||
do_check_false(policy.dataSubmissionPolicyBypassAcceptance);
|
||||
policyPrefs.set("dataSubmissionPolicyBypassAcceptance", true);
|
||||
do_check_true(policy.dataSubmissionPolicyBypassAcceptance);
|
||||
do_check_false(policy.dataSubmissionPolicyBypassNotification);
|
||||
policy.dataSubmissionPolicyBypassNotification = true;
|
||||
do_check_true(policy.dataSubmissionPolicyBypassNotification);
|
||||
do_check_true(policyPrefs.get("dataSubmissionPolicyBypassNotification"));
|
||||
|
||||
policy.lastDataSubmissionRequestedDate = now;
|
||||
do_check_eq(hrPrefs.get("lastDataSubmissionRequestedTime"), nowT);
|
||||
@ -142,153 +154,78 @@ add_test(function test_prefs() {
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
add_test(function test_notify_state_prefs() {
|
||||
let [policy, policyPrefs, hrPrefs, listener] = getPolicy("notify_state_prefs");
|
||||
add_task(function test_migratePrefs () {
|
||||
let [policy, policyPrefs, hrPrefs, listener] = getPolicy("migratePrefs");
|
||||
let outdated_prefs = {
|
||||
dataSubmissionPolicyAccepted: true,
|
||||
dataSubmissionPolicyBypassAcceptance: true,
|
||||
dataSubmissionPolicyResponseType: "something",
|
||||
dataSubmissionPolicyResponseTime: Date.now() + "",
|
||||
};
|
||||
|
||||
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();
|
||||
// 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));
|
||||
}
|
||||
});
|
||||
|
||||
add_task(function test_initial_submission_notification() {
|
||||
add_task(function test_userNotifiedOfCurrentPolicy () {
|
||||
let [policy, policyPrefs, hrPrefs, listener] = getPolicy("initial_submission_notification");
|
||||
|
||||
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_false(policy.userNotifiedOfCurrentPolicy,
|
||||
"The initial state should be unnotified.");
|
||||
do_check_eq(policy.dataSubmissionPolicyNotifiedDate.getTime(), 0);
|
||||
|
||||
// We have crossed the threshold. We should see notification.
|
||||
defineNow(policy, new Date(policy.firstRunDate.getTime() +
|
||||
policy.SUBMISSION_NOTIFY_INTERVAL_MSEC));
|
||||
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);
|
||||
do_check_eq(policy.dataSubmissionPolicyNotifiedDate.getTime(), 0);
|
||||
|
||||
// Uploads will trigger user notifications as needed.
|
||||
policy.checkStateAndTrigger();
|
||||
do_check_eq(listener.notifyUserCount, 1);
|
||||
yield listener.lastNotifyRequest.onUserNotifyComplete();
|
||||
do_check_true(policy._dataSubmissionPolicyNotifiedDate instanceof Date);
|
||||
do_check_eq(listener.requestDataUploadCount, 0);
|
||||
|
||||
yield ensureUserNotifiedAndTrigger(policy);
|
||||
|
||||
do_check_eq(listener.notifyUserCount, 1);
|
||||
do_check_true(policy.dataSubmissionPolicyNotifiedDate.getTime() > 0);
|
||||
do_check_eq(policy.dataSubmissionPolicyNotifiedDate.getTime(),
|
||||
policy._dataSubmissionPolicyNotifiedDate.getTime());
|
||||
do_check_eq(policy.notifyState, policy.STATE_NOTIFY_WAIT);
|
||||
do_check_true(policy.userNotifiedOfCurrentPolicy);
|
||||
});
|
||||
|
||||
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));
|
||||
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);
|
||||
policy.checkStateAndTrigger();
|
||||
do_check_eq(listener.requestDataUploadCount, 0);
|
||||
});
|
||||
|
||||
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();
|
||||
yield ensureUserNotifiedAndTrigger(policy);
|
||||
do_check_eq(listener.requestDataUploadCount, 1);
|
||||
|
||||
defineNow(policy,
|
||||
@ -296,39 +233,32 @@ add_test(function test_submission_kill_switch() {
|
||||
policy.dataSubmissionEnabled = false;
|
||||
policy.checkStateAndTrigger();
|
||||
do_check_eq(listener.requestDataUploadCount, 1);
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
add_test(function test_upload_kill_switch() {
|
||||
let [policy, policyPrefs, hrPrefs, listener] = getPolicy("upload_kill_switch");
|
||||
add_task(function* test_upload_kill_switch() {
|
||||
let [policy, policyPrefs, hrPrefs, listener] = getPolicy("upload_kill_switch");
|
||||
|
||||
defineNow(policy, policy._futureDate(-24 * 60 * 60 * 1000));
|
||||
policy.recordUserAcceptance();
|
||||
yield ensureUserNotifiedAndTrigger(policy);
|
||||
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;
|
||||
policy.checkStateAndTrigger();
|
||||
yield policy.checkStateAndTrigger();
|
||||
do_check_eq(listener.requestDataUploadCount, 0);
|
||||
policy.healthReportUploadEnabled = true;
|
||||
policy.checkStateAndTrigger();
|
||||
yield ensureUserNotifiedAndTrigger(policy);
|
||||
do_check_eq(listener.requestDataUploadCount, 1);
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
add_test(function test_data_submission_no_data() {
|
||||
add_task(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);
|
||||
policy.checkStateAndTrigger();
|
||||
yield ensureUserNotifiedAndTrigger(policy);
|
||||
do_check_eq(listener.requestDataUploadCount, 1);
|
||||
listener.lastDataRequest.onNoDataAvailable();
|
||||
|
||||
@ -336,20 +266,16 @@ add_test(function test_data_submission_no_data() {
|
||||
defineNow(policy, new Date(now.getTime() + 155 * 60 * 1000));
|
||||
policy.checkStateAndTrigger();
|
||||
do_check_eq(listener.requestDataUploadCount, 2);
|
||||
});
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
add_task(function test_data_submission_submit_failure_hard() {
|
||||
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);
|
||||
|
||||
policy.checkStateAndTrigger();
|
||||
yield ensureUserNotifiedAndTrigger(policy);
|
||||
do_check_eq(listener.requestDataUploadCount, 1);
|
||||
yield listener.lastDataRequest.onSubmissionFailureHard();
|
||||
do_check_eq(listener.lastDataRequest.state,
|
||||
@ -363,30 +289,27 @@ 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);
|
||||
policy.checkStateAndTrigger();
|
||||
yield ensureUserNotifiedAndTrigger(policy);
|
||||
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);
|
||||
policy.checkStateAndTrigger();
|
||||
yield ensureUserNotifiedAndTrigger(policy);
|
||||
do_check_eq(listener.requestDataUploadCount, 1);
|
||||
do_check_eq(policy.lastDataSubmissionRequestedDate.getTime(), now.getTime());
|
||||
|
||||
@ -414,18 +337,17 @@ add_task(function test_submission_daily_scheduling() {
|
||||
new Date(nextScheduled.getTime() + 24 * 60 * 60 * 1000 + 200).getTime());
|
||||
});
|
||||
|
||||
add_test(function test_submission_far_future_scheduling() {
|
||||
add_task(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);
|
||||
policy.recordUserAcceptance();
|
||||
now = new Date();
|
||||
defineNow(policy, now);
|
||||
yield ensureUserNotifiedAndTrigger(policy);
|
||||
|
||||
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());
|
||||
|
||||
@ -434,21 +356,17 @@ add_test(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);
|
||||
policy.checkStateAndTrigger();
|
||||
yield ensureUserNotifiedAndTrigger(policy);
|
||||
do_check_eq(listener.requestDataUploadCount, 1);
|
||||
do_check_eq(policy.currentDaySubmissionFailureCount, 0);
|
||||
|
||||
@ -499,15 +417,13 @@ add_task(function test_submission_backoff() {
|
||||
});
|
||||
|
||||
// Ensure that only one submission request can be active at a time.
|
||||
add_test(function test_submission_expiring() {
|
||||
add_task(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);
|
||||
policy.checkStateAndTrigger();
|
||||
yield ensureUserNotifiedAndTrigger(policy);
|
||||
do_check_eq(listener.requestDataUploadCount, 1);
|
||||
defineNow(policy, new Date(now.getTime() + 500));
|
||||
policy.checkStateAndTrigger();
|
||||
@ -518,11 +434,9 @@ add_test(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);
|
||||
@ -546,15 +460,13 @@ add_task(function test_delete_remote_data() {
|
||||
});
|
||||
|
||||
// Ensure that deletion requests take priority over regular data submission.
|
||||
add_test(function test_delete_remote_data_priority() {
|
||||
add_task(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));
|
||||
|
||||
policy.checkStateAndTrigger();
|
||||
yield ensureUserNotifiedAndTrigger(policy);
|
||||
do_check_eq(listener.requestDataUploadCount, 1);
|
||||
policy._inProgressSubmissionRequest = null;
|
||||
|
||||
@ -563,16 +475,12 @@ add_test(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();
|
||||
@ -600,15 +508,12 @@ 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);
|
||||
|
||||
policy.checkStateAndTrigger();
|
||||
yield ensureUserNotifiedAndTrigger(policy);
|
||||
do_check_eq(listener.requestDataUploadCount, 1);
|
||||
defineNow(policy, policy._futureDate(50 * 1000));
|
||||
|
||||
@ -654,7 +559,6 @@ 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();
|
||||
@ -672,79 +576,7 @@ add_test(function test_polling() {
|
||||
policy.startPolling();
|
||||
});
|
||||
|
||||
// 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() {
|
||||
add_task(function* test_record_health_report_upload_enabled() {
|
||||
let [policy, policyPrefs, hrPrefs, listener] = getPolicy("record_health_report_upload_enabled");
|
||||
|
||||
// Preconditions.
|
||||
@ -791,10 +623,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(shouldBeAccepted = false,
|
||||
function createPolicy(shouldBeNotified = false,
|
||||
currentPolicyVersion = 1, minimumPolicyVersion = 1,
|
||||
branchMinimumVersionOverride) {
|
||||
[policy, policyPrefs, hrPrefs, listener] =
|
||||
@ -804,8 +636,7 @@ add_task(function* test_policy_version() {
|
||||
if (firstRun) {
|
||||
firstRunTime = policy.firstRunDate.getTime();
|
||||
do_check_true(firstRunTime > 0);
|
||||
now = new Date(policy.firstRunDate.getTime() +
|
||||
policy.SUBMISSION_NOTIFY_INTERVAL_MSEC);
|
||||
now = new Date(policy.firstRunDate.getTime());
|
||||
}
|
||||
else {
|
||||
// The first-run time should not be reset even after policy-version
|
||||
@ -813,23 +644,18 @@ add_task(function* test_policy_version() {
|
||||
do_check_eq(policy.firstRunDate.getTime(), firstRunTime);
|
||||
}
|
||||
defineNow(policy, now);
|
||||
do_check_eq(policy.dataSubmissionPolicyAccepted, shouldBeAccepted);
|
||||
do_check_eq(policy.userNotifiedOfCurrentPolicy, shouldBeNotified);
|
||||
}
|
||||
|
||||
function* triggerPolicyCheckAndEnsureNotified(notified = true, accept = true) {
|
||||
function* triggerPolicyCheckAndEnsureNotified(notified = true) {
|
||||
policy.checkStateAndTrigger();
|
||||
do_check_eq(listener.notifyUserCount, Number(notified));
|
||||
if (notified) {
|
||||
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"));
|
||||
}
|
||||
policy.ensureUserNotified();
|
||||
yield listener.lastNotifyRequest.deferred.promise;
|
||||
do_check_true(policy.userNotifiedOfCurrentPolicy);
|
||||
do_check_eq(policyPrefs.get("dataSubmissionPolicyAcceptedVersion"),
|
||||
policyPrefs.get("currentPolicyVersion"));
|
||||
}
|
||||
}
|
||||
|
||||
@ -844,16 +670,16 @@ add_task(function* test_policy_version() {
|
||||
// version must be changed.
|
||||
let currentPolicyVersion = policyPrefs.get("currentPolicyVersion");
|
||||
let minimumPolicyVersion = policyPrefs.get("minimumPolicyVersion");
|
||||
createPolicy(true, ++currentPolicyVersion, minimumPolicyVersion);
|
||||
yield triggerPolicyCheckAndEnsureNotified(false);
|
||||
do_check_true(policy.dataSubmissionPolicyAccepted);
|
||||
do_check_eq(policyPrefs.get("dataSubmissionPolicyAcceptedVersion"),
|
||||
minimumPolicyVersion);
|
||||
createPolicy(false, ++currentPolicyVersion, minimumPolicyVersion);
|
||||
yield triggerPolicyCheckAndEnsureNotified(true);
|
||||
do_check_eq(policyPrefs.get("dataSubmissionPolicyAcceptedVersion"), currentPolicyVersion);
|
||||
|
||||
// Increase the minimum policy version and check if we're notified.
|
||||
createPolicy(false, currentPolicyVersion, ++minimumPolicyVersion);
|
||||
do_check_false(policyPrefs.has("dataSubmissionPolicyAcceptedVersion"));
|
||||
yield triggerPolicyCheckAndEnsureNotified();
|
||||
|
||||
createPolicy(true, currentPolicyVersion, ++minimumPolicyVersion);
|
||||
do_check_true(policyPrefs.has("dataSubmissionPolicyAcceptedVersion"));
|
||||
yield triggerPolicyCheckAndEnsureNotified(false);
|
||||
|
||||
|
||||
// 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.dataSubmissionPolicyAccepted &&
|
||||
this._policy.healthReportUploadEnabled;
|
||||
return this._policy.userNotifiedOfCurrentPolicy &&
|
||||
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;
|
||||
|
||||
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.ensureNotifyResponse(new Date()) &&
|
||||
this._policy.healthReportUploadEnabled) {
|
||||
this._policy.healthReportUploadEnabled &&
|
||||
this._policy.ensureUserNotified()) {
|
||||
// We don't care about what happens to this request. It's best
|
||||
// effort.
|
||||
let request = {
|
||||
|
@ -25,6 +25,7 @@ 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 = {
|
||||
@ -190,18 +191,16 @@ this.getHealthReporter = function (name, uri=DUMMY_URI, inspected=false) {
|
||||
let reporter;
|
||||
|
||||
let policyPrefs = new Preferences(branch + "policy.");
|
||||
let policy = new DataReportingPolicy(policyPrefs, prefs, {
|
||||
onRequestDataUpload: function (request) {
|
||||
reporter.requestDataUpload(request);
|
||||
},
|
||||
|
||||
onNotifyDataPolicy: function (request) { },
|
||||
|
||||
onRequestRemoteDelete: function (request) {
|
||||
reporter.deleteRemoteData(request);
|
||||
},
|
||||
});
|
||||
|
||||
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 type = inspected ? InspectedHealthReporter : HealthReporter;
|
||||
reporter = new type(branch + "healthreport.", policy, null,
|
||||
"state-" + name + ".json");
|
||||
|
@ -92,6 +92,21 @@ 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();
|
||||
}
|
||||
@ -673,9 +688,8 @@ 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;
|
||||
@ -712,8 +726,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);
|
||||
@ -800,16 +814,12 @@ add_task(function test_policy_accept_reject() {
|
||||
try {
|
||||
let policy = reporter._policy;
|
||||
|
||||
do_check_false(policy.dataSubmissionPolicyAccepted);
|
||||
do_check_eq(policy.dataSubmissionPolicyNotifiedDate.getTime(), 0);
|
||||
do_check_true(policy.dataSubmissionPolicyAcceptedVersion < DATAREPORTING_POLICY_VERSION);
|
||||
do_check_false(reporter.willUploadData);
|
||||
|
||||
policy.recordUserAcceptance();
|
||||
do_check_true(policy.dataSubmissionPolicyAccepted);
|
||||
yield ensureUserNotified(reporter);
|
||||
do_check_true(reporter.willUploadData);
|
||||
|
||||
policy.recordUserRejection();
|
||||
do_check_false(policy.dataSubmissionPolicyAccepted);
|
||||
do_check_false(reporter.willUploadData);
|
||||
} finally {
|
||||
yield reporter._shutdown();
|
||||
yield shutdownServer(server);
|
||||
@ -940,9 +950,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;
|
||||
|
@ -128,8 +128,10 @@ user_pref("dom.use_xbl_scopes_for_remote_xul", true);
|
||||
// Get network events.
|
||||
user_pref("network.activity.blipIntervalMilliseconds", 250);
|
||||
|
||||
// Don't allow the Data Reporting service to prompt for policy acceptance.
|
||||
user_pref("datareporting.policy.dataSubmissionPolicyBypassAcceptance", true);
|
||||
// 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);
|
||||
|
||||
// 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