Bug 1393489 - Add API to give Snippets in Activity Stream control over when to show onboarding
MozReview-Commit-ID: D5sHjvYeZoB
--- a/browser/extensions/activity-stream/data/content/activity-stream-prerendered.html
+++ b/browser/extensions/activity-stream/data/content/activity-stream-prerendered.html
@@ -3,17 +3,17 @@
<head>
<meta charset="utf-8">
<meta http-equiv="Content-Security-Policy-Report-Only" content="script-src 'unsafe-inline'; img-src http: https: data: blob:; style-src 'unsafe-inline'; child-src 'none'; object-src 'none'; report-uri https://tiles.services.mozilla.com/v4/links/activity-stream/csp">
<title></title>
<link rel="icon" type="image/png" id="favicon" href="chrome://branding/content/icon32.png"/>
<link rel="stylesheet" href="chrome://browser/content/contentSearchUI.css" />
<link rel="stylesheet" href="resource://activity-stream/data/content/activity-stream.css" />
</head>
- <body class="activity-stream">
+ <body class="activity-stream" data-auto-onboarding-notification="false">
<div id="root"><div class="outer-wrapper" data-reactroot="" data-reactid="1" data-react-checksum="57168132"><main data-reactid="2"><div class="search-wrapper" data-reactid="3"><label for="newtab-search-text" class="search-label" data-reactid="4"><span class="sr-only" data-reactid="5"><span data-reactid="6">Search the Web</span></span></label><input type="search" id="newtab-search-text" maxlength="256" placeholder="Search the Web" title="Search the Web" data-reactid="7"/><button id="searchSubmit" class="search-button" title=" " data-reactid="8"><span class="sr-only" data-reactid="9"><span data-reactid="10"> </span></span></button></div><section class="top-sites" data-reactid="11"><h3 class="section-title" data-reactid="12"><span class="icon icon-small-spacer icon-topsites" data-reactid="13"></span><span data-reactid="14"> </span></h3><ul class="top-sites-list" data-reactid="15"><li class="top-site-outer placeholder" data-reactid="16"><a data-reactid="17"><div class="tile" aria-hidden="true" data-reactid="18"><span class="letter-fallback" data-reactid="19"></span><div class="screenshot" style="background-image:none;" data-reactid="20"></div></div><div class="title " data-reactid="21"><span dir="auto" data-reactid="22"></span></div></a></li><li class="top-site-outer placeholder" data-reactid="23"><a data-reactid="24"><div class="tile" aria-hidden="true" data-reactid="25"><span class="letter-fallback" data-reactid="26"></span><div class="screenshot" style="background-image:none;" data-reactid="27"></div></div><div class="title " data-reactid="28"><span dir="auto" data-reactid="29"></span></div></a></li><li class="top-site-outer placeholder" data-reactid="30"><a data-reactid="31"><div class="tile" aria-hidden="true" data-reactid="32"><span class="letter-fallback" data-reactid="33"></span><div class="screenshot" style="background-image:none;" data-reactid="34"></div></div><div class="title " data-reactid="35"><span dir="auto" data-reactid="36"></span></div></a></li><li class="top-site-outer placeholder" data-reactid="37"><a data-reactid="38"><div class="tile" aria-hidden="true" data-reactid="39"><span class="letter-fallback" data-reactid="40"></span><div class="screenshot" style="background-image:none;" data-reactid="41"></div></div><div class="title " data-reactid="42"><span dir="auto" data-reactid="43"></span></div></a></li><li class="top-site-outer placeholder" data-reactid="44"><a data-reactid="45"><div class="tile" aria-hidden="true" data-reactid="46"><span class="letter-fallback" data-reactid="47"></span><div class="screenshot" style="background-image:none;" data-reactid="48"></div></div><div class="title " data-reactid="49"><span dir="auto" data-reactid="50"></span></div></a></li><li class="top-site-outer placeholder" data-reactid="51"><a data-reactid="52"><div class="tile" aria-hidden="true" data-reactid="53"><span class="letter-fallback" data-reactid="54"></span><div class="screenshot" style="background-image:none;" data-reactid="55"></div></div><div class="title " data-reactid="56"><span dir="auto" data-reactid="57"></span></div></a></li></ul></section><div class="sections-list" data-reactid="58"><section data-reactid="59"><div class="section-top-bar" data-reactid="60"><h3 class="section-title" data-reactid="61"><span class="icon icon-small-spacer icon-pocket" data-reactid="62"></span><span data-reactid="63"> </span></h3></div><ul class="section-list" style="padding:0;" data-reactid="64"><li class="card-outer placeholder" data-reactid="65"><a data-reactid="66"><div class="card" data-reactid="67"><div class="card-details no-image" data-reactid="68"><div class="card-text no-image no-host-name no-context" data-reactid="69"><h4 class="card-title" dir="auto" data-reactid="70"></h4><p class="card-description" dir="auto" data-reactid="71"></p></div></div></div></a></li><li class="card-outer placeholder" data-reactid="72"><a data-reactid="73"><div class="card" data-reactid="74"><div class="card-details no-image" data-reactid="75"><div class="card-text no-image no-host-name no-context" data-reactid="76"><h4 class="card-title" dir="auto" data-reactid="77"></h4><p class="card-description" dir="auto" data-reactid="78"></p></div></div></div></a></li><li class="card-outer placeholder" data-reactid="79"><a data-reactid="80"><div class="card" data-reactid="81"><div class="card-details no-image" data-reactid="82"><div class="card-text no-image no-host-name no-context" data-reactid="83"><h4 class="card-title" dir="auto" data-reactid="84"></h4><p class="card-description" dir="auto" data-reactid="85"></p></div></div></div></a></li></ul><div class="topic" data-reactid="86"><span data-reactid="87"><span data-reactid="88"> </span></span><ul data-reactid="89"><li data-reactid="90"><a class="topic-link" data-reactid="91"></a></li></ul></div></section></div><!-- react-empty: 92 --></main></div></div>
<div id="snippets-container">
<div id="snippets"></div>
</div>
<script src="resource://activity-stream/data/content/activity-stream-initial-state.js"></script>
<script src="chrome://browser/content/contentSearchUI.js"></script>
<script src="resource://activity-stream/vendor/react.js"></script>
<script src="resource://activity-stream/vendor/react-dom.js"></script>
--- a/browser/extensions/activity-stream/data/content/activity-stream.html
+++ b/browser/extensions/activity-stream/data/content/activity-stream.html
@@ -3,17 +3,17 @@
<head>
<meta charset="utf-8">
<meta http-equiv="Content-Security-Policy-Report-Only" content="script-src 'unsafe-inline'; img-src http: https: data: blob:; style-src 'unsafe-inline'; child-src 'none'; object-src 'none'; report-uri https://tiles.services.mozilla.com/v4/links/activity-stream/csp">
<title></title>
<link rel="icon" type="image/png" id="favicon" href="chrome://branding/content/icon32.png"/>
<link rel="stylesheet" href="chrome://browser/content/contentSearchUI.css" />
<link rel="stylesheet" href="resource://activity-stream/data/content/activity-stream.css" />
</head>
- <body class="activity-stream">
+ <body class="activity-stream" data-auto-onboarding-notification="false">
<div id="root"></div>
<div id="snippets-container">
<div id="snippets"></div>
</div>
<script src="chrome://browser/content/contentSearchUI.js"></script>
<script src="resource://activity-stream/vendor/react.js"></script>
<script src="resource://activity-stream/vendor/react-dom.js"></script>
<script src="resource://activity-stream/vendor/react-intl.js"></script>
--- a/browser/extensions/onboarding/content/onboarding.js
+++ b/browser/extensions/onboarding/content/onboarding.js
@@ -18,17 +18,18 @@ const UITOUR_JS_URI = "resource://onboar
const TOUR_AGENT_JS_URI = "resource://onboarding/onboarding-tour-agent.js";
const BRAND_SHORT_NAME = Services.strings
.createBundle("chrome://branding/locale/brand.properties")
.GetStringFromName("brandShortName");
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 PREF_AS_ENABLED = "browser.newtabpage.activity-stream.enabled";
+const PREF_AS_HOME_ENABLED = "browser.newtabpage.activity-stream.aboutHome.enabled";
/**
* Add any number of tours, key is the tourId, value should follow the format below
* "tourId": { // The short tour id which could be saved in pref
* // The unique tour id
* id: "onboarding-tour-addons",
* // The string id of tour name which would be displayed on the navigation bar
* tourNameId: "onboarding.tour-addon",
* // The method returing strings used on tour notification
@@ -339,21 +340,29 @@ function sendMessageToChrome(action, par
});
}
/**
* The script won't be initialized if we turned off onboarding by
* setting "browser.onboarding.enabled" to false.
*/
class Onboarding {
constructor(contentWindow) {
+
+ this._window = contentWindow;
+
+ // If we're loading on a page with activity stream, do not enable notifications right away.
+ // Instead, they will be enabled by the gOnboarding API.
+ const autoShowNotifications = this._window.document.body.getAttribute("data-auto-onboarding-notification") !== "false";
+ this._notificationsEnabled = autoShowNotifications;
+
+ this._notificationInitializationNeeded = false;
this.init(contentWindow);
}
async init(contentWindow) {
- this._window = contentWindow;
this._tours = [];
this._tourType = Services.prefs.getStringPref("browser.onboarding.tour-type", "update");
let tourIds = this._getTourIDList();
tourIds.forEach(tourId => {
if (onboardingTourset[tourId]) {
this._tours.push(onboardingTourset[tourId]);
}
@@ -374,16 +383,38 @@ 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.uiInitialized = false;
this._resizeTimerId =
this._window.requestIdleCallback(() => this._resizeUI());
+
+ exposeOnboarding(contentWindow, this)
+ }
+
+ get notificationsEnabled() {
+ return this._notificationsEnabled;
+ }
+
+ enableNotifications() {
+ this._notificationsEnabled = true;
+ if (this._notificationInitializationNeeded) {
+ this.initNotifications();
+ }
+ }
+
+ disableNotifications() {
+ this._notificationsEnabled = false;
+ this.hideNotification();
+ }
+
+ initNotifications() {
+ this._window.requestIdleCallback(() => this._initNotification());
}
_resizeUI() {
let width = this._window.document.body.getBoundingClientRect().width;
if (width < ONBOARDING_MIN_WIDTH_PX) {
// Don't show the overlay UI before we get to a better, responsive design.
this.destroy();
return;
@@ -414,26 +445,37 @@ class Onboarding {
this._overlay = this._renderOverlay();
this._overlay.addEventListener("click", this);
this._overlay.addEventListener("keypress", this);
body.appendChild(this._overlay);
this._loadJS(TOUR_AGENT_JS_URI);
this._initPrefObserver();
- // Doing tour notification takes some effort. Let's do it on idle.
- this._window.requestIdleCallback(() => this._initNotification());
+
+ this._notificationInitializationNeeded = true;
+
+ if (this.notificationsEnabled) {
+ this.initNotifications();
+ }
+
}
_getTourIDList() {
let tours = Services.prefs.getStringPref(`browser.onboarding.${this._tourType}tour`, "");
return tours.split(",").filter(tourId => tourId !== "").map(tourId => tourId.trim());
}
_initNotification() {
+ if (!this._notificationInitializationNeeded || !this.shouldShowNotifications) {
+ return;
+ }
+
+ this._notificationInitializationNeeded = false;
+
let doc = this._window.document;
if (doc.hidden) {
// When the preloaded-browser feature is on,
// it would preload an hidden about:newtab in the background.
// We don't want to show notification in that hidden state.
let onVisible = () => {
if (!doc.hidden) {
doc.removeEventListener("visibilitychange", onVisible);
@@ -878,25 +920,30 @@ class Onboarding {
sendMessageToChrome("set-prefs", [{
name: "browser.onboarding.notification.tour-ids-queue",
value: queue
}]);
}
return queue ? queue.split(",") : [];
}
- showNotification() {
+ get shouldShowNotifications() {
if (Services.prefs.getBoolPref("browser.onboarding.notification.finished", false)) {
- return;
+ return false;
}
-
let lastTime = this._getLastTourChangeTime();
if (this._muteNotificationOnFirstSession(lastTime)) {
- return;
+ return false;
}
+ return true;
+ }
+
+ showNotification() {
+ let lastTime = this._getLastTourChangeTime();
+
// After the notification mute on the 1st session,
// we don't want to show the speech bubble by default
this._overlayIcon.classList.remove("onboarding-speech-bubble");
let queue = this._getNotificationQueue();
let totalMaxTime = Services.prefs.getIntPref("browser.onboarding.notification.max-life-time-all-tours-ms");
if (lastTime && Date.now() - lastTime >= totalMaxTime) {
// Reach total max life time for all tour notifications.
@@ -1134,16 +1181,34 @@ class Onboarding {
let doc = this._window.document;
let script = doc.createElement("script");
script.type = "text/javascript";
script.src = uri;
doc.head.appendChild(script);
}
}
+// This should be called with onboarding has initialized
+function exposeOnboarding(window, onboarding) {
+ const gOnboarding = {
+ shouldShowNotifications: false,
+ notificationsEnabled: false
+ };
+ if (onboarding) {
+ gOnboarding.notificationsEnabled = onboarding.notificationsEnabled;
+ gOnboarding.tourType = onboarding._tourType;
+ gOnboarding.shouldShowNotifications = onboarding.shouldShowNotifications;
+ gOnboarding.enableNotifications = () => onboarding.enableNotifications();
+ gOnboarding.disableNotifications = () => onboarding.disableNotifications();
+ }
+ window.wrappedJSObject.gOnboarding = Cu.cloneInto(gOnboarding, window, {cloneFunctions: true});
+ const event = new Event("gOnboarding:Loaded");
+ window.dispatchEvent(event);
+}
+
// Load onboarding module only when we enable it.
if (Services.prefs.getBoolPref("browser.onboarding.enabled", false)) {
addEventListener("load", function onLoad(evt) {
if (!content || evt.target != content.document) {
return;
}
let window = evt.target.defaultView;
--- a/browser/extensions/onboarding/test/browser/browser.ini
+++ b/browser/extensions/onboarding/test/browser/browser.ini
@@ -6,13 +6,14 @@ support-files =
[browser_onboarding_keyboard.js]
skip-if = debug || os == "mac" # Full keyboard navigation on OSX only works if Full Keyboard Access setting is set to All Control in System Keyboard Preferences
[browser_onboarding_notification.js]
[browser_onboarding_notification_2.js]
[browser_onboarding_notification_3.js]
[browser_onboarding_notification_4.js]
[browser_onboarding_notification_5.js]
[browser_onboarding_notification_click_auto_complete_tour.js]
+[browser_onboarding_notification_enabled.js]
[browser_onboarding_select_default_tour.js]
[browser_onboarding_skip_tour.js]
[browser_onboarding_tours.js]
[browser_onboarding_tourset.js]
[browser_onboarding_uitour.js]
--- a/browser/extensions/onboarding/test/browser/browser_onboarding_notification.js
+++ b/browser/extensions/onboarding/test/browser/browser_onboarding_notification.js
@@ -15,16 +15,17 @@ add_task(async function test_show_tour_n
let targetTourId = null;
let expectedPrefUpdate = null;
await loopTourNotificationQueueOnceInOrder();
await loopTourNotificationQueueOnceInOrder();
expectedPrefUpdate = promisePrefUpdated("browser.onboarding.notification.finished", true);
await reloadTab(tab);
await promiseOnboardingOverlayLoaded(tab.linkedBrowser);
+ await promiseTourNotificationOpened(tab.linkedBrowser, false);
await expectedPrefUpdate;
let tourId = await getCurrentNotificationTargetTourId(tab.linkedBrowser);
ok(!tourId, "Should not prompt each tour for more than 2 chances.");
await BrowserTestUtils.removeTab(tab);
async function loopTourNotificationQueueOnceInOrder() {
for (let i = 0; i < tourIds.length; ++i) {
if (tab) {
--- a/browser/extensions/onboarding/test/browser/browser_onboarding_notification_4.js
+++ b/browser/extensions/onboarding/test/browser/browser_onboarding_notification_4.js
@@ -12,16 +12,17 @@ add_task(async function test_remove_all_
let tourIds = TOUR_IDs;
let tab = null;
let targetTourId = null;
await closeTourNotificationsOneByOne();
let expectedPrefUpdate = promisePrefUpdated("browser.onboarding.notification.finished", true);
await reloadTab(tab);
await promiseOnboardingOverlayLoaded(tab.linkedBrowser);
+ await promiseTourNotificationOpened(tab.linkedBrowser, false);
await expectedPrefUpdate;
let tourId = await getCurrentNotificationTargetTourId(tab.linkedBrowser);
ok(!tourId, "Should not prompt tour notifications any more after closing all notifcations.");
await BrowserTestUtils.removeTab(tab);
async function closeTourNotificationsOneByOne() {
for (let i = 0; i < tourIds.length; ++i) {
if (tab) {
@@ -46,16 +47,17 @@ add_task(async function test_remove_all_
let tourIds = TOUR_IDs;
let tab = null;
let targetTourId = null;
await clickTourNotificationActionButtonsOneByOne();
let expectedPrefUpdate = promisePrefUpdated("browser.onboarding.notification.finished", true);
await reloadTab(tab);
await promiseOnboardingOverlayLoaded(tab.linkedBrowser);
+ await promiseTourNotificationOpened(tab.linkedBrowser, false);
await expectedPrefUpdate;
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);
async function clickTourNotificationActionButtonsOneByOne() {
for (let i = 0; i < tourIds.length; ++i) {
if (tab) {
--- a/browser/extensions/onboarding/test/browser/browser_onboarding_notification_5.js
+++ b/browser/extensions/onboarding/test/browser/browser_onboarding_notification_5.js
@@ -11,11 +11,12 @@ add_task(async function test_finish_tour
await promiseOnboardingOverlayLoaded(tab.linkedBrowser);
await promiseTourNotificationOpened(tab.linkedBrowser);
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);
await reloadTab(tab);
await promiseOnboardingOverlayLoaded(tab.linkedBrowser);
+ await promiseTourNotificationOpened(tab.linkedBrowser, false);
await expectedPrefUpdate;
await BrowserTestUtils.removeTab(tab);
});
new file mode 100644
--- /dev/null
+++ b/browser/extensions/onboarding/test/browser/browser_onboarding_notification_enabled.js
@@ -0,0 +1,101 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+requestLongerTimeout(3);
+
+async function openTabWithPrefs(url, prefs) {
+ resetOnboardingDefaultState();
+ skipMuteNotificationOnFirstSession();
+ await SpecialPowers.pushPrefEnv({set: prefs});
+ return await openTab(url);
+}
+
+async function getNotificationAttribute(browser) {
+ return ContentTask.spawn(browser, {}, async () => {
+ return content.document.body.getAttribute("data-auto-onboarding-notification");
+ });
+}
+
+add_task(async function test_activity_stream_has_expectedAttribute() {
+ const tab = await openTabWithPrefs(ABOUT_NEWTAB_URL, [
+ ["browser.newtabpage.activity-stream.enabled", true]
+ ]);
+
+ is(await getNotificationAttribute(tab.linkedBrowser), "false",
+ "data-auto-onboarding-notification attr on <body> should be \"false\" on about:newtab with Activity Stream");
+
+ await BrowserTestUtils.removeTab(tab);
+});
+
+add_task(async function test_activity_stream_has_expectedAttribute() {
+ const tab = await openTabWithPrefs(ABOUT_HOME_URL, [
+ ["browser.newtabpage.activity-stream.enabled", true],
+ ["browser.newtabpage.activity-stream.aboutHome.enabled", true],
+ ]);
+
+ is(await getNotificationAttribute(tab.linkedBrowser), "false",
+ "data-auto-onboarding-notification attr on <body> should be \"false\" on about:home with Activity Stream");
+
+ await BrowserTestUtils.removeTab(tab);
+});
+
+add_task(async function test_activity_stream_notificationsEnabled_about_newtab_with_as() {
+ const tab = await openTabWithPrefs(ABOUT_NEWTAB_URL, [
+ ["browser.newtabpage.activity-stream.enabled", true],
+ ["browser.newtabpage.activity-stream.aboutHome.enabled", false],
+ ]);
+
+ const shown = await checkNotificationUIShown(tab.linkedBrowser, false);
+ is(shown, false, "should hide notifications on about:newtab with AS");
+
+ await BrowserTestUtils.removeTab(tab);
+});
+
+add_task(async function test_activity_stream_notificationsEnabled_about_newtab_without_as() {
+ const tab = await openTabWithPrefs(ABOUT_NEWTAB_URL, [
+ ["browser.newtabpage.activity-stream.enabled", false],
+ ["browser.newtabpage.activity-stream.aboutHome.enabled", false],
+ ]);
+
+ const shown = await checkNotificationUIShown(tab.linkedBrowser, false);
+ is(shown, true, "should show notifications on about:newtab without AS");
+
+ await BrowserTestUtils.removeTab(tab);
+});
+
+add_task(async function test_activity_stream_notificationsEnabled_about_home_without_as() {
+ const tab = await openTabWithPrefs(ABOUT_HOME_URL, [
+ ["browser.newtabpage.activity-stream.enabled", true],
+ ["browser.newtabpage.activity-stream.aboutHome.enabled", false],
+ ]);
+
+ const shown = await checkNotificationUIShown(tab.linkedBrowser, true);
+ is(shown, true, "should show notifications on about:home without AS");
+
+ await BrowserTestUtils.removeTab(tab);
+});
+
+add_task(async function test_activity_stream_notificationsEnabled_about_home_with_as() {
+ const tab = await openTabWithPrefs(ABOUT_HOME_URL, [
+ ["browser.newtabpage.activity-stream.enabled", true],
+ ["browser.newtabpage.activity-stream.aboutHome.enabled", true],
+ ]);
+
+ const shown = await checkNotificationUIShown(tab.linkedBrowser, false);
+ is(shown, false, "should hide notifications on about:home with AS");
+
+ await BrowserTestUtils.removeTab(tab);
+});
+
+add_task(async function test_enable_notifications() {
+ const tab = await openTabWithPrefs(ABOUT_HOME_URL, [
+ ["browser.newtabpage.activity-stream.enabled", true],
+ ["browser.newtabpage.activity-stream.aboutHome.enabled", true],
+ ]);
+
+ await promiseTourNotificationOpened(tab.linkedBrowser);
+ ok(true, "should show notifications after calling gOnboarding.enableNotifications()");
+
+ await BrowserTestUtils.removeTab(tab);
+});
--- a/browser/extensions/onboarding/test/browser/head.js
+++ b/browser/extensions/onboarding/test/browser/head.js
@@ -112,42 +112,58 @@ function promisePrefUpdated(name, expect
Preferences.ignore(name, onUpdate);
is(expectedValue, actualValue, `Should update the pref of ${name}`);
resolve();
};
Preferences.observe(name, onUpdate);
});
}
-function promiseTourNotificationOpened(browser) {
- function isOpened() {
- let doc = content && content.document;
- let notification = doc.querySelector("#onboarding-notification-bar");
- if (notification && notification.classList.contains("onboarding-opened")) {
- ok(true, "Should open tour notification");
- return Promise.resolve();
+function promiseWaitForOnboardingAPI(browser) {
+ return ContentTask.spawn(browser, {}, async () => {
+ if (!content.wrappedJSObject.gOnboarding) {
+ await new Promise(resolve => {
+ content.addEventListener("gOnboarding:Loaded", resolve);
+ });
}
- return new Promise(resolve => {
- let observer = new content.MutationObserver(mutations => {
- mutations.forEach(mutation => {
- let bar = Array.from(mutation.addedNodes)
- .find(node => node.id == "onboarding-notification-bar");
- if (bar && bar.classList.contains("onboarding-opened")) {
- observer.disconnect();
- ok(true, "Should open tour notification");
- resolve();
- }
- });
+ });
+}
+
+async function checkNotificationUIShown(browser, shouldBeShown) {
+ await promiseWaitForOnboardingAPI(browser);
+ if (shouldBeShown) {
+ await ContentTask.spawn(browser, {}, async () => {
+ await ContentTaskUtils.waitForCondition(() => {
+ const el = content.document.getElementById("onboarding-notification-bar")
+ return el && el.classList.contains("onboarding-opened");
});
- observer.observe(doc.body, { childList: true });
});
+ return true;
}
- return ContentTask.spawn(browser, {}, isOpened);
+ return await ContentTask.spawn(browser, {}, async () => {
+ const el = content.document.getElementById("onboarding-notification-bar")
+ if (el && el.classList.contains("onboarding-opened")) {
+ return true;
+ }
+ return content.wrappedJSObject.gOnboarding.notificationsEnabled;
+ });
}
+async function promiseTourNotificationOpened(browser, waitForUi = true) {
+ await promiseWaitForOnboardingAPI(browser);
+ await ContentTask.spawn(browser, {}, async function() {
+ ok(content.wrappedJSObject.gOnboarding.shouldShowNotifications, "gOnboarding.shouldShowNotifications is true");
+ content.wrappedJSObject.gOnboarding.enableNotifications();
+ });
+ if (waitForUi) {
+ await checkNotificationUIShown(browser, true);
+ }
+}
+
+
function promiseTourNotificationClosed(browser) {
let condition = () => {
return ContentTask.spawn(browser, {}, function() {
return new Promise(resolve => {
let bar = content.document.querySelector("#onboarding-notification-bar");
if (bar && !bar.classList.contains("onboarding-opened")) {
resolve(true);
return;