Bug 1377439 - Should adapt the oboarding UI to the hight-contrast display mode, r=mossop draft
authorFischer.json <fischer.json@gmail.com>
Tue, 18 Jul 2017 14:26:25 +0800
changeset 612205 a934639cb587aa85559359c7915fba6b5e9256db
parent 612204 9f2a223193f0c869f5841affe7a5399c7d8750b8
child 638351 bc0b2b8efc9e5245729d3b2d96163dc58810696a
push id69431
push userbmo:fliu@mozilla.com
push dateThu, 20 Jul 2017 14:08:34 +0000
reviewersmossop
bugs1377439
milestone56.0a1
Bug 1377439 - Should adapt the oboarding UI to the hight-contrast display mode, r=mossop This patch makes the mininmum and the important elements and images visible in the high-constrast mode, including - making button have border (using the given default grey border in the high-constrast mode) - making the upper-left overlay button's fox image visible - making the images of the X close buttons on the notification bar and on the overlay visible - making that the navigation item of the current tour on the overlay's right side would have outline - making the hovered navigation item on the overlay's right side would have outline - making sure using <button> element for buttons for a better semantic - changing #onboarding-overlay-icon to #onboarding-overlay-button for a better semantic MozReview-Commit-ID: Aj6wndb4to9
browser/base/content/test/newtab/browser_newtab_focus.js
browser/extensions/onboarding/content/onboarding.css
browser/extensions/onboarding/content/onboarding.js
browser/extensions/onboarding/test/browser/browser_onboarding_tours.js
browser/extensions/onboarding/test/browser/browser_onboarding_tourset.js
--- a/browser/base/content/test/newtab/browser_newtab_focus.js
+++ b/browser/base/content/test/newtab/browser_newtab_focus.js
@@ -3,17 +3,17 @@
 
 /*
  * These tests make sure that focusing the 'New Tab Page' works as expected.
  */
 add_task(async function() {
   await pushPrefs(["accessibility.tabfocus", 7]);
 
   // When the onboarding component is enabled, it would inject extra tour notification into
-  // the newtab page so there would be 2 more notification close button and action button
+  // the newtab page so there would be 3 more overlay button, notification close button and action button
   let onbardingEnabled = AppConstants.NIGHTLY_BUILD && Services.prefs.getBoolPref("browser.onboarding.enabled");
 
   // Focus count in new tab page.
   // 30 = 9 * 3 + 3 = 9 sites, each with link, pin and remove buttons; search
   // bar; search button; and toggle button. Additionaly there may or may not be
   // a scroll bar caused by fix to 1180387, which will eat an extra focus
   let FOCUS_COUNT = 30;
 
@@ -21,28 +21,28 @@ add_task(async function() {
   await setLinks("0,1,2,3,4,5,6,7,8");
   setPinnedLinks("");
 
   if (onbardingEnabled) {
     await promiseNoMuteNotificationOnFirstSession();
   }
   let tab = await addNewTabPageTab();
   if (onbardingEnabled) {
-    FOCUS_COUNT += 2;
+    FOCUS_COUNT += 3;
     await promiseTourNotificationOpened(tab.linkedBrowser);
   }
   gURLBar.focus();
   // Count the focus with the enabled page.
   countFocus(FOCUS_COUNT);
   // Disable page and count the focus with the disabled page.
   NewTabUtils.allPages.enabled = false;
 
   let expectedCount = 4;
   if (onbardingEnabled) {
-    expectedCount += 2;
+    expectedCount += 3;
   }
   countFocus(expectedCount);
 
   NewTabUtils.allPages.enabled = true;
 });
 
 /**
  * Focus the urlbar and count how many focus stops to return again to the urlbar.
--- a/browser/extensions/onboarding/content/onboarding.css
+++ b/browser/extensions/onboarding/content/onboarding.css
@@ -18,74 +18,84 @@
   background: rgb(54, 57, 89, 0.8); /* #363959, 0.8 opacity */
   display: none;
 }
 
 #onboarding-overlay.onboarding-opened {
   display: block;
 }
 
