1
0
mirror of https://github.com/rkd77/elinks.git synced 2024-06-12 23:10:51 +00:00

Merge branch 'elinks-0.12' into elinks-0.13

This commit is contained in:
Kalle Olavi Niemitalo 2008-07-20 14:47:40 +03:00 committed by Kalle Olavi Niemitalo
commit bbee237ff0
43 changed files with 968 additions and 342 deletions

28
NEWS
View File

@ -22,8 +22,6 @@ Miscellaneous:
document.css.media.
* bug 638: Propagate the existence of $DISPLAY from slave terminals to
mailcap test commands.
* bug 698: Attach controls to the intended form even if it is
incorrectly nested in a table. (Was broken in 0.11.4.)
* bug 963: New option document.css.ignore_display_none.
* bug 977: Fixed crash when opening in new tab a non link with onclick
attribute.
@ -64,14 +62,38 @@ ELinks 0.12pre1.GIT now:
To be released as 0.12pre2, 0.12rc1, or even 0.12.0. This branch
generally also includes the bug fixes made in ELinks 0.11.4.GIT.
* bug 954, enhancement 952: Keep track of ECMAScript form and input
objects instead of constructing new ones on every access. When the
corresponding ELinks internal objects are destroyed, detach the
ECMAScript objects from them, to prevent crashes. (Bug 954 was
first added in ELinks 0.11.4, and the bug 620 fix in ELinks 0.12pre1
made crashes more likely.)
* critical bug 1029 in user SMJS: Prefer JS_CallFunctionValue over
JS_CallFunction, which can crash if given a closure.
* critical bug 1031: Use the same JSRuntime for both user SMJS and
scripts on web pages, to work around SpiderMonkey bug 378918.
* bug 698: Attach controls to the intended form even if it is
incorrectly nested in a table. (Was broken in 0.11.4.)
* minor bug 951: SpiderMonkey scripting objects used to prevent ELinks
from removing files from the memory cache
Bugs that should be removed from NEWS before the 0.12.0 release:
* critical: Fix crash after a tab was opened during reload. This was
triggered by the bug 620 fix in ELinks 0.12pre1.
* major bug 1026 in user SMJS: Protect the callback of elinks.load_uri
from the garbage collector. The elinks.load_uri method was added in
ELinks 0.12pre1.
* bug 955: Reset buttons no longer run FORM/@onsubmit, and
``harmless'' buttons no longer submit the form. ELinks 0.12pre1
was the first release that had these bugs.
* bug 1033: Fix memory leak in ECMAScript window.open. ELinks 0.12pre1
was the first release that had this bug.
* bug 1034: ``Content-Encoding: deflate'' allows a zlib header as
specified in RFC 2616.
* Global ECMAScript functions alert, open, and setTimeout again work
with SEE. ELinks 0.12pre1 was the first release that supported SEE
at all.
ELinks 0.12pre1:
----------------
@ -237,6 +259,8 @@ To be released as 0.11.5.
* critical bug 1027 in user SMJS: make elinks.keymaps treat null and
"none" as equivalent actions, avoiding a segfault
* critical bug 1030: an assertion used to fail in the search dialog
on systems that lack a usable <regex.h>
* major bug 503: various fixes in parsing and updating of elinks.conf
* build bug 1021: fixed uninitialized variable in http_got_header

View File

@ -1408,6 +1408,25 @@ AC_SUBST(LIBDIR)
EL_LOG_CONFIG(CONFDIR, [System configuration directory], [])
EL_LOG_CONFIG(LOCALEDIR, [Locale catalogs directory], [])
AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include <stddef.h>
]], [[#if !defined(__TINYC__) || !defined(alloca)
#error not tcc
#endif ]])],[cf_result=yes],[cf_result=no])
if test "$cf_result" = "yes"; then
AC_DEFINE([HAVE_ALLOCA])
AC_DEFINE([_ALLOCA_H], [1], [Define as 1 if you are using Tiny C Compiler
with the GNU C Library, and <alloca.h> of glibc would otherwise
override the alloca macro defined in <stddef.h> of TCC.
If <alloca.h> of glibc sees the _ALLOCA_H macro, it assumes
it has already been included, and does not redefine alloca.
This might not work in future glibc versions though, because
the names of the #include guard macros are not documented.
The incompatibility has been reported to the tinycc-devel
mailing list on 2008-07-14. If a future version of TCC provides
an <alloca.h> of its own, this hack won't be needed.])
fi
# ===================================================================
# A little fine tuning of gcc specific options (continued)
# ===================================================================

View File

@ -131,7 +131,7 @@ yet if it does not work in the Mozilla browsers neither ;-).
Now, I would still like NJS or a new JS engine from scratch...
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
...and you don't fear some coding? That's fine then! ELinks is in no way tied
\...and you don't fear some coding? That's fine then! ELinks is in no way tied
to SpiderMonkey, in fact the ECMAScript support was carefully implemented so
that there are no SpiderMonkey references outside of
`src/ecmascript/spidermonkey.*`. If you want to implement an alternative

View File

@ -20,8 +20,7 @@ Note that if you don't use dietlibc, you definitively want to add `-Os` or
`-O2` to `CFLAGS`; GCC 2.95 does not know `-Os`, and some say `-O2` gives
smaller executables even for GCC 3.x.
[NOTE]
.Warning
[TIP]
===============================================================================
If you use these `CFLAGS` on Cygwin and you get unresolved symbols (`htons` and
suite in particular), try removing `-fno-inline` parameter.
@ -38,8 +37,8 @@ $ ./configure --disable-ipv6 --disable-backtrace --disable-nls \
You can disable bookmarks, globhist and more, too, if you want to.
[NOTE]
.Notes
[TIP]
.Other configure options that can reduce the size
===============================================================================
- \--disable-backtrace disables internal backtrace code.
- \--disable-nls disables i18n support.

View File

@ -338,8 +338,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);

View File

@ -8,6 +8,16 @@ SUBDIRS-$(CONFIG_ECMASCRIPT_SMJS) += spidermonkey
OBJS-$(CONFIG_ECMASCRIPT_SEE) += see.o
OBJS-$(CONFIG_ECMASCRIPT_SMJS) += spidermonkey.o
ifeq ($(CONFIG_ECMASCRIPT_SMJS), yes)
CONFIG_ANY_SPIDERMONKEY = yes
else ifeq ($(CONFIG_SCRIPTING_SPIDERMONKEY), yes)
CONFIG_ANY_SPIDERMONKEY = yes
else
CONFIG_ANY_SPIDERMONKEY = no
endif
OBJS-$(CONFIG_ANY_SPIDERMONKEY) += spidermonkey-shared.o
OBJS = ecmascript.o
include $(top_srcdir)/Makefile.lib

View File

@ -214,6 +214,33 @@ ecmascript_eval_boolback(struct ecmascript_interpreter *interpreter,
return result;
}
void
ecmascript_detach_form_view(struct form_view *fv)
{
#ifdef CONFIG_ECMASCRIPT_SEE
see_detach_form_view(fv);
#else
spidermonkey_detach_form_view(fv);
#endif
}
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,15 +248,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. */
foreach (fv, vs->forms)
ecmascript_detach_form_view(fv);
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)
vs->ecmascript_fragile = 1;

View File

@ -8,6 +8,8 @@
#include "main/module.h"
#include "util/time.h"
struct form_state;
struct form_view;
struct string;
struct terminal;
struct uri;
@ -70,6 +72,10 @@ 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_view(struct form_view *fv);
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);

