From cdad9adf19221b49a637316d2c8e75ab49ad7c79 Mon Sep 17 00:00:00 2001 From: Miciah Dashiel Butler Masters Date: Sun, 13 Nov 2011 07:12:08 +0000 Subject: [PATCH] SMJS: add session object Add session_class, which defines a JSObject wrapper for struct session. Add location_array_class, which defines a JSObject wrapper for struct ses_history. The "history" member of struct session is a struct ses_history, which is a linked list of struct location. Add a pointer from struct session to the session_class object and the location_array object. Add smjs_get_session_object to return a session_class JSObject wrapper for a given struct session. Add smjs_get_session_location_array_object to return a location_array_class JSObject wrapper for a given struct session. Add "session" property to the "elinks" object, which uses smjs_get_session_object to get a JSObject wrapper for smjs_ses. Add smjs_location_array_get_property, which allows indexing a location_array object using a positive number for history forward or a negative number for history backward. Add session_props, session_get_property, session_set_property, session_funcs, smjs_session_goto_url (which implements the "goto" method), and smjs_init_session_interface for session_class. Add session_construct, which creates a new tab and returns the JSObject session_class wrapper. Add session_finalize and smjs_location_array_finalize, which clear the pointers between struct session and the JSObject wrappers in question. Add smjs_detach_session_object, which clears the pointers between a given struct session and the corresponding JSObject wrappers. In destroy_session, call smjs_detach_session_object. Add jsval_to_object helper in ecmascript/spidermonkey/util.h; jsval_to_object is used in smjs_session_goto_url. Modify delayed_goto_uri_frame to allow the target to be NULL. smjs_session_goto_url needs this modification. --- src/ecmascript/spidermonkey/util.h | 12 + src/scripting/smjs/Makefile | 2 +- src/scripting/smjs/elinks_object.c | 17 + src/scripting/smjs/session_object.c | 645 ++++++++++++++++++++++++++++ src/scripting/smjs/session_object.h | 16 + src/scripting/smjs/smjs.h | 3 + src/session/session.c | 6 + src/session/session.h | 7 + src/session/task.c | 4 +- 9 files changed, 709 insertions(+), 3 deletions(-) create mode 100644 src/scripting/smjs/session_object.c create mode 100644 src/scripting/smjs/session_object.h diff --git a/src/ecmascript/spidermonkey/util.h b/src/ecmascript/spidermonkey/util.h index 84da9a6c5..b02bc20b1 100644 --- a/src/ecmascript/spidermonkey/util.h +++ b/src/ecmascript/spidermonkey/util.h @@ -65,4 +65,16 @@ jsval_to_boolean(JSContext *ctx, jsval *vp) return JSVAL_TO_BOOLEAN(val); } +static inline JSObject * +jsval_to_object(JSContext *ctx, jsval *vp) +{ + jsval val; + + if (JS_ConvertValue(ctx, *vp, JSTYPE_OBJECT, &val) == JS_FALSE) { + return NULL; + } + + return JSVAL_TO_OBJECT(val); +} + #endif diff --git a/src/scripting/smjs/Makefile b/src/scripting/smjs/Makefile index 0cca13edb..cdba41772 100644 --- a/src/scripting/smjs/Makefile +++ b/src/scripting/smjs/Makefile @@ -5,6 +5,6 @@ INCLUDES += $(SPIDERMONKEY_CFLAGS) OBJS = smjs.o core.o global_object.o hooks.o elinks_object.o cache_object.o \ view_state_object.o action_object.o bookmarks.o globhist.o \ - keybinding.o load_uri.o + keybinding.o load_uri.o session_object.o include $(top_srcdir)/Makefile.lib diff --git a/src/scripting/smjs/elinks_object.c b/src/scripting/smjs/elinks_object.c index 3807516cc..7b0f8c8fc 100644 --- a/src/scripting/smjs/elinks_object.c +++ b/src/scripting/smjs/elinks_object.c @@ -9,6 +9,7 @@ #include "bfu/msgbox.h" #include "config/home.h" #include "ecmascript/spidermonkey-shared.h" +#include "ecmascript/spidermonkey/util.h" #include "intl/gettext/libintl.h" #include "protocol/uri.h" #include "scripting/scripting.h" @@ -20,6 +21,7 @@ #include "scripting/smjs/globhist.h" #include "scripting/smjs/keybinding.h" #include "scripting/smjs/load_uri.h" +#include "scripting/smjs/session_object.h" #include "scripting/smjs/view_state_object.h" #include "session/location.h" #include "session/session.h" @@ -87,11 +89,13 @@ elinks_execute(JSContext *ctx, uintN argc, jsval *rval) enum elinks_prop { ELINKS_HOME, ELINKS_LOCATION, + ELINKS_SESSION, }; static const JSPropertySpec elinks_props[] = { { "home", ELINKS_HOME, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY }, { "location", ELINKS_LOCATION, JSPROP_ENUMERATE | JSPROP_PERMANENT }, + { "session", ELINKS_SESSION, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY }, { NULL } }; @@ -133,6 +137,18 @@ elinks_get_property(JSContext *ctx, JSObject *obj, jsid id, jsval *vp) return JS_TRUE; } + case ELINKS_SESSION: { + JSObject *jsobj; + + if (!smjs_ses) return JS_FALSE; + + jsobj = smjs_get_session_object(smjs_ses); + if (!jsobj) return JS_FALSE; + + object_to_jsval(ctx, vp, jsobj); + + return JS_TRUE; + } default: INTERNAL("Invalid ID %d in elinks_get_property().", JSID_TO_INT(id)); @@ -218,6 +234,7 @@ smjs_init_elinks_object(void) smjs_init_keybinding_interface(); smjs_init_load_uri_interface(); smjs_init_view_state_interface(); + smjs_init_session_interface(); } /* If elinks. is defined, call it with the given arguments, diff --git a/src/scripting/smjs/session_object.c b/src/scripting/smjs/session_object.c new file mode 100644 index 000000000..76b24dfd4 --- /dev/null +++ b/src/scripting/smjs/session_object.c @@ -0,0 +1,645 @@ +/* Exports struct session to the world of ECMAScript */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include + +#include "elinks.h" + +#include "ecmascript/spidermonkey/util.h" +#include "main/select.h" +#include "protocol/uri.h" +#include "scripting/smjs/core.h" +#include "scripting/smjs/elinks_object.h" +#include "scripting/smjs/global_object.h" +#include "scripting/smjs/session_object.h" +#include "scripting/smjs/view_state_object.h" +#include "session/history.h" +#include "session/location.h" +#include "session/session.h" +#include "session/task.h" +#include "terminal/tab.h" +#include "util/error.h" +#include "util/memory.h" +#include "viewer/text/vs.h" + + +static JSObject *smjs_session_object; + +static const JSClass session_class; /* Defined below. */ + +static const JSClass location_array_class; /* Defined below. */ + +/* location_array_class is the class for array object, the elements of which + * correspond to the elements of session.history. + * + * session_class.history returns a location_array_class object, so define + * location_array_class and related routines before session_class. */ + +/* @location_array.getProperty */ +static JSBool +smjs_location_array_get_property(JSContext *ctx, JSObject *obj, jsid id, jsval *vp) +{ + struct session *ses; + int index; + struct location *loc; + + /* This can be called if @obj if not itself an instance of the + * appropriate class but has one in its prototype chain. Fail + * such calls. */ + if (!JS_InstanceOf(ctx, obj, (JSClass *) &location_array_class, NULL)) + return JS_FALSE; + + ses = JS_GetInstancePrivate(ctx, obj, + (JSClass *) &location_array_class, NULL); + if (!ses) return JS_FALSE; + + undef_to_jsval(ctx, vp); + + if (!JSID_IS_INT(id)) + return JS_FALSE; + + assert(ses); + if_assert_failed return JS_TRUE; + + if (!have_location(ses)) return JS_FALSE; + + index = JSID_TO_INT(id); + for (loc = cur_loc(ses); + loc != (struct location *) &ses->history.history; + loc = index > 0 ? loc->next : loc->prev) { + if (!index) { + JSObject *obj = smjs_get_view_state_object(&loc->vs); + + if (obj) object_to_jsval(ctx, vp, obj); + + return JS_TRUE; + } + + index += index > 0 ? -1 : 1; + } + + return JS_FALSE; +} + +/** Pointed to by location_array_class.finalize. SpiderMonkey automatically + * finalizes all objects before it frees the JSRuntime, so + * session.history_jsobject won't be left dangling. */ +static void +smjs_location_array_finalize(JSContext *ctx, JSObject *obj) +{ + struct session *ses; + + assert(JS_InstanceOf(ctx, obj, (JSClass *) &location_array_class, NULL)); + if_assert_failed return; + + ses = JS_GetInstancePrivate(ctx, obj, + (JSClass *) &location_array_class, NULL); + + if (!ses) return; /* already detached */ + + JS_SetPrivate(ctx, obj, NULL); /* perhaps not necessary */ + assert(ses->history_jsobject == obj); + if_assert_failed return; + ses->history_jsobject = NULL; +} + +static const JSClass location_array_class = { + "location_array", + JSCLASS_HAS_PRIVATE, /* struct session *; a weak reference */ + JS_PropertyStub, JS_PropertyStub, + smjs_location_array_get_property, JS_StrictPropertyStub, + JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, smjs_location_array_finalize, +}; + +/** Return an SMJS object through which scripts can access @a ses.history. If + * there already is such an object, return that; otherwise create a new one. + * The SMJS object holds only a weak reference to @a ses. */ +JSObject * +smjs_get_session_location_array_object(struct session *ses) +{ + JSObject *obj; + + if (ses->history_jsobject) return ses->history_jsobject; + + assert(smjs_ctx); + if_assert_failed return NULL; + + obj = JS_NewObject(smjs_ctx, (JSClass *) &location_array_class, NULL, NULL); + if (!obj) return NULL; + + /* Do this last, so that if any previous step fails, we can + * just forget the object and its finalizer won't attempt to + * access @ses. */ + if (JS_FALSE == JS_SetPrivate(smjs_ctx, obj, ses)) + return NULL; + + ses->history_jsobject = obj; + return obj; +} + +/* There is no smjs_detach_session_location_array_object because + * smjs_detach_session_object detaches both session.jsobject and + * session.history.js_object. */ + + +enum session_prop { + SESSION_VISITED, + SESSION_HISTORY, + SESSION_LOADING_URI, + SESSION_RELOADLEVEL, + SESSION_REDIRECT_CNT, + /* XXX: SESSION_DOC_VIEW, */ + /* XXX: SESSION_FRAMES, */ + SESSION_SEARCH_DIRECTION, + SESSION_KBDPREFIX, + SESSION_MARK_WAITING_FOR, + SESSION_EXIT_QUERY, + SESSION_INSERT_MODE, + SESSION_NAVIGATE_MODE, + SESSION_SEARCH_WORD, + SESSION_LAST_SEARCH_WORD, + /* XXX: SESSION_TYPE_QUERIES, */ +}; + +static const JSPropertySpec session_props[] = { + { "visited", SESSION_VISITED, JSPROP_ENUMERATE }, + { "history", SESSION_HISTORY, JSPROP_ENUMERATE | JSPROP_READONLY }, + { "loading_uri", SESSION_LOADING_URI, JSPROP_ENUMERATE | JSPROP_READONLY }, + { "reloadlevel", SESSION_RELOADLEVEL, JSPROP_ENUMERATE }, + { "redirect_cnt", SESSION_REDIRECT_CNT, JSPROP_ENUMERATE }, + /* XXX: { "doc_view", SESSION_DOC_VIEW, JSPROP_ENUMERATE | JSPROP_READONLY }, */ + /* XXX: { "frames", SESSION_FRAMES, JSPROP_ENUMERATE | JSPROP_READONLY }, */ + { "search_direction", SESSION_SEARCH_DIRECTION, JSPROP_ENUMERATE }, + { "kbdprefix", SESSION_KBDPREFIX, JSPROP_ENUMERATE }, + { "mark", SESSION_MARK_WAITING_FOR, JSPROP_ENUMERATE }, + { "exit_query", SESSION_EXIT_QUERY, JSPROP_ENUMERATE | JSPROP_READONLY }, + { "insert_mode", SESSION_INSERT_MODE, JSPROP_ENUMERATE }, + { "navigate_mode", SESSION_NAVIGATE_MODE, JSPROP_ENUMERATE }, + { "search_word", SESSION_SEARCH_WORD, JSPROP_ENUMERATE }, + { "last_search_word", SESSION_LAST_SEARCH_WORD, JSPROP_ENUMERATE }, + /* XXX: { "type_queries", SESSION_TYPE_QUERIES, JSPROP_ENUMERATE }, */ + { NULL } +}; + +/* @session_class.getProperty */ +static JSBool +session_get_property(JSContext *ctx, JSObject *obj, jsid id, jsval *vp) +{ + struct session *ses; + + /* This can be called if @obj if not itself an instance of the + * appropriate class but has one in its prototype chain. Fail + * such calls. */ + if (!JS_InstanceOf(ctx, obj, (JSClass *) &session_class, NULL)) + return JS_FALSE; + + ses = JS_GetInstancePrivate(ctx, obj, + (JSClass *) &session_class, NULL); + if (!ses) return JS_FALSE; + + if (!JSID_IS_INT(id)) { + /* Note: If we return JS_FALSE here, the object's methods do not + * work. */ + return JS_TRUE; + } + + /* XXX: Lock session here if it is ever changed to have an OBJECT_HEAD. */ + + undef_to_jsval(ctx, vp); + + switch (JSID_TO_INT(id)) { + case SESSION_VISITED: + int_to_jsval(ctx, vp, ses->status.visited); + + return JS_TRUE; + case SESSION_HISTORY: { + JSObject *obj = smjs_get_session_location_array_object(ses); + + if (obj) object_to_jsval(ctx, vp, obj); + + return JS_TRUE; + } + case SESSION_LOADING_URI: { + struct uri *uri = have_location(ses) ? cur_loc(ses)->vs.uri + : ses->loading_uri; + + if (uri) string_to_jsval(ctx, vp, struri(uri)); + + return JS_TRUE; + } + case SESSION_RELOADLEVEL: + int_to_jsval(ctx, vp, ses->reloadlevel); + + return JS_TRUE; + case SESSION_REDIRECT_CNT: + int_to_jsval(ctx, vp, ses->redirect_cnt); + + return JS_TRUE; + case SESSION_SEARCH_DIRECTION: + string_to_jsval(ctx, vp, ses->search_direction == 1 ? "down" + : "up"); + + return JS_TRUE; + case SESSION_KBDPREFIX: + int_to_jsval(ctx, vp, ses->kbdprefix.repeat_count); + + return JS_TRUE; + case SESSION_MARK_WAITING_FOR: + string_to_jsval(ctx, vp, ses->kbdprefix.mark == KP_MARK_NOTHING + ? "nothing" + : ses->kbdprefix.mark == KP_MARK_SET + ? "set" + : "goto"); + + return JS_TRUE; + case SESSION_EXIT_QUERY: + int_to_jsval(ctx, vp, ses->exit_query); + + return JS_TRUE; + case SESSION_INSERT_MODE: + string_to_jsval(ctx, vp, + ses->insert_mode == INSERT_MODE_LESS + ? "disabled" + : ses->insert_mode == INSERT_MODE_ON + ? "on" + : "off"); + + return JS_TRUE; + case SESSION_NAVIGATE_MODE: + string_to_jsval(ctx, vp, + ses->navigate_mode == NAVIGATE_CURSOR_ROUTING + ? "cursor" + : "linkwise"); + + return JS_TRUE; + case SESSION_SEARCH_WORD: + string_to_jsval(ctx, vp, ses->search_word); + + return JS_TRUE; + case SESSION_LAST_SEARCH_WORD: + string_to_jsval(ctx, vp, ses->last_search_word); + + return JS_TRUE; + default: + INTERNAL("Invalid ID %d in session_get_property().", + JSID_TO_INT(id)); + } + + return JS_FALSE; +} + +static JSBool +session_set_property(JSContext *ctx, JSObject *obj, jsid id, JSBool strict, jsval *vp) +{ + struct session *ses; + + /* This can be called if @obj if not itself an instance of the + * appropriate class but has one in its prototype chain. Fail + * such calls. */ + if (!JS_InstanceOf(ctx, obj, (JSClass *) &session_class, NULL)) + return JS_FALSE; + + ses = JS_GetInstancePrivate(ctx, obj, + (JSClass *) &session_class, NULL); + if (!ses) return JS_FALSE; + + if (!JSID_IS_INT(id)) + return JS_FALSE; + + switch (JSID_TO_INT(id)) { + case SESSION_VISITED: + ses->status.visited = atol(jsval_to_string(ctx, vp)); + + return JS_TRUE; + /* SESSION_HISTORY is RO */ + /* SESSION_LOADING_URI is RO */ + case SESSION_RELOADLEVEL: + ses->reloadlevel = atol(jsval_to_string(ctx, vp)); + + return JS_TRUE; + case SESSION_REDIRECT_CNT: + ses->redirect_cnt = atol(jsval_to_string(ctx, vp)); + + return JS_TRUE; + case SESSION_SEARCH_DIRECTION: { + unsigned char *str; + JSString *jsstr; + + jsstr = JS_ValueToString(ctx, *vp); + if (!jsstr) return JS_TRUE; + + str = JS_EncodeString(ctx, jsstr); + if (!str) return JS_TRUE; + + if (!strcmp(str, "up")) + ses->search_direction = -1; + else if (!strcmp(str, "down")) + ses->search_direction = 1; + else + return JS_FALSE; + + return JS_TRUE; + } + case SESSION_KBDPREFIX: + ses->kbdprefix.repeat_count = atol(jsval_to_string(ctx, vp)); + + return JS_TRUE; + case SESSION_MARK_WAITING_FOR: { + unsigned char *str; + JSString *jsstr; + + jsstr = JS_ValueToString(ctx, *vp); + if (!jsstr) return JS_TRUE; + + str = JS_EncodeString(ctx, jsstr); + if (!str) return JS_TRUE; + + if (!strcmp(str, "nothing")) + ses->kbdprefix.mark = KP_MARK_NOTHING; + else if (!strcmp(str, "set")) + ses->kbdprefix.mark = KP_MARK_SET; + else if (!strcmp(str, "goto")) + ses->kbdprefix.mark = KP_MARK_GOTO; + else + return JS_FALSE; + + return JS_TRUE; + } + /* SESSION_EXIT_QUERY is RO */ + case SESSION_INSERT_MODE: { + unsigned char *str; + JSString *jsstr; + + jsstr = JS_ValueToString(ctx, *vp); + if (!jsstr) return JS_TRUE; + + str = JS_EncodeString(ctx, jsstr); + if (!str) return JS_TRUE; + + if (!strcmp(str, "disabled")) + ses->insert_mode = INSERT_MODE_LESS; + else if (!strcmp(str, "on")) + ses->insert_mode = INSERT_MODE_ON; + else if (!strcmp(str, "off")) + ses->insert_mode = INSERT_MODE_OFF; + else + return JS_FALSE; + + return JS_TRUE; + } + case SESSION_NAVIGATE_MODE: { + unsigned char *str; + JSString *jsstr; + + jsstr = JS_ValueToString(ctx, *vp); + if (!jsstr) return JS_TRUE; + + str = JS_EncodeString(ctx, jsstr); + if (!str) return JS_TRUE; + + if (!strcmp(str, "cursor")) + ses->navigate_mode = NAVIGATE_CURSOR_ROUTING; + else if (!strcmp(str, "linkwise")) + ses->navigate_mode = NAVIGATE_LINKWISE; + else + return JS_FALSE; + + return JS_TRUE; + } + case SESSION_SEARCH_WORD: { + unsigned char *str; + JSString *jsstr; + + jsstr = JS_ValueToString(ctx, *vp); + if (!jsstr) return JS_TRUE; + + str = JS_EncodeString(ctx, jsstr); + if (!str) return JS_TRUE; + + mem_free_set(&ses->search_word, str); + + return JS_TRUE; + } + case SESSION_LAST_SEARCH_WORD: { + unsigned char *str; + JSString *jsstr; + + jsstr = JS_ValueToString(ctx, *vp); + if (!jsstr) return JS_TRUE; + + str = JS_EncodeString(ctx, jsstr); + if (!str) return JS_TRUE; + + mem_free_set(&ses->last_search_word, str); + + return JS_TRUE; + } + default: + INTERNAL("Invalid ID %d in session_set_property().", + JSID_TO_INT(id)); + } + + return JS_FALSE; +} + +/** Pointed to by session_class.construct. Create a new session (tab) + * and return the JSObject wrapper. */ +static JSBool +session_construct(JSContext *ctx, uintN argc, jsval *rval) +{ + jsval val; + jsval *argv = JS_ARGV(ctx, rval); + int bg; /* open new tab in background */ + struct session *ses; + JSObject *jsobj; + + if (argc > 1) { + return JS_TRUE; + } + + if (argc >= 1) { + bg = jsval_to_boolean(ctx, &argv[0]); + } + + if (!smjs_ses) return JS_FALSE; + + ses = init_session(smjs_ses, smjs_ses->tab->term, NULL, bg); + if (!ses) return JS_FALSE; + + jsobj = smjs_get_session_object(ses); + if (!jsobj) return JS_FALSE; + + object_to_jsval(ctx, &val, jsobj); + JS_SET_RVAL(ctx, rval, val); + + return JS_TRUE; +} + +/** Pointed to by session_class.finalize. SpiderMonkey automatically + * finalizes all objects before it frees the JSRuntime, so session.jsobject + * won't be left dangling. */ +static void +session_finalize(JSContext *ctx, JSObject *obj) +{ + struct session *ses; + + assert(JS_InstanceOf(ctx, obj, (JSClass *) &session_class, NULL)); + if_assert_failed return; + + ses = JS_GetInstancePrivate(ctx, obj, + (JSClass *) &session_class, NULL); + + if (!ses) return; /* already detached */ + + JS_SetPrivate(ctx, obj, NULL); /* perhaps not necessary */ + assert(ses->jsobject == obj); + if_assert_failed return; + ses->jsobject = NULL; +} + +static const JSClass session_class = { + "session", + JSCLASS_HAS_PRIVATE, /* struct session *; a weak reference */ + JS_PropertyStub, JS_PropertyStub, + session_get_property, session_set_property, + JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, session_finalize, + NULL, NULL, session_construct +}; + +/** Return an SMJS object through which scripts can access @a ses. + * If there already is such an object, return that; otherwise create a + * new one. The SMJS object holds only a weak reference to @a ses. */ +JSObject * +smjs_get_session_object(struct session *ses) +{ + JSObject *obj; + + if (ses->jsobject) return ses->jsobject; + + assert(smjs_ctx); + if_assert_failed return NULL; + + obj = JS_NewObject(smjs_ctx, (JSClass *) &session_class, NULL, NULL); + if (!obj) return NULL; + + if (JS_FALSE == JS_DefineProperties(smjs_ctx, obj, + (JSPropertySpec *) session_props)) + return NULL; + + /* Do this last, so that if any previous step fails, we can + * just forget the object and its finalizer won't attempt to + * access @ses. */ + if (JS_FALSE == JS_SetPrivate(smjs_ctx, obj, ses)) /* to @session_class */ + return NULL; + + ses->jsobject = obj; + return obj; +} + +/** Ensure that no JSObject contains the pointer @a ses. This is + * called from destroy_session before @a ses is freed. If a JSObject was + * previously attached to the session object, the object will remain in + * memory but it will no longer be able to access the session object. */ +void +smjs_detach_session_object(struct session *ses) +{ + assert(smjs_ctx); + assert(ses); + if_assert_failed return; + + if (ses->jsobject) { + assert(JS_GetInstancePrivate(smjs_ctx, ses->jsobject, + (JSClass *) &session_class, NULL) + == ses); + if_assert_failed {} + + JS_SetPrivate(smjs_ctx, ses->jsobject, NULL); + ses->jsobject = NULL; + } + + if (ses->history_jsobject) { + assert(JS_GetInstancePrivate(smjs_ctx, ses->history_jsobject, + (JSClass *) &location_array_class, + NULL) + == ses); + if_assert_failed {} + + JS_SetPrivate(smjs_ctx, ses->history_jsobject, NULL); + ses->history_jsobject = NULL; + } +} + +static JSBool +smjs_session_goto_url(JSContext *ctx, uintN argc, jsval *rval) +{ + jsval val; + struct delayed_open *deo; + struct uri *uri; + jsval *argv = JS_ARGV(ctx, rval); + JSString *jsstr; + unsigned char *url; + struct session *ses; + struct JSObject *this; + + if (argc != 1) return JS_FALSE; + + this = jsval_to_object(ctx, &argv[-1]); + if (!JS_InstanceOf(ctx, this, (JSClass *) &session_class, NULL)) + return JS_FALSE; + + ses = JS_GetInstancePrivate(ctx, this, + (JSClass *) &session_class, NULL); + if (!ses) return JS_FALSE; /* detached */ + + jsstr = JS_ValueToString(ctx, argv[0]); + if (!jsstr) return JS_FALSE; + + url = JS_EncodeString(ctx, jsstr); + if (!url) return JS_FALSE; + + uri = get_uri(url, 0); + if (!uri) return JS_FALSE; + + deo = mem_calloc(1, sizeof(*deo)); + if (!deo) { + done_uri(uri); + return JS_FALSE; + } + + deo->ses = ses; + deo->uri = get_uri_reference(uri); + /* deo->target = NULL; */ + register_bottom_half(delayed_goto_uri_frame, deo); + + undef_to_jsval(ctx, &val); + JS_SET_RVAL(ctx, rval, val); + + done_uri(uri); + + return JS_TRUE; +} + +static const spidermonkeyFunctionSpec session_funcs[] = { + { "goto", smjs_session_goto_url, 1 }, + { NULL } +}; + +void +smjs_init_session_interface(void) +{ + JSObject *jsobj; + + assert(smjs_ctx); + assert(smjs_global_object); + + smjs_session_object = + spidermonkey_InitClass(smjs_ctx, smjs_global_object, NULL, + (JSClass *) &session_class, session_construct, + 0, (JSPropertySpec *) session_props, + session_funcs, NULL, NULL); +} diff --git a/src/scripting/smjs/session_object.h b/src/scripting/smjs/session_object.h new file mode 100644 index 000000000..b022ef9d2 --- /dev/null +++ b/src/scripting/smjs/session_object.h @@ -0,0 +1,16 @@ +#ifndef EL__SCRIPTING_SMJS_SESSION_OBJECT_H +#define EL__SCRIPTING_SMJS_SESSION_OBJECT_H + +#include "ecmascript/spidermonkey-shared.h" + +struct session; +struct terminal; + +JSObject *smjs_get_session_object(struct session *ses); +JSObject *smjs_get_session_array_object(struct terminal *term); + +void smjs_detach_session_array_object(struct terminal *term); + +void smjs_init_session_interface(void); + +#endif diff --git a/src/scripting/smjs/smjs.h b/src/scripting/smjs/smjs.h index c4155a7fa..24d42caa8 100644 --- a/src/scripting/smjs/smjs.h +++ b/src/scripting/smjs/smjs.h @@ -3,6 +3,7 @@ struct module; struct cache_entry; +struct session; extern struct module smjs_scripting_module; @@ -12,4 +13,6 @@ extern struct module smjs_scripting_module; * scripting modules have pointers from there to their objects. */ void smjs_detach_cache_entry_object(struct cache_entry *cached); +void smjs_detach_session_object(struct session *ses); + #endif diff --git a/src/session/session.c b/src/session/session.c index e1f2d05b5..244a97ea6 100644 --- a/src/session/session.c +++ b/src/session/session.c @@ -33,6 +33,9 @@ #include "osdep/newwin.h" #include "protocol/protocol.h" #include "protocol/uri.h" +#ifdef CONFIG_SCRIPTING_SPIDERMONKEY +# include "scripting/smjs/smjs.h" +#endif #include "session/download.h" #include "session/history.h" #include "session/location.h" @@ -1178,6 +1181,9 @@ destroy_session(struct session *ses) assert(ses); if_assert_failed return; +#ifdef CONFIG_SCRIPTING_SPIDERMONKEY + smjs_detach_session_object(ses); +#endif destroy_downloads(ses); abort_loading(ses, 0); free_files(ses); diff --git a/src/session/session.h b/src/session/session.h index 41cbd7b2b..f4f679b9d 100644 --- a/src/session/session.h +++ b/src/session/session.h @@ -134,6 +134,10 @@ struct session { LIST_HEAD(struct session); +#ifdef CONFIG_SCRIPTING_SPIDERMONKEY + struct JSObject *jsobject; /* Instance of session_class */ +#endif + /** @name The vital session data * @{ */ @@ -148,6 +152,9 @@ struct session { * @{ */ struct ses_history history; +#ifdef CONFIG_SCRIPTING_SPIDERMONKEY + struct JSObject *history_jsobject; /* Instance of location_array_class */ +#endif /** @} @name The current document diff --git a/src/session/task.c b/src/session/task.c index 5c6d45cce..8d01bc9c2 100644 --- a/src/session/task.c +++ b/src/session/task.c @@ -702,14 +702,14 @@ delayed_goto_uri_frame(void *data) struct frame *frame; assert(deo); - frame = ses_find_frame(deo->ses, deo->target); + frame = deo->target ? ses_find_frame(deo->ses, deo->target) : NULL; if (frame) goto_uri_frame(deo->ses, deo->uri, frame->name, CACHE_MODE_NORMAL); else { goto_uri_frame(deo->ses, deo->uri, NULL, CACHE_MODE_NORMAL); } done_uri(deo->uri); - mem_free(deo->target); + mem_free_if(deo->target); mem_free(deo); }