Bug 1354084 - add sync / Firefox account button to hamburger panel, r?mikedeboer draft
authorGijs Kruitbosch <gijskruitbosch@gmail.com>
Tue, 09 May 2017 15:56:19 -0400
changeset 582502 2036826bf36588c39c56534872beb569da7c944c
parent 582482 f9ca97a334296facd2e0ea5582e7f12d0fe70fe4
child 629804 d2b6dc2490cac32dab4deb5c0944670c79fd3729
push id60119
push usergijskruitbosch@gmail.com
push dateMon, 22 May 2017 19:24:50 +0000
reviewersmikedeboer
bugs1354084
milestone55.0a1
Bug 1354084 - add sync / Firefox account button to hamburger panel, r?mikedeboer MozReview-Commit-ID: 8YOFAY5VpQl
browser/base/content/browser-sync.js
browser/base/content/test/sync/browser_sync.js
browser/components/customizableui/content/panelUI.inc.xul
browser/components/uitour/UITour.jsm
browser/themes/shared/customizableui/panelUI.inc.css
--- a/browser/base/content/browser-sync.js
+++ b/browser/base/content/browser-sync.js
@@ -19,41 +19,16 @@ var gSync = {
   _syncAnimationTimer: 0,
 
   _obs: [
     "weave:engine:sync:finish",
     "quit-application",
     UIState.ON_UPDATE
   ],
 
-  get panelUIFooter() {
-    delete this.panelUIFooter;
-    return this.panelUIFooter = document.getElementById("PanelUI-footer-fxa");
-  },
-
-  get panelUIStatus() {
-    delete this.panelUIStatus;
-    return this.panelUIStatus = document.getElementById("PanelUI-fxa-status");
-  },
-
-  get panelUIAvatar() {
-    delete this.panelUIAvatar;
-    return this.panelUIAvatar = document.getElementById("PanelUI-fxa-avatar");
-  },
-
-  get panelUILabel() {
-    delete this.panelUILabel;
-    return this.panelUILabel = document.getElementById("PanelUI-fxa-label");
-  },
-
-  get panelUIIcon() {
-    delete this.panelUIIcon;
-    return this.panelUIIcon = document.getElementById("PanelUI-fxa-icon");
-  },
-
   get fxaStrings() {
     delete this.fxaStrings;
     return this.fxaStrings = Services.strings.createBundle(
       "chrome://browser/locale/accounts.properties"
     );
   },
 
   get syncStrings() {
@@ -69,41 +44,67 @@ var gSync = {
     return Services.prefs.getBoolPref("services.sync.sendTabToDevice.enabled");
   },
 
   get remoteClients() {
     return Weave.Service.clientsEngine.remoteClients
            .sort((a, b) => a.name.localeCompare(b.name));
   },
 
+  _generateNodeGetters(usePhoton) {
+    let prefix = usePhoton ? "appMenu-" : "PanelUI-";
+    for (let k of ["Status", "Avatar", "Label", "Container"]) {
+      let prop = "appMenu" + k;
+      let suffix = k.toLowerCase();
+      delete this[prop];
+      this.__defineGetter__(prop, function() {
+        delete this[prop];
+        return this[prop] = document.getElementById(prefix + "fxa-" + suffix);
+      });
+    }
+  },
+
+  _maybeUpdateUIState() {
+    // Update the UI.
+    if (UIState.isReady()) {
+      const state = UIState.get();
+      // If we are not configured, the UI is already in the right state when
+      // we open the window. We can avoid a repaint.
+      if (state.status != UIState.STATUS_NOT_CONFIGURED) {
+        this.updateAllUI(state);
+      }
+    }
+
+    this.maybeMoveSyncedTabsButton();
+  },
+
   init() {
     // Bail out if we're already initialized or for pop-up windows.
     if (this._initialized || !window.toolbar.visible) {
       return;
     }
 
     for (let topic of this._obs) {
       Services.obs.addObserver(this, topic, true);
     }
 
+    // Use this getter not because 'lazy' but because of the observer,
+    // which lets us easily update our UI according to the pref flip
+    XPCOMUtils.defineLazyPreferenceGetter(this, "gPhotonStructure",
+      "browser.photon.structure.enabled", (pref, old, newValue) => {
+      this._generateNodeGetters(newValue);
+      this._maybeUpdateUIState();
+    });
+    this._generateNodeGetters(this.gPhotonStructure);
+
     // initial label for the sync buttons.
     let broadcaster = document.getElementById("sync-status");
     broadcaster.setAttribute("label", this.syncStrings.GetStringFromName("syncnow.label"));
 
-    // Update the UI
-    if (UIState.isReady()) {
-      const state = UIState.get();
-      // If we are not configured, the UI is already in the right state when
-      // we open the window. We can avoid a repaint.
-      if (state.status != UIState.STATUS_NOT_CONFIGURED) {
-        this.updateAllUI(state);
-      }
-    }
-
-    this.maybeMoveSyncedTabsButton();
+    this._maybeUpdateUIState();
 
     EnsureFxAccountsWebChannel();
 
     this._initialized = true;
   },
 
   uninit() {
     if (!this._initialized) {
@@ -145,62 +146,62 @@ var gSync = {
     this.updatePanelBadge(state);
     this.updatePanelPopup(state);
     this.updateStateBroadcasters(state);
     this.updateSyncButtonsTooltip(state);
     this.updateSyncStatus(state);
   },
 
   updatePanelPopup(state) {
-    let defaultLabel = this.panelUIStatus.getAttribute("defaultlabel");
+    let defaultLabel = this.appMenuStatus.getAttribute("defaultlabel");
     // The localization string is for the signed in text, but it's the default text as well
-    let defaultTooltiptext = this.panelUIStatus.getAttribute("signedinTooltiptext");
+    let defaultTooltiptext = this.appMenuStatus.getAttribute("signedinTooltiptext");
 
     const status = state.status;
     // Reset the status bar to its original state.
-    this.panelUILabel.setAttribute("label", defaultLabel);
-    this.panelUIStatus.setAttribute("tooltiptext", defaultTooltiptext);
-    this.panelUIFooter.removeAttribute("fxastatus");
-    this.panelUIAvatar.style.removeProperty("list-style-image");
+    this.appMenuLabel.setAttribute("label", defaultLabel);
+    this.appMenuStatus.setAttribute("tooltiptext", defaultTooltiptext);
+    this.appMenuContainer.removeAttribute("fxastatus");
+    this.appMenuAvatar.style.removeProperty("list-style-image");
 
     if (status == UIState.STATUS_NOT_CONFIGURED) {
       return;
     }
 
     // At this point we consider sync to be configured (but still can be in an error state).
     if (status == UIState.STATUS_LOGIN_FAILED) {
       let tooltipDescription = this.fxaStrings.formatStringFromName("reconnectDescription", [state.email], 1);
-      let errorLabel = this.panelUIStatus.getAttribute("errorlabel");
-      this.panelUIFooter.setAttribute("fxastatus", "login-failed");
-      this.panelUILabel.setAttribute("label", errorLabel);
-      this.panelUIStatus.setAttribute("tooltiptext", tooltipDescription);
+      let errorLabel = this.appMenuStatus.getAttribute("errorlabel");
+      this.appMenuContainer.setAttribute("fxastatus", "login-failed");
+      this.appMenuLabel.setAttribute("label", errorLabel);
+      this.appMenuStatus.setAttribute("tooltiptext", tooltipDescription);
       return;
     } else if (status == UIState.STATUS_NOT_VERIFIED) {
       let tooltipDescription = this.fxaStrings.formatStringFromName("verifyDescription", [state.email], 1);
-      let unverifiedLabel = this.panelUIStatus.getAttribute("unverifiedlabel");
-      this.panelUIFooter.setAttribute("fxastatus", "unverified");
-      this.panelUILabel.setAttribute("label", unverifiedLabel);
-      this.panelUIStatus.setAttribute("tooltiptext", tooltipDescription);
+      let unverifiedLabel = this.appMenuStatus.getAttribute("unverifiedlabel");
+      this.appMenuContainer.setAttribute("fxastatus", "unverified");
+      this.appMenuLabel.setAttribute("label", unverifiedLabel);
+      this.appMenuStatus.setAttribute("tooltiptext", tooltipDescription);
       return;
     }
 
     // At this point we consider sync to be logged-in.
-    this.panelUIFooter.setAttribute("fxastatus", "signedin");
-    this.panelUILabel.setAttribute("label", state.displayName || state.email);
+    this.appMenuContainer.setAttribute("fxastatus", "signedin");
+    this.appMenuLabel.setAttribute("label", state.displayName || state.email);
 
     if (state.avatarURL) {
       let bgImage = "url(\"" + state.avatarURL + "\")";
-      this.panelUIAvatar.style.listStyleImage = bgImage;
+      this.appMenuAvatar.style.listStyleImage = bgImage;
 
       let img = new Image();
       img.onerror = () => {
         // Clear the image if it has trouble loading. Since this callback is asynchronous
         // we check to make sure the image is still the same before we clear it.
-        if (this.panelUIAvatar.style.listStyleImage === bgImage) {
-          this.panelUIAvatar.style.removeProperty("list-style-image");
+        if (this.appMenuAvatar.style.listStyleImage === bgImage) {
+          this.appMenuAvatar.style.removeProperty("list-style-image");
         }
       };
       img.src = state.avatarURL;
     }
   },
 
   updatePanelBadge(state) {
     if (state.status == UIState.STATUS_LOGIN_FAILED ||
@@ -234,22 +235,22 @@ var gSync = {
     const broadcaster = document.getElementById("sync-status");
     const syncingUI = broadcaster.getAttribute("syncstatus") == "active";
     if (state.syncing != syncingUI) { // Do we need to update the UI?
       state.syncing ? this.onActivityStart() : this.onActivityStop();
     }
   },
 
   onMenuPanelCommand() {
-    switch (this.panelUIFooter.getAttribute("fxastatus")) {
+    switch (this.appMenuContainer.getAttribute("fxastatus")) {
     case "signedin":
       this.openPrefs("menupanel", "fxaSignedin");
       break;
     case "error":
-      if (this.panelUIFooter.getAttribute("fxastatus") == "unverified") {
+      if (this.appMenuContainer.getAttribute("fxastatus") == "unverified") {
         this.openPrefs("menupanel", "fxaError");
       } else {
         this.openSignInAgainPage("menupanel");
       }
       break;
     default:
       this.openPrefs("menupanel", "fxa");
       break;
--- a/browser/base/content/test/sync/browser_sync.js
+++ b/browser/base/content/test/sync/browser_sync.js
@@ -22,17 +22,17 @@ add_task(async function test_ui_state_si
     avatarURL: "https://foo.bar",
     lastSync: new Date(),
     syncing: false
   };
 
   gSync.updateAllUI(state);
 
   checkFxABadge(false);
-  let statusBarTooltip = gSync.panelUIStatus.getAttribute("signedinTooltiptext");
+  let statusBarTooltip = gSync.appMenuStatus.getAttribute("signedinTooltiptext");
   let lastSyncTooltip = gSync.formatLastSyncDate(new Date(state.lastSync));
   checkPanelUIStatusBar({
     label: "Foo Bar",
     tooltip: statusBarTooltip,
     fxastatus: "signedin",
     avatarURL: "https://foo.bar",
     syncing: false,
     syncNowTooltip: lastSyncTooltip
@@ -70,18 +70,18 @@ add_task(async function test_ui_state_sy
 add_task(async function test_ui_state_unconfigured() {
   let state = {
     status: UIState.STATUS_NOT_CONFIGURED
   };
 
   gSync.updateAllUI(state);
 
   checkFxABadge(false);
-  let signedOffLabel = gSync.panelUIStatus.getAttribute("defaultlabel");
-  let statusBarTooltip = gSync.panelUIStatus.getAttribute("signedinTooltiptext");
+  let signedOffLabel = gSync.appMenuStatus.getAttribute("defaultlabel");
+  let statusBarTooltip = gSync.appMenuStatus.getAttribute("signedinTooltiptext");
   checkPanelUIStatusBar({
     label: signedOffLabel,
     tooltip: statusBarTooltip
   });
   checkRemoteTabsPanel("PanelUI-remotetabs-setupsync");
   checkMenuBarItem("sync-setup");
 });
 
@@ -91,17 +91,17 @@ add_task(async function test_ui_state_un
     email: "foo@bar.com",
     lastSync: new Date(),
     syncing: false
   };
 
   gSync.updateAllUI(state);
 
   checkFxABadge(true);
-  let expectedLabel = gSync.panelUIStatus.getAttribute("unverifiedlabel");
+  let expectedLabel = gSync.appMenuStatus.getAttribute("unverifiedlabel");
   let tooltipText = gSync.fxaStrings.formatStringFromName("verifyDescription", [state.email], 1);
   checkPanelUIStatusBar({
     label: expectedLabel,
     tooltip: tooltipText,
     fxastatus: "unverified",
     avatarURL: null,
     syncing: false,
     syncNowTooltip: tooltipText
@@ -114,17 +114,17 @@ add_task(async function test_ui_state_lo
   let state = {
     status: UIState.STATUS_LOGIN_FAILED,
     email: "foo@bar.com"
   };
 
   gSync.updateAllUI(state);
 
   checkFxABadge(true);
-  let expectedLabel = gSync.panelUIStatus.getAttribute("errorlabel");
+  let expectedLabel = gSync.appMenuStatus.getAttribute("errorlabel");
   let tooltipText = gSync.fxaStrings.formatStringFromName("reconnectDescription", [state.email], 1);
   checkPanelUIStatusBar({
     label: expectedLabel,
     tooltip: tooltipText,
     fxastatus: "login-failed",
     avatarURL: null,
     syncing: false,
     syncNowTooltip: tooltipText
@@ -155,36 +155,37 @@ function checkFxABadge(shouldBeShown) {
       isShown = true;
       break;
     }
   }
   is(isShown, shouldBeShown, "the fxa badge has the right visibility");
 }
 
 function checkPanelUIStatusBar({label, tooltip, fxastatus, avatarURL, syncing, syncNowTooltip}) {
-  let labelNode = document.getElementById("PanelUI-fxa-label");
-  let tooltipNode = document.getElementById("PanelUI-fxa-status");
-  let statusNode = document.getElementById("PanelUI-footer-fxa");
-  let avatar = document.getElementById("PanelUI-fxa-avatar");
+  let prefix = gPhotonStructure ? "appMenu" : "PanelUI"
+  let labelNode = document.getElementById(`${prefix}-fxa-label`);
+  let tooltipNode = document.getElementById(`${prefix}-fxa-status`);
+  let statusNode = document.getElementById(`${prefix}-fxa-container`);
+  let avatar = document.getElementById(`${prefix}-fxa-avatar`);
 
-  is(labelNode.getAttribute("label"), label, "panelUI-fxa label has the right value");
-  is(tooltipNode.getAttribute("tooltiptext"), tooltip, "panelUI-fxa tooltip has the right value");
+  is(labelNode.getAttribute("label"), label, "fxa label has the right value");
+  is(tooltipNode.getAttribute("tooltiptext"), tooltip, "fxa tooltip has the right value");
   if (fxastatus) {
-    is(statusNode.getAttribute("fxastatus"), fxastatus, "panelUI-fxa fxastatus has the right value");
+    is(statusNode.getAttribute("fxastatus"), fxastatus, "fxa fxastatus has the right value");
   } else {
-    ok(!statusNode.hasAttribute("fxastatus"), "panelUI-fxa fxastatus is unset")
+    ok(!statusNode.hasAttribute("fxastatus"), "fxastatus is unset")
   }
   if (avatarURL) {
-    is(avatar.style.listStyleImage, `url("${avatarURL}")`, "panelUI-fxa avatar URL is set");
+    is(avatar.style.listStyleImage, `url("${avatarURL}")`, "fxa avatar URL is set");
   } else {
-    ok(!statusNode.style.listStyleImage, "panelUI-fxa avatar URL is unset");
+    ok(!statusNode.style.listStyleImage, "fxa avatar URL is unset");
   }
 
   if (syncing != undefined && syncNowTooltip != undefined) {
-    checkSyncNowButton("PanelUI-fxa-icon", syncing, syncNowTooltip);
+    checkSyncNowButton(`${prefix}-fxa-icon`, syncing, syncNowTooltip);
   }
 }
 
 function checkRemoteTabsPanel(expectedShownItemId, syncing, syncNowTooltip) {
   checkItemsVisiblities(["PanelUI-remotetabs-main",
                          "PanelUI-remotetabs-setupsync",
                          "PanelUI-remotetabs-reauthsync"],
                         expectedShownItemId);
--- a/browser/components/customizableui/content/panelUI.inc.xul
+++ b/browser/components/customizableui/content/panelUI.inc.xul
@@ -19,17 +19,17 @@
         <vbox id="PanelUI-footer-addons"></vbox>
         <toolbarbutton class="panel-banner-item"
                        label-update-available="&updateAvailable.panelUI.label;"
                        label-update-manual="&updateManual.panelUI.label;"
                        label-update-restart="&updateRestart.panelUI.label;"
                        oncommand="PanelUI._onBannerItemSelected(event)"
                        wrap="true"
                        hidden="true"/>
-        <hbox id="PanelUI-footer-fxa">
+        <hbox id="PanelUI-fxa-container">
           <hbox id="PanelUI-fxa-status"
                 label="&fxaSignedIn.tooltip;"
                 defaultlabel="&fxaSignIn.label;"
                 signedinTooltiptext="&fxaSignedIn.tooltip;"
                 tooltiptext="&fxaSignedIn.tooltip;"
                 errorlabel="&fxaSignInError.label;"
                 unverifiedlabel="&fxaUnverified.label;"
                 onclick="if (event.which == 1) gSync.onMenuPanelCommand();">
@@ -503,16 +503,41 @@
         <vbox id="appMenu-addon-banners"/>
         <toolbarbutton class="panel-banner-item"
                        label-update-available="&updateAvailable.panelUI.label;"
                        label-update-manual="&updateManual.panelUI.label;"
                        label-update-restart="&updateRestart.panelUI.label;"
                        oncommand="PanelUI._onBannerItemSelected(event)"
                        wrap="true"
                        hidden="true"/>
+        <toolbaritem id="appMenu-fxa-container">
+          <hbox id="appMenu-fxa-status"
+                flex="1"
+                defaultlabel="&fxaSignIn.label;"
+                signedinTooltiptext="&fxaSignedIn.tooltip;"
+                tooltiptext="&fxaSignedIn.tooltip;"
+                errorlabel="&fxaSignInError.label;"
+                unverifiedlabel="&fxaUnverified.label;"
+                onclick="if (event.which == 1) gSync.onMenuPanelCommand();">
+            <image id="appMenu-fxa-avatar"/>
+            <toolbarbutton id="appMenu-fxa-label"
+                           class="subviewbutton subviewbutton-iconic"
+                           label="&fxaSignIn.label;"
+                           fxabrandname="&syncBrand.fxAccount.label;"/>
+          </hbox>
+          <toolbarseparator/>
+          <toolbarbutton id="appMenu-fxa-icon"
+                         class="subviewbutton subviewbutton-iconic"
+                         oncommand="gSync.doSync();"
+                         closemenu="none">
+            <observes element="sync-status" attribute="syncstatus"/>
+            <observes element="sync-status" attribute="tooltiptext"/>
+          </toolbarbutton>
+        </toolbaritem>
+        <toolbarseparator/>
         <toolbarbutton id="appMenu-new-window-button"
                        class="subviewbutton subviewbutton-iconic"
                        label="&newNavigatorCmd.label;"
                        key="key_newNavigator"
                        command="cmd_newNavigator"/>
         <toolbarbutton id="appMenu-private-window-button"
                        class="subviewbutton subviewbutton-iconic"
                        label="&newPrivateWindow.label;"
--- a/browser/components/uitour/UITour.jsm
+++ b/browser/components/uitour/UITour.jsm
@@ -94,17 +94,17 @@ this.UITour = {
 
   _annotationPanelMutationObservers: new WeakMap(),
 
   highlightEffects: ["random", "wobble", "zoom", "color"],
   targets: new Map([
     ["accountStatus", {
       query: (aDocument) => {
         // If the user is logged in, use the avatar element.
-        let fxAFooter = aDocument.getElementById("PanelUI-footer-fxa");
+        let fxAFooter = aDocument.getElementById("PanelUI-fxa-container");
         if (fxAFooter.getAttribute("fxastatus")) {
           return aDocument.getElementById("PanelUI-fxa-avatar");
         }
 
         // Otherwise use the sync setup icon.
         let statusButton = aDocument.getElementById("PanelUI-fxa-label");
         return aDocument.getAnonymousElementByAttribute(statusButton,
                                                         "class",
--- a/browser/themes/shared/customizableui/panelUI.inc.css
+++ b/browser/themes/shared/customizableui/panelUI.inc.css
@@ -17,16 +17,20 @@
 %define buttonStateActive :not([disabled]):-moz-any([open],:hover:active)
 %define menuStateActive :not([disabled])[_moz-menuactive]:active
 %define menuStateMenuActive :not([disabled])[_moz-menuactive]
 
 %include ../browser.inc
 
 :root {
   --panel-ui-exit-subview-gutter-width: 38px;
+  --appmenu-yellow-warning-border-color: hsl(45, 100%, 77%);
+  --appmenu-yellow-warning-color: #FFEFBF;
+  --appmenu-yellow-warning-hover-color: #FFE8A2;
+  --appmenu-yellow-warning-active-color: #FFE38F;
 }
 
 #PanelUI-popup #PanelUI-contents:empty {
   height: 128px;
 }
 
 #PanelUI-popup #PanelUI-contents:empty::before {
   content: "";
@@ -484,19 +488,19 @@ toolbaritem[cui-areatype="menu-panel"][s
 
 #PanelUI-multiView[viewtype="subview"] > .panel-viewcontainer > .panel-viewstack > .panel-mainview >  #PanelUI-mainView {
   background-color: var(--arrowpanel-dimmed);
 }
 
 #PanelUI-multiView[viewtype="subview"] #PanelUI-mainView > #PanelUI-contents-scroller > #PanelUI-contents > .panel-wide-item,
 #PanelUI-multiView[viewtype="subview"] #PanelUI-mainView > #PanelUI-contents-scroller > #PanelUI-contents > .toolbarbutton-1:not([panel-multiview-anchor="true"]),
 #PanelUI-multiView[viewtype="subview"] #PanelUI-mainView > #PanelUI-footer > .panel-banner-item,
-#PanelUI-multiView[viewtype="subview"] #PanelUI-mainView > #PanelUI-footer > #PanelUI-footer-fxa > #PanelUI-fxa-status > #PanelUI-fxa-avatar,
-#PanelUI-multiView[viewtype="subview"] #PanelUI-mainView > #PanelUI-footer > #PanelUI-footer-fxa > #PanelUI-fxa-status > #PanelUI-fxa-label,
-#PanelUI-multiView[viewtype="subview"] #PanelUI-mainView > #PanelUI-footer > #PanelUI-footer-fxa > #PanelUI-fxa-icon,
+#PanelUI-multiView[viewtype="subview"] #PanelUI-mainView > #PanelUI-footer > #PanelUI-fxa-container > #PanelUI-fxa-status > #PanelUI-fxa-avatar,
+#PanelUI-multiView[viewtype="subview"] #PanelUI-mainView > #PanelUI-footer > #PanelUI-fxa-container > #PanelUI-fxa-status > #PanelUI-fxa-label,
+#PanelUI-multiView[viewtype="subview"] #PanelUI-mainView > #PanelUI-footer > #PanelUI-fxa-container > #PanelUI-fxa-icon,
 #PanelUI-multiView[viewtype="subview"] #PanelUI-mainView > #PanelUI-footer > #PanelUI-footer-inner > #PanelUI-footer-inner > toolbarseparator,
 #PanelUI-multiView[viewtype="subview"] #PanelUI-mainView > #PanelUI-footer > #PanelUI-footer-inner > #PanelUI-customize,
 #PanelUI-multiView[viewtype="subview"] #PanelUI-mainView > #PanelUI-footer > #PanelUI-footer-inner > #PanelUI-help:not([panel-multiview-anchor="true"]) {
   opacity: .5;
 }
 
 /*
  * XXXgijs: this is a workaround for a layout issue that was caused by these iframes,
@@ -584,27 +588,27 @@ toolbarpaletteitem[place="palette"] > to
   display: flex;
   flex-shrink: 0;
   flex-direction: column;
   background-color: var(--arrowpanel-dimmed);
   padding: 0;
   margin: 0;
 }
 
-#main-window[customizing] #PanelUI-footer-fxa {
+#main-window[customizing] #PanelUI-fxa-container {
   display: none;
 }
 
-#PanelUI-footer-fxa:not([fxastatus="signedin"]) > toolbarseparator,
-#PanelUI-footer-fxa:not([fxastatus="signedin"]) > #PanelUI-fxa-icon {
+#PanelUI-fxa-container:not([fxastatus="signedin"]) > toolbarseparator,
+#PanelUI-fxa-container:not([fxastatus="signedin"]) > #PanelUI-fxa-icon {
   display: none;
 }
 
-#PanelUI-footer-fxa[fxastatus="login-failed"] > #PanelUI-fxa-status::after,
-#PanelUI-footer-fxa[fxastatus="unverified"] > #PanelUI-fxa-status::after {
+#PanelUI-fxa-container[fxastatus="login-failed"] > #PanelUI-fxa-status::after,
+#PanelUI-fxa-container[fxastatus="unverified"] > #PanelUI-fxa-status::after {
   content: url(chrome://browser/skin/warning.svg);
   filter: drop-shadow(0 1px 0 hsla(206,50%,10%,.15));
   width: 47px;
   padding-top: 1px;
   display: block;
   text-align: center;
   position: relative;
   top: 25%;
@@ -615,32 +619,32 @@ toolbarpaletteitem[place="palette"] > to
   content: "";
   width: 16px;
   height: 16px;
   margin-inline-end: 16.5px;
   display: -moz-box;
 }
 
 .addon-banner-item {
-  background-color: #FFEFBF;
+  background-color: var(--appmenu-yellow-warning-color);
   /* Force border to override `.addon-banner-item` selector below */
-  border-top: 1px solid hsl(45, 100%, 77%) !important;
+  border-top: 1px solid var(--appmenu-yellow-warning-border-color) !important;
   display: flex;
   flex: 1 1 0%;
   width: calc(@menuPanelWidth@ + 30px);
   padding-inline-start: 15px;
   border-inline-start-style: none;
 }
 
 .addon-banner-item:hover {
-  background-color: #FFE8A2;
+  background-color: var(--appmenu-yellow-warning-hover-color);
 }
 
 .addon-banner-item:active {
-  background-color: #FFE38F;
+  background-color: var(--appmenu-yellow-warning-active-color);
 }
 
 .addon-banner-item > .toolbarbutton-icon {
   width: 14px;
   height: 14px;
 }
 
 .addon-banner-item::after {
@@ -649,36 +653,36 @@ toolbarpaletteitem[place="palette"] > to
 
 #PanelUI-fxa-status {
   display: flex;
   flex: 1 1 0%;
   width: 1px;
 }
 
 #PanelUI-footer-inner,
-#PanelUI-footer-fxa:not([hidden]) {
+#PanelUI-fxa-container:not([hidden]) {
   display: flex;
   border-top: 1px solid var(--panel-separator-color);
 }
 
 #PanelUI-multiView[viewtype="subview"] #PanelUI-footer-inner,
-#PanelUI-multiView[viewtype="subview"] #PanelUI-footer-fxa {
+#PanelUI-multiView[viewtype="subview"] #PanelUI-fxa-container {
   position: relative;
 }
 
 #PanelUI-footer-inner > toolbarseparator,
-#PanelUI-footer-fxa > toolbarseparator {
+#PanelUI-fxa-container > toolbarseparator {
   border: 0;
   border-left: 1px solid var(--panel-separator-color);
   margin: 7px 0 7px;
   -moz-appearance: none;
 }
 
 #PanelUI-footer-inner:hover > toolbarseparator,
-#PanelUI-footer-fxa:hover > toolbarseparator {
+#PanelUI-fxa-container:hover > toolbarseparator {
   margin: 0;
 }
 
 .addon-banner-item,
 .panel-banner-item,
 #PanelUI-help,
 #PanelUI-fxa-label,
 #PanelUI-fxa-icon,
@@ -705,21 +709,128 @@ toolbarpaletteitem[place="palette"] > to
 /* in Photon, we have a bottom border as well. Reconcile with the above rule
  * after photon launch. */
 #appMenu-mainView > .panel-subview-body > .panel-banner-item {
   border-bottom: 1px solid var(--panel-separator-color);
   margin-bottom: 3px;
   padding-inline-start: 10px;
 }
 
-
 .panel-banner-item > .toolbarbutton-text {
   width: 0; /* Fancy cropping solution for flexbox. */
 }
 
+/* FxAccount indicator bits. */
+#appMenu-fxa-label,
+#appMenu-fxa-icon {
+  padding: 4px;
+  -moz-appearance: none;
+  box-shadow: none;
+  border-radius: 0;
+  border: 0 none;
+  -moz-box-orient: horizontal;
+  -moz-image-region: rect(0, 16px, 16px, 0);
+  list-style-image: url(chrome://browser/skin/sync-horizontalbar.png);
+}
+
+#appMenu-fxa-label {
+  -moz-box-flex: 1;
+  padding-inline-start: 12px;
+  margin: 0;
+}
+
+#appMenu-fxa-icon {
+  margin: 0 10px;
+}
+
+#appMenu-fxa-icon > .toolbarbutton-text {
+  display: none;
+}
+
+#appMenu-fxa-icon[syncstatus="active"] {
+  list-style-image: url(chrome://browser/skin/syncProgress-horizontalbar.png);
+}
+
+#appMenu-fxa-avatar {
+  pointer-events: none;
+  list-style-image: url(chrome://browser/skin/fxa/default-avatar.svg);
+}
+
+@media (min-resolution: 1.1dppx) {
+  #appMenu-fxa-label,
+  #appMenu-fxa-icon {
+    list-style-image: url(chrome://browser/skin/sync-horizontalbar@2x.png);
+    -moz-image-region: rect(0, 32px, 32px, 0);
+  }
+
+  #appMenu-fxa-icon[syncstatus="active"] {
+    list-style-image: url(chrome://browser/skin/syncProgress-horizontalbar@2x.png);
+  }
+}
+
+#appMenu-fxa-container {
+  -moz-box-orient: horizontal;
+}
+
+/* Handle different UI states. */
+#appMenu-fxa-container[fxastatus="signedin"] > #appMenu-fxa-status > #appMenu-fxa-label > .toolbarbutton-icon,
+#appMenu-fxa-container:not([fxastatus="signedin"]) > toolbarseparator,
+#appMenu-fxa-container:not([fxastatus="signedin"]) > #appMenu-fxa-icon,
+#appMenu-fxa-container:not([fxastatus="signedin"]) > #appMenu-fxa-status > #appMenu-fxa-avatar {
+  display: none;
+}
+
+#appMenu-fxa-container[fxastatus="signedin"] > #appMenu-fxa-status > #appMenu-fxa-label {
+  /* 12px space before the avatar, then 16px for the avatar */
+  padding-inline-start: 28px;
+  margin-inline-start: -28px;
+}
+#appMenu-fxa-container[fxastatus="signedin"] > #appMenu-fxa-status {
+  margin-inline-end: 10px;
+}
+
+#appMenu-fxa-container[fxastatus="signedin"] > #appMenu-fxa-status > #appMenu-fxa-avatar {
+  margin-inline-start: 12px;
+}
+
+#appMenu-fxa-container[fxastatus="signedin"] > toolbarseparator {
+  -moz-appearance: none;
+  height: 24px;
+  margin: 0;
+  border: 0 none;
+  width: 1px;
+  /* Use background: rather than background-color: to override background-image
+   * styles on OS X */
+  background: var(--arrowpanel-dimmed);
+}
+
+/* Error states */
+#appMenu-fxa-container[fxastatus="unverified"] > #appMenu-fxa-status > #appMenu-fxa-label,
+#appMenu-fxa-container[fxastatus="login-failed"] > #appMenu-fxa-status > #appMenu-fxa-label {
+  list-style-image: url(chrome://browser/skin/warning.svg);
+  -moz-image-region: rect(0, 16px, 16px, 0);
+}
+
+#appMenu-fxa-container[fxastatus="login-failed"],
+#appMenu-fxa-container[fxastatus="unverified"] {
+  background-color: var(--appmenu-yellow-warning-color);
+  border-top: 1px solid var(--appmenu-yellow-warning-border-color);
+  border-bottom: 1px solid var(--appmenu-yellow-warning-border-color);
+}
+
+#appMenu-fxa-container[fxastatus="login-failed"] > #appMenu-fxa-status:hover,
+#appMenu-fxa-container[fxastatus="unverified"] > #appMenu-fxa-status:hover {
+  background-color: var(--appmenu-yellow-warning-hover-color);
+}
+
+#appMenu-fxa-container[fxastatus="login-failed"] > #appMenu-fxa-status:hover:active,
+#appMenu-fxa-container[fxastatus="unverified"] > #appMenu-fxa-status:hover:active {
+  background-color: var(--appmenu-yellow-warning-active-color);
+}
+
 #PanelUI-help,
 #PanelUI-quit {
   min-width: 46px;
 }
 
 .addon-banner-item > .toolbarbutton-text,
 .panel-banner-item > .toolbarbutton-text,
 #PanelUI-fxa-label > .toolbarbutton-text,
@@ -752,17 +863,17 @@ toolbarpaletteitem[place="palette"] > to
 #PanelUI-fxa-label,
 .addon-banner-item,
 #PanelUI-customize {
   flex: 1;
   padding-inline-start: 15px;
   border-inline-start-style: none;
 }
 
-#PanelUI-footer-fxa[fxastatus="signedin"] > #PanelUI-fxa-status > #PanelUI-fxa-label {
+#PanelUI-fxa-container[fxastatus="signedin"] > #PanelUI-fxa-status > #PanelUI-fxa-label {
   padding-inline-start: 0px;
 }
 
 /* descend from #PanelUI-footer to add specificity, or else the
    padding-inline-start will be overridden */
 #PanelUI-footer > .panel-banner-item {
   width: calc(@menuPanelWidth@ + 30px);
   padding-inline-start: 15px;
@@ -914,18 +1025,18 @@ toolbarpaletteitem[place="palette"] > to
 #PanelUI-fxa-icon,
 .addon-banner-item,
 #PanelUI-customize,
 #PanelUI-help,
 #PanelUI-quit {
   -moz-image-region: rect(0, 16px, 16px, 0);
 }
 
-#PanelUI-footer-fxa[fxastatus="signedin"] > #PanelUI-fxa-status > #PanelUI-fxa-label > .toolbarbutton-icon,
-#PanelUI-footer-fxa:not([fxastatus="signedin"]) > #PanelUI-fxa-status > #PanelUI-fxa-avatar {
+#PanelUI-fxa-container[fxastatus="signedin"] > #PanelUI-fxa-status > #PanelUI-fxa-label > .toolbarbutton-icon,
+#PanelUI-fxa-container:not([fxastatus="signedin"]) > #PanelUI-fxa-status > #PanelUI-fxa-avatar {
   display: none;
 }
 
 #PanelUI-fxa-avatar {
   width: 32px;
   height: 32px;
   border-radius: 50%;
   background-repeat: no-repeat;
