Bug 1377470 - run onboarding scripts after browser UI is ready;r=mossop
authorgasolin <gasolin@gmail.com>
Thu, 06 Jul 2017 11:40:26 +0800
changeset 368364 ed0ec2f26de406e90f0badfa5aff54390acedf47
parent 368363 da08d4b7545ece28ec13c92410d6ad0f246284f0
child 368365 f95be8a7110243a841c380be24888dda63b23c62
push id46311
push usercbook@mozilla.com
push dateWed, 12 Jul 2017 09:10:58 +0000
treeherderautoland@ed0ec2f26de4 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmossop
bugs1377470
milestone56.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1377470 - run onboarding scripts after browser UI is ready;r=mossop MozReview-Commit-ID: BRxWc962EW2
browser/extensions/onboarding/README.md
browser/extensions/onboarding/bootstrap.js
--- 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);
+  }
+}