Replace uses of co_call/continuation API with uses of spawn/Promise API
authorJeremy Maitin-Shepard <jeremy@jeremyms.com>
Wed, 15 Jan 2014 21:38:22 +0000 (15 13:38 -0800)
committerJeremy Maitin-Shepard <jeremy@jeremyms.com>
Wed, 12 Feb 2014 02:40:59 +0000 (11 18:40 -0800)
13 files changed:
modules/cache.js
modules/content-handler.js
modules/help.js
modules/hints.js
modules/keymap.js
modules/minibuffer-read-option.js
modules/minibuffer-read.js
modules/minibuffer.js
modules/save.js
modules/session.js
modules/spawn-process.js
modules/utils.js
modules/window.js

index 4049691..cd0e991 100644 (file)
@@ -33,20 +33,20 @@ function cache_entry_open(cache_session, uri) {
     let session = cache_service.createSession(cache_session, 0, true);
     session.doomEntriesIfExpired = false;
 
-    let cc = yield CONTINUATION;
+    let deferred = Promise.defer();
 
     let cache_listener = {
         onCacheEntryAvailable: function onCacheEntryAvailable(descriptor, accessGranted, status) {
             if (status != Cr.NS_OK)
-                cc.throw(cache_error(status));
+                deferred.reject(cache_error(status));
             else
-                cc(descriptor);
+                deferred.resolve(descriptor);
         }
     };
 
     let cache_key = uri.replace(/#.*$/, "");
     session.asyncOpenCacheEntry(cache_key, Ci.nsICache.ACCESS_READ, cache_listener);
-    yield co_return(yield SUSPEND);
+    return make_simple_cancelable(deferred);
 }
 
 function cache_entry_clear(cache_session, uri) {
@@ -56,26 +56,26 @@ function cache_entry_clear(cache_session, uri) {
     let session = cache_service.createSession(cache_session, 0, true);
     session.doomEntriesIfExpired = false;
 
-    let cc = yield CONTINUATION;
+    let deferred = Promise.defer();
 
     let cache_listener = {
         onCacheEntryDoomed: function onCacheEntryDoomed(status) {
             switch (status) {
             case Cr.NS_OK:
-                cc(true);
+                deferred.resolve(true);
                 break;
             case Cr.NS_ERROR_NOT_AVAILABLE:
-                cc(false);
+                deferred.resolve(false);
                 break;
             default:
-                cc.throw(cache_error(status));
+                deferred.reject(cache_error(status));
             }
         }
     };
 
     let cache_key = uri.replace(/#.*$/, "");
     session.doomEntry(cache_key, cache_listener);
-    yield co_return(yield SUSPEND);
+    return make_simple_cancelable(deferred);
 }
 
 function cache_clear (cache_type) {
index f1a938b..766024a 100644 (file)
@@ -234,7 +234,7 @@ download_helper.prototype = {
             var mime_type = launcher.MIMEInfo.MIMEType;
             var action = content_handlers.get(mime_type) ||
                 content_handler_prompt;
-            co_call(action(ctx));
+            spawn(action(ctx));
         } catch (e) {
             handle_interactive_error(ctx.window, e);
         }
index d704716..69675fb 100644 (file)
@@ -51,7 +51,7 @@ help_document_generator.prototype = {
                              "class", "source-code-reference",
                              "href", "javascript:");
         x.addEventListener("click", function (event) {
-            co_call(function () {
+            spawn(function () {
                 try {
                     yield ref.open_in_editor();
                 } catch (e) {
index 2d1d232..71abce4 100644 (file)
@@ -500,14 +500,18 @@ hints_minibuffer_annotation_mode(true);
  * $abort_callback
  */
 define_keywords("$keymap", "$auto", "$hint_xpath_expression", "$multiple");
-function hints_minibuffer_state (minibuffer, continuation, buffer) {
+function hints_minibuffer_state (minibuffer, buffer) {
     keywords(arguments, $keymap = hint_keymap, $auto);
     basic_minibuffer_state.call(this, minibuffer, $prompt = arguments.$prompt,
                                 $keymap = arguments.$keymap);
     if (hints_minibuffer_annotation_mode_enabled)
        this.hints_minibuffer_annotation = new hints_minibuffer_annotation(this, buffer.window);
     this.original_prompt = arguments.$prompt;
-    this.continuation = continuation;
+
+    let deferred = Promise.defer();
+    this.deferred = deferred;
+    this.promise = make_simple_cancelable(deferred);
+
     this.auto_exit = arguments.$auto ? true : false;
     this.xpath_expr = arguments.$hint_xpath_expression;
     this.auto_exit_timer_ID = null;
@@ -547,6 +551,7 @@ hints_minibuffer_state.prototype = {
         basic_minibuffer_state.prototype.unload.call(this);
     },
     destroy: function () {
+        this.promise.cancel();
         this.clear_auto_exit_timer();
         this.manager.remove();
         if (this.hints_minibuffer_annotation)
@@ -701,11 +706,8 @@ function hints_exit (window, s) {
     else if (cur == 0)
         elem = window.buffers.current.top_frame;
     if (elem !== null) {
-        var c = s.continuation;
-        delete s.continuation;
+        s.deferred.resolve(elem);
         window.minibuffer.pop_state();
-        if (c)
-            c(elem);
     }
 }
 
@@ -725,10 +727,9 @@ define_keywords("$buffer");
 minibuffer.prototype.read_hinted_element = function () {
     keywords(arguments);
     var buf = arguments.$buffer;
-    var s = new hints_minibuffer_state(this, (yield CONTINUATION), buf, forward_keywords(arguments));
+    var s = new hints_minibuffer_state(this, buf, forward_keywords(arguments));
     this.push_state(s);
-    var result = yield SUSPEND;
-    yield co_return(result);
+    yield co_return(yield s.promise);
 };
 
 provide("hints");
index d7f1d4c..3aad138 100644 (file)
@@ -559,10 +559,11 @@ define_keymap("key_binding_reader_keymap");
 define_key(key_binding_reader_keymap, match_any_key, "read-key-binding-key");
 
 define_keywords("$keymap");
-function key_binding_reader (minibuffer, continuation) {
+function key_binding_reader (minibuffer) {
     keywords(arguments, $prompt = "Describe key:");
     minibuffer_input_state.call(this, minibuffer, key_binding_reader_keymap, arguments.$prompt);
-    this.continuation = continuation;
+    this.deferred = Promise.defer();
+    this.promise = make_simple_cancelable(this.deferred);
     if (arguments.$keymap)
         this.target_keymap = arguments.$keymap;
     else
@@ -573,8 +574,7 @@ key_binding_reader.prototype = {
     constructor: key_binding_reader,
     __proto__: minibuffer_input_state.prototype,
     destroy: function () {
-        if (this.continuation)
-            this.continuation.throw(abort());
+        this.promise.cancel();
         minibuffer_input_state.prototype.destroy.call(this);
     }
 };
@@ -597,10 +597,8 @@ function read_key_binding_key (window, state, event) {
     state.key_sequence.push(combo);
 
     if (binding == null) {
-        var c = state.continuation;
-        delete state.continuation;
+        state.deferred.reject(invalid_key_binding(state.key_sequence));
         window.minibuffer.pop_state();
-        c.throw(invalid_key_binding(state.key_sequence));
         return;
     }
 
@@ -611,13 +609,8 @@ function read_key_binding_key (window, state, event) {
         return;
     }
 
-    var c = state.continuation;
-    delete state.continuation;
-
+    state.deferred.resolve([state.key_sequence, binding]);
     window.minibuffer.pop_state();
-
-    if (c != null)
-        c([state.key_sequence, binding]);
 }
 interactive("read-key-binding-key",
     "Handle a keystroke in the key binding reader minibuffer state.",
@@ -629,10 +622,9 @@ interactive("read-key-binding-key",
 
 minibuffer.prototype.read_key_binding = function () {
     keywords(arguments);
-    var s = new key_binding_reader(this, (yield CONTINUATION), forward_keywords(arguments));
+    var s = new key_binding_reader(this, forward_keywords(arguments));
     this.push_state(s);
-    var result = yield SUSPEND;
-    yield co_return(result);
+    yield co_return(yield s.promise);
 };
 
 provide("keymap");
index dcdf71b..0bdf363 100644 (file)
@@ -33,29 +33,26 @@ minibuffer.prototype.read_yes_or_no = function () {
     yield co_return(result == "yes");
 };
 
-function single_character_options_minibuffer_state (minibuffer, continuation) {
+function single_character_options_minibuffer_state (minibuffer) {
     keywords(arguments);
     minibuffer_input_state.call(this, minibuffer, single_character_options_minibuffer_keymap, arguments.$prompt);
-    this.continuation = continuation;
+    this.deferred = Promise.defer();
+    this.promise = make_simple_cancelable(this.deferred);
     this.options = arguments.$options;
 }
 single_character_options_minibuffer_state.prototype = {
     constructor: single_character_options_minibuffer_state,
     __proto__: minibuffer_input_state.prototype,
     destroy: function () {
-        if (this.continuation)
-            this.continuation.throw(abort());
+        this.promise.cancel();
         minibuffer_input_state.prototype.destroy.call(this);
     }
 };
 function single_character_options_enter_character (window, s, event) {
     var ch = String.fromCharCode(event.charCode);
     if (s.options.indexOf(ch) != -1) {
-        var c = s.continuation;
-        delete s.continuation;
+        s.deferred.resolve(ch);
         window.minibuffer.pop_state();
-        if (c)
-            c(ch);
         return;
     }
     var str = "Please answer " + or_string(s.options) + ".";
@@ -72,10 +69,9 @@ interactive("single-character-options-enter-character", null,
 
 minibuffer.prototype.read_single_character_option = function () {
     keywords(arguments);
-    var s = new single_character_options_minibuffer_state(this, (yield CONTINUATION), forward_keywords(arguments));
+    var s = new single_character_options_minibuffer_state(this, forward_keywords(arguments));
     this.push_state(s);
-    var result = yield SUSPEND;
-    yield co_return(result);
+    yield co_return(yield s.promise);
 };
 
 provide("minibuffer-read-options");
index 000bbed..e79f1d7 100644 (file)
@@ -99,13 +99,16 @@ define_keywords("$keymap", "$history", "$validator",
                 "$auto_complete_delay", "$enable_icons",
                 "$space_completes");
 /* FIXME: support completing in another thread */
-function text_entry_minibuffer_state (minibuffer, continuation) {
+function text_entry_minibuffer_state (minibuffer) {
     keywords(arguments, $keymap = minibuffer_keymap,
              $enable_icons = false);
 
     basic_minibuffer_state.call(this, minibuffer, forward_keywords(arguments));
 
-    this.continuation = continuation;
+    let deferred = Promise.defer();
+    this.deferred = deferred;
+    this.promise = make_simple_cancelable(deferred);
+
     if (arguments.$history) {
         this.history = minibuffer_history_data[arguments.$history] =
             minibuffer_history_data[arguments.$history] || [];
@@ -206,7 +209,7 @@ text_entry_minibuffer_state.prototype = {
             this.completions.destroy();
         delete this.completions;
         if (this.completions_cont)
-            this.completions_cont.throw(abort());
+            this.completions_cont.cancel();
         delete this.completions_cont;
 
         var el = this.completions_display_element;
@@ -214,8 +217,8 @@ text_entry_minibuffer_state.prototype = {
             el.parentNode.removeChild(el);
             this.completions_display_element = null;
         }
-        if (this.continuation)
-            this.continuation.throw(abort());
+        if (this.promise)
+            this.promise.cancel();
         basic_minibuffer_state.prototype.destroy.call(this);
     },
 
@@ -270,7 +273,7 @@ text_entry_minibuffer_state.prototype = {
         }
         var m = this.minibuffer;
         if (this.completions_cont) {
-            this.completions_cont.throw(abort());
+            this.completions_cont.cancel();
             this.completions_cont = null;
         }
         let c = this.completer(m._input_text, m._selection_start,
@@ -278,7 +281,7 @@ text_entry_minibuffer_state.prototype = {
         if (is_coroutine(c)) {
             var s = this;
             var already_done = false;
-            this.completions_cont = co_call(function () {
+            this.completions_cont = spawn(function () {
                 try {
                     var x = yield c;
                 } catch (e) {
@@ -447,15 +450,16 @@ function exit_minibuffer (window) {
         if (s.history.length > minibuffer_history_max_items)
             s.history.splice(0, s.history.length - minibuffer_history_max_items);
     }
-    var cont = s.continuation;
-    delete s.continuation;
-    m.pop_state();
-    if (cont) {
-        if (s.match_required)
-            cont(match);
-        else
-            cont(val);
+    var deferred = s.deferred;
+    if (deferred) {
+        if (s.match_required) {
+            deferred.resolve(match);
+        }
+        else {
+            deferred.resolve(val);
+        }
     }
+    m.pop_state();
 }
 interactive("exit-minibuffer", null,
     function (I) { exit_minibuffer(I.window); });
@@ -501,10 +505,9 @@ interactive("minibuffer-history-previous", null,
 
 // Define the asynchronous minibuffer.read function
 minibuffer.prototype.read = function () {
-    var s = new text_entry_minibuffer_state(this, (yield CONTINUATION), forward_keywords(arguments));
+    var s = new text_entry_minibuffer_state(this, forward_keywords(arguments));
     this.push_state(s);
-    var result = yield SUSPEND;
-    yield co_return(result);
+    yield co_return(yield s.promise);
 };
 
 minibuffer.prototype.read_command = function () {
index 617232a..d20e764 100644 (file)
@@ -410,17 +410,11 @@ minibuffer.prototype.show_wait_message = function (initial_message, cleanup_func
 };
 
 minibuffer.prototype.wait_for = function (message, coroutine) {
-    var cc = yield CONTINUATION;
-    var done = false;
-    var s = this.show_wait_message(message, function () { if (!done) cc.throw(abort()); });
-    var result;
-    try {
-        result = yield coroutine;
-    } finally {
-        done = true;
-        this.remove_state(s);
-    }
-    yield co_return(result);
+    let promise = spawn(coroutine);
+    var s = this.show_wait_message(message, promise.cancel);
+    let cleanup = s.minibuffer.remove_state.bind(s);
+    promise.then(cleanup, cleanup);
+    return promise;
 };
 
 
index f9e1006..452b668 100644 (file)
@@ -238,12 +238,12 @@ function download_as_temporary (lspec) {
     if (uri.scheme == "file") {
         let file = uri.QueryInterface(Ci.nsIFileURL).file;
 
-        yield co_return([file, false /* not temporary */]);
+        return Promise.resolve([file, false /* not temporary */]);
     }
 
     var file = get_temporary_file(suggest_file_name(lspec));
 
-    var cc = yield CONTINUATION;
+    var deferred = Promise.defer();
 
     function handle_state_change (info) {
         var state = info.state;
@@ -255,10 +255,10 @@ function download_as_temporary (lspec) {
                 // Delete the temporary file
                 file.remove(false /*non-recursive*/);
             } catch (e) {}
-            cc.throw(download_failed_error());
+            deferred.reject(download_failed_error());
             break;
         case DOWNLOAD_FINISHED:
-            cc([file, true /* temporary */]);
+            deferred.resolve([file, true /* temporary */]);
             break;
         }
     }
@@ -277,8 +277,8 @@ function download_as_temporary (lspec) {
                  add_hook.call(info, "download_state_change_hook", handle_state_change);
              });
 
-    var result = yield SUSPEND;
-    yield co_return(result);
+    // FIXME: add cancelation support: tricky to handle cancelation coming before download starts
+    return deferred.promise;
 }
 
 provide("save");
index fa1c882..d0fddd3 100644 (file)
                 break;
             }
         }
-        co_call(_session_auto_save_auto_load(user_gave_urls));
+        spawn(_session_auto_save_auto_load(user_gave_urls));
     };
 
     let _session_auto_save_mode_enable = function () {
index 5e94175..b43aeb3 100644 (file)
@@ -117,8 +117,10 @@ var spawn_process_helper_setup_timeout = 2000;
  *        process.
  */
 function spawn_process (program_name, args, working_dir,
-                        success_callback, failure_callback, fds,
-                        fd_wait_timeout) {
+                        fds, fd_wait_timeout) {
+
+    let deferred = Promise.defer();
+
     var spawn_process_helper_program = find_spawn_helper();
     if (spawn_process_helper_program == null)
         throw new Error("Error spawning process: conkeror-spawn-helper not found");
@@ -202,9 +204,8 @@ function spawn_process (program_name, args, working_dir,
 
     function fail (e) {
         if (!terminate_pending) {
+            deferred.reject(e);
             terminate();
-            if (failure_callback)
-                failure_callback(e);
         }
     }
 
@@ -277,12 +278,18 @@ function spawn_process (program_name, args, working_dir,
         return exit_status;
     }
 
+    function canceler (e) {
+        if (!terminate_pending) {
+            deferred.reject(e);
+            terminate();
+        }
+    }
+
     function finished () {
         // Only call success_callback if terminate was not already called
         if (!terminate_pending) {
+            deferred.resolve(exit_status);
             terminate();
-            if (success_callback)
-                success_callback(exit_status);
         }
     }
 
@@ -477,7 +484,7 @@ function spawn_process (program_name, args, working_dir,
             });
 
         spawn_process_internal(spawn_process_helper_program, [key_file.path, server.port], false);
-        return terminate;
+        return make_cancelable(deferred.promise, canceler);
     } catch (e) {
         terminate();
 
@@ -504,10 +511,7 @@ function spawn_process_blind (program_name, args) {
     if (cwd == null && fds == null && args[0] == null)
         spawn_process_internal(program_name, args.slice(1));
     else {
-        spawn_process(program_name, args, cwd,
-                      null /* success callback */,
-                      null /* failure callback */,
-                      fds);
+        spawn_process(program_name, args, cwd, fds);
     }
 }
 
@@ -515,11 +519,8 @@ function spawn_process_blind (program_name, args) {
 //  Keyword arguments: $cwd, $fds
 function spawn_and_wait_for_process (program_name, args) {
     keywords(arguments, $cwd = null, $fds = null);
-    var cc = yield CONTINUATION;
-    spawn_process(program_name, args, arguments.$cwd,
-                  cc, cc.throw,
-                  arguments.$fds);
-    var result = yield SUSPEND;
+    let result = yield spawn_process(program_name, args, arguments.$cwd,
+                                     arguments.$fds);
     yield co_return(result);
 }
 
index cef6090..c5e6940 100644 (file)
@@ -196,14 +196,7 @@ function generate_QI () {
     return new Function("iid", fstr);
 }
 
-
-function abort (str) {
-    var e = new Error(str);
-    e.__proto__ = abort.prototype;
-    return e;
-}
-abort.prototype.__proto__ = Error.prototype;
-
+var abort = task_canceled;
 
 function get_temporary_file (name) {
     if (name == null)
@@ -420,7 +413,7 @@ var xml_http_request_load_listener = {
 
 
 /**
- * Coroutine interface for sending an HTTP request and waiting for the
+ * Promise interface for sending an HTTP request and waiting for the
  * response. (This includes so-called "AJAX" requests.)
  *
  * @param lspec (required) a load_spec object or URI string (see load-spec.js)
@@ -443,16 +436,11 @@ var xml_http_request_load_listener = {
  *                 a two-element array) specifying additional headers to add to
  *                 the request.
  *
- * @returns After the request completes (either successfully or with an error),
- *          the nsIXMLHttpRequest object is returned.  Its responseText (for any
- *          arbitrary document) or responseXML (if the response type is an XML
- *          content type) properties can be accessed to examine the response
- *          document.
- *
- * If an exception is thrown to the continutation (which can be obtained by the
- * caller by calling yield CONTINUATION prior to calling this function) while the
- * request is in progress (i.e. before this coroutine returns), the request will
- * be aborted, and the exception will be propagated to the caller.
+ * @returns Promise that resolves to nsIXMLHttpRequest after the request
+ *          completes (either successfully or with an error).  Its responseText
+ *          (for any arbitrary document) or responseXML (if the response type is
+ *          an XML content type) properties can be accessed to examine the
+ *          response document.
  *
  **/
 define_keywords("$user", "$password", "$override_mime_type", "$headers");
@@ -464,14 +452,12 @@ function send_http_request (lspec) {
     if (! (lspec instanceof load_spec))
         lspec = load_spec(lspec);
     var req = xml_http_request();
-    var cc = yield CONTINUATION;
-    var aborting = false;
+
+    let deferred = Promise.defer();
     req.onreadystatechange = function send_http_request__onreadystatechange () {
         if (req.readyState != 4)
             return;
-        if (aborting)
-            return;
-        cc();
+        deferred.resolve(req);
     };
 
     if (arguments.$override_mime_type)
@@ -494,16 +480,7 @@ function send_http_request (lspec) {
     } else
         req.send(null);
 
-    try {
-        yield SUSPEND;
-    } catch (e) {
-        aborting = true;
-        req.abort();
-        throw e;
-    }
-
-    // Let the caller access the status and reponse data
-    yield co_return(req);
+    return make_simple_cancelable(deferred);
 }
 
 
index 77fcbcb..e2132c3 100644 (file)
@@ -92,7 +92,7 @@ function window_install_close_intercept (window) {
                 close.call(window);
             }
         }
-        co_call(attempt_close());
+        spawn(attempt_close());
     };
 }