@@ -933,17 +1044,17 @@ toolbarpaletteitem[place="palette"] > to
   background-size: contain;
   align-self: center;
   margin: 0px 7px;
   padding: 0px;
   border: 0px none;
   margin-inline-end: 0;
 }
 
-#PanelUI-footer-fxa > #PanelUI-fxa-status > #PanelUI-fxa-avatar {
+#PanelUI-fxa-container > #PanelUI-fxa-status > #PanelUI-fxa-avatar {
   list-style-image: url(chrome://browser/skin/fxa/default-avatar.svg);
 }
 
 #PanelUI-customize:hover,
 #PanelUI-help:not([disabled]):hover,
 #PanelUI-quit:not([disabled]):hover {
   -moz-image-region: rect(0, 32px, 16px, 16px);
 }
@@ -984,29 +1095,29 @@ toolbarpaletteitem[place="palette"] > to
 
 #PanelUI-fxa-status:hover,
 #PanelUI-fxa-status:hover:active,
 #PanelUI-fxa-icon:hover,
 #PanelUI-fxa-icon:hover:active {
   outline: none;
 }
 
-#PanelUI-footer-fxa[fxastatus="login-failed"],
-#PanelUI-footer-fxa[fxastatus="unverified"] {
+#PanelUI-fxa-container[fxastatus="login-failed"],
+#PanelUI-fxa-container[fxastatus="unverified"] {
   background-color: hsl(42,94%,88%);
   border-top: 1px solid hsl(42,94%,70%);
 }
 
