From bce7e87bb8e66435da0f649aa6ec1c99ea304535 Mon Sep 17 00:00:00 2001 From: Witold Filipczyk Date: Wed, 7 Sep 2022 20:41:46 +0200 Subject: [PATCH] [ecmascript] Handle more than 1 timeout at the same time. --- src/document/document.cpp | 23 +++- src/document/document.h | 4 +- src/ecmascript/ecmascript.cpp | 145 +++++++++++++++---------- src/ecmascript/ecmascript.h | 20 +++- src/ecmascript/mujs/window.cpp | 23 ++-- src/ecmascript/quickjs/window.cpp | 14 ++- src/ecmascript/spidermonkey/window.cpp | 15 ++- 7 files changed, 170 insertions(+), 74 deletions(-) diff --git a/src/document/document.cpp b/src/document/document.cpp index a932dfe73..9ff29d312 100644 --- a/src/document/document.cpp +++ b/src/document/document.cpp @@ -142,6 +142,7 @@ init_document(struct cache_entry *cached, struct document_options *options) #ifdef CONFIG_ECMASCRIPT init_list(document->onload_snippets); + init_list(document->timeouts); #endif #ifdef CONFIG_COMBINE @@ -351,7 +352,17 @@ done_document(struct document *document) #if defined(CONFIG_ECMASCRIPT_SMJS) || defined(CONFIG_QUICKJS) || defined(CONFIG_MUJS) free_string_list(&document->onload_snippets); free_uri_list(&document->ecmascript_imports); - kill_timer(&document->timeout); + + { + struct ecmascript_timeout *t; + + foreach(t, document->timeouts) { + kill_timer(&t->tid); + done_string(&t->code); + } + } + free_list(document->timeouts); + mem_free_if(document->text); free_document(document->dom); #endif @@ -376,7 +387,15 @@ release_document(struct document *document) if (document->refresh) kill_document_refresh(document->refresh); #ifdef CONFIG_ECMASCRIPT - kill_timer(&document->timeout); + { + struct ecmascript_timeout *t; + + foreach(t, document->timeouts) { + kill_timer(&t->tid); + done_string(&t->code); + } + } + free_list(document->timeouts); #endif object_unlock(document); move_to_top_of_list(format_cache, document); diff --git a/src/document/document.h b/src/document/document.h index 542c1d5a0..392355f5a 100644 --- a/src/document/document.h +++ b/src/document/document.h @@ -16,6 +16,7 @@ extern "C" { struct cache_entry; struct document_refresh; +struct ecmascript_timeout; struct el_form_control; struct frame_desc; struct frameset_desc; @@ -67,7 +68,6 @@ struct tag { char name[1]; /* must be last of struct. --Zas */ }; - enum link_type { LINK_HYPERTEXT, LINK_MAP, @@ -209,7 +209,7 @@ struct document { * unneeded. */ struct uri_list ecmascript_imports; /** used by setTimeout */ - timer_id_T timeout; + LIST_OF(struct ecmascript_timeout) timeouts; int ecmascript_counter; void *dom; char *text; diff --git a/src/ecmascript/ecmascript.cpp b/src/ecmascript/ecmascript.cpp index f79e279de..b33df88bb 100644 --- a/src/ecmascript/ecmascript.cpp +++ b/src/ecmascript/ecmascript.cpp @@ -275,8 +275,15 @@ ecmascript_put_interpreter(struct ecmascript_interpreter *interpreter) free_string_list(&interpreter->onload_snippets); done_string(&interpreter->code); /* Is it superfluous? */ - if (interpreter->vs->doc_view) - kill_timer(&interpreter->vs->doc_view->document->timeout); + if (interpreter->vs->doc_view) { + struct ecmascript_timeout *t; + + foreach (t, interpreter->vs->doc_view->document->timeouts) { + kill_timer(&t->tid); + done_string(&t->code); + } + free_list(interpreter->vs->doc_view->document->timeouts); + } interpreter->vs->ecmascript = NULL; interpreter->vs->ecmascript_fragile = 1; mem_free(interpreter); @@ -573,17 +580,22 @@ ecmascript_set_action(char **action, char *string) * As explained in @install_timer, this function must erase the * expired timer ID from all variables. */ static void -ecmascript_timeout_handler(void *i) +ecmascript_timeout_handler(void *val) { - struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)i; + struct ecmascript_timeout *t = (struct ecmascript_timeout *)val; + struct ecmascript_interpreter *interpreter = t->interpreter; assertm(interpreter->vs->doc_view != NULL, "setTimeout: vs with no document (e_f %d)", interpreter->vs->ecmascript_fragile); - interpreter->vs->doc_view->document->timeout = TIMER_ID_UNDEF; + t->tid = TIMER_ID_UNDEF; /* The expired timer ID has now been erased. */ + ecmascript_eval(interpreter, &t->code, NULL); + + del_from_list(t); + done_string(&t->code); + mem_free(t); - ecmascript_eval(interpreter, &interpreter->code, NULL); check_for_rerender(interpreter, "handler"); } @@ -592,16 +604,25 @@ ecmascript_timeout_handler(void *i) * As explained in @install_timer, this function must erase the * expired timer ID from all variables. */ static void -ecmascript_timeout_handler2(void *i) +ecmascript_timeout_handler2(void *val) { - struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)i; + struct ecmascript_timeout *t = (struct ecmascript_timeout *)val; + struct ecmascript_interpreter *interpreter = t->interpreter; assertm(interpreter->vs->doc_view != NULL, "setTimeout: vs with no document (e_f %d)", interpreter->vs->ecmascript_fragile); - interpreter->vs->doc_view->document->timeout = TIMER_ID_UNDEF; + t->tid = TIMER_ID_UNDEF; /* The expired timer ID has now been erased. */ - ecmascript_call_function(interpreter, interpreter->fun, NULL); + ecmascript_call_function(interpreter, t->fun, NULL); + + del_from_list(t); + done_string(&t->code); +#ifdef CONFIG_MUJS +// js_unref(t->ctx, t->fun); +#endif + mem_free(t); + check_for_rerender(interpreter, "handler2"); } #endif @@ -611,21 +632,25 @@ ecmascript_set_timeout(struct ecmascript_interpreter *interpreter, char *code, i { assert(interpreter && interpreter->vs->doc_view->document); if (!code) return nullptr; - if (interpreter->code.source) { - done_string(&interpreter->code); - } - if (!init_string(&interpreter->code)) { + struct ecmascript_timeout *t = (struct ecmascript_timeout *)calloc(1, sizeof(*t)); + + if (!t) { mem_free(code); return nullptr; } - add_to_string(&interpreter->code, code); - mem_free(code); - if (found_in_map_timer(interpreter->vs->doc_view->document->timeout)) { - kill_timer(&interpreter->vs->doc_view->document->timeout); + if (!init_string(&t->code)) { + mem_free(t); + mem_free(code); + return nullptr; } - install_timer(&interpreter->vs->doc_view->document->timeout, timeout, ecmascript_timeout_handler, interpreter); + add_to_string(&t->code, code); + mem_free(code); - return interpreter->vs->doc_view->document->timeout; + t->interpreter = interpreter; + add_to_list(interpreter->vs->doc_view->document->timeouts, t); + install_timer(&t->tid, timeout, ecmascript_timeout_handler, t); + + return t->tid; } #ifdef CONFIG_ECMASCRIPT_SMJS @@ -633,21 +658,23 @@ timer_id_T ecmascript_set_timeout2(struct ecmascript_interpreter *interpreter, JS::HandleValue f, int timeout) { assert(interpreter && interpreter->vs->doc_view->document); - if (interpreter->code.source) { - done_string(&interpreter->code); - } - if (!init_string(&interpreter->code)) { - return TIMER_ID_UNDEF; + struct ecmascript_timeout *t = (struct ecmascript_timeout *)calloc(1, sizeof(*t)); + + if (!t) { + return nullptr; } - if (found_in_map_timer(interpreter->vs->doc_view->document->timeout)) { - kill_timer(&interpreter->vs->doc_view->document->timeout); + if (!init_string(&t->code)) { + mem_free(t); + return nullptr; } + t->interpreter = interpreter; JS::RootedValue fun((JSContext *)interpreter->backend_data, f); - interpreter->fun = fun; - install_timer(&interpreter->vs->doc_view->document->timeout, timeout, ecmascript_timeout_handler2, interpreter); + t->fun = fun; + add_to_list(interpreter->vs->doc_view->document->timeouts, t); + install_timer(&t->tid, timeout, ecmascript_timeout_handler2, t); - return interpreter->vs->doc_view->document->timeout; + return t->tid; } #endif @@ -656,40 +683,48 @@ timer_id_T ecmascript_set_timeout2q(struct ecmascript_interpreter *interpreter, JSValueConst fun, int timeout) { assert(interpreter && interpreter->vs->doc_view->document); - if (interpreter->code.source) { - done_string(&interpreter->code); - } - if (!init_string(&interpreter->code)) { - return TIMER_ID_UNDEF; - } - if (found_in_map_timer(interpreter->vs->doc_view->document->timeout)) { - kill_timer(&interpreter->vs->doc_view->document->timeout); - } - interpreter->fun = fun; - install_timer(&interpreter->vs->doc_view->document->timeout, timeout, ecmascript_timeout_handler2, interpreter); + struct ecmascript_timeout *t = (struct ecmascript_timeout *)calloc(1, sizeof(*t)); - return interpreter->vs->doc_view->document->timeout; + if (!t) { + return nullptr; + } + if (!init_string(&t->code)) { + mem_free(t); + return nullptr; + } + t->interpreter = interpreter; + t->fun = fun; + add_to_list(interpreter->vs->doc_view->document->timeouts, t); + install_timer(&t->tid, timeout, ecmascript_timeout_handler2, t); + + return t->tid; } #endif #ifdef CONFIG_MUJS timer_id_T -ecmascript_set_timeout2m(struct ecmascript_interpreter *interpreter, const char *handle, int timeout) +ecmascript_set_timeout2m(js_State *J, const char *handle, int timeout) { + struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)js_getcontext(J); assert(interpreter && interpreter->vs->doc_view->document); - if (interpreter->code.source) { - done_string(&interpreter->code); - } - if (!init_string(&interpreter->code)) { - return TIMER_ID_UNDEF; - } - if (found_in_map_timer(interpreter->vs->doc_view->document->timeout)) { - kill_timer(&interpreter->vs->doc_view->document->timeout); - } - interpreter->fun = handle; - install_timer(&interpreter->vs->doc_view->document->timeout, timeout, ecmascript_timeout_handler2, interpreter); - return interpreter->vs->doc_view->document->timeout; + struct ecmascript_timeout *t = (struct ecmascript_timeout *)calloc(1, sizeof(*t)); + + if (!t) { + return nullptr; + } + if (!init_string(&t->code)) { + mem_free(t); + return nullptr; + } + t->interpreter = interpreter; + t->ctx = J; + t->fun = handle; + + add_to_list(interpreter->vs->doc_view->document->timeouts, t); + install_timer(&t->tid, timeout, ecmascript_timeout_handler2, t); + + return t->tid; } #endif diff --git a/src/ecmascript/ecmascript.h b/src/ecmascript/ecmascript.h index 3a8e61963..dcb768324 100644 --- a/src/ecmascript/ecmascript.h +++ b/src/ecmascript/ecmascript.h @@ -99,6 +99,24 @@ struct ecmascript_interpreter { bool changed; }; +struct ecmascript_timeout { + LIST_HEAD(struct ecmascript_timeout); + struct string code; +#ifdef CONFIG_QUICKJS + JSValueConst fun; +#endif +#ifdef CONFIG_ECMASCRIPT_SMJS + JS::RootedValue fun; +#endif +#ifdef CONFIG_MUJS + js_State *ctx; + const char *fun; +#endif + struct ecmascript_interpreter *interpreter; + timer_id_T tid; +}; + + struct delayed_goto { /* It might look more convenient to pass doc_view around but it could * disappear during wild dances inside of frames or so. */ @@ -154,7 +172,7 @@ timer_id_T ecmascript_set_timeout2q(struct ecmascript_interpreter *interpreter, #endif #ifdef CONFIG_MUJS -timer_id_T ecmascript_set_timeout2m(struct ecmascript_interpreter *interpreter, const char *handle, int timeout); +timer_id_T ecmascript_set_timeout2m(js_State *J, const char *handle, int timeout); #endif int get_ecmascript_enable(struct ecmascript_interpreter *interpreter); diff --git a/src/ecmascript/mujs/window.cpp b/src/ecmascript/mujs/window.cpp index abd120374..2a247df1b 100644 --- a/src/ecmascript/mujs/window.cpp +++ b/src/ecmascript/mujs/window.cpp @@ -193,8 +193,18 @@ mjs_window_clearTimeout(js_State *J) int64_t number = atoll(text); timer_id_T id = reinterpret_cast(number); - if (found_in_map_timer(id) && (id == interpreter->vs->doc_view->document->timeout)) { - kill_timer(&interpreter->vs->doc_view->document->timeout); + if (found_in_map_timer(id)) { + struct ecmascript_timeout *t; + + foreach (t, interpreter->vs->doc_view->document->timeouts) { + if (id == t->tid) { + kill_timer(&t->tid); + done_string(&t->code); + del_from_list(t); + mem_free(t); + break; + } + } } js_pushundefined(J); } @@ -330,8 +340,6 @@ end: js_pushboolean(J, ret); } -const char *handle = NULL; - static void mjs_window_setTimeout(js_State *J) { @@ -363,12 +371,9 @@ mjs_window_setTimeout(js_State *J) return; } } else { - if (handle) { - js_unref(J, handle); - } js_copy(J, 1); - handle = js_ref(J); - timer_id_T id = ecmascript_set_timeout2m(interpreter, handle, timeout); + const char *handle = js_ref(J); + timer_id_T id = ecmascript_set_timeout2m(J, handle, timeout); char res[32]; snprintf(res, 31, "%ld", (int64_t)id); js_pushstring(J, res); diff --git a/src/ecmascript/quickjs/window.cpp b/src/ecmascript/quickjs/window.cpp index 38303b388..a998ec260 100644 --- a/src/ecmascript/quickjs/window.cpp +++ b/src/ecmascript/quickjs/window.cpp @@ -246,8 +246,18 @@ js_window_clearTimeout(JSContext *ctx, JSValueConst this_val, int argc, JSValueC timer_id_T id = reinterpret_cast(number); - if (found_in_map_timer(id) && (id == interpreter->vs->doc_view->document->timeout)) { - kill_timer(&interpreter->vs->doc_view->document->timeout); + if (found_in_map_timer(id)) { + struct ecmascript_timeout *t; + + foreach (t, interpreter->vs->doc_view->document->timeouts) { + if (id == t->tid) { + kill_timer(&t->tid); + done_string(&t->code); + del_from_list(t); + mem_free(t); + break; + } + } } return JS_UNDEFINED; diff --git a/src/ecmascript/spidermonkey/window.cpp b/src/ecmascript/spidermonkey/window.cpp index 53203b6bf..a6578dc37 100644 --- a/src/ecmascript/spidermonkey/window.cpp +++ b/src/ecmascript/spidermonkey/window.cpp @@ -406,13 +406,22 @@ window_clearTimeout(JSContext *ctx, unsigned int argc, JS::Value *rval) int64_t number = JS::ToBigInt64(bi); timer_id_T id = reinterpret_cast(number); - if (found_in_map_timer(id) && (id == interpreter->vs->doc_view->document->timeout)) { - kill_timer(&id); + if (found_in_map_timer(id)) { + struct ecmascript_timeout *t; + + foreach (t, interpreter->vs->doc_view->document->timeouts) { + if (id == t->tid) { + kill_timer(&t->tid); + done_string(&t->code); + del_from_list(t); + mem_free(t); + break; + } + } } return true; } - #if 0 static bool window_get_property_closed(JSContext *ctx, JS::HandleObject hobj, JS::HandleId hid, JS::MutableHandleValue hvp)