summaryrefslogtreecommitdiff
path: root/calendar/base/modules/calAsyncUtils.jsm
diff options
context:
space:
mode:
Diffstat (limited to 'calendar/base/modules/calAsyncUtils.jsm')
-rw-r--r--calendar/base/modules/calAsyncUtils.jsm128
1 files changed, 128 insertions, 0 deletions
diff --git a/calendar/base/modules/calAsyncUtils.jsm b/calendar/base/modules/calAsyncUtils.jsm
new file mode 100644
index 000000000..cbb2adb5a
--- /dev/null
+++ b/calendar/base/modules/calAsyncUtils.jsm
@@ -0,0 +1,128 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+Components.utils.import("resource://calendar/modules/calUtils.jsm");
+Components.utils.import("resource://gre/modules/Promise.jsm");
+Components.utils.import("resource://gre/modules/PromiseUtils.jsm");
+Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
+
+/*
+ * Asynchronous tools for handling calendar operations.
+ */
+
+this.EXPORTED_SYMBOLS = ["cal"]; // even though it's defined in calUtils.jsm, import needs this
+var cIOL = Components.interfaces.calIOperationListener;
+var cIC = Components.interfaces.calICalendar;
+
+var promisifyProxyHandler = {
+ promiseOperation: function(target, name, args) {
+ let deferred = PromiseUtils.defer();
+ let listener = cal.async.promiseOperationListener(deferred);
+ args.push(listener);
+ target[name](...args);
+ return deferred.promise;
+ },
+ get: function(target, name) {
+ switch (name) {
+ case "adoptItem":
+ case "addItem":
+ case "modifyItem":
+ case "deleteItem":
+ case "getItem":
+ case "getItems":
+ return (...args) => this.promiseOperation(target, name, args);
+ case "getAllItems":
+ return () => this.promiseOperation(target, "getItems", [cIC.ITEM_FILTER_ALL_ITEMS, 0, null, null]);
+ default:
+ return target[name];
+ }
+ }
+};
+
+cal.async = {
+ /**
+ * Creates a proxy to the given calendar where the CRUD operations are replaced
+ * with versions that return a promise and don't take a listener.
+ *
+ * Before:
+ * calendar.addItem(item, {
+ * onGetResult: function() {},
+ * onOperationComplete: function (c,status,t,c,detail) {
+ * if (Components.isSuccessCode(status)) {
+ * handleSuccess(detail);
+ * } else {
+ * handleFailure(status);
+ * }
+ * }
+ * });
+ *
+ * After:
+ * let pcal = promisifyCalendar(calendar);
+ * pcal.addItem(item).then(handleSuccess, handleFailure);
+ *
+ * Bonus methods in addition:
+ * pcal.getAllItems() // alias for getItems without any filters
+ *
+ * IMPORTANT: Don't pass this around thinking its like an xpcom calICalendar,
+ * otherwise code might indefinitely wait for the listener to return or there
+ * will be complaints that an argument is missing.
+ */
+ promisifyCalendar: function(aCalendar) {
+ return new Proxy(aCalendar, promisifyProxyHandler);
+ },
+ /**
+ * Create an operation listener (calIOperationListener) that resolves when
+ * the operation succeeds. Note this listener will collect the items, so it
+ * might not be a good idea in a situation where a lot of items will be
+ * retrieved.
+ *
+ * Standalone Usage:
+ * function promiseAddItem(aItem) {
+ * let deferred = PromiseUtils.defer();
+ * let listener = cal.async.promiseOperationListener(deferred);
+ * aItem.calendar.addItem(aItem, listener);
+ * return deferred.promise;
+ * }
+ *
+ * See also promisifyCalendar, where the above can be replaced with:
+ * function promiseAddItem(aItem) {
+ * let calendar = cal.async.promisifyCalendar(aItem.calendar);
+ * return calendar.addItem(aItem);
+ * }
+ */
+ promiseOperationListener: function(deferred) {
+ return {
+ QueryInterface: XPCOMUtils.generateQI([Components.interfaces.calIOperationListener]),
+ items: [],
+ itemStatus: Components.results.NS_OK,
+ onGetResult: function(aCalendar, aStatus, aItemType, aDetail,
+ aCount, aItems) {
+ this.itemStatus = aStatus;
+ if (Components.isSuccessCode(aStatus)) {
+ this.items = this.items.concat(aItems);
+ } else {
+ this.itemSuccess = aStatus;
+ }
+ },
+
+ onOperationComplete: function(aCalendar, aStatus, aOpType, aId, aDetail) {
+ if (!Components.isSuccessCode(aStatus)) {
+ // This function has failed, reject with the status
+ deferred.reject(aStatus);
+ } else if (!Components.isSuccessCode(this.itemStatus)) {
+ // onGetResult has failed, reject with its status
+ deferred.reject(this.itemStatus);
+ } else if (aOpType == cIOL.GET) {
+ // Success of a GET operation: resolve with array of
+ // resulting items.
+ deferred.resolve(this.items);
+ } else { /* ADD,MODIFY,DELETE: resolve with 1 item */
+ // Success of an ADD MODIFY or DELETE operation, resolve
+ // with the one item that was processed.
+ deferred.resolve(aDetail);
+ }
+ }
+ };
+ }
+};