Bug 1394730 - Should clean up UITour highlight while navigating to another tour, r?rexboy
This patch:
- fixes UITour highlight not being cleaned up while navigating to another tour by mouse (the regression caused by the
bug 1377298)
- adds the ability to clean up UITour while navigating to another tour by keyboard
MozReview-Commit-ID: CH8w5CloGCv
--- a/browser/extensions/onboarding/content/onboarding-tour-agent.js
+++ b/browser/extensions/onboarding/content/onboarding-tour-agent.js
@@ -1,27 +1,27 @@
/* 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 Mozilla */
+/* globals Mozilla */
+(function() {
"use strict";
-document.addEventListener("Agent:CanSetDefaultBrowserInBackground", () => {
+let onCanSetDefaultBrowserInBackground = () => {
Mozilla.UITour.getConfiguration("appinfo", config => {
let canSetInBackGround = config.canSetDefaultBrowserInBackground;
let btn = document.getElementById("onboarding-tour-default-browser-button");
btn.setAttribute("data-cansetbg", canSetInBackGround);
btn.textContent = canSetInBackGround ? btn.getAttribute("data-bg") : btn.getAttribute("data-panel");
});
-});
+};
-document.getElementById("onboarding-overlay")
- .addEventListener("click", evt => {
+let onClick = evt => {
switch (evt.target.id) {
case "onboarding-tour-addons-button":
Mozilla.UITour.showHighlight("addons");
break;
case "onboarding-tour-customize-button":
Mozilla.UITour.showHighlight("customize");
break;
case "onboarding-tour-default-browser-button":
@@ -58,18 +58,32 @@ document.getElementById("onboarding-over
}
break;
case "onboarding-overlay":
case "onboarding-overlay-close-btn":
// Dismiss any highlights if a user tries to close the dialog.
Mozilla.UITour.hideHighlight();
break;
}
- // Dismiss any highlights if a user tries to change to other tours.
- if (evt.target.classList.contains("onboarding-tour-item")) {
- Mozilla.UITour.hideHighlight();
+ let classList = evt.target.classList;
+ // On keyboard navigation the target would be .onboarding-tour-item.
+ // On mouse clicking the target would be .onboarding-tour-item-container.
+ if (classList.contains("onboarding-tour-item") || classList.contains("onboarding-tour-item-container")) {
+ Mozilla.UITour.hideHighlight(); // Clean up UITour if a user tries to change to other tours.
+ }
+};
+
+let overlay = document.getElementById("onboarding-overlay");
+overlay.addEventListener("click", onClick);
+overlay.addEventListener("keypress", e => {
+ let { target, key } = e;
+ let classList = target.classList;
+ if ((key == " " || key == "Enter") &&
+ // On keyboard navigation the target would be .onboarding-tour-item.
+ // On mouse clicking the target would be .onboarding-tour-item-container.
+ (classList.contains("onboarding-tour-item") || classList.contains("onboarding-tour-item-container"))) {
+ Mozilla.UITour.hideHighlight(); // Clean up UITour if a user tries to change to other tours.
}
});
+document.getElementById("onboarding-overlay-button").addEventListener("Agent:Destroy", () => Mozilla.UITour.hideHighlight());
+document.addEventListener("Agent:CanSetDefaultBrowserInBackground", onCanSetDefaultBrowserInBackground);
-document.getElementById("onboarding-overlay-button").addEventListener("Agent:Destroy", () => {
- Mozilla.UITour.hideHighlight();
- Mozilla.UITour.hideMenu("urlbar");
-});
+})();
--- a/browser/extensions/onboarding/test/browser/browser.ini
+++ b/browser/extensions/onboarding/test/browser/browser.ini
@@ -10,8 +10,9 @@ skip-if = debug || os == "mac" # Full ke
[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_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_tours.js
+++ b/browser/extensions/onboarding/test/browser/browser_onboarding_tours.js
@@ -20,23 +20,16 @@ function assertTourCompleted(tourId, exp
} else {
ok(!item.classList.contains("onboarding-complete"), `Should not set the incomplete #${args.tourId} tour with the complete style`);
ok(!completedText, "Text label should not be present for an incomplete item");
ok(!item.hasAttribute("aria-describedby"), "Incomplete item should not have aria-describedby attribute set");
}
});
}
-function promisePopupChange(popup, expectedState) {
- return new Promise(resolve => {
- let event = expectedState == "open" ? "popupshown" : "popuphidden";
- popup.addEventListener(event, resolve, { once: true });
- });
-}
-
add_task(async function test_set_right_tour_completed_style_on_overlay() {
resetOnboardingDefaultState();
let tourIds = TOUR_IDs;
// Mark the tours of even number as completed
for (let i = 0; i < tourIds.length; ++i) {
setTourCompletedState(tourIds[i], i % 2 == 0);
}
@@ -90,84 +83,8 @@ add_task(async function test_click_actio
let tab = tabs[i];
await assertOverlaySemantics(tab.linkedBrowser);
for (let id of tourIds) {
await assertTourCompleted(id, id == completedTourId, tab.linkedBrowser);
}
await BrowserTestUtils.removeTab(tab);
}
});
-
-add_task(async function test_clean_up_uitour_on_page_unload() {
- resetOnboardingDefaultState();
- await SpecialPowers.pushPrefEnv({set: [
- ["browser.onboarding.newtour", "singlesearch,customize"],
- ]});
-
- let tab = await openTab(ABOUT_NEWTAB_URL);
- await promiseOnboardingOverlayLoaded(tab.linkedBrowser);
- await BrowserTestUtils.synthesizeMouseAtCenter("#onboarding-overlay-button", {}, tab.linkedBrowser);
- await promiseOnboardingOverlayOpened(tab.linkedBrowser);
-
- // Trigger UITour showHighlight and showMenu
- let highlight = document.getElementById("UITourHighlightContainer");
- let urlbarOpenPromise = promisePopupChange(gURLBar.popup, "open");
- let highlightOpenPromise = promisePopupChange(highlight, "open");
- BrowserTestUtils.synthesizeMouseAtCenter("#onboarding-tour-singlesearch", {}, tab.linkedBrowser);
- BrowserTestUtils.synthesizeMouseAtCenter("#onboarding-tour-singlesearch-button", {}, tab.linkedBrowser);
- BrowserTestUtils.synthesizeMouseAtCenter("#onboarding-tour-customize", {}, tab.linkedBrowser);
- BrowserTestUtils.synthesizeMouseAtCenter("#onboarding-tour-customize-button", {}, tab.linkedBrowser);
- await urlbarOpenPromise;
- await highlightOpenPromise;
- is(gURLBar.popup.state, "open", "Should show urlbar popup");
- is(highlight.state, "open", "Should show UITour highlight");
- is(highlight.getAttribute("targetName"), "customize", "UITour should highlight customize");
-
- // Load another page to unload the current page
- let urlbarClosePromise = promisePopupChange(gURLBar.popup, "closed");
- let highlightClosePromise = promisePopupChange(highlight, "closed");
- await BrowserTestUtils.loadURI(tab.linkedBrowser, "http://example.com");
- await urlbarClosePromise;
- await highlightClosePromise;
- is(gURLBar.popup.state, "closed", "Should close urlbar popup after page unloaded");
- is(highlight.state, "closed", "Should close UITour highlight after page unloaded");
-
- await BrowserTestUtils.removeTab(tab);
-});
-
-add_task(async function test_clean_up_uitour_on_window_resize() {
- resetOnboardingDefaultState();
- await SpecialPowers.pushPrefEnv({set: [
- ["browser.onboarding.newtour", "singlesearch,customize"],
- ]});
-
- let tab = await openTab(ABOUT_NEWTAB_URL);
- await promiseOnboardingOverlayLoaded(tab.linkedBrowser);
- await BrowserTestUtils.synthesizeMouseAtCenter("#onboarding-overlay-button", {}, tab.linkedBrowser);
- await promiseOnboardingOverlayOpened(tab.linkedBrowser);
-
- // Trigger UITour showHighlight and showMenu
- let highlight = document.getElementById("UITourHighlightContainer");
- let urlbarOpenPromise = promisePopupChange(gURLBar.popup, "open");
- let highlightOpenPromise = promisePopupChange(highlight, "open");
- BrowserTestUtils.synthesizeMouseAtCenter("#onboarding-tour-singlesearch", {}, tab.linkedBrowser);
- BrowserTestUtils.synthesizeMouseAtCenter("#onboarding-tour-singlesearch-button", {}, tab.linkedBrowser);
- BrowserTestUtils.synthesizeMouseAtCenter("#onboarding-tour-customize", {}, tab.linkedBrowser);
- BrowserTestUtils.synthesizeMouseAtCenter("#onboarding-tour-customize-button", {}, tab.linkedBrowser);
- await urlbarOpenPromise;
- await highlightOpenPromise;
- is(gURLBar.popup.state, "open", "Should show urlbar popup");
- is(highlight.state, "open", "Should show UITour highlight");
- is(highlight.getAttribute("targetName"), "customize", "UITour should highlight customize");
-
- // Resize window to destroy the onboarding tour
- const originalWidth = window.innerWidth;
- let urlbarClosePromise = promisePopupChange(gURLBar.popup, "closed");
- let highlightClosePromise = promisePopupChange(highlight, "closed");
- window.innerWidth = 300;
- await urlbarClosePromise;
- await highlightClosePromise;
- is(gURLBar.popup.state, "closed", "Should close urlbar popup after window resized");
- is(highlight.state, "closed", "Should close UITour highlight after window resized");
-
- window.innerWidth = originalWidth;
- await BrowserTestUtils.removeTab(tab);
-});
new file mode 100644
--- /dev/null
+++ b/browser/extensions/onboarding/test/browser/browser_onboarding_uitour.js
@@ -0,0 +1,129 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+requestLongerTimeout(3);
+
+function promisePopupChange(popup, expectedState) {
+ return new Promise(resolve => {
+ let event = expectedState == "open" ? "popupshown" : "popuphidden";
+ popup.addEventListener(event, resolve, { once: true });
+ });
+}
+
+async function promiseOpenOnboardingOverlay(tab) {
+ await promiseOnboardingOverlayLoaded(tab.linkedBrowser);
+ await BrowserTestUtils.synthesizeMouseAtCenter("#onboarding-overlay-button", {}, tab.linkedBrowser);
+ return promiseOnboardingOverlayOpened(tab.linkedBrowser);
+}
+
+async function triggerCustomizeUITourHighlight(tab) {
+ await promiseOpenOnboardingOverlay(tab);
+ BrowserTestUtils.synthesizeMouseAtCenter("#onboarding-tour-customize", {}, tab.linkedBrowser);
+ BrowserTestUtils.synthesizeMouseAtCenter("#onboarding-tour-customize-button", {}, tab.linkedBrowser);
+}
+
+add_task(async function test_clean_up_uitour_after_navigating_to_other_tour_by_keyboard() {
+ resetOnboardingDefaultState();
+ await SpecialPowers.pushPrefEnv({set: [
+ ["browser.onboarding.newtour", "singlesearch,customize"],
+ ]});
+
+ let tab = await openTab(ABOUT_NEWTAB_URL);
+ await promiseOpenOnboardingOverlay(tab);
+
+ // Navigate to the Customize tour to trigger UITour showHighlight
+ let highlight = document.getElementById("UITourHighlightContainer");
+ let highlightOpenPromise = promisePopupChange(highlight, "open");
+ tab.linkedBrowser.focus(); // Make sure the key event will be fired on the focused page
+ await BrowserTestUtils.synthesizeKey("VK_TAB", {}, tab.linkedBrowser);
+ await BrowserTestUtils.synthesizeKey("VK_TAB", {}, tab.linkedBrowser);
+ await BrowserTestUtils.synthesizeKey("VK_RETURN", {}, tab.linkedBrowser);
+ await BrowserTestUtils.synthesizeKey("VK_TAB", {}, tab.linkedBrowser);
+ await BrowserTestUtils.synthesizeKey("VK_RETURN", {}, tab.linkedBrowser);
+ await highlightOpenPromise;
+ is(highlight.state, "open", "Should show UITour highlight");
+ is(highlight.getAttribute("targetName"), "customize", "UITour should highlight customize");
+
+ // Navigate to the Single-Search tour
+ let highlightClosePromise = promisePopupChange(highlight, "closed");
+ tab.linkedBrowser.focus(); // Make sure the key event will be fired on the focused page
+ await BrowserTestUtils.synthesizeKey("VK_TAB", { shiftKey: true }, tab.linkedBrowser);
+ await BrowserTestUtils.synthesizeKey("VK_TAB", { shiftKey: true }, tab.linkedBrowser);
+ await BrowserTestUtils.synthesizeKey("VK_RETURN", {}, tab.linkedBrowser);
+ await highlightClosePromise;
+ is(highlight.state, "closed", "Should close UITour highlight after navigating to another tour by keyboard");
+ await BrowserTestUtils.removeTab(tab);
+});
+
+add_task(async function test_clean_up_uitour_after_navigating_to_other_tour_by_mouse() {
+ resetOnboardingDefaultState();
+ await SpecialPowers.pushPrefEnv({set: [
+ ["browser.onboarding.newtour", "singlesearch,customize"],
+ ]});
+
+ // Navigate to the Customize tour to trigger UITour showHighlight
+ let highlight = document.getElementById("UITourHighlightContainer");
+ let highlightOpenPromise = promisePopupChange(highlight, "open");
+ let tab = await openTab(ABOUT_NEWTAB_URL);
+ await triggerCustomizeUITourHighlight(tab);
+ await highlightOpenPromise;
+ is(highlight.state, "open", "Should show UITour highlight");
+ is(highlight.getAttribute("targetName"), "customize", "UITour should highlight customize");
+
+ // Navigate to the Single-Search tour
+ let highlightClosePromise = promisePopupChange(highlight, "closed");
+ BrowserTestUtils.synthesizeMouseAtCenter("#onboarding-tour-singlesearch", {}, tab.linkedBrowser);
+ await highlightClosePromise;
+ is(highlight.state, "closed", "Should close UITour highlight after navigating to another tour by mouse");
+ await BrowserTestUtils.removeTab(tab);
+});
+
+add_task(async function test_clean_up_uitour_on_page_unload() {
+ resetOnboardingDefaultState();
+ await SpecialPowers.pushPrefEnv({set: [
+ ["browser.onboarding.newtour", "singlesearch,customize"],
+ ]});
+
+ // Trigger UITour showHighlight
+ let highlight = document.getElementById("UITourHighlightContainer");
+ let highlightOpenPromise = promisePopupChange(highlight, "open");
+ let tab = await openTab(ABOUT_NEWTAB_URL);
+ await triggerCustomizeUITourHighlight(tab);
+ await highlightOpenPromise;
+ is(highlight.state, "open", "Should show UITour highlight");
+ is(highlight.getAttribute("targetName"), "customize", "UITour should highlight customize");
+
+ // Load another page to unload the current page
+ let highlightClosePromise = promisePopupChange(highlight, "closed");
+ await BrowserTestUtils.loadURI(tab.linkedBrowser, "http://example.com");
+ await highlightClosePromise;
+ is(highlight.state, "closed", "Should close UITour highlight after page unloaded");
+ await BrowserTestUtils.removeTab(tab);
+});
+
+add_task(async function test_clean_up_uitour_on_window_resize() {
+ resetOnboardingDefaultState();
+ await SpecialPowers.pushPrefEnv({set: [
+ ["browser.onboarding.newtour", "singlesearch,customize"],
+ ]});
+
+ // Trigger UITour showHighlight
+ let highlight = document.getElementById("UITourHighlightContainer");
+ let highlightOpenPromise = promisePopupChange(highlight, "open");
+ let tab = await openTab(ABOUT_NEWTAB_URL);
+ await triggerCustomizeUITourHighlight(tab);
+ await highlightOpenPromise;
+ is(highlight.state, "open", "Should show UITour highlight");
+ is(highlight.getAttribute("targetName"), "customize", "UITour should highlight customize");
+
+ // Resize window to destroy the onboarding tour
+ const originalWidth = window.innerWidth;
+ let highlightClosePromise = promisePopupChange(highlight, "closed");
+ window.innerWidth = 300;
+ await highlightClosePromise;
+ is(highlight.state, "closed", "Should close UITour highlight after window resized");
+ window.innerWidth = originalWidth;
+ await BrowserTestUtils.removeTab(tab);
+});