Bug 1298011 - Update the Synced Tabs sidebar UI when account password changed. r?markh draft
authorEdouard Oger <eoger@fastmail.com>
Thu, 05 Jan 2017 16:54:12 -0500
changeset 456518 6276d4ecebff0ab0352993f05d5806697c16f65c
parent 456417 b548da4e16f067e5b69349376e37b2db97983cf7
child 541257 c1fd2dc55da777620cee805b92bf00278f72cebc
push id40530
push userbmo:eoger@fastmail.com
push dateThu, 05 Jan 2017 21:54:13 +0000
reviewersmarkh
bugs1298011
milestone53.0a1
Bug 1298011 - Update the Synced Tabs sidebar UI when account password changed. r?markh MozReview-Commit-ID: 2cJK8ZAQH5R
browser/base/content/browser-syncui.js
browser/components/syncedtabs/SyncedTabsDeckComponent.js
browser/components/syncedtabs/test/xpcshell/test_SyncedTabsDeckComponent.js
--- a/browser/base/content/browser-syncui.js
+++ b/browser/base/content/browser-syncui.js
@@ -132,25 +132,25 @@ var gSyncUI = {
     // Otherwise we are configured for legacy Sync, which has no verification
     // concept.
     return Promise.resolve(false);
   },
 
   // Note that we don't show login errors in a notification bar here, but do
   // still need to track a login-failed state so the "Tools" menu updates
   // with the correct state.