View File

@ -7,6 +7,10 @@ struct string;
void *see_get_interpreter(struct ecmascript_interpreter *interpreter);
void see_put_interpreter(struct ecmascript_interpreter *interpreter);
void see_detach_form_view(struct form_view *fv);
void see_detach_form_state(struct form_state *fs);
void see_moved_form_state(struct form_state *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);

View File

@ -22,6 +22,7 @@
#include "document/forms.h"
#include "document/view.h"
#include "ecmascript/ecmascript.h"
#include "ecmascript/see.h"
#include "ecmascript/see/checktype.h"
#include "ecmascript/see/document.h"
#include "ecmascript/see/form.h"
@ -56,8 +57,9 @@ static void js_input_focus(struct SEE_interpreter *, struct SEE_object *, struct
static void js_input_select(struct SEE_interpreter *, struct SEE_object *, struct SEE_object *, int, struct SEE_value **, struct SEE_value *);
static int input_canput(struct SEE_interpreter *, struct SEE_object *, struct SEE_string *);
static int input_hasproperty(struct SEE_interpreter *, struct SEE_object *, struct SEE_string *);
static struct js_input *js_get_input_object(struct SEE_interpreter *, struct js_form *, int);
static struct js_input *js_get_form_control_object(struct SEE_interpreter *, struct js_form *, enum form_type, int);
static struct js_input *js_get_input_object(struct SEE_interpreter *, struct js_form *, struct form_state *);
static void input_finalize(struct SEE_interpreter *, void *, void *);
static struct js_input *js_get_form_control_object(struct SEE_interpreter *, struct js_form *, enum form_type, struct form_state *);
static void js_form_elems_item(struct SEE_interpreter *, struct SEE_object *, struct SEE_object *, int, struct SEE_value **, struct SEE_value *);
static void js_form_elems_namedItem(struct SEE_interpreter *, struct SEE_object *, struct SEE_object *, int, struct SEE_value **, struct SEE_value *);
@ -75,6 +77,7 @@ static int form_canput(struct SEE_interpreter *, struct SEE_object *, struct SEE
static int form_hasproperty(struct SEE_interpreter *, struct SEE_object *, struct SEE_string *);
static void js_form_reset(struct SEE_interpreter *, struct SEE_object *, struct SEE_object *, int, struct SEE_value **, struct SEE_value *);
static void js_form_submit(struct SEE_interpreter *, struct SEE_object *, struct SEE_object *, int, struct SEE_value **, struct SEE_value *);
static void form_finalize(struct SEE_interpreter *, void *, void *);
struct SEE_objectclass js_input_object_class = {
@ -140,7 +143,7 @@ struct js_input {
struct SEE_object *click;
struct SEE_object *focus;
struct SEE_object *select;
int form_number;
struct form_state *fs;
};
struct js_forms_object {
@ -159,12 +162,21 @@ struct js_form_elems {
static inline struct form_state *
form_state_of_js_input(struct view_state *vs, const struct js_input *input)
form_state_of_js_input(struct SEE_interpreter *interp,
const struct js_input *input)
{
assert(input->form_number >= 0);
assert(input->form_number < vs->form_info_len);
if_assert_failed return NULL;
return &vs->form_info[input->form_number];
struct form_state *fs = input->fs;
if (!fs)
SEE_error_throw(interp, interp->Error,
"Input field has been destroyed");
assert(fs->ecmascript_obj == input);
if_assert_failed
SEE_error_throw(interp, interp->Error,
"Internal corruption");
return fs;
}
static void
@ -177,7 +189,7 @@ input_get(struct SEE_interpreter *interp, struct SEE_object *o,
struct document *document = doc_view->document;
struct js_input *input = (struct js_input *)o;
struct js_form *parent = input->parent;
struct form_state *fs = form_state_of_js_input(vs, input);
struct form_state *fs = form_state_of_js_input(interp, input);
struct form_control *fc = find_form_control(document, fs);
int linknum;
struct link *link = NULL;
@ -280,7 +292,7 @@ input_put(struct SEE_interpreter *interp, struct SEE_object *o,
struct document_view *doc_view = vs->doc_view;
struct document *document = doc_view->document;
struct js_input *input = (struct js_input *)o;
struct form_state *fs = form_state_of_js_input(vs, input);
struct form_state *fs = form_state_of_js_input(interp, input);
struct form_control *fc = find_form_control(document, fs);
int linknum;
struct link *link = NULL;
@ -380,7 +392,7 @@ js_input_click(struct SEE_interpreter *interp, struct SEE_object *self,
struct js_input *input = (
see_check_class(interp, thisobj, &js_input_object_class),
(struct js_input *)thisobj);
struct form_state *fs = form_state_of_js_input(vs, input);
struct form_state *fs = form_state_of_js_input(interp, input);
struct form_control *fc;
int linknum;
@ -415,7 +427,7 @@ js_input_focus(struct SEE_interpreter *interp, struct SEE_object *self,
struct js_input *input = (
see_check_class(interp, thisobj, &js_input_object_class),
(struct js_input *)thisobj);
struct form_state *fs = form_state_of_js_input(vs, input);
struct form_state *fs = form_state_of_js_input(interp, input);
struct form_control *fc;
int linknum;
@ -460,19 +472,23 @@ input_hasproperty(struct SEE_interpreter *interp, struct SEE_object *o,
}
static struct js_input *
js_get_input_object(struct SEE_interpreter *interp, struct js_form *jsform, int num)
js_get_input_object(struct SEE_interpreter *interp, struct js_form *jsform,
struct form_state *fs)
{
struct js_input *jsinput;
struct js_input *jsinput = fs->ecmascript_obj;
if (jsinput) {
assert(jsinput->fs == fs);
if_assert_failed return NULL;
return jsinput;
}
#if 0
if (fs->ecmascript_obj)
return fs->ecmascript_obj;
#endif
/* jsform ('form') is input's parent */
/* FIXME: That is NOT correct since the real containing element
* should be its parent, but gimme DOM first. --pasky */
jsinput = SEE_NEW(interp, struct js_input);
jsinput = SEE_NEW_FINALIZE(interp, struct js_input,
input_finalize, NULL);
jsinput->object.objectclass = &js_input_object_class;
jsinput->object.Prototype = NULL;
@ -482,14 +498,15 @@ js_get_input_object(struct SEE_interpreter *interp, struct js_form *jsform, int
jsinput->focus = SEE_cfunction_make(interp, js_input_focus, s_focus, 0);
jsinput->select = SEE_cfunction_make(interp, js_input_select, s_select, 0);
jsinput->form_number = num;
jsinput->parent = jsform;
jsinput->fs = fs;
fs->ecmascript_obj = jsinput;
return jsinput;
}
static struct js_input *
js_get_form_control_object(struct SEE_interpreter *interp, struct js_form *jsform,
enum form_type type, int num)
enum form_type type, struct form_state *fs)
{
switch (type) {
case FC_TEXT:
@ -503,7 +520,7 @@ js_get_form_control_object(struct SEE_interpreter *interp, struct js_form *jsfor
case FC_BUTTON:
case FC_HIDDEN:
case FC_SELECT:
return js_get_input_object(interp, jsform, num);
return js_get_input_object(interp, jsform, fs);
case FC_TEXTAREA:
/* TODO */
@ -515,8 +532,90 @@ js_get_form_control_object(struct SEE_interpreter *interp, struct js_form *jsfor
}
}
static void
input_finalize(struct SEE_interpreter *interp, void *jsinput_void, void *dummy)
{
struct js_input *jsinput = jsinput_void;
struct form_state *fs = jsinput->fs;
if (fs) {
/* Reset jsinput->fs in case some ELinks code uses it
* even after this finalizer has run. Such use should
* not be possible but the explanation is somewhat
* complex so it seems safest to do this.
*
* Unlike SpiderMonkey, Boehm's GC allows a finalizer
* to resurrect the object by making something point
* to it. And it's a conservative GC so it can see
* such a pointer where none actually exists. However
* it also implicitly unregisters the finalizer before
* calling it, so the object becomes just a chunk of
* memory that nothing really uses, even if the GC
* never realizes it is garbage. */
jsinput->fs = NULL;
/* If this assertion fails, leave fs->ecmascript_obj
* unchanged, because it may point to a different
* structure whose js_input.fs pointer will later have
* to be updated to avoid crashes.
*
* If the assertion fails and we leave jsinput->fs
* unchanged, and something then deletes fs,
* jsinput->fs becomes a dangling pointer because fs
* does not know about jsinput. So that's why the
* assertion comes after the jsinput->fs assignment
* above. */
assert(fs->ecmascript_obj == jsinput);
if_assert_failed return;
fs->ecmascript_obj = NULL;
}
}
void
see_detach_form_state(struct form_state *fs)
{
struct js_input *jsinput = fs->ecmascript_obj;
if (jsinput) {
/* If this assertion fails, it is not clear whether
* jsinput->fs should be reset; crashes seem possible
* either way. Resetting it is easiest. */
assert(jsinput->fs == fs);
if_assert_failed {}
jsinput->fs = NULL;
fs->ecmascript_obj = NULL;
}
}
void
see_moved_form_state(struct form_state *fs)
{
struct js_input *jsinput = fs->ecmascript_obj;
if (jsinput)
jsinput->fs = fs;
}
static inline struct form_view *
form_view_of_js_form(struct SEE_interpreter *interp,
const struct js_form *jsform)
{
struct form_view *fv = jsform->fv;
if (!fv)
SEE_error_throw(interp, interp->Error,
"Form has been destroyed");
assert(fv->ecmascript_obj == jsform);
if_assert_failed
SEE_error_throw(interp, interp->Error,
"Internal corruption");
return fv;
}
static void
js_form_elems_item(struct SEE_interpreter *interp, struct SEE_object *self,
@ -531,7 +630,7 @@ js_form_elems_item(struct SEE_interpreter *interp, struct SEE_object *self,
see_check_class(interp, thisobj, &js_form_elems_class),
(struct js_form_elems *)thisobj);
struct js_form *parent_form = jsfe->parent;
struct form_view *fv = parent_form->fv;
struct form_view *fv = form_view_of_js_form(interp, parent_form);
struct form *form = find_form_by_form_view(document, fv);
struct form_control *fc;
unsigned char *string;
@ -553,7 +652,7 @@ js_form_elems_item(struct SEE_interpreter *interp, struct SEE_object *self,
struct form_state *fs = find_form_state(doc_view, fc);
if (fs) {
struct js_input *fcobj = js_get_form_control_object(interp, parent_form, fc->type, fc->g_ctrl_num);
struct js_input *fcobj = js_get_form_control_object(interp, parent_form, fc->type, fs);
if (fcobj)
SEE_SET_OBJECT(res, (struct SEE_object *)fcobj);
@ -577,7 +676,7 @@ js_form_elems_namedItem(struct SEE_interpreter *interp, struct SEE_object *self,
see_check_class(interp, thisobj, &js_form_elems_class),
(struct js_form_elems *)thisobj);
struct js_form *parent_form = jsfe->parent;
struct form_view *fv = parent_form->fv;
struct form_view *fv = form_view_of_js_form(interp, parent_form);
struct form *form = find_form_by_form_view(document, fv);
struct form_control *fc;
unsigned char *string;
@ -594,7 +693,7 @@ js_form_elems_namedItem(struct SEE_interpreter *interp, struct SEE_object *self,
struct form_state *fs = find_form_state(doc_view, fc);
if (fs) {
struct js_input *fcobj = js_get_form_control_object(interp, parent_form, fc->type, fc->g_ctrl_num);
struct js_input *fcobj = js_get_form_control_object(interp, parent_form, fc->type, fs);
if (fcobj)
SEE_SET_OBJECT(res, (struct SEE_object *)fcobj);
@ -615,7 +714,7 @@ form_elems_get(struct SEE_interpreter *interp, struct SEE_object *o,
struct document *document = doc_view->document;
struct js_form_elems *jsfe = (struct js_form_elems *)o;
struct js_form *parent_form = jsfe->parent;
struct form_view *fv = parent_form->fv;
struct form_view *fv = form_view_of_js_form(interp, parent_form);
struct form *form = find_form_by_form_view(document, fv);
if (p == s_length) {
@ -783,7 +882,7 @@ form_get(struct SEE_interpreter *interp, struct SEE_object *o,
struct view_state *vs = g->win->vs;
struct document_view *doc_view = vs->doc_view;
struct js_form *js_form = (struct js_form *)o;
struct form_view *fv = js_form->fv;
struct form_view *fv = form_view_of_js_form(interp, js_form);
struct form *form = find_form_by_form_view(doc_view->document, fv);
struct SEE_string *str;
@ -857,7 +956,7 @@ form_get(struct SEE_interpreter *interp, struct SEE_object *o,
continue;
fs = find_form_state(doc_view, fc);
if (fs) {
fcobj = js_get_form_control_object(interp, js_form, fc->type, fc->g_ctrl_num);
fcobj = js_get_form_control_object(interp, js_form, fc->type, fs);
if (fcobj)
SEE_SET_OBJECT(res, (struct SEE_object *)fcobj);
@ -876,7 +975,7 @@ form_put(struct SEE_interpreter *interp, struct SEE_object *o,
struct view_state *vs = g->win->vs;
struct document_view *doc_view = vs->doc_view;
struct js_form *js_form = (struct js_form *)o;
struct form_view *fv = js_form->fv;
struct form_view *fv = form_view_of_js_form(interp, js_form);
struct form *form = find_form_by_form_view(doc_view->document, fv);
unsigned char *string = see_value_to_unsigned_char(interp, val);
@ -938,7 +1037,7 @@ js_form_reset(struct SEE_interpreter *interp, struct SEE_object *self,
struct js_form *js_form = (
see_check_class(interp, thisobj, &js_form_class),
(struct js_form *)thisobj);
struct form_view *fv = js_form->fv;
struct form_view *fv = form_view_of_js_form(interp, js_form);
struct form *form = find_form_by_form_view(doc_view->document, fv);
assert(form);
@ -960,7 +1059,7 @@ js_form_submit(struct SEE_interpreter *interp, struct SEE_object *self,
struct js_form *js_form = (
see_check_class(interp, thisobj, &js_form_class),
(struct js_form *)thisobj);
struct form_view *fv = js_form->fv;
struct form_view *fv = form_view_of_js_form(interp, js_form);
struct form *form = find_form_by_form_view(doc_view->document, fv);
assert(form);
@ -971,27 +1070,89 @@ js_form_submit(struct SEE_interpreter *interp, struct SEE_object *self,
struct js_form *js_get_form_object(struct SEE_interpreter *interp,
struct js_document_object *doc, struct form_view *fv)
{
struct js_form *js_form;
struct js_form *js_form = fv->ecmascript_obj;
if (js_form) {
assert(js_form->fv == fv);
if_assert_failed return NULL;
return js_form;
}
#if 0
if (fv->ecmascript_obj)
return fv->ecmascript_obj;
#endif
/* jsdoc ('document') is fv's parent */
/* FIXME: That is NOT correct since the real containing element
* should be its parent, but gimme DOM first. --pasky */
js_form = SEE_NEW(interp, struct js_form);
js_form = SEE_NEW_FINALIZE(interp, struct js_form,
form_finalize, NULL);
js_form->object.objectclass = &js_form_class;
js_form->object.Prototype = NULL; /* TODO: use prototype for form */
js_form->parent = doc;
js_form->reset = SEE_cfunction_make(interp, js_form_reset, s_reset, 0);
js_form->submit = SEE_cfunction_make(interp, js_form_submit, s_submit, 0);
js_form->fv = fv;
js_form->fv = fv;
fv->ecmascript_obj = js_form;
return js_form;
}
static void
form_finalize(struct SEE_interpreter *interp, void *jsform_void, void *dummy)
{
struct js_form *jsform = jsform_void;
struct form_view *fv = jsform->fv;
if (fv) {
/* Reset jsform->fv in case some ELinks code uses it
* even after this finalizer has run. Such use should
* not be possible but the explanation is somewhat
* complex so it seems safest to do this.
*
* Unlike SpiderMonkey, Boehm's GC allows a finalizer
* to resurrect the object by making something point
* to it. And it's a conservative GC so it can see
* such a pointer where none actually exists. However
* it also implicitly unregisters the finalizer before
* calling it, so the object becomes just a chunk of
* memory that nothing really uses, even if the GC
* never realizes it is garbage. */
jsform->fv = NULL;
/* If this assertion fails, leave fv->ecmascript_obj
* unchanged, because it may point to a different
* structure whose js_form.fv pointer will later have
* to be updated to avoid crashes.
*
* If the assertion fails and we leave jsform->fv
* unchanged, and something then deletes fv,
* jsform->fv becomes a dangling pointer because fv
* does not know about jsform. So that's why the
* assertion comes after the jsform->fv assignment
* above. */
assert(fv->ecmascript_obj == jsform);
if_assert_failed return;
fv->ecmascript_obj = NULL;
}
}
void
see_detach_form_view(struct form_view *fv)
{
struct js_form *jsform = fv->ecmascript_obj;
if (jsform) {
/* If this assertion fails, it is not clear whether
* jsform->fv should be reset; crashes seem possible
* either way. Resetting it is easiest. */
assert(jsform->fv == fv);
if_assert_failed {}
jsform->fv = NULL;
fv->ecmascript_obj = NULL;
}
}
void
init_js_forms_object(struct ecmascript_interpreter *interpreter)
{

View File

@ -211,7 +211,9 @@ js_window_alert(struct SEE_interpreter *interp, struct SEE_object *self,
struct view_state *vs = win->vs;
unsigned char *string;
see_check_class(interp, thisobj, &js_window_object_class);
/* Do not check thisobj->objectclass. ELinks sets this
* function as a property of both the window object and the
* global object, so thisobj may validly refer to either. */
SEE_SET_BOOLEAN(res, 1);
if (argc < 1)
@ -240,7 +242,7 @@ js_window_open(struct SEE_interpreter *interp, struct SEE_object *self,
struct document_view *doc_view = vs->doc_view;
struct session *ses = doc_view->session;
unsigned char *frame = "";
unsigned char *url;
unsigned char *url, *url2;
struct uri *uri;
struct SEE_value url_value;
#if 0
@ -248,7 +250,9 @@ js_window_open(struct SEE_interpreter *interp, struct SEE_object *self,
static int ratelimit_count;
#endif
see_check_class(interp, thisobj, &js_window_object_class);
/* Do not check thisobj->objectclass. ELinks sets this
* function as a property of both the window object and the
* global object, so thisobj may validly refer to either. */
SEE_SET_OBJECT(res, (struct SEE_object *)win);
if (get_opt_bool("ecmascript.block_window_opening", ses)) {
@ -291,10 +295,11 @@ js_window_open(struct SEE_interpreter *interp, struct SEE_object *self,
}
/* TODO: Support for window naming and perhaps some window features? */
url = join_urls(doc_view->document->uri, url);
if (!url) return;
uri = get_uri(url, 0);
url2 = join_urls(doc_view->document->uri, url);
mem_free(url);
if (!url2) return;
uri = get_uri(url2, 0);
mem_free(url2);
if (!uri) return;
if (*frame && strcasecmp(frame, "_blank")) {
@ -341,7 +346,9 @@ js_setTimeout(struct SEE_interpreter *interp, struct SEE_object *self,
unsigned char *code;
int timeout;
see_check_class(interp, thisobj, &js_window_object_class);
/* Do not check thisobj->objectclass. ELinks sets this
* function as a property of both the window object and the
* global object, so thisobj may validly refer to either. */
if (argc != 2) return;
ei = ((struct global_object *)interp)->interpreter;

View File

@ -0,0 +1,140 @@
/** SpiderMonkey support for both user scripts and web scripts.
* @file */
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "elinks.h"
#include "ecmascript/spidermonkey-shared.h"
/** A shared runtime used for both user scripts (scripting/smjs/) and
* scripts on web pages (ecmascript/spidermonkey/).
*
* SpiderMonkey has bugs that corrupt memory when multiple JSRuntimes
* are used: https://bugzilla.mozilla.org/show_bug.cgi?id=378918 and
* perhaps others. */
JSRuntime *spidermonkey_runtime;
/** A JSContext that can be used in JS_SetPrivate and JS_GetPrivate
* when no better one is available. This context has no global
* object, so scripts cannot be evaluated in it.
*
* XXX: This also works around a crash on exit. SMJS will crash on
* JS_DestroyRuntime if the given runtime has never had any context
* created, which will be the case if one closes ELinks without having
* loaded any documents. */
JSContext *spidermonkey_empty_context;
/** A reference count for ::spidermonkey_runtime so that modules using
* it can be initialized and shut down in arbitrary order. */
static int spidermonkey_runtime_refcount;
/** Initialize ::spidermonkey_runtime and ::spidermonkey_empty_context.
* If already initialized, just increment the reference count.
*
* @return 1 if successful or 0 on error. If this succeeds, the
* caller must eventually call spidermonkey_runtime_release(). */
int
spidermonkey_runtime_addref(void)
{
if (spidermonkey_runtime_refcount == 0) {
assert(spidermonkey_runtime == NULL);
assert(spidermonkey_empty_context == NULL);
if_assert_failed return 0;
spidermonkey_runtime = JS_NewRuntime(4L * 1024L * 1024L);
if (!spidermonkey_runtime) return 0;
spidermonkey_empty_context = JS_NewContext(spidermonkey_runtime,
0);
if (!spidermonkey_empty_context) {
/* Perhaps JS_DestroyRuntime will now crash
* because no context was created, but there's
* not much else to do. */
JS_DestroyRuntime(spidermonkey_runtime);
spidermonkey_runtime = NULL;
JS_ShutDown();
}
}
assert(spidermonkey_runtime);
assert(spidermonkey_empty_context);
spidermonkey_runtime_refcount++;
assert(spidermonkey_runtime_refcount > 0);
if_assert_failed { spidermonkey_runtime_refcount--; return 0; }
return 1;
}
/** Release a reference to ::spidermonkey_runtime, and destroy it if
* that was the last reference. If spidermonkey_runtime_addref()
* failed, then this must not be called. */
void
spidermonkey_runtime_release(void)
{
assert(spidermonkey_runtime_refcount > 0);
assert(spidermonkey_runtime);
assert(spidermonkey_empty_context);
if_assert_failed return;
--spidermonkey_runtime_refcount;
if (spidermonkey_runtime_refcount == 0) {
JS_DestroyContext(spidermonkey_empty_context);
spidermonkey_empty_context = NULL;
JS_DestroyRuntime(spidermonkey_runtime);
spidermonkey_runtime = NULL;
JS_ShutDown();
}
}
/** An ELinks-specific replacement for JS_DefineFunctions().
*
* @relates spidermonkeyFunctionSpec */
JSBool
spidermonkey_DefineFunctions(JSContext *cx, JSObject *obj,
const spidermonkeyFunctionSpec *fs)
{
for (; fs->name; fs++) {
if (!JS_DefineFunction(cx, obj, fs->name, fs->call,
fs->nargs, 0))
return JS_FALSE;
}
return JS_TRUE;
}
/** An ELinks-specific replacement for JS_InitClass().
*
* @relates spidermonkeyFunctionSpec */
JSObject *
spidermonkey_InitClass(JSContext *cx, JSObject *obj,
JSObject *parent_proto, JSClass *clasp,
JSNative constructor, uintN nargs,
JSPropertySpec *ps,
const spidermonkeyFunctionSpec *fs,
JSPropertySpec *static_ps,
const spidermonkeyFunctionSpec *static_fs)
{
JSObject *proto = JS_InitClass(cx, obj, parent_proto, clasp,
constructor, nargs,
ps, NULL, static_ps, NULL);
if (proto == NULL)
return NULL;
if (fs) {
if (!spidermonkey_DefineFunctions(cx, proto, fs))
return NULL;
}
if (static_fs) {
JSObject *cons_obj = JS_GetConstructor(cx, proto);
if (cons_obj == NULL)
return NULL;
if (!spidermonkey_DefineFunctions(cx, cons_obj, static_fs))
return NULL;
}
return proto;
}

View File

@ -0,0 +1,74 @@
#ifndef EL__ECMASCRIPT_SPIDERMONKEY_SHARED_H
#define EL__ECMASCRIPT_SPIDERMONKEY_SHARED_H
/* For wild SpiderMonkey installations. */
#ifdef CONFIG_OS_BEOS
#define XP_BEOS
#elif CONFIG_OS_OS2
#define XP_OS2
#elif CONFIG_OS_RISCOS
#error Out of luck, buddy!
#elif CONFIG_OS_UNIX
#define XP_UNIX
#elif CONFIG_OS_WIN32
#define XP_WIN
#endif
#include <jsapi.h>
#include "util/string.h"
extern JSRuntime *spidermonkey_runtime;
extern JSContext *spidermonkey_empty_context;
int spidermonkey_runtime_addref(void);
void spidermonkey_runtime_release(void);
/** An ELinks-specific replacement for JSFunctionSpec.
*
* Bug 1016: In SpiderMonkey 1.7 bundled with XULRunner 1.8, jsapi.h
* defines JSFunctionSpec in different ways depending on whether
* MOZILLA_1_8_BRANCH is defined, and there is no obvious way for
* ELinks to check whether MOZILLA_1_8_BRANCH was defined when the
* library was built. Avoid the unstable JSFunctionSpec definitions
* and use this ELinks-specific structure instead. */
typedef struct spidermonkeyFunctionSpec {
const char *name;
JSNative call;
uint8 nargs;
/* ELinks does not use "flags" and "extra" so omit them here. */
} spidermonkeyFunctionSpec;
JSBool spidermonkey_DefineFunctions(JSContext *cx, JSObject *obj,
const spidermonkeyFunctionSpec *fs);
JSObject *spidermonkey_InitClass(JSContext *cx, JSObject *obj,
JSObject *parent_proto, JSClass *clasp,
JSNative constructor, uintN nargs,
JSPropertySpec *ps,
const spidermonkeyFunctionSpec *fs,
JSPropertySpec *static_ps,
const spidermonkeyFunctionSpec *static_fs);
static void undef_to_jsval(JSContext *ctx, jsval *vp);
static unsigned char *jsval_to_string(JSContext *ctx, jsval *vp);
/* Inline functions */
static inline void
undef_to_jsval(JSContext *ctx, jsval *vp)
{
*vp = JSVAL_NULL;
}
static inline unsigned char *
jsval_to_string(JSContext *ctx, jsval *vp)
{
jsval val;
if (JS_ConvertValue(ctx, *vp, JSTYPE_STRING, &val) == JS_FALSE) {
return "";
}
return empty_string_or_(JS_GetStringBytes(JS_ValueToString(ctx, val)));
}
#endif

View File

@ -55,7 +55,7 @@
/* TODO? Are there any which need to be implemented? */
static JSRuntime *jsrt;
static int js_module_init_ok;
static void
error_reporter(JSContext *ctx, const char *message, JSErrorReport *report)
@ -138,19 +138,14 @@ setup_safeguard(struct ecmascript_interpreter *interpreter,
static void
spidermonkey_init(struct module *xxx)
{
jsrt = JS_NewRuntime(0x400000UL);
/* XXX: This is a hack to avoid a crash on exit. SMJS will crash
* on JS_DestroyRuntime if the given runtime has never had any context
* created, which will be the case if one closes ELinks without having
* loaded any documents. */
JS_DestroyContext(JS_NewContext(jsrt, 0));
js_module_init_ok = spidermonkey_runtime_addref();
}
static void
spidermonkey_done(struct module *xxx)
{
JS_DestroyRuntime(jsrt);
JS_ShutDown();
if (js_module_init_ok)
spidermonkey_runtime_release();
}
@ -162,8 +157,10 @@ spidermonkey_get_interpreter(struct ecmascript_interpreter *interpreter)
*statusbar_obj, *menubar_obj, *navigator_obj;
assert(interpreter);
if (!js_module_init_ok) return NULL;
ctx = JS_NewContext(jsrt, 8192 /* Stack allocation chunk size */);
ctx = JS_NewContext(spidermonkey_runtime,
8192 /* Stack allocation chunk size */);
if (!ctx)
return NULL;
interpreter->backend_data = ctx;
@ -237,6 +234,7 @@ spidermonkey_put_interpreter(struct ecmascript_interpreter *interpreter)
JSContext *ctx;
assert(interpreter);
if (!js_module_init_ok) return;
ctx = interpreter->backend_data;
JS_DestroyContext(ctx);
interpreter->backend_data = NULL;
@ -251,6 +249,7 @@ spidermonkey_eval(struct ecmascript_interpreter *interpreter,
jsval rval;
assert(interpreter);
if (!js_module_init_ok) return;
ctx = interpreter->backend_data;
setup_safeguard(interpreter, ctx);
interpreter->ret = ret;
@ -267,6 +266,7 @@ spidermonkey_eval_stringback(struct ecmascript_interpreter *interpreter,
jsval rval;
assert(interpreter);
if (!js_module_init_ok) return NULL;
ctx = interpreter->backend_data;
setup_safeguard(interpreter, ctx);
interpreter->ret = NULL;
@ -294,6 +294,7 @@ spidermonkey_eval_boolback(struct ecmascript_interpreter *interpreter,
int ret;
assert(interpreter);
if (!js_module_init_ok) return 0;
ctx = interpreter->backend_data;
setup_safeguard(interpreter, ctx);
interpreter->ret = NULL;

View File

@ -7,6 +7,10 @@ struct string;
void *spidermonkey_get_interpreter(struct ecmascript_interpreter *interpreter);
void spidermonkey_put_interpreter(struct ecmascript_interpreter *interpreter);
void spidermonkey_detach_form_view(struct form_view *fv);
void spidermonkey_detach_form_state(struct form_state *fs);
void spidermonkey_moved_form_state(struct form_state *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);

View File

@ -2,6 +2,6 @@ top_builddir=../../..
include $(top_builddir)/Makefile.config
INCLUDES += $(SPIDERMONKEY_CFLAGS)
OBJS = document.o form.o location.o navigator.o unibar.o util.o window.o
OBJS = document.o form.o location.o navigator.o unibar.o window.o
include $(top_srcdir)/Makefile.lib

View File

@ -22,6 +22,7 @@
#include "document/forms.h"
#include "document/view.h"
#include "ecmascript/ecmascript.h"
#include "ecmascript/spidermonkey.h"
#include "ecmascript/spidermonkey/document.h"
#include "ecmascript/spidermonkey/form.h"
#include "ecmascript/spidermonkey/window.h"
@ -56,28 +57,15 @@ static const JSClass form_class; /* defined below */
static JSBool input_get_property(JSContext *ctx, JSObject *obj, jsval id, jsval *vp);
static JSBool input_set_property(JSContext *ctx, JSObject *obj, jsval id, jsval *vp);
/* Indexes of reserved slots in instances of @input_class. */
enum {
/* The slot contains an integer used as an index to
* view_state.form_info[]. This allows ELinks to reallocate
* form_info[] without keeping track of SMJS objects that
* refer to its elements. We do not use JSCLASS_HAS_PRIVATE
* for that because SMJS expects the private data to be an
* aligned pointer. */
JSRS_INPUT_FSINDEX,
/* Number of reserved slots. */
JSRS_INPUT_COUNT
};
static void input_finalize(JSContext *ctx, JSObject *obj);
/* Each @input_class object must have a @form_class parent. */
static const JSClass input_class = {
"input", /* here, we unleash ourselves */
JSCLASS_HAS_RESERVED_SLOTS(JSRS_INPUT_COUNT),
JSCLASS_HAS_PRIVATE, /* struct form_state *, or NULL if detached */
JS_PropertyStub, JS_PropertyStub,
input_get_property, input_set_property,
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, input_finalize
};
/* Tinyids of properties. Use negative values to distinguish these
@ -146,23 +134,18 @@ static unicode_val_T jsval_to_accesskey(JSContext *ctx, jsval *vp);
static struct form_state *
input_get_form_state(JSContext *ctx, JSObject *obj, struct view_state *vs)
input_get_form_state(JSContext *ctx, JSObject *jsinput)
{
jsval val;
int n;
JSBool ok;
struct form_state *fs = JS_GetInstancePrivate(ctx, jsinput,
(JSClass *) &input_class,
NULL);
ok = JS_GetReservedSlot(ctx, obj, JSRS_INPUT_FSINDEX, &val);
assert(ok);
assert(JSVAL_IS_INT(val));
if (!fs) return NULL; /* detached */
assert(fs->ecmascript_obj == jsinput);
if_assert_failed return NULL;
n = JSVAL_TO_INT(val);
assert(n >= 0);
assert(n < vs->form_info_len);
if_assert_failed return NULL;
return &vs->form_info[n];
return fs;
}
/* @input_class.getProperty */
@ -199,7 +182,8 @@ input_get_property(JSContext *ctx, JSObject *obj, jsval id, jsval *vp)
(JSClass *) &window_class, NULL);
doc_view = vs->doc_view;
document = doc_view->document;
fs = input_get_form_state(ctx, obj, vs);
fs = input_get_form_state(ctx, obj);
if (!fs) return JS_FALSE; /* detached */
fc = find_form_control(document, fs);
assert(fc);
@ -350,7 +334,8 @@ input_set_property(JSContext *ctx, JSObject *obj, jsval id, jsval *vp)
(JSClass *) &window_class, NULL);
doc_view = vs->doc_view;
document = doc_view->document;
fs = input_get_form_state(ctx, obj, vs);
fs = input_get_form_state(ctx, obj);
if (!fs) return JS_FALSE; /* detached */
fc = find_form_control(document, fs);
assert(fc);
@ -475,7 +460,8 @@ input_click(JSContext *ctx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
doc_view = vs->doc_view;
document = doc_view->document;
ses = doc_view->session;
fs = input_get_form_state(ctx, obj, vs);
fs = input_get_form_state(ctx, obj);
if (!fs) return JS_FALSE; /* detached */
assert(fs);
fc = find_form_control(document, fs);
@ -528,7 +514,8 @@ input_focus(JSContext *ctx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
doc_view = vs->doc_view;
document = doc_view->document;
ses = doc_view->session;
fs = input_get_form_state(ctx, obj, vs);
fs = input_get_form_state(ctx, obj);
if (!fs) return JS_FALSE; /* detached */
assert(fs);
fc = find_form_control(document, fs);
@ -555,26 +542,99 @@ input_select(JSContext *ctx, JSObject *obj, uintN argc, jsval *argv, jsval *rval
}
static JSObject *
get_input_object(JSContext *ctx, JSObject *jsform, long number)
get_input_object(JSContext *ctx, JSObject *jsform, struct form_state *fs)
{
#if 0
if (fs->ecmascript_obj)
return fs->ecmascript_obj;
#endif
JSObject *jsinput = fs->ecmascript_obj;
if (jsinput) {
/* This assumes JS_GetInstancePrivate cannot GC. */
assert(JS_GetInstancePrivate(ctx, jsinput,
(JSClass *) &input_class, NULL)
== fs);
if_assert_failed return NULL;
return jsinput;
}
/* jsform ('form') is input's parent */
/* FIXME: That is NOT correct since the real containing element
* should be its parent, but gimme DOM first. --pasky */
JSObject *jsinput = JS_NewObject(ctx, (JSClass *) &input_class, NULL, jsform);
jsinput = JS_NewObject(ctx, (JSClass *) &input_class, NULL, jsform);
if (!jsinput)
return NULL;
JS_DefineProperties(ctx, jsinput, (JSPropertySpec *) input_props);
spidermonkey_DefineFunctions(ctx, jsinput, input_funcs);
JS_SetReservedSlot(ctx, jsinput, JSRS_INPUT_FSINDEX, INT_TO_JSVAL(number));
return jsinput;;
if (!JS_SetPrivate(ctx, jsinput, fs)) /* to @input_class */
return NULL;
fs->ecmascript_obj = jsinput;
return jsinput;
}
static void
input_finalize(JSContext *ctx, JSObject *jsinput)
{
struct form_state *fs = JS_GetInstancePrivate(ctx, jsinput,
(JSClass *) &input_class,
NULL);
if (fs) {
/* If this assertion fails, leave fs->ecmascript_obj
* unchanged, because it may point to a different
* JSObject whose private pointer will later have to
* be updated to avoid crashes. */
assert(fs->ecmascript_obj == jsinput);
if_assert_failed return;
fs->ecmascript_obj = NULL;
/* No need to JS_SetPrivate, because jsinput is being
* destroyed. */
}
}
void
spidermonkey_detach_form_state(struct form_state *fs)
{
JSObject *jsinput = fs->ecmascript_obj;
if (jsinput) {
/* This assumes JS_GetInstancePrivate and JS_SetPrivate
* cannot GC. */
/* If this assertion fails, it is not clear whether
* the private pointer of jsinput should be reset;
* crashes seem possible either way. Resetting it is
* easiest. */
assert(JS_GetInstancePrivate(spidermonkey_empty_context,
jsinput,
(JSClass *) &input_class, NULL)
== fs);
if_assert_failed {}
JS_SetPrivate(spidermonkey_empty_context, jsinput, NULL);
fs->ecmascript_obj = NULL;
}
}
void
spidermonkey_moved_form_state(struct form_state *fs)
{
JSObject *jsinput = fs->ecmascript_obj;
if (jsinput) {
/* This assumes JS_SetPrivate cannot GC. If it could,
* then the GC might call input_finalize for some
* other object whose struct form_state has also been
* reallocated, and an assertion would fail in
* input_finalize. */
JS_SetPrivate(spidermonkey_empty_context, jsinput, fs);
}
}
static JSObject *
get_form_control_object(JSContext *ctx, JSObject *jsform, enum form_type type, int number)
get_form_control_object(JSContext *ctx, JSObject *jsform,
enum form_type type, struct form_state *fs)
{
switch (type) {
case FC_TEXT:
@ -588,7 +648,7 @@ get_form_control_object(JSContext *ctx, JSObject *jsform, enum form_type type, i
case FC_BUTTON:
case FC_HIDDEN:
case FC_SELECT:
return get_input_object(ctx, jsform, (long)number);
return get_input_object(ctx, jsform, fs);
case FC_TEXTAREA:
/* TODO */
@ -601,7 +661,7 @@ get_form_control_object(JSContext *ctx, JSObject *jsform, enum form_type type, i
}
static struct form_view *form_get_form_view(JSContext *ctx, JSObject *jsform, jsval *argv);
static JSBool form_elements_get_property(JSContext *ctx, JSObject *obj, jsval id, jsval *vp);