diff --git a/config2.h.in b/config2.h.in index f498fa51..61406678 100644 --- a/config2.h.in +++ b/config2.h.in @@ -196,6 +196,9 @@ /* Define if you want: Pod2HTML support */ #mesondefine CONFIG_POD2HTML +/* Define if you want: QuickJS support */ +#mesondefine CONFIG_QUICKJS + /* Define if you want: Browser scripting support */ #mesondefine CONFIG_SCRIPTING diff --git a/meson.build b/meson.build index 439caf9e..5d96dec6 100644 --- a/meson.build +++ b/meson.build @@ -91,7 +91,7 @@ conf_data.set('CONFIG_X', get_option('x')) conf_data.set('CONFIG_XML', get_option('xml')) conf_data.set('CONFIG_QUICKJS', get_option('quickjs')) -if conf_data.get('CONFIG_SCRIPTING_SPIDERMONKEY') or conf_data.get('CONFIG_ECMASCRIPT_SMJS') +if conf_data.get('CONFIG_SCRIPTING_SPIDERMONKEY') or conf_data.get('CONFIG_ECMASCRIPT_SMJS') or conf_data.get('CONFIG_QUICKJS') conf_data.set('CONFIG_ECMASCRIPT', true) else conf_data.set('CONFIG_ECMASCRIPT', false) @@ -252,9 +252,12 @@ if conf_data.get('CONFIG_BZIP2') deps += bz2deps endif -if conf_data.get('CONFIG_ECMASCRIPT') +if conf_data.get('CONFIG_ECMASCRIPT_SMJS') mozjsdeps = dependency('mozjs-78') deps += mozjsdeps +endif + +if conf_data.get('CONFIG_ECMASCRIPT') sqlite3deps = dependency('sqlite3') deps += sqlite3deps conf_data.set('CONFIG_XML', true) diff --git a/src/document/document.c b/src/document/document.c index 2813612f..15f606ec 100644 --- a/src/document/document.c +++ b/src/document/document.c @@ -62,6 +62,9 @@ #include "document/refresh.h" #ifdef CONFIG_ECMASCRIPT +#include "ecmascript/ecmascript.h" +#endif +#ifdef CONFIG_ECMASCRIPT_SMJS #include "ecmascript/spidermonkey.h" #endif diff --git a/src/document/renderer.c b/src/document/renderer.c index ff366478..97012f1f 100644 --- a/src/document/renderer.c +++ b/src/document/renderer.c @@ -29,6 +29,8 @@ #include "document/view.h" #ifdef CONFIG_ECMASCRIPT #include "ecmascript/ecmascript.h" +#endif +#ifdef CONFIG_ECMASCRIPT_SMJS #include "ecmascript/spidermonkey/document.h" #endif #include "encoding/encoding.h" @@ -50,7 +52,7 @@ #include "viewer/text/vs.h" -#ifdef CONFIG_ECMASCRIPT_SMJS +#if defined(CONFIG_ECMASCRIPT_SMJS) || defined(CONFIG_QUICKJS) /** @todo XXX: This function is de facto obsolete, since we do not need to copy * snippets around anymore (we process them in one go after the document is * loaded; gradual processing was practically impossible because the snippets @@ -347,7 +349,7 @@ render_document(struct view_state *vs, struct document_view *doc_view, vs->doc_view->used = 0; /* A bit risky, but... */ vs->doc_view->vs = NULL; vs->doc_view = NULL; -#ifdef CONFIG_ECMASCRIPT_SMJS +#if defined(CONFIG_ECMASCRIPT_SMJS) || defined(CONFIG_QUICKJS) vs->ecmascript_fragile = 1; /* And is this good? ;-) */ #endif } @@ -401,7 +403,7 @@ render_document(struct view_state *vs, struct document_view *doc_view, document->css_magic = get_document_css_magic(document); #endif } -#ifdef CONFIG_ECMASCRIPT_SMJS +#if defined(CONFIG_ECMASCRIPT_SMJS) || defined(CONFIG_QUICKJS) if (!vs->ecmascript_fragile) assert(vs->ecmascript); if (!options->dump && !options->gradual_rerendering) { diff --git a/src/document/xml/renderer2.c b/src/document/xml/renderer2.c index 358301fa..02b09c3a 100644 --- a/src/document/xml/renderer2.c +++ b/src/document/xml/renderer2.c @@ -23,7 +23,9 @@ #include "document/plain/renderer.h" #include "document/renderer.h" #include "document/xml/renderer2.h" -#include "ecmascript/spidermonkey/document.h" +#ifdef CONFIG_ECMASCRIPT +#include "ecmascript/ecmascript.h" +#endif #include "globhist/globhist.h" #include "intl/charsets.h" #include "protocol/protocol.h" diff --git a/src/ecmascript/ecmascript.c b/src/ecmascript/ecmascript.c index bc6c052e..9b3ba59c 100644 --- a/src/ecmascript/ecmascript.c +++ b/src/ecmascript/ecmascript.c @@ -18,7 +18,11 @@ #include "document/xml/renderer.h" #include "document/xml/renderer2.h" #include "ecmascript/ecmascript.h" +#ifdef CONFIG_QUICKJS +#include "ecmascript/quickjs.h" +#else #include "ecmascript/spidermonkey.h" +#endif #include "intl/libintl.h" #include "main/module.h" #include "main/select.h" @@ -37,6 +41,9 @@ #include "viewer/text/form.h" /* <-ecmascript_reset_state() */ #include "viewer/text/vs.h" +#include +#include +#include /* TODO: We should have some kind of ACL for the scripts - i.e. ability to * disallow the scripts to open new windows (or so that the windows are always @@ -222,7 +229,11 @@ ecmascript_get_interpreter(struct view_state *vs) init_list(interpreter->onload_snippets); /* The following backend call reads interpreter->vs. */ if ( +#ifdef CONFIG_QUICKJS + !quickjs_get_interpreter(interpreter) +#else !spidermonkey_get_interpreter(interpreter) +#endif ) { /* Undo what was done above. */ interpreter->vs->ecmascript_fragile = 1; @@ -243,8 +254,11 @@ ecmascript_put_interpreter(struct ecmascript_interpreter *interpreter) /* If the assertion fails, it is better to leak the * interpreter than to corrupt memory. */ if_assert_failed return; - +#ifdef CONFIG_QUICKJS + quickjs_put_interpreter(interpreter); +#else spidermonkey_put_interpreter(interpreter); +#endif free_string_list(&interpreter->onload_snippets); done_string(&interpreter->code); /* Is it superfluous? */ @@ -314,10 +328,15 @@ ecmascript_eval(struct ecmascript_interpreter *interpreter, return; assert(interpreter); interpreter->backend_nesting++; +#ifdef CONFIG_QUICKJS + quickjs_eval(interpreter, code, ret); +#else spidermonkey_eval(interpreter, code, ret); +#endif interpreter->backend_nesting--; } +#ifdef CONFIG_ECMASCRIPT_SMJS static void ecmascript_call_function(struct ecmascript_interpreter *interpreter, JS::HandleValue fun, struct string *ret) @@ -326,10 +345,14 @@ ecmascript_call_function(struct ecmascript_interpreter *interpreter, return; assert(interpreter); interpreter->backend_nesting++; +#ifdef CONFIG_QUICKJS + quickjs_call_function(interpreter, fun, ret); +#else spidermonkey_call_function(interpreter, fun, ret); +#endif interpreter->backend_nesting--; } - +#endif char * ecmascript_eval_stringback(struct ecmascript_interpreter *interpreter, @@ -341,7 +364,11 @@ ecmascript_eval_stringback(struct ecmascript_interpreter *interpreter, return NULL; assert(interpreter); interpreter->backend_nesting++; +#ifdef CONFIG_QUICKJS + result = quickjs_eval_stringback(interpreter, code); +#else result = spidermonkey_eval_stringback(interpreter, code); +#endif interpreter->backend_nesting--; check_for_rerender(interpreter, "stringback"); @@ -359,7 +386,11 @@ ecmascript_eval_boolback(struct ecmascript_interpreter *interpreter, return -1; assert(interpreter); interpreter->backend_nesting++; +#ifdef CONFIG_QUICKJS + result = quickjs_eval_boolback(interpreter, code); +#else result = spidermonkey_eval_boolback(interpreter, code); +#endif interpreter->backend_nesting--; check_for_rerender(interpreter, "boolback"); @@ -370,17 +401,26 @@ ecmascript_eval_boolback(struct ecmascript_interpreter *interpreter, void ecmascript_detach_form_view(struct form_view *fv) { +#ifdef CONFIG_QUICKJS +#else spidermonkey_detach_form_view(fv); +#endif } void ecmascript_detach_form_state(struct form_state *fs) { +#ifdef CONFIG_QUICKJS +#else spidermonkey_detach_form_state(fs); +#endif } void ecmascript_moved_form_state(struct form_state *fs) { +#ifdef CONFIG_QUICKJS +#else spidermonkey_moved_form_state(fs); +#endif } void @@ -513,6 +553,7 @@ ecmascript_timeout_handler(void *i) check_for_rerender(interpreter, "handler"); } +#ifdef CONFIG_ECMASCRIPT_SMJS /* Timer callback for @interpreter->vs->doc_view->document->timeout. * As explained in @install_timer, this function must erase the * expired timer ID from all variables. */ @@ -530,7 +571,7 @@ ecmascript_timeout_handler2(void *i) ecmascript_call_function(interpreter, interpreter->fun, NULL); check_for_rerender(interpreter, "handler2"); } - +#endif void ecmascript_set_timeout(struct ecmascript_interpreter *interpreter, char *code, int timeout) @@ -545,6 +586,7 @@ ecmascript_set_timeout(struct ecmascript_interpreter *interpreter, char *code, i install_timer(&interpreter->vs->doc_view->document->timeout, timeout, ecmascript_timeout_handler, interpreter); } +#ifdef CONFIG_ECMASCRIPT_SMJS void ecmascript_set_timeout2(struct ecmascript_interpreter *interpreter, JS::HandleValue f, int timeout) { @@ -556,6 +598,7 @@ ecmascript_set_timeout2(struct ecmascript_interpreter *interpreter, JS::HandleVa interpreter->fun = fun; install_timer(&interpreter->vs->doc_view->document->timeout, timeout, ecmascript_timeout_handler2, interpreter); } +#endif static void init_ecmascript_module(struct module *module) @@ -583,10 +626,51 @@ done_ecmascript_module(struct module *module) static struct module *ecmascript_modules[] = { #ifdef CONFIG_ECMASCRIPT_SMJS &spidermonkey_module, +#endif +#ifdef CONFIG_QUICKJS + &quickjs_module, #endif NULL, }; +void +free_document(void *doc) +{ + if (!doc) { + return; + } + xmlpp::Document *docu = doc; + delete docu; +} + +void * +document_parse(struct document *document) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + struct cache_entry *cached = document->cached; + struct fragment *f = get_cache_fragment(cached); + + if (!f || !f->length) { + return NULL; + } + + struct string str; + init_string(&str); + + add_bytes_to_string(&str, f->data, f->length); + + // Parse HTML and create a DOM tree + xmlDoc* doc = htmlReadDoc((xmlChar*)str.source, NULL, get_cp_mime_name(document->cp), + HTML_PARSE_RECOVER | HTML_PARSE_NOERROR | HTML_PARSE_NOWARNING); + // Encapsulate raw libxml document in a libxml++ wrapper + xmlpp::Document *docu = new xmlpp::Document(doc); + done_string(&str); + + return (void *)docu; +} + struct module ecmascript_module = struct_module( /* name: */ N_("ECMAScript"), /* options: */ ecmascript_options, diff --git a/src/ecmascript/ecmascript.h b/src/ecmascript/ecmascript.h index 823cdeba..4c4e80d5 100644 --- a/src/ecmascript/ecmascript.h +++ b/src/ecmascript/ecmascript.h @@ -9,9 +9,11 @@ #include "config.h" #endif -#ifdef CONFIG_ECMASCRIPT - +#ifdef CONFIG_ECMASCRIPT_SMJS #include +#endif + +#ifdef CONFIG_ECMASCRIPT #include "main/module.h" #include "util/time.h" @@ -70,7 +72,9 @@ struct ecmascript_interpreter { void *ac2; void *document_obj; void *location_obj; +#ifdef CONFIG_ECMASCRIPT_SMJS JS::RootedValue fun; +#endif bool changed; }; @@ -113,7 +117,9 @@ void ecmascript_set_action(char **action, char *string); void ecmascript_set_timeout(struct ecmascript_interpreter *interpreter, char *code, int timeout); +#ifdef CONFIG_ECMASCRIPT_SMJS void ecmascript_set_timeout2(struct ecmascript_interpreter *interpreter, JS::HandleValue f, int timeout); +#endif int get_ecmascript_enable(struct ecmascript_interpreter *interpreter); @@ -121,6 +127,9 @@ void check_for_rerender(struct ecmascript_interpreter *interpreter, const char* void toggle_ecmascript(struct session *ses); +void *document_parse(struct document *document); +void free_document(void *doc); + extern char *console_error_filename; extern char *console_log_filename; diff --git a/src/ecmascript/meson.build b/src/ecmascript/meson.build index d171694d..63d4d50c 100644 --- a/src/ecmascript/meson.build +++ b/src/ecmascript/meson.build @@ -16,3 +16,7 @@ if CONFIG_ANY_SPIDERMONKEY srcs += files('spidermonkey-shared.c', 'empty.cpp') endif +if conf_data.get('CONFIG_QUICKJS') + subdir('quickjs') + srcs += files('ecmascript.c', 'quickjs.c', 'empty.cpp') +endif diff --git a/src/ecmascript/quickjs.c b/src/ecmascript/quickjs.c new file mode 100644 index 00000000..1c6428a4 --- /dev/null +++ b/src/ecmascript/quickjs.c @@ -0,0 +1,531 @@ +/* The Quickjs ECMAScript backend. */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include + +#include "elinks.h" + +#include "bfu/dialog.h" +#include "cache/cache.h" +#include "config/options.h" +#include "cookies/cookies.h" +#include "dialogs/menu.h" +#include "dialogs/status.h" +#include "document/html/frames.h" +#include "document/xml/renderer.h" +#include "document/xml/renderer2.h" +#include "document/document.h" +#include "document/forms.h" +#include "document/renderer.h" +#include "document/view.h" +#include "ecmascript/ecmascript.h" +#include "ecmascript/quickjs.h" +#include "ecmascript/quickjs/window.h" +#include "intl/libintl.h" +#include "main/select.h" +#include "osdep/newwin.h" +#include "osdep/sysname.h" +#include "protocol/http/http.h" +#include "protocol/uri.h" +#include "session/history.h" +#include "session/location.h" +#include "session/session.h" +#include "session/task.h" +#include "terminal/tab.h" +#include "terminal/terminal.h" +#include "util/conv.h" +#include "util/string.h" +#include "viewer/text/draw.h" +#include "viewer/text/form.h" +#include "viewer/text/link.h" +#include "viewer/text/view.h" +#include "viewer/text/vs.h" + +#include + +/*** Global methods */ + + +#if 0 +/* TODO? Are there any which need to be implemented? */ + +static int js_module_init_ok; + +static void +error_reporter(JSContext *ctx, JSErrorReport *report) +{ + JS::Realm *comp = js::GetContextRealm(ctx); + + if (!comp) { + return; + } + struct ecmascript_interpreter *interpreter = JS::GetRealmPrivate(comp); + struct session *ses = interpreter->vs->doc_view->session; + struct terminal *term; + struct string msg; + char *ptr; + size_t size; + FILE *f; + + assert(interpreter && interpreter->vs && interpreter->vs->doc_view + && ses && ses->tab); + if_assert_failed goto reported; + + term = ses->tab->term; + +#ifdef CONFIG_LEDS + set_led_value(ses->status.ecmascript_led, 'J'); +#endif + + if (!get_opt_bool("ecmascript.error_reporting", ses)) + goto reported; + + f = open_memstream(&ptr, &size); + + if (f) { + JS::PrintError(ctx, f, report, true/*reportWarnings*/); + fclose(f); + + if (!init_string(&msg)) { + free(ptr); + } else { + add_to_string(&msg, + _("A script embedded in the current document raised the following:\n", term)); + add_bytes_to_string(&msg, ptr, size); + free(ptr); + + info_box(term, MSGBOX_FREE_TEXT, N_("JavaScript Error"), ALIGN_CENTER, msg.source); + } + } +reported: + JS_ClearPendingException(ctx); +} +#endif + +static void +quickjs_init(struct module *xxx) +{ + //js_module_init_ok = spidermonkey_runtime_addref(); +} + +static void +quickjs_done(struct module *xxx) +{ +// if (js_module_init_ok) +// spidermonkey_runtime_release(); +} + +void * +quickjs_get_interpreter(struct ecmascript_interpreter *interpreter) +{ + JSContext *ctx; +// JSObject *console_obj, *document_obj, /* *forms_obj,*/ *history_obj, *location_obj, +// *statusbar_obj, *menubar_obj, *navigator_obj, *localstorage_obj, *screen_obj; + + static int initialized = 0; + + assert(interpreter); +// if (!js_module_init_ok) return NULL; + + JSRuntime *rt = JS_NewRuntime(); + if (!rt) { + return nullptr; + } + + ctx = JS_NewContext(rt); + + if (!ctx) { + JS_FreeRuntime(rt); + return nullptr; + } + + interpreter->backend_data = ctx; + JS_SetContextOpaque(ctx, interpreter); + +// JS::SetWarningReporter(ctx, error_reporter); + +// JS_AddInterruptCallback(ctx, heartbeat_callback); +// JS::RealmOptions options; + +// JS::RootedObject window_obj(ctx, JS_NewGlobalObject(ctx, &window_class, NULL, JS::FireOnNewGlobalHook, options)); + + JSValue global_obj = JS_GetGlobalObject(ctx); + JSValue window_obj = JS_NewObject(ctx); + JS_SetPropertyStr(ctx, window_obj, "alert", + JS_NewCFunction(ctx, js_window_alert, "alert", 1)); + JS_SetPropertyStr(ctx, global_obj, "window", window_obj); + + JS_FreeValue(ctx, global_obj); + + return ctx; +#if 0 + + + + + if (window_obj) { + interpreter->ac = window_obj; + interpreter->ac2 = new JSAutoRealm(ctx, window_obj); + } else { + goto release_and_fail; + } + + if (!JS::InitRealmStandardClasses(ctx)) { + goto release_and_fail; + } + + if (!JS_DefineProperties(ctx, window_obj, window_props)) { + goto release_and_fail; + } + + if (!spidermonkey_DefineFunctions(ctx, window_obj, window_funcs)) { + goto release_and_fail; + } + //JS_SetPrivate(window_obj, interpreter); /* to @window_class */ + + document_obj = spidermonkey_InitClass(ctx, window_obj, NULL, + &document_class, NULL, 0, + document_props, + document_funcs, + NULL, NULL); + if (!document_obj) { + goto release_and_fail; + } + + interpreter->document_obj = document_obj; + +/* + forms_obj = spidermonkey_InitClass(ctx, document_obj, NULL, + &forms_class, NULL, 0, + forms_props, + forms_funcs, + NULL, NULL); + if (!forms_obj) { + goto release_and_fail; + } +*/ + + history_obj = spidermonkey_InitClass(ctx, window_obj, NULL, + &history_class, NULL, 0, + (JSPropertySpec *) NULL, + history_funcs, + NULL, NULL); + if (!history_obj) { + goto release_and_fail; + } + + location_obj = spidermonkey_InitClass(ctx, window_obj, NULL, + &location_class, NULL, 0, + location_props, + location_funcs, + NULL, NULL); + if (!location_obj) { + goto release_and_fail; + } + + interpreter->location_obj = location_obj; + + screen_obj = spidermonkey_InitClass(ctx, window_obj, NULL, + &screen_class, NULL, 0, + screen_props, + NULL, + NULL, NULL); + + if (!screen_obj) { + goto release_and_fail; + } + + menubar_obj = JS_InitClass(ctx, window_obj, nullptr, + &menubar_class, NULL, 0, + unibar_props, NULL, + NULL, NULL); + if (!menubar_obj) { + goto release_and_fail; + } + JS_SetPrivate(menubar_obj, "t"); /* to @menubar_class */ + + statusbar_obj = JS_InitClass(ctx, window_obj, nullptr, + &statusbar_class, NULL, 0, + unibar_props, NULL, + NULL, NULL); + if (!statusbar_obj) { + goto release_and_fail; + } + JS_SetPrivate(statusbar_obj, "s"); /* to @statusbar_class */ + + navigator_obj = JS_InitClass(ctx, window_obj, nullptr, + &navigator_class, NULL, 0, + navigator_props, NULL, + NULL, NULL); + if (!navigator_obj) { + goto release_and_fail; + } + + console_obj = spidermonkey_InitClass(ctx, window_obj, NULL, + &console_class, NULL, 0, + nullptr, + console_funcs, + NULL, NULL); + if (!console_obj) { + goto release_and_fail; + } + + localstorage_obj = spidermonkey_InitClass(ctx, window_obj, NULL, + &localstorage_class, NULL, 0, + nullptr, + localstorage_funcs, + NULL, NULL); + if (!localstorage_obj) { + goto release_and_fail; + } + + JS::SetRealmPrivate(js::GetContextRealm(ctx), interpreter); + + return ctx; + +release_and_fail: + spidermonkey_put_interpreter(interpreter); + return NULL; + +#endif +} + +void +quickjs_put_interpreter(struct ecmascript_interpreter *interpreter) +{ +#if 0 + JSContext *ctx; + + assert(interpreter); + if (!js_module_init_ok) return; + + ctx = interpreter->backend_data; + if (interpreter->ac2) { + delete (JSAutoRealm *)interpreter->ac2; + } +// JS_DestroyContext(ctx); + interpreter->backend_data = NULL; + interpreter->ac = nullptr; + interpreter->ac2 = nullptr; +#endif +} + +#if 0 +void +spidermonkey_check_for_exception(JSContext *ctx) { + if (JS_IsExceptionPending(ctx)) + { + JS::RootedValue exception(ctx); + if(JS_GetPendingException(ctx,&exception) && exception.isObject()) { + JS::AutoSaveExceptionState savedExc(ctx); + JS::Rooted exceptionObject(ctx, &exception.toObject()); + JSErrorReport *report = JS_ErrorFromException(ctx,exceptionObject); + if(report) { + if (report->lineno>0) { + /* Somehow the reporter alway reports first error + * Undefined and with line 0. Let's filter this. */ + /* Optional printing javascript error to file */ + //FILE *f = fopen("js.err","a"); + //PrintError(ctx, f, report->message(), report, true); + /* Send the error to the tui */ + error_reporter(ctx, report); + //DBG("file: %s",report->filename); + //DBG("file: %s",report->message()); + //DBG("file: %d",(int) report->lineno); + } + } + //JS_ClearPendingException(ctx); + } + /* This absorbs all following exceptions + * probably not the 100% correct solution + * to the javascript error handling but + * at least there isn't too much click-bait + * on each site with javascript enabled */ + JS_ClearPendingException(ctx); + } +} +#endif + +void +quickjs_eval(struct ecmascript_interpreter *interpreter, + struct string *code, struct string *ret) +{ + JSContext *ctx; + + assert(interpreter); +// if (!js_module_init_ok) { +// return; +// } + ctx = interpreter->backend_data; + +// JS::Realm *comp = JS::EnterRealm(ctx, interpreter->ac); + +// interpreter->heartbeat = add_heartbeat(interpreter); + interpreter->ret = ret; + +// JS::RootedObject cg(ctx, JS::CurrentGlobalOrNull(ctx)); +// JS::RootedValue r_val(ctx, rval); +// JS::CompileOptions options(ctx); + +// JS::SourceText srcBuf; +// if (!srcBuf.init(ctx, code->source, code->length, JS::SourceOwnership::Borrowed)) { +// return; +// } + JSValue r = JS_Eval(ctx, code->source, code->length, "", 0); +// JS::Evaluate(ctx, options, srcBuf, &r_val); + +// spidermonkey_check_for_exception(ctx); + +// done_heartbeat(interpreter->heartbeat); +// JS::LeaveRealm(ctx, comp); +} + +#if 0 +void +quickjs_call_function(struct ecmascript_interpreter *interpreter, + JS::HandleValue fun, struct string *ret) +{ +#if 0 + JSContext *ctx; + JS::Value rval; + + assert(interpreter); + if (!js_module_init_ok) { + return; + } + ctx = interpreter->backend_data; + JS::Realm *comp = JS::EnterRealm(ctx, interpreter->ac); + + interpreter->heartbeat = add_heartbeat(interpreter); + interpreter->ret = ret; + + JS::RootedValue r_val(ctx, rval); + JS::RootedObject cg(ctx, JS::CurrentGlobalOrNull(ctx)); + JS_CallFunctionValue(ctx, cg, fun, JS::HandleValueArray::empty(), &r_val); + done_heartbeat(interpreter->heartbeat); + JS::LeaveRealm(ctx, comp); +#endif +} +#endif + +char * +quickjs_eval_stringback(struct ecmascript_interpreter *interpreter, + struct string *code) +{ +#if 0 + bool ret; + JSContext *ctx; + JS::Value rval; + char *result = NULL; + + assert(interpreter); + if (!js_module_init_ok) return NULL; + ctx = interpreter->backend_data; + interpreter->ret = NULL; + interpreter->heartbeat = add_heartbeat(interpreter); + + JS::Realm *comp = JS::EnterRealm(ctx, interpreter->ac); + + JS::RootedObject cg(ctx, JS::CurrentGlobalOrNull(ctx)); + JS::RootedValue r_rval(ctx, rval); + JS::CompileOptions options(ctx); + +// options.setIntroductionType("js shell load") +// .setUTF8(true) +// .setCompileAndGo(true) +// .setNoScriptRval(true); + + JS::SourceText srcBuf; + if (!srcBuf.init(ctx, code->source, code->length, JS::SourceOwnership::Borrowed)) { + return NULL; + } + ret = JS::Evaluate(ctx, options, srcBuf, &r_rval); + done_heartbeat(interpreter->heartbeat); + + if (ret == false) { + result = NULL; + } + else if (r_rval.isNullOrUndefined()) { + /* Undefined value. */ + result = NULL; + } else { + result = jsval_to_string(ctx, r_rval); + } + JS::LeaveRealm(ctx, comp); + + return result; +#endif + return nullptr; +} + +int +quickjs_eval_boolback(struct ecmascript_interpreter *interpreter, + struct string *code) +{ +#if 0 + JSContext *ctx; + JS::Value rval; + int ret; + int result = 0; + + assert(interpreter); + if (!js_module_init_ok) return 0; + ctx = interpreter->backend_data; + interpreter->ret = NULL; + + JS::Realm *comp = JS::EnterRealm(ctx, interpreter->ac); + + JS::CompileOptions options(ctx); + JS::RootedObjectVector ag(ctx); + + JS::SourceText srcBuf; + if (!srcBuf.init(ctx, code->source, code->length, JS::SourceOwnership::Borrowed)) { + return -1; + } + + JSFunction *funs = JS::CompileFunction(ctx, ag, options, "aaa", 0, nullptr, srcBuf); + if (!funs) { + return -1; + }; + + interpreter->heartbeat = add_heartbeat(interpreter); + JS::RootedValue r_val(ctx, rval); + JS::RootedObject cg(ctx, JS::CurrentGlobalOrNull(ctx)); + JS::RootedFunction fun(ctx, funs); + ret = JS_CallFunction(ctx, cg, fun, JS::HandleValueArray::empty(), &r_val); + done_heartbeat(interpreter->heartbeat); + + if (ret == 2) { /* onClick="history.back()" */ + result = 0; + } + else if (ret == false) { + result = -1; + } + else if (r_val.isUndefined()) { + /* Undefined value. */ + result = -1; + } else { + result = r_val.toBoolean(); + } + + JS::LeaveRealm(ctx, comp); + + return result; +#endif + return 0; +} + +struct module quickjs_module = struct_module( + /* name: */ N_("QuickJS"), + /* options: */ NULL, + /* events: */ NULL, + /* submodules: */ NULL, + /* data: */ NULL, + /* init: */ quickjs_init, + /* done: */ quickjs_done +); diff --git a/src/ecmascript/quickjs.h b/src/ecmascript/quickjs.h new file mode 100644 index 00000000..865e02df --- /dev/null +++ b/src/ecmascript/quickjs.h @@ -0,0 +1,25 @@ +#ifndef EL__ECMASCRIPT_QUICKJS_H +#define EL__ECMASCRIPT_QUICKJS_H + +#include + +struct ecmascript_interpreter; +struct form_view; +struct form_state; +struct string; + +void *quickjs_get_interpreter(struct ecmascript_interpreter *interpreter); +void quickjs_put_interpreter(struct ecmascript_interpreter *interpreter); + +void quickjs_detach_form_view(struct form_view *fv); +void quickjs_detach_form_state(struct form_state *fs); +void quickjs_moved_form_state(struct form_state *fs); + +void quickjs_eval(struct ecmascript_interpreter *interpreter, struct string *code, struct string *ret); +char *quickjs_eval_stringback(struct ecmascript_interpreter *interpreter, struct string *code); +int quickjs_eval_boolback(struct ecmascript_interpreter *interpreter, struct string *code); + +//void quickjs_call_function(struct ecmascript_interpreter *interpreter, JS::HandleValue fun, struct string *ret); + +extern struct module quickjs_module; +#endif diff --git a/src/ecmascript/quickjs/meson.build b/src/ecmascript/quickjs/meson.build new file mode 100644 index 00000000..a73839e3 --- /dev/null +++ b/src/ecmascript/quickjs/meson.build @@ -0,0 +1 @@ +srcs += files('window.c') diff --git a/src/ecmascript/quickjs/window.c b/src/ecmascript/quickjs/window.c new file mode 100644 index 00000000..d99bcd77 --- /dev/null +++ b/src/ecmascript/quickjs/window.c @@ -0,0 +1,75 @@ +/* The Quickjs window object implementation. */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include + +#include "elinks.h" + +#include "bfu/dialog.h" +#include "cache/cache.h" +#include "cookies/cookies.h" +#include "dialogs/menu.h" +#include "dialogs/status.h" +#include "document/html/frames.h" +#include "document/document.h" +#include "document/forms.h" +#include "document/view.h" +#include "ecmascript/ecmascript.h" +#include "ecmascript/quickjs/window.h" +#include "intl/libintl.h" +#include "main/select.h" +#include "osdep/newwin.h" +#include "osdep/sysname.h" +#include "protocol/http/http.h" +#include "protocol/uri.h" +#include "session/history.h" +#include "session/location.h" +#include "session/session.h" +#include "session/task.h" +#include "terminal/tab.h" +#include "terminal/terminal.h" +#include "util/conv.h" +#include "util/memory.h" +#include "util/string.h" +#include "viewer/text/draw.h" +#include "viewer/text/form.h" +#include "viewer/text/link.h" +#include "viewer/text/vs.h" + +JSValue +js_window_alert(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)JS_GetContextOpaque(ctx); + + assert(interpreter); + struct view_state *vs; + const char *str, *string; + size_t len; + + vs = interpreter->vs; + + if (argc != 1) + return JS_UNDEFINED; + + str = JS_ToCStringLen(ctx, &len, argv[0]); + + if (!str) { + return JS_EXCEPTION; + } + + string = stracpy(str); + JS_FreeCString(ctx, str); + + info_box(vs->doc_view->session->tab->term, MSGBOX_FREE_TEXT, + N_("JavaScript Alert"), ALIGN_CENTER, string); + + return JS_UNDEFINED; +} diff --git a/src/ecmascript/quickjs/window.h b/src/ecmascript/quickjs/window.h new file mode 100644 index 00000000..b3b35fc4 --- /dev/null +++ b/src/ecmascript/quickjs/window.h @@ -0,0 +1,10 @@ + +#ifndef EL__ECMASCRIPT_QUICKJS_WINDOW_H +#define EL__ECMASCRIPT_QUICKJS_WINDOW_H + +#include + +JSValue js_window_alert(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv); + +#endif diff --git a/src/ecmascript/spidermonkey.c b/src/ecmascript/spidermonkey.c index daff136d..59ec076b 100644 --- a/src/ecmascript/spidermonkey.c +++ b/src/ecmascript/spidermonkey.c @@ -341,16 +341,6 @@ spidermonkey_check_for_exception(JSContext *ctx) { } } -void -free_document(void *doc) -{ - if (!doc) { - return; - } - xmlpp::Document *docu = doc; - delete docu; -} - void spidermonkey_eval(struct ecmascript_interpreter *interpreter, struct string *code, struct string *ret) diff --git a/src/ecmascript/spidermonkey.h b/src/ecmascript/spidermonkey.h index 317fbc74..b6d641f4 100644 --- a/src/ecmascript/spidermonkey.h +++ b/src/ecmascript/spidermonkey.h @@ -21,7 +21,5 @@ int spidermonkey_eval_boolback(struct ecmascript_interpreter *interpreter, struc void spidermonkey_call_function(struct ecmascript_interpreter *interpreter, JS::HandleValue fun, struct string *ret); -void free_document(void *doc); - extern struct module spidermonkey_module; #endif diff --git a/src/ecmascript/spidermonkey/document.c b/src/ecmascript/spidermonkey/document.c index d9ae8e5d..0738530b 100644 --- a/src/ecmascript/spidermonkey/document.c +++ b/src/ecmascript/spidermonkey/document.c @@ -1520,34 +1520,6 @@ document_replace(JSContext *ctx, unsigned int argc, JS::Value *vp) return(true); } -void * -document_parse(struct document *document) -{ -#ifdef ECMASCRIPT_DEBUG - fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); -#endif - struct cache_entry *cached = document->cached; - struct fragment *f = get_cache_fragment(cached); - - if (!f || !f->length) { - return NULL; - } - - struct string str; - init_string(&str); - - add_bytes_to_string(&str, f->data, f->length); - - // Parse HTML and create a DOM tree - xmlDoc* doc = htmlReadDoc((xmlChar*)str.source, NULL, get_cp_mime_name(document->cp), - HTML_PARSE_RECOVER | HTML_PARSE_NOERROR | HTML_PARSE_NOWARNING); - // Encapsulate raw libxml document in a libxml++ wrapper - xmlpp::Document *docu = new xmlpp::Document(doc); - done_string(&str); - - return (void *)docu; -} - static bool document_createComment(JSContext *ctx, unsigned int argc, JS::Value *vp) { diff --git a/src/ecmascript/spidermonkey/document.h b/src/ecmascript/spidermonkey/document.h index 3d031c43..33c4c033 100644 --- a/src/ecmascript/spidermonkey/document.h +++ b/src/ecmascript/spidermonkey/document.h @@ -7,7 +7,6 @@ extern JSClass document_class; extern const spidermonkeyFunctionSpec document_funcs[]; extern JSPropertySpec document_props[]; -void *document_parse(struct document *document); JSObject *getDocument(JSContext *ctx, void *doc); diff --git a/src/main/module.c b/src/main/module.c index a3e077ac..796e0a30 100644 --- a/src/main/module.c +++ b/src/main/module.c @@ -66,7 +66,7 @@ struct module *builtin_modules[] = { #ifdef CONFIG_COOKIES &cookies_module, #endif -#ifdef CONFIG_ECMASCRIPT_SMJS +#if defined(CONFIG_ECMASCRIPT_SMJS) || defined(CONFIG_QUICKJS) &ecmascript_module, #endif #ifdef CONFIG_FORMHIST diff --git a/src/protocol/protocol.c b/src/protocol/protocol.c index 513345cc..4cdf9717 100644 --- a/src/protocol/protocol.c +++ b/src/protocol/protocol.c @@ -221,7 +221,7 @@ generic_external_protocol_handler(struct session *ses, struct uri *uri) switch (uri->protocol) { case PROTOCOL_JAVASCRIPT: -#ifdef CONFIG_ECMASCRIPT_SMJS +#if defined(CONFIG_ECMASCRIPT_SMJS) || defined(CONFIG_QUICKJS) ecmascript_protocol_handler(ses, uri); return; #else diff --git a/src/viewer/text/form.c b/src/viewer/text/form.c index 762435e1..50858fde 100644 --- a/src/viewer/text/form.c +++ b/src/viewer/text/form.c @@ -252,7 +252,7 @@ find_form_state(struct document_view *doc_view, struct el_form_control *fc) if (n >= vs->form_info_len) { int nn = n + 1; -#ifdef CONFIG_ECMASCRIPT_SMJS +#if defined(CONFIG_ECMASCRIPT_SMJS) || defined(CONFIG_QUICKJS) const struct form_state *const old_form_info = vs->form_info; #endif @@ -261,7 +261,7 @@ find_form_state(struct document_view *doc_view, struct el_form_control *fc) vs->form_info = fs; vs->form_info_len = nn; -#ifdef CONFIG_ECMASCRIPT_SMJS +#if defined(CONFIG_ECMASCRIPT_SMJS) || defined(CONFIG_QUICKJS) /* TODO: Standard C does not allow this comparison; * if the memory to which old_form_info pointed has * been freed, then the value of the pointer itself is diff --git a/src/viewer/text/link.c b/src/viewer/text/link.c index 0554607a..7700280e 100644 --- a/src/viewer/text/link.c +++ b/src/viewer/text/link.c @@ -52,7 +52,7 @@ static int current_link_evhook(struct document_view *doc_view, enum script_event_hook_type type) { -#ifdef CONFIG_ECMASCRIPT_SMJS +#if defined(CONFIG_ECMASCRIPT_SMJS) || defined(CONFIG_QUICKJS) struct link *link; struct script_event_hook *evhook; @@ -915,7 +915,7 @@ call_onsubmit_and_submit(struct session *ses, struct document_view *doc_view, assert(fc->form); /* regardless of whether there is a FORM element */ if_assert_failed return 0; -#ifdef CONFIG_ECMASCRIPT_SMJS +#if defined(CONFIG_ECMASCRIPT_SMJS) || defined(CONFIG_QUICKJS) /* If the form has multiple submit buttons, this does not * explicitly tell the ECMAScript code which of them was * pressed. W3C DOM Level 3 doesn't seem to include such a diff --git a/src/viewer/text/vs.c b/src/viewer/text/vs.c index 0b154049..ef26d3c0 100644 --- a/src/viewer/text/vs.c +++ b/src/viewer/text/vs.c @@ -38,7 +38,7 @@ init_vs(struct view_state *vs, struct uri *uri, int plain) vs->plain = plain; vs->uri = uri ? get_uri_reference(uri) : NULL; vs->did_fragment = !uri->fragmentlen; -#ifdef CONFIG_ECMASCRIPT_SMJS +#if defined(CONFIG_ECMASCRIPT_SMJS) || defined(CONFIG_QUICKJS) /* If we ever get to render this vs, give it an interpreter. */ vs->ecmascript_fragile = 1; #endif @@ -67,7 +67,7 @@ destroy_vs(struct view_state *vs, int blast_ecmascript) } if (vs->uri) done_uri(vs->uri); -#ifdef CONFIG_ECMASCRIPT_SMJS +#if defined(CONFIG_ECMASCRIPT_SMJS) || defined(CONFIG_QUICKJS) if (blast_ecmascript && vs->ecmascript) ecmascript_put_interpreter(vs->ecmascript); #endif @@ -87,7 +87,7 @@ copy_vs(struct view_state *dst, struct view_state *src) /* We do not copy ecmascript stuff around since it's specific for * a single location, offsprings (followups and so) nedd their own. */ -#ifdef CONFIG_ECMASCRIPT_SMJS +#if defined(CONFIG_ECMASCRIPT_SMJS) || defined(CONFIG_QUICKJS) dst->ecmascript = NULL; /* If we ever get to render this vs, give it an interpreter. */ dst->ecmascript_fragile = 1; @@ -131,7 +131,7 @@ copy_vs(struct view_state *dst, struct view_state *src) struct form_state *srcfs = &src->form_info[i]; struct form_state *dstfs = &dst->form_info[i]; -#ifdef CONFIG_ECMASCRIPT_SMJS +#if defined(CONFIG_ECMASCRIPT_SMJS) || defined(CONFIG_QUICKJS) dstfs->ecmascript_obj = NULL; #endif if (srcfs->value)