From 6ab4eee456e478673fc25a7b378ecc596c0b28fa Mon Sep 17 00:00:00 2001 From: Miciah Dashiel Butler Masters Date: Wed, 3 May 2006 13:52:58 +0000 Subject: [PATCH] SMJS: Introduce elinks.globhist. Items can be accessed via elinks.globhist["http://www.foo.net/"]. The resulting object has title, url, and last_visit properties. last_visit is the seconds since the epoch. See comments in the code for why I don't provide a nicer interface for last_visit. --- src/scripting/smjs/Makefile | 2 +- src/scripting/smjs/elinks_object.c | 2 + src/scripting/smjs/globhist.c | 220 +++++++++++++++++++++++++++++ src/scripting/smjs/globhist.h | 6 + 4 files changed, 229 insertions(+), 1 deletion(-) create mode 100644 src/scripting/smjs/globhist.c create mode 100644 src/scripting/smjs/globhist.h diff --git a/src/scripting/smjs/Makefile b/src/scripting/smjs/Makefile index 4e8d5705f..96693b3b8 100644 --- a/src/scripting/smjs/Makefile +++ b/src/scripting/smjs/Makefile @@ -4,6 +4,6 @@ include $(top_builddir)/Makefile.config INCLUDES += $(SPIDERMONKEY_CFLAGS) OBJS = smjs.o core.o global_object.o hooks.o elinks_object.o cache_object.o \ - view_state_object.o bookmarks.o keybinding.o load_uri.o + view_state_object.o bookmarks.o globhist.o keybinding.o load_uri.o include $(top_srcdir)/Makefile.lib diff --git a/src/scripting/smjs/elinks_object.c b/src/scripting/smjs/elinks_object.c index b3ec84d98..1b9c72f40 100644 --- a/src/scripting/smjs/elinks_object.c +++ b/src/scripting/smjs/elinks_object.c @@ -16,6 +16,7 @@ #include "scripting/smjs/core.h" #include "scripting/smjs/elinks_object.h" #include "scripting/smjs/global_object.h" +#include "scripting/smjs/globhist.h" #include "scripting/smjs/keybinding.h" #include "scripting/smjs/load_uri.h" #include "session/location.h" @@ -130,6 +131,7 @@ smjs_init_elinks_object(void) smjs_elinks_object = smjs_get_elinks_object(); smjs_init_bookmarks_interface(); + smjs_init_globhist_interface(); smjs_init_keybinding_interface(); smjs_init_load_uri_interface(); } diff --git a/src/scripting/smjs/globhist.c b/src/scripting/smjs/globhist.c new file mode 100644 index 000000000..13b8db13d --- /dev/null +++ b/src/scripting/smjs/globhist.c @@ -0,0 +1,220 @@ +/* "elinks.globhist" */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "elinks.h" + +#include "globhist/globhist.h" +#include "ecmascript/spidermonkey/util.h" +#include "scripting/smjs/core.h" +#include "scripting/smjs/elinks_object.h" +#include "util/memory.h" + + +static void +smjs_globhist_item_finalize(JSContext *ctx, JSObject *obj) +{ + struct global_history_item *history_item = JS_GetPrivate(ctx, obj); + + if (history_item) object_unlock(history_item); +} + +enum smjs_globhist_item_prop { + GLOBHIST_TITLE, + GLOBHIST_URL, + GLOBHIST_LAST_VISIT, +}; + +static const JSPropertySpec smjs_globhist_item_props[] = { + { "title", GLOBHIST_TITLE, JSPROP_ENUMERATE }, + { "url", GLOBHIST_URL, JSPROP_ENUMERATE }, + { "last_visit", GLOBHIST_LAST_VISIT, JSPROP_ENUMERATE }, + { NULL } +}; + +static JSBool +smjs_globhist_item_get_property(JSContext *ctx, JSObject *obj, jsval id, + jsval *vp) +{ + struct global_history_item *history_item = JS_GetPrivate(ctx, obj); + + if (!history_item) return JS_FALSE; + + undef_to_jsval(ctx, vp); + + if (!JSVAL_IS_INT(id)) + return JS_FALSE; + + switch (JSVAL_TO_INT(id)) { + case GLOBHIST_TITLE: + *vp = STRING_TO_JSVAL(JS_NewStringCopyZ(smjs_ctx, + history_item->title)); + + return JS_TRUE; + case GLOBHIST_URL: + *vp = STRING_TO_JSVAL(JS_NewStringCopyZ(smjs_ctx, + history_item->url)); + + return JS_TRUE; + case GLOBHIST_LAST_VISIT: + /* TODO: I'd rather return a date object, but that introduces + * synchronisation issues: + * + * - How do we cause a change to that date object to affect + * the actual global history item? + * - How do we get a change to that global history item + * to affect all date objects? + * + * The biggest obstacle is that we have no way to trigger code + * when one messes with the date object. + * + * -- Miciah */ + /* XXX: Currently, ECMAScript gets seconds since the epoch. + * Since the Date object uses milliseconds since the epoch, + * I'd rather export that, but SpiderMonkey doesn't provide + * a suitable type. -- Miciah */ + JS_NewNumberValue(smjs_ctx, history_item->last_visit, vp); + + return JS_TRUE; + default: + INTERNAL("Invalid ID %d in globhist_get_property().", + JSVAL_TO_INT(id)); + } + + return JS_FALSE; +} + +static JSBool +smjs_globhist_item_set_property(JSContext *ctx, JSObject *obj, jsval id, jsval *vp) +{ + struct global_history_item *history_item = JS_GetPrivate(ctx, obj); + + if (!history_item) return JS_FALSE; + + if (!JSVAL_IS_INT(id)) + return JS_FALSE; + + switch (JSVAL_TO_INT(id)) { + case GLOBHIST_TITLE: { + JSString *jsstr = JS_ValueToString(smjs_ctx, *vp); + unsigned char *str = JS_GetStringBytes(jsstr); + + mem_free_set(&history_item->title, stracpy(str)); + + return JS_TRUE; + } + case GLOBHIST_URL: { + JSString *jsstr = JS_ValueToString(smjs_ctx, *vp); + unsigned char *str = JS_GetStringBytes(jsstr); + + mem_free_set(&history_item->url, stracpy(str)); + + return JS_TRUE; + } + case GLOBHIST_LAST_VISIT: { + uint32 seconds; + + JS_ValueToECMAUint32(smjs_ctx, *vp, &seconds); + history_item->last_visit = seconds; + + return JS_TRUE; + } + default: + INTERNAL("Invalid ID %d in bookmark_set_property().", + JSVAL_TO_INT(id)); + } + + return JS_FALSE; +} + +static const JSClass smjs_globhist_item_class = { + "global_history_item", + JSCLASS_HAS_PRIVATE, + JS_PropertyStub, JS_PropertyStub, + smjs_globhist_item_get_property, smjs_globhist_item_set_property, + JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, + smjs_globhist_item_finalize, +}; + +static JSObject * +smjs_get_globhist_item_object(struct global_history_item *history_item) +{ + JSObject *jsobj; + + jsobj = JS_NewObject(smjs_ctx, (JSClass *) &smjs_globhist_item_class, + NULL, NULL); + if (!jsobj + || JS_TRUE != JS_DefineProperties(smjs_ctx, jsobj, + (JSPropertySpec *) smjs_globhist_item_props) + || JS_TRUE != JS_SetPrivate(smjs_ctx, jsobj, history_item)) + return NULL; + + object_lock(history_item); + + return jsobj; +} + + +static JSBool +smjs_globhist_get_property(JSContext *ctx, JSObject *obj, jsval id, jsval *vp) +{ + JSObject *jsobj; + unsigned char *uri_string; + struct global_history_item *history_item; + + uri_string = JS_GetStringBytes(JS_ValueToString(ctx, id)); + if (!uri_string) goto ret_null; + + history_item = get_global_history_item(uri_string); + if (!history_item) goto ret_null; + + jsobj = smjs_get_globhist_item_object(history_item); + if (!jsobj) goto ret_null; + + *vp = OBJECT_TO_JSVAL(jsobj); + + return JS_TRUE; + +ret_null: + *vp = JSVAL_NULL; + + return JS_TRUE; +} + +static const JSClass smjs_globhist_class = { + "global_history", 0, + JS_PropertyStub, JS_PropertyStub, + smjs_globhist_get_property, JS_PropertyStub, + JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub, +}; + +static JSObject * +smjs_get_globhist_object(void) +{ + JSObject *globhist; + + globhist = JS_NewObject(smjs_ctx, (JSClass *) &smjs_globhist_class, + NULL, NULL); + if (!globhist) return NULL; + + return globhist; +} + +void +smjs_init_globhist_interface(void) +{ + jsval val; + struct JSObject *globhist; + + if (!smjs_ctx || !smjs_elinks_object) + return; + + globhist = smjs_get_globhist_object(); + if (!globhist) return; + + val = OBJECT_TO_JSVAL(globhist); + + JS_SetProperty(smjs_ctx, smjs_elinks_object, "globhist", &val); +} diff --git a/src/scripting/smjs/globhist.h b/src/scripting/smjs/globhist.h new file mode 100644 index 000000000..f8e14aee4 --- /dev/null +++ b/src/scripting/smjs/globhist.h @@ -0,0 +1,6 @@ +#ifndef EL__SCRIPTING_SMJS_GLOBHIST_H +#define EL__SCRIPTING_SMJS_GLOBHIST_H + +void smjs_init_globhist_interface(void); + +#endif