Bug 1377470 - run onboarding scripts after browser UI is ready;r=mossop draft
authorgasolin <gasolin@gmail.com>
Thu, 06 Jul 2017 11:40:26 +0800
changeset 607315 a76fe7aa601f019a775103ef2389e53ee926ee44
parent 606958 6fec4855b5345eb63fef57089e61829b88f5f4eb
child 637008 d240c4d3c0763d26e97749c40af4682320697c71
push id67964
push userbmo:gasolin@mozilla.com
push dateWed, 12 Jul 2017 07:23:57 +0000
reviewersmossop
bugs1377470
milestone56.0a1
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);
+  }
+}