Merge fx-team to central, a=merge
|
@ -8,6 +8,11 @@ let TrackingProtection = {
|
|||
PREF_ENABLED_IN_PRIVATE_WINDOWS: "privacy.trackingprotection.pbmode.enabled",
|
||||
enabledGlobally: false,
|
||||
enabledInPrivateWindows: false,
|
||||
container: null,
|
||||
content: null,
|
||||
icon: null,
|
||||
activeTooltipText: null,
|
||||
disabledTooltipText: null,
|
||||
|
||||
init() {
|
||||
let $ = selector => document.querySelector(selector);
|
||||
|
@ -19,6 +24,11 @@ let TrackingProtection = {
|
|||
Services.prefs.addObserver(this.PREF_ENABLED_GLOBALLY, this, false);
|
||||
Services.prefs.addObserver(this.PREF_ENABLED_IN_PRIVATE_WINDOWS, this, false);
|
||||
|
||||
this.activeTooltipText =
|
||||
gNavigatorBundle.getString("trackingProtection.icon.activeTooltip");
|
||||
this.disabledTooltipText =
|
||||
gNavigatorBundle.getString("trackingProtection.icon.disabledTooltip");
|
||||
|
||||
this.enabledHistogram.add(this.enabledGlobally);
|
||||
},
|
||||
|
||||
|
@ -66,21 +76,14 @@ let TrackingProtection = {
|
|||
this.icon.setAttribute("animate", "true");
|
||||
}
|
||||
|
||||
let {
|
||||
STATE_BLOCKED_TRACKING_CONTENT, STATE_LOADED_TRACKING_CONTENT
|
||||
} = Ci.nsIWebProgressListener;
|
||||
let isBlocking = state & Ci.nsIWebProgressListener.STATE_BLOCKED_TRACKING_CONTENT;
|
||||
let isAllowing = state & Ci.nsIWebProgressListener.STATE_LOADED_TRACKING_CONTENT;
|
||||
|
||||
for (let element of [this.icon, this.content]) {
|
||||
if (state & STATE_BLOCKED_TRACKING_CONTENT) {
|
||||
element.setAttribute("state", "blocked-tracking-content");
|
||||
} else if (state & STATE_LOADED_TRACKING_CONTENT) {
|
||||
element.setAttribute("state", "loaded-tracking-content");
|
||||
} else {
|
||||
element.removeAttribute("state");
|
||||
}
|
||||
}
|
||||
if (isBlocking) {
|
||||
this.icon.setAttribute("tooltiptext", this.activeTooltipText);
|
||||
this.icon.setAttribute("state", "blocked-tracking-content");
|
||||
this.content.setAttribute("state", "blocked-tracking-content");
|
||||
|
||||
if (state & STATE_BLOCKED_TRACKING_CONTENT) {
|
||||
// Open the tracking protection introduction panel, if applicable.
|
||||
let introCount = gPrefService.getIntPref("privacy.trackingprotection.introCount");
|
||||
if (introCount < TrackingProtection.MAX_INTROS) {
|
||||
|
@ -88,6 +91,14 @@ let TrackingProtection = {
|
|||
gPrefService.savePrefFile(null);
|
||||
this.showIntroPanel();
|
||||
}
|
||||
} else if (isAllowing) {
|
||||
this.icon.setAttribute("tooltiptext", this.disabledTooltipText);
|
||||
this.icon.setAttribute("state", "loaded-tracking-content");
|
||||
this.content.setAttribute("state", "loaded-tracking-content");
|
||||
} else {
|
||||
this.icon.removeAttribute("tooltiptext");
|
||||
this.icon.removeAttribute("state");
|
||||
this.content.removeAttribute("state");
|
||||
}
|
||||
|
||||
// Telemetry for state change.
|
||||
|
|
|
@ -278,6 +278,8 @@ skip-if = os == "mac" || e10s # bug 967013; e10s: bug 1094761 - test hits the ne
|
|||
[browser_ctrlTab.js]
|
||||
[browser_datareporting_notification.js]
|
||||
skip-if = !datareporting
|
||||
[browser_datachoices_notification.js]
|
||||
skip-if = !datareporting
|
||||
[browser_devedition.js]
|
||||
[browser_devices_get_user_media.js]
|
||||
skip-if = buildapp == 'mulet' || (os == "linux" && debug) || e10s # linux: bug 976544; e10s: bug 1071623
|
||||
|
|
|
@ -0,0 +1,213 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
// Pass an empty scope object to the import to prevent "leaked window property"
|
||||
// errors in tests.
|
||||
let Preferences = Cu.import("resource://gre/modules/Preferences.jsm", {}).Preferences;
|
||||
let TelemetryReportingPolicy =
|
||||
Cu.import("resource://gre/modules/TelemetryReportingPolicy.jsm", {}).TelemetryReportingPolicy;
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "gDatareportingService",
|
||||
() => Cc["@mozilla.org/datareporting/service;1"]
|
||||
.getService(Ci.nsISupports)
|
||||
.wrappedJSObject);
|
||||
|
||||
const PREF_BRANCH = "datareporting.policy.";
|
||||
const PREF_DRS_ENABLED = "datareporting.healthreport.service.enabled";
|
||||
const PREF_BYPASS_NOTIFICATION = PREF_BRANCH + "dataSubmissionPolicyBypassNotification";
|
||||
const PREF_CURRENT_POLICY_VERSION = PREF_BRANCH + "currentPolicyVersion";
|
||||
const PREF_ACCEPTED_POLICY_VERSION = PREF_BRANCH + "dataSubmissionPolicyAcceptedVersion";
|
||||
const PREF_ACCEPTED_POLICY_DATE = PREF_BRANCH + "dataSubmissionPolicyNotifiedTime";
|
||||
|
||||
const TEST_POLICY_VERSION = 37;
|
||||
|
||||
/**
|
||||
* Wait for a tick.
|
||||
*/
|
||||
function promiseNextTick() {
|
||||
return new Promise(resolve => executeSoon(resolve));
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait for a notification to be shown in a notification box.
|
||||
* @param {Object} aNotificationBox The notification box.
|
||||
* @return {Promise} Resolved when the notification is displayed.
|
||||
*/
|
||||
function promiseWaitForAlertActive(aNotificationBox) {
|
||||
let deferred = PromiseUtils.defer();
|
||||
aNotificationBox.addEventListener("AlertActive", function onActive() {
|
||||
aNotificationBox.removeEventListener("AlertActive", onActive, true);
|
||||
deferred.resolve();
|
||||
});
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait for a notification to be closed.
|
||||
* @param {Object} aNotification The notification.
|
||||
* @return {Promise} Resolved when the notification is closed.
|
||||
*/
|
||||
function promiseWaitForNotificationClose(aNotification) {
|
||||
let deferred = PromiseUtils.defer();
|
||||
waitForNotificationClose(aNotification, deferred.resolve);
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
let checkInfobarButton = Task.async(function* (aNotification) {
|
||||
// Check that the button on the data choices infobar does the right thing.
|
||||
let buttons = aNotification.getElementsByTagName("button");
|
||||
Assert.equal(buttons.length, 1, "There is 1 button in the data reporting notification.");
|
||||
let button = buttons[0];
|
||||
|
||||
// Add an observer to ensure the "advanced" pane opened (but don't bother
|
||||
// closing it - we close the entire window when done.)
|
||||
let paneLoadedPromise = promiseTopicObserved("advanced-pane-loaded");
|
||||
|
||||
// Click on the button.
|
||||
button.click();
|
||||
|
||||
// Wait for the preferences panel to open.
|
||||
let preferenceWindow = yield paneLoadedPromise;
|
||||
yield promiseNextTick();
|
||||
// If the prefs are being displayed in a dialog we need to close it.
|
||||
// If in a tab (ie, in-content prefs) it closes with the window.
|
||||
if (!Services.prefs.getBoolPref("browser.preferences.inContent")) {
|
||||
prefWin.close();
|
||||
}
|
||||
});
|
||||
|
||||
add_task(function* setup(){
|
||||
const drsEnabled = Preferences.get(PREF_DRS_ENABLED, true);
|
||||
const bypassNotification = Preferences.get(PREF_BYPASS_NOTIFICATION, true);
|
||||
const currentPolicyVersion = Preferences.get(PREF_CURRENT_POLICY_VERSION, 1);
|
||||
|
||||
// Register a cleanup function to reset our preferences.
|
||||
registerCleanupFunction(() => {
|
||||
Preferences.set(PREF_DRS_ENABLED, drsEnabled);
|
||||
Preferences.set(PREF_BYPASS_NOTIFICATION, bypassNotification);
|
||||
Preferences.set(PREF_CURRENT_POLICY_VERSION, currentPolicyVersion);
|
||||
|
||||
// Start polling again.
|
||||
gDatareportingService.policy.startPolling();
|
||||
|
||||
return closeAllNotifications();
|
||||
});
|
||||
|
||||
// Disable Healthreport/Data reporting service.
|
||||
Preferences.set(PREF_DRS_ENABLED, false);
|
||||
// Don't skip the infobar visualisation.
|
||||
Preferences.set(PREF_BYPASS_NOTIFICATION, false);
|
||||
// Set the current policy version.
|
||||
Preferences.set(PREF_CURRENT_POLICY_VERSION, TEST_POLICY_VERSION);
|
||||
|
||||
// Stop the polling to make sure no policy gets displayed by FHR.
|
||||
gDatareportingService.policy.stopPolling();
|
||||
});
|
||||
|
||||
function clearAcceptedPolicy() {
|
||||
// Reset the accepted policy.
|
||||
Preferences.reset(PREF_ACCEPTED_POLICY_VERSION);
|
||||
Preferences.reset(PREF_ACCEPTED_POLICY_DATE);
|
||||
}
|
||||
|
||||
add_task(function* test_single_window(){
|
||||
clearAcceptedPolicy();
|
||||
|
||||
// Close all the notifications, then try to trigger the data choices infobar.
|
||||
yield closeAllNotifications();
|
||||
|
||||
let notificationBox = document.getElementById("global-notificationbox");
|
||||
|
||||
// Make sure that we have a coherent initial state.
|
||||
Assert.equal(Preferences.get(PREF_ACCEPTED_POLICY_VERSION, 0), 0,
|
||||
"No version should be set on init.");
|
||||
Assert.equal(Preferences.get(PREF_ACCEPTED_POLICY_DATE, 0), 0,
|
||||
"No date should be set on init.");
|
||||
Assert.ok(!TelemetryReportingPolicy.testIsUserNotified(),
|
||||
"User not notified about datareporting policy.");
|
||||
|
||||
let alertShownPromise = promiseWaitForAlertActive(notificationBox);
|
||||
// This should be false and trigger the Infobar.
|
||||
Assert.ok(!TelemetryReportingPolicy.canUpload(),
|
||||
"User should not be allowed to upload and the infobar should be triggered.");
|
||||
|
||||
// Wait for the infobar to be displayed.
|
||||
yield alertShownPromise;
|
||||
|
||||
Assert.equal(notificationBox.allNotifications.length, 1, "Notification Displayed.");
|
||||
Assert.ok(TelemetryReportingPolicy.canUpload(), "User should be allowed to upload now.");
|
||||
|
||||
yield promiseNextTick();
|
||||
let promiseClosed = promiseWaitForNotificationClose(notificationBox.currentNotification);
|
||||
yield checkInfobarButton(notificationBox.currentNotification);
|
||||
yield promiseClosed;
|
||||
|
||||
Assert.equal(notificationBox.allNotifications.length, 0, "No notifications remain.");
|
||||
|
||||
// Check that we are still clear to upload and that the policy data is saved.
|
||||
Assert.ok(TelemetryReportingPolicy.canUpload());
|
||||
Assert.equal(TelemetryReportingPolicy.testIsUserNotified(), true,
|
||||
"User notified about datareporting policy.");
|
||||
Assert.equal(Preferences.get(PREF_ACCEPTED_POLICY_VERSION, 0), TEST_POLICY_VERSION,
|
||||
"Version pref set.");
|
||||
Assert.greater(parseInt(Preferences.get(PREF_ACCEPTED_POLICY_DATE, null), 10), -1,
|
||||
"Date pref set.");
|
||||
});
|
||||
|
||||
add_task(function* test_multiple_windows(){
|
||||
clearAcceptedPolicy();
|
||||
|
||||
// Close all the notifications, then try to trigger the data choices infobar.
|
||||
yield closeAllNotifications();
|
||||
|
||||
// Ensure we see the notification on all windows and that action on one window
|
||||
// results in dismiss on every window.
|
||||
let otherWindow = yield BrowserTestUtils.openNewBrowserWindow();
|
||||
|
||||
// Get the notification box for both windows.
|
||||
let notificationBoxes = [
|
||||
document.getElementById("global-notificationbox"),
|
||||
otherWindow.document.getElementById("global-notificationbox")
|
||||
];
|
||||
|
||||
Assert.ok(notificationBoxes[1], "2nd window has a global notification box.");
|
||||
|
||||
// Make sure that we have a coherent initial state.
|
||||
Assert.equal(Preferences.get(PREF_ACCEPTED_POLICY_VERSION, 0), 0, "No version should be set on init.");
|
||||
Assert.equal(Preferences.get(PREF_ACCEPTED_POLICY_DATE, 0), 0, "No date should be set on init.");
|
||||
Assert.ok(!TelemetryReportingPolicy.testIsUserNotified(), "User not notified about datareporting policy.");
|
||||
|
||||
let showAlertPromises = [
|
||||
promiseWaitForAlertActive(notificationBoxes[0]),
|
||||
promiseWaitForAlertActive(notificationBoxes[1])
|
||||
];
|
||||
|
||||
// This should be false and trigger the Infobar.
|
||||
Assert.ok(!TelemetryReportingPolicy.canUpload(),
|
||||
"User should not be allowed to upload and the infobar should be triggered.");
|
||||
|
||||
yield Promise.all(showAlertPromises);
|
||||
|
||||
// Both notification were displayed. Close one and check that both gets closed.
|
||||
let closeAlertPromises = [
|
||||
promiseWaitForNotificationClose(notificationBoxes[0].currentNotification),
|
||||
promiseWaitForNotificationClose(notificationBoxes[1].currentNotification)
|
||||
];
|
||||
notificationBoxes[0].currentNotification.close();
|
||||
yield Promise.all(closeAlertPromises);
|
||||
|
||||
// Close the second window we opened.
|
||||
yield BrowserTestUtils.closeWindow(otherWindow);
|
||||
|
||||
// Check that we are clear to upload and that the policy data us saved.
|
||||
Assert.ok(TelemetryReportingPolicy.canUpload(),"User should be allowed to upload now.");
|
||||
Assert.equal(TelemetryReportingPolicy.testIsUserNotified(), true,
|
||||
"User notified about datareporting policy.");
|
||||
Assert.equal(Preferences.get(PREF_ACCEPTED_POLICY_VERSION, 0), TEST_POLICY_VERSION,
|
||||
"Version pref set.");
|
||||
Assert.greater(parseInt(Preferences.get(PREF_ACCEPTED_POLICY_DATE, null), 10), -1,
|
||||
"Date pref set.");
|
||||
});
|
|
@ -47,30 +47,6 @@ function sendNotifyRequest(name) {
|
|||
return [policy, deferred.promise];
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait for a <notification> to be closed then call the specified callback.
|
||||
*/
|
||||
function waitForNotificationClose(notification, cb) {
|
||||
let parent = notification.parentNode;
|
||||
|
||||
let observer = new MutationObserver(function onMutatations(mutations) {
|
||||
for (let mutation of mutations) {
|
||||
for (let i = 0; i < mutation.removedNodes.length; i++) {
|
||||
let node = mutation.removedNodes.item(i);
|
||||
|
||||
if (node != notification) {
|
||||
continue;
|
||||
}
|
||||
|
||||
observer.disconnect();
|
||||
cb();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
observer.observe(parent, {childList: true});
|
||||
}
|
||||
|
||||
let dumpAppender, rootLogger;
|
||||
|
||||
function test() {
|
||||
|
|
|
@ -46,6 +46,7 @@ function testBenignPage() {
|
|||
ok(!TrackingProtection.container.hidden, "The container is visible");
|
||||
ok(!TrackingProtection.content.hasAttribute("state"), "content: no state");
|
||||
ok(!TrackingProtection.icon.hasAttribute("state"), "icon: no state");
|
||||
ok(!TrackingProtection.icon.hasAttribute("tooltiptext"), "icon: no tooltip");
|
||||
|
||||
ok(hidden("#tracking-protection-icon"), "icon is hidden");
|
||||
ok(hidden("#tracking-action-block"), "blockButton is hidden");
|
||||
|
@ -64,6 +65,8 @@ function testTrackingPage(window) {
|
|||
'content: state="blocked-tracking-content"');
|
||||
is(TrackingProtection.icon.getAttribute("state"), "blocked-tracking-content",
|
||||
'icon: state="blocked-tracking-content"');
|
||||
is(TrackingProtection.icon.getAttribute("tooltiptext"),
|
||||
gNavigatorBundle.getString("trackingProtection.icon.activeTooltip"), "correct tooltip");
|
||||
|
||||
ok(!hidden("#tracking-protection-icon"), "icon is visible");
|
||||
ok(hidden("#tracking-action-block"), "blockButton is hidden");
|
||||
|
@ -90,6 +93,8 @@ function testTrackingPageUnblocked() {
|
|||
'content: state="loaded-tracking-content"');
|
||||
is(TrackingProtection.icon.getAttribute("state"), "loaded-tracking-content",
|
||||
'icon: state="loaded-tracking-content"');
|
||||
is(TrackingProtection.icon.getAttribute("tooltiptext"),
|
||||
gNavigatorBundle.getString("trackingProtection.icon.disabledTooltip"), "correct tooltip");
|
||||
|
||||
ok(!hidden("#tracking-protection-icon"), "icon is visible");
|
||||
ok(!hidden("#tracking-action-block"), "blockButton is visible");
|
||||
|
|
|
@ -9,6 +9,27 @@ XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
|
|||
XPCOMUtils.defineLazyModuleGetter(this, "PlacesTestUtils",
|
||||
"resource://testing-common/PlacesTestUtils.jsm");
|
||||
|
||||
/**
|
||||
* Wait for a <notification> to be closed then call the specified callback.
|
||||
*/
|
||||
function waitForNotificationClose(notification, cb) {
|
||||
let parent = notification.parentNode;
|
||||
|
||||
let observer = new MutationObserver(function onMutatations(mutations) {
|
||||
for (let mutation of mutations) {
|
||||
for (let i = 0; i < mutation.removedNodes.length; i++) {
|
||||
let node = mutation.removedNodes.item(i);
|
||||
if (node != notification) {
|
||||
continue;
|
||||
}
|
||||
observer.disconnect();
|
||||
cb();
|
||||
}
|
||||
}
|
||||
});
|
||||
observer.observe(parent, {childList: true});
|
||||
}
|
||||
|
||||
function closeAllNotifications () {
|
||||
let notificationBox = document.getElementById("global-notificationbox");
|
||||
|
||||
|
|
Двоичные данные
browser/branding/aurora/content/identity-icons-brand.png
До Ширина: | Высота: | Размер: 1.5 KiB |
После Ширина: | Высота: | Размер: 10 KiB |
Двоичные данные
browser/branding/aurora/content/identity-icons-brand@2x.png
До Ширина: | Высота: | Размер: 3.3 KiB |
|
@ -14,7 +14,6 @@ browser.jar:
|
|||
content/branding/icon16.png (../default16.png)
|
||||
content/branding/icon32.png (../default32.png)
|
||||
content/branding/icon128.png (../mozicon128.png)
|
||||
content/branding/identity-icons-brand.png (identity-icons-brand.png)
|
||||
content/branding/identity-icons-brand@2x.png (identity-icons-brand@2x.png)
|
||||
content/branding/identity-icons-brand.svg (identity-icons-brand.svg)
|
||||
content/branding/silhouette-40.svg (silhouette-40.svg)
|
||||
content/branding/aboutDialog.css (aboutDialog.css)
|
||||
|
|
Двоичные данные
browser/branding/nightly/content/identity-icons-brand.png
До Ширина: | Высота: | Размер: 1.6 KiB |
После Ширина: | Высота: | Размер: 38 KiB |
Двоичные данные
browser/branding/nightly/content/identity-icons-brand@2x.png
До Ширина: | Высота: | Размер: 4.2 KiB |
|
@ -14,7 +14,6 @@ browser.jar:
|
|||
content/branding/icon16.png (../default16.png)
|
||||
content/branding/icon32.png (../default32.png)
|
||||
content/branding/icon128.png (../mozicon128.png)
|
||||
content/branding/identity-icons-brand.png (identity-icons-brand.png)
|
||||
content/branding/identity-icons-brand@2x.png (identity-icons-brand@2x.png)
|
||||
content/branding/identity-icons-brand.svg (identity-icons-brand.svg)
|
||||
content/branding/silhouette-40.svg (silhouette-40.svg)
|
||||
content/branding/aboutDialog.css (aboutDialog.css)
|
||||
|
|
Двоичные данные
browser/branding/official/content/identity-icons-brand.png
До Ширина: | Высота: | Размер: 1.3 KiB |
После Ширина: | Высота: | Размер: 10 KiB |
Двоичные данные
browser/branding/official/content/identity-icons-brand@2x.png
До Ширина: | Высота: | Размер: 3.1 KiB |
|
@ -13,7 +13,6 @@ browser.jar:
|
|||
content/branding/icon16.png (../default16.png)
|
||||
content/branding/icon32.png (../default32.png)
|
||||
content/branding/icon128.png (../mozicon128.png)
|
||||
content/branding/identity-icons-brand.png (identity-icons-brand.png)
|
||||
content/branding/identity-icons-brand@2x.png (identity-icons-brand@2x.png)
|
||||
content/branding/identity-icons-brand.svg (identity-icons-brand.svg)
|
||||
content/branding/silhouette-40.svg (silhouette-40.svg)
|
||||
content/branding/aboutDialog.css (aboutDialog.css)
|
||||
|
|
Двоичные данные
browser/branding/unofficial/content/identity-icons-brand.png
До Ширина: | Высота: | Размер: 1.6 KiB |
После Ширина: | Высота: | Размер: 38 KiB |
Двоичные данные
browser/branding/unofficial/content/identity-icons-brand@2x.png
До Ширина: | Высота: | Размер: 4.2 KiB |
|
@ -14,7 +14,6 @@ browser.jar:
|
|||
content/branding/icon16.png (../default16.png)
|
||||
content/branding/icon32.png (../default32.png)
|
||||
content/branding/icon128.png (../mozicon128.png)
|
||||
content/branding/identity-icons-brand.png (identity-icons-brand.png)
|
||||
content/branding/identity-icons-brand@2x.png (identity-icons-brand@2x.png)
|
||||
content/branding/identity-icons-brand.svg (identity-icons-brand.svg)
|
||||
content/branding/silhouette-40.svg (silhouette-40.svg)
|
||||
content/branding/aboutDialog.css (aboutDialog.css)
|
||||
|
|
|
@ -638,6 +638,7 @@ loop.roomViews = (function(mozL10n) {
|
|||
return true;
|
||||
|
||||
case ROOM_STATES.READY:
|
||||
case ROOM_STATES.GATHER:
|
||||
case ROOM_STATES.INIT:
|
||||
case ROOM_STATES.JOINING:
|
||||
case ROOM_STATES.SESSION_CONNECTED:
|
||||
|
|
|
@ -638,6 +638,7 @@ loop.roomViews = (function(mozL10n) {
|
|||
return true;
|
||||
|
||||
case ROOM_STATES.READY:
|
||||
case ROOM_STATES.GATHER:
|
||||
case ROOM_STATES.INIT:
|
||||
case ROOM_STATES.JOINING:
|
||||
case ROOM_STATES.SESSION_CONNECTED:
|
||||
|
|
|
@ -372,6 +372,7 @@ loop.standaloneRoomViews = (function(mozL10n) {
|
|||
return true;
|
||||
|
||||
case ROOM_STATES.READY:
|
||||
case ROOM_STATES.GATHER:
|
||||
case ROOM_STATES.INIT:
|
||||
case ROOM_STATES.JOINING:
|
||||
case ROOM_STATES.SESSION_CONNECTED:
|
||||
|
|
|
@ -372,6 +372,7 @@ loop.standaloneRoomViews = (function(mozL10n) {
|
|||
return true;
|
||||
|
||||
case ROOM_STATES.READY:
|
||||
case ROOM_STATES.GATHER:
|
||||
case ROOM_STATES.INIT:
|
||||
case ROOM_STATES.JOINING:
|
||||
case ROOM_STATES.SESSION_CONNECTED:
|
||||
|
|
|
@ -343,6 +343,9 @@ trackingProtection.intro.description=When the shield is visible, that means Fire
|
|||
trackingProtection.intro.step1of3=1 of 3
|
||||
trackingProtection.intro.nextButton.label=Next
|
||||
|
||||
trackingProtection.icon.activeTooltip=Tracking attempts blocked
|
||||
trackingProtection.icon.disabledTooltip=Tracking content detected
|
||||
|
||||
# Edit Bookmark UI
|
||||
editBookmarkPanel.pageBookmarkedTitle=Page Bookmarked
|
||||
editBookmarkPanel.pageBookmarkedDescription=%S will always remember this page for you.
|
||||
|
|
|
@ -34,7 +34,7 @@
|
|||
--toolbarbutton-combined-boxshadow: 0 0 0 1px hsla(0,0%,100%,.2);
|
||||
--toolbarbutton-combined-backgroundimage: linear-gradient(hsla(210,54%,20%,.2) 0, hsla(210,54%,20%,.2) 18px);
|
||||
|
||||
--verified-identity-box-backgroundcolor: #fff;
|
||||
--identity-box-verified-background-color: #fff;
|
||||
}
|
||||
|
||||
#menubar-items {
|
||||
|
@ -986,7 +986,7 @@ toolbarbutton[constrain-size="true"][cui-areatype="toolbar"] > .toolbarbutton-ba
|
|||
}
|
||||
|
||||
#identity-box.verifiedIdentity:not(:-moz-lwtheme):not(:hover):not([open=true]) {
|
||||
background-color: var(--verified-identity-box-backgroundcolor);
|
||||
background-color: var(--identity-box-verified-background-color);
|
||||
}
|
||||
|
||||
#identity-box:-moz-focusring {
|
||||
|
|
|
@ -174,16 +174,6 @@ panelmultiview[nosubviews=true] > .panel-viewcontainer > .panel-viewstack > .pan
|
|||
flex-direction: column;
|
||||
}
|
||||
|
||||
#app-extension-point-end > #PanelUI-menu-button {
|
||||
padding: 2px 5px;
|
||||
}
|
||||
#app-extension-point-end > #PanelUI-menu-button .toolbarbutton-text {
|
||||
display: none;
|
||||
}
|
||||
#app-extension-point-end > #PanelUI-menu-button .toolbarbutton-icon {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
#PanelUI-popup > arrowscrollbox > autorepeatbutton {
|
||||
display: none;
|
||||
}
|
||||
|
|
|
@ -63,7 +63,7 @@
|
|||
:root[devtoolstheme="dark"] #identity-box {
|
||||
--identity-box-border-color: #5F6670;
|
||||
--identity-box-chrome-color: #46afe3;
|
||||
--verified-identity-box-background-color: transparent;
|
||||
--identity-box-verified-background-color: transparent;
|
||||
--identity-box-selected-background-color: rgba(231,230,230,.2);
|
||||
}
|
||||
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
%endif
|
||||
%endif
|
||||
|
||||
border-inline-end: 1px solid;
|
||||
border-inline-end: 1px solid var(--identity-box-border-color);
|
||||
border-image: linear-gradient(transparent 15%,
|
||||
var(--identity-box-border-color) 15%,
|
||||
var(--identity-box-border-color) 85%,
|
||||
|
@ -33,6 +33,7 @@
|
|||
#identity-box:hover,
|
||||
#identity-box[open=true] {
|
||||
background-color: var(--identity-box-selected-background-color);
|
||||
border-image-source: none;
|
||||
}
|
||||
|
||||
#urlbar[pageproxystate="valid"] > #identity-box.verifiedIdentity {
|
||||
|
@ -119,8 +120,7 @@
|
|||
}
|
||||
|
||||
.chromeUI > #page-proxy-favicon[pageproxystate="valid"] {
|
||||
list-style-image: url(chrome://branding/content/identity-icons-brand.png);
|
||||
-moz-image-region: rect(0, 16px, 16px, 0);
|
||||
list-style-image: url(chrome://branding/content/identity-icons-brand.svg);
|
||||
}
|
||||
|
||||
.verifiedDomain > #page-proxy-favicon[pageproxystate="valid"],
|
||||
|
@ -146,13 +146,6 @@
|
|||
opacity: 0.3;
|
||||
}
|
||||
|
||||
@media (min-resolution: 1.1dppx) {
|
||||
.chromeUI > #page-proxy-favicon[pageproxystate="valid"] {
|
||||
list-style-image: url(chrome://branding/content/identity-icons-brand@2x.png);
|
||||
-moz-image-region: rect(0, 32px, 32px, 0);
|
||||
}
|
||||
}
|
||||
|
||||
#urlbar[actiontype="searchengine"] > #identity-box > #page-proxy-favicon {
|
||||
-moz-image-region: inherit;
|
||||
list-style-image: url(chrome://global/skin/icons/autocomplete-search.svg#search-icon);
|
||||
|
|
|
@ -38,7 +38,7 @@
|
|||
--toolbarbutton-combined-boxshadow: none;
|
||||
--toolbarbutton-combined-backgroundimage: linear-gradient(hsla(210,54%,20%,.2) 0, hsla(210,54%,20%,.2) 16px);
|
||||
|
||||
--verified-identity-box-backgroundcolor: #FFF;
|
||||
--identity-box-verified-background-color: #fff;
|
||||
|
||||
--urlbar-dropmarker-url: url("chrome://browser/skin/urlbar-history-dropmarker.png");
|
||||
--urlbar-dropmarker-region: rect(0px, 11px, 14px, 0px);
|
||||
|
@ -1261,6 +1261,8 @@ toolbarbutton[constrain-size="true"][cui-areatype="toolbar"] > .toolbarbutton-ba
|
|||
.searchbar-textbox {
|
||||
font-size: 1.15em;
|
||||
min-height: 28px;
|
||||
transition-property: border-color, box-shadow;
|
||||
transition-duration: .1s;
|
||||
}
|
||||
|
||||
:root {
|
||||
|
@ -1435,7 +1437,7 @@ html|*.urlbar-input:-moz-lwtheme::-moz-placeholder,
|
|||
}
|
||||
|
||||
#identity-box.verifiedIdentity:not(:-moz-lwtheme):not(:hover):not([open=true]) {
|
||||
background-color: var(--verified-identity-box-backgroundcolor);
|
||||
background-color: var(--identity-box-verified-background-color);
|
||||
}
|
||||
|
||||
#identity-box:-moz-focusring {
|
||||
|
|
|
@ -653,10 +653,6 @@ just addresses the organization to follow, e.g. "This site is run by " -->
|
|||
|
||||
<!-- Voice search in the awesome bar -->
|
||||
<!ENTITY voicesearch_prompt "Speak now">
|
||||
<!ENTITY voicesearch_failed_title "&brandShortName; Voice Search">
|
||||
<!ENTITY voicesearch_failed_message "There is a problem with voice search right now. Please try later.">
|
||||
<!ENTITY voicesearch_failed_message_recoverable "Sorry! We could not recognize your words. Please try again.">
|
||||
<!ENTITY voicesearch_failed_retry "Try again">
|
||||
|
||||
<!-- Localization note (remote_tabs_last_synced): the variable is replaced by a
|
||||
"relative time span string" produced by Android. This string describes the
|
||||
|
|
|
@ -689,8 +689,8 @@ OnSharedPreferenceChangeListener
|
|||
pref.setSummary(getString(R.string.pref_category_customize_alt_summary));
|
||||
}
|
||||
if (getResources().getString(R.string.pref_category_input_options).equals(key)) {
|
||||
if (!AppConstants.NIGHTLY_BUILD || (!InputOptionsUtils.supportsVoiceRecognizer(getApplicationContext(), getResources().getString(R.string.voicesearch_prompt)) &&
|
||||
!InputOptionsUtils.supportsQrCodeReader(getApplicationContext()))) {
|
||||
if (!InputOptionsUtils.supportsVoiceRecognizer(getApplicationContext(), getResources().getString(R.string.voicesearch_prompt)) &&
|
||||
!InputOptionsUtils.supportsQrCodeReader(getApplicationContext())) {
|
||||
preferences.removePreference(pref);
|
||||
i--;
|
||||
continue;
|
||||
|
@ -824,7 +824,7 @@ OnSharedPreferenceChangeListener
|
|||
continue;
|
||||
}
|
||||
} else if (PREFS_VOICE_INPUT_ENABLED.equals(key)) {
|
||||
if (!AppConstants.NIGHTLY_BUILD || !InputOptionsUtils.supportsVoiceRecognizer(getApplicationContext(), getResources().getString(R.string.voicesearch_prompt))) {
|
||||
if (!InputOptionsUtils.supportsVoiceRecognizer(getApplicationContext(), getResources().getString(R.string.voicesearch_prompt))) {
|
||||
// Remove UI for voice input on non nightly builds.
|
||||
preferences.removePreference(pref);
|
||||
i--;
|
||||
|
|
|
@ -540,10 +540,6 @@
|
|||
|
||||
<!-- Voice search from the Awesome Bar -->
|
||||
<string name="voicesearch_prompt">&voicesearch_prompt;</string>
|
||||
<string name="voicesearch_failed_title">&voicesearch_failed_title;</string>
|
||||
<string name="voicesearch_failed_message">&voicesearch_failed_message;</string>
|
||||
<string name="voicesearch_failed_message_recoverable">&voicesearch_failed_message_recoverable;</string>
|
||||
<string name="voicesearch_failed_retry">&voicesearch_failed_retry;</string>
|
||||
|
||||
<!-- Miscellaneous -->
|
||||
<string name="ellipsis">&ellipsis;</string>
|
||||
|
|
|
@ -218,10 +218,6 @@ public class ToolbarEditLayout extends ThemedLinearLayout {
|
|||
}
|
||||
|
||||
private boolean voiceIsEnabled(Context context, String prompt) {
|
||||
// Voice input is enabled for nightly only
|
||||
if(!AppConstants.NIGHTLY_BUILD) {
|
||||
return false;
|
||||
}
|
||||
final boolean voiceIsSupported = InputOptionsUtils.supportsVoiceRecognizer(context, prompt);
|
||||
if (!voiceIsSupported) {
|
||||
return false;
|
||||
|
@ -237,22 +233,6 @@ public class ToolbarEditLayout extends ThemedLinearLayout {
|
|||
ActivityHandlerHelper.startIntentForActivity(activity, intent, new ActivityResultHandler() {
|
||||
@Override
|
||||
public void onActivityResult(int resultCode, Intent data) {
|
||||
switch (resultCode) {
|
||||
case RecognizerIntent.RESULT_CLIENT_ERROR:
|
||||
case RecognizerIntent.RESULT_NETWORK_ERROR:
|
||||
case RecognizerIntent.RESULT_SERVER_ERROR:
|
||||
// We have an temporarily unrecoverable error.
|
||||
handleVoiceSearchError(false);
|
||||
break;
|
||||
case RecognizerIntent.RESULT_AUDIO_ERROR:
|
||||
case RecognizerIntent.RESULT_NO_MATCH:
|
||||
// Maybe the user can say it differently?
|
||||
handleVoiceSearchError(true);
|
||||
break;
|
||||
case Activity.RESULT_CANCELED:
|
||||
break;
|
||||
}
|
||||
|
||||
if (resultCode != Activity.RESULT_OK) {
|
||||
return;
|
||||
}
|
||||
|
@ -272,33 +252,6 @@ public class ToolbarEditLayout extends ThemedLinearLayout {
|
|||
});
|
||||
}
|
||||
|
||||
private void handleVoiceSearchError(boolean offerRetry) {
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(getContext())
|
||||
.setTitle(R.string.voicesearch_failed_title)
|
||||
.setIcon(R.drawable.icon).setNeutralButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
dialog.dismiss();
|
||||
}
|
||||
});
|
||||
|
||||
if (offerRetry) {
|
||||
builder.setMessage(R.string.voicesearch_failed_message_recoverable)
|
||||
.setNegativeButton(R.string.voicesearch_failed_retry, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
launchVoiceRecognizer();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
builder.setMessage(R.string.voicesearch_failed_message);
|
||||
}
|
||||
|
||||
AlertDialog dialog = builder.create();
|
||||
|
||||
dialog.show();
|
||||
}
|
||||
|
||||
private boolean qrCodeIsEnabled(Context context) {
|
||||
// QR code is enabled for nightly only
|
||||
if(!AppConstants.NIGHTLY_BUILD) {
|
||||
|
|
|
@ -229,7 +229,7 @@ public class testSettingsMenuItems extends PixelTest {
|
|||
}
|
||||
|
||||
// Voice input
|
||||
if (AppConstants.NIGHTLY_BUILD && InputOptionsUtils.supportsVoiceRecognizer(this.getActivity().getApplicationContext(), this.getActivity().getResources().getString(R.string.voicesearch_prompt))) {
|
||||
if (InputOptionsUtils.supportsVoiceRecognizer(this.getActivity().getApplicationContext(), this.getActivity().getResources().getString(R.string.voicesearch_prompt))) {
|
||||
String[] voiceInputUi = { mStringHelper.VOICE_INPUT_TITLE_LABEL, mStringHelper.VOICE_INPUT_SUMMARY_LABEL };
|
||||
settingsMap.get(PATH_DISPLAY).add(voiceInputUi);
|
||||
}
|
||||
|
|
|
@ -87,6 +87,8 @@ XPCOMUtils.defineLazyModuleGetter(this, "TelemetrySession",
|
|||
"resource://gre/modules/TelemetrySession.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "TelemetrySend",
|
||||
"resource://gre/modules/TelemetrySend.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "TelemetryReportingPolicy",
|
||||
"resource://gre/modules/TelemetryReportingPolicy.jsm");
|
||||
|
||||
/**
|
||||
* Setup Telemetry logging. This function also gets called when loggin related
|
||||
|
@ -651,6 +653,9 @@ let Impl = {
|
|||
this._sessionRecorder.onStartup();
|
||||
}
|
||||
|
||||
// This will trigger displaying the datachoices infobar.
|
||||
TelemetryReportingPolicy.setup();
|
||||
|
||||
if (!this.enableTelemetryRecording()) {
|
||||
this._log.config("setupChromeProcess - Telemetry recording is disabled, skipping Chrome process setup.");
|
||||
return Promise.resolve();
|
||||
|
@ -726,6 +731,9 @@ let Impl = {
|
|||
|
||||
// Now do an orderly shutdown.
|
||||
try {
|
||||
// Stop the datachoices infobar display.
|
||||
TelemetryReportingPolicy.shutdown();
|
||||
|
||||
// Stop any ping sending.
|
||||
yield TelemetrySend.shutdown();
|
||||
|
||||
|
|
|
@ -0,0 +1,419 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
this.EXPORTED_SYMBOLS = [
|
||||
"TelemetryReportingPolicy"
|
||||
];
|
||||
|
||||
const {classes: Cc, interfaces: Ci, results: Cr, utils: Cu} = Components;
|
||||
|
||||
Cu.import("resource://gre/modules/Log.jsm", this);
|
||||
Cu.import("resource://gre/modules/Preferences.jsm", this);
|
||||
Cu.import("resource://gre/modules/Services.jsm", this);
|
||||
Cu.import("resource://gre/modules/Timer.jsm", this);
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm", this);
|
||||
Cu.import("resource://services-common/observers.js", this);
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "TelemetrySend",
|
||||
"resource://gre/modules/TelemetrySend.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "UpdateChannel",
|
||||
"resource://gre/modules/UpdateChannel.jsm");
|
||||
|
||||
const LOGGER_NAME = "Toolkit.Telemetry";
|
||||
const LOGGER_PREFIX = "TelemetryReportingPolicy::";
|
||||
|
||||
// Oldest year to allow in date preferences. The FHR infobar was implemented in
|
||||
// 2012 and no dates older than that should be encountered.
|
||||
const OLDEST_ALLOWED_ACCEPTANCE_YEAR = 2012;
|
||||
|
||||
const PREF_BRANCH = "datareporting.policy.";
|
||||
// Indicates whether this is the first run or not. This is used to decide when to display
|
||||
// the policy.
|
||||
const PREF_FIRST_RUN = "toolkit.telemetry.reportingpolicy.firstRun";
|
||||
// Allows to skip the datachoices infobar. This should only be used in tests.
|
||||
const PREF_BYPASS_NOTIFICATION = PREF_BRANCH + "dataSubmissionPolicyBypassNotification";
|
||||
// The submission kill switch: if this preference is disable, no submission will ever take place.
|
||||
const PREF_DATA_SUBMISSION_ENABLED = PREF_BRANCH + "dataSubmissionEnabled";
|
||||
// This preference holds the current policy version, which overrides
|
||||
// DEFAULT_DATAREPORTING_POLICY_VERSION
|
||||
const PREF_CURRENT_POLICY_VERSION = PREF_BRANCH + "currentPolicyVersion";
|
||||
// This indicates the minimum required policy version. If the accepted policy version
|
||||
// is lower than this, the notification bar must be showed again.
|
||||
const PREF_MINIMUM_POLICY_VERSION = PREF_BRANCH + "minimumPolicyVersion";
|
||||
// The version of the accepted policy.
|
||||
const PREF_ACCEPTED_POLICY_VERSION = PREF_BRANCH + "dataSubmissionPolicyAcceptedVersion";
|
||||
// The date user accepted the policy.
|
||||
const PREF_ACCEPTED_POLICY_DATE = PREF_BRANCH + "dataSubmissionPolicyNotifiedTime";
|
||||
// The following preferences are deprecated and will be purged during the preferences
|
||||
// migration process.
|
||||
const DEPRECATED_FHR_PREFS = [
|
||||
PREF_BRANCH + "dataSubmissionPolicyAccepted",
|
||||
PREF_BRANCH + "dataSubmissionPolicyBypassAcceptance",
|
||||
PREF_BRANCH + "dataSubmissionPolicyResponseType",
|
||||
PREF_BRANCH + "dataSubmissionPolicyResponseTime"
|
||||
];
|
||||
|
||||
// How much time until we display the data choices notification bar, on the first run.
|
||||
const NOTIFICATION_DELAY_FIRST_RUN_MSEC = 60 * 1000; // 60s
|
||||
// Same as above, for the next runs.
|
||||
const NOTIFICATION_DELAY_NEXT_RUNS_MSEC = 10 * 1000; // 10s
|
||||
|
||||
/**
|
||||
* This is a policy object used to override behavior within this module.
|
||||
* Tests override properties on this object to allow for control of behavior
|
||||
* that would otherwise be very hard to cover.
|
||||
*/
|
||||
let Policy = {
|
||||
now: () => new Date(),
|
||||
setShowInfobarTimeout: (callback, delayMs) => setTimeout(callback, delayMs),
|
||||
clearShowInfobarTimeout: (id) => clearTimeout(id),
|
||||
};
|
||||
|
||||
/**
|
||||
* Represents a request to display data 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`.
|
||||
*
|
||||
* If for whatever reason the callee could not display a notice,
|
||||
* it should call `onUserNotifyFailed`.
|
||||
*
|
||||
* @param {Object} aLog The log object used to log the error in case of failures.
|
||||
*/
|
||||
function NotifyPolicyRequest(aLog) {
|
||||
this._log = aLog;
|
||||
}
|
||||
|
||||
NotifyPolicyRequest.prototype = Object.freeze({
|
||||
/**
|
||||
* Called when the user is notified of the policy.
|
||||
*/
|
||||
onUserNotifyComplete: function () {
|
||||
return TelemetryReportingPolicyImpl._infobarShownCallback();
|
||||
},
|
||||
|
||||
/**
|
||||
* Called when there was an error notifying the user about the policy.
|
||||
*
|
||||
* @param error
|
||||
* (Error) Explains what went wrong.
|
||||
*/
|
||||
onUserNotifyFailed: function (error) {
|
||||
this._log.error("onUserNotifyFailed - " + error);
|
||||
},
|
||||
});
|
||||
|
||||
this.TelemetryReportingPolicy = {
|
||||
// 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.
|
||||
DEFAULT_DATAREPORTING_POLICY_VERSION: 1,
|
||||
|
||||
/**
|
||||
* Setup the policy.
|
||||
*/
|
||||
setup: function() {
|
||||
return TelemetryReportingPolicyImpl.setup();
|
||||
},
|
||||
|
||||
/**
|
||||
* Shutdown and clear the policy.
|
||||
*/
|
||||
shutdown: function() {
|
||||
return TelemetryReportingPolicyImpl.shutdown();
|
||||
},
|
||||
|
||||
/**
|
||||
* Check if we are allowed to upload data. In order to submit data both these conditions
|
||||
* should be true:
|
||||
* - The data submission preference should be true.
|
||||
* - The datachoices infobar should have been displayed.
|
||||
*
|
||||
* @return {Boolean} True if we are allowed to upload data, false otherwise.
|
||||
*/
|
||||
canUpload: function() {
|
||||
return TelemetryReportingPolicyImpl.canUpload();
|
||||
},
|
||||
|
||||
/**
|
||||
* Test only method, restarts the policy.
|
||||
*/
|
||||
reset: function() {
|
||||
return TelemetryReportingPolicyImpl.reset();
|
||||
},
|
||||
|
||||
/**
|
||||
* Test only method, used to check if user is notified of the policy in tests.
|
||||
*/
|
||||
testIsUserNotified: function() {
|
||||
return TelemetryReportingPolicyImpl.isUserNotifiedOfCurrentPolicy;
|
||||
},
|
||||
|
||||
/**
|
||||
* Test only method, used to simulate the infobar being shown in xpcshell tests.
|
||||
*/
|
||||
testInfobarShown: function() {
|
||||
return TelemetryReportingPolicyImpl._infobarShownCallback();
|
||||
},
|
||||
};
|
||||
|
||||
let TelemetryReportingPolicyImpl = {
|
||||
_logger: null,
|
||||
// Keep track of the notification status if user wasn't notified already.
|
||||
_notificationInProgress: false,
|
||||
// The timer used to show the datachoices notification at startup.
|
||||
_startupNotificationTimerId: null,
|
||||
|
||||
get _log() {
|
||||
if (!this._logger) {
|
||||
this._logger = Log.repository.getLoggerWithMessagePrefix(LOGGER_NAME, LOGGER_PREFIX);
|
||||
}
|
||||
|
||||
return this._logger;
|
||||
},
|
||||
|
||||
/**
|
||||
* Get the date the policy was notified.
|
||||
* @return {Object} A date object or null on errors.
|
||||
*/
|
||||
get dataSubmissionPolicyNotifiedDate() {
|
||||
let prefString = Preferences.get(PREF_ACCEPTED_POLICY_DATE, 0);
|
||||
let valueInteger = parseInt(prefString, 10);
|
||||
|
||||
// If nothing or an invalid value is saved in the prefs, bail out.
|
||||
if (Number.isNaN(valueInteger) || valueInteger == 0) {
|
||||
this._log.error("get dataSubmissionPolicyNotifiedDate - Invalid date stored.");
|
||||
return null;
|
||||
}
|
||||
|
||||
// Make sure the notification date is newer then the oldest allowed date.
|
||||
let date = new Date(valueInteger);
|
||||
if (date.getFullYear() < OLDEST_ALLOWED_ACCEPTANCE_YEAR) {
|
||||
this._log.error("get dataSubmissionPolicyNotifiedDate - The stored date is too old.");
|
||||
return null;
|
||||
}
|
||||
|
||||
return date;
|
||||
},
|
||||
|
||||
/**
|
||||
* Set the date the policy was notified.
|
||||
* @param {Object} aDate A valid date object.
|
||||
*/
|
||||
set dataSubmissionPolicyNotifiedDate(aDate) {
|
||||
this._log.trace("set dataSubmissionPolicyNotifiedDate - aDate: " + aDate);
|
||||
|
||||
if (!aDate || aDate.getFullYear() < OLDEST_ALLOWED_ACCEPTANCE_YEAR) {
|
||||
this._log.error("set dataSubmissionPolicyNotifiedDate - Invalid notification date.");
|
||||
return;
|
||||
}
|
||||
|
||||
Preferences.set(PREF_ACCEPTED_POLICY_DATE, aDate.getTime().toString());
|
||||
},
|
||||
|
||||
/**
|
||||
* Whether submission of data is allowed.
|
||||
*
|
||||
* This is the master switch for remote server communication. If it is
|
||||
* false, we never request upload or deletion.
|
||||
*/
|
||||
get dataSubmissionEnabled() {
|
||||
// Default is true because we are opt-out.
|
||||
return Preferences.get(PREF_DATA_SUBMISSION_ENABLED, true);
|
||||
},
|
||||
|
||||
get currentPolicyVersion() {
|
||||
return Preferences.get(PREF_CURRENT_POLICY_VERSION,
|
||||
TelemetryReportingPolicy.DEFAULT_DATAREPORTING_POLICY_VERSION);
|
||||
},
|
||||
|
||||
/**
|
||||
* The minimum policy version which for dataSubmissionPolicyAccepted to
|
||||
* to be valid.
|
||||
*/
|
||||
get minimumPolicyVersion() {
|
||||
const minPolicyVersion = Preferences.get(PREF_MINIMUM_POLICY_VERSION, 1);
|
||||
|
||||
// First check if the current channel has a specific minimum policy version. If not,
|
||||
// use the general minimum policy version.
|
||||
let channel = "";
|
||||
try {
|
||||
channel = UpdateChannel.get(false);
|
||||
} catch(e) {
|
||||
this._log.error("minimumPolicyVersion - Unable to retrieve the current channel.");
|
||||
return minPolicyVersion;
|
||||
}
|
||||
const channelPref = PREF_MINIMUM_POLICY_VERSION + ".channel-" + channel;
|
||||
return Preferences.get(channelPref, minPolicyVersion);
|
||||
},
|
||||
|
||||
get dataSubmissionPolicyAcceptedVersion() {
|
||||
return Preferences.get(PREF_ACCEPTED_POLICY_VERSION, 0);
|
||||
},
|
||||
|
||||
set dataSubmissionPolicyAcceptedVersion(value) {
|
||||
Preferences.set(PREF_ACCEPTED_POLICY_VERSION, value);
|
||||
},
|
||||
|
||||
/**
|
||||
* Checks to see if the user has been notified about data submission
|
||||
* @return {Bool} True if user has been notified and the notification is still valid,
|
||||
* false otherwise.
|
||||
*/
|
||||
get isUserNotifiedOfCurrentPolicy() {
|
||||
// If we don't have a sane notification date, the user was not notified yet.
|
||||
if (!this.dataSubmissionPolicyNotifiedDate ||
|
||||
this.dataSubmissionPolicyNotifiedDate.getTime() <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// The accepted policy version should not be less than the minimum policy version.
|
||||
if (this.dataSubmissionPolicyAcceptedVersion < this.minimumPolicyVersion) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Otherwise the user was already notified.
|
||||
return true;
|
||||
},
|
||||
|
||||
/**
|
||||
* Test only method, restarts the policy.
|
||||
*/
|
||||
reset: function() {
|
||||
this.shutdown();
|
||||
return this.setup();
|
||||
},
|
||||
|
||||
/**
|
||||
* Setup the policy.
|
||||
*/
|
||||
setup: function() {
|
||||
this._log.trace("setup");
|
||||
|
||||
// Migrate the data choices infobar, if needed.
|
||||
this._migratePreferences();
|
||||
|
||||
// Add the event observers.
|
||||
Services.obs.addObserver(this, "sessionstore-windows-restored", false);
|
||||
},
|
||||
|
||||
/**
|
||||
* Clean up the reporting policy.
|
||||
*/
|
||||
shutdown: function() {
|
||||
this._log.trace("shutdown");
|
||||
|
||||
this._detachObservers();
|
||||
|
||||
Policy.clearShowInfobarTimeout(this._startupNotificationTimerId);
|
||||
},
|
||||
|
||||
/**
|
||||
* Detach the observers that were attached during setup.
|
||||
*/
|
||||
_detachObservers: function() {
|
||||
Services.obs.removeObserver(this, "sessionstore-windows-restored");
|
||||
},
|
||||
|
||||
/**
|
||||
* Check if we are allowed to upload data. In order to submit data both these conditions
|
||||
* should be true:
|
||||
* - The data submission preference should be true.
|
||||
* - The datachoices infobar should have been displayed.
|
||||
*
|
||||
* @return {Boolean} True if we are allowed to upload data, false otherwise.
|
||||
*/
|
||||
canUpload: function() {
|
||||
// If data submission is disabled, there's no point in showing the infobar. Just
|
||||
// forbid to upload.
|
||||
if (!this.dataSubmissionEnabled) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Make sure the user is notified of the current policy. If he isn't, don't try
|
||||
// to upload anything.
|
||||
if (!this._ensureUserNotified()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Submission is enabled and user is notified: upload is allowed.
|
||||
return true;
|
||||
},
|
||||
|
||||
/**
|
||||
* Migrate the data policy preferences, if needed.
|
||||
*/
|
||||
_migratePreferences: function() {
|
||||
// Current prefs are mostly the same than the old ones, except for some deprecated ones.
|
||||
for (let pref of DEPRECATED_FHR_PREFS) {
|
||||
Preferences.reset(pref);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Make sure the user is notified about the policy before allowing upload.
|
||||
* @return {Boolean} True if the user was notified, false otherwise.
|
||||
*/
|
||||
_ensureUserNotified: function() {
|
||||
const BYPASS_NOTIFICATION = Preferences.get(PREF_BYPASS_NOTIFICATION, false);
|
||||
if (this.isUserNotifiedOfCurrentPolicy || BYPASS_NOTIFICATION) {
|
||||
return true;
|
||||
}
|
||||
|
||||
this._log.trace("ensureUserNotified - User not notified, notifying now.");
|
||||
if (this._notificationInProgress) {
|
||||
this._log.trace("ensureUserNotified - User not notified, notification in progress.");
|
||||
return false;
|
||||
}
|
||||
|
||||
this._notificationInProgress = true;
|
||||
let request = new NotifyPolicyRequest(this._log);
|
||||
Observers.notify("datareporting:notify-data-policy:request", request);
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
/**
|
||||
* Called when the user is notified with the infobar.
|
||||
*/
|
||||
_infobarShownCallback: function() {
|
||||
this._log.trace("_infobarShownCallback");
|
||||
this._recordNotificationData();
|
||||
TelemetrySend.notifyCanUpload();
|
||||
},
|
||||
|
||||
/**
|
||||
* Record date and the version of the accepted policy.
|
||||
*/
|
||||
_recordNotificationData: function() {
|
||||
this._log.trace("_recordNotificationData");
|
||||
this.dataSubmissionPolicyNotifiedDate = Policy.now();
|
||||
this.dataSubmissionPolicyAcceptedVersion = this.currentPolicyVersion;
|
||||
// The user was notified and the notification data saved: the notification
|
||||
// is no longer in progress.
|
||||
this._notificationInProgress = false;
|
||||
},
|
||||
|
||||
observe: function(aSubject, aTopic, aData) {
|
||||
if (aTopic != "sessionstore-windows-restored") {
|
||||
return;
|
||||
}
|
||||
|
||||
const isFirstRun = Preferences.get(PREF_FIRST_RUN, true);
|
||||
const delay =
|
||||
isFirstRun ? NOTIFICATION_DELAY_FIRST_RUN_MSEC: NOTIFICATION_DELAY_NEXT_RUNS_MSEC;
|
||||
|
||||
this._startupNotificationTimerId = Policy.setShowInfobarTimeout(
|
||||
// Calling |canUpload| eventually shows the infobar, if needed.
|
||||
() => this.canUpload(), delay);
|
||||
// We performed at least a run, flip the firstRun preference.
|
||||
Preferences.set(PREF_FIRST_RUN, false);
|
||||
},
|
||||
};
|
|
@ -30,6 +30,8 @@ XPCOMUtils.defineLazyModuleGetter(this, "AsyncShutdown",
|
|||
"resource://gre/modules/AsyncShutdown.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "TelemetryStorage",
|
||||
"resource://gre/modules/TelemetryStorage.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "TelemetryReportingPolicy",
|
||||
"resource://gre/modules/TelemetryReportingPolicy.jsm");
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "Telemetry",
|
||||
"@mozilla.org/base/telemetry;1",
|
||||
"nsITelemetry");
|
||||
|
@ -198,6 +200,13 @@ this.TelemetrySend = {
|
|||
return TelemetrySendImpl.overduePingsCount;
|
||||
},
|
||||
|
||||
/**
|
||||
* Notify that we can start submitting data to the servers.
|
||||
*/
|
||||
notifyCanUpload: function() {
|
||||
return TelemetrySendImpl.notifyCanUpload();
|
||||
},
|
||||
|
||||
/**
|
||||
* Only used in tests. Used to reset the module data to emulate a restart.
|
||||
*/
|
||||
|
@ -382,8 +391,8 @@ let SendScheduler = {
|
|||
let pending = TelemetryStorage.getPendingPingList();
|
||||
let current = TelemetrySendImpl.getUnpersistedPings();
|
||||
this._log.trace("_doSendTask - pending: " + pending.length + ", current: " + current.length);
|
||||
pending = pending.filter(p => TelemetrySendImpl.canSend(p));
|
||||
current = current.filter(p => TelemetrySendImpl.canSend(p));
|
||||
pending = pending.filter(p => TelemetrySendImpl.sendingEnabled(p));
|
||||
current = current.filter(p => TelemetrySendImpl.sendingEnabled(p));
|
||||
this._log.trace("_doSendTask - can send - pending: " + pending.length + ", current: " + current.length);
|
||||
|
||||
// Bail out if there is nothing to send.
|
||||
|
@ -632,6 +641,15 @@ let TelemetrySendImpl = {
|
|||
return SendScheduler.reset();
|
||||
},
|
||||
|
||||
/**
|
||||
* Notify that we can start submitting data to the servers.
|
||||
*/
|
||||
notifyCanUpload: function() {
|
||||
// Let the scheduler trigger sending pings if possible.
|
||||
SendScheduler.triggerSendingPings(true);
|
||||
return this.promisePendingPingActivity();
|
||||
},
|
||||
|
||||
observe: function(subject, topic, data) {
|
||||
switch(topic) {
|
||||
case TOPIC_IDLE_DAILY:
|
||||
|
@ -643,15 +661,15 @@ let TelemetrySendImpl = {
|
|||
submitPing: function(ping) {
|
||||
this._log.trace("submitPing - ping id: " + ping.id);
|
||||
|
||||
if (!this.canSend(ping)) {
|
||||
if (!this.sendingEnabled(ping)) {
|
||||
this._log.trace("submitPing - Telemetry is not allowed to send pings.");
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
if (!this._sendingEnabled) {
|
||||
if (!this.canSendNow) {
|
||||
// Sending is disabled or throttled, add this to the persisted pending pings.
|
||||
this._log.trace("submitPing - can't send ping now, persisting to disk - " +
|
||||
"sendingEnabled: " + this._sendingEnabled);
|
||||
"canSendNow: " + this.canSendNow);
|
||||
return TelemetryStorage.savePendingPing(ping);
|
||||
}
|
||||
|
||||
|
@ -806,7 +824,7 @@ let TelemetrySendImpl = {
|
|||
},
|
||||
|
||||
_doPing: function(ping, id, isPersisted) {
|
||||
if (!this.canSend(ping)) {
|
||||
if (!this.sendingEnabled(ping)) {
|
||||
// We can't send the pings to the server, so don't try to.
|
||||
this._log.trace("_doPing - Can't send ping " + ping.id);
|
||||
return Promise.resolve();
|
||||
|
@ -910,7 +928,21 @@ let TelemetrySendImpl = {
|
|||
},
|
||||
|
||||
/**
|
||||
* Check if pings can be sent to the server. If FHR is not allowed to upload,
|
||||
* Check if sending is temporarily disabled.
|
||||
* @return {Boolean} True if we can send pings to the server right now, false if
|
||||
* sending is temporarily disabled.
|
||||
*/
|
||||
get canSendNow() {
|
||||
// If the reporting policy was not accepted yet, don't send pings.
|
||||
if (!TelemetryReportingPolicy.canUpload()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return this._sendingEnabled;
|
||||
},
|
||||
|
||||
/**
|
||||
* Check if sending is disabled. If FHR is not allowed to upload,
|
||||
* pings are not sent to the server (Telemetry is a sub-feature of FHR). If trying
|
||||
* to send a deletion ping, don't block it.
|
||||
* If unified telemetry is off, don't send pings if Telemetry is disabled.
|
||||
|
@ -918,7 +950,7 @@ let TelemetrySendImpl = {
|
|||
* @param {Object} [ping=null] A ping to be checked.
|
||||
* @return {Boolean} True if pings can be send to the servers, false otherwise.
|
||||
*/
|
||||
canSend: function(ping = null) {
|
||||
sendingEnabled: function(ping = null) {
|
||||
// We only send pings from official builds, but allow overriding this for tests.
|
||||
if (!Telemetry.isOfficialTelemetry && !this._testMode) {
|
||||
return false;
|
||||
|
|
|
@ -1614,40 +1614,42 @@ let Impl = {
|
|||
/**
|
||||
* Save both the "saved-session" and the "shutdown" pings to disk.
|
||||
*/
|
||||
saveShutdownPings: Task.async(function*() {
|
||||
saveShutdownPings: function() {
|
||||
this._log.trace("saveShutdownPings");
|
||||
|
||||
if (IS_UNIFIED_TELEMETRY) {
|
||||
try {
|
||||
let shutdownPayload = this.getSessionPayload(REASON_SHUTDOWN, false);
|
||||
// We don't wait for "shutdown" pings to be written to disk before gathering the
|
||||
// "saved-session" payload. Instead we append the promises to this list and wait
|
||||
// on both to be saved after kicking off their collection.
|
||||
let p = [];
|
||||
|
||||
let options = {
|
||||
addClientId: true,
|
||||
addEnvironment: true,
|
||||
overwrite: true,
|
||||
};
|
||||
yield TelemetryController.addPendingPing(getPingType(shutdownPayload), shutdownPayload, options);
|
||||
} catch (ex) {
|
||||
this._log.error("saveShutdownPings - failed to submit shutdown ping", ex);
|
||||
}
|
||||
if (IS_UNIFIED_TELEMETRY) {
|
||||
let shutdownPayload = this.getSessionPayload(REASON_SHUTDOWN, false);
|
||||
|
||||
let options = {
|
||||
addClientId: true,
|
||||
addEnvironment: true,
|
||||
overwrite: true,
|
||||
};
|
||||
p.push(TelemetryController.addPendingPing(getPingType(shutdownPayload), shutdownPayload, options)
|
||||
.catch(e => this._log.error("saveShutdownPings - failed to submit shutdown ping", e)));
|
||||
}
|
||||
|
||||
// As a temporary measure, we want to submit saved-session too if extended Telemetry is enabled
|
||||
// to keep existing performance analysis working.
|
||||
if (Telemetry.canRecordExtended) {
|
||||
try {
|
||||
let payload = this.getSessionPayload(REASON_SAVED_SESSION, false);
|
||||
let payload = this.getSessionPayload(REASON_SAVED_SESSION, false);
|
||||
|
||||
let options = {
|
||||
addClientId: true,
|
||||
addEnvironment: true,
|
||||
};
|
||||
yield TelemetryController.addPendingPing(getPingType(payload), payload, options);
|
||||
} catch (ex) {
|
||||
this._log.error("saveShutdownPings - failed to submit saved-session ping", ex);
|
||||
}
|
||||
let options = {
|
||||
addClientId: true,
|
||||
addEnvironment: true,
|
||||
};
|
||||
p.push(TelemetryController.addPendingPing(getPingType(payload), payload, options)
|
||||
.catch (e => this._log.error("saveShutdownPings - failed to submit saved-session ping", e)));
|
||||
}
|
||||
}),
|
||||
|
||||
// Wait on pings to be saved.
|
||||
return Promise.all(p);
|
||||
},
|
||||
|
||||
|
||||
testSavePendingPing: function testSaveHistograms() {
|
||||
|
|
|
@ -44,3 +44,38 @@ Sending only happens on official builds with ``MOZ_TELEMETRY_REPORTING`` defined
|
|||
``toolkit.telemetry.log.dump``
|
||||
|
||||
Sets whether to dump Telemetry log messages to ``stdout`` too.
|
||||
|
||||
Data-choices notification
|
||||
-------------------------
|
||||
|
||||
``toolkit.telemetry.reportingpolicy.firstRun``
|
||||
|
||||
This preference is not present until the first run. After, its value is set to false. This is used to show the infobar with a more aggressive timeout if it wasn't shown yet.
|
||||
|
||||
``datareporting.policy.dataSubmissionEnabled``
|
||||
|
||||
This is the data submission master kill switch. If disabled, no policy is shown or upload takes place, ever.
|
||||
|
||||
``datareporting.policy.dataSubmissionPolicyNotifiedTime``
|
||||
|
||||
Records the date user was shown the policy. This preference is also used on Android.
|
||||
|
||||
``datareporting.policy.dataSubmissionPolicyAcceptedVersion``
|
||||
|
||||
Records the version of the policy notified to the user. This preference is also used on Android.
|
||||
|
||||
``datareporting.policy.dataSubmissionPolicyBypassNotification``
|
||||
|
||||
Used in tests, it allows to skip the notification check.
|
||||
|
||||
``datareporting.policy.currentPolicyVersion``
|
||||
|
||||
Stores the current policy version, overrides the default value defined in TelemetryReportingPolicy.jsm.
|
||||
|
||||
``datareporting.policy.minimumPolicyVersion``
|
||||
|
||||
The minimum policy version that is accepted for the current policy. This can be set per channel.
|
||||
|
||||
``datareporting.policy.minimumPolicyVersion.channel-NAME``
|
||||
|
||||
This is the only channel-specific version that we currently use for the minimum policy version.
|
||||
|
|
|
@ -32,6 +32,7 @@ EXTRA_COMPONENTS += [
|
|||
EXTRA_JS_MODULES += [
|
||||
'TelemetryArchive.jsm',
|
||||
'TelemetryLog.jsm',
|
||||
'TelemetryReportingPolicy.jsm',
|
||||
'TelemetrySend.jsm',
|
||||
'TelemetryStopwatch.jsm',
|
||||
'TelemetryStorage.jsm',
|
||||
|
|
|
@ -238,6 +238,7 @@ function fakeNow(...args) {
|
|||
Cu.import("resource://gre/modules/TelemetryController.jsm"),
|
||||
Cu.import("resource://gre/modules/TelemetryStorage.jsm"),
|
||||
Cu.import("resource://gre/modules/TelemetrySend.jsm"),
|
||||
Cu.import("resource://gre/modules/TelemetryReportingPolicy.jsm"),
|
||||
];
|
||||
|
||||
for (let m of modules) {
|
||||
|
@ -285,6 +286,8 @@ if (runningInParent) {
|
|||
Services.prefs.setCharPref("toolkit.telemetry.log.level", "Trace");
|
||||
// Telemetry archiving should be on.
|
||||
Services.prefs.setBoolPref("toolkit.telemetry.archive.enabled", true);
|
||||
// Telemetry xpcshell tests cannot show the infobar.
|
||||
Services.prefs.setBoolPref("datareporting.policy.dataSubmissionPolicyBypassNotification", true);
|
||||
|
||||
fakePingSendTimer((callback, timeout) => {
|
||||
Services.tm.mainThread.dispatch(() => callback(), Ci.nsIThread.DISPATCH_NORMAL);
|
||||
|
|
|
@ -0,0 +1,255 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// Test that TelemetryController sends close to shutdown don't lead
|
||||
// to AsyncShutdown timeouts.
|
||||
|
||||
"use strict";
|
||||
|
||||
Cu.import("resource://gre/modules/Preferences.jsm", this);
|
||||
Cu.import("resource://gre/modules/Services.jsm", this);
|
||||
Cu.import("resource://gre/modules/TelemetryController.jsm", this);
|
||||
Cu.import("resource://gre/modules/TelemetrySend.jsm", this);
|
||||
Cu.import("resource://gre/modules/TelemetryReportingPolicy.jsm", this);
|
||||
Cu.import("resource://gre/modules/TelemetryUtils.jsm", this);
|
||||
Cu.import("resource://gre/modules/Timer.jsm", this);
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm", this);
|
||||
|
||||
const PREF_BRANCH = "toolkit.telemetry.";
|
||||
const PREF_ENABLED = PREF_BRANCH + "enabled";
|
||||
const PREF_SERVER = PREF_BRANCH + "server";
|
||||
const PREF_DRS_ENABLED = "datareporting.healthreport.service.enabled";
|
||||
|
||||
const TEST_CHANNEL = "TestChannelABC";
|
||||
|
||||
const PREF_POLICY_BRANCH = "datareporting.policy.";
|
||||
const PREF_BYPASS_NOTIFICATION = PREF_POLICY_BRANCH + "dataSubmissionPolicyBypassNotification";
|
||||
const PREF_DATA_SUBMISSION_ENABLED = PREF_POLICY_BRANCH + "dataSubmissionEnabled";
|
||||
const PREF_CURRENT_POLICY_VERSION = PREF_POLICY_BRANCH + "currentPolicyVersion";
|
||||
const PREF_MINIMUM_POLICY_VERSION = PREF_POLICY_BRANCH + "minimumPolicyVersion";
|
||||
const PREF_MINIMUM_CHANNEL_POLICY_VERSION = PREF_MINIMUM_POLICY_VERSION + ".channel-" + TEST_CHANNEL;
|
||||
const PREF_ACCEPTED_POLICY_VERSION = PREF_POLICY_BRANCH + "dataSubmissionPolicyAcceptedVersion";
|
||||
const PREF_ACCEPTED_POLICY_DATE = PREF_POLICY_BRANCH + "dataSubmissionPolicyNotifiedTime";
|
||||
|
||||
function fakeShowPolicyTimeout(set, clear) {
|
||||
let reportingPolicy = Cu.import("resource://gre/modules/TelemetryReportingPolicy.jsm");
|
||||
reportingPolicy.Policy.setShowInfobarTimeout = set;
|
||||
reportingPolicy.Policy.clearShowInfobarTimeout = clear;
|
||||
}
|
||||
|
||||
function fakeResetAcceptedPolicy() {
|
||||
Preferences.reset(PREF_ACCEPTED_POLICY_DATE);
|
||||
Preferences.reset(PREF_ACCEPTED_POLICY_VERSION);
|
||||
}
|
||||
|
||||
function run_test() {
|
||||
// Addon manager needs a profile directory
|
||||
do_get_profile(true);
|
||||
loadAddonManager("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
|
||||
|
||||
Services.prefs.setBoolPref(PREF_ENABLED, true);
|
||||
// We need to disable FHR in order to use the policy from telemetry.
|
||||
Services.prefs.setBoolPref(PREF_DRS_ENABLED, false);
|
||||
// Don't bypass the notifications in this test, we'll fake it.
|
||||
Services.prefs.setBoolPref(PREF_BYPASS_NOTIFICATION, false);
|
||||
|
||||
TelemetryReportingPolicy.setup();
|
||||
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_task(function* test_firstRun() {
|
||||
const PREF_FIRST_RUN = "toolkit.telemetry.reportingpolicy.firstRun";
|
||||
const FIRST_RUN_TIMEOUT_MSEC = 60 * 1000; // 60s
|
||||
const OTHER_RUNS_TIMEOUT_MSEC = 10 * 1000; // 10s
|
||||
|
||||
Preferences.reset(PREF_FIRST_RUN);
|
||||
|
||||
let startupTimeout = 0;
|
||||
fakeShowPolicyTimeout((callback, timeout) => startupTimeout = timeout, () => {});
|
||||
TelemetryReportingPolicy.reset();
|
||||
|
||||
Services.obs.notifyObservers(null, "sessionstore-windows-restored", null);
|
||||
Assert.equal(startupTimeout, FIRST_RUN_TIMEOUT_MSEC,
|
||||
"The infobar display timeout should be 60s on the first run.");
|
||||
|
||||
// Run again, and check that we actually wait only 10 seconds.
|
||||
TelemetryReportingPolicy.reset();
|
||||
Services.obs.notifyObservers(null, "sessionstore-windows-restored", null);
|
||||
Assert.equal(startupTimeout, OTHER_RUNS_TIMEOUT_MSEC,
|
||||
"The infobar display timeout should be 10s on other runs.");
|
||||
});
|
||||
|
||||
add_task(function* test_prefs() {
|
||||
TelemetryReportingPolicy.reset();
|
||||
|
||||
let now = fakeNow(2009, 11, 18);
|
||||
|
||||
// If the date is not valid (earlier than 2012), we don't regard the policy as accepted.
|
||||
TelemetryReportingPolicy.testInfobarShown();
|
||||
Assert.ok(!TelemetryReportingPolicy.testIsUserNotified());
|
||||
Assert.equal(Preferences.get(PREF_ACCEPTED_POLICY_DATE, null), 0,
|
||||
"Invalid dates should not make the policy accepted.");
|
||||
|
||||
// Check that the notification date and version are correctly saved to the prefs.
|
||||
now = fakeNow(2012, 11, 18);
|
||||
TelemetryReportingPolicy.testInfobarShown();
|
||||
Assert.equal(Preferences.get(PREF_ACCEPTED_POLICY_DATE, null), now.getTime(),
|
||||
"A valid date must correctly be saved.");
|
||||
|
||||
// Now that user is notified, check if we are allowed to upload.
|
||||
Assert.ok(TelemetryReportingPolicy.canUpload(),
|
||||
"We must be able to upload after the policy is accepted.");
|
||||
|
||||
// Disable submission and check that we're no longer allowed to upload.
|
||||
Preferences.set(PREF_DATA_SUBMISSION_ENABLED, false);
|
||||
Assert.ok(!TelemetryReportingPolicy.canUpload(),
|
||||
"We must not be able to upload if data submission is disabled.");
|
||||
|
||||
// Turn the submission back on.
|
||||
Preferences.set(PREF_DATA_SUBMISSION_ENABLED, true);
|
||||
Assert.ok(TelemetryReportingPolicy.canUpload(),
|
||||
"We must be able to upload if data submission is enabled and the policy was accepted.");
|
||||
|
||||
// Set a new minimum policy version and check that user is no longer notified.
|
||||
let newMinimum = Preferences.get(PREF_CURRENT_POLICY_VERSION, 1) + 1;
|
||||
Preferences.set(PREF_MINIMUM_POLICY_VERSION, newMinimum);
|
||||
Assert.ok(!TelemetryReportingPolicy.testIsUserNotified(),
|
||||
"A greater minimum policy version must invalidate the policy and disable upload.");
|
||||
|
||||
// Eventually accept the policy and make sure user is notified.
|
||||
Preferences.set(PREF_CURRENT_POLICY_VERSION, newMinimum);
|
||||
TelemetryReportingPolicy.testInfobarShown();
|
||||
Assert.ok(TelemetryReportingPolicy.testIsUserNotified(),
|
||||
"Accepting the policy again should show the user as notified.");
|
||||
Assert.ok(TelemetryReportingPolicy.canUpload(),
|
||||
"Accepting the policy again should let us upload data.");
|
||||
|
||||
// Set a new, per channel, minimum policy version. Start by setting a test current channel.
|
||||
let defaultPrefs = new Preferences({ defaultBranch: true });
|
||||
defaultPrefs.set("app.update.channel", TEST_CHANNEL);
|
||||
|
||||
// Increase and set the new minimum version, then check that we're not notified anymore.
|
||||
newMinimum++;
|
||||
Preferences.set(PREF_MINIMUM_CHANNEL_POLICY_VERSION, newMinimum);
|
||||
Assert.ok(!TelemetryReportingPolicy.testIsUserNotified(),
|
||||
"Increasing the minimum policy version should invalidate the policy.");
|
||||
|
||||
// Eventually accept the policy and make sure user is notified.
|
||||
Preferences.set(PREF_CURRENT_POLICY_VERSION, newMinimum);
|
||||
TelemetryReportingPolicy.testInfobarShown();
|
||||
Assert.ok(TelemetryReportingPolicy.testIsUserNotified(),
|
||||
"Accepting the policy again should show the user as notified.");
|
||||
Assert.ok(TelemetryReportingPolicy.canUpload(),
|
||||
"Accepting the policy again should let us upload data.");
|
||||
});
|
||||
|
||||
add_task(function* test_migratePrefs() {
|
||||
const DEPRECATED_FHR_PREFS = {
|
||||
"datareporting.policy.dataSubmissionPolicyAccepted": true,
|
||||
"datareporting.policy.dataSubmissionPolicyBypassAcceptance": true,
|
||||
"datareporting.policy.dataSubmissionPolicyResponseType": "foxyeah",
|
||||
"datareporting.policy.dataSubmissionPolicyResponseTime": Date.now().toString(),
|
||||
};
|
||||
|
||||
// Make sure the preferences are set before setting up the policy.
|
||||
for (let name in DEPRECATED_FHR_PREFS) {
|
||||
Preferences.set(name, DEPRECATED_FHR_PREFS[name]);
|
||||
}
|
||||
// Set up the policy.
|
||||
TelemetryReportingPolicy.reset();
|
||||
// They should have been removed by now.
|
||||
for (let name in DEPRECATED_FHR_PREFS) {
|
||||
Assert.ok(!Preferences.has(name), name + " should have been removed.");
|
||||
}
|
||||
});
|
||||
|
||||
add_task(function* test_userNotifiedOfCurrentPolicy() {
|
||||
fakeResetAcceptedPolicy();
|
||||
TelemetryReportingPolicy.reset();
|
||||
|
||||
// User should be reported as not notified by default.
|
||||
Assert.ok(!TelemetryReportingPolicy.testIsUserNotified(),
|
||||
"The initial state should be unnotified.");
|
||||
|
||||
// Forcing a policy version should not automatically make the user notified.
|
||||
Preferences.set(PREF_ACCEPTED_POLICY_VERSION,
|
||||
TelemetryReportingPolicy.DEFAULT_DATAREPORTING_POLICY_VERSION);
|
||||
Assert.ok(!TelemetryReportingPolicy.testIsUserNotified(),
|
||||
"The default state of the date should have a time of 0 and it should therefore fail");
|
||||
|
||||
// Showing the notification bar should make the user notified.
|
||||
let now = fakeNow(2012, 11, 11);
|
||||
TelemetryReportingPolicy.testInfobarShown();
|
||||
Assert.ok(TelemetryReportingPolicy.testIsUserNotified(),
|
||||
"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.
|
||||
let newVersion =
|
||||
Preferences.get(PREF_CURRENT_POLICY_VERSION, 1) + 1;
|
||||
Preferences.set(PREF_ACCEPTED_POLICY_VERSION, newVersion);
|
||||
Assert.ok(TelemetryReportingPolicy.testIsUserNotified(),
|
||||
"A future version of the policy should pass.");
|
||||
|
||||
newVersion =
|
||||
Preferences.get(PREF_CURRENT_POLICY_VERSION, 1) - 1;
|
||||
Preferences.set(PREF_ACCEPTED_POLICY_VERSION, newVersion);
|
||||
Assert.ok(!TelemetryReportingPolicy.testIsUserNotified(),
|
||||
"A previous version of the policy should fail.");
|
||||
});
|
||||
|
||||
add_task(function* test_canSend() {
|
||||
const TEST_PING_TYPE = "test-ping";
|
||||
|
||||
PingServer.start();
|
||||
Preferences.set(PREF_SERVER, "http://localhost:" + PingServer.port);
|
||||
|
||||
yield TelemetryController.reset();
|
||||
TelemetryReportingPolicy.reset();
|
||||
|
||||
// User should be reported as not notified by default.
|
||||
Assert.ok(!TelemetryReportingPolicy.testIsUserNotified(),
|
||||
"The initial state should be unnotified.");
|
||||
|
||||
// Assert if we receive any ping before the policy is accepted.
|
||||
PingServer.registerPingHandler(() => Assert.ok(false, "Should not have received any pings now"));
|
||||
yield TelemetryController.submitExternalPing(TEST_PING_TYPE, {});
|
||||
|
||||
// Reset the ping handler.
|
||||
PingServer.resetPingHandler();
|
||||
|
||||
// Fake the infobar: this should also trigger the ping send task.
|
||||
TelemetryReportingPolicy.testInfobarShown();
|
||||
let ping = yield PingServer.promiseNextPings(1);
|
||||
Assert.equal(ping.length, 1, "We should have received one ping.");
|
||||
Assert.equal(ping[0].type, TEST_PING_TYPE,
|
||||
"We should have received the previous ping.");
|
||||
|
||||
// Submit another ping, to make sure it gets sent.
|
||||
yield TelemetryController.submitExternalPing(TEST_PING_TYPE, {});
|
||||
|
||||
// Get the ping and check its type.
|
||||
ping = yield PingServer.promiseNextPings(1);
|
||||
Assert.equal(ping.length, 1, "We should have received one ping.");
|
||||
Assert.equal(ping[0].type, TEST_PING_TYPE, "We should have received the new ping.");
|
||||
|
||||
// Fake a restart with a pending ping.
|
||||
yield TelemetryController.addPendingPing(TEST_PING_TYPE, {});
|
||||
yield TelemetryController.reset();
|
||||
|
||||
// We should be immediately sending the ping out.
|
||||
ping = yield PingServer.promiseNextPings(1);
|
||||
Assert.equal(ping.length, 1, "We should have received one ping.");
|
||||
Assert.equal(ping[0].type, TEST_PING_TYPE, "We should have received the pending ping.");
|
||||
|
||||
// Submit another ping, to make sure it gets sent.
|
||||
yield TelemetryController.submitExternalPing(TEST_PING_TYPE, {});
|
||||
|
||||
// Get the ping and check its type.
|
||||
ping = yield PingServer.promiseNextPings(1);
|
||||
Assert.equal(ping.length, 1, "We should have received one ping.");
|
||||
Assert.equal(ping[0].type, TEST_PING_TYPE, "We should have received the new ping.");
|
||||
|
||||
yield PingServer.stop();
|
||||
});
|
|
@ -53,3 +53,4 @@ run-sequentially = Bug 1046307, test can fail intermittently when CPU load is hi
|
|||
[test_TelemetrySend.js]
|
||||
[test_ChildHistograms.js]
|
||||
skip-if = os == "android"
|
||||
[test_TelemetryReportingPolicy.js]
|
||||
|
|