-#onboarding-overlay-icon {
-  width: 36px;
-  height: 29px;
+#onboarding-overlay-button {
   position: absolute;
   cursor: pointer;
   top: 30px;
   offset-inline-start: 30px;
-  background: url("img/overlay-icon.svg") no-repeat;
+  border: none;
+  /* Set to none so no grey contrast background in the high-contrast mode */
+  background: none;
+}
+
+#onboarding-overlay-button-icon {
+  width: 36px;
 }
 
 #onboarding-notification-icon::after,
-#onboarding-overlay-icon::after {
+#onboarding-overlay-button::after {
   background: #5ce6e6;
   position: absolute;
   font-size: 12px;
   border: 1px solid #fff;
   text-align: center;
   color: #10404a;
   box-sizing: content-box;
 }
 
-#onboarding-overlay-icon::after {
+#onboarding-overlay-button::after {
   content: attr(aria-label);
   top: -6px;
   offset-inline-start: 39px;
   border-radius: 22px;
   padding: 5px 8px;
   min-width: 100px;
 }
 
 #onboarding-overlay-dialog,
 .onboarding-hidden {
   display: none;
 }
 
-#onboarding-overlay-close-btn,
-#onboarding-notification-close-btn {
-  position: absolute;
-  top: 15px;
-  offset-inline-end: 15px;
-  cursor: pointer;
-  width: 16px;
-  height: 16px;
-  background-image: url(chrome://browser/skin/sidebar/close.svg);
-  background-position: center center;
-  background-repeat: no-repeat;
-  padding: 12px;
+.onboarding-close-btn {
+   position: absolute;
+   top: 15px;
+   offset-inline-end: 15px;
+   cursor: pointer;
+   width: 16px;
+   height: 16px;
+   padding: 12px;
+   border: none;
+   background: var(--onboarding-overlay-dialog-background-color);
+ }
+
+.onboarding-close-btn::before {
+  content: url(chrome://browser/skin/sidebar/close.svg);
+  display: block;
+  margin-top: -8px;
+  margin-inline-start: -8px;
 }
 
-#onboarding-overlay-close-btn:hover,
+.onboarding-close-btn:hover,
 #onboarding-notification-close-btn:hover {
   background-color: rgba(204, 204, 204, 0.6);
 }
 
 #onboarding-overlay.onboarding-opened > #onboarding-overlay-dialog {
+  --onboarding-overlay-dialog-background-color: #f5f5f7;
   width: 960px;
   height: 510px;
-  background: #f5f5f7;
+  background: var(--onboarding-overlay-dialog-background-color);
   border: 1px solid rgba(9, 6, 13, 0.1); /* #09060D, 0.1 opacity */
   border-radius: 3px;
   position: relative;
   margin: 0 calc(50% - 480px);
   top: calc(50% - 255px);
   display: grid;
   grid-template-rows: [dialog-start] 70px [page-start] 1fr [footer-start] 30px [dialog-end];
   grid-template-columns: [dialog-start] 230px [page-start] 1fr [dialog-end];
@@ -131,20 +141,23 @@
 
 /* Onboarding tour list */
 #onboarding-tour-list {
   margin: 40px 0 0 0;
   padding: 0;
 }
 
 #onboarding-tour-list > li {
+  --padding-inline-start: 49px;
+  --padding-top: 14px;
+  --padding-bottom: 14px;
   list-style: none;
-  padding-inline-start: 49px;
-  padding-top: 14px;
-  padding-bottom: 14px;
+  padding-inline-start: var(--padding-inline-start);
+  padding-top: var(--padding-top);
+  padding-bottom: var(--padding-bottom);
   margin-inline-start: 16px;
   margin-bottom: 9px;
   background-repeat: no-repeat;
   background-position: left 17px top 14px;
   background-size: 20px;
   font-size: 16px;
   cursor: pointer;
 }