-  _loginFailed() {
+  loginFailed() {
     // If Sync isn't already ready, we don't want to force it to initialize
     // by referencing Weave.Status - and it isn't going to be accurate before
     // Sync is ready anyway.
     if (!this.weaveService.ready) {
-      this.log.debug("_loginFailed has sync not ready, so returning false");
+      this.log.debug("loginFailed has sync not ready, so returning false");
       return false;
     }
-    this.log.debug("_loginFailed has sync state=${sync}",
+    this.log.debug("loginFailed has sync state=${sync}",
                    { sync: Weave.Status.login});
     return Weave.Status.login == Weave.LOGIN_FAILED_LOGIN_REJECTED;
   },
 
   // Kick off an update of the UI - does *not* return a promise.
   updateUI() {
     this._promiseUpdateUI().catch(err => {
       this.log.error("updateUI failed", err);
@@ -158,17 +158,17 @@ var gSyncUI = {
   },
 
   // Updates the UI - returns a promise.
   _promiseUpdateUI() {
     return this._needsSetup().then(needsSetup => {
       if (!gBrowser)
         return Promise.resolve();
 
-      let loginFailed = this._loginFailed();
+      let loginFailed = this.loginFailed();
 
       // 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;
 
       if (CloudSync && CloudSync.ready && CloudSync().adapters.count) {
         document.getElementById("sync-syncnow-state").hidden = false;
@@ -261,17 +261,17 @@ var gSyncUI = {
     });
   },
 
   // Handle clicking the toolbar button - which either opens the Sync setup
   // pages or forces a sync now. Does *not* return a promise as it is called
   // via the UI.
   handleToolbarButton() {
     this._needsSetup().then(needsSetup => {
-      if (needsSetup || this._loginFailed()) {
+      if (needsSetup || this.loginFailed()) {
         this.openSetup();
       } else {
         this.doSync();
       }
     }).catch(err => {
       this.log.error("Failed to handle toolbar button command", err);
     });
   },
@@ -375,17 +375,17 @@ var gSyncUI = {
 
     let email;
     try {
       email = Services.prefs.getCharPref("services.sync.username");
     } catch (ex) {}
 
     let needsSetup = yield this._needsSetup();
     let needsVerification = yield this._needsVerification();
-    let loginFailed = this._loginFailed();
+    let loginFailed = this.loginFailed();
     // This is a little messy as the Sync buttons are 1/2 Sync related and
     // 1/2 FxA related - so for some strings we use Sync strings, but for
     // others we reach into gFxAccounts for strings.
     let tooltiptext;
     if (needsVerification) {
       // "needs verification"
       tooltiptext = gFxAccounts.strings.formatStringFromName("verifyDescription", [email], 1);
     } else if (needsSetup) {
--- a/browser/components/syncedtabs/SyncedTabsDeckComponent.js
+++ b/browser/components/syncedtabs/SyncedTabsDeckComponent.js
@@ -67,16 +67,17 @@ SyncedTabsDeckComponent.prototype = {
 
   get container() {
     return this._deckView ? this._deckView.container : null;
   },
 
   init() {
     Services.obs.addObserver(this, this._SyncedTabs.TOPIC_TABS_CHANGED, false);
     Services.obs.addObserver(this, FxAccountsCommon.ONLOGIN_NOTIFICATION, false);
+    Services.obs.addObserver(this, "weave:service:login:change", false);
 
     // Go ahead and trigger sync
     this._SyncedTabs.syncTabs()
                     .catch(Cu.reportError);
 
     this._deckView = new this._DeckView(this._window, this.tabListComponent, {
       onAndroidClick: event => this.openAndroidLink(event),
       oniOSClick: event => this.openiOSLink(event),
@@ -89,42 +90,44 @@ SyncedTabsDeckComponent.prototype = {
     this._deckStore.setPanels(Object.keys(this.PANELS).map(k => this.PANELS[k]));
     // Set the initial panel to display
     this.updatePanel();
   },
 
   uninit() {
     Services.obs.removeObserver(this, this._SyncedTabs.TOPIC_TABS_CHANGED);
     Services.obs.removeObserver(this, FxAccountsCommon.ONLOGIN_NOTIFICATION);
+    Services.obs.removeObserver(this, "weave:service:login:change");
     this._deckView.destroy();
   },
 
   observe(subject, topic, data) {
     switch (topic) {
       case this._SyncedTabs.TOPIC_TABS_CHANGED:
         this._syncedTabsListStore.getData();
         this.updatePanel();
         break;
       case FxAccountsCommon.ONLOGIN_NOTIFICATION:
+      case "weave:service:login:change":
         this.updatePanel();
         break;
       default:
         break;
     }
   },
 
   // There's no good way to mock fxAccounts in browser tests where it's already
   // been instantiated, so we have this method for stubbing.
   _accountStatus() {
     return this._fxAccounts.accountStatus();
   },
 
   getPanelStatus() {
     return this._accountStatus().then(exists => {
-      if (!exists) {
+      if (!exists || this._getChromeWindow(this._window).gSyncUI.loginFailed()) {
         return this.PANELS.NOT_AUTHED_INFO;
       }
       if (!this._SyncedTabs.isConfiguredToSyncTabs) {
         return this.PANELS.TABS_DISABLED;
       }
       if (!this._SyncedTabs.hasSyncedThisSession) {
         return this.PANELS.TABS_FETCHING;
       }
--- a/browser/components/syncedtabs/test/xpcshell/test_SyncedTabsDeckComponent.js
+++ b/browser/components/syncedtabs/test/xpcshell/test_SyncedTabsDeckComponent.js
@@ -117,46 +117,68 @@ add_task(function* testObserver() {
   Assert.ok(listStore.getData.called, "gets list data");
   Assert.ok(component.updatePanel.calledTwice, "triggers panel update");
 
   Services.obs.notifyObservers(null, FxAccountsCommon.ONLOGIN_NOTIFICATION, "");
 
   Assert.ok(component.observe.calledWith(null, FxAccountsCommon.ONLOGIN_NOTIFICATION, ""),
     "component is notified of login");
   Assert.equal(component.updatePanel.callCount, 3, "triggers panel update again");
+
+  Services.obs.notifyObservers(null, "weave:service:login:change", "");
+
+  Assert.ok(component.observe.calledWith(null, "weave:service:login:change", ""),
+    "component is notified of login change");
+  Assert.equal(component.updatePanel.callCount, 4, "triggers panel update again");
 });
 
 add_task(function* testPanelStatus() {
   let deckStore = new SyncedTabsDeckStore();
   let listStore = new SyncedTabsListStore();
   let listComponent = {};
   let fxAccounts = {
     accountStatus() {}
   };
   let SyncedTabsMock = {
     getTabClients() {}
   };
+  let loginFailed = false;
+  let chromeWindowMock = {
+    gSyncUI: {
+      loginFailed() {
+        return loginFailed;
+      }
+    }
+  };
+  let getChromeWindowMock = sinon.stub();
+  getChromeWindowMock.returns(chromeWindowMock);
 
   sinon.stub(listStore, "getData");
 
 
   let component = new SyncedTabsDeckComponent({
     fxAccounts,
     deckStore,
     listComponent,
     SyncedTabs: SyncedTabsMock,
+    getChromeWindowMock
   });
 
   let isAuthed = false;
   sinon.stub(fxAccounts, "accountStatus", () => Promise.resolve(isAuthed));
   let result = yield component.getPanelStatus();
   Assert.equal(result, component.PANELS.NOT_AUTHED_INFO);
 
   isAuthed = true;
 
+  loginFailed = true;
+  result = yield component.getPanelStatus();
+  Assert.equal(result, component.PANELS.NOT_AUTHED_INFO);
+  loginFailed = false;
+
   SyncedTabsMock.isConfiguredToSyncTabs = false;
   result = yield component.getPanelStatus();
   Assert.equal(result, component.PANELS.TABS_DISABLED);
 
   SyncedTabsMock.isConfiguredToSyncTabs = true;
 
   SyncedTabsMock.hasSyncedThisSession = false;
   result = yield component.getPanelStatus();