diff --git a/src/document/renderer.c b/src/document/renderer.c index 4708430c5..e5534ed20 100644 --- a/src/document/renderer.c +++ b/src/document/renderer.c @@ -337,8 +337,8 @@ render_document(struct view_state *vs, struct document_view *doc_view, if (doc_view->session && doc_view->session->reloadlevel > CACHE_MODE_NORMAL) - while (vs->form_info_len) - mem_free_if(vs->form_info[--vs->form_info_len].value); + for (; vs->form_info_len > 0; vs->form_info_len--) + done_form_state(&vs->form_info[vs->form_info_len - 1]); shrink_memory(0); diff --git a/src/ecmascript/ecmascript.c b/src/ecmascript/ecmascript.c index c5f255b14..c822b5096 100644 --- a/src/ecmascript/ecmascript.c +++ b/src/ecmascript/ecmascript.c @@ -214,6 +214,23 @@ ecmascript_eval_boolback(struct ecmascript_interpreter *interpreter, return result; } +void ecmascript_detach_form_state(struct form_state *fs) +{ +#ifdef CONFIG_ECMASCRIPT_SEE + see_detach_form_state(fs); +#else + spidermonkey_detach_form_state(fs); +#endif +} + +void ecmascript_moved_form_state(struct form_state *fs) +{ +#ifdef CONFIG_ECMASCRIPT_SEE + see_moved_form_state(fs); +#else + spidermonkey_moved_form_state(fs); +#endif +} void ecmascript_reset_state(struct view_state *vs) @@ -221,14 +238,19 @@ ecmascript_reset_state(struct view_state *vs) struct form_view *fv; int i; + /* Normally, if vs->ecmascript == NULL, the associated + * ecmascript_obj pointers are also NULL. However, they might + * be non-NULL if the ECMAScript objects have been lazily + * created because of scripts running in sibling HTML frames. */ + for (i = 0; i < vs->form_info_len; i++) + ecmascript_detach_form_state(&vs->form_info[i]); + vs->ecmascript_fragile = 0; if (vs->ecmascript) ecmascript_put_interpreter(vs->ecmascript); foreach (fv, vs->forms) fv->ecmascript_obj = NULL; - for (i = 0; i < vs->form_info_len; i++) - vs->form_info[i].ecmascript_obj = NULL; vs->ecmascript = ecmascript_get_interpreter(vs); if (!vs->ecmascript) diff --git a/src/ecmascript/ecmascript.h b/src/ecmascript/ecmascript.h index e39bf4e16..ff93737f7 100644 --- a/src/ecmascript/ecmascript.h +++ b/src/ecmascript/ecmascript.h @@ -8,6 +8,7 @@ #include "main/module.h" #include "util/time.h" +struct form_state; struct string; struct terminal; struct uri; @@ -70,6 +71,9 @@ void ecmascript_free_urls(struct module *module); struct ecmascript_interpreter *ecmascript_get_interpreter(struct view_state*vs); void ecmascript_put_interpreter(struct ecmascript_interpreter *interpreter); +void ecmascript_detach_form_state(struct form_state *fs); +void ecmascript_moved_form_state(struct form_state *fs); + void ecmascript_reset_state(struct view_state *vs); void ecmascript_eval(struct ecmascript_interpreter *interpreter, struct string *code, struct string *ret); diff --git a/src/ecmascript/see.h b/src/ecmascript/see.h index 1c6439125..f22fe32e0 100644 --- a/src/ecmascript/see.h +++ b/src/ecmascript/see.h @@ -7,6 +7,9 @@ struct string; void *see_get_interpreter(struct ecmascript_interpreter *interpreter); void see_put_interpreter(struct ecmascript_interpreter *interpreter); +#define see_detach_form_state(fs) ((fs)->ecmascript_obj = NULL) +#define see_moved_form_state(fs) ((void) (fs)) + void see_eval(struct ecmascript_interpreter *interpreter, struct string *code, struct string *ret); unsigned char *see_eval_stringback(struct ecmascript_interpreter *interpreter, struct string *code); int see_eval_boolback(struct ecmascript_interpreter *interpreter, struct string *code); diff --git a/src/ecmascript/spidermonkey.h b/src/ecmascript/spidermonkey.h index 5f4f94415..8dfbb7b95 100644 --- a/src/ecmascript/spidermonkey.h +++ b/src/ecmascript/spidermonkey.h @@ -7,6 +7,9 @@ struct string; void *spidermonkey_get_interpreter(struct ecmascript_interpreter *interpreter); void spidermonkey_put_interpreter(struct ecmascript_interpreter *interpreter); +#define spidermonkey_detach_form_state(fs) ((fs)->ecmascript_obj = NULL) +#define spidermonkey_moved_form_state(fs) ((void) (fs)) + void spidermonkey_eval(struct ecmascript_interpreter *interpreter, struct string *code, struct string *ret); unsigned char *spidermonkey_eval_stringback(struct ecmascript_interpreter *interpreter, struct string *code); int spidermonkey_eval_boolback(struct ecmascript_interpreter *interpreter, struct string *code); diff --git a/src/viewer/text/form.c b/src/viewer/text/form.c index bc8ef2127..4437bd644 100644 --- a/src/viewer/text/form.c +++ b/src/viewer/text/form.c @@ -31,6 +31,7 @@ #include "document/forms.h" #include "document/html/parser.h" #include "document/view.h" +#include "ecmascript/ecmascript.h" #include "intl/gettext/libintl.h" #include "formhist/formhist.h" #include "mime/mime.h" @@ -239,11 +240,30 @@ find_form_state(struct document_view *doc_view, struct form_control *fc) if (n >= vs->form_info_len) { int nn = n + 1; +#ifdef CONFIG_ECMASCRIPT + const struct form_state *const old_form_info = vs->form_info; +#endif fs = mem_align_alloc(&vs->form_info, vs->form_info_len, nn, 0); if (!fs) return NULL; vs->form_info = fs; vs->form_info_len = nn; + +#ifdef CONFIG_ECMASCRIPT + /* TODO: Standard C does not allow this comparison; + * if the memory to which old_form_info pointed has + * been freed, then the value of the pointer itself is + * indeterminate. Fixing this would require changing + * mem_align_alloc to tell the caller whether it did + * realloc or not. */ + if (vs->form_info != old_form_info) { + /* vs->form_info[] was moved to a different address. + * Update all the ECMAScript objects that have + * pointers to its elements. */ + for (nn = 0; nn < vs->form_info_len; nn++) + ecmascript_moved_form_state(&vs->form_info[nn]); + } +#endif /* CONFIG_ECMASCRIPT */ } fs = &vs->form_info[n]; @@ -315,6 +335,17 @@ find_form_by_form_view(struct document *document, struct form_view *fv) return NULL; } +/** Free any data owned by @a fs, but not the struct form_state + * itself, because that is normally allocated as part of an array. + * @relates form_state */ +void +done_form_state(struct form_state *fs) +{ +#ifdef CONFIG_ECMASCRIPT + ecmascript_detach_form_state(fs); +#endif + mem_free_if(fs->value); +} int get_current_state(struct session *ses) diff --git a/src/viewer/text/form.h b/src/viewer/text/form.h index 79542b0eb..bc3bba1f6 100644 --- a/src/viewer/text/form.h +++ b/src/viewer/text/form.h @@ -113,6 +113,8 @@ struct form_view *find_form_view_in_vs(struct view_state *vs, int form_num); struct form_view *find_form_view(struct document_view *doc_view, struct form *form); struct form *find_form_by_form_view(struct document *document, struct form_view *fv); +void done_form_state(struct form_state *); + enum frame_event_status field_op(struct session *ses, struct document_view *doc_view, struct link *link, struct term_event *ev); void draw_form_entry(struct terminal *term, struct document_view *doc_view, struct link *link); diff --git a/src/viewer/text/vs.c b/src/viewer/text/vs.c index a7978dbee..46a683365 100644 --- a/src/viewer/text/vs.c +++ b/src/viewer/text/vs.c @@ -45,13 +45,13 @@ init_vs(struct view_state *vs, struct uri *uri, int plain) void destroy_vs(struct view_state *vs, int blast_ecmascript) { - int i; - - for (i = 0; i < vs->form_info_len; i++) - mem_free_if(vs->form_info[i].value); + /* form_state contains a pointer to form_view, so it's safest + * to delete the form_state first. */ + for (; vs->form_info_len > 0; vs->form_info_len--) + done_form_state(&vs->form_info[vs->form_info_len - 1]); + mem_free_set(&vs->form_info, NULL); if (vs->uri) done_uri(vs->uri); - mem_free_if(vs->form_info); free_list(vs->forms); #ifdef CONFIG_ECMASCRIPT if (blast_ecmascript && vs->ecmascript) @@ -114,6 +114,9 @@ copy_vs(struct view_state *dst, struct view_state *src) struct form_state *srcfs = &src->form_info[i]; struct form_state *dstfs = &dst->form_info[i]; +#ifdef CONFIG_ECMASCRIPT + dstfs->ecmascript_obj = NULL; +#endif if (srcfs->value) dstfs->value = stracpy(srcfs->value); /* XXX: This makes it O(nm). */