@@ -157,22 +170,29 @@
   content: url("img/icons_tour-complete.svg");
   position: relative;
   offset-inline-start: 3px;
   top: -10px;
   float: inline-start;
 }
 
 #onboarding-tour-list > li.onboarding-complete {
-  padding-inline-start: 29px;
+  --padding-inline-start: 29px;
 }
 
 #onboarding-tour-list > li.onboarding-active,
 #onboarding-tour-list > li:hover {
   color: #0A84FF;
+  /* With 1px transparent border, could see a border in the high-constrast mode */
+  border: 1px solid transparent;
+  /* Substract 1px for the 1px transparent or a 1px shift would happen */
+  padding-inline-start: calc(var(--padding-inline-start) - 1px);
+  padding-top: calc(var(--padding-top) - 1px);
+  padding-bottom: calc(var(--padding-bottom) - 1px);
+  background-color: #fff;
 }
 
 /* Default browser tour */
 #onboarding-tour-is-default-browser-msg {
   font-size: 16px;
   line-height: 21px;
   float: inline-end;
   margin-inline-end: 26px;
@@ -279,17 +299,18 @@
 }
 
 .onboarding-tour-action-button {
   padding: 10px 20px;
   font-size: 15px;
   font-weight: 600;
   line-height: 21px;
   background: #0a84ff;
-  border: none;
+  /* With 1px transparent border, could see a border in the high-constrast mode */
+  border: 1px solid transparent;
   border-radius: 0;
   color: #fff;
   float: inline-end;
   margin-inline-end: 26px;
   margin-top: -32px;
 }
 
 .onboarding-tour-action-button:hover:not([disabled]) ,
@@ -384,24 +405,25 @@
 }
 
 #onboarding-notification-bar[data-target-tour-id=onboarding-tour-sync] #onboarding-notification-tour-icon {
   background-image: url("img/icons_sync-notification.svg");
 }
 
 /* Tour Notifications */
 #onboarding-notification-bar {
+  --onboarding-notification-bar-background-color: rgba(255, 255, 255, 0.97);
   position: fixed;
   z-index: 20998; /* We want this always under #onboarding-overlay */
   left: 0;
   bottom: 0;
   width: 100%;
   height: 122px;
   min-width: 640px;
-  background: rgba(255, 255, 255, 0.97);
+  background: var(--onboarding-notification-bar-background-color);
   border-top: 2px solid #e9e9e9;
   transition: transform 0.8s;
   transform: translateY(122px);
 }
 
 #onboarding-notification-bar.onboarding-opened {
   transition: none;
   transform: translateY(0px);
@@ -429,18 +451,17 @@
   top: 0;
   offset-inline-start: 73px;
   line-height: calc(var(--height) - var(--vpadding) * 2);
   border-radius: calc(var(--height) / 2);
   padding: var(--vpadding) 10px;
 }
 
 #onboarding-notification-close-btn {
-  background-color: rgba(255, 255, 255, 0.97);
-  border: none;
+  background: var(--onboarding-notification-bar-background-color);
   position: absolute;
   offset-block-start: 50%;
   offset-inline-end: 34px;
   transform: translateY(-50%);
 }
 
 #onboarding-notification-message-section {
   height: 100%;
@@ -475,17 +496,18 @@
   min-width: 64px;
   height: 64px;
   background-size: 64px;
   background-repeat: no-repeat;
 }
 
 #onboarding-notification-action-btn {
   background: #0a84ff;
