Bug 1422106 - Show broken heart when unverified in synced tabs sidebar/panel. r=markh
☠☠ backed out by ecc5c1b8efc4 ☠ ☠
authorEdouard Oger <eoger@fastmail.com>
Thu, 30 Nov 2017 16:01:40 -0500
changeset 394714 1ca98c1618a65139d690b475a0ff9fd1160679fb
parent 394713 f59a06c4253d1793b7392aea391030ffdc51ae0a
child 394715 1e69e9b141a2bbaa15c63d4ab5d294a9482b68a9
push id33019
push userbtara@mozilla.com
push dateMon, 04 Dec 2017 20:16:32 +0000
treeherdermozilla-central@4a003542df78 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmarkh
bugs1422106
milestone59.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 1422106 - Show broken heart when unverified in synced tabs sidebar/panel. r=markh MozReview-Commit-ID: BDTdmcIOHmn
browser/base/content/browser-menubar.inc
browser/base/content/browser-sets.inc
browser/base/content/browser-sync.js
browser/base/content/test/sync/browser_sync.js
browser/components/customizableui/content/panelUI.inc.xul
browser/components/customizableui/test/browser_synced_tabs_menu.js
browser/components/syncedtabs/SyncedTabsDeckComponent.js
browser/components/syncedtabs/sidebar.xhtml
browser/components/syncedtabs/test/browser/browser_sidebar_syncedtabslist.js
browser/components/syncedtabs/test/xpcshell/test_SyncedTabsDeckComponent.js
browser/locales/en-US/chrome/browser/browser.dtd
browser/modules/test/browser/browser_BrowserUITelemetry_syncedtabs.js
browser/themes/shared/customizableui/panelUI.inc.css
--- a/browser/base/content/browser-menubar.inc
+++ b/browser/base/content/browser-menubar.inc
@@ -472,22 +472,27 @@
                         key="key_openDownloads"
                         command="Tools:Downloads"/>
               <menuitem id="menu_openAddons"
                         label="&addons.label;"
                         accesskey="&addons.accesskey;"
                         key="key_openAddons"
                         command="Tools:Addons"/>
 
-              <!-- only one of sync-setup, sync-syncnowitem or sync-reauthitem will be showing at once -->
+              <!-- only one of sync-setup, sync-unverifieditem, sync-syncnowitem or sync-reauthitem will be showing at once -->
               <menuitem id="sync-setup"
                         label="&syncSignIn.label;"
                         accesskey="&syncSignIn.accesskey;"
                         observes="sync-setup-state"
                         oncommand="gSync.openPrefs('menubar')"/>
+              <menuitem id="sync-unverifieditem"
+                        label="&syncSignIn.label;"
+                        accesskey="&syncSignIn.accesskey;"
+                        observes="sync-unverified-state"
+                        oncommand="gSync.openPrefs('menubar')"/>
               <menuitem id="sync-syncnowitem"
                         label="&syncSyncNowItem.label;"
                         accesskey="&syncSyncNowItem.accesskey;"
                         observes="sync-syncnow-state"
                         oncommand="gSync.doSync(event);"/>
               <menuitem id="sync-reauthitem"
                         label="&syncReAuthItem.label;"
                         accesskey="&syncReAuthItem.accesskey;"
--- a/browser/base/content/browser-sets.inc
+++ b/browser/base/content/browser-sets.inc
@@ -161,16 +161,17 @@
     <!-- Sync broadcasters -->
     <!-- A broadcaster of a number of attributes suitable for "sync now" UI -
         A 'syncstatus' attribute is set while actively syncing, and the label
         attribute which changes from "sync now" to "syncing" etc. -->
     <broadcaster id="sync-status"/>
     <!-- broadcasters of the "hidden" attribute to reflect setup state for
          menus -->
     <broadcaster id="sync-setup-state"/>
+    <broadcaster id="sync-unverified-state" hidden="true"/>
     <broadcaster id="sync-syncnow-state" hidden="true"/>
     <broadcaster id="sync-reauth-state" hidden="true"/>
     <broadcaster id="viewTabsSidebar" autoCheck="false" sidebartitle="&syncedTabs.sidebar.label;"
                  type="checkbox" group="sidebar"
                  sidebarurl="chrome://browser/content/syncedtabs/sidebar.xhtml"
                  oncommand="SidebarUI.toggle('viewTabsSidebar');"/>
     <broadcaster id="workOfflineMenuitemState"/>
 
