mirror of
https://github.com/rkd77/elinks.git
synced 2024-11-04 08:17:17 -05:00
SMJS: add terminal object
Add terminal_class, which defines a JSObject wrapper for struct terminal. Add terminal_array_class, which defines a JSObject wrapper for accessing the "terminals" linked list of struct terminal. Add session_array_class, which defines a JSObject wrapper for accessing the tabs (sessions) of a terminal. Add pointers from struct terminal to the terminal_class object and the session_array_class object. Add terminal_props and terminal_get_property for terminal_class. Add terminal_finalize, which clears the pointers between a struct terminal and the JSObject wrapper in question. Add smjs_init_terminal_interface, which creates a terminal_array_class instance and assigns it to the "terminal" property on the "elinks" object. Call smjs_init_terminal_interface from smjs_init_elinks_object. Add smjs_get_terminal_object and smjs_get_session_array_object to get the wrapper objects for a given struct terminal, and add smjs_get_terminal_array_object to get a terminal_array_class object. Add smjs_detach_terminal_object and smjs_detach_session_array_object, which clear the pointers between a given struct terminal and its JSObject wrappers. Add terminal_array_get_property for terminal_array_class. Add session_array_get_property for session_array_class.
This commit is contained in:
parent
cdad9adf19
commit
c74ddb29c5
@ -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 session_object.o
|
||||
keybinding.o load_uri.o session_object.o terminal_object.o
|
||||
|
||||
include $(top_srcdir)/Makefile.lib
|
||||
|
@ -23,6 +23,7 @@
|
||||
#include "scripting/smjs/load_uri.h"
|
||||
#include "scripting/smjs/session_object.h"
|
||||
#include "scripting/smjs/view_state_object.h"
|
||||
#include "scripting/smjs/terminal_object.h"
|
||||
#include "session/location.h"
|
||||
#include "session/session.h"
|
||||
#include "session/task.h"
|
||||
@ -235,6 +236,7 @@ smjs_init_elinks_object(void)
|
||||
smjs_init_load_uri_interface();
|
||||
smjs_init_view_state_interface();
|
||||
smjs_init_session_interface();
|
||||
smjs_init_terminal_interface();
|
||||
}
|
||||
|
||||
/* If elinks.<method> is defined, call it with the given arguments,
|
||||
|
@ -574,6 +574,89 @@ smjs_detach_session_object(struct session *ses)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** Ensure that no JSObject contains the pointer @a ses. This is
|
||||
* called when the reference count of the session object *@a ses is
|
||||
* already 0 and it is about to be 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. */
|
||||
static JSBool
|
||||
session_array_get_property(JSContext *ctx, JSObject *obj, jsid id, jsval *vp)
|
||||
{
|
||||
JSObject *tabobj;
|
||||
struct terminal *term = JS_GetPrivate(ctx, obj);
|
||||
int index;
|
||||
struct window *tab;
|
||||
|
||||
undef_to_jsval(ctx, vp);
|
||||
|
||||
if (!JSID_IS_INT(id))
|
||||
return JS_FALSE;
|
||||
|
||||
assert(term);
|
||||
if_assert_failed return JS_TRUE;
|
||||
|
||||
index = JSID_TO_INT(id);
|
||||
foreach_tab (tab, term->windows) {
|
||||
if (!index) break;
|
||||
--index;
|
||||
}
|
||||
if ((void *) tab == (void *) &term->windows) return JS_FALSE;
|
||||
|
||||
tabobj = smjs_get_session_object(tab->data);
|
||||
if (tabobj) object_to_jsval(ctx, vp, tabobj);
|
||||
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
static const JSClass session_array_class = {
|
||||
"session_array",
|
||||
JSCLASS_HAS_PRIVATE, /* struct terminal *term; a weak reference */
|
||||
JS_PropertyStub, JS_PropertyStub,
|
||||
session_array_get_property, JS_StrictPropertyStub,
|
||||
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub
|
||||
};
|
||||
|
||||
JSObject *
|
||||
smjs_get_session_array_object(struct terminal *term)
|
||||
{
|
||||
JSObject *obj;
|
||||
|
||||
assert(smjs_ctx);
|
||||
if_assert_failed return NULL;
|
||||
|
||||
obj = JS_NewObject(smjs_ctx, (JSClass *) &session_array_class,
|
||||
NULL, NULL);
|
||||
if (!obj) return NULL;
|
||||
|
||||
if (JS_FALSE == JS_SetPrivate(smjs_ctx, obj, term))
|
||||
return NULL;
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/** Ensure that no JSObject contains the pointer @a term. This is called from
|
||||
* smjs_detach_terminal_object. If a JSObject was previously attached to the
|
||||
* terminal object, the object will remain in memory but it will no longer be
|
||||
* able to access the terminal object. */
|
||||
void
|
||||
smjs_detach_session_array_object(struct terminal *term)
|
||||
{
|
||||
assert(smjs_ctx);
|
||||
assert(term);
|
||||
if_assert_failed return;
|
||||
|
||||
if (!term->session_array_jsobject) return;
|
||||
|
||||
assert(JS_GetInstancePrivate(smjs_ctx, term->session_array_jsobject,
|
||||
(JSClass *) &session_array_class, NULL)
|
||||
== term);
|
||||
if_assert_failed {}
|
||||
|
||||
JS_SetPrivate(smjs_ctx, term->session_array_jsobject, NULL);
|
||||
term->session_array_jsobject = NULL;
|
||||
}
|
||||
|
||||
static JSBool
|
||||
smjs_session_goto_url(JSContext *ctx, uintN argc, jsval *rval)
|
||||
{
|
||||
|
@ -4,6 +4,7 @@
|
||||
struct module;
|
||||
struct cache_entry;
|
||||
struct session;
|
||||
struct terminal;
|
||||
|
||||
extern struct module smjs_scripting_module;
|
||||
|
||||
@ -15,4 +16,6 @@ void smjs_detach_cache_entry_object(struct cache_entry *cached);
|
||||
|
||||
void smjs_detach_session_object(struct session *ses);
|
||||
|
||||
void smjs_detach_terminal_object(struct terminal *term);
|
||||
|
||||
#endif
|
||||
|
214
src/scripting/smjs/terminal_object.c
Normal file
214
src/scripting/smjs/terminal_object.c
Normal file
@ -0,0 +1,214 @@
|
||||
/* Exports struct terminal to the world of ECMAScript */
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "elinks.h"
|
||||
|
||||
#include "ecmascript/spidermonkey/util.h"
|
||||
#include "scripting/smjs/core.h"
|
||||
#include "scripting/smjs/elinks_object.h"
|
||||
#include "scripting/smjs/session_object.h"
|
||||
#include "terminal/terminal.h"
|
||||
#include "util/error.h"
|
||||
#include "util/memory.h"
|
||||
#include "viewer/text/vs.h"
|
||||
|
||||
static const JSClass terminal_class; /* Defined below. */
|
||||
|
||||
enum terminal_prop {
|
||||
TERMINAL_TAB,
|
||||
};
|
||||
|
||||
static const JSPropertySpec terminal_props[] = {
|
||||
{ "tab", TERMINAL_TAB, JSPROP_ENUMERATE | JSPROP_READONLY },
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
/* @terminal_class.getProperty */
|
||||
static JSBool
|
||||
terminal_get_property(JSContext *ctx, JSObject *obj, jsid id, jsval *vp)
|
||||
{
|
||||
struct terminal *term;
|
||||
|
||||
/* 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 *) &terminal_class, NULL))
|
||||
return JS_FALSE;
|
||||
|
||||
term = JS_GetInstancePrivate(ctx, obj,
|
||||
(JSClass *) &terminal_class, NULL);
|
||||
if (!term) return JS_FALSE; /* already detached */
|
||||
|
||||
undef_to_jsval(ctx, vp);
|
||||
|
||||
if (!JSID_IS_INT(id)) return JS_FALSE;
|
||||
|
||||
switch (JSID_TO_INT(id)) {
|
||||
case TERMINAL_TAB: {
|
||||
JSObject *obj = smjs_get_session_array_object(term);
|
||||
|
||||
if (obj) object_to_jsval(ctx, vp, obj);
|
||||
|
||||
return JS_TRUE;
|
||||
}
|
||||
default:
|
||||
INTERNAL("Invalid ID %d in terminal_get_property().",
|
||||
JSID_TO_INT(id));
|
||||
}
|
||||
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
/** Pointed to by terminal_class.finalize. SpiderMonkey automatically
|
||||
* finalizes all objects before it frees the JSRuntime, so terminal.jsobject
|
||||
* won't be left dangling. */
|
||||
static void
|
||||
terminal_finalize(JSContext *ctx, JSObject *obj)
|
||||
{
|
||||
struct terminal *term;
|
||||
|
||||
assert(JS_InstanceOf(ctx, obj, (JSClass *) &terminal_class, NULL));
|
||||
if_assert_failed return;
|
||||
|
||||
term = JS_GetInstancePrivate(ctx, obj,
|
||||
(JSClass *) &terminal_class, NULL);
|
||||
|
||||
if (!term) return; /* already detached */
|
||||
|
||||
JS_SetPrivate(ctx, obj, NULL); /* perhaps not necessary */
|
||||
assert(term->jsobject == obj);
|
||||
if_assert_failed return;
|
||||
term->jsobject = NULL;
|
||||
}
|
||||
|
||||
static const JSClass terminal_class = {
|
||||
"terminal",
|
||||
JSCLASS_HAS_PRIVATE, /* struct terminal *; a weak refernce */
|
||||
JS_PropertyStub, JS_PropertyStub,
|
||||
terminal_get_property, JS_StrictPropertyStub,
|
||||
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, terminal_finalize
|
||||
};
|
||||
|
||||
/** Return an SMJS object through which scripts can access @a term.
|
||||
* If there already is such an object, return that; otherwise create a
|
||||
* new one. The SMJS object holds only a weak reference to @a term. */
|
||||
JSObject *
|
||||
smjs_get_terminal_object(struct terminal *term)
|
||||
{
|
||||
JSObject *obj;
|
||||
|
||||
if (term->jsobject) return term->jsobject;
|
||||
|
||||
assert(smjs_ctx);
|
||||
if_assert_failed return NULL;
|
||||
|
||||
obj = JS_NewObject(smjs_ctx, (JSClass *) &terminal_class, NULL, NULL);
|
||||
|
||||
if (!obj) return NULL;
|
||||
|
||||
if (JS_FALSE == JS_DefineProperties(smjs_ctx, obj,
|
||||
(JSPropertySpec *) terminal_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 @cached. */
|
||||
if (JS_FALSE == JS_SetPrivate(smjs_ctx, obj, term)) /* to @terminal_class */
|
||||
return NULL;
|
||||
|
||||
term->jsobject = obj;
|
||||
return obj;
|
||||
}
|
||||
|
||||
/** Ensure that no JSObject contains the pointer @a term. This is called from
|
||||
* destroy_terminal before @a term is freed. If a JSObject was previously
|
||||
* attached to the terminal object, the object will remain in memory but it
|
||||
* will no longer be able to access the terminal object. */
|
||||
void
|
||||
smjs_detach_terminal_object(struct terminal *term)
|
||||
{
|
||||
assert(smjs_ctx);
|
||||
assert(term);
|
||||
if_assert_failed return;
|
||||
|
||||
smjs_detach_session_array_object(term);
|
||||
|
||||
if (!term->jsobject) return;
|
||||
|
||||
assert(JS_GetInstancePrivate(smjs_ctx, term->jsobject,
|
||||
(JSClass *) &terminal_class, NULL)
|
||||
== term);
|
||||
if_assert_failed {}
|
||||
|
||||
JS_SetPrivate(smjs_ctx, term->jsobject, NULL);
|
||||
term->jsobject = NULL;
|
||||
}
|
||||
|
||||
|
||||
/* @terminal_array_class.getProperty */
|
||||
static JSBool
|
||||
terminal_array_get_property(JSContext *ctx, JSObject *obj, jsid id, jsval *vp)
|
||||
{
|
||||
int index;
|
||||
struct terminal *term;
|
||||
|
||||
undef_to_jsval(ctx, vp);
|
||||
|
||||
if (!JSID_IS_INT(id))
|
||||
return JS_FALSE;
|
||||
|
||||
index = JSID_TO_INT(id);
|
||||
foreach (term, terminals) {
|
||||
if (!index) break;
|
||||
--index;
|
||||
}
|
||||
if ((void *) term == (void *) &terminals) return JS_FALSE;
|
||||
|
||||
obj = smjs_get_terminal_object(term);
|
||||
if (obj) object_to_jsval(ctx, vp, obj);
|
||||
|
||||
return JS_TRUE;
|
||||
;
|
||||
}
|
||||
|
||||
static const JSClass terminal_array_class = {
|
||||
"terminal_array",
|
||||
0,
|
||||
JS_PropertyStub, JS_PropertyStub,
|
||||
terminal_array_get_property, JS_StrictPropertyStub,
|
||||
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub
|
||||
};
|
||||
|
||||
/** Return an SMJS object that scripts can use an array to get terminal
|
||||
* objects. */
|
||||
static JSObject *
|
||||
smjs_get_terminal_array_object(void)
|
||||
{
|
||||
assert(smjs_ctx);
|
||||
if_assert_failed return NULL;
|
||||
|
||||
return JS_NewObject(smjs_ctx, (JSClass *) &terminal_array_class,
|
||||
NULL, NULL);
|
||||
}
|
||||
|
||||
void
|
||||
smjs_init_terminal_interface(void)
|
||||
{
|
||||
jsval val;
|
||||
struct JSObject *obj;
|
||||
|
||||
if (!smjs_ctx || !smjs_elinks_object)
|
||||
return;
|
||||
|
||||
obj = smjs_get_terminal_array_object();
|
||||
if (!obj) return;
|
||||
|
||||
val = OBJECT_TO_JSVAL(obj);
|
||||
|
||||
JS_SetProperty(smjs_ctx, smjs_elinks_object, "terminal", &val);
|
||||
}
|
10
src/scripting/smjs/terminal_object.h
Normal file
10
src/scripting/smjs/terminal_object.h
Normal file
@ -0,0 +1,10 @@
|
||||
#ifndef EL__SCRIPTING_SMJS_TERMINAL_OBJECT_H
|
||||
#define EL__SCRIPTING_SMJS_TERMINAL_OBJECT_H
|
||||
|
||||
struct terminal;
|
||||
|
||||
JSObject *smjs_get_terminal_object(struct terminal *term);
|
||||
|
||||
void smjs_init_terminal_interface(void);
|
||||
|
||||
#endif
|
@ -23,6 +23,9 @@
|
||||
#include "main/select.h"
|
||||
#include "osdep/osdep.h"
|
||||
#include "osdep/signals.h"
|
||||
#ifdef CONFIG_SCRIPTING_SPIDERMONKEY
|
||||
# include "scripting/smjs/smjs.h"
|
||||
#endif
|
||||
#include "session/session.h"
|
||||
#include "terminal/draw.h"
|
||||
#include "terminal/event.h"
|
||||
@ -148,6 +151,9 @@ redraw_all_terminals(void)
|
||||
void
|
||||
destroy_terminal(struct terminal *term)
|
||||
{
|
||||
#ifdef CONFIG_SCRIPTING_SPIDERMONKEY
|
||||
smjs_detach_terminal_object(term);
|
||||
#endif
|
||||
#ifdef CONFIG_BOOKMARKS
|
||||
bookmark_auto_save_tabs(term);
|
||||
#endif
|
||||
|
@ -61,6 +61,11 @@ enum term_redrawing_state {
|
||||
struct terminal {
|
||||
LIST_HEAD(struct terminal); /*!< ::terminals is the sentinel. */
|
||||
|
||||
#ifdef CONFIG_SCRIPTING_SPIDERMONKEY
|
||||
struct JSObject *jsobject; /* Instance of terminal_class */
|
||||
struct JSObject *session_array_jsobject; /* Instance of session_array_class */
|
||||
#endif
|
||||
|
||||
/** This is (at least partially) a stack of all the windows living in
|
||||
* this terminal. A window can be wide range of stuff, from a menu box
|
||||
* through classical dialog window to a tab. See terminal/window.h for
|
||||
|
Loading…
Reference in New Issue
Block a user