browser/base/content/browser-syncui.js

Thu, 15 Jan 2015 15:55:04 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 15 Jan 2015 15:55:04 +0100
branch
TOR_BUG_9701
changeset 9
a63d609f5ebe
permissions
-rw-r--r--

Back out 97036ab72558 which inappropriately compared turds to third parties.

     1 # This Source Code Form is subject to the terms of the Mozilla Public
     2 # License, v. 2.0. If a copy of the MPL was not distributed with this
     3 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
     5 // gSyncUI handles updating the tools menu and displaying notifications.
     6 let gSyncUI = {
     7   DEFAULT_EOL_URL: "https://www.mozilla.org/firefox/?utm_source=synceol",
     9   _obs: ["weave:service:sync:start",
    10          "weave:service:quota:remaining",
    11          "weave:service:setup-complete",
    12          "weave:service:login:start",
    13          "weave:service:login:finish",
    14          "weave:service:logout:finish",
    15          "weave:service:start-over",
    16          "weave:service:start-over:finish",
    17          "weave:ui:login:error",
    18          "weave:ui:sync:error",
    19          "weave:ui:sync:finish",
    20          "weave:ui:clear-error",
    21          "weave:eol",
    22   ],
    24   _unloaded: false,
    26   init: function () {
    27     Cu.import("resource://services-common/stringbundle.js");
    29     // Proceed to set up the UI if Sync has already started up.
    30     // Otherwise we'll do it when Sync is firing up.
    31     let xps = Components.classes["@mozilla.org/weave/service;1"]
    32                                 .getService(Components.interfaces.nsISupports)
    33                                 .wrappedJSObject;
    34     if (xps.ready) {
    35       this.initUI();
    36       return;
    37     }
    39     Services.obs.addObserver(this, "weave:service:ready", true);
    41     // Remove the observer if the window is closed before the observer
    42     // was triggered.
    43     window.addEventListener("unload", function onUnload() {
    44       gSyncUI._unloaded = true;
    45       window.removeEventListener("unload", onUnload, false);
    46       Services.obs.removeObserver(gSyncUI, "weave:service:ready");
    48       if (Weave.Status.ready) {
    49         gSyncUI._obs.forEach(function(topic) {
    50           Services.obs.removeObserver(gSyncUI, topic);
    51         });
    52       }
    53     }, false);
    54   },
    56   initUI: function SUI_initUI() {
    57     // If this is a browser window?
    58     if (gBrowser) {
    59       this._obs.push("weave:notification:added");
    60     }
    62     this._obs.forEach(function(topic) {
    63       Services.obs.addObserver(this, topic, true);
    64     }, this);
    66     if (gBrowser && Weave.Notifications.notifications.length) {
    67       this.initNotifications();
    68     }
    69     this.updateUI();
    70   },
    72   initNotifications: function SUI_initNotifications() {
    73     const XULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
    74     let notificationbox = document.createElementNS(XULNS, "notificationbox");
    75     notificationbox.id = "sync-notifications";
    76     notificationbox.setAttribute("flex", "1");
    78     let bottombox = document.getElementById("browser-bottombox");
    79     bottombox.insertBefore(notificationbox, bottombox.firstChild);
    81     // Force a style flush to ensure that our binding is attached.
    82     notificationbox.clientTop;
    84     // notificationbox will listen to observers from now on.
    85     Services.obs.removeObserver(this, "weave:notification:added");
    86   },
    88   _needsSetup: function SUI__needsSetup() {
    89     // We want to treat "account needs verification" as "needs setup". So
    90     // "reach in" to Weave.Status._authManager to check whether we the signed-in
    91     // user is verified.
    92     // Referencing Weave.Status spins a nested event loop to initialize the
    93     // authManager, so this should always return a value directly.
    94     // This only applies to fxAccounts-based Sync.
    95     if (Weave.Status._authManager._signedInUser) {
    96       // If we have a signed in user already, and that user is not verified,
    97       // revert to the "needs setup" state.
    98       if (!Weave.Status._authManager._signedInUser.verified) {
    99         return true;
   100       }
   101     }
   103     let firstSync = "";
   104     try {
   105       firstSync = Services.prefs.getCharPref("services.sync.firstSync");
   106     } catch (e) { }
   108     return Weave.Status.checkSetup() == Weave.CLIENT_NOT_CONFIGURED ||
   109            firstSync == "notReady";
   110   },
   112   _loginFailed: function () {
   113     return Weave.Status.login == Weave.LOGIN_FAILED_LOGIN_REJECTED;
   114   },
   116   updateUI: function SUI_updateUI() {
   117     let needsSetup = this._needsSetup();
   118     let loginFailed = this._loginFailed();
   120     // Start off with a clean slate
   121     document.getElementById("sync-reauth-state").hidden = true;
   122     document.getElementById("sync-setup-state").hidden = true;
   123     document.getElementById("sync-syncnow-state").hidden = true;
   125     if (loginFailed) {
   126       document.getElementById("sync-reauth-state").hidden = false;
   127     } else if (needsSetup) {
   128       document.getElementById("sync-setup-state").hidden = false;
   129     } else {
   130       document.getElementById("sync-syncnow-state").hidden = false;
   131     }
   133     if (!gBrowser)
   134       return;
   136     let syncButton = document.getElementById("sync-button");
   137     let panelHorizontalButton = document.getElementById("PanelUI-fxa-status");
   138     [syncButton, panelHorizontalButton].forEach(function(button) {
   139       if (!button)
   140         return;
   141       button.removeAttribute("status");
   142     });
   144     if (needsSetup && syncButton)
   145       syncButton.removeAttribute("tooltiptext");
   147     this._updateLastSyncTime();
   148   },
   151   // Functions called by observers
   152   onActivityStart: function SUI_onActivityStart() {
   153     if (!gBrowser)
   154       return;
   156     ["sync-button", "PanelUI-fxa-status"].forEach(function(id) {
   157       let button = document.getElementById(id);
   158       if (!button)
   159         return;
   160       button.setAttribute("status", "active");
   161     });
   162   },
   164   onLoginFinish: function SUI_onLoginFinish() {
   165     // Clear out any login failure notifications
   166     let title = this._stringBundle.GetStringFromName("error.login.title");
   167     this.clearError(title);
   168   },
   170   onSetupComplete: function SUI_onSetupComplete() {
   171     this.onLoginFinish();
   172   },
   174   onLoginError: function SUI_onLoginError() {
   175     // if login fails, any other notifications are essentially moot
   176     Weave.Notifications.removeAll();
   178     // if we haven't set up the client, don't show errors
   179     if (this._needsSetup()) {
   180       this.updateUI();
   181       return;
   182     }
   183     // if we are still waiting for the identity manager to initialize, don't show errors
   184     if (Weave.Status.login == Weave.LOGIN_FAILED_NOT_READY) {
   185       this.updateUI();
   186       return;
   187     }
   189     let title = this._stringBundle.GetStringFromName("error.login.title");
   191     let description;
   192     if (Weave.Status.sync == Weave.PROLONGED_SYNC_FAILURE) {
   193       // Convert to days
   194       let lastSync =
   195         Services.prefs.getIntPref("services.sync.errorhandler.networkFailureReportTimeout") / 86400;
   196       description =
   197         this._stringBundle.formatStringFromName("error.sync.prolonged_failure", [lastSync], 1);
   198     } else {
   199       let reason = Weave.Utils.getErrorString(Weave.Status.login);
   200       description =
   201         this._stringBundle.formatStringFromName("error.sync.description", [reason], 1);
   202     }
   204     let buttons = [];
   205     buttons.push(new Weave.NotificationButton(
   206       this._stringBundle.GetStringFromName("error.login.prefs.label"),
   207       this._stringBundle.GetStringFromName("error.login.prefs.accesskey"),
   208       function() { gSyncUI.openPrefs(); return true; }
   209     ));
   211     let notification = new Weave.Notification(title, description, null,
   212                                               Weave.Notifications.PRIORITY_WARNING, buttons);
   213     Weave.Notifications.replaceTitle(notification);
   214     this.updateUI();
   215   },
   217   onLogout: function SUI_onLogout() {
   218     this.updateUI();
   219   },
   221   onStartOver: function SUI_onStartOver() {
   222     this.clearError();
   223   },
   225   onQuotaNotice: function onQuotaNotice(subject, data) {
   226     let title = this._stringBundle.GetStringFromName("warning.sync.quota.label");
   227     let description = this._stringBundle.GetStringFromName("warning.sync.quota.description");
   228     let buttons = [];
   229     buttons.push(new Weave.NotificationButton(
   230       this._stringBundle.GetStringFromName("error.sync.viewQuotaButton.label"),
   231       this._stringBundle.GetStringFromName("error.sync.viewQuotaButton.accesskey"),
   232       function() { gSyncUI.openQuotaDialog(); return true; }
   233     ));
   235     let notification = new Weave.Notification(
   236       title, description, null, Weave.Notifications.PRIORITY_WARNING, buttons);
   237     Weave.Notifications.replaceTitle(notification);
   238   },
   240   _getAppName: function () {
   241     let brand = new StringBundle("chrome://branding/locale/brand.properties");
   242     return brand.get("brandShortName");
   243   },
   245   onEOLNotice: function (data) {
   246     let code = data.code;
   247     let kind = (code == "hard-eol") ? "error" : "warning";
   248     let url = data.url || gSyncUI.DEFAULT_EOL_URL;
   250     let title = this._stringBundle.GetStringFromName(kind + ".sync.eol.label");
   251     let description = this._stringBundle.formatStringFromName(kind + ".sync.eol.description",
   252                                                               [this._getAppName()],
   253                                                               1);
   255     let buttons = [];
   256     buttons.push(new Weave.NotificationButton(
   257       this._stringBundle.GetStringFromName("sync.eol.learnMore.label"),
   258       this._stringBundle.GetStringFromName("sync.eol.learnMore.accesskey"),
   259       function() {
   260         window.openUILinkIn(url, "tab");
   261         return true;
   262       }
   263     ));
   265     let priority = (kind == "error") ? Weave.Notifications.PRIORITY_WARNING :
   266                                        Weave.Notifications.PRIORITY_INFO;
   267     let notification = new Weave.Notification(title, description, null, priority, buttons);
   268     Weave.Notifications.replaceTitle(notification);
   269   },
   271   openServerStatus: function () {
   272     let statusURL = Services.prefs.getCharPref("services.sync.statusURL");
   273     window.openUILinkIn(statusURL, "tab");
   274   },
   276   // Commands
   277   doSync: function SUI_doSync() {
   278     setTimeout(function() Weave.Service.errorHandler.syncAndReportErrors(), 0);
   279   },
   281   handleToolbarButton: function SUI_handleStatusbarButton() {
   282     if (this._needsSetup())
   283       this.openSetup();
   284     else
   285       this.doSync();
   286   },
   288   //XXXzpao should be part of syncCommon.js - which we might want to make a module...
   289   //        To be fixed in a followup (bug 583366)
   291   /**
   292    * Invoke the Sync setup wizard.
   293    *
   294    * @param wizardType
   295    *        Indicates type of wizard to launch:
   296    *          null    -- regular set up wizard
   297    *          "pair"  -- pair a device first
   298    *          "reset" -- reset sync
   299    */
   301   openSetup: function SUI_openSetup(wizardType) {
   302     let xps = Components.classes["@mozilla.org/weave/service;1"]
   303                                 .getService(Components.interfaces.nsISupports)
   304                                 .wrappedJSObject;
   305     if (xps.fxAccountsEnabled) {
   306       fxAccounts.getSignedInUser().then(userData => {
   307         if (userData) {
   308           this.openPrefs();
   309         } else {
   310           switchToTabHavingURI("about:accounts", true);
   311         }
   312       });
   313     } else {
   314       let win = Services.wm.getMostRecentWindow("Weave:AccountSetup");
   315       if (win)
   316         win.focus();
   317       else {
   318         window.openDialog("chrome://browser/content/sync/setup.xul",
   319                           "weaveSetup", "centerscreen,chrome,resizable=no",
   320                           wizardType);
   321       }
   322     }
   323   },
   325   openAddDevice: function () {
   326     if (!Weave.Utils.ensureMPUnlocked())
   327       return;
   329     let win = Services.wm.getMostRecentWindow("Sync:AddDevice");
   330     if (win)
   331       win.focus();
   332     else
   333       window.openDialog("chrome://browser/content/sync/addDevice.xul",
   334                         "syncAddDevice", "centerscreen,chrome,resizable=no");
   335   },
   337   openQuotaDialog: function SUI_openQuotaDialog() {
   338     let win = Services.wm.getMostRecentWindow("Sync:ViewQuota");
   339     if (win)
   340       win.focus();
   341     else
   342       Services.ww.activeWindow.openDialog(
   343         "chrome://browser/content/sync/quota.xul", "",
   344         "centerscreen,chrome,dialog,modal");
   345   },
   347   openPrefs: function SUI_openPrefs() {
   348     openPreferences("paneSync");
   349   },
   351   openSignInAgainPage: function () {
   352     switchToTabHavingURI("about:accounts?action=reauth", true);
   353   },
   355   // Helpers
   356   _updateLastSyncTime: function SUI__updateLastSyncTime() {
   357     if (!gBrowser)
   358       return;
   360     let syncButton = document.getElementById("sync-button");
   361     if (!syncButton)
   362       return;
   364     let lastSync;
   365     try {
   366       lastSync = Services.prefs.getCharPref("services.sync.lastSync");
   367     }
   368     catch (e) { };
   369     if (!lastSync || this._needsSetup()) {
   370       syncButton.removeAttribute("tooltiptext");
   371       return;
   372     }
   374     // Show the day-of-week and time (HH:MM) of last sync
   375     let lastSyncDate = new Date(lastSync).toLocaleFormat("%a %H:%M");
   376     let lastSyncLabel =
   377       this._stringBundle.formatStringFromName("lastSync2.label", [lastSyncDate], 1);
   379     syncButton.setAttribute("tooltiptext", lastSyncLabel);
   380   },
   382   clearError: function SUI_clearError(errorString) {
   383     Weave.Notifications.removeAll(errorString);
   384     this.updateUI();
   385   },
   387   onSyncFinish: function SUI_onSyncFinish() {
   388     let title = this._stringBundle.GetStringFromName("error.sync.title");
   390     // Clear out sync failures on a successful sync
   391     this.clearError(title);
   392   },
   394   onSyncError: function SUI_onSyncError() {
   395     let title = this._stringBundle.GetStringFromName("error.sync.title");
   397     if (Weave.Status.login != Weave.LOGIN_SUCCEEDED) {
   398       this.onLoginError();
   399       return;
   400     }
   402     let description;
   403     if (Weave.Status.sync == Weave.PROLONGED_SYNC_FAILURE) {
   404       // Convert to days
   405       let lastSync =
   406         Services.prefs.getIntPref("services.sync.errorhandler.networkFailureReportTimeout") / 86400;
   407       description =
   408         this._stringBundle.formatStringFromName("error.sync.prolonged_failure", [lastSync], 1);
   409     } else {
   410       let error = Weave.Utils.getErrorString(Weave.Status.sync);
   411       description =
   412         this._stringBundle.formatStringFromName("error.sync.description", [error], 1);
   413     }
   414     let priority = Weave.Notifications.PRIORITY_WARNING;
   415     let buttons = [];
   417     // Check if the client is outdated in some way
   418     let outdated = Weave.Status.sync == Weave.VERSION_OUT_OF_DATE;
   419     for (let [engine, reason] in Iterator(Weave.Status.engines))
   420       outdated = outdated || reason == Weave.VERSION_OUT_OF_DATE;
   422     if (outdated) {
   423       description = this._stringBundle.GetStringFromName(
   424         "error.sync.needUpdate.description");
   425       buttons.push(new Weave.NotificationButton(
   426         this._stringBundle.GetStringFromName("error.sync.needUpdate.label"),
   427         this._stringBundle.GetStringFromName("error.sync.needUpdate.accesskey"),
   428         function() { window.openUILinkIn("https://services.mozilla.com/update/", "tab"); return true; }
   429       ));
   430     }
   431     else if (Weave.Status.sync == Weave.OVER_QUOTA) {
   432       description = this._stringBundle.GetStringFromName(
   433         "error.sync.quota.description");
   434       buttons.push(new Weave.NotificationButton(
   435         this._stringBundle.GetStringFromName(
   436           "error.sync.viewQuotaButton.label"),
   437         this._stringBundle.GetStringFromName(
   438           "error.sync.viewQuotaButton.accesskey"),
   439         function() { gSyncUI.openQuotaDialog(); return true; } )
   440       );
   441     }
   442     else if (Weave.Status.enforceBackoff) {
   443       priority = Weave.Notifications.PRIORITY_INFO;
   444       buttons.push(new Weave.NotificationButton(
   445         this._stringBundle.GetStringFromName("error.sync.serverStatusButton.label"),
   446         this._stringBundle.GetStringFromName("error.sync.serverStatusButton.accesskey"),
   447         function() { gSyncUI.openServerStatus(); return true; }
   448       ));
   449     }
   450     else {
   451       priority = Weave.Notifications.PRIORITY_INFO;
   452       buttons.push(new Weave.NotificationButton(
   453         this._stringBundle.GetStringFromName("error.sync.tryAgainButton.label"),
   454         this._stringBundle.GetStringFromName("error.sync.tryAgainButton.accesskey"),
   455         function() { gSyncUI.doSync(); return true; }
   456       ));
   457     }
   459     let notification =
   460       new Weave.Notification(title, description, null, priority, buttons);
   461     Weave.Notifications.replaceTitle(notification);
   463     this.updateUI();
   464   },
   466   observe: function SUI_observe(subject, topic, data) {
   467     if (this._unloaded) {
   468       Cu.reportError("SyncUI observer called after unload: " + topic);
   469       return;
   470     }
   472     // Unwrap, just like Svc.Obs, but without pulling in that dependency.
   473     if (subject && typeof subject == "object" &&
   474         ("wrappedJSObject" in subject) &&
   475         ("observersModuleSubjectWrapper" in subject.wrappedJSObject)) {
   476       subject = subject.wrappedJSObject.object;
   477     }
   479     switch (topic) {
   480       case "weave:service:sync:start":
   481         this.onActivityStart();
   482         break;
   483       case "weave:ui:sync:finish":
   484         this.onSyncFinish();
   485         break;
   486       case "weave:ui:sync:error":
   487         this.onSyncError();
   488         break;
   489       case "weave:service:quota:remaining":
   490         this.onQuotaNotice();
   491         break;
   492       case "weave:service:setup-complete":
   493         this.onSetupComplete();
   494         break;
   495       case "weave:service:login:start":
   496         this.onActivityStart();
   497         break;
   498       case "weave:service:login:finish":
   499         this.onLoginFinish();
   500         break;
   501       case "weave:ui:login:error":
   502         this.onLoginError();
   503         break;
   504       case "weave:service:logout:finish":
   505         this.onLogout();
   506         break;
   507       case "weave:service:start-over":
   508         this.onStartOver();
   509         break;
   510       case "weave:service:start-over:finish":
   511         this.updateUI();
   512         break;
   513       case "weave:service:ready":
   514         this.initUI();
   515         break;
   516       case "weave:notification:added":
   517         this.initNotifications();
   518         break;
   519       case "weave:ui:clear-error":
   520         this.clearError();
   521         break;
   522       case "weave:eol":
   523         this.onEOLNotice(subject);
   524         break;
   525     }
   526   },
   528   QueryInterface: XPCOMUtils.generateQI([
   529     Ci.nsIObserver,
   530     Ci.nsISupportsWeakReference
   531   ])
   532 };
   534 XPCOMUtils.defineLazyGetter(gSyncUI, "_stringBundle", function() {
   535   //XXXzpao these strings should probably be moved from /services to /browser... (bug 583381)
   536   //        but for now just make it work
   537   return Cc["@mozilla.org/intl/stringbundle;1"].
   538          getService(Ci.nsIStringBundleService).
   539          createBundle("chrome://weave/locale/services/sync.properties");
   540 });

mercurial