diff --git a/src/cookies/dialogs.c b/src/cookies/dialogs.c index 8cb25eb3..cd461503 100644 --- a/src/cookies/dialogs.c +++ b/src/cookies/dialogs.c @@ -448,7 +448,7 @@ push_add_server_button(struct dialog_data *dlg_data, struct widget_data *button) add_dlg_button(dlg, _("~OK", term), B_ENTER, ok_dialog, NULL); add_dlg_button(dlg, _("~Cancel", term), B_ESC, cancel_dialog, NULL); add_dlg_end(dlg, SERVER_WIDGETS_COUNT); - do_dialog(term, dlg, getml(dlg, text, NULL)); + do_dialog(term, dlg, getml(dlg, NULL)); return EVENT_PROCESSED; #undef SERVER_WIDGETS_COUNT diff --git a/src/globhist/globhist.c b/src/globhist/globhist.c index 39127c83..b4801f33 100644 --- a/src/globhist/globhist.c +++ b/src/globhist/globhist.c @@ -39,6 +39,7 @@ INIT_INPUT_HISTORY(global_history); +INIT_LIST_HEAD(global_history_reap_list); /* GUI stuff. Declared here because done_global_history() frees it. */ @@ -106,13 +107,29 @@ remove_item_from_global_history(struct global_history_item *history_item) } } +static void +reap_deleted_globhist_items(void) +{ + struct global_history_item *history_item, *next; + + foreachsafe(history_item, next, global_history_reap_list) { + if (!is_object_used(history_item)) { + del_from_list(history_item); + mem_free(history_item->title); + mem_free(history_item->url); + mem_free(history_item); + } + } +} + static void done_global_history_item(struct global_history_item *history_item) { done_listbox_item(&globhist_browser, history_item->box_item); - mem_free(history_item->title); - mem_free(history_item->url); - mem_free(history_item); + + history_item->box_item = NULL; + + add_to_list(global_history_reap_list, history_item); } void @@ -260,6 +277,8 @@ add_global_history_item(unsigned char *url, unsigned char *title, time_t vtime) if (!cap_global_history(max_globhist_items)) return; + reap_deleted_globhist_items(); + history_item = init_global_history_item(url, title, vtime); if (!history_item) return; @@ -394,6 +413,8 @@ free_global_history(void) while (!list_empty(global_history.entries)) delete_global_history_item(global_history.entries.next); + + reap_deleted_globhist_items(); } static enum evhook_status diff --git a/src/scripting/smjs/Makefile b/src/scripting/smjs/Makefile index 4e8d5705..96693b3b 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 b3ec84d9..1b9c72f4 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 00000000..13b8db13 --- /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 00000000..f8e14aee --- /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