diff options
Diffstat (limited to 'calendar/base/modules/calAsyncUtils.jsm')
-rw-r--r-- | calendar/base/modules/calAsyncUtils.jsm | 128 |
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); + } + } + }; + } +}; |