зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1357020 - Should hide the onboarding tour if user explicitly checked the hide-the-tour checkbox, r=gasolin,mossop,rexboy
This patch - adds one hide-onboarding-tour checkbox - after toggling the overlay, hides the onboarding tour if user checked hide-the-tour checkbox - creates the message channel between the chrome process and the content process to set prefs. - listens to the pref-updated event and then hide the onboarding tour across pages. - Add one browser_onboarding_hide_tours.js test MozReview-Commit-ID: 7ZjbrhfO9dB --HG-- extra : rebase_source : 5c59527ff7cb16996539a4eec49b47a9decafb3a
This commit is contained in:
Родитель
0d02dac163
Коммит
a228efff93
|
@ -1695,6 +1695,11 @@ pref("browser.suppress_first_window_animation", true);
|
|||
|
||||
// Preferences for Photon onboarding system extension
|
||||
pref("browser.onboarding.enabled", true);
|
||||
pref("browser.onboarding.hidden", false);
|
||||
// On the Activity-Stream page, the snippet's position overlaps with our notification.
|
||||
// So use `browser.onboarding.notification.finished` to let the AS page know
|
||||
// if our notification is finished and safe to show their snippet.
|
||||
pref("browser.onboarding.notification.finished", false);
|
||||
|
||||
// Preferences for the Screenshots feature:
|
||||
// Temporarily disable Screenshots in Beta & Release, so that we can gradually
|
||||
|
|
|
@ -6,6 +6,41 @@
|
|||
|
||||
const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/Preferences.jsm");
|
||||
|
||||
const PREF_WHITELIST = [
|
||||
"browser.onboarding.enabled",
|
||||
"browser.onboarding.hidden",
|
||||
"browser.onboarding.notification.finished"
|
||||
];
|
||||
|
||||
/**
|
||||
* Set pref. Why no `getPrefs` function is due to the priviledge level.
|
||||
* We cannot set prefs inside a framescript but can read.
|
||||
* For simplicity and effeciency, we still read prefs inside the framescript.
|
||||
*
|
||||
* @param {Array} prefs the array of prefs to set.
|
||||
* The array element carrys info to set pref, should contain
|
||||
* - {String} name the pref name, such as `browser.onboarding.hidden`
|
||||
* - {*} value the value to set
|
||||
**/
|
||||
function setPrefs(prefs) {
|
||||
prefs.forEach(pref => {
|
||||
if (PREF_WHITELIST.includes(pref.name)) {
|
||||
Preferences.set(pref.name, pref.value);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function initContentMessageListener() {
|
||||
Services.mm.addMessageListener("Onboarding:OnContentMessage", msg => {
|
||||
switch (msg.data.action) {
|
||||
case "set-prefs":
|
||||
setPrefs(msg.data.params);
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function install(aData, aReason) {}
|
||||
|
||||
|
@ -13,6 +48,7 @@ function uninstall(aData, aReason) {}
|
|||
|
||||
function startup(aData, reason) {
|
||||
Services.mm.loadFrameScript("resource://onboarding/onboarding.js", true);
|
||||
initContentMessageListener();
|
||||
}
|
||||
|
||||
function shutdown(aData, reason) {}
|
||||
|
|
|
@ -96,6 +96,12 @@
|
|||
#onboarding-overlay-dialog > footer {
|
||||
grid-row: footer-start;
|
||||
grid-column: dialog-start / tour-end;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
#onboarding-tour-hidden-checkbox {
|
||||
margin-inline-start: 27px;
|
||||
margin-inline-end: 10px;
|
||||
}
|
||||
|
||||
/* Onboarding tour list */
|
||||
|
|
|
@ -2,12 +2,13 @@
|
|||
* 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/. */
|
||||
|
||||
/* global content */
|
||||
/* eslint-env mozilla/frame-script */
|
||||
|
||||
"use strict";
|
||||
|
||||
const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/Preferences.jsm");
|
||||
|
||||
const ONBOARDING_CSS_URL = "resource://onboarding/onboarding.css";
|
||||
const ABOUT_HOME_URL = "about:home";
|
||||
|
@ -125,6 +126,43 @@ class Onboarding {
|
|||
// Destroy on unload. This is to ensure we remove all the stuff we left.
|
||||
// No any leak out there.
|
||||
this._window.addEventListener("unload", () => this.destroy());
|
||||
|
||||
this._initPrefObserver();
|
||||
}
|
||||
|
||||
_initPrefObserver() {
|
||||
if (this._prefsObserved) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._prefsObserved = new Map();
|
||||
this._prefsObserved.set("browser.onboarding.hidden", prefValue => {
|
||||
if (prefValue) {
|
||||
this.destroy();
|
||||
}
|
||||
});
|
||||
for (let [name, callback] of this._prefsObserved) {
|
||||
Preferences.observe(name, callback);
|
||||
}
|
||||
}
|
||||
|
||||
_clearPrefObserver() {
|
||||
if (this._prefsObserved) {
|
||||
for (let [name, callback] of this._prefsObserved) {
|
||||
Preferences.ignore(name, callback);
|
||||
}
|
||||
this._prefsObserved = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {String} action the action to ask the chrome to do
|
||||
* @param {Array} params the parameters for the action
|
||||
*/
|
||||
sendMessageToChrome(action, params) {
|
||||
sendAsyncMessage("Onboarding:OnContentMessage", {
|
||||
action, params
|
||||
});
|
||||
}
|
||||
|
||||
handleEvent(evt) {
|
||||
|
@ -144,6 +182,7 @@ class Onboarding {
|
|||
}
|
||||
|
||||
destroy() {
|
||||
this._clearPrefObserver();
|
||||
this._overlayIcon.remove();
|
||||
this._overlay.remove();
|
||||
}
|
||||
|
@ -154,6 +193,13 @@ class Onboarding {
|
|||
this._loadTours(onboardingTours);
|
||||
}
|
||||
|
||||
this._overlay.classList.toggle("opened");
|
||||
let hiddenCheckbox = this._window.document.getElementById("onboarding-tour-hidden-checkbox");
|
||||
if (hiddenCheckbox.checked) {
|
||||
this.hide();
|
||||
return;
|
||||
}
|
||||
|
||||
this._overlay.classList.toggle("onboarding-opened");
|
||||
}
|
||||
|
||||
|
@ -171,6 +217,19 @@ class Onboarding {
|
|||
}
|
||||
}
|
||||
|
||||
hide() {
|
||||
this.sendMessageToChrome("set-prefs", [
|
||||
{
|
||||
name: "browser.onboarding.hidden",
|
||||
value: true
|
||||
},
|
||||
{
|
||||
name: "browser.onboarding.notification.finished",
|
||||
value: true
|
||||
}
|
||||
]);
|
||||
}
|
||||
|
||||
_renderOverlay() {
|
||||
let div = this._window.document.createElement("div");
|
||||
div.id = "onboarding-overlay";
|
||||
|
@ -185,10 +244,13 @@ class Onboarding {
|
|||
<ul id="onboarding-tour-list"></ul>
|
||||
</nav>
|
||||
<footer id="onboarding-footer">
|
||||
<input type="checkbox" id="onboarding-tour-hidden-checkbox" /><label for="onboarding-tour-hidden-checkbox"></label>
|
||||
</footer>
|
||||
</div>
|
||||
`;
|
||||
|
||||
div.querySelector("label[for='onboarding-tour-hidden-checkbox']").textContent =
|
||||
this._bundle.GetStringFromName("onboarding.hidden-checkbox-label");
|
||||
div.querySelector("#onboarding-header").textContent =
|
||||
this._bundle.formatStringFromName("onboarding.overlay-title", [BRAND_SHORT_NAME], 1);
|
||||
return div;
|
||||
|
@ -264,20 +326,22 @@ class Onboarding {
|
|||
}
|
||||
}
|
||||
|
||||
addEventListener("load", function onLoad(evt) {
|
||||
if (!content || evt.target != content.document) {
|
||||
return;
|
||||
}
|
||||
removeEventListener("load", onLoad);
|
||||
// Load onboarding module only when we enable it.
|
||||
if (Services.prefs.getBoolPref("browser.onboarding.enabled", false) &&
|
||||
!Services.prefs.getBoolPref("browser.onboarding.hidden", false)) {
|
||||
|
||||
let window = evt.target.defaultView;
|
||||
// Load onboarding module only when we enable it.
|
||||
if ((window.location.href == ABOUT_NEWTAB_URL ||
|
||||
window.location.href == ABOUT_HOME_URL) &&
|
||||
Services.prefs.getBoolPref("browser.onboarding.enabled", false)) {
|
||||
addEventListener("load", function onLoad(evt) {
|
||||
if (!content || evt.target != content.document) {
|
||||
return;
|
||||
}
|
||||
removeEventListener("load", onLoad);
|
||||
|
||||
window.requestIdleCallback(() => {
|
||||
new Onboarding(window);
|
||||
});
|
||||
}
|
||||
}, true);
|
||||
let window = evt.target.defaultView;
|
||||
let location = window.location.href;
|
||||
if (location == ABOUT_NEWTAB_URL || location == ABOUT_HOME_URL) {
|
||||
window.requestIdleCallback(() => {
|
||||
new Onboarding(window);
|
||||
});
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
|
|
|
@ -21,3 +21,4 @@ onboarding.tour-private-browsing.title=A little privacy goes a long way.
|
|||
# brandShortName.
|
||||
onboarding.tour-private-browsing.description=Browse the internet without saving your searches or the sites you visited. When your session ends, the cookies disappear from %S like they were never there.
|
||||
onboarding.tour-private-browsing.button=Show Private Browsing in Menu
|
||||
onboarding.hidden-checkbox-label=Hide the tour
|
||||
|
|
|
@ -17,4 +17,6 @@ FINAL_TARGET_FILES.features['onboarding@mozilla.org'] += [
|
|||
'bootstrap.js',
|
||||
]
|
||||
|
||||
BROWSER_CHROME_MANIFESTS += ['test/browser/browser.ini']
|
||||
|
||||
JAR_MANIFESTS += ['jar.mn']
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
"use strict";
|
||||
|
||||
module.exports = {
|
||||
"extends": [
|
||||
"plugin:mozilla/browser-test"
|
||||
],
|
||||
};
|
|
@ -0,0 +1,5 @@
|
|||
[DEFAULT]
|
||||
support-files =
|
||||
head.js
|
||||
|
||||
[browser_onboarding_hide_tours.js]
|
|
@ -0,0 +1,47 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
const ABOUT_HOME_URL = "about:home";
|
||||
const ABOUT_NEWTAB_URL = "about:newtab";
|
||||
|
||||
function assertOnboardingDestroyed(browser) {
|
||||
return ContentTask.spawn(browser, {}, function() {
|
||||
let expectedRemovals = [
|
||||
"#onboarding-overlay",
|
||||
"#onboarding-overlay-icon"
|
||||
];
|
||||
for (let selector of expectedRemovals) {
|
||||
let removal = content.document.querySelector(selector);
|
||||
ok(!removal, `Should remove ${selector} onboarding element`);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
add_task(async function test_hide_onboarding_tours() {
|
||||
await SpecialPowers.pushPrefEnv({set: [["browser.onboarding.enabled", true]]});
|
||||
await SpecialPowers.pushPrefEnv({set: [["browser.onboarding.hidden", false]]});
|
||||
await SpecialPowers.pushPrefEnv({set: [["browser.onboarding.notification.finished", false]]});
|
||||
|
||||
let newtab = await BrowserTestUtils.openNewForegroundTab(gBrowser, ABOUT_NEWTAB_URL);
|
||||
await promiseOnboardingOverlayLoaded(newtab.linkedBrowser);
|
||||
let hometab = await BrowserTestUtils.openNewForegroundTab(gBrowser, ABOUT_HOME_URL);
|
||||
await promiseOnboardingOverlayLoaded(hometab.linkedBrowser);
|
||||
|
||||
let expectedPrefUpdates = [
|
||||
promisePrefUpdated("browser.onboarding.hidden", true),
|
||||
promisePrefUpdated("browser.onboarding.notification.finished", true)
|
||||
];
|
||||
await BrowserTestUtils.synthesizeMouseAtCenter("#onboarding-overlay-icon", {}, hometab.linkedBrowser);
|
||||
await promiseOnboardingOverlayOpened(hometab.linkedBrowser);
|
||||
await BrowserTestUtils.synthesizeMouseAtCenter("#onboarding-tour-hidden-checkbox", {}, hometab.linkedBrowser);
|
||||
await BrowserTestUtils.synthesizeMouseAtCenter("#onboarding-overlay-close-btn", {}, hometab.linkedBrowser);
|
||||
await Promise.all(expectedPrefUpdates);
|
||||
|
||||
// Test the hiding operation works arcoss pages
|
||||
await assertOnboardingDestroyed(hometab.linkedBrowser);
|
||||
await BrowserTestUtils.removeTab(hometab);
|
||||
await assertOnboardingDestroyed(newtab.linkedBrowser);
|
||||
await BrowserTestUtils.removeTab(newtab);
|
||||
});
|
|
@ -0,0 +1,59 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
Cu.import("resource://gre/modules/Preferences.jsm");
|
||||
|
||||
function promiseOnboardingOverlayLoaded(browser) {
|
||||
// The onboarding overlay is init inside window.requestIdleCallback, not immediately,
|
||||
// so we use check conditions here.
|
||||
let condition = () => {
|
||||
return ContentTask.spawn(browser, {}, function() {
|
||||
return new Promise(resolve => {
|
||||
let doc = content && content.document;
|
||||
if (doc && doc.querySelector("#onboarding-overlay")) {
|
||||
resolve(true);
|
||||
return;
|
||||
}
|
||||
resolve(false);
|
||||
});
|
||||
})
|
||||
};
|
||||
return BrowserTestUtils.waitForCondition(
|
||||
condition,
|
||||
"Should load onboarding overlay",
|
||||
100,
|
||||
30
|
||||
);
|
||||
}
|
||||
|
||||
function promiseOnboardingOverlayOpened(browser) {
|
||||
let condition = () => {
|
||||
return ContentTask.spawn(browser, {}, function() {
|
||||
return new Promise(resolve => {
|
||||
let overlay = content.document.querySelector("#onboarding-overlay");
|
||||
if (overlay.classList.contains("opened")) {
|
||||
resolve(true);
|
||||
return;
|
||||
}
|
||||
resolve(false);
|
||||
});
|
||||
})
|
||||
};
|
||||
return BrowserTestUtils.waitForCondition(
|
||||
condition,
|
||||
"Should open onboarding overlay",
|
||||
100,
|
||||
30
|
||||
);
|
||||
}
|
||||
|
||||
function promisePrefUpdated(name, expectedValue) {
|
||||
return new Promise(resolve => {
|
||||
let onUpdate = actualValue => {
|
||||
Preferences.ignore(name, onUpdate);
|
||||
is(expectedValue, actualValue, `Should update the pref of ${name}`);
|
||||
resolve();
|
||||
};
|
||||
Preferences.observe(name, onUpdate);
|
||||
});
|
||||
}
|
Загрузка…
Ссылка в новой задаче