diff -r 08e0f72e2948 chrome/content/load-weave.js
--- a/chrome/content/load-weave.js Tue Jun 24 08:51:40 2008 -0700
+++ b/chrome/content/load-weave.js Tue Jun 24 17:38:48 2008 -0700
@@ -23,6 +23,7 @@ Components.utils.import("resource://weav
Components.utils.import("resource://weave/util.js", {});
Components.utils.import("resource://weave/async.js", {});
Components.utils.import("resource://weave/crypto.js", {});
+Components.utils.import("resource://weave/notifications.js", {});
Components.utils.import("resource://weave/identity.js", {});
Components.utils.import("resource://weave/dav.js", {});
Components.utils.import("resource://weave/remote.js", {});
diff -r 08e0f72e2948 chrome/content/notification.css
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/chrome/content/notification.css Tue Jun 24 17:38:48 2008 -0700
@@ -0,0 +1,15 @@
+
+#sync-notifications-box {
+ -moz-binding: url("chrome://weave/content/notification.xml#notificationbox");
+ width: 30em;
+ height: 30em;
+ overflow: auto;
+}
+
+#sync-notifications-box notification {
+ -moz-binding: url("chrome://weave/content/notification.xml#notification");
+}
+
+#sync-notifications-box notification[type="TabsNotification"] {
+ -moz-binding: url("chrome://weave/content/notification.xml#TabsNotification");
+}
diff -r 08e0f72e2948 chrome/content/notification.xml
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/chrome/content/notification.xml Tue Jun 24 17:38:48 2008 -0700
@@ -0,0 +1,257 @@
+
+
+
+%notificationDTD;
+
+%weaveSyncDTD;
+]>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ null
+
+
+
+
+
+
+
+
+
+
+
+
+
+ &syncTabsPanel.title;
+
+
+
+
+
+ &syncTabsPanel.description;
+
+
+
+
+
+
+
+
+
+
+
+
+
+ this._initTabsPanel();
+
+
+
+ Cc["@mozilla.org/browser/sessionstore;1"].getService(Ci.nsISessionStore);
+
+
+
+ Cc["@mozilla.org/dom/json;1"].createInstance(Ci.nsIJSON);
+
+
+
+
+ b.position ? 1 :
+ a.position < b.position ? -1 : 0);
+
+ return virtualTabs;
+ ]]>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff -r 08e0f72e2948 chrome/content/sync.css
--- a/chrome/content/sync.css Tue Jun 24 08:51:40 2008 -0700
+++ b/chrome/content/sync.css Tue Jun 24 17:38:48 2008 -0700
@@ -1,25 +1,17 @@
-#sync-throbber-active, #sync-throbber-online, #sync-throbber-offline {
- border: 0px !important;
+#sync-menu-button, #sync-menu-button[status="offline"] {
+ list-style-image: url("chrome://weave/skin/sync-16x16.png");
}
-/* #sync-menu, */ #sync-menu-button, #sync-menu-item[status="offline"], #sync-menu-button[status="offline"] {
- list-style-image: url(chrome://weave/skin/sync-throbber-16x16.png);
+#sync-menu-button[status="idle"] {
+ list-style-image: url("chrome://weave/skin/sync-16x16.png");
}
-/* #sync-menu[status="idle"], */ #sync-menu-button[status="idle"] {
- list-style-image: url(chrome://weave/skin/sync-throbber-16x16.png);
+#sync-menu-button[status="active"] {
+ list-style-image: url("chrome://weave/skin/sync-throbber-16x16-active.apng");
}
-/* #sync-menu[status="active"], */ #sync-menu-button[status="active"] {
- list-style-image: url(chrome://weave/skin/sync-throbber-16x16-active.apng);
-}
-
-/* #sync-menu[status="error"], */ #sync-menu-button[status="error"] {
- list-style-image: url(chrome://weave/skin/sync-throbber-16x16-error.png);
-}
-
-#sync-menu-button {
- -moz-box-direction: reverse;
+#sync-tabs-panel {
+ max-width: 30em;
}
#sync-tabs-panel {
diff -r 08e0f72e2948 chrome/content/sync.js
--- a/chrome/content/sync.js Tue Jun 24 08:51:40 2008 -0700
+++ b/chrome/content/sync.js Tue Jun 24 17:38:48 2008 -0700
@@ -47,6 +47,8 @@ function Sync() {
this._os.addObserver(this, "weave:service:sync:start", false);
this._os.addObserver(this, "weave:service:sync:success", false);
this._os.addObserver(this, "weave:service:sync:error", false);
+ this._os.addObserver(this, "weave:notification:added", false);
+ this._os.addObserver(this, "weave:notification:removed", false);
this._os.addObserver(this, "weave:store:tabs:virtual:created", false);
this._os.addObserver(this, "weave:store:tabs:virtual:removed", false);
@@ -83,7 +85,7 @@ function Sync() {
this._onLogin();
Weave.Service.onWindowOpened();
- this._updateSyncTabsButton();
+ this._onVirtualTabCreated();
}
Sync.prototype = {
get _isTopBrowserWindow() {
@@ -190,10 +192,22 @@ Sync.prototype = {
}
},
- _setThrobber: function Sync__setThrobber(status) {
+ _setStatus: function Sync__setStatus(status) {
document.getElementById("sync-menu-button").setAttribute("status", status);
- document.getElementById("sync-menu").setAttribute("status", status);
- let label = this._stringBundle.getString("status." + status);
+
+ let label;
+ if (status == "offline")
+ label = this._stringBundle.getString("status.offline");
+ else {
+ let username = this._prefSvc.getCharPref("extensions.weave.username");
+ if (!username || username == 'nobody@mozilla.com') {
+ this._log.error("status is " + status + ", but username not set");
+ // Fall back to a generic string.
+ label = this._stringBundle.getString("status." + status);
+ }
+ else
+ label = username;
+ }
document.getElementById("sync-menu-status").setAttribute("value", label);
},
@@ -201,12 +215,10 @@ Sync.prototype = {
this._log.info("Logging in...");
this._log.info("User string: " + navigator.userAgent);
this._log.info("Weave version: " + Weave.WEAVE_VERSION);
- this._setThrobber("active");
+ this._setStatus("active");
},
_onLoginError: function Sync__onLoginError() {
- this._setThrobber("error");
-
// TODO: We may want to just display a notification here that the user
// can deal with on their own time instead of forcing them to deal
// with an unsuccessful login immediately.
@@ -221,7 +233,7 @@ Sync.prototype = {
this._userLogin = false;
- this._setThrobber("idle");
+ this._setStatus("idle");
let loginitem = document.getElementById("sync-loginitem");
let logoutitem = document.getElementById("sync-logoutitem");
@@ -237,9 +249,17 @@ Sync.prototype = {
_onLogout: function Sync__onLogout(status) {
if (status)
- this._setThrobber("offline");
- else
- this._setThrobber("error");
+ this._setStatus("offline");
+ else {
+ let title = this._stringBundle.getString("error.logout.title");
+ let description =
+ this._stringBundle.getString("error.logout.description");
+ let notification =
+ new Weave.Notification(title,
+ description,
+ Weave.Notifications.PRIORITY_WARNING_LOW);
+ Weave.Notifications.add(notification);
+ }
let loginitem = document.getElementById("sync-loginitem");
let logoutitem = document.getElementById("sync-logoutitem");
@@ -254,7 +274,7 @@ Sync.prototype = {
},
_onSyncStart: function Sync_onSyncStart() {
- this._setThrobber("active");
+ this._setStatus("active");
let syncitem = document.getElementById("sync-syncnowitem");
if(syncitem)
@@ -263,12 +283,19 @@ Sync.prototype = {
_onSyncEnd: function Sync_onSyncEnd(status) {
if (status)
- this._setThrobber("idle");
- else
- this._setThrobber("error");
+ this._setStatus("idle");
+ else {
+ let title = this._stringBundle.getString("error.sync.title");
+ let description = this._stringBundle.getString("error.sync.description");
+ let notification =
+ new Weave.Notification(title,
+ description,
+ Weave.Notifications.PRIORITY_WARNING_LOW);
+ Weave.Notifications.add(notification);
+ }
let syncitem = document.getElementById("sync-syncnowitem");
- if(syncitem)
+ if (syncitem)
syncitem.setAttribute("active", "true");
if (this._isTopBrowserWindow)
@@ -287,6 +314,8 @@ Sync.prototype = {
this._os.removeObserver(this, "weave:service:sync:start");
this._os.removeObserver(this, "weave:service:sync:success");
this._os.removeObserver(this, "weave:service:sync:error");
+ this._os.removeObserver(this, "weave:notification:added");
+ this._os.removeObserver(this, "weave:notification:removed");
this._os.removeObserver(this, "weave:store:tabs:virtual:created");
this._os.removeObserver(this, "weave:store:tabs:virtual:removed");
},
@@ -349,6 +378,8 @@ Sync.prototype = {
this._updateLastSyncItem();
},
+ // FIXME: refactor this function with the identical one in the notification
+ // binding.
_getSortedVirtualTabs: function Sync__getSortedVirtualTabs() {
let virtualTabs = Weave.Engines.get("tabs").store.virtualTabs;
@@ -390,7 +421,8 @@ Sync.prototype = {
[currentEntry.title, currentEntry.url].filter(function(v) v).join("\n");
}
- document.getElementById("sync-no-tabs-menu-item").hidden = (menu.itemCount > 1);
+ document.getElementById("sync-no-tabs-menu-item").hidden =
+ (menu.itemCount > 1);
},
onCommandTabsMenu: function Sync_onCommandTabsMenu(event) {
@@ -402,90 +434,68 @@ Sync.prototype = {
this._sessionStore.setTabState(tab, this._json.encode(virtualTab.state));
gBrowser.selectedTab = tab;
delete virtualTabs[tabID];
+
+ // FIXME: update a notification that lists the opened tab, if any.
+ },
+
+ _onNotificationAdded: function Sync__onNotificationAdded() {
+ document.getElementById("sync-notifications-button").hidden = false;
},
_onVirtualTabCreated: function Sync__onVirtualTabCreated() {
- this._updateSyncTabsButton();
- // FIXME: do more to alert the user about new undisposed virtual tabs?
+ let virtualTabs = Weave.Engines.get("tabs").store.virtualTabs;
+
+ // As long as there is at least one virtual tab that hasn't previously been
+ // disposed of by the user, notify the user about available tabs.
+ for (id in virtualTabs) {
+ if (!virtualTabs[id]._disposed) {
+ // Get the first (which should also be the only) tabs notification.
+ let [notification] =
+ Weave.Notifications.notifications.
+ filter(function(v) v.constructor.name == "TabsNotification");
+
+ // If there's an existing tabs notification, remove it so we don't
+ // show the user multiple notifications. The new one will contain
+ // all the tabs listed on the old one as well as the new tab(s).
+ // FIXME: make tabs notifications automatically update themselves
+ // with the current list of available tabs whenever it changes.
+ if (notification)
+ Weave.Notifications.remove(notification);
+
+ Weave.Notifications.add(new Weave.TabsNotification());
+
+ return;
+ }
+ }
},
_onVirtualTabRemoved: function Sync__onVirtualTabRemoved() {
- this._updateSyncTabsButton();
- },
-
- _updateSyncTabsButton: function Sync__updateSyncTabsButton() {
let virtualTabs = Weave.Engines.get("tabs").store.virtualTabs;
+ // Get the first (which should also be the only) tabs notification.
+ let [notification] =
+ Weave.Notifications.notifications.
+ filter(function(v) v.constructor.name == "TabsNotification");
+
// As long as there is at least one virtual tab that hasn't previously been
- // disposed of by the user, show the button for opening the sync tabs panel.
+ // disposed of by the user, update the existing notification.
+ // FIXME: make tabs notifications automatically update themselves
+ // with the current list of available tabs whenever it changes.
for (id in virtualTabs) {
if (!virtualTabs[id]._disposed) {
- document.getElementById("sync-tabs-button").hidden = false;
+ Weave.Notifications.replace(notification, new Weave.TabsNotification());
return;
}
}
- // Otherwise, hide the button.
- document.getElementById("sync-tabs-button").hidden = true;
+ // If we've gotten this far, it means there are no tabs left to notify
+ // the user about, so remove any existing tabs notification.
+ Weave.Notifications.remove(notification);
},
- doInitTabsPanel: function Sync_doInitTabsPanel() {
- let list = document.getElementById("sync-tabs-list");
-
- let virtualTabs = this._getSortedVirtualTabs();
-
- // Remove virtual tabs that have previously been disposed of by the user.
- virtualTabs = virtualTabs.filter(function(v) !v._disposed);
-
- while (list.hasChildNodes())
- list.removeChild(list.lastChild);
-
- for each (let virtualTab in virtualTabs) {
- let currentEntry = virtualTab.state.entries[virtualTab.state.index - 1];
- if (!currentEntry || !currentEntry.url) {
- this._log.warn("doInitTabsPanel: no current entry or no URL, can't " +
- "identify " + this._json.encode(virtualTab));
- continue;
- }
-
- let label = currentEntry.title ? currentEntry.title : currentEntry.url;
- let listitem = list.appendItem(label, virtualTab.id);
- listitem.setAttribute("type", "checkbox");
- // Make a tooltip that contains either or both of the title and URL.
- listitem.tooltipText =
- [currentEntry.title, currentEntry.url].filter(function(v) v).join("\n");
- }
- },
-
- doCloseTabsPanel: function Sync_doCloseTabsPanel() {
- document.getElementById("sync-tabs-panel").hidePopup();
- },
-
- doSyncTabs: function Sync_doSyncTabs() {
- let list = document.getElementById("sync-tabs-list");
- let virtualTabs = Weave.Engines.get("tabs").store.virtualTabs;
-
- for (let i = 0; i < list.childNodes.length; i++) {
- let listitem = list.childNodes[i];
- let virtualTab = virtualTabs[listitem.value];
- if (listitem.checked) {
- let tab = gBrowser.addTab("about:blank");
- this._sessionStore.setTabState(tab, this._json.encode(virtualTab.state));
- delete virtualTabs[listitem.value];
- }
- else {
- // Mark the tab disposed of by the user so we don't show it the next
- // time the user opens the sync tabs panel. Note: this flag does not
- // get synced to the server, so disposal happens on each client
- // separately, which means the user will still be prompted about this
- // tab when syncing to a third client.
- virtualTab._disposed = true;
- }
- }
-
- Weave.Engines.get("tabs").store.virtualTabs = virtualTabs;
- this.doCloseTabsPanel();
- document.getElementById("sync-tabs-button").hidden = true;
+ _onNotificationRemoved: function Sync__onNotificationRemoved() {
+ if (Weave.Notifications.notifications.length == 0)
+ document.getElementById("sync-notifications-button").hidden = true;
},
_updateLastSyncItem: function Sync__updateLastSyncItem() {
@@ -557,6 +567,12 @@ Sync.prototype = {
case "weave:store:tabs:virtual:removed":
this._onVirtualTabRemoved();
break;
+ case "weave:notification:added":
+ this._onNotificationAdded();
+ break;
+ case "weave:notification:removed":
+ this._onNotificationRemoved();
+ break;
default:
this._log.warn("Unknown observer notification topic: " + topic);
break;
diff -r 08e0f72e2948 chrome/content/sync.xul
--- a/chrome/content/sync.xul Tue Jun 24 08:51:40 2008 -0700
+++ b/chrome/content/sync.xul Tue Jun 24 17:38:48 2008 -0700
@@ -1,5 +1,6 @@
+
@@ -68,25 +69,13 @@
control="sync-menu-button"
value="&status.offline.label;"/>
-
-
-
-
-
-
- &syncTabsPanel.description;
-
-
-
-
-
-
-
+
+
+
diff -r 08e0f72e2948 chrome/locale/en-US/sync.properties
--- a/chrome/locale/en-US/sync.properties Tue Jun 24 08:51:40 2008 -0700
+++ b/chrome/locale/en-US/sync.properties Tue Jun 24 17:38:48 2008 -0700
@@ -1,8 +1,12 @@
# %S is the date and time at which the last sync successfully completed
lastSync.label = Last Update: %S
-status.idle = Idle
-status.active = Working...
-status.offline = Offline
-status.error = Error
+
+weaveButtonOffline.label = Sign In
+weaveButtonOnline.label = Weave
shareBookmark.menuItem = Share This Folder...
-unShareBookmark.menuItem = Stop Sharing This Folder
\ No newline at end of file
+unShareBookmark.menuItem = Stop Sharing This Folder
+
+error.logout.title = Error While Signing Out
+error.logout.description = Weave encountered an error while signing you out. It's probably ok, and you don't have to do anything about it (then why are we bugging you with this info?).
+error.sync.title = Error While Syncing
+error.sync.description = Weave encountered an error while syncing. You might want to try syncing manually to make sure you are up-to-date.
diff -r 08e0f72e2948 chrome/skin/notification.css
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/chrome/skin/notification.css Tue Jun 24 17:38:48 2008 -0700
@@ -0,0 +1,62 @@
+/* Based on toolkit/themes/winstripe/global/notification.css */
+
+.notification {
+ background-color: InfoBackground;
+ color: InfoText;
+}
+
+.notification[type="info"] {
+ background-color: -moz-Dialog;
+ color: -moz-DialogText;
+}
+
+.notification[type="critical"] {
+ background-color: red;
+ color: white;
+}
+
+.messageImage {
+ width: 16px;
+ height: 16px;
+ margin: 0px 1px 0px 6px;
+}
+
+/* Default icons for notifications */
+
+.notification[type="info"] .messageImage {
+ list-style-image: url("chrome://global/skin/icons/information-16.png");
+}
+
+.notification[type="warning"] .messageImage {
+ list-style-image: url("chrome://global/skin/icons/warning-16.png");
+}
+
+.notification[type="critical"] .messageImage {
+ list-style-image: url("chrome://global/skin/icons/error-16.png");
+}
+
+.messageText {
+ -moz-margin-start: 5px;
+}
+
+.messageButton {
+ margin: 0px 5px 0px 5px;
+}
+
+.messageCloseButton {
+ /* FIXME: make this platform-specific by moving it into platform-specific
+ * subdirectories of the chrome/skin/ directory. */
+ list-style-image: url("chrome://weave/skin/icons/close.png");
+ -moz-appearance: none;
+ -moz-image-region: rect(0px, 14px, 14px, 0px);
+ padding: 4px 2px;
+ border: none !important;
+}
+
+.messageCloseButton:hover {
+ -moz-image-region: rect(0px, 28px, 14px, 14px);
+}
+
+.messageCloseButton:hover:active {
+ -moz-image-region: rect(0px, 42px, 14px, 28px);
+}
diff -r 08e0f72e2948 modules/Observers.js
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/Observers.js Tue Jun 24 17:38:48 2008 -0700
@@ -0,0 +1,100 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Observers.
+ *
+ * The Initial Developer of the Original Code is Daniel Aquino.
+ * Portions created by the Initial Developer are Copyright (C) 2008
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Daniel Aquino
+ * Myk Melez
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+let EXPORTED_SYMBOLS = ["Observers"];
+
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+const Cr = Components.results;
+const Cu = Components.utils;
+
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+
+let Observers = {
+ add: function(callback, topic) {
+ let observer = new Observer(callback);
+ if (!(topic in Observers._observers))
+ Observers._observers[topic] = {};
+ Observers._observers[topic][callback] = observer;
+ Observers._service.addObserver(observer, topic, true);
+ return observer;
+ },
+
+ remove: function(callback, topic) {
+ let observer = Observers._observers[topic][callback];
+ Observers._service.removeObserver(observer, topic);
+ delete this._observers[topic][callback];
+ },
+
+ notify: function(subject, topic, data) {
+ Observers._service.notifyObservers(new Subject(subject), topic, data);
+ },
+
+ _service: Cc["@mozilla.org/observer-service;1"].
+ getService(Ci.nsIObserverService),
+
+ _observers: {}
+};
+
+
+function Observer(callback) {
+ this._callback = callback;
+}
+
+Observer.prototype = {
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver, Ci.nsISupportsWeakReference]),
+ observe: function(subject, topic, data) {
+ // Pass the wrappedJSObject for subjects that have one. Otherwise pass
+ // the subject itself. This way we support both wrapped subjects created
+ // using this module and those that are real XPCOM components.
+ if (subject.wrappedJSObject)
+ this._callback(subject.wrappedJSObject, topic, data);
+ else
+ this._callback(subject, topic, data);
+ }
+}
+
+
+function Subject(object) {
+ this.wrappedJSObject = object;
+}
+
+Subject.prototype = {
+ QueryInterface: XPCOMUtils.generateQI([]),
+ getHelperForLanguage: function() {},
+ getInterfaces: function() {}
+};
diff -r 08e0f72e2948 modules/notifications.js
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/notifications.js Tue Jun 24 17:38:48 2008 -0700
@@ -0,0 +1,155 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Bookmarks Sync.
+ *
+ * The Initial Developer of the Original Code is Mozilla.
+ * Portions created by the Initial Developer are Copyright (C) 2007
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Myk Melez
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+const EXPORTED_SYMBOLS = ["Notifications", "Notification", "TabsNotification"];
+
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+const Cr = Components.results;
+const Cu = Components.utils;
+
+Cu.import("resource://weave/Observers.js");
+Cu.import("resource://weave/log4moz.js");
+Cu.import("resource://weave/util.js");
+
+let Notifications = {
+ // Identical to the values in toolkit/content/widgets/notification.xml.
+ // XXX Should we repurpose "CRITICAL" to mean "ERROR"?
+ get PRIORITY_INFO_LOW() 1,
+ get PRIORITY_INFO_MEDIUM() 2,
+ get PRIORITY_INFO_HIGH() 3,
+ get PRIORITY_WARNING_LOW() 4,
+ get PRIORITY_WARNING_MEDIUM() 5,
+ get PRIORITY_WARNING_HIGH() 6,
+ get PRIORITY_CRITICAL_LOW() 7,
+ get PRIORITY_CRITICAL_MEDIUM() 8,
+ get PRIORITY_CRITICAL_HIGH() 9,
+ get PRIORITY_CRITICAL_BLOCK() 10,
+
+ // FIXME: instead of making this public, dress the Notifications object
+ // to behave like an iterator (using generators?) and have callers access
+ // this array through the Notifications object.
+ notifications: [],
+
+ _observers: [],
+
+ // XXX Should we have a helper method for adding a simple notification?
+ // I.e. something like |function notify(title, description, priority)|.
+
+ add: function Notifications_add(notification) {
+ this.notifications.push(notification);
+ Observers.notify(notification, "weave:notification:added", null);
+ },
+
+ remove: function Notifications_remove(notification) {
+ let index = this.notifications.indexOf(notification);
+ if (index != -1) {
+ this.notifications.splice(index, 1);
+ Observers.notify(notification, "weave:notification:removed", null);
+ }
+ },
+
+ /**
+ * Replace an existing notification.
+ */
+ replace: function Notifications_replace(oldNotification, newNotification) {
+ let index = this.notifications.indexOf(oldNotification);
+
+ if (index != -1)
+ this.notifications.splice(index, 1, newNotification);
+ else {
+ this.notifications.push(notification);
+ // XXX Should we throw because we didn't find the existing notification?
+ // XXX Should we notify observers about weave:notification:added?
+ }
+
+ // XXX Should we notify observers about weave:notification:replaced?
+ }
+
+};
+
+
+/**
+ * A basic notification. Subclass this to create more complex notifications.
+ */
+function Notification(title, description, priority) {
+ this.title = title;
+ this.description = description;
+
+ if (priority) {
+ this.priority = priority;
+
+ switch(priority) {
+ case Notifications.PRIORITY_CRITICAL_LOW:
+ case Notifications.PRIORITY_CRITICAL_MEDIUM:
+ case Notifications.PRIORITY_CRITICAL_HIGH:
+ case Notifications.PRIORITY_CRITICAL_BLOCK:
+ iconURI = Utils.makeURI("chrome://global/skin/icons/error-24.png");
+ break;
+
+ case Notifications.PRIORITY_WARNING_LOW:
+ case Notifications.PRIORITY_WARNING_MEDIUM:
+ case Notifications.PRIORITY_WARNING_HIGH:
+ iconURI = Utils.makeURI("chrome://global/skin/icons/warning-24.png");
+ break;
+
+ case Notifications.PRIORITY_INFO_LOW:
+ case Notifications.PRIORITY_INFO_MEDIUM:
+ case Notifications.PRIORITY_INFO_HIGH:
+ default:
+ iconURI = Utils.makeURI("chrome://global/skin/icons/information-24.png");
+ break;
+ }
+ }
+}
+
+// We set each value manually instead of redefining the entire prototype
+// to avoid blowing away the "constructor" property, which we use to bind
+// notification objects to their XBL representations.
+Notification.prototype.priority = Notifications.PRIORITY_INFO_LOW;
+Notification.prototype.iconURI = Utils.makeURI("chrome://global/skin/icons/information-24.png");
+
+
+function TabsNotification() {
+ // Call the base class's constructor to initialize the new instance.
+ // XXX Can we simply pass null, null for the title, description?
+ Notification.call(this, "", "", Notifications.PRIORITY_INFO_LOW);
+}
+
+// We set each value manually instead of redefining the entire prototype
+// to avoid blowing away the "constructor" property, which we use to bind
+// notification objects to their XBL representations.
+TabsNotification.prototype.__proto__ = Notification.prototype;
diff -r 08e0f72e2948 modules/service.js
--- a/modules/service.js Tue Jun 24 08:51:40 2008 -0700
+++ b/modules/service.js Tue Jun 24 17:38:48 2008 -0700
@@ -89,6 +89,7 @@ Cu.import("resource://weave/util.js", We
Cu.import("resource://weave/util.js", Weave);
Cu.import("resource://weave/async.js", Weave);
Cu.import("resource://weave/crypto.js", Weave);
+Cu.import("resource://weave/notifications.js", Weave);
Cu.import("resource://weave/identity.js", Weave);
Cu.import("resource://weave/dav.js", Weave);
Cu.import("resource://weave/stores.js", Weave);
diff -r 08e0f72e2948 modules/xmpp/transportLayer.js
--- a/modules/xmpp/transportLayer.js Tue Jun 24 08:51:40 2008 -0700
+++ b/modules/xmpp/transportLayer.js Tue Jun 24 17:38:48 2008 -0700
@@ -177,11 +177,11 @@ HTTPPollingTransport.prototype = {
this._retryCap = 0;
},
- __request: null,
get _request() {
- if (!this.__request)
- this.__request = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].createInstance( Ci.nsIXMLHttpRequest );
- return this.__request;
+ let request = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].createInstance( Ci.nsIXMLHttpRequest );
+ request.mozBackgroundRequest = true;
+ this.__defineGetter__("_request", function() request);
+ return this._request;
},
__hasher: null,
@@ -256,7 +256,6 @@ HTTPPollingTransport.prototype = {
_doPost: function( requestXml ) {
var request = this._request;
- request.mozBackgroundRequest = true;
var callbackObj = this._callbackObject;
var self = this;
var contents = "";