Bug 1354084 - add sync / Firefox account button to hamburger panel, r=mikedeboer
authorGijs Kruitbosch <gijskruitbosch@gmail.com>
Tue, 09 May 2017 15:56:19 -0400
changeset 359974 e7575b0dd085856c842615e7a1e7e9bfb12e453d
parent 359973 b04dbc3924b9920ad25a0e678e45afc7aa314920
child 359975 e6fa2aa73b4631408611e61c715c5f1dc28db49a
push id43175
push usergijskruitbosch@gmail.com
push dateMon, 22 May 2017 20:03:19 +0000
treeherderautoland@e7575b0dd085 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmikedeboer
bugs1354084
milestone55.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 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);
 }
@@ -1316,16 +1427,20 @@ menuitem.panel-subview-footer@menuStateA
   color: GrayText;
 }
 
 #PanelUI-remotetabs-tabslist > toolbarbutton[itemtype="tab"],
 #PanelUI-historyItems > toolbarbutton {
   list-style-image: url("chrome://mozapps/skin/places/defaultFavicon.svg");
 }
 
+#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;
 }
 
@@ -1556,21 +1671,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;
 }