diff --git a/src/ecmascript/quickjs.c b/src/ecmascript/quickjs.c index 9442bf6b..01ae40be 100644 --- a/src/ecmascript/quickjs.c +++ b/src/ecmascript/quickjs.c @@ -26,6 +26,7 @@ #include "ecmascript/ecmascript.h" #include "ecmascript/quickjs.h" #include "ecmascript/quickjs/history.h" +#include "ecmascript/quickjs/location.h" #include "ecmascript/quickjs/navigator.h" #include "ecmascript/quickjs/screen.h" #include "ecmascript/quickjs/unibar.h" @@ -167,6 +168,7 @@ quickjs_get_interpreter(struct ecmascript_interpreter *interpreter) js_unibar_init(ctx, global_obj); js_navigator_init(ctx, global_obj); js_history_init(ctx, global_obj); + js_location_init(ctx, global_obj); JS_FreeValue(ctx, global_obj); diff --git a/src/ecmascript/quickjs/location.c b/src/ecmascript/quickjs/location.c new file mode 100644 index 00000000..6b51635b --- /dev/null +++ b/src/ecmascript/quickjs/location.c @@ -0,0 +1,644 @@ +/* The QuickJS location 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/location.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" + + + +#define countof(x) (sizeof(x) / sizeof((x)[0])) + +static JSClassID js_location_class_id; + +static JSValue +js_location_get_property_hash(JSContext *ctx, JSValueConst this_val, int magic) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)JS_GetContextOpaque(ctx); + struct view_state *vs = interpreter->vs; + + if (!vs) { +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s %d\n", __FILE__, __FUNCTION__, __LINE__); +#endif + return JS_EXCEPTION; + } + + struct string fragment; + init_string(&fragment); + + if (vs->uri->fragmentlen) { + add_bytes_to_string(&fragment, vs->uri->fragment, vs->uri->fragmentlen); + } + + JSValue ret = JS_NewString(ctx, fragment.source); + done_string(&fragment); + + return ret; +} + +static JSValue +js_location_get_property_host(JSContext *ctx, JSValueConst this_val, int magic) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)JS_GetContextOpaque(ctx); + struct view_state *vs = interpreter->vs; + + if (!vs) { +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s %d\n", __FILE__, __FUNCTION__, __LINE__); +#endif + return JS_EXCEPTION; + } + + char *str = get_uri_string(vs->uri, URI_HOST | URI_PORT); + + if (!str) { +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s %d\n", __FILE__, __FUNCTION__, __LINE__); +#endif + return JS_EXCEPTION; + } + + JSValue ret = JS_NewString(ctx, str); + mem_free(str); + + return ret; +} + +static JSValue +js_location_get_property_hostname(JSContext *ctx, JSValueConst this_val, int magic) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)JS_GetContextOpaque(ctx); + struct view_state *vs = interpreter->vs; + + if (!vs) { +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s %d\n", __FILE__, __FUNCTION__, __LINE__); +#endif + return JS_EXCEPTION; + } + + char *str = get_uri_string(vs->uri, URI_HOST); + + if (!str) { +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s %d\n", __FILE__, __FUNCTION__, __LINE__); +#endif + return JS_EXCEPTION; + } + + JSValue ret = JS_NewString(ctx, str); + mem_free(str); + + return ret; +} + +static JSValue +js_location_get_property_href(JSContext *ctx, JSValueConst this_val, int magic) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)JS_GetContextOpaque(ctx); + struct view_state *vs = interpreter->vs; + + if (!vs) { +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s %d\n", __FILE__, __FUNCTION__, __LINE__); +#endif + return JS_EXCEPTION; + } + + char *str = get_uri_string(vs->uri, URI_ORIGINAL); + + if (!str) { +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s %d\n", __FILE__, __FUNCTION__, __LINE__); +#endif + return JS_EXCEPTION; + } + + JSValue ret = JS_NewString(ctx, str); + mem_free(str); + + return ret; +} + +static JSValue +js_location_get_property_origin(JSContext *ctx, JSValueConst this_val, int magic) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)JS_GetContextOpaque(ctx); + struct view_state *vs = interpreter->vs; + + if (!vs) { +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s %d\n", __FILE__, __FUNCTION__, __LINE__); +#endif + return JS_EXCEPTION; + } + + char *str = get_uri_string(vs->uri, URI_SERVER); + + if (!str) { +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s %d\n", __FILE__, __FUNCTION__, __LINE__); +#endif + return JS_EXCEPTION; + } + + JSValue ret = JS_NewString(ctx, str); + mem_free(str); + + return ret; +} + +static JSValue +js_location_get_property_pathname(JSContext *ctx, JSValueConst this_val, int magic) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)JS_GetContextOpaque(ctx); + struct view_state *vs = interpreter->vs; + + if (!vs) { +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s %d\n", __FILE__, __FUNCTION__, __LINE__); +#endif + return JS_EXCEPTION; + } + + struct string pathname; + init_string(&pathname); + + const char *query = memchr(vs->uri->data, '?', vs->uri->datalen); + int len = (query ? query - vs->uri->data : vs->uri->datalen); + + add_bytes_to_string(&pathname, vs->uri->data, len); + + JSValue ret = JS_NewString(ctx, pathname.source); + done_string(&pathname); + + return ret; +} + +static JSValue +js_location_get_property_port(JSContext *ctx, JSValueConst this_val, int magic) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)JS_GetContextOpaque(ctx); + struct view_state *vs = interpreter->vs; + + if (!vs) { +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s %d\n", __FILE__, __FUNCTION__, __LINE__); +#endif + return JS_EXCEPTION; + } + + struct string port; + init_string(&port); + if (vs->uri->portlen) { + add_bytes_to_string(&port, vs->uri->port, vs->uri->portlen); + } + + JSValue ret = JS_NewString(ctx, port.source); + done_string(&port); + + return ret; +} + +static JSValue +js_location_get_property_protocol(JSContext *ctx, JSValueConst this_val, int magic) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)JS_GetContextOpaque(ctx); + struct view_state *vs = interpreter->vs; + + if (!vs) { +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s %d\n", __FILE__, __FUNCTION__, __LINE__); +#endif + return JS_EXCEPTION; + } + + struct string proto; + init_string(&proto); + + /* Custom or unknown keep the URI untouched. */ + if (vs->uri->protocol == PROTOCOL_UNKNOWN) { + add_to_string(&proto, struri(vs->uri)); + } else { + add_bytes_to_string(&proto, vs->uri->string, vs->uri->protocollen); + add_char_to_string(&proto, ':'); + } + + JSValue ret = JS_NewString(ctx, proto.source); + done_string(&proto); + + return ret; +} + +static JSValue +js_location_get_property_search(JSContext *ctx, JSValueConst this_val, int magic) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)JS_GetContextOpaque(ctx); + struct view_state *vs = interpreter->vs; + + if (!vs) { +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s %d\n", __FILE__, __FUNCTION__, __LINE__); +#endif + return JS_EXCEPTION; + } + + struct string search; + init_string(&search); + + const char *query = memchr(vs->uri->data, '?', vs->uri->datalen); + + if (query) { + add_bytes_to_string(&search, query, strcspn(query, "#" POST_CHAR_S)); + } + + JSValue ret = JS_NewString(ctx, search.source); + done_string(&search); + + return ret; +} + +static JSValue +js_location_set_property_hash(JSContext *ctx, JSValueConst this_val, JSValue val, int magic) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)JS_GetContextOpaque(ctx); + struct view_state *vs = interpreter->vs; + + if (!vs) { +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s %d\n", __FILE__, __FUNCTION__, __LINE__); +#endif + return JS_EXCEPTION; + } + + size_t len; + + const char *str = JS_ToCStringLen(ctx, &len, val); + + if (!str) { + return JS_EXCEPTION; + } + location_goto(vs->doc_view, str); + JS_FreeCString(ctx, str); + + return JS_UNDEFINED; +} + +static JSValue +js_location_set_property_host(JSContext *ctx, JSValueConst this_val, JSValue val, int magic) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)JS_GetContextOpaque(ctx); + struct view_state *vs = interpreter->vs; + + if (!vs) { +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s %d\n", __FILE__, __FUNCTION__, __LINE__); +#endif + return JS_EXCEPTION; + } + size_t len; + + const char *str = JS_ToCStringLen(ctx, &len, val); + + if (!str) { + return JS_EXCEPTION; + } + location_goto(vs->doc_view, str); + JS_FreeCString(ctx, str); + + return JS_UNDEFINED; +} + +static JSValue +js_location_set_property_hostname(JSContext *ctx, JSValueConst this_val, JSValue val, int magic) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)JS_GetContextOpaque(ctx); + struct view_state *vs = interpreter->vs; + + if (!vs) { +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s %d\n", __FILE__, __FUNCTION__, __LINE__); +#endif + return JS_EXCEPTION; + } + size_t len; + + const char *str = JS_ToCStringLen(ctx, &len, val); + + if (!str) { + return JS_EXCEPTION; + } + location_goto(vs->doc_view, str); + JS_FreeCString(ctx, str); + + return JS_UNDEFINED; +} + +static JSValue +js_location_set_property_href(JSContext *ctx, JSValueConst this_val, JSValue val, int magic) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)JS_GetContextOpaque(ctx); + struct view_state *vs = interpreter->vs; + + if (!vs) { +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s %d\n", __FILE__, __FUNCTION__, __LINE__); +#endif + return JS_EXCEPTION; + } + size_t len; + + const char *str = JS_ToCStringLen(ctx, &len, val); + + if (!str) { + return JS_EXCEPTION; + } + location_goto(vs->doc_view, str); + JS_FreeCString(ctx, str); + + return JS_UNDEFINED; +} + +static JSValue +js_location_set_property_pathname(JSContext *ctx, JSValueConst this_val, JSValue val, int magic) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)JS_GetContextOpaque(ctx); + struct view_state *vs = interpreter->vs; + + if (!vs) { +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s %d\n", __FILE__, __FUNCTION__, __LINE__); +#endif + return JS_EXCEPTION; + } + size_t len; + + const char *str = JS_ToCStringLen(ctx, &len, val); + + if (!str) { + return JS_EXCEPTION; + } + location_goto(vs->doc_view, str); + JS_FreeCString(ctx, str); + + return JS_UNDEFINED; +} + +static JSValue +js_location_set_property_port(JSContext *ctx, JSValueConst this_val, JSValue val, int magic) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)JS_GetContextOpaque(ctx); + struct view_state *vs = interpreter->vs; + + if (!vs) { +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s %d\n", __FILE__, __FUNCTION__, __LINE__); +#endif + return JS_EXCEPTION; + } + size_t len; + + const char *str = JS_ToCStringLen(ctx, &len, val); + + if (!str) { + return JS_EXCEPTION; + } + location_goto(vs->doc_view, str); + JS_FreeCString(ctx, str); + + return JS_UNDEFINED; +} + +static JSValue +js_location_set_property_protocol(JSContext *ctx, JSValueConst this_val, JSValue val, int magic) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)JS_GetContextOpaque(ctx); + struct view_state *vs = interpreter->vs; + + if (!vs) { +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s %d\n", __FILE__, __FUNCTION__, __LINE__); +#endif + return JS_EXCEPTION; + } + size_t len; + + const char *str = JS_ToCStringLen(ctx, &len, val); + + if (!str) { + return JS_EXCEPTION; + } + location_goto(vs->doc_view, str); + JS_FreeCString(ctx, str); + + return JS_UNDEFINED; +} + +static JSValue +js_location_set_property_search(JSContext *ctx, JSValueConst this_val, JSValue val, int magic) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)JS_GetContextOpaque(ctx); + struct view_state *vs = interpreter->vs; + + if (!vs) { +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s %d\n", __FILE__, __FUNCTION__, __LINE__); +#endif + return JS_EXCEPTION; + } + size_t len; + + const char *str = JS_ToCStringLen(ctx, &len, val); + + if (!str) { + return JS_EXCEPTION; + } + location_goto(vs->doc_view, str); + JS_FreeCString(ctx, str); + + return JS_UNDEFINED; +} + +static JSValue +js_location_reload(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); + struct view_state *vs = interpreter->vs; + + if (!vs) { +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s %d\n", __FILE__, __FUNCTION__, __LINE__); +#endif + return JS_EXCEPTION; + } + location_goto(vs->doc_view, ""); + + return JS_UNDEFINED; +} + +/* @location_funcs{"toString"}, @location_funcs{"toLocaleString"} */ +static JSValue +js_location_toString(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) +{ +#ifdef ECMASCRIPT_DEBUG + fprintf(stderr, "%s:%s\n", __FILE__, __FUNCTION__); +#endif + return js_location_get_property_href(ctx, this_val, 0); +} + +static const JSCFunctionListEntry js_location_proto_funcs[] = { + JS_CGETSET_MAGIC_DEF("hash", js_location_get_property_hash, js_location_set_property_hash, 0), + JS_CGETSET_MAGIC_DEF("host", js_location_get_property_host, js_location_set_property_host, 0), + JS_CGETSET_MAGIC_DEF("hostname", js_location_get_property_hostname, js_location_set_property_hostname, 0), + JS_CGETSET_MAGIC_DEF("href", js_location_get_property_href, js_location_set_property_href, 0), + JS_CGETSET_MAGIC_DEF("origin", js_location_get_property_origin, nullptr, 0), + JS_CGETSET_MAGIC_DEF("pathname", js_location_get_property_pathname, js_location_set_property_pathname, 0), + JS_CGETSET_MAGIC_DEF("port", js_location_get_property_port, js_location_set_property_port, 0), + JS_CGETSET_MAGIC_DEF("protocol", js_location_get_property_protocol, js_location_set_property_protocol, 0), + JS_CGETSET_MAGIC_DEF("search", js_location_get_property_search, js_location_set_property_search, 0), + JS_CFUNC_DEF("reload", 0, js_location_reload), + JS_CFUNC_DEF("toString", 0, js_location_toString), + JS_CFUNC_DEF("toLocaleString", 0, js_location_toString), +}; + +static JSClassDef js_location_class = { + "location", +}; + +static JSValue +js_location_ctor(JSContext *ctx, JSValueConst new_target, int argc, JSValueConst *argv) +{ + JSValue obj = JS_UNDEFINED; + JSValue proto; + /* using new_target to get the prototype is necessary when the + class is extended. */ + proto = JS_GetPropertyStr(ctx, new_target, "prototype"); + + if (JS_IsException(proto)) { + goto fail; + } + obj = JS_NewObjectProtoClass(ctx, proto, js_location_class_id); + JS_FreeValue(ctx, proto); + + if (JS_IsException(obj)) { + goto fail; + } + return obj; + +fail: + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; +} + +int +js_location_init(JSContext *ctx, JSValue global_obj) +{ + JSValue location_proto, location_class; + + /* create the location class */ + JS_NewClassID(&js_location_class_id); + JS_NewClass(JS_GetRuntime(ctx), js_location_class_id, &js_location_class); + + location_proto = JS_NewObject(ctx); + JS_SetPropertyFunctionList(ctx, location_proto, js_location_proto_funcs, countof(js_location_proto_funcs)); + + location_class = JS_NewCFunction2(ctx, js_location_ctor, "location", 0, JS_CFUNC_constructor, 0); + /* set proto.constructor and ctor.prototype */ + JS_SetConstructor(ctx, location_class, location_proto); + JS_SetClassProto(ctx, js_location_class_id, location_proto); + + JS_SetPropertyStr(ctx, global_obj, "location", location_proto); + return 0; +} diff --git a/src/ecmascript/quickjs/location.h b/src/ecmascript/quickjs/location.h new file mode 100644 index 00000000..9f8b948e --- /dev/null +++ b/src/ecmascript/quickjs/location.h @@ -0,0 +1,8 @@ +#ifndef EL__ECMASCRIPT_QUICKJS_LOCATION_H +#define EL__ECMASCRIPT_QUICKJS_LOCATION_H + +#include + +int js_location_init(JSContext *ctx, JSValue global_obj); + +#endif diff --git a/src/ecmascript/quickjs/meson.build b/src/ecmascript/quickjs/meson.build index 3b65e47b..b704d618 100644 --- a/src/ecmascript/quickjs/meson.build +++ b/src/ecmascript/quickjs/meson.build @@ -1 +1 @@ -srcs += files('history.c', 'navigator.c', 'screen.c', 'unibar.c', 'window.c') +srcs += files('history.c', 'location.c', 'navigator.c', 'screen.c', 'unibar.c', 'window.c') diff --git a/test/ecmascript/location_js.html b/test/ecmascript/location_js.html new file mode 100644 index 00000000..a3134a20 --- /dev/null +++ b/test/ecmascript/location_js.html @@ -0,0 +1,12 @@ + \ No newline at end of file