-#PanelUI-footer-fxa[fxastatus="login-failed"] > #PanelUI-fxa-status:hover,
-#PanelUI-footer-fxa[fxastatus="unverified"] > #PanelUI-fxa-status:hover {
+#PanelUI-fxa-container[fxastatus="login-failed"] > #PanelUI-fxa-status:hover,
+#PanelUI-fxa-container[fxastatus="unverified"] > #PanelUI-fxa-status:hover {
   background-color: hsl(42,94%,85%);
 }
 
-#PanelUI-footer-fxa[fxastatus="login-failed"] > #PanelUI-fxa-status:hover:active,
-#PanelUI-footer-fxa[fxastatus="unverified"] > #PanelUI-fxa-status:hover:active {
+#PanelUI-fxa-container[fxastatus="login-failed"] > #PanelUI-fxa-status:hover:active,
+#PanelUI-fxa-container[fxastatus="unverified"] > #PanelUI-fxa-status:hover:active {
   background-color: hsl(42,94%,82%);
   box-shadow: 0 1px 0 hsla(210,4%,10%,.05) inset;
 }
 
 .panel-banner-item {
   color: black;
   background-color: hsla(96,65%,75%,.5);
 }
@@ -1319,16 +1430,20 @@ menuitem.panel-subview-footer@menuStateA
 
 @media (min-resolution: 1.1dppx) {
   #PanelUI-remotetabs-tabslist > toolbarbutton[itemtype="tab"],
   #PanelUI-historyItems > toolbarbutton {
     list-style-image: url("chrome://mozapps/skin/places/defaultFavicon@2x.png");
   }
 }
 
