From: Jeremy Maitin-Shepard Date: Wed, 15 Jan 2014 21:38:22 +0000 (-0800) Subject: Replace uses of co_call/continuation API with uses of spawn/Promise API X-Git-Tag: debian-1.0--pre-1+git160130-1~101 X-Git-Url: https://repo.or.cz/w/conkeror.git/commitdiff_plain/19013a402a8ec5bdc9f215295bf5c8a24fb304e3 Replace uses of co_call/continuation API with uses of spawn/Promise API --- diff --git a/modules/cache.js b/modules/cache.js index 4049691..cd0e991 100644 --- a/modules/cache.js +++ b/modules/cache.js @@ -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) { diff --git a/modules/content-handler.js b/modules/content-handler.js index f1a938b..766024a 100644 --- a/modules/content-handler.js +++ b/modules/content-handler.js @@ -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); } diff --git a/modules/help.js b/modules/help.js index d704716..69675fb 100644 --- a/modules/help.js +++ b/modules/help.js @@ -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) { diff --git a/modules/hints.js b/modules/hints.js index 2d1d232..71abce4 100644 --- a/modules/hints.js +++ b/modules/hints.js @@ -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"); diff --git a/modules/keymap.js b/modules/keymap.js index d7f1d4c..3aad138 100644 --- a/modules/keymap.js +++ b/modules/keymap.js @@ -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"); diff --git a/modules/minibuffer-read-option.js b/modules/minibuffer-read-option.js index dcdf71b..0bdf363 100644 --- a/modules/minibuffer-read-option.js +++ b/modules/minibuffer-read-option.js @@ -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"); diff --git a/modules/minibuffer-read.js b/modules/minibuffer-read.js index 000bbed..e79f1d7 100644 --- a/modules/minibuffer-read.js +++ b/modules/minibuffer-read.js @@ -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 () { diff --git a/modules/minibuffer.js b/modules/minibuffer.js index 617232a..d20e764 100644 --- a/modules/minibuffer.js +++ b/modules/minibuffer.js @@ -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; }; diff --git a/modules/save.js b/modules/save.js index f9e1006..452b668 100644 --- a/modules/save.js +++ b/modules/save.js @@ -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"); diff --git a/modules/session.js b/modules/session.js index fa1c882..d0fddd3 100644 --- a/modules/session.js +++ b/modules/session.js @@ -474,7 +474,7 @@ 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 () { diff --git a/modules/spawn-process.js b/modules/spawn-process.js index 5e94175..b43aeb3 100644 --- a/modules/spawn-process.js +++ b/modules/spawn-process.js @@ -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); } diff --git a/modules/utils.js b/modules/utils.js index cef6090..c5e6940 100644 --- a/modules/utils.js +++ b/modules/utils.js @@ -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); } diff --git a/modules/window.js b/modules/window.js index 77fcbcb..e2132c3 100644 --- a/modules/window.js +++ b/modules/window.js @@ -92,7 +92,7 @@ function window_install_close_intercept (window) { close.call(window); } } - co_call(attempt_close()); + spawn(attempt_close()); }; }