Bug 1377470 - run onboarding scripts after browser UI is ready;r=mossop
MozReview-Commit-ID: BRxWc962EW2
--- a/browser/extensions/onboarding/README.md
+++ b/browser/extensions/onboarding/README.md
@@ -1,29 +1,43 @@
# Onboarding
-System addon to provide the onboarding overlay for user friendly tours.
+System addon to provide the onboarding overlay for user-friendly tours.
+
+## How to show the onboarding tour
+
+Open `about:config` page and filter with `onboarding` keyword. Then set following preferences:
+
+```
+browser.onboarding.disabled = false
+browser.onboarding.hidden = false
+browser.onboarding.tour-set = "new" // for new user tour, or "update" for update user tour
+```
+And make sure the value of `browser.onboarding.tourset-verion` and `browser.onboarding.seen-tourset-verion` are the same.
## Architecture
-Everytime `about:home` or `about:newtab` page is opened, onboarding overlay is injected into that page (if `browser.onboarding.enabled` preference is `true`).
+Everytime `about:home` or `about:newtab` page is opened, onboarding overlay is injected into that page.
+
+`OnboardingTourType.jsm` will check the onboarding tour type (currently support `new` and `update`). Then in `onboarding.js`, All tours are defined inside of `onboardingTourset` dictionary. `getTourIDList` function will load tours from proper preferences. (Check `How to change the order of tours` section for more detail).
+
+When user clicks the action button in each tour, We use [UITour](http://bedrock.readthedocs.io/en/latest/uitour.html) to highlight the correspondent browser UI element.
## Landing rules
We would apply some rules:
-* Avoid `chrome://` in `onbaording.js` since onboarding is intented to be injected into a normal content process page.
-* All styles and ids should be formated as `onboarding-*` to avoid conflict with the origin page.
-* All strings in `locales` should be formated as `onboarding.*` for consistency.
+* All styles and ids should be formatted as `onboarding-*` to avoid conflict with the origin page.
+* All strings in `locales` should be formatted as `onboarding.*` for consistency.
## How to change the order of tours
Edit `browser/app/profile/firefox.js` and modify `browser.onboarding.newtour` for the new user tour or `browser.onboarding.updatetour` for the update user tour. You can change the tour list and the order by concate `tourIds` with `,` sign. You can find available `tourId` from `onboardingTourset` in `onboarding.js`.
## How to pump tour set version after update tours
The tourset version is used to track the last major tourset change version. The `tourset-version` pref store the major tourset version (ex: `1`) but not the current browser version. When browser update to the next version (ex: 58, 59) the tourset pref is still `1` if we didn't do any major tourset update.
-Once the tour set version is updated (ex: `2`), onboarding overlay should show the update tour to the updated user (ex: update from v56 -> v57), even when user have watched the previous tours or preferred to hide the previous tours.
+Once the tour set version is updated (ex: `2`), onboarding overlay should show the update tour to the updated user (ex: update from v56 -> v57), even when user has watched the previous tours or preferred to hide the previous tours.
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`.
+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`.
--- a/browser/extensions/onboarding/bootstrap.js
+++ b/browser/extensions/onboarding/bootstrap.js
@@ -1,39 +1,45 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* 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/. */
+/* globals APP_STARTUP, ADDON_INSTALL */
"use strict";
const {utils: Cu} = Components;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "OnboardingTourType",
"resource://onboarding/modules/OnboardingTourType.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Preferences",
"resource://gre/modules/Preferences.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Services",
"resource://gre/modules/Services.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "setTimeout",
+ "resource://gre/modules/Timer.jsm");
+const BROWSER_READY_NOTIFICATION = "final-ui-startup";
const PREF_WHITELIST = [
"browser.onboarding.enabled",
"browser.onboarding.hidden",
"browser.onboarding.notification.finished",
"browser.onboarding.notification.lastPrompted"
];
[
"onboarding-tour-private-browsing",
"onboarding-tour-addons",
"onboarding-tour-customize",
"onboarding-tour-search",
"onboarding-tour-default-browser",
"onboarding-tour-sync",
].forEach(tourId => PREF_WHITELIST.push(`browser.onboarding.tour.${tourId}.completed`));
+let waitingForBrowserReady = true;
+
/**
* 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`
@@ -42,29 +48,64 @@ const PREF_WHITELIST = [
function setPrefs(prefs) {
prefs.forEach(pref => {
if (PREF_WHITELIST.includes(pref.name)) {
Preferences.set(pref.name, pref.value);
}
});
}
+/**
+ * Listen and process events from content.
+ */
function initContentMessageListener() {
Services.mm.addMessageListener("Onboarding:OnContentMessage", msg => {
switch (msg.data.action) {
case "set-prefs":
setPrefs(msg.data.params);
break;
}
});
}
-function install(aData, aReason) {}
+/**
+ * onBrowserReady - Continues startup of the add-on after browser is ready.
+ */
+function onBrowserReady() {
+ waitingForBrowserReady = false;
-function uninstall(aData, aReason) {}
-
-function startup(aData, reason) {
OnboardingTourType.check();
Services.mm.loadFrameScript("resource://onboarding/onboarding.js", true);
initContentMessageListener();
}
-function shutdown(aData, reason) {}
+/**
+ * observe - nsIObserver callback to handle various browser notifications.
+ */
+function observe(subject, topic, data) {
+ switch (topic) {
+ case BROWSER_READY_NOTIFICATION:
+ Services.obs.removeObserver(observe, BROWSER_READY_NOTIFICATION);
+ // Avoid running synchronously during this event that's used for timing
+ setTimeout(() => onBrowserReady());
+ break;
+ }
+}
+
+function install(aData, aReason) {}
+
+function uninstall(aData, aReason) {}
+
+function startup(aData, aReason) {
+ // Only start Onboarding when the browser UI is ready
+ if (aReason === APP_STARTUP || aReason === ADDON_INSTALL) {
+ Services.obs.addObserver(observe, BROWSER_READY_NOTIFICATION);
+ } else {
+ onBrowserReady();
+ }
+}
+
+function shutdown(aData, aReason) {
+ // Stop waiting for browser to be ready
+ if (waitingForBrowserReady) {
+ Services.obs.removeObserver(observe, BROWSER_READY_NOTIFICATION);
+ }
+}