browser/base/content/browser-syncui.js

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/browser/base/content/browser-syncui.js	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,541 @@
     1.4 +# This Source Code Form is subject to the terms of the Mozilla Public
     1.5 +# License, v. 2.0. If a copy of the MPL was not distributed with this
     1.6 +# file, You can obtain one at http://mozilla.org/MPL/2.0/.
     1.7 +
     1.8 +// gSyncUI handles updating the tools menu and displaying notifications.
     1.9 +let gSyncUI = {
    1.10 +  DEFAULT_EOL_URL: "https://www.mozilla.org/firefox/?utm_source=synceol",
    1.11 +
    1.12 +  _obs: ["weave:service:sync:start",
    1.13 +         "weave:service:quota:remaining",
    1.14 +         "weave:service:setup-complete",
    1.15 +         "weave:service:login:start",
    1.16 +         "weave:service:login:finish",
    1.17 +         "weave:service:logout:finish",
    1.18 +         "weave:service:start-over",
    1.19 +         "weave:service:start-over:finish",
    1.20 +         "weave:ui:login:error",
    1.21 +         "weave:ui:sync:error",
    1.22 +         "weave:ui:sync:finish",
    1.23 +         "weave:ui:clear-error",
    1.24 +         "weave:eol",
    1.25 +  ],
    1.26 +
    1.27 +  _unloaded: false,
    1.28 +
    1.29 +  init: function () {
    1.30 +    Cu.import("resource://services-common/stringbundle.js");
    1.31 +
    1.32 +    // Proceed to set up the UI if Sync has already started up.
    1.33 +    // Otherwise we'll do it when Sync is firing up.
    1.34 +    let xps = Components.classes["@mozilla.org/weave/service;1"]
    1.35 +                                .getService(Components.interfaces.nsISupports)
    1.36 +                                .wrappedJSObject;
    1.37 +    if (xps.ready) {
    1.38 +      this.initUI();
    1.39 +      return;
    1.40 +    }
    1.41 +
    1.42 +    Services.obs.addObserver(this, "weave:service:ready", true);
    1.43 +
    1.44 +    // Remove the observer if the window is closed before the observer
    1.45 +    // was triggered.
    1.46 +    window.addEventListener("unload", function onUnload() {
    1.47 +      gSyncUI._unloaded = true;
    1.48 +      window.removeEventListener("unload", onUnload, false);
    1.49 +      Services.obs.removeObserver(gSyncUI, "weave:service:ready");
    1.50 +
    1.51 +      if (Weave.Status.ready) {
    1.52 +        gSyncUI._obs.forEach(function(topic) {
    1.53 +          Services.obs.removeObserver(gSyncUI, topic);
    1.54 +        });
    1.55 +      }
    1.56 +    }, false);
    1.57 +  },
    1.58 +
    1.59 +  initUI: function SUI_initUI() {
    1.60 +    // If this is a browser window?
    1.61 +    if (gBrowser) {
    1.62 +      this._obs.push("weave:notification:added");
    1.63 +    }
    1.64 +
    1.65 +    this._obs.forEach(function(topic) {
    1.66 +      Services.obs.addObserver(this, topic, true);
    1.67 +    }, this);
    1.68 +
    1.69 +    if (gBrowser && Weave.Notifications.notifications.length) {
    1.70 +      this.initNotifications();
    1.71 +    }
    1.72 +    this.updateUI();
    1.73 +  },
    1.74 +
    1.75 +  initNotifications: function SUI_initNotifications() {
    1.76 +    const XULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
    1.77 +    let notificationbox = document.createElementNS(XULNS, "notificationbox");
    1.78 +    notificationbox.id = "sync-notifications";
    1.79 +    notificationbox.setAttribute("flex", "1");
    1.80 +
    1.81 +    let bottombox = document.getElementById("browser-bottombox");
    1.82 +    bottombox.insertBefore(notificationbox, bottombox.firstChild);
    1.83 +
    1.84 +    // Force a style flush to ensure that our binding is attached.
    1.85 +    notificationbox.clientTop;
    1.86 +
    1.87 +    // notificationbox will listen to observers from now on.
    1.88 +    Services.obs.removeObserver(this, "weave:notification:added");
    1.89 +  },
    1.90 +
    1.91 +  _needsSetup: function SUI__needsSetup() {
    1.92 +    // We want to treat "account needs verification" as "needs setup". So
    1.93 +    // "reach in" to Weave.Status._authManager to check whether we the signed-in
    1.94 +    // user is verified.
    1.95 +    // Referencing Weave.Status spins a nested event loop to initialize the
    1.96 +    // authManager, so this should always return a value directly.
    1.97 +    // This only applies to fxAccounts-based Sync.
    1.98 +    if (Weave.Status._authManager._signedInUser) {
    1.99 +      // If we have a signed in user already, and that user is not verified,
   1.100 +      // revert to the "needs setup" state.
   1.101 +      if (!Weave.Status._authManager._signedInUser.verified) {
   1.102 +        return true;
   1.103 +      }
   1.104 +    }
   1.105 +
   1.106 +    let firstSync = "";
   1.107 +    try {
   1.108 +      firstSync = Services.prefs.getCharPref("services.sync.firstSync");
   1.109 +    } catch (e) { }
   1.110 +
   1.111 +    return Weave.Status.checkSetup() == Weave.CLIENT_NOT_CONFIGURED ||
   1.112 +           firstSync == "notReady";
   1.113 +  },
   1.114 +
   1.115 +  _loginFailed: function () {
   1.116 +    return Weave.Status.login == Weave.LOGIN_FAILED_LOGIN_REJECTED;
   1.117 +  },
   1.118 +
   1.119 +  updateUI: function SUI_updateUI() {
   1.120 +    let needsSetup = this._needsSetup();
   1.121 +    let loginFailed = this._loginFailed();
   1.122 +
   1.123 +    // Start off with a clean slate
   1.124 +    document.getElementById("sync-reauth-state").hidden = true;
   1.125 +    document.getElementById("sync-setup-state").hidden = true;
   1.126 +    document.getElementById("sync-syncnow-state").hidden = true;
   1.127 +
   1.128 +    if (loginFailed) {
   1.129 +      document.getElementById("sync-reauth-state").hidden = false;
   1.130 +    } else if (needsSetup) {
   1.131 +      document.getElementById("sync-setup-state").hidden = false;
   1.132 +    } else {
   1.133 +      document.getElementById("sync-syncnow-state").hidden = false;
   1.134 +    }
   1.135 +
   1.136 +    if (!gBrowser)
   1.137 +      return;
   1.138 +
   1.139 +    let syncButton = document.getElementById("sync-button");
   1.140 +    let panelHorizontalButton = document.getElementById("PanelUI-fxa-status");
   1.141 +    [syncButton, panelHorizontalButton].forEach(function(button) {
   1.142 +      if (!button)
   1.143 +        return;
   1.144 +      button.removeAttribute("status");
   1.145 +    });
   1.146 +
   1.147 +    if (needsSetup && syncButton)
   1.148 +      syncButton.removeAttribute("tooltiptext");
   1.149 +
   1.150 +    this._updateLastSyncTime();
   1.151 +  },
   1.152 +
   1.153 +
   1.154 +  // Functions called by observers
   1.155 +  onActivityStart: function SUI_onActivityStart() {
   1.156 +    if (!gBrowser)
   1.157 +      return;
   1.158 +
   1.159 +    ["sync-button", "PanelUI-fxa-status"].forEach(function(id) {
   1.160 +      let button = document.getElementById(id);
   1.161 +      if (!button)
   1.162 +        return;
   1.163 +      button.setAttribute("status", "active");
   1.164 +    });
   1.165 +  },
   1.166 +
   1.167 +  onLoginFinish: function SUI_onLoginFinish() {
   1.168 +    // Clear out any login failure notifications
   1.169 +    let title = this._stringBundle.GetStringFromName("error.login.title");
   1.170 +    this.clearError(title);
   1.171 +  },
   1.172 +
   1.173 +  onSetupComplete: function SUI_onSetupComplete() {
   1.174 +    this.onLoginFinish();
   1.175 +  },
   1.176 +
   1.177 +  onLoginError: function SUI_onLoginError() {
   1.178 +    // if login fails, any other notifications are essentially moot
   1.179 +    Weave.Notifications.removeAll();
   1.180 +
   1.181 +    // if we haven't set up the client, don't show errors
   1.182 +    if (this._needsSetup()) {
   1.183 +      this.updateUI();
   1.184 +      return;
   1.185 +    }
   1.186 +    // if we are still waiting for the identity manager to initialize, don't show errors
   1.187 +    if (Weave.Status.login == Weave.LOGIN_FAILED_NOT_READY) {
   1.188 +      this.updateUI();
   1.189 +      return;
   1.190 +    }
   1.191 +
   1.192 +    let title = this._stringBundle.GetStringFromName("error.login.title");
   1.193 +
   1.194 +    let description;
   1.195 +    if (Weave.Status.sync == Weave.PROLONGED_SYNC_FAILURE) {
   1.196 +      // Convert to days
   1.197 +      let lastSync =
   1.198 +        Services.prefs.getIntPref("services.sync.errorhandler.networkFailureReportTimeout") / 86400;
   1.199 +      description =
   1.200 +        this._stringBundle.formatStringFromName("error.sync.prolonged_failure", [lastSync], 1);
   1.201 +    } else {
   1.202 +      let reason = Weave.Utils.getErrorString(Weave.Status.login);
   1.203 +      description =
   1.204 +        this._stringBundle.formatStringFromName("error.sync.description", [reason], 1);
   1.205 +    }
   1.206 +
   1.207 +    let buttons = [];
   1.208 +    buttons.push(new Weave.NotificationButton(
   1.209 +      this._stringBundle.GetStringFromName("error.login.prefs.label"),
   1.210 +      this._stringBundle.GetStringFromName("error.login.prefs.accesskey"),
   1.211 +      function() { gSyncUI.openPrefs(); return true; }
   1.212 +    ));
   1.213 +
   1.214 +    let notification = new Weave.Notification(title, description, null,
   1.215 +                                              Weave.Notifications.PRIORITY_WARNING, buttons);
   1.216 +    Weave.Notifications.replaceTitle(notification);
   1.217 +    this.updateUI();
   1.218 +  },
   1.219 +
   1.220 +  onLogout: function SUI_onLogout() {
   1.221 +    this.updateUI();
   1.222 +  },
   1.223 +
   1.224 +  onStartOver: function SUI_onStartOver() {
   1.225 +    this.clearError();
   1.226 +  },
   1.227 +
   1.228 +  onQuotaNotice: function onQuotaNotice(subject, data) {
   1.229 +    let title = this._stringBundle.GetStringFromName("warning.sync.quota.label");
   1.230 +    let description = this._stringBundle.GetStringFromName("warning.sync.quota.description");
   1.231 +    let buttons = [];
   1.232 +    buttons.push(new Weave.NotificationButton(
   1.233 +      this._stringBundle.GetStringFromName("error.sync.viewQuotaButton.label"),
   1.234 +      this._stringBundle.GetStringFromName("error.sync.viewQuotaButton.accesskey"),
   1.235 +      function() { gSyncUI.openQuotaDialog(); return true; }
   1.236 +    ));
   1.237 +
   1.238 +    let notification = new Weave.Notification(
   1.239 +      title, description, null, Weave.Notifications.PRIORITY_WARNING, buttons);
   1.240 +    Weave.Notifications.replaceTitle(notification);
   1.241 +  },
   1.242 +
   1.243 +  _getAppName: function () {
   1.244 +    let brand = new StringBundle("chrome://branding/locale/brand.properties");
   1.245 +    return brand.get("brandShortName");
   1.246 +  },
   1.247 +
   1.248 +  onEOLNotice: function (data) {
   1.249 +    let code = data.code;
   1.250 +    let kind = (code == "hard-eol") ? "error" : "warning";
   1.251 +    let url = data.url || gSyncUI.DEFAULT_EOL_URL;
   1.252 +
   1.253 +    let title = this._stringBundle.GetStringFromName(kind + ".sync.eol.label");
   1.254 +    let description = this._stringBundle.formatStringFromName(kind + ".sync.eol.description",
   1.255 +                                                              [this._getAppName()],
   1.256 +                                                              1);
   1.257 +
   1.258 +    let buttons = [];
   1.259 +    buttons.push(new Weave.NotificationButton(
   1.260 +      this._stringBundle.GetStringFromName("sync.eol.learnMore.label"),
   1.261 +      this._stringBundle.GetStringFromName("sync.eol.learnMore.accesskey"),
   1.262 +      function() {
   1.263 +        window.openUILinkIn(url, "tab");
   1.264 +        return true;
   1.265 +      }
   1.266 +    ));
   1.267 +
   1.268 +    let priority = (kind == "error") ? Weave.Notifications.PRIORITY_WARNING :
   1.269 +                                       Weave.Notifications.PRIORITY_INFO;
   1.270 +    let notification = new Weave.Notification(title, description, null, priority, buttons);
   1.271 +    Weave.Notifications.replaceTitle(notification);
   1.272 +  },
   1.273 +
   1.274 +  openServerStatus: function () {
   1.275 +    let statusURL = Services.prefs.getCharPref("services.sync.statusURL");
   1.276 +    window.openUILinkIn(statusURL, "tab");
   1.277 +  },
   1.278 +
   1.279 +  // Commands
   1.280 +  doSync: function SUI_doSync() {
   1.281 +    setTimeout(function() Weave.Service.errorHandler.syncAndReportErrors(), 0);
   1.282 +  },
   1.283 +
   1.284 +  handleToolbarButton: function SUI_handleStatusbarButton() {
   1.285 +    if (this._needsSetup())
   1.286 +      this.openSetup();
   1.287 +    else
   1.288 +      this.doSync();
   1.289 +  },
   1.290 +
   1.291 +  //XXXzpao should be part of syncCommon.js - which we might want to make a module...
   1.292 +  //        To be fixed in a followup (bug 583366)
   1.293 +
   1.294 +  /**
   1.295 +   * Invoke the Sync setup wizard.
   1.296 +   *
   1.297 +   * @param wizardType
   1.298 +   *        Indicates type of wizard to launch:
   1.299 +   *          null    -- regular set up wizard
   1.300 +   *          "pair"  -- pair a device first
   1.301 +   *          "reset" -- reset sync
   1.302 +   */
   1.303 +
   1.304 +  openSetup: function SUI_openSetup(wizardType) {
   1.305 +    let xps = Components.classes["@mozilla.org/weave/service;1"]
   1.306 +                                .getService(Components.interfaces.nsISupports)
   1.307 +                                .wrappedJSObject;
   1.308 +    if (xps.fxAccountsEnabled) {
   1.309 +      fxAccounts.getSignedInUser().then(userData => {
   1.310 +        if (userData) {
   1.311 +          this.openPrefs();
   1.312 +        } else {
   1.313 +          switchToTabHavingURI("about:accounts", true);
   1.314 +        }
   1.315 +      });
   1.316 +    } else {
   1.317 +      let win = Services.wm.getMostRecentWindow("Weave:AccountSetup");
   1.318 +      if (win)
   1.319 +        win.focus();
   1.320 +      else {
   1.321 +        window.openDialog("chrome://browser/content/sync/setup.xul",
   1.322 +                          "weaveSetup", "centerscreen,chrome,resizable=no",
   1.323 +                          wizardType);
   1.324 +      }
   1.325 +    }
   1.326 +  },
   1.327 +
   1.328 +  openAddDevice: function () {
   1.329 +    if (!Weave.Utils.ensureMPUnlocked())
   1.330 +      return;
   1.331 +
   1.332 +    let win = Services.wm.getMostRecentWindow("Sync:AddDevice");
   1.333 +    if (win)
   1.334 +      win.focus();
   1.335 +    else
   1.336 +      window.openDialog("chrome://browser/content/sync/addDevice.xul",
   1.337 +                        "syncAddDevice", "centerscreen,chrome,resizable=no");
   1.338 +  },
   1.339 +
   1.340 +  openQuotaDialog: function SUI_openQuotaDialog() {
   1.341 +    let win = Services.wm.getMostRecentWindow("Sync:ViewQuota");
   1.342 +    if (win)
   1.343 +      win.focus();
   1.344 +    else
   1.345 +      Services.ww.activeWindow.openDialog(
   1.346 +        "chrome://browser/content/sync/quota.xul", "",
   1.347 +        "centerscreen,chrome,dialog,modal");
   1.348 +  },
   1.349 +
   1.350 +  openPrefs: function SUI_openPrefs() {
   1.351 +    openPreferences("paneSync");
   1.352 +  },
   1.353 +
   1.354 +  openSignInAgainPage: function () {
   1.355 +    switchToTabHavingURI("about:accounts?action=reauth", true);
   1.356 +  },
   1.357 +
   1.358 +  // Helpers
   1.359 +  _updateLastSyncTime: function SUI__updateLastSyncTime() {
   1.360 +    if (!gBrowser)
   1.361 +      return;
   1.362 +
   1.363 +    let syncButton = document.getElementById("sync-button");
   1.364 +    if (!syncButton)
   1.365 +      return;
   1.366 +
   1.367 +    let lastSync;
   1.368 +    try {
   1.369 +      lastSync = Services.prefs.getCharPref("services.sync.lastSync");
   1.370 +    }
   1.371 +    catch (e) { };
   1.372 +    if (!lastSync || this._needsSetup()) {
   1.373 +      syncButton.removeAttribute("tooltiptext");
   1.374 +      return;
   1.375 +    }
   1.376 +
   1.377 +    // Show the day-of-week and time (HH:MM) of last sync
   1.378 +    let lastSyncDate = new Date(lastSync).toLocaleFormat("%a %H:%M");
   1.379 +    let lastSyncLabel =
   1.380 +      this._stringBundle.formatStringFromName("lastSync2.label", [lastSyncDate], 1);
   1.381 +
   1.382 +    syncButton.setAttribute("tooltiptext", lastSyncLabel);
   1.383 +  },
   1.384 +
   1.385 +  clearError: function SUI_clearError(errorString) {
   1.386 +    Weave.Notifications.removeAll(errorString);
   1.387 +    this.updateUI();
   1.388 +  },
   1.389 +
   1.390 +  onSyncFinish: function SUI_onSyncFinish() {
   1.391 +    let title = this._stringBundle.GetStringFromName("error.sync.title");
   1.392 +
   1.393 +    // Clear out sync failures on a successful sync
   1.394 +    this.clearError(title);
   1.395 +  },
   1.396 +
   1.397 +  onSyncError: function SUI_onSyncError() {
   1.398 +    let title = this._stringBundle.GetStringFromName("error.sync.title");
   1.399 +
   1.400 +    if (Weave.Status.login != Weave.LOGIN_SUCCEEDED) {
   1.401 +      this.onLoginError();
   1.402 +      return;
   1.403 +    }
   1.404 +
   1.405 +    let description;
   1.406 +    if (Weave.Status.sync == Weave.PROLONGED_SYNC_FAILURE) {
   1.407 +      // Convert to days
   1.408 +      let lastSync =
   1.409 +        Services.prefs.getIntPref("services.sync.errorhandler.networkFailureReportTimeout") / 86400;
   1.410 +      description =
   1.411 +        this._stringBundle.formatStringFromName("error.sync.prolonged_failure", [lastSync], 1);
   1.412 +    } else {
   1.413 +      let error = Weave.Utils.getErrorString(Weave.Status.sync);
   1.414 +      description =
   1.415 +        this._stringBundle.formatStringFromName("error.sync.description", [error], 1);
   1.416 +    }
   1.417 +    let priority = Weave.Notifications.PRIORITY_WARNING;
   1.418 +    let buttons = [];
   1.419 +
   1.420 +    // Check if the client is outdated in some way
   1.421 +    let outdated = Weave.Status.sync == Weave.VERSION_OUT_OF_DATE;
   1.422 +    for (let [engine, reason] in Iterator(Weave.Status.engines))
   1.423 +      outdated = outdated || reason == Weave.VERSION_OUT_OF_DATE;
   1.424 +
   1.425 +    if (outdated) {
   1.426 +      description = this._stringBundle.GetStringFromName(
   1.427 +        "error.sync.needUpdate.description");
   1.428 +      buttons.push(new Weave.NotificationButton(
   1.429 +        this._stringBundle.GetStringFromName("error.sync.needUpdate.label"),
   1.430 +        this._stringBundle.GetStringFromName("error.sync.needUpdate.accesskey"),
   1.431 +        function() { window.openUILinkIn("https://services.mozilla.com/update/", "tab"); return true; }
   1.432 +      ));
   1.433 +    }
   1.434 +    else if (Weave.Status.sync == Weave.OVER_QUOTA) {
   1.435 +      description = this._stringBundle.GetStringFromName(
   1.436 +        "error.sync.quota.description");
   1.437 +      buttons.push(new Weave.NotificationButton(
   1.438 +        this._stringBundle.GetStringFromName(
   1.439 +          "error.sync.viewQuotaButton.label"),
   1.440 +        this._stringBundle.GetStringFromName(
   1.441 +          "error.sync.viewQuotaButton.accesskey"),
   1.442 +        function() { gSyncUI.openQuotaDialog(); return true; } )
   1.443 +      );
   1.444 +    }
   1.445 +    else if (Weave.Status.enforceBackoff) {
   1.446 +      priority = Weave.Notifications.PRIORITY_INFO;
   1.447 +      buttons.push(new Weave.NotificationButton(
   1.448 +        this._stringBundle.GetStringFromName("error.sync.serverStatusButton.label"),
   1.449 +        this._stringBundle.GetStringFromName("error.sync.serverStatusButton.accesskey"),
   1.450 +        function() { gSyncUI.openServerStatus(); return true; }
   1.451 +      ));
   1.452 +    }
   1.453 +    else {
   1.454 +      priority = Weave.Notifications.PRIORITY_INFO;
   1.455 +      buttons.push(new Weave.NotificationButton(
   1.456 +        this._stringBundle.GetStringFromName("error.sync.tryAgainButton.label"),
   1.457 +        this._stringBundle.GetStringFromName("error.sync.tryAgainButton.accesskey"),
   1.458 +        function() { gSyncUI.doSync(); return true; }
   1.459 +      ));
   1.460 +    }
   1.461 +
   1.462 +    let notification =
   1.463 +      new Weave.Notification(title, description, null, priority, buttons);
   1.464 +    Weave.Notifications.replaceTitle(notification);
   1.465 +
   1.466 +    this.updateUI();
   1.467 +  },
   1.468 +
   1.469 +  observe: function SUI_observe(subject, topic, data) {
   1.470 +    if (this._unloaded) {
   1.471 +      Cu.reportError("SyncUI observer called after unload: " + topic);
   1.472 +      return;
   1.473 +    }
   1.474 +
   1.475 +    // Unwrap, just like Svc.Obs, but without pulling in that dependency.
   1.476 +    if (subject && typeof subject == "object" &&
   1.477 +        ("wrappedJSObject" in subject) &&
   1.478 +        ("observersModuleSubjectWrapper" in subject.wrappedJSObject)) {
   1.479 +      subject = subject.wrappedJSObject.object;
   1.480 +    }
   1.481 +
   1.482 +    switch (topic) {
   1.483 +      case "weave:service:sync:start":
   1.484 +        this.onActivityStart();
   1.485 +        break;
   1.486 +      case "weave:ui:sync:finish":
   1.487 +        this.onSyncFinish();
   1.488 +        break;
   1.489 +      case "weave:ui:sync:error":
   1.490 +        this.onSyncError();
   1.491 +        break;
   1.492 +      case "weave:service:quota:remaining":
   1.493 +        this.onQuotaNotice();
   1.494 +        break;
   1.495 +      case "weave:service:setup-complete":
   1.496 +        this.onSetupComplete();
   1.497 +        break;
   1.498 +      case "weave:service:login:start":
   1.499 +        this.onActivityStart();
   1.500 +        break;
   1.501 +      case "weave:service:login:finish":
   1.502 +        this.onLoginFinish();
   1.503 +        break;
   1.504 +      case "weave:ui:login:error":
   1.505 +        this.onLoginError();
   1.506 +        break;
   1.507 +      case "weave:service:logout:finish":
   1.508 +        this.onLogout();
   1.509 +        break;
   1.510 +      case "weave:service:start-over":
   1.511 +        this.onStartOver();
   1.512 +        break;
   1.513 +      case "weave:service:start-over:finish":
   1.514 +        this.updateUI();
   1.515 +        break;
   1.516 +      case "weave:service:ready":
   1.517 +        this.initUI();
   1.518 +        break;
   1.519 +      case "weave:notification:added":
   1.520 +        this.initNotifications();
   1.521 +        break;
   1.522 +      case "weave:ui:clear-error":
   1.523 +        this.clearError();
   1.524 +        break;
   1.525 +      case "weave:eol":
   1.526 +        this.onEOLNotice(subject);
   1.527 +        break;
   1.528 +    }
   1.529 +  },
   1.530 +
   1.531 +  QueryInterface: XPCOMUtils.generateQI([
   1.532 +    Ci.nsIObserver,
   1.533 +    Ci.nsISupportsWeakReference
   1.534 +  ])
   1.535 +};
   1.536 +
   1.537 +XPCOMUtils.defineLazyGetter(gSyncUI, "_stringBundle", function() {
   1.538 +  //XXXzpao these strings should probably be moved from /services to /browser... (bug 583381)
   1.539 +  //        but for now just make it work
   1.540 +  return Cc["@mozilla.org/intl/stringbundle;1"].
   1.541 +         getService(Ci.nsIStringBundleService).
   1.542 +         createBundle("chrome://weave/locale/services/sync.properties");
   1.543 +});
   1.544 +

mercurial