+#appMenu-fxa-avatar,
+#appMenu-fxa-label > .toolbarbutton-icon,
+#appMenu-fxa-icon > .toolbarbutton-icon,
+#PanelUI-containersItems > .subviewbutton > .toolbarbutton-icon,
 #PanelUI-remotetabs-tabslist > toolbarbutton[itemtype="tab"] > .toolbarbutton-icon,
 #PanelUI-recentlyClosedWindows > toolbarbutton > .toolbarbutton-icon,
 #PanelUI-recentlyClosedTabs > toolbarbutton > .toolbarbutton-icon,
 #PanelUI-historyItems > toolbarbutton > .toolbarbutton-icon {
   width: 16px;
   height: 16px;
 }
 
@@ -1559,21 +1674,16 @@ toolbaritem[overflowedItem=true],
   -moz-appearance: none;
   margin-inline-end: 3px;
 }
 
 menuitem[checked="true"].subviewbutton > .menu-iconic-left {
   visibility: hidden;
 }
 
-#PanelUI-containersItems > .subviewbutton > .toolbarbutton-icon {
-  width: 16px;
-  height: 16px;
-}
-
 .panel-mainview[panelid=customizationui-widget-panel],
 #customizationui-widget-multiview > .panel-viewcontainer,
 #customizationui-widget-multiview > .panel-viewcontainer > .panel-viewstack,
 #PanelUI-panicView > .panel-subview-body,
 #PanelUI-panicView {
   overflow: visible;
 }