Bug 1377439 - Should adapt the oboarding UI to the hight-contrast display mode, r=mossop
authorFischer.json <fischer.json@gmail.com>
Tue, 18 Jul 2017 14:26:25 +0800
changeset 369946 e9b1c60ebd4f
parent 369945 aeb68364f85e
child 369947 0c5cf70baa50
push id46882
push userryanvm@gmail.com
push date2017-07-20 17:28 +0000
treeherderautoland@e9b1c60ebd4f [default view] [failures only]
reviewersmossop
bugs1377439
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 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]) ,
@@ -413,24 +434,25 @@
 #onboarding-notification-bar[data-target-tour-id=onboarding-tour-performance] #onboarding-notification-tour-icon {
   /* TODO: Placeholder icon. It should be replaced upon assets are available.
            This is tracking in Bug 1382520. */
   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);
@@ -458,18 +480,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%;
@@ -504,17 +525,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
@@ -360,17 +360,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);
@@ -449,17 +449,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":
@@ -734,17 +734,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;
   }
@@ -765,17 +765,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>
@@ -783,26 +783,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) => {