--- a/browser/base/content/browser-sync.js
+++ b/browser/base/content/browser-sync.js
@@ -213,23 +213,25 @@ var gSync = {
 
   updateStateBroadcasters(state) {
     const status = state.status;
 
     // Start off with a clean slate
     document.getElementById("sync-reauth-state").hidden = true;
     document.getElementById("sync-setup-state").hidden = true;
     document.getElementById("sync-syncnow-state").hidden = true;
+    document.getElementById("sync-unverified-state").hidden = true;
 
     if (status == UIState.STATUS_LOGIN_FAILED) {
       // unhiding this element makes the menubar show the login failure state.
       document.getElementById("sync-reauth-state").hidden = false;
-    } else if (status == UIState.STATUS_NOT_CONFIGURED ||
-               status == UIState.STATUS_NOT_VERIFIED) {
+    } else if (status == UIState.STATUS_NOT_CONFIGURED) {
       document.getElementById("sync-setup-state").hidden = false;
+    } else if (status == UIState.STATUS_NOT_VERIFIED) {
+      document.getElementById("sync-unverified-state").hidden = false;
     } else {
       document.getElementById("sync-syncnow-state").hidden = false;
     }
   },
 
   updateSyncStatus(state) {
     const broadcaster = document.getElementById("sync-status");
     const syncingUI = broadcaster.getAttribute("syncstatus") == "active";
--- a/browser/base/content/test/sync/browser_sync.js
+++ b/browser/base/content/test/sync/browser_sync.js
@@ -103,18 +103,18 @@ add_task(async function test_ui_state_un
   checkPanelUIStatusBar({
     label: expectedLabel,
     tooltip: tooltipText,
     fxastatus: "unverified",
     avatarURL: null,
     syncing: false,
     syncNowTooltip: tooltipText
   });
-  checkRemoteTabsPanel("PanelUI-remotetabs-setupsync", false);
-  checkMenuBarItem("sync-setup");
+  checkRemoteTabsPanel("PanelUI-remotetabs-unverified", false);
+  checkMenuBarItem("sync-unverifieditem");
 });
 
 add_task(async function test_ui_state_loginFailed() {
   let state = {
     status: UIState.STATUS_LOGIN_FAILED,
     email: "foo@bar.com"
   };
 
@@ -171,26 +171,27 @@ function checkPanelUIStatusBar({label, t
   if (syncing != undefined && syncNowTooltip != undefined) {
     checkSyncNowButton("appMenu-fxa-icon", syncing, syncNowTooltip);
   }
 }
 
 function checkRemoteTabsPanel(expectedShownItemId, syncing, syncNowTooltip) {
   checkItemsVisiblities(["PanelUI-remotetabs-main",
                          "PanelUI-remotetabs-setupsync",
-                         "PanelUI-remotetabs-reauthsync"],
+                         "PanelUI-remotetabs-reauthsync",
+                         "PanelUI-remotetabs-unverified"],
                         expectedShownItemId);
 
   if (syncing != undefined && syncNowTooltip != undefined) {
     checkSyncNowButton("PanelUI-remotetabs-syncnow", syncing, syncNowTooltip);
   }
 }
 
 function checkMenuBarItem(expectedShownItemId) {
-  checkItemsVisiblities(["sync-setup", "sync-syncnowitem", "sync-reauthitem"],
+  checkItemsVisiblities(["sync-setup", "sync-syncnowitem", "sync-reauthitem", "sync-unverifieditem"],
                         expectedShownItemId);
 }
 
 function checkSyncNowButton(buttonId, syncing, tooltip = null) {
   const remoteTabsButton = document.getElementById(buttonId);
 
   is(remoteTabsButton.getAttribute("syncstatus"), syncing ? "active" : "", "button active has the right value");
   if (tooltip) {
--- a/browser/components/customizableui/content/panelUI.inc.xul
+++ b/browser/components/customizableui/content/panelUI.inc.xul
@@ -486,16 +486,27 @@
                 class="PanelUI-remotetabs-instruction-box"
                 observes="sync-reauth-state">
             <image class="fxaSyncIllustrationIssue"/>
             <label class="PanelUI-remotetabs-instruction-label">&appMenuRemoteTabs.notsignedin.label;</label>
             <toolbarbutton class="PanelUI-remotetabs-button"
                            label="&appMenuRemoteTabs.signin.label;"
                            oncommand="gSync.openPrefs('synced-tabs');"/>
           </vbox>
+          <vbox id="PanelUI-remotetabs-unverified"
+                flex="1"
+                align="center"
+                class="PanelUI-remotetabs-instruction-box"
+                observes="sync-unverified-state">
+            <image class="fxaSyncIllustrationIssue"/>
+            <label class="PanelUI-remotetabs-instruction-label">&appMenuRemoteTabs.unverified.label;</label>
+            <toolbarbutton class="PanelUI-remotetabs-prefs-button"
+                           label="&appMenuRemoteTabs.signin.label;"
+                           oncommand="gSync.openPrefs('synced-tabs');"/>
+          </vbox>
         </hbox>
       </vbox>
     </panelview>
 
     <panelview id="PanelUI-bookmarks" flex="1" class="PanelUI-subView">
       <vbox class="panel-subview-body">
         <toolbarbutton id="panelMenuBookmarkThisPage"
                        class="subviewbutton subviewbutton-iconic"
--- a/browser/components/customizableui/test/browser_synced_tabs_menu.js
+++ b/browser/components/customizableui/test/browser_synced_tabs_menu.js
@@ -134,16 +134,23 @@ async function asyncCleanup() {
 
 // When Sync is not setup.
 add_task(async function() {
   gSync.updateAllUI({ status: UIState.STATUS_NOT_CONFIGURED });
   await openPrefsFromMenuPanel("PanelUI-remotetabs-setupsync", "synced-tabs");
 });
 add_task(asyncCleanup);
 
+// When Sync is configured in an unverified state.
+add_task(async function() {
+  gSync.updateAllUI({ status: UIState.STATUS_NOT_VERIFIED, email: "foo@bar.com" });
+  await openPrefsFromMenuPanel("PanelUI-remotetabs-unverified", "synced-tabs");
+});
+add_task(asyncCleanup);
+
 // When Sync is configured in a "needs reauthentication" state.
 add_task(async function() {
   gSync.updateAllUI({ status: UIState.STATUS_LOGIN_FAILED, email: "foo@bar.com" });
   await openPrefsFromMenuPanel("PanelUI-remotetabs-reauthsync", "synced-tabs");
 });
 
 // Test the Connect Another Device button
 add_task(async function() {
--- a/browser/components/syncedtabs/SyncedTabsDeckComponent.js
+++ b/browser/components/syncedtabs/SyncedTabsDeckComponent.js
@@ -58,16 +58,17 @@ function SyncedTabsDeckComponent({
 
 SyncedTabsDeckComponent.prototype = {
   PANELS: {
     TABS_CONTAINER: "tabs-container",
     TABS_FETCHING: "tabs-fetching",
     NOT_AUTHED_INFO: "notAuthedInfo",
     SINGLE_DEVICE_INFO: "singleDeviceInfo",
     TABS_DISABLED: "tabs-disabled",
+    UNVERIFIED: "unverified"
   },
 
   get container() {
     return this._deckView ? this._deckView.container : null;
   },
 
   init() {
     Services.obs.addObserver(this, this._SyncedTabs.TOPIC_TABS_CHANGED);
@@ -116,19 +117,22 @@ SyncedTabsDeckComponent.prototype = {
   // There's no good way to mock fxAccounts in browser tests where it's already
   // been instantiated, so we have this method for stubbing.
   _getSignedInUser() {
     return this._fxAccounts.getSignedInUser();
   },
 
   getPanelStatus() {
     return this._getSignedInUser().then(user => {
-      if (!user || !user.verified || this._SyncedTabs.loginFailed) {
+      if (!user || this._SyncedTabs.loginFailed) {
         return this.PANELS.NOT_AUTHED_INFO;
       }
+      if (!user.verified) {
+        return this.PANELS.UNVERIFIED;
+      }
       if (!this._SyncedTabs.isConfiguredToSyncTabs) {
         return this.PANELS.TABS_DISABLED;
       }
       if (!this._SyncedTabs.hasSyncedThisSession) {
         return this.PANELS.TABS_FETCHING;
       }
       return this._SyncedTabs.getTabClients().then(clients => {
         if (clients.length) {
--- a/browser/components/syncedtabs/sidebar.xhtml
+++ b/browser/components/syncedtabs/sidebar.xhtml
@@ -75,16 +75,21 @@
         <div class="tabs-fetching sync-state">
           <!-- Show intentionally blank panel, see bug 1239845 -->
         </div>
         <div class="notAuthedInfo sync-state">
           <div class="syncIllustration"></div>
           <p class="instructions">&syncedTabs.sidebar.notsignedin.label;</p>
           <button class="button sync-prefs">&fxaSignIn.label;</button>
         </div>
+        <div class="unverified sync-state">
+          <div class="syncIllustration"></div>
+          <p class="instructions">&syncedTabs.sidebar.unverified.label;</p>
+          <button class="button sync-prefs">&syncedTabs.sidebar.openprefs.label;</button>
+        </div>
         <div class="singleDeviceInfo sync-state">
           <div class="syncIllustrationIssue"></div>
           <p class="instructions">&syncedTabs.sidebar.noclients.subtitle;</p>
           <button class="button connect-device">&syncedTabs.sidebar.connectAnotherDevice;</button>
         </div>
         <div class="tabs-disabled sync-state">
           <div class="syncIllustrationIssue"></div>
           <p class="instructions">&syncedTabs.sidebar.tabsnotsyncing.label;</p>
--- a/browser/components/syncedtabs/test/browser/browser_sidebar_syncedtabslist.js
+++ b/browser/components/syncedtabs/test/browser/browser_sidebar_syncedtabslist.js
@@ -223,16 +223,22 @@ add_task(async function testSyncedTabsSi
 
   syncedTabsDeckComponent._getSignedInUser.restore();
   sinon.stub(syncedTabsDeckComponent, "_getSignedInUser", () => Promise.resolve(account));
   await syncedTabsDeckComponent.updatePanel();
   selectedPanel = syncedTabsDeckComponent.container.querySelector(".sync-state.selected");
   Assert.ok(selectedPanel.classList.contains("notAuthedInfo"),
     "not-authed panel is selected");
 
+  account = {verified: false};
+  await syncedTabsDeckComponent.updatePanel();
+  selectedPanel = syncedTabsDeckComponent.container.querySelector(".sync-state.selected");
+  Assert.ok(selectedPanel.classList.contains("unverified"),
+    "unverified panel is selected");
+
   account = {verified: true};
   await syncedTabsDeckComponent.updatePanel();
   selectedPanel = syncedTabsDeckComponent.container.querySelector(".sync-state.selected");
   Assert.ok(selectedPanel.classList.contains("tabs-disabled"),
     "tabs disabled panel is selected");
 
   SyncedTabs._internal.isConfiguredToSyncTabs = true;
   await syncedTabsDeckComponent.updatePanel();
--- a/browser/components/syncedtabs/test/xpcshell/test_SyncedTabsDeckComponent.js
+++ b/browser/components/syncedtabs/test/xpcshell/test_SyncedTabsDeckComponent.js
@@ -154,17 +154,17 @@ add_task(async function testPanelStatus(
   let account = null;
   sinon.stub(fxAccounts, "getSignedInUser", () => Promise.resolve(account));
   let result = await component.getPanelStatus();
   Assert.equal(result, component.PANELS.NOT_AUTHED_INFO);
 
   account = {verified: false};
 
   result = await component.getPanelStatus();
-  Assert.equal(result, component.PANELS.NOT_AUTHED_INFO);
+  Assert.equal(result, component.PANELS.UNVERIFIED);
 
   account = {verified: true};
 
   SyncedTabsMock.loginFailed = true;
   result = await component.getPanelStatus();
   Assert.equal(result, component.PANELS.NOT_AUTHED_INFO);
   SyncedTabsMock.loginFailed = false;
 
--- a/browser/locales/en-US/chrome/browser/browser.dtd
+++ b/browser/locales/en-US/chrome/browser/browser.dtd
@@ -376,16 +376,17 @@ These should match what Safari and other
      when Sync is configured but syncing tabs is disabled. -->
 <!ENTITY appMenuRemoteTabs.tabsnotsyncing.label "Turn on tab syncing to view a list of tabs from your other devices.">
 <!-- LOCALIZATION NOTE (appMenuRemoteTabs.noclients.label): This is shown
      when Sync is configured but this appears to be the only device attached to
      the account. We also show links to download Firefox for android/ios. -->
 <!ENTITY appMenuRemoteTabs.noclients.subtitle "Want to see your tabs from other devices here?">
 <!ENTITY appMenuRemoteTabs.openprefs.label "Sync Preferences">
 <!ENTITY appMenuRemoteTabs.notsignedin.label "Sign in to view a list of tabs from your other devices.">
+<!ENTITY appMenuRemoteTabs.unverified.label "Your account needs to be verified.">
 <!ENTITY appMenuRemoteTabs.signin.label "Sign in to Sync">
 <!ENTITY appMenuRemoteTabs.managedevices.label "Manage Devices…">
 <!ENTITY appMenuRemoteTabs.sidebar.label "View Synced Tabs Sidebar">
 <!ENTITY appMenuRemoteTabs.connectdevice.label "Connect Another Device">
 
 <!ENTITY appMenuRecentHighlights.label "Recent Highlights">
 
 <!ENTITY customizeMenu.addToToolbar.label "Add to Toolbar">
@@ -789,16 +790,17 @@ you can use these alternative items. Oth
 
 <!-- LOCALIZATION NOTE (syncTabsMenu3.label): This appears in the history menu -->
 <!ENTITY syncTabsMenu3.label     "Synced Tabs">
 
 <!ENTITY syncedTabs.sidebar.label              "Synced Tabs">
 <!ENTITY syncedTabs.sidebar.noclients.label    "Sign in to Firefox from your other devices to view their tabs here.">
 <!ENTITY syncedTabs.sidebar.noclients.subtitle "Want to see your tabs from other devices here?">
 <!ENTITY syncedTabs.sidebar.notsignedin.label  "Sign in to view a list of tabs from your other devices.">
+<!ENTITY syncedTabs.sidebar.unverified.label   "Your account needs to be verified.">
 <!ENTITY syncedTabs.sidebar.notabs.label       "No open tabs">
 <!ENTITY syncedTabs.sidebar.openprefs.label    "Open &syncBrand.shortName.label; Preferences">
 <!-- LOCALIZATION NOTE (syncedTabs.sidebar.tabsnotsyncing.label): This is shown
      when Sync is configured but syncing tabs is disabled. -->
 <!ENTITY syncedTabs.sidebar.tabsnotsyncing.label       "Turn on tab syncing to view a list of tabs from your other devices.">
 <!ENTITY syncedTabs.sidebar.searchPlaceholder  "Search synced tabs">
 <!ENTITY syncedTabs.sidebar.connectAnotherDevice  "Connect Another Device">
 
--- a/browser/modules/test/browser/browser_BrowserUITelemetry_syncedtabs.js
+++ b/browser/modules/test/browser/browser_BrowserUITelemetry_syncedtabs.js
@@ -31,23 +31,25 @@ function mockSyncedTabs() {
   };
 
   let oldInternal = SyncedTabs._internal;
   SyncedTabs._internal = mockedInternal;
 
   // configure our broadcasters so we are in the right state.
   document.getElementById("sync-reauth-state").hidden = true;
   document.getElementById("sync-setup-state").hidden = true;
+  document.getElementById("sync-unverified-state").hidden = true;
   document.getElementById("sync-syncnow-state").hidden = false;
 
   registerCleanupFunction(() => {
     SyncedTabs._internal = oldInternal;
 
     document.getElementById("sync-reauth-state").hidden = true;
     document.getElementById("sync-setup-state").hidden = false;
+    document.getElementById("sync-unverified-state").hidden = true;
     document.getElementById("sync-syncnow-state").hidden = true;
   });
 }
 
 mockSyncedTabs();
 
 function promiseTabsUpdated() {
   return new Promise(resolve => {
--- a/browser/themes/shared/customizableui/panelUI.inc.css
+++ b/browser/themes/shared/customizableui/panelUI.inc.css
@@ -713,16 +713,17 @@ toolbarbutton[constrain-size="true"][cui
   min-width: 19em;
 }
 
 /* Work around bug 1224412 - these boxes will cause scrollbars to appear when
    the panel is anchored to a toolbar button.
 */
 #PanelUI-remotetabs[mainview] #PanelUI-remotetabs-setupsync,
 #PanelUI-remotetabs[mainview] #PanelUI-remotetabs-reauthsync,
+#PanelUI-remotetabs[mainview] #PanelUI-remotetabs-unverified,
 #PanelUI-remotetabs[mainview] #PanelUI-remotetabs-nodevicespane,
 #PanelUI-remotetabs[mainview] #PanelUI-remotetabs-tabsdisabledpane {
   min-height: calc(var(--panel-ui-sync-illustration-height) +
                    20px + /* margin of .PanelUI-remotetabs-button */
                    16px + /* padding of .PanelUI-remotetabs-button */
                    30px + /* margin of .PanelUI-remotetabs-instruction-label */
                    30px + 15px + /* padding of .PanelUI-remotetabs-instruction-box */
                    11em);