diff --git a/browser/base/content/browser-fxaccounts.js b/browser/base/content/browser-fxaccounts.js
index 292e2fdbca42..0c827d87d90b 100644
--- a/browser/base/content/browser-fxaccounts.js
+++ b/browser/base/content/browser-fxaccounts.js
@@ -36,12 +36,28 @@ let gFxAccounts = {
this.FxAccountsCommon.ONVERIFIED_NOTIFICATION,
this.FxAccountsCommon.ONLOGOUT_NOTIFICATION,
"weave:notification:removed",
+ this.FxAccountsCommon.ON_PROFILE_CHANGE_NOTIFICATION,
];
},
- get button() {
- delete this.button;
- return this.button = document.getElementById("PanelUI-fxa-status");
+ get panelUIFooter() {
+ return document.getElementById("PanelUI-footer-fxa");
+ },
+
+ get panelUIStatus() {
+ return document.getElementById("PanelUI-fxa-status");
+ },
+
+ get panelUIAvatar() {
+ return document.getElementById("PanelUI-fxa-avatar");
+ },
+
+ get panelUILabel() {
+ return document.getElementById("PanelUI-fxa-label");
+ },
+
+ get panelUIIcon() {
+ return document.getElementById("PanelUI-fxa-icon");
},
get strings() {
@@ -135,6 +151,9 @@ let gFxAccounts = {
this.fxaMigrator.recordTelemetry(this.fxaMigrator.TELEMETRY_DECLINED);
}
break;
+ case this.FxAccountsCommon.ONPROFILE_IMAGE_CHANGE_NOTIFICATION:
+ this.updateUI();
+ break;
default:
this.updateUI();
break;
@@ -211,59 +230,96 @@ let gFxAccounts = {
return;
}
+ let profileInfoEnabled = false;
+ try {
+ profileInfoEnabled = Services.prefs.getBoolPref("identity.fxaccounts.profile_image.enabled");
+ } catch (e) { }
+
// Bail out if FxA is disabled.
if (!this.weave.fxAccountsEnabled) {
// When migration transitions from needs-verification to the null state,
// fxAccountsEnabled is false because migration has not yet finished. In
// that case, hide the button. We'll get another notification with a null
// state once migration is complete.
- this.button.hidden = true;
- this.button.removeAttribute("fxastatus");
+ this.panelUIFooter.removeAttribute("fxastatus");
return;
}
- // FxA is enabled, show the widget.
- this.button.hidden = false;
-
// Make sure the button is disabled in customization mode.
if (this._inCustomizationMode) {
- this.button.setAttribute("disabled", "true");
+ this.panelUILabel.setAttribute("disabled", "true");
+ this.panelUIAvatar.setAttribute("disabled", "true");
+ this.panelUIIcon.setAttribute("disabled", "true");
} else {
- this.button.removeAttribute("disabled");
+ this.panelUILabel.removeAttribute("disabled");
+ this.panelUIAvatar.removeAttribute("disabled");
+ this.panelUIIcon.removeAttribute("disabled");
}
- let defaultLabel = this.button.getAttribute("defaultlabel");
- let errorLabel = this.button.getAttribute("errorlabel");
+ let defaultLabel = this.panelUIStatus.getAttribute("defaultlabel");
+ let errorLabel = this.panelUIStatus.getAttribute("errorlabel");
+ let signedInTooltiptext = this.panelUIStatus.getAttribute("signedinTooltiptext");
// If the user is signed into their Firefox account and we are not
// currently in customization mode, show their email address.
- let doUpdate = userData => {
+ let doUpdate = (profile, userData) => {
+
// Reset the button to its original state.
- this.button.setAttribute("label", defaultLabel);
- this.button.removeAttribute("tooltiptext");
- this.button.removeAttribute("fxastatus");
+ this.panelUILabel.setAttribute("label", defaultLabel);
+ this.panelUILabel.removeAttribute("tooltiptext");
+ this.panelUIAvatar.removeAttribute("tooltiptext");
+ this.panelUIFooter.removeAttribute("fxastatus");
+ this.panelUIFooter.removeAttribute("fxaprofileimage");
+ this.panelUIAvatar.style.removeProperty("background-image");
if (!this._inCustomizationMode) {
if (this.loginFailed) {
let tooltipDescription = this.strings.formatStringFromName("reconnectDescription", [userData.email], 1);
- this.button.setAttribute("fxastatus", "error");
- this.button.setAttribute("label", errorLabel);
- this.button.setAttribute("tooltiptext", tooltipDescription);
- } else if (userData) {
- this.button.setAttribute("fxastatus", "signedin");
- this.button.setAttribute("label", userData.email);
- this.button.setAttribute("tooltiptext", userData.email);
+ this.panelUIFooter.setAttribute("fxastatus", "error");
+ this.panelUILabel.setAttribute("label", errorLabel);
+ this.panelUIStatus.setAttribute("tooltiptext", tooltipDescription);
+ this.panelUIAvatar.setAttribute("tooltiptext", tooltipDescription);
+ } else {
+ let label = profile && profile.displayName ? profile.displayName : userData.email;
+ this.panelUIFooter.setAttribute("fxastatus", "signedin");
+ this.panelUILabel.setAttribute("label", label);
+ this.panelUIStatus.setAttribute("tooltiptext", signedInTooltiptext);
+ this.panelUIAvatar.setAttribute("tooltiptext", signedInTooltiptext);
+ }
+ if (profileInfoEnabled) {
+ this.panelUIFooter.setAttribute("fxaprofileimage", "enabled");
+ if (profile && profile.avatar) {
+ let img = new Image();
+ // Make sure the image is available before attempting to display it
+ img.onload = () => {
+ this.panelUIFooter.setAttribute("fxaprofileimage", "set");
+ this.panelUIAvatar.style.backgroundImage = "url('" + profile.avatar + "')";
+ };
+ img.src = profile.avatar;
+ }
}
}
}
- fxAccounts.getSignedInUser().then(userData => {
- doUpdate(userData);
- }).then(null, error => {
+ let userData, profile;
+ // Calling getSignedInUserProfile() without a user logged in causes log
+ // noise that looks like an actual error...
+ fxAccounts.getSignedInUser().then(data => {
+ userData = data;
+ if (!userData) {
+ return null;
+ }
+ return fxAccounts.getSignedInUserProfile();
+ }).then(data => {
+ profile = data;
+ // profile may be null here, but that's expected.
+ doUpdate(profile, userData);
+ }).catch(error => {
// This is most likely in tests, were we quickly log users in and out.
// The most likely scenario is a user logged out, so reflect that.
// Bug 995134 calls for better errors so we could retry if we were
// sure this was the failure reason.
- doUpdate(null);
+ this.FxAccountsCommon.log.error("Error updating FxA profile", error);
+ doUpdate(profile, userData);
});
},
@@ -274,7 +330,7 @@ let gFxAccounts = {
case this.fxaMigrator.STATE_USER_FXA:
status = "migrate-signup";
label = this.strings.formatStringFromName("needUserShort",
- [this.button.getAttribute("fxabrandname")], 1);
+ [this.panelUILabel.getAttribute("fxabrandname")], 1);
break;
case this.fxaMigrator.STATE_USER_FXA_VERIFIED:
status = "migrate-verify";
@@ -283,9 +339,8 @@ let gFxAccounts = {
1);
break;
}
- this.button.label = label;
- this.button.hidden = false;
- this.button.setAttribute("fxastatus", status);
+ this.panelUILabel.label = label;
+ this.panelUIFooter.setAttribute("fxastatus", status);
}),
updateMigrationNotification: Task.async(function* () {
@@ -352,10 +407,9 @@ let gFxAccounts = {
Weave.Notifications.replaceTitle(note);
}),
- onMenuPanelCommand: function (event) {
- let button = event.originalTarget;
+ onMenuPanelCommand: function () {
- switch (button.getAttribute("fxastatus")) {
+ switch (this.panelUIFooter.getAttribute("fxastatus")) {
case "signedin":
this.openPreferences();
break;
diff --git a/browser/base/content/browser-syncui.js b/browser/base/content/browser-syncui.js
index aa97ac41a876..062de3267e93 100644
--- a/browser/base/content/browser-syncui.js
+++ b/browser/base/content/browser-syncui.js
@@ -166,8 +166,15 @@ let gSyncUI = {
return;
let syncButton = document.getElementById("sync-button");
- if (needsSetup && syncButton)
- syncButton.removeAttribute("tooltiptext");
+ let statusButton = document.getElementById("PanelUI-fxa-icon");
+ if (needsSetup) {
+ if (syncButton) {
+ syncButton.removeAttribute("tooltiptext");
+ }
+ if (statusButton) {
+ statusButton.removeAttribute("tooltiptext");
+ }
+ }
this._updateLastSyncTime();
},
@@ -184,9 +191,9 @@ let gSyncUI = {
if (button) {
button.setAttribute("status", "active");
}
- button = document.getElementById("PanelUI-fxa-status");
- if (button) {
- button.setAttribute("syncstatus", "active");
+ let container = document.getElementById("PanelUI-footer-fxa");
+ if (container) {
+ container.setAttribute("syncstatus", "active");
}
}
},
@@ -210,9 +217,9 @@ let gSyncUI = {
if (syncButton) {
syncButton.removeAttribute("status");
}
- let panelHorizontalButton = document.getElementById("PanelUI-fxa-status");
- if (panelHorizontalButton) {
- panelHorizontalButton.removeAttribute("syncstatus");
+ let fxaContainer = document.getElementById("PanelUI-footer-fxa");
+ if (fxaContainer) {
+ fxaContainer.removeAttribute("syncstatus");
}
},
@@ -418,8 +425,7 @@ let gSyncUI = {
return;
let syncButton = document.getElementById("sync-button");
- if (!syncButton)
- return;
+ let statusButton = document.getElementById("PanelUI-fxa-icon");
let lastSync;
try {
@@ -435,7 +441,12 @@ let gSyncUI = {
}
catch (e) { };
if (!lastSync || this._needsSetup()) {
- syncButton.removeAttribute("tooltiptext");
+ if (syncButton) {
+ syncButton.removeAttribute("tooltiptext");
+ }
+ if (statusButton) {
+ statusButton.removeAttribute("tooltiptext");
+ }
return;
}
@@ -444,7 +455,12 @@ let gSyncUI = {
let lastSyncLabel =
this._stringBundle.formatStringFromName("lastSync2.label", [lastSyncDateString], 1);
- syncButton.setAttribute("tooltiptext", lastSyncLabel);
+ if (syncButton) {
+ syncButton.setAttribute("tooltiptext", lastSyncLabel);
+ }
+ if (statusButton) {
+ statusButton.setAttribute("tooltiptext", lastSyncLabel);
+ }
},
clearError: function SUI_clearError(errorString) {
diff --git a/browser/base/content/test/general/browser_fxa_migrate.js b/browser/base/content/test/general/browser_fxa_migrate.js
index 7612de469115..558b06410ed6 100644
--- a/browser/base/content/test/general/browser_fxa_migrate.js
+++ b/browser/base/content/test/general/browser_fxa_migrate.js
@@ -9,24 +9,24 @@ Cu.import("resource://services-sync/FxaMigrator.jsm", imports);
add_task(function* test() {
// Fake the state where we need an FxA user.
- let buttonPromise = promiseButtonMutation();
+ let fxaPanelUIPromise = promiseButtonMutation();
Services.obs.notifyObservers(null, STATE_CHANGED_TOPIC,
imports.fxaMigrator.STATE_USER_FXA);
- let buttonState = yield buttonPromise;
- assertButtonState(buttonState, "migrate-signup", true);
+ let buttonState = yield fxaPanelUIPromise;
+ assertButtonState(buttonState, "migrate-signup");
Assert.ok(Weave.Notifications.notifications.some(n => {
return n.title == NOTIFICATION_TITLE;
}), "Needs-user notification should be present");
// Fake the state where we need a verified FxA user.
- buttonPromise = promiseButtonMutation();
+ fxaPanelUIPromise = promiseButtonMutation();
let email = Cc["@mozilla.org/supports-string;1"].
createInstance(Ci.nsISupportsString);
email.data = "foo@example.com";
Services.obs.notifyObservers(email, STATE_CHANGED_TOPIC,
imports.fxaMigrator.STATE_USER_FXA_VERIFIED);
- buttonState = yield buttonPromise;
- assertButtonState(buttonState, "migrate-verify", true,
+ buttonState = yield fxaPanelUIPromise;
+ assertButtonState(buttonState, "migrate-verify",
"foo@example.com not verified");
let note = Weave.Notifications.notifications.find(n => {
return n.title == NOTIFICATION_TITLE;
@@ -36,23 +36,22 @@ add_task(function* test() {
"Needs-verification notification should include email");
// Fake the state where no migration is needed.
- buttonPromise = promiseButtonMutation();
+ fxaPanelUIPromise = promiseButtonMutation();
Services.obs.notifyObservers(null, STATE_CHANGED_TOPIC, null);
- buttonState = yield buttonPromise;
+ buttonState = yield fxaPanelUIPromise;
// In this case, the front end has called fxAccounts.getSignedInUser() to
// update the button label and status. But since there isn't actually a user,
// the button is left with no fxastatus.
- assertButtonState(buttonState, "", true);
+ assertButtonState(buttonState, "");
Assert.ok(!Weave.Notifications.notifications.some(n => {
return n.title == NOTIFICATION_TITLE;
}), "Migration notifications should no longer be present");
});
-function assertButtonState(buttonState, expectedStatus, expectedVisible,
+function assertButtonState(buttonState, expectedStatus,
expectedLabel=undefined) {
Assert.equal(buttonState.fxastatus, expectedStatus,
"Button fxstatus attribute");
- Assert.equal(!buttonState.hidden, expectedVisible, "Button visibility");
if (expectedLabel !== undefined) {
Assert.equal(buttonState.label, expectedLabel, "Button label");
}
@@ -66,12 +65,11 @@ function promiseButtonMutation() {
if (mutations.some(m => m.attributeName == "fxastatus")) {
obs.disconnect();
resolve({
- fxastatus: gFxAccounts.button.getAttribute("fxastatus"),
- hidden: gFxAccounts.button.hidden,
- label: gFxAccounts.button.label,
+ fxastatus: gFxAccounts.panelUIFooter.getAttribute("fxastatus"),
+ label: gFxAccounts.panelUILabel.label,
});
}
});
- obs.observe(gFxAccounts.button, { attributes: true });
+ obs.observe(gFxAccounts.panelUIFooter, { attributes: true });
});
}
diff --git a/browser/base/content/test/general/browser_syncui.js b/browser/base/content/test/general/browser_syncui.js
index 4c91ff9b6e82..cc8cbdab97ca 100644
--- a/browser/base/content/test/general/browser_syncui.js
+++ b/browser/base/content/test/general/browser_syncui.js
@@ -255,13 +255,13 @@ add_task(function* testRLLoginErrorRemains() {
function checkButtonsStatus(shouldBeActive) {
let button = document.getElementById("sync-button");
- let panelbutton = document.getElementById("PanelUI-fxa-status");
+ let fxaContainer = document.getElementById("PanelUI-footer-fxa");
if (shouldBeActive) {
Assert.equal(button.getAttribute("status"), "active");
- Assert.equal(panelbutton.getAttribute("syncstatus"), "active");
+ Assert.equal(fxaContainer.getAttribute("syncstatus"), "active");
} else {
Assert.ok(!button.hasAttribute("status"));
- Assert.ok(!panelbutton.hasAttribute("syncstatus"));
+ Assert.ok(!fxaContainer.hasAttribute("syncstatus"));
}
}
diff --git a/browser/components/customizableui/content/panelUI.inc.xul b/browser/components/customizableui/content/panelUI.inc.xul
index df5db6de13d6..b33176f04f05 100644
--- a/browser/components/customizableui/content/panelUI.inc.xul
+++ b/browser/components/customizableui/content/panelUI.inc.xul
@@ -20,12 +20,21 @@
oncommand="gMenuButtonUpdateBadge.onMenuPanelCommand(event);"
wrap="true"
hidden="true"/>
-
+