diff --git a/browser/devtools/shared/test/Makefile.in b/browser/devtools/shared/test/Makefile.in index d7b39ec57e9d..872330db9be7 100644 --- a/browser/devtools/shared/test/Makefile.in +++ b/browser/devtools/shared/test/Makefile.in @@ -15,7 +15,6 @@ XPCSHELL_TESTS = unit MOCHITEST_BROWSER_FILES = \ browser_browser_basic.js \ - browser_promise_basic.js \ browser_require_basic.js \ browser_templater_basic.js \ browser_toolbar_basic.js \ diff --git a/browser/devtools/shared/test/browser_promise_basic.js b/browser/devtools/shared/test/browser_promise_basic.js deleted file mode 100644 index 2466e4982810..000000000000 --- a/browser/devtools/shared/test/browser_promise_basic.js +++ /dev/null @@ -1,305 +0,0 @@ -/* Any copyright is dedicated to the Public Domain. - http://creativecommons.org/publicdomain/zero/1.0/ */ - -// Tests that our Promise implementation works properly - -let tempScope = {}; -Cu.import("resource://gre/modules/devtools/_Promise.jsm", tempScope); -let Promise = tempScope.Promise; - -function test() { - addTab("about:blank", function() { - info("Starting Promise Tests"); - testBasic(); - }); -} - -var postResolution; - -function testBasic() { - postResolution = new Promise(); - ok(postResolution.isPromise, "We have a promise"); - ok(!postResolution.isComplete(), "Promise is initially incomplete"); - ok(!postResolution.isResolved(), "Promise is initially unresolved"); - ok(!postResolution.isRejected(), "Promise is initially unrejected"); - - // Test resolve() *after* then() in the same context - var reply = postResolution.then(testPostResolution, fail) - .resolve("postResolution"); - is(reply, postResolution, "return this; working ok"); -} - -var preResolution; - -function testPostResolution(data) { - is(data, "postResolution", "data is postResolution"); - ok(postResolution.isComplete(), "postResolution Promise is complete"); - ok(postResolution.isResolved(), "postResolution Promise is resolved"); - ok(!postResolution.isRejected(), "postResolution Promise is unrejected"); - - try { - info("Expected double resolve error"); - postResolution.resolve("double resolve"); - ok(false, "double resolve"); - } - catch (ex) { - // Expected - } - - // Test resolve() *before* then() in the same context - preResolution = new Promise(); - var reply = preResolution.resolve("preResolution") - .then(testPreResolution, fail); - is(reply, preResolution, "return this; working ok"); -} - -var laterResolution; - -function testPreResolution(data) { - is(data, "preResolution", "data is preResolution"); - ok(preResolution.isComplete(), "preResolution Promise is complete"); - ok(preResolution.isResolved(), "preResolution Promise is resolved"); - ok(!preResolution.isRejected(), "preResolution Promise is unrejected"); - - // Test resolve() *after* then() in a later context - laterResolution = new Promise(); - laterResolution.then(testLaterResolution, fail); - executeSoon(function() { - laterResolution.resolve("laterResolution"); - }); -} - -var laterRejection; - -function testLaterResolution(data) { - is(data, "laterResolution", "data is laterResolution"); - ok(laterResolution.isComplete(), "laterResolution Promise is complete"); - ok(laterResolution.isResolved(), "laterResolution Promise is resolved"); - ok(!laterResolution.isRejected(), "laterResolution Promise is unrejected"); - - // Test reject() *after* then() in a later context - laterRejection = new Promise().then(fail, testLaterRejection); - executeSoon(function() { - laterRejection.reject("laterRejection"); - }); -} - -function testLaterRejection(data) { - is(data, "laterRejection", "data is laterRejection"); - ok(laterRejection.isComplete(), "laterRejection Promise is complete"); - ok(!laterRejection.isResolved(), "laterRejection Promise is unresolved"); - ok(laterRejection.isRejected(), "laterRejection Promise is rejected"); - - // Test chaining - var orig = new Promise(); - orig.chainPromise(function(data) { - is(data, "origData", "data is origData"); - return data.replace(/orig/, "new"); - }).then(function(data) { - is(data, "newData", "data is newData"); - testChain(); - }); - orig.resolve("origData"); -} - -var member1; -var member2; -var member3; -var laterGroup; - -function testChain() { - // Test an empty group - var empty1 = Promise.group(); - ok(empty1.isComplete(), "empty1 Promise is complete"); - ok(empty1.isResolved(), "empty1 Promise is resolved"); - ok(!empty1.isRejected(), "empty1 Promise is unrejected"); - - // Test a group with no members - var empty2 = Promise.group([]); - ok(empty2.isComplete(), "empty2 Promise is complete"); - ok(empty2.isResolved(), "empty2 Promise is resolved"); - ok(!empty2.isRejected(), "empty2 Promise is unrejected"); - - // Test grouping using resolve() in a later context - member1 = new Promise(); - member2 = new Promise(); - member3 = new Promise(); - laterGroup = Promise.group(member1, member2, member3); - laterGroup.then(testLaterGroup, fail); - - member1.then(function(data) { - is(data, "member1", "member1 is member1"); - executeSoon(function() { - member2.resolve("member2"); - }); - }, fail); - member2.then(function(data) { - is(data, "member2", "member2 is member2"); - executeSoon(function() { - member3.resolve("member3"); - }); - }, fail); - member3.then(function(data) { - is(data, "member3", "member3 is member3"); - // The group should now fire - }, fail); - executeSoon(function() { - member1.resolve("member1"); - }); -} - -var tidyGroup; - -function testLaterGroup(data) { - is(data[0], "member1", "member1 is member1"); - is(data[1], "member2", "member2 is member2"); - is(data[2], "member3", "member3 is member3"); - is(data.length, 3, "data.length is right"); - ok(laterGroup.isComplete(), "laterGroup Promise is complete"); - ok(laterGroup.isResolved(), "laterGroup Promise is resolved"); - ok(!laterGroup.isRejected(), "laterGroup Promise is unrejected"); - - // Test grouping resolve() *before* then() in the same context - tidyGroup = Promise.group([ - postResolution, preResolution, laterResolution, - member1, member2, member3, laterGroup - ]); - tidyGroup.then(testTidyGroup, fail); -} - -var failGroup; - -function testTidyGroup(data) { - is(data[0], "postResolution", "postResolution is postResolution"); - is(data[1], "preResolution", "preResolution is preResolution"); - is(data[2], "laterResolution", "laterResolution is laterResolution"); - is(data[3], "member1", "member1 is member1"); - is(data[6][1], "member2", "laterGroup is laterGroup"); - is(data.length, 7, "data.length is right"); - ok(tidyGroup.isComplete(), "tidyGroup Promise is complete"); - ok(tidyGroup.isResolved(), "tidyGroup Promise is resolved"); - ok(!tidyGroup.isRejected(), "tidyGroup Promise is unrejected"); - - // Test grouping resolve() *before* then() in the same context - failGroup = Promise.group(postResolution, laterRejection); - failGroup.then(fail, testFailGroup); -} - -function testFailGroup(data) { - is(data, "laterRejection", "laterRejection is laterRejection"); - - postResolution = undefined; - preResolution = undefined; - laterResolution = undefined; - member1 = undefined; - member2 = undefined; - member3 = undefined; - laterGroup = undefined; - laterRejection = undefined; - - testTrap(); -} - -function testTrap() { - var p = new Promise(); - var message = "Expected exception"; - p.chainPromise( - function() { - throw new Error(message); - }).trap( - function(aError) { - is(aError instanceof Error, true, "trap received exception"); - is(aError.message, message, "trap received correct exception"); - return 1; - }).chainPromise( - function(aResult) { - is(aResult, 1, "trap restored correct result"); - testAlways(); - }); - p.resolve(); -} - -function testAlways() { - var shouldbeTrue1 = false; - var shouldbeTrue2 = false; - var p = new Promise(); - p.chainPromise( - function() { - throw new Error(); - } - ).chainPromise(// Promise rejected, should not be executed - function() { - ok(false, "This should not be executed"); - } - ).always( - function(x) { - shouldbeTrue1 = true; - return "random value"; - } - ).trap( - function(arg) { - ok((arg instanceof Error), "The random value should be ignored"); - return 1;// We should still have this result later - } - ).trap( - function() { - ok(false, "This should not be executed 2"); - } - ).always( - function() { - shouldbeTrue2 = true; - } - ).then( - function(aResult){ - ok(shouldbeTrue1, "First always must be executed"); - ok(shouldbeTrue2, "Second always must be executed"); - is(aResult, 1, "Result should be unaffected by always"); - - testComplete(); - } - ); - p.resolve(); -} - -function fail() { - gBrowser.removeCurrentTab(); - info("Failed Promise Tests"); - ok(false, "fail called"); - finish(); -} - -/** - * We wish to launch all tests with several configurations (at the moment, - * non-debug and debug mode). - * - * If 0, we have not completed any test yet. - * If 1, we have completed the tests in non-debug mode. - * If 2, we have also completed the tests in debug mode. - */ -var configurationTestComplete = 0; -function testComplete() { - switch (configurationTestComplete) { - case 0: - info("Finished run in non-debug mode"); - configurationTestComplete = 1; - Promise.Debug.setDebug(true); - window.setTimeout(testBasic, 0); - return; - case 1: - info("Finished run in debug mode"); - configurationTestComplete = 2; - Promise.Debug.setDebug(false); - window.setTimeout(finished, 0); - return; - default: - ok(false, "Internal error in testComplete "+configurationTestComplete); - return; - } -} - - -function finished() { - gBrowser.removeCurrentTab(); - info("Finishing Promise Tests"); - finish(); -} diff --git a/browser/devtools/shared/test/browser_templater_basic.js b/browser/devtools/shared/test/browser_templater_basic.js index 6d83220f379d..e3a6591ce8b6 100644 --- a/browser/devtools/shared/test/browser_templater_basic.js +++ b/browser/devtools/shared/test/browser_templater_basic.js @@ -11,7 +11,7 @@ var imports = {}; Cu.import("resource:///modules/devtools/Templater.jsm", imports); -Cu.import("resource://gre/modules/devtools/_Promise.jsm", imports); +Cu.import("resource://gre/modules/commonjs/promise/core.js", imports); function test() { addTab("http://example.com/browser/browser/devtools/shared/test/browser_templater_basic.html", function() { @@ -278,9 +278,9 @@ var tests = [ ]; function delayReply(data) { - var p = new imports.Promise(); + var d = imports.Promise.defer(); executeSoon(function() { - p.resolve(data); + d.resolve(data); }); - return p; + return d.promise; } diff --git a/toolkit/devtools/_Promise.jsm b/toolkit/devtools/_Promise.jsm deleted file mode 100644 index 0ea8c54d040b..000000000000 --- a/toolkit/devtools/_Promise.jsm +++ /dev/null @@ -1,409 +0,0 @@ -/* - * Copyright 2009-2011 Mozilla Foundation and contributors - * Licensed under the New BSD license. See LICENSE.txt or: - * http://opensource.org/licenses/BSD-3-Clause - */ - - -this.EXPORTED_SYMBOLS = [ "Promise" ]; - -/** - * Create an unfulfilled promise - * - * @param {*=} aTrace A debugging value - * - * @constructor - */ -this.Promise = function Promise(aTrace) { - this._status = Promise.PENDING; - this._value = undefined; - this._onSuccessHandlers = []; - this._onErrorHandlers = []; - this._trace = aTrace; - - // Debugging help - if (Promise.Debug._debug) { - this._id = Promise.Debug._nextId++; - Promise.Debug._outstanding[this._id] = this; - } -} - -/** - * Debugging options and tools. - */ -Promise.Debug = { - /** - * Set current debugging mode. - * - * @param {boolean} value If |true|, maintain _nextId, _outstanding, _recent. - * Otherwise, cleanup debugging data. - */ - setDebug: function(value) { - Promise.Debug._debug = value; - if (!value) { - Promise.Debug._outstanding = []; - Promise.Debug._recent = []; - } - }, - - _debug: false, - - /** - * We give promises and ID so we can track which are outstanding. - */ - _nextId: 0, - - /** - * Outstanding promises. Handy for debugging (only). - */ - _outstanding: [], - - /** - * Recently resolved promises. Also for debugging only. - */ - _recent: [] -}; - - -/** - * A promise can be in one of 2 states. - * The ERROR and SUCCESS states are terminal, the PENDING state is the only - * start state. - */ -Promise.ERROR = -1; -Promise.PENDING = 0; -Promise.SUCCESS = 1; - -/** - * Yeay for RTTI - */ -Promise.prototype.isPromise = true; - -/** - * Have we either been resolve()ed or reject()ed? - */ -Promise.prototype.isComplete = function() { - return this._status != Promise.PENDING; -}; - -/** - * Have we resolve()ed? - */ -Promise.prototype.isResolved = function() { - return this._status == Promise.SUCCESS; -}; - -/** - * Have we reject()ed? - */ -Promise.prototype.isRejected = function() { - return this._status == Promise.ERROR; -}; - -/** - * Take the specified action of fulfillment of a promise, and (optionally) - * a different action on promise rejection - */ -Promise.prototype.then = function(onSuccess, onError) { - if (typeof onSuccess === 'function') { - if (this._status === Promise.SUCCESS) { - onSuccess.call(null, this._value); - } - else if (this._status === Promise.PENDING) { - this._onSuccessHandlers.push(onSuccess); - } - } - - if (typeof onError === 'function') { - if (this._status === Promise.ERROR) { - onError.call(null, this._value); - } - else if (this._status === Promise.PENDING) { - this._onErrorHandlers.push(onError); - } - } - - return this; -}; - -/** - * Like then() except that rather than returning this we return - * a promise which resolves when the original promise resolves - */ -Promise.prototype.chainPromise = function(onSuccess) { - var chain = new Promise(); - chain._chainedFrom = this; - this.then(function(data) { - try { - chain.resolve(onSuccess(data)); - } - catch (ex) { - chain.reject(ex); - } - }, function(ex) { - chain.reject(ex); - }); - return chain; -}; - -/** - * Supply the fulfillment of a promise - */ -Promise.prototype.resolve = function(data) { - return this._complete(this._onSuccessHandlers, - Promise.SUCCESS, data, 'resolve'); -}; - -/** - * Renege on a promise - */ -Promise.prototype.reject = function(data) { - return this._complete(this._onErrorHandlers, Promise.ERROR, data, 'reject'); -}; - -/** - * Internal method to be called on resolve() or reject() - * @private - */ -Promise.prototype._complete = function(list, status, data, name) { - // Complain if we've already been completed - if (this._status != Promise.PENDING) { - Promise._error("Promise complete.", "Attempted ", name, "() with ", data); - Promise._error("Previous status: ", this._status, ", value =", this._value); - throw new Error('Promise already complete'); - } - - if (list.length == 0 && status == Promise.ERROR) { - var frame; - var text; - - //Complain if a rejection is ignored - //(this is the equivalent of an empty catch-all clause) - Promise._error("Promise rejection ignored and silently dropped", data); - if (data.stack) {// This looks like an exception. Try harder to display it - if (data.fileName && data.lineNumber) { - Promise._error("Error originating at", data.fileName, - ", line", data.lineNumber ); - } - try { - for (frame = data.stack; frame; frame = frame.caller) { - text += frame + "\n"; - } - Promise._error("Attempting to extract exception stack", text); - } catch (x) { - Promise._error("Could not extract exception stack."); - } - } else { - Promise._error("Exception stack not available."); - } - if (Components && Components.stack) { - try { - text = ""; - for (frame = Components.stack; frame; frame = frame.caller) { - text += frame + "\n"; - } - Promise._error("Attempting to extract current stack", text); - } catch (x) { - Promise._error("Could not extract current stack."); - } - } else { - Promise._error("Current stack not available."); - } - } - - - this._status = status; - this._value = data; - - // Call all the handlers, and then delete them - list.forEach(function(handler) { - handler.call(null, this._value); - }, this); - delete this._onSuccessHandlers; - delete this._onErrorHandlers; - - // Remove the given {promise} from the _outstanding list, and add it to the - // _recent list, pruning more than 20 recent promises from that list - delete Promise.Debug._outstanding[this._id]; - // The original code includes this very useful debugging aid, however there - // is concern that it will create a memory leak, so we leave it out here. - /* - Promise._recent.push(this); - while (Promise._recent.length > 20) { - Promise._recent.shift(); - } - */ - - return this; -}; - -/** - * Log an error on the most appropriate channel. - * - * If the console is available, this method uses |console.warn|. Otherwise, - * this method falls back to |dump|. - * - * @param {...*} items Items to log. - */ -Promise._error = null; -if (typeof console != "undefined" && console.warn) { - Promise._error = function() { - var args = Array.prototype.slice.call(arguments); - args.unshift("Promise"); - console.warn.call(console, args); - }; -} else { - Promise._error = function() { - var i; - var len = arguments.length; - dump("Promise: "); - for (i = 0; i < len; ++i) { - dump(arguments[i]+" "); - } - dump("\n"); - }; -} - -/** - * Takes an array of promises and returns a promise that that is fulfilled once - * all the promises in the array are fulfilled - * @param promiseList The array of promises - * @return the promise that is fulfilled when all the array is fulfilled - */ -Promise.group = function(promiseList) { - if (!Array.isArray(promiseList)) { - promiseList = Array.prototype.slice.call(arguments); - } - - // If the original array has nothing in it, return now to avoid waiting - if (promiseList.length === 0) { - return new Promise().resolve([]); - } - - var groupPromise = new Promise(); - var results = []; - var fulfilled = 0; - - var onSuccessFactory = function(index) { - return function(data) { - results[index] = data; - fulfilled++; - // If the group has already failed, silently drop extra results - if (groupPromise._status !== Promise.ERROR) { - if (fulfilled === promiseList.length) { - groupPromise.resolve(results); - } - } - }; - }; - - promiseList.forEach(function(promise, index) { - var onSuccess = onSuccessFactory(index); - var onError = groupPromise.reject.bind(groupPromise); - promise.then(onSuccess, onError); - }); - - return groupPromise; -}; - -/** - * Trap errors. - * - * This function serves as an asynchronous counterpart to |catch|. - * - * Example: - * myPromise.chainPromise(a) //May reject - * .chainPromise(b) //May reject - * .chainPromise(c) //May reject - * .trap(d) //Catch any rejection from a, b or c - * .chainPromise(e) //If either a, b and c or - * //d has resolved, execute - * - * Scenario 1: - * If a, b, c resolve, e is executed as if d had not been added. - * - * Scenario 2: - * If a, b or c rejects, d is executed. If d resolves, we proceed - * with e as if nothing had happened. Otherwise, we proceed with - * the rejection of d. - * - * @param {Function} aTrap Called if |this| promise is rejected, - * with one argument: the rejection. - * @return {Promise} A new promise. This promise resolves if all - * previous promises have resolved or if |aTrap| succeeds. - */ -Promise.prototype.trap = function(aTrap) { - var promise = new Promise(); - var resolve = Promise.prototype.resolve.bind(promise); - var reject = function(aRejection) { - try { - //Attempt to handle issue - var result = aTrap.call(aTrap, aRejection); - promise.resolve(result); - } catch (x) { - promise.reject(x); - } - }; - this.then(resolve, reject); - return promise; -}; - -/** - * Execute regardless of errors. - * - * This function serves as an asynchronous counterpart to |finally|. - * - * Example: - * myPromise.chainPromise(a) //May reject - * .chainPromise(b) //May reject - * .chainPromise(c) //May reject - * .always(d) //Executed regardless - * .chainPromise(e) - * - * Whether |a|, |b| or |c| resolve or reject, |d| is executed. - * - * @param {Function} aTrap Called regardless of whether |this| - * succeeds or fails. - * @return {Promise} A new promise. This promise holds the same - * resolution/rejection as |this|. - */ -Promise.prototype.always = function(aTrap) { - var promise = new Promise(); - var resolve = function(result) { - try { - aTrap.call(aTrap); - promise.resolve(result); - } catch (x) { - promise.reject(x); - } - }; - var reject = function(result) { - try { - aTrap.call(aTrap); - promise.reject(result); - } catch (x) { - promise.reject(result); - } - }; - this.then(resolve, reject); - return promise; -}; - - -Promise.prototype.toString = function() { - var status; - switch (this._status) { - case Promise.PENDING: - status = "pending"; - break; - case Promise.SUCCESS: - status = "resolved"; - break; - case Promise.ERROR: - status = "rejected"; - break; - default: - status = "invalid status: "+this._status; - } - return "[Promise " + this._id + " (" + status + ")]"; -}; diff --git a/toolkit/devtools/debugger/server/dbg-script-actors.js b/toolkit/devtools/debugger/server/dbg-script-actors.js index 9c4ae00ce5da..824fbf8d05cd 100644 --- a/toolkit/devtools/debugger/server/dbg-script-actors.js +++ b/toolkit/devtools/debugger/server/dbg-script-actors.js @@ -1346,27 +1346,23 @@ SourceActor.prototype = { * Handler for the "source" packet. */ onSource: function SA_onSource(aRequest) { - this + return this ._loadSource() - .chainPromise(function(aSource) { + .then(function(aSource) { return this._threadActor.createValueGrip( aSource, this.threadActor.threadLifetimePool); }.bind(this)) - .chainPromise(function (aSourceGrip) { + .then(function (aSourceGrip) { return { from: this.actorID, source: aSourceGrip }; - }.bind(this)) - .trap(function (aError) { + }.bind(this), function (aError) { return { "from": this.actorID, "error": "loadSourceError", "message": "Could not load the source for " + this._script.url + "." }; - }.bind(this)) - .chainPromise(function (aPacket) { - this.conn.send(aPacket); }.bind(this)); }, @@ -1404,7 +1400,7 @@ SourceActor.prototype = { * http://www.softwareishard.com/blog/firebug/nsitraceablechannel-intercept-http-traffic/ */ _loadSource: function SA__loadSource() { - let promise = new Promise(); + let deferred = defer(); let url = this._script.url; let scheme; try { @@ -1424,16 +1420,16 @@ SourceActor.prototype = { try { NetUtil.asyncFetch(url, function onFetch(aStream, aStatus) { if (!Components.isSuccessCode(aStatus)) { - promise.reject(new Error("Request failed")); + deferred.reject(new Error("Request failed")); return; } let source = NetUtil.readInputStreamToString(aStream, aStream.available()); - promise.resolve(this._convertToUnicode(source)); + deferred.resolve(this._convertToUnicode(source)); aStream.close(); }.bind(this)); } catch (ex) { - promise.reject(new Error("Request failed")); + deferred.reject(new Error("Request failed")); } break; @@ -1451,7 +1447,7 @@ SourceActor.prototype = { let streamListener = { onStartRequest: function(aRequest, aContext, aStatusCode) { if (!Components.isSuccessCode(aStatusCode)) { - promise.reject("Request failed"); + deferred.reject("Request failed"); } }, onDataAvailable: function(aRequest, aContext, aStream, aOffset, aCount) { @@ -1459,12 +1455,12 @@ SourceActor.prototype = { }, onStopRequest: function(aRequest, aContext, aStatusCode) { if (!Components.isSuccessCode(aStatusCode)) { - promise.reject("Request failed"); + deferred.reject("Request failed"); return; } - promise.resolve(this._convertToUnicode(chunks.join(""), - channel.contentCharset)); + deferred.resolve(this._convertToUnicode(chunks.join(""), + channel.contentCharset)); }.bind(this) }; @@ -1473,7 +1469,7 @@ SourceActor.prototype = { break; } - return promise; + return deferred.promise; } }; diff --git a/toolkit/devtools/debugger/server/dbg-server.js b/toolkit/devtools/debugger/server/dbg-server.js index d2446d908dad..119d33622b4b 100644 --- a/toolkit/devtools/debugger/server/dbg-server.js +++ b/toolkit/devtools/debugger/server/dbg-server.js @@ -24,7 +24,8 @@ let wantLogging = Services.prefs.getBoolPref("devtools.debugger.log"); Cu.import("resource://gre/modules/jsdebugger.jsm"); addDebuggerToGlobal(this); -Cu.import("resource://gre/modules/devtools/_Promise.jsm"); +Cu.import("resource://gre/modules/commonjs/promise/core.js"); +const { defer, resolve, reject } = Promise; function dumpn(str) { if (wantLogging) { @@ -664,16 +665,17 @@ DebuggerServerConnection.prototype = { } if (!ret) { - // XXX: The actor wasn't ready to reply yet, don't process new - // requests until it does. + // This should become an error once we've converted every user + // of this to promises in bug 794078. return; } - if (!ret.from) { - ret.from = aPacket.to; - } - - this.transport.send(ret); + resolve(ret).then(function(returnPacket) { + if (!returnPacket.from) { + returnPacket.from = aPacket.to; + } + this.transport.send(returnPacket); + }.bind(this)); }, /**