-  border: none;
+  /* With 1px transparent border, could see a border in the high-constrast mode */
+  border: 1px solid transparent;
   border-radius: 0;
   padding: 10px 20px;
   font-size: 14px;
   color: #fff;
 }
 
 @media all and (max-width: 960px) {
   #onboarding-notification-icon {
--- a/browser/extensions/onboarding/content/onboarding.js
+++ b/browser/extensions/onboarding/content/onboarding.js
@@ -280,17 +280,17 @@ class Onboarding {
   _initUI() {
     if (this.uiInitialized) {
       return;
     }
     this.uiInitialized = true;
     this._tourItems = [];
     this._tourPages = [];
 
-    this._overlayIcon = this._renderOverlayIcon();
+    this._overlayIcon = this._renderOverlayButton();
     this._overlayIcon.addEventListener("click", this);
     this._window.document.body.appendChild(this._overlayIcon);
 
     this._overlay = this._renderOverlay();
     this._overlay.addEventListener("click", this);
     this._window.document.body.appendChild(this._overlay);
 
     this._loadJS(TOUR_AGENT_JS_URI);
@@ -369,17 +369,17 @@ class Onboarding {
       this._window.cancelIdleCallback(this._resizeTimerId);
       this._resizeTimerId =
         this._window.requestIdleCallback(() => this._resizeUI());
 
       return;
     }
 
     switch (evt.target.id) {
-      case "onboarding-overlay-icon":
+      case "onboarding-overlay-button":
       case "onboarding-overlay-close-btn":
       // If the clicking target is directly on the outer-most overlay,
       // that means clicking outside the tour content area.
       // Let's toggle the overlay.
       case "onboarding-overlay":
         this.toggleOverlay();
         break;
       case "onboarding-notification-close-btn":
@@ -648,17 +648,17 @@ class Onboarding {
       <section id="onboarding-notification-message-section">
         <div id="onboarding-notification-tour-icon"></div>
         <div id="onboarding-notification-body">
           <h6 id="onboarding-notification-tour-title"></h6>
           <span id="onboarding-notification-tour-message"></span>
         </div>
         <button id="onboarding-notification-action-btn"></button>
       </section>
-      <button id="onboarding-notification-close-btn"></button>
+      <button id="onboarding-notification-close-btn" class="onboarding-close-btn"></button>
     `;
     let toolTip = this._bundle.formatStringFromName(
       this._tourType === "new" ? "onboarding.notification-icon-tool-tip" :
                                  "onboarding.notification-icon-tooltip-updated",
       [BRAND_SHORT_NAME], 1);
     div.querySelector("#onboarding-notification-icon").setAttribute("data-tooltip", toolTip);
     return div;
   }
@@ -679,17 +679,17 @@ class Onboarding {
 
   _renderOverlay() {
     let div = this._window.document.createElement("div");
     div.id = "onboarding-overlay";
     // We use `innerHTML` for more friendly reading.
     // The security should be fine because this is not from an external input.
     div.innerHTML = `
       <div id="onboarding-overlay-dialog">
-        <span id="onboarding-overlay-close-btn"></span>
+        <button id="onboarding-overlay-close-btn" class="onboarding-close-btn"></button>
         <header id="onboarding-header"></header>
         <nav>
           <ul id="onboarding-tour-list"></ul>
         </nav>
         <footer id="onboarding-footer">
           <input type="checkbox" id="onboarding-tour-hidden-checkbox" /><label for="onboarding-tour-hidden-checkbox"></label>
         </footer>
       </div>
@@ -697,26 +697,28 @@ class Onboarding {
 
     div.querySelector("label[for='onboarding-tour-hidden-checkbox']").textContent =
        this._bundle.GetStringFromName("onboarding.hidden-checkbox-label-text");
     div.querySelector("#onboarding-header").textContent =
        this._bundle.GetStringFromName("onboarding.overlay-title2");
     return div;
   }
 
-  _renderOverlayIcon() {
-    let img = this._window.document.createElement("div");
-    let tooltip = this._bundle.formatStringFromName(
-      this._tourType === "new" ? "onboarding.overlay-icon-tooltip" :
-                                 "onboarding.overlay-icon-tooltip-updated",
-      [BRAND_SHORT_NAME], 1);
-
-    img.setAttribute("aria-label", tooltip);
-    img.id = "onboarding-overlay-icon";
-    return img;
+  _renderOverlayButton() {
+    let button = this._window.document.createElement("button");
+    let tooltipStringId = this._tourType === "new" ?
+      "onboarding.overlay-icon-tooltip" : "onboarding.overlay-icon-tooltip-updated";
+    let tooltip = this._bundle.formatStringFromName(tooltipStringId, [BRAND_SHORT_NAME], 1);
+    button.setAttribute("aria-label", tooltip);
+    button.id = "onboarding-overlay-button";
+    let img = this._window.document.createElement("img");
+    img.id = "onboarding-overlay-button-icon";
+    img.src = "resource://onboarding/img/overlay-icon.svg";
+    button.appendChild(img);
+    return button;
   }
 
   _loadTours(tours) {
     let itemsFrag = this._window.document.createDocumentFragment();
     let pagesFrag = this._window.document.createDocumentFragment();
     for (let tour of tours) {
       // Create tour navigation items dynamically
       let li = this._window.document.createElement("li");
--- a/browser/extensions/onboarding/test/browser/browser_onboarding_tours.js
+++ b/browser/extensions/onboarding/test/browser/browser_onboarding_tours.js
@@ -2,17 +2,17 @@
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
  "use strict";
 
 function assertOnboardingDestroyed(browser) {
   return ContentTask.spawn(browser, {}, function() {
     let expectedRemovals = [
       "#onboarding-overlay",
-      "#onboarding-overlay-icon"
+      "#onboarding-overlay-button"
     ];
     for (let selector of expectedRemovals) {
       let removal = content.document.querySelector(selector);
       ok(!removal, `Should remove ${selector} onboarding element`);
     }
   });
 }
 
@@ -31,17 +31,17 @@ add_task(async function test_hide_onboar
   resetOnboardingDefaultState();
 
   let tourIds = TOUR_IDs;
   let tabs = [];
   for (let url of URLs) {
     let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser);
     await BrowserTestUtils.loadURI(tab.linkedBrowser, url);
     await promiseOnboardingOverlayLoaded(tab.linkedBrowser);
-    await BrowserTestUtils.synthesizeMouseAtCenter("#onboarding-overlay-icon", {}, tab.linkedBrowser);
+    await BrowserTestUtils.synthesizeMouseAtCenter("#onboarding-overlay-button", {}, tab.linkedBrowser);
     await promiseOnboardingOverlayOpened(tab.linkedBrowser);
     tabs.push(tab);
   }
 
   let expectedPrefUpdates = [
     promisePrefUpdated("browser.onboarding.hidden", true),
     promisePrefUpdated("browser.onboarding.notification.finished", true)
   ];
@@ -62,17 +62,17 @@ add_task(async function test_click_actio
   resetOnboardingDefaultState();
 
   let tourIds = TOUR_IDs;
   let tabs = [];
   for (let url of URLs) {
     let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser);
     await BrowserTestUtils.loadURI(tab.linkedBrowser, url);
     await promiseOnboardingOverlayLoaded(tab.linkedBrowser);
-    await BrowserTestUtils.synthesizeMouseAtCenter("#onboarding-overlay-icon", {}, tab.linkedBrowser);
+    await BrowserTestUtils.synthesizeMouseAtCenter("#onboarding-overlay-button", {}, tab.linkedBrowser);
     await promiseOnboardingOverlayOpened(tab.linkedBrowser);
     tabs.push(tab);
   }
 
   let completedTourId = tourIds[0];
   let expectedPrefUpdate = promisePrefUpdated(`browser.onboarding.tour.${completedTourId}.completed`, true);
   await BrowserTestUtils.synthesizeMouseAtCenter(`#${completedTourId}-page .onboarding-tour-action-button`, {}, gBrowser.selectedBrowser);
   await expectedPrefUpdate;
@@ -96,17 +96,17 @@ add_task(async function test_set_right_t
     setTourCompletedState(tourIds[i], i % 2 == 0);
   }
 
   let tabs = [];
   for (let url of URLs) {
     let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser);
     await BrowserTestUtils.loadURI(tab.linkedBrowser, url);
     await promiseOnboardingOverlayLoaded(tab.linkedBrowser);
-    await BrowserTestUtils.synthesizeMouseAtCenter("#onboarding-overlay-icon", {}, tab.linkedBrowser);
+    await BrowserTestUtils.synthesizeMouseAtCenter("#onboarding-overlay-button", {}, tab.linkedBrowser);
     await promiseOnboardingOverlayOpened(tab.linkedBrowser);
     tabs.push(tab);
   }
 
   for (let i = tabs.length - 1; i >= 0; --i) {
     let tab = tabs[i];
     for (let j = 0; j < tourIds.length; ++j) {
       await assertTourCompletedStyle(tourIds[j], j % 2 == 0, tab.linkedBrowser);
--- a/browser/extensions/onboarding/test/browser/browser_onboarding_tourset.js
+++ b/browser/extensions/onboarding/test/browser/browser_onboarding_tourset.js
@@ -5,17 +5,17 @@
 
 add_task(async function test_onboarding_default_new_tourset() {
   resetOnboardingDefaultState();
   let tabs = [];
   for (let url of URLs) {
     let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser);
     await BrowserTestUtils.loadURI(tab.linkedBrowser, url);
     await promiseOnboardingOverlayLoaded(tab.linkedBrowser);
-    await BrowserTestUtils.synthesizeMouseAtCenter("#onboarding-overlay-icon", {}, tab.linkedBrowser);
+    await BrowserTestUtils.synthesizeMouseAtCenter("#onboarding-overlay-button", {}, tab.linkedBrowser);
     await promiseOnboardingOverlayOpened(tab.linkedBrowser);
     tabs.push(tab);
   }
 
   let doc = content && content.document;
   let doms = doc.querySelectorAll(".onboarding-tour-item");
   is(doms.length, TOUR_IDs.length, "has exact tour numbers");
   doms.forEach((dom, idx) => {
@@ -43,17 +43,17 @@ add_task(async function test_onboarding_
     ["browser.onboarding.newtour", "private,addons,customize"],
   ]});
 
   let tabs = [];
   for (let url of URLs) {
     let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser);
     await BrowserTestUtils.loadURI(tab.linkedBrowser, url);
     await promiseOnboardingOverlayLoaded(tab.linkedBrowser);
-    await BrowserTestUtils.synthesizeMouseAtCenter("#onboarding-overlay-icon", {}, tab.linkedBrowser);
+    await BrowserTestUtils.synthesizeMouseAtCenter("#onboarding-overlay-button", {}, tab.linkedBrowser);
     await promiseOnboardingOverlayOpened(tab.linkedBrowser);
     tabs.push(tab);
   }
 
   let doc = content && content.document;
   let doms = doc.querySelectorAll(".onboarding-tour-item");
   is(doms.length, CUSTOM_NEW_TOURs.length, "has exact tour numbers");
   doms.forEach((dom, idx) => {
@@ -80,17 +80,17 @@ add_task(async function test_onboarding_
     ["browser.onboarding.updatetour", "customize,private,addons"],
   ]});
 
   let tabs = [];
   for (let url of URLs) {
     let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser);
     await BrowserTestUtils.loadURI(tab.linkedBrowser, url);
     await promiseOnboardingOverlayLoaded(tab.linkedBrowser);
-    await BrowserTestUtils.synthesizeMouseAtCenter("#onboarding-overlay-icon", {}, tab.linkedBrowser);
+    await BrowserTestUtils.synthesizeMouseAtCenter("#onboarding-overlay-button", {}, tab.linkedBrowser);
     await promiseOnboardingOverlayOpened(tab.linkedBrowser);
     tabs.push(tab);
   }
 
   let doc = content && content.document;
   let doms = doc.querySelectorAll(".onboarding-tour-item");
   is(doms.length, CUSTOM_UPDATE_TOURs.length, "has exact tour numbers");
   doms.forEach((dom, idx) => {