зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1392475
- [Onboarding] Turn fox logo to watermark if all tours or notifications are finished. r=Fischer,gasolin
MozReview-Commit-ID: CLbiHqCmxr0 --HG-- extra : rebase_source : d04e9ec333b02e7c0c3f4b48d1b7712272228a80
This commit is contained in:
Родитель
a1632b557b
Коммит
4a265bd8d1
|
@ -1721,6 +1721,7 @@ pref("browser.suppress_first_window_animation", true);
|
|||
pref("browser.onboarding.enabled", true);
|
||||
// Mark this as an upgraded profile so we don't offer the initial new user onboarding tour.
|
||||
pref("browser.onboarding.tourset-version", 2);
|
||||
pref("browser.onboarding.state", "default");
|
||||
// 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.
|
||||
|
|
|
@ -1687,7 +1687,7 @@ BrowserGlue.prototype = {
|
|||
|
||||
// eslint-disable-next-line complexity
|
||||
_migrateUI: function BG__migrateUI() {
|
||||
const UI_VERSION = 53;
|
||||
const UI_VERSION = 54;
|
||||
const BROWSER_DOCURL = "chrome://browser/content/browser.xul";
|
||||
|
||||
let currentUIVersion;
|
||||
|
@ -2078,6 +2078,15 @@ BrowserGlue.prototype = {
|
|||
}
|
||||
}
|
||||
|
||||
if (currentUIVersion < 54) {
|
||||
// Migrate browser.onboarding.hidden to browser.onboarding.state.
|
||||
if (Services.prefs.prefHasUserValue("browser.onboarding.hidden")) {
|
||||
let state = Services.prefs.getBoolPref("browser.onboarding.hidden") ? "watermark" : "default";
|
||||
Services.prefs.setStringPref("browser.onboarding.state", state);
|
||||
Services.prefs.clearUserPref("browser.onboarding.hidden");
|
||||
}
|
||||
}
|
||||
|
||||
// Update the migration version.
|
||||
Services.prefs.setIntPref("browser.migration.version", UI_VERSION);
|
||||
},
|
||||
|
|
|
@ -37,6 +37,7 @@ var OnboardingTourType = {
|
|||
Services.prefs.clearUserPref("browser.onboarding.notification.prompt-count");
|
||||
Services.prefs.clearUserPref("browser.onboarding.notification.last-time-of-changing-tour-sec");
|
||||
Services.prefs.clearUserPref("browser.onboarding.notification.tour-ids-queue");
|
||||
Services.prefs.clearUserPref("browser.onboarding.state");
|
||||
}
|
||||
Services.prefs.setIntPref(PREF_SEEN_TOURSET_VERSION, TOURSET_VERSION);
|
||||
},
|
||||
|
|
|
@ -52,3 +52,11 @@ Once the tour set version is updated (ex: `2`), onboarding overlay should show t
|
|||
Edit `browser/app/profile/firefox.js` and set `browser.onboarding.tourset-version` as `[tourset version]` (in integer format).
|
||||
|
||||
For example, if we update the tourset in v60 and decide to show all update users the tour, we set `browser.onboarding.tourset-version` as `3`.
|
||||
|
||||
## Icon states
|
||||
|
||||
Onboarding module has two states for its overlay icon: `default` and `watermark`.
|
||||
By default, it shows `default` state.
|
||||
When either tours or notifications are all completed, the icon changes to the `watermark` state.
|
||||
The icon state is stored in `browser.onboarding.state`.
|
||||
When `tourset-version` is updated, or when we detect the `tour-type` is changed to `update`, icon state will be changed back to the `default` state.
|
||||
|
|
|
@ -20,6 +20,7 @@ const BROWSER_READY_NOTIFICATION = "browser-delayed-startup-finished";
|
|||
const BROWSER_SESSION_STORE_NOTIFICATION = "sessionstore-windows-restored";
|
||||
const PREF_WHITELIST = [
|
||||
["browser.onboarding.enabled", PREF_BOOL],
|
||||
["browser.onboarding.state", PREF_STRING],
|
||||
["browser.onboarding.notification.finished", PREF_BOOL],
|
||||
["browser.onboarding.notification.prompt-count", PREF_INT],
|
||||
["browser.onboarding.notification.last-time-of-changing-tour-sec", PREF_INT],
|
||||
|
@ -47,7 +48,7 @@ let waitingForBrowserReady = true;
|
|||
*
|
||||
* @param {Array} prefs the array of prefs to set.
|
||||
* The array element carrys info to set pref, should contain
|
||||
* - {String} name the pref name
|
||||
* - {String} name the pref name, such as `browser.onboarding.state`
|
||||
* - {*} value the value to set
|
||||
**/
|
||||
function setPrefs(prefs) {
|
||||
|
|
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 2.1 KiB |
|
@ -45,7 +45,7 @@
|
|||
transition: outline-offset 150ms;
|
||||
}
|
||||
|
||||
#onboarding-overlay-button-icon {
|
||||
#onboarding-overlay-button > img {
|
||||
width: 32px;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
@ -94,6 +94,16 @@
|
|||
box-shadow: 2px 0 5px 0 rgba(74, 74, 79, 0.25);
|
||||
}
|
||||
|
||||
#onboarding-overlay-button-watermark-icon,
|
||||
#onboarding-overlay-button.onboarding-watermark:not(:hover)::after,
|
||||
#onboarding-overlay-button.onboarding-watermark:not(:hover) > #onboarding-overlay-button-icon {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#onboarding-overlay-button.onboarding-watermark:not(:hover) > #onboarding-overlay-button-watermark-icon {
|
||||
display: block;
|
||||
}
|
||||
|
||||
#onboarding-overlay-dialog,
|
||||
.onboarding-hidden,
|
||||
#onboarding-tour-sync-page[data-login-state=logged-in] .show-on-logged-out,
|
||||
|
|
|
@ -23,6 +23,8 @@ const PROMPT_COUNT_PREF = "browser.onboarding.notification.prompt-count";
|
|||
const ONBOARDING_DIALOG_ID = "onboarding-overlay-dialog";
|
||||
const ONBOARDING_MIN_WIDTH_PX = 960;
|
||||
const SPEECH_BUBBLE_MIN_WIDTH_PX = 1130;
|
||||
const ICON_STATE_WATERMARK = "watermark";
|
||||
const ICON_STATE_DEFAULT = "default";
|
||||
|
||||
/**
|
||||
* Add any number of tours, key is the tourId, value should follow the format below
|
||||
|
@ -419,6 +421,8 @@ class Onboarding {
|
|||
this._loadJS(TOUR_AGENT_JS_URI);
|
||||
|
||||
this._initPrefObserver();
|
||||
this._onIconStateChange(Services.prefs.getStringPref("browser.onboarding.state", ICON_STATE_DEFAULT));
|
||||
|
||||
// Doing tour notification takes some effort. Let's do it on idle.
|
||||
this._window.requestIdleCallback(() => this._initNotification());
|
||||
}
|
||||
|
@ -452,10 +456,14 @@ class Onboarding {
|
|||
}
|
||||
|
||||
this._prefsObserved = new Map();
|
||||
this._prefsObserved.set("browser.onboarding.state", () => {
|
||||
this._onIconStateChange(Services.prefs.getStringPref("browser.onboarding.state", ICON_STATE_DEFAULT));
|
||||
});
|
||||
this._tours.forEach(tour => {
|
||||
let tourId = tour.id;
|
||||
this._prefsObserved.set(`browser.onboarding.tour.${tourId}.completed`, () => {
|
||||
this.markTourCompletionState(tourId);
|
||||
this._checkWatermarkByTours();
|
||||
});
|
||||
});
|
||||
for (let [name, callback] of this._prefsObserved) {
|
||||
|
@ -463,6 +471,16 @@ class Onboarding {
|
|||
}
|
||||
}
|
||||
|
||||
_checkWatermarkByTours() {
|
||||
let tourDone = this._tours.every(tour => this.isTourCompleted(tour.id));
|
||||
if (tourDone) {
|
||||
sendMessageToChrome("set-prefs", [{
|
||||
name: "browser.onboarding.state",
|
||||
value: ICON_STATE_WATERMARK
|
||||
}]);
|
||||
}
|
||||
}
|
||||
|
||||
_clearPrefObserver() {
|
||||
if (this._prefsObserved) {
|
||||
for (let [name, callback] of this._prefsObserved) {
|
||||
|
@ -655,6 +673,18 @@ class Onboarding {
|
|||
this._overlayIcon = this._overlay = this._notificationBar = null;
|
||||
}
|
||||
|
||||
_onIconStateChange(state) {
|
||||
switch (state) {
|
||||
case ICON_STATE_DEFAULT:
|
||||
this._overlayIcon.classList.remove("onboarding-watermark");
|
||||
break;
|
||||
case ICON_STATE_WATERMARK:
|
||||
this._overlayIcon.classList.add("onboarding-watermark");
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
showOverlay() {
|
||||
if (this._tourItems.length == 0) {
|
||||
// Lazy loading until first toggle.
|
||||
|
@ -923,6 +953,10 @@ class Onboarding {
|
|||
{
|
||||
name: "browser.onboarding.notification.tour-ids-queue",
|
||||
value: ""
|
||||
},
|
||||
{
|
||||
name: "browser.onboarding.state",
|
||||
value: ICON_STATE_WATERMARK
|
||||
}
|
||||
]);
|
||||
return;
|
||||
|
@ -1006,6 +1040,10 @@ class Onboarding {
|
|||
{
|
||||
name: "browser.onboarding.notification.finished",
|
||||
value: true
|
||||
},
|
||||
{
|
||||
name: "browser.onboarding.state",
|
||||
value: ICON_STATE_WATERMARK
|
||||
}
|
||||
]);
|
||||
}
|
||||
|
@ -1050,11 +1088,16 @@ class Onboarding {
|
|||
button.id = "onboarding-overlay-button";
|
||||
button.setAttribute("aria-haspopup", true);
|
||||
button.setAttribute("aria-controls", `${ONBOARDING_DIALOG_ID}`);
|
||||
let img = this._window.document.createElement("img");
|
||||
img.id = "onboarding-overlay-button-icon";
|
||||
img.setAttribute("role", "presentation");
|
||||
img.src = "chrome://branding/content/icon64.png";
|
||||
button.appendChild(img);
|
||||
let defaultImg = this._window.document.createElement("img");
|
||||
defaultImg.id = "onboarding-overlay-button-icon";
|
||||
defaultImg.setAttribute("role", "presentation");
|
||||
defaultImg.src = "chrome://branding/content/icon64.png";
|
||||
button.appendChild(defaultImg);
|
||||
let watermarkImg = this._window.document.createElement("img");
|
||||
watermarkImg.id = "onboarding-overlay-button-watermark-icon";
|
||||
watermarkImg.setAttribute("role", "presentation");
|
||||
watermarkImg.src = "resource://onboarding/img/watermark64.png";
|
||||
button.appendChild(watermarkImg);
|
||||
return button;
|
||||
}
|
||||
|
||||
|
|
|
@ -13,14 +13,18 @@ add_task(async function test_show_tour_notifications_in_order() {
|
|||
let tourIds = TOUR_IDs;
|
||||
let tab = null;
|
||||
let targetTourId = null;
|
||||
let expectedPrefUpdate = null;
|
||||
let expectedPrefUpdates = null;
|
||||
await loopTourNotificationQueueOnceInOrder();
|
||||
await loopTourNotificationQueueOnceInOrder();
|
||||
|
||||
expectedPrefUpdate = promisePrefUpdated("browser.onboarding.notification.finished", true);
|
||||
expectedPrefUpdates = Promise.all([
|
||||
promisePrefUpdated("browser.onboarding.notification.finished", true),
|
||||
promisePrefUpdated("browser.onboarding.state", ICON_STATE_WATERMARK)
|
||||
]);
|
||||
await reloadTab(tab);
|
||||
await promiseOnboardingOverlayLoaded(tab.linkedBrowser);
|
||||
await expectedPrefUpdate;
|
||||
await expectedPrefUpdates;
|
||||
await assertWatermarkIconDisplayed(tab.linkedBrowser);
|
||||
let tourId = await getCurrentNotificationTargetTourId(tab.linkedBrowser);
|
||||
ok(!tourId, "Should not prompt each tour for more than 2 chances.");
|
||||
await BrowserTestUtils.removeTab(tab);
|
||||
|
|
|
@ -14,10 +14,15 @@ add_task(async function test_remove_all_tour_notifications_through_close_button(
|
|||
let targetTourId = null;
|
||||
await closeTourNotificationsOneByOne();
|
||||
|
||||
let expectedPrefUpdate = promisePrefUpdated("browser.onboarding.notification.finished", true);
|
||||
let expectedPrefUpdates = [
|
||||
promisePrefUpdated("browser.onboarding.notification.finished", true),
|
||||
promisePrefUpdated("browser.onboarding.state", ICON_STATE_WATERMARK)
|
||||
];
|
||||
await reloadTab(tab);
|
||||
await promiseOnboardingOverlayLoaded(tab.linkedBrowser);
|
||||
await expectedPrefUpdate;
|
||||
await Promise.all(expectedPrefUpdates);
|
||||
await assertWatermarkIconDisplayed(tab.linkedBrowser);
|
||||
|
||||
let tourId = await getCurrentNotificationTargetTourId(tab.linkedBrowser);
|
||||
ok(!tourId, "Should not prompt tour notifications any more after closing all notifcations.");
|
||||
await BrowserTestUtils.removeTab(tab);
|
||||
|
@ -48,10 +53,15 @@ add_task(async function test_remove_all_tour_notifications_through_action_button
|
|||
let targetTourId = null;
|
||||
await clickTourNotificationActionButtonsOneByOne();
|
||||
|
||||
let expectedPrefUpdate = promisePrefUpdated("browser.onboarding.notification.finished", true);
|
||||
let expectedPrefUpdates = [
|
||||
promisePrefUpdated("browser.onboarding.notification.finished", true),
|
||||
promisePrefUpdated("browser.onboarding.state", ICON_STATE_WATERMARK)
|
||||
];
|
||||
await reloadTab(tab);
|
||||
await promiseOnboardingOverlayLoaded(tab.linkedBrowser);
|
||||
await expectedPrefUpdate;
|
||||
await Promise.all(expectedPrefUpdates);
|
||||
await assertWatermarkIconDisplayed(tab.linkedBrowser);
|
||||
|
||||
let tourId = await getCurrentNotificationTargetTourId(tab.linkedBrowser);
|
||||
ok(!tourId, "Should not prompt tour notifcations any more after taking actions on all notifcations.");
|
||||
await BrowserTestUtils.removeTab(tab);
|
||||
|
|
|
@ -13,9 +13,13 @@ add_task(async function test_finish_tour_notifcations_after_total_max_life_time(
|
|||
|
||||
let totalMaxTime = Preferences.get("browser.onboarding.notification.max-life-time-all-tours-ms");
|
||||
Preferences.set("browser.onboarding.notification.last-time-of-changing-tour-sec", Math.floor((Date.now() - totalMaxTime) / 1000));
|
||||
let expectedPrefUpdate = promisePrefUpdated("browser.onboarding.notification.finished", true);
|
||||
let expectedPrefUpdates = Promise.all([
|
||||
promisePrefUpdated("browser.onboarding.notification.finished", true),
|
||||
promisePrefUpdated("browser.onboarding.state", ICON_STATE_WATERMARK)
|
||||
]);
|
||||
await reloadTab(tab);
|
||||
await promiseOnboardingOverlayLoaded(tab.linkedBrowser);
|
||||
await expectedPrefUpdate;
|
||||
await expectedPrefUpdates;
|
||||
await assertWatermarkIconDisplayed(tab.linkedBrowser);
|
||||
await BrowserTestUtils.removeTab(tab);
|
||||
});
|
||||
|
|
|
@ -8,7 +8,8 @@ add_task(async function test_skip_onboarding_tours() {
|
|||
|
||||
let tourIds = TOUR_IDs;
|
||||
let expectedPrefUpdates = [
|
||||
promisePrefUpdated("browser.onboarding.notification.finished", true)
|
||||
promisePrefUpdated("browser.onboarding.notification.finished", true),
|
||||
promisePrefUpdated("browser.onboarding.state", ICON_STATE_WATERMARK)
|
||||
];
|
||||
tourIds.forEach((id, idx) => expectedPrefUpdates.push(promisePrefUpdated(`browser.onboarding.tour.${id}.completed`, true)));
|
||||
|
||||
|
@ -21,6 +22,7 @@ add_task(async function test_skip_onboarding_tours() {
|
|||
await BrowserTestUtils.synthesizeMouseAtCenter("#onboarding-skip-tour-button", {}, tab.linkedBrowser);
|
||||
await overlayClosedPromise;
|
||||
await Promise.all(expectedPrefUpdates);
|
||||
await assertWatermarkIconDisplayed(tab.linkedBrowser);
|
||||
|
||||
await BrowserTestUtils.removeTab(tab);
|
||||
});
|
||||
|
|
|
@ -88,3 +88,28 @@ add_task(async function test_click_action_button_to_set_tour_completed() {
|
|||
await BrowserTestUtils.removeTab(tab);
|
||||
}
|
||||
});
|
||||
|
||||
add_task(async function test_set_watermark_after_all_tour_completed() {
|
||||
resetOnboardingDefaultState();
|
||||
|
||||
await SpecialPowers.pushPrefEnv({set: [
|
||||
["browser.onboarding.tour-type", "new"]
|
||||
]});
|
||||
|
||||
let tabs = [];
|
||||
for (let url of URLs) {
|
||||
let tab = await openTab(url);
|
||||
await promiseOnboardingOverlayLoaded(tab.linkedBrowser);
|
||||
await BrowserTestUtils.synthesizeMouseAtCenter("#onboarding-overlay-button", {}, tab.linkedBrowser);
|
||||
await promiseOnboardingOverlayOpened(tab.linkedBrowser);
|
||||
tabs.push(tab);
|
||||
}
|
||||
let expectedPrefUpdate = promisePrefUpdated("browser.onboarding.state", ICON_STATE_WATERMARK);
|
||||
TOUR_IDs.forEach(id => Preferences.set(`browser.onboarding.tour.${id}.completed`, true));
|
||||
await expectedPrefUpdate;
|
||||
|
||||
for (let tab of tabs) {
|
||||
await assertWatermarkIconDisplayed(tab.linkedBrowser);
|
||||
await BrowserTestUtils.removeTab(tab);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -22,6 +22,8 @@ const UPDATE_TOUR_IDs = [
|
|||
"onboarding-tour-customize",
|
||||
"onboarding-tour-sync",
|
||||
];
|
||||
const ICON_STATE_WATERMARK = "watermark";
|
||||
const ICON_STATE_DEFAULT = "default";
|
||||
|
||||
registerCleanupFunction(resetOnboardingDefaultState);
|
||||
|
||||
|
@ -29,6 +31,7 @@ function resetOnboardingDefaultState() {
|
|||
// All the prefs should be reset to the default states
|
||||
// and no need to revert back so we don't use `SpecialPowers.pushPrefEnv` here.
|
||||
Preferences.set("browser.onboarding.enabled", true);
|
||||
Preferences.set("browser.onboarding.state", ICON_STATE_DEFAULT);
|
||||
Preferences.set("browser.onboarding.notification.finished", false);
|
||||
Preferences.set("browser.onboarding.notification.mute-duration-on-first-session-ms", 300000);
|
||||
Preferences.set("browser.onboarding.notification.max-life-time-per-tour-ms", 432000000);
|
||||
|
@ -275,3 +278,10 @@ function assertModalDialog(browser, args) {
|
|||
}
|
||||
});
|
||||
}
|
||||
|
||||
function assertWatermarkIconDisplayed(browser) {
|
||||
return ContentTask.spawn(browser, {}, function() {
|
||||
let overlayButton = content.document.getElementById("onboarding-overlay-button");
|
||||
ok(overlayButton.classList.contains("onboarding-watermark"), "Should display the watermark onboarding icon");
|
||||
});
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче