1
0
mirror of https://github.com/rkd77/elinks.git synced 2024-12-04 14:46:47 -05: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);
/* Each @form_elements_class object must have a @form_class parent. */
@ -666,8 +726,8 @@ form_elements_get_property(JSContext *ctx, JSObject *obj, jsval id, jsval *vp)
(JSClass *) &window_class, NULL);
doc_view = vs->doc_view;
document = doc_view->document;
form_view = JS_GetInstancePrivate(ctx, parent_form,
(JSClass *) &form_class, NULL);
form_view = form_get_form_view(ctx, parent_form, NULL);
if (!form_view) return JS_FALSE; /* detached */
form = find_form_by_form_view(document, form_view);
if (JSVAL_IS_STRING(id)) {
@ -724,8 +784,8 @@ form_elements_item(JSContext *ctx, JSObject *obj, uintN argc, jsval *argv, jsval
(JSClass *) &window_class, NULL);
doc_view = vs->doc_view;
document = doc_view->document;
form_view = JS_GetInstancePrivate(ctx, parent_form,
(JSClass *) &form_class, NULL);
form_view = form_get_form_view(ctx, parent_form, NULL);
if (!form_view) return JS_FALSE; /* detached */
form = find_form_by_form_view(document, form_view);
if (argc != 1)
@ -741,7 +801,7 @@ form_elements_item(JSContext *ctx, JSObject *obj, uintN argc, jsval *argv, jsval
struct form_state *fs = find_form_state(doc_view, fc);
if (fs) {
JSObject *fcobj = get_form_control_object(ctx, parent_form, fc->type, fc->g_ctrl_num);
JSObject *fcobj = get_form_control_object(ctx, parent_form, fc->type, fs);
if (fcobj)
object_to_jsval(ctx, rval, fcobj);
@ -783,8 +843,8 @@ form_elements_namedItem(JSContext *ctx, JSObject *obj, uintN argc, jsval *argv,
(JSClass *) &window_class, NULL);
doc_view = vs->doc_view;
document = doc_view->document;
form_view = JS_GetInstancePrivate(ctx, parent_form,
(JSClass *) &form_class, NULL);
form_view = form_get_form_view(ctx, parent_form, NULL);
if (!form_view) return JS_FALSE; /* detached */
form = find_form_by_form_view(document, form_view);
if (argc != 1)
@ -801,7 +861,7 @@ form_elements_namedItem(JSContext *ctx, JSObject *obj, uintN argc, jsval *argv,
struct form_state *fs = find_form_state(doc_view, fc);
if (fs) {
JSObject *fcobj = get_form_control_object(ctx, parent_form, fc->type, fc->g_ctrl_num);
JSObject *fcobj = get_form_control_object(ctx, parent_form, fc->type, fs);
if (fcobj)
object_to_jsval(ctx, rval, fcobj);
@ -817,14 +877,15 @@ form_elements_namedItem(JSContext *ctx, JSObject *obj, uintN argc, jsval *argv,
static JSBool form_get_property(JSContext *ctx, JSObject *obj, jsval id, jsval *vp);
static JSBool form_set_property(JSContext *ctx, JSObject *obj, jsval id, jsval *vp);
static void form_finalize(JSContext *ctx, JSObject *obj);
/* Each @form_class object must have a @document_class parent. */
static const JSClass form_class = {
"form",
JSCLASS_HAS_PRIVATE, /* struct form_view * */
JSCLASS_HAS_PRIVATE, /* struct form_view *, or NULL if detached */
JS_PropertyStub, JS_PropertyStub,
form_get_property, form_set_property,
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, form_finalize
};
/* Tinyids of properties. Use negative values to distinguish these
@ -861,6 +922,21 @@ static const spidermonkeyFunctionSpec form_funcs[] = {
{ NULL }
};
static struct form_view *
form_get_form_view(JSContext *ctx, JSObject *jsform, jsval *argv)
{
struct form_view *fv = JS_GetInstancePrivate(ctx, jsform,
(JSClass *) &form_class,
argv);
if (!fv) return NULL; /* detached */
assert(fv->ecmascript_obj == jsform);
if_assert_failed return NULL;
return fv;
}
/* @form_class.getProperty */
static JSBool
form_get_property(JSContext *ctx, JSObject *obj, jsval id, jsval *vp)
@ -888,7 +964,8 @@ form_get_property(JSContext *ctx, JSObject *obj, jsval id, jsval *vp)
vs = JS_GetInstancePrivate(ctx, parent_win,
(JSClass *) &window_class, NULL);
doc_view = vs->doc_view;
fv = JS_GetInstancePrivate(ctx, obj, (JSClass *) &form_class, NULL);
fv = form_get_form_view(ctx, obj, NULL);
if (!fv) return JS_FALSE; /* detached */
form = find_form_by_form_view(doc_view->document, fv);
assert(form);
@ -908,7 +985,7 @@ form_get_property(JSContext *ctx, JSObject *obj, jsval id, jsval *vp)
undef_to_jsval(ctx, vp);
fs = find_form_state(doc_view, fc);
if (fs) {
fcobj = get_form_control_object(ctx, obj, fc->type, fc->g_ctrl_num);
fcobj = get_form_control_object(ctx, obj, fc->type, fs);
if (fcobj)
object_to_jsval(ctx, vp, fcobj);
}
@ -1022,7 +1099,8 @@ form_set_property(JSContext *ctx, JSObject *obj, jsval id, jsval *vp)
vs = JS_GetInstancePrivate(ctx, parent_win,
(JSClass *) &window_class, NULL);
doc_view = vs->doc_view;
fv = JS_GetInstancePrivate(ctx, obj, (JSClass *) &form_class, NULL);
fv = form_get_form_view(ctx, obj, NULL);
if (!fv) return JS_FALSE; /* detached */
form = find_form_by_form_view(doc_view->document, fv);
assert(form);
@ -1102,7 +1180,8 @@ form_reset(JSContext *ctx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
vs = JS_GetInstancePrivate(ctx, parent_win,
(JSClass *) &window_class, NULL);
doc_view = vs->doc_view;
fv = JS_GetInstancePrivate(ctx, obj, (JSClass *) &form_class, argv);
fv = form_get_form_view(ctx, obj, argv);
if (!fv) return JS_FALSE; /* detached */
form = find_form_by_form_view(doc_view->document, fv);
assert(form);
@ -1139,7 +1218,8 @@ form_submit(JSContext *ctx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
(JSClass *) &window_class, NULL);
doc_view = vs->doc_view;
ses = doc_view->session;
fv = JS_GetInstancePrivate(ctx, obj, (JSClass *) &form_class, argv);
fv = form_get_form_view(ctx, obj, argv);
if (!fv) return JS_FALSE; /* detached */
form = find_form_by_form_view(doc_view->document, fv);
assert(form);
@ -1153,21 +1233,79 @@ form_submit(JSContext *ctx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
JSObject *
get_form_object(JSContext *ctx, JSObject *jsdoc, struct form_view *fv)
{
#if 0
if (fv->ecmascript_obj)
return fv->ecmascript_obj;
#endif
JSObject *jsform = fv->ecmascript_obj;
if (jsform) {
/* This assumes JS_GetInstancePrivate cannot GC. */
assert(JS_GetInstancePrivate(ctx, jsform,
(JSClass *) &form_class, NULL)
== fv);
if_assert_failed return NULL;
return jsform;
}
/* 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 */
JSObject *jsform = JS_NewObject(ctx, (JSClass *) &form_class, NULL, jsdoc);
jsform = JS_NewObject(ctx, (JSClass *) &form_class, NULL, jsdoc);
if (jsform == NULL)
return NULL;
JS_DefineProperties(ctx, jsform, (JSPropertySpec *) form_props);
spidermonkey_DefineFunctions(ctx, jsform, form_funcs);
JS_SetPrivate(ctx, jsform, fv); /* to @form_class */
if (!JS_SetPrivate(ctx, jsform, fv)) /* to @form_class */
return NULL;
fv->ecmascript_obj = jsform;
return fv->ecmascript_obj;
return jsform;
}
static void
form_finalize(JSContext *ctx, JSObject *jsform)
{
struct form_view *fv = JS_GetInstancePrivate(ctx, jsform,
(JSClass *) &form_class,
NULL);
if (fv) {
/* If this assertion fails, leave fv->ecmascript_obj
* unchanged, because it may point to a different
* JSObject whose private pointer will later have to
* be updated to avoid crashes. */
assert(fv->ecmascript_obj == jsform);
if_assert_failed return;
fv->ecmascript_obj = NULL;
/* No need to JS_SetPrivate, because the object is
* being destroyed. */
}
}
void
spidermonkey_detach_form_view(struct form_view *fv)
{
JSObject *jsform = fv->ecmascript_obj;
if (jsform) {
/* This assumes JS_GetInstancePrivate and JS_SetPrivate
* cannot GC. */
/* If this assertion fails, it is not clear whether
* the private pointer of jsform should be reset;
* crashes seem possible either way. Resetting it is
* easiest. */
assert(JS_GetInstancePrivate(spidermonkey_empty_context,
jsform,
(JSClass *) &form_class, NULL)
== fv);
if_assert_failed {}
JS_SetPrivate(spidermonkey_empty_context, jsform, NULL);
fv->ecmascript_obj = NULL;
}
}
static JSBool forms_get_property(JSContext *ctx, JSObject *obj, jsval id, jsval *vp);
/* Each @forms_class object must have a @document_class parent. */

View File

@ -1,60 +0,0 @@
/* Better compatibility across versions of SpiderMonkey. */
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "elinks.h"
#include "ecmascript/spidermonkey/util.h"
/** 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

@ -2,32 +2,16 @@
#ifndef EL__ECMASCRIPT_SPIDERMONKEY_UTIL_H
#define EL__ECMASCRIPT_SPIDERMONKEY_UTIL_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 "ecmascript/spidermonkey-shared.h"
#include "util/memory.h"
#include "util/string.h"
static void string_to_jsval(JSContext *ctx, jsval *vp, unsigned char *string);
static void astring_to_jsval(JSContext *ctx, jsval *vp, unsigned char *string);
static void int_to_jsval(JSContext *ctx, jsval *vp, int number);
static void object_to_jsval(JSContext *ctx, jsval *vp, JSObject *object);
static void boolean_to_jsval(JSContext *ctx, jsval *vp, int boolean);
static void undef_to_jsval(JSContext *ctx, jsval *vp);
static int jsval_to_boolean(JSContext *ctx, jsval *vp);
static unsigned char *jsval_to_string(JSContext *ctx, jsval *vp);
@ -68,12 +52,6 @@ boolean_to_jsval(JSContext *ctx, jsval *vp, int boolean)
*vp = BOOLEAN_TO_JSVAL(boolean);
}
static inline void
undef_to_jsval(JSContext *ctx, jsval *vp)
{
*vp = JSVAL_NULL;
}
static inline int
jsval_to_boolean(JSContext *ctx, jsval *vp)
@ -87,41 +65,4 @@ jsval_to_boolean(JSContext *ctx, jsval *vp)
return JSVAL_TO_BOOLEAN(val);
}
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)));
}
/** 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);
#endif

View File

@ -342,7 +342,7 @@ window_open(JSContext *ctx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
struct document_view *doc_view;
struct session *ses;
unsigned char *frame = "";
unsigned char *url;
unsigned char *url, *url2;
struct uri *uri;
static time_t ratelimit_start;
static int ratelimit_count;
@ -387,10 +387,13 @@ window_open(JSContext *ctx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
/* TODO: Support for window naming and perhaps some window features? */
url = join_urls(doc_view->document->uri, url);
if (!url) return JS_TRUE;
uri = get_uri(url, 0);
url2 = join_urls(doc_view->document->uri, url);
mem_free(url);
if (!url2) {
return JS_TRUE;
}
uri = get_uri(url2, 0);
mem_free(url2);
if (!uri) return JS_TRUE;

View File

@ -30,6 +30,7 @@ struct deflate_enc_data {
int fdread;
unsigned int last_read:1;
unsigned int after_first_read:1;
/* A buffer for data that has been read from the file but not
* yet decompressed. z_stream.next_in and z_stream.avail_in
@ -59,6 +60,7 @@ deflate_open(int window_size, struct stream_encoded *stream, int fd)
copy_struct(&data->deflate_stream, &null_z_stream);
data->fdread = fd;
data->last_read = 0;
data->after_first_read = 0;
err = inflateInit2(&data->deflate_stream, window_size);
if (err != Z_OK) {
@ -70,12 +72,14 @@ deflate_open(int window_size, struct stream_encoded *stream, int fd)
return 0;
}
#if 0
static int
deflate_raw_open(struct stream_encoded *stream, int fd)
{
/* raw DEFLATE with neither zlib nor gzip header */
return deflate_open(-MAX_WBITS, stream, fd);
}
#endif
static int
deflate_gzip_open(struct stream_encoded *stream, int fd)
@ -89,6 +93,7 @@ deflate_read(struct stream_encoded *stream, unsigned char *buf, int len)
{
struct deflate_enc_data *data = (struct deflate_enc_data *) stream->data;
int err = 0;
int l = 0;
if (!data) return -1;
@ -101,7 +106,7 @@ deflate_read(struct stream_encoded *stream, unsigned char *buf, int len)
do {
if (data->deflate_stream.avail_in == 0) {
int l = safe_read(data->fdread, data->buf,
l = safe_read(data->fdread, data->buf,
ELINKS_DEFLATE_BUFFER_LENGTH);
if (l == -1) {
@ -117,7 +122,39 @@ deflate_read(struct stream_encoded *stream, unsigned char *buf, int len)
data->deflate_stream.next_in = data->buf;
data->deflate_stream.avail_in = l;
}
restart:
err = inflate(&data->deflate_stream, Z_SYNC_FLUSH);
if (err == Z_DATA_ERROR && !data->after_first_read) {
/* RFC 2616 requires a zlib header for
* "Content-Encoding: deflate", but some HTTP
* servers (Microsoft-IIS/6.0 at blogs.msdn.com,
* and reportedly Apache with mod_deflate) omit
* that, causing Z_DATA_ERROR. Clarification of
* the term "deflate" has been requested for the
* next version of HTTP:
* http://www3.tools.ietf.org/wg/httpbis/trac/ticket/73
*
* Try to recover by telling zlib not to expect
* the header. If the error does not happen on
* the first inflate() call, then it is too late
* to recover because ELinks may already have
* discarded part of the input data.
*
* TODO: This fallback to raw DEFLATE is currently
* enabled for "Content-Encoding: gzip" too. It
* might be better to fall back to no compression
* at all, because Apache can send that header for
* uncompressed *.gz.md5 files. */
data->after_first_read = 1;
inflateEnd(&data->deflate_stream);
data->deflate_stream.avail_out = len;
data->deflate_stream.next_out = buf;
data->deflate_stream.next_in = data->buf;
data->deflate_stream.avail_in = l;
err = inflateInit2(&data->deflate_stream, -MAX_WBITS);
if (err == Z_OK) goto restart;
}
data->after_first_read = 1;
if (err == Z_STREAM_END) {
data->last_read = 1;
break;
@ -211,7 +248,7 @@ static const unsigned char *const deflate_extensions[] = { NULL };
const struct decoding_backend deflate_decoding_backend = {
"deflate",
deflate_extensions,
deflate_raw_open,
deflate_gzip_open,
deflate_read,
deflate_raw_decode_buffer,
deflate_close,

View File

@ -7,7 +7,7 @@
#include "elinks.h"
#include "config/kbdbind.h"
#include "ecmascript/spidermonkey/util.h"
#include "ecmascript/spidermonkey-shared.h"
#include "scripting/smjs/core.h"
#include "scripting/smjs/elinks_object.h"
#include "session/session.h"

View File

@ -7,7 +7,7 @@
#include "elinks.h"
#include "bookmarks/bookmarks.h"
#include "ecmascript/spidermonkey/util.h"
#include "ecmascript/spidermonkey-shared.h"
#include "main/event.h"
#include "scripting/smjs/core.h"
#include "scripting/smjs/elinks_object.h"

View File

@ -1,7 +1,7 @@
#ifndef EL__SCRIPTING_SMJS_BOOKMARKS_H
#define EL__SCRIPTING_SMJS_BOOKMARKS_H
#include "ecmascript/spidermonkey/util.h"
#include "ecmascript/spidermonkey-shared.h"
void smjs_init_bookmarks_interface(void);

View File

@ -7,7 +7,7 @@
#include "elinks.h"
#include "cache/cache.h"
#include "ecmascript/spidermonkey/util.h"
#include "ecmascript/spidermonkey-shared.h"
#include "protocol/uri.h"
#include "scripting/smjs/cache_object.h"
#include "scripting/smjs/core.h"

View File

@ -7,7 +7,7 @@
#include "elinks.h"
#include "config/home.h"
#include "ecmascript/spidermonkey/util.h"
#include "ecmascript/spidermonkey-shared.h"
#include "main/module.h"
#include "osdep/osdep.h"
#include "scripting/scripting.h"
@ -68,8 +68,6 @@ reported:
JS_ClearPendingException(ctx);
}
static JSRuntime *smjs_rt;
static int
smjs_do_file(unsigned char *path)
{
@ -127,13 +125,11 @@ smjs_load_hooks(void)
void
init_smjs(struct module *module)
{
smjs_rt = JS_NewRuntime(1L * 1024L * 1024L);
if (!smjs_rt) return;
if (!spidermonkey_runtime_addref()) return;
smjs_ctx = JS_NewContext(smjs_rt, 8192);
smjs_ctx = JS_NewContext(spidermonkey_runtime, 8192);
if (!smjs_ctx) {
JS_DestroyRuntime(smjs_rt);
smjs_rt = NULL;
spidermonkey_runtime_release();
return;
}
@ -154,9 +150,15 @@ cleanup_smjs(struct module *module)
{
if (!smjs_ctx) return;
/* These calls also finalize all JSObjects that have been
* allocated in the JSRuntime, so cache_entry_finalize gets
* called and resets each cache_entry.jsobject = NULL. */
/* JS_DestroyContext also collects garbage in the JSRuntime.
* Because the JSObjects created in smjs_ctx have not been
* made visible to any other JSContext, and the garbage
* collector of SpiderMonkey is precise, SpiderMonkey
* finalizes all of those objects, so cache_entry_finalize
* gets called and resets each cache_entry.jsobject = NULL.
* If the garbage collector were conservative, ELinks would
* have to call smjs_detach_cache_entry_object on each cache
* entry before it releases the runtime here. */
JS_DestroyContext(smjs_ctx);
JS_DestroyRuntime(smjs_rt);
spidermonkey_runtime_release();
}

View File

@ -1,7 +1,7 @@
#ifndef EL__SCRIPTING_SMJS_CORE_H
#define EL__SCRIPTING_SMJS_CORE_H
#include "ecmascript/spidermonkey/util.h"
#include "ecmascript/spidermonkey-shared.h"
struct module;
struct session;

View File

@ -8,7 +8,7 @@
#include "bfu/msgbox.h"
#include "config/home.h"
#include "ecmascript/spidermonkey/util.h"
#include "ecmascript/spidermonkey-shared.h"
#include "intl/gettext/libintl.h"
#include "protocol/uri.h"
#include "scripting/scripting.h"
@ -69,7 +69,7 @@ elinks_set_location(JSContext *ctx, JSObject *obj, jsval id, jsval *vp)
return JS_TRUE;
}
/* function "alert" in the object returned by smjs_get_elinks_object() */
/* @elinks_funcs{"alert"} */
static JSBool
elinks_alert(JSContext *ctx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
@ -90,7 +90,7 @@ elinks_alert(JSContext *ctx, JSObject *obj, uintN argc, jsval *argv, jsval *rval
return JS_TRUE;
}
/* function "execute" in the object returned by smjs_get_elinks_object() */
/* @elinks_funcs{"execute"} */
static JSBool
elinks_execute(JSContext *ctx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
@ -117,6 +117,12 @@ static const JSClass elinks_class = {
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub
};
static const spidermonkeyFunctionSpec elinks_funcs[] = {
{ "alert", elinks_alert, 1 },
{ "execute", elinks_execute, 1 },
{ NULL }
};
static JSObject *
smjs_get_elinks_object(void)
{
@ -125,24 +131,9 @@ smjs_get_elinks_object(void)
assert(smjs_ctx);
assert(smjs_global_object);
jsobj = JS_InitClass(smjs_ctx, smjs_global_object, NULL,
jsobj = spidermonkey_InitClass(smjs_ctx, smjs_global_object, NULL,
(JSClass *) &elinks_class, NULL, 0, NULL,
(JSFunctionSpec *) NULL, NULL, NULL);
/* 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 instead use JS_DefineFunction
* directly.
*
* In elinks/src/ecmascript/spidermonkey/, there is an
* ELinks-specific replacement for JSFunctionSpec; however, to
* keep the modules independent, elinks/src/scripting/smjs/
* does not use that. */
JS_DefineFunction(smjs_ctx, jsobj, "alert", elinks_alert, 1, 0);
JS_DefineFunction(smjs_ctx, jsobj, "execute", elinks_execute, 1, 0);
elinks_funcs, NULL, NULL);
JS_DefineProperty(smjs_ctx, jsobj, "location", JSVAL_NULL,
elinks_get_location, elinks_set_location,
@ -176,8 +167,6 @@ JSBool
smjs_invoke_elinks_object_method(unsigned char *method, jsval argv[], int argc,
jsval *rval)
{
JSFunction *func;
assert(smjs_ctx);
assert(smjs_elinks_object);
assert(rval);
@ -190,9 +179,6 @@ smjs_invoke_elinks_object_method(unsigned char *method, jsval argv[], int argc,
if (JSVAL_VOID == *rval)
return JS_FALSE;
func = JS_ValueToFunction(smjs_ctx, *rval);
if (!func) return JS_FALSE;
return JS_CallFunction(smjs_ctx, smjs_elinks_object,
func, argc, argv, rval);
return JS_CallFunctionValue(smjs_ctx, smjs_elinks_object,
*rval, argc, argv, rval);
}

View File

@ -1,7 +1,7 @@
#ifndef EL__SCRIPTING_SMJS_ELINKS_OBJECT_H
#define EL__SCRIPTING_SMJS_ELINKS_OBJECT_H
#include "ecmascript/spidermonkey/util.h"
#include "ecmascript/spidermonkey-shared.h"
/* This is the all-powerful elinks object through which all client scripts
* will interface with ELinks. */

View File

@ -6,7 +6,7 @@
#include "elinks.h"
#include "ecmascript/spidermonkey/util.h"
#include "ecmascript/spidermonkey-shared.h"
#include "scripting/scripting.h"
#include "scripting/smjs/core.h"
#include "scripting/smjs/global_object.h"

View File

@ -1,7 +1,7 @@
#ifndef EL__SCRIPTING_SMJS_GLOBAL_OBJECT_H
#define EL__SCRIPTING_SMJS_GLOBAL_OBJECT_H
#include "ecmascript/spidermonkey/util.h"
#include "ecmascript/spidermonkey-shared.h"
/* The root of the object hierarchy. If object 'foo' has this as its parent,
* you can use foo's method and properties with 'foo.<method|property>'. */

View File

@ -7,7 +7,7 @@
#include "elinks.h"
#include "globhist/globhist.h"
#include "ecmascript/spidermonkey/util.h"
#include "ecmascript/spidermonkey-shared.h"
#include "scripting/smjs/core.h"
#include "scripting/smjs/elinks_object.h"
#include "util/memory.h"

View File

@ -7,7 +7,7 @@
#include "elinks.h"
#include "cache/cache.h"
#include "ecmascript/spidermonkey/util.h"
#include "ecmascript/spidermonkey-shared.h"
#include "protocol/uri.h"
#include "main/event.h"
#include "main/module.h"

View File

@ -7,7 +7,7 @@
#include "elinks.h"
#include "config/kbdbind.h"
#include "ecmascript/spidermonkey/util.h"
#include "ecmascript/spidermonkey-shared.h"
#include "main/event.h"
#include "scripting/smjs/core.h"
#include "scripting/smjs/elinks_object.h"
@ -55,15 +55,13 @@ smjs_keybinding_action_callback(va_list ap, void *data)
jsval rval;
struct session *ses = va_arg(ap, struct session *);
JSObject *jsobj = data;
JSFunction *func = JS_ValueToFunction(smjs_ctx, OBJECT_TO_JSVAL(jsobj));
evhook_use_params(ses);
assert(func);
smjs_ses = ses;
JS_CallFunction(smjs_ctx, NULL, func, 0, NULL, &rval);
JS_CallFunctionValue(smjs_ctx, NULL, OBJECT_TO_JSVAL(jsobj),
0, NULL, &rval);
smjs_ses = NULL;

View File

@ -1,7 +1,7 @@
#ifndef EL__SCRIPTING_SMJS_KEYBINDING_H
#define EL__SCRIPTING_SMJS_KEYBINDING_H
#include "ecmascript/spidermonkey/util.h"
#include "ecmascript/spidermonkey-shared.h"
void smjs_init_keybinding_interface(void);

View File

@ -6,7 +6,7 @@
#include "elinks.h"
#include "ecmascript/spidermonkey/util.h"
#include "ecmascript/spidermonkey-shared.h"
#include "network/connection.h"
#include "protocol/uri.h"
#include "scripting/smjs/core.h"
@ -17,7 +17,13 @@
struct smjs_load_uri_hop {
struct session *ses;
JSFunction *callback;
/* SpiderMonkey versions earlier than 1.8 cannot properly call
* a closure if given just a JSFunction pointer. They need a
* jsval that points to the corresponding JSObject. Besides,
* JS_AddNamedRoot is not documented to support JSFunction
* pointers. */
jsval callback;
};
static void
@ -39,20 +45,18 @@ smjs_loading_callback(struct download *download, void *data)
* the script is using it. */
object_lock(download->cached);
assert(hop->callback);
smjs_ses = hop->ses;
cache_entry_object = smjs_get_cache_entry_object(download->cached);
if (!cache_entry_object) goto end;
args[0] = OBJECT_TO_JSVAL(cache_entry_object);
JS_CallFunction(smjs_ctx, NULL, hop->callback, 1, args, &rval);
JS_CallFunctionValue(smjs_ctx, NULL, hop->callback, 1, args, &rval);
end:
if (download->cached)
object_unlock(download->cached);
JS_RemoveRoot(smjs_ctx, &hop->callback);
mem_free(download->data);
mem_free(download);
@ -85,13 +89,20 @@ smjs_load_uri(JSContext *ctx, JSObject *obj, uintN argc, jsval *argv,
hop = mem_alloc(sizeof(*hop));
if (!hop) {
done_uri(uri);
mem_free(download);
done_uri(uri);
return JS_FALSE;
}
hop->callback = JS_ValueToFunction(smjs_ctx, argv[1]);
hop->callback = argv[1];
hop->ses = smjs_ses;
if (!JS_AddNamedRoot(smjs_ctx, &hop->callback,
"smjs_load_uri_hop.callback")) {
mem_free(hop);
mem_free(download);
done_uri(uri);
return JS_FALSE;
}
download->data = hop;
download->callback = (download_callback_T *) smjs_loading_callback;

View File

@ -8,7 +8,7 @@
#include "elinks.h"
#include "ecmascript/spidermonkey/util.h"
#include "ecmascript/spidermonkey-shared.h"
#include "protocol/uri.h"
#include "scripting/smjs/elinks_object.h"
#include "scripting/smjs/view_state_object.h"

View File

@ -30,6 +30,7 @@
#include "document/document.h"
#include "document/forms.h"
#include "document/view.h"
#include "ecmascript/ecmascript.h"
#include "intl/gettext/libintl.h"
#include "formhist/formhist.h"
#include "mime/mime.h"
@ -251,11 +252,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];
@ -327,6 +347,29 @@ 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);
}
/** Free @a fv and any data owned by it. This does not call
* del_from_list(fv), so the caller must usually do that first.
* @relates form_view */
void
done_form_view(struct form_view *fv)
{
#ifdef CONFIG_ECMASCRIPT
ecmascript_detach_form_view(fv);
#endif
mem_free(fv);
}
int
get_current_state(struct session *ses)

View File

@ -113,6 +113,9 @@ 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 *);
void done_form_view(struct form_view *);
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);

View File

@ -262,6 +262,7 @@ get_range(struct document *document, int y, int height, int l,
return 0;
}
#ifdef HAVE_REGEX_H
/** Returns a string @c doc that is a copy of the text in the search
* nodes from @a s1 to (@a s1 + @a doclen - 1) with the space at the
* end of each line converted to a new-line character (LF). */
@ -293,7 +294,6 @@ get_search_region_from_search_nodes(struct search *s1, struct search *s2,
return doc;
}
#ifdef HAVE_REGEX_H
struct regex_match_context {
struct search *s1;
struct search *s2;
@ -1579,14 +1579,17 @@ search_typeahead(struct session *ses, struct document_view *doc_view,
* a nice cleanup target ;-). --pasky */
enum search_option {
#ifdef HAVE_REGEX_H
SEARCH_OPT_REGEX,
#endif
SEARCH_OPT_CASE,
SEARCH_OPTIONS,
};
static struct option_resolver resolvers[] = {
#ifdef HAVE_REGEX_H
{ SEARCH_OPT_REGEX, "regex" },
#endif
{ SEARCH_OPT_CASE, "case" },
};
@ -1651,7 +1654,11 @@ search_dlg_do(struct terminal *term, struct memory_list *ml,
hop->values, SEARCH_OPTIONS);
hop->data = data;
#ifdef HAVE_REGEX_H
#define SEARCH_WIDGETS_COUNT 8
#else
#define SEARCH_WIDGETS_COUNT 5
#endif
dlg = calloc_dialog(SEARCH_WIDGETS_COUNT, MAX_STR_LEN);
if (!dlg) {
mem_free(hop);
@ -1671,9 +1678,11 @@ search_dlg_do(struct terminal *term, struct memory_list *ml,
field = get_dialog_offset(dlg, SEARCH_WIDGETS_COUNT);
add_dlg_field(dlg, text, 0, 0, NULL, MAX_STR_LEN, field, history);
#ifdef HAVE_REGEX_H
add_dlg_radio(dlg, _("Normal search", term), 1, 0, &hop->values[SEARCH_OPT_REGEX].number);
add_dlg_radio(dlg, _("Regexp search", term), 1, 1, &hop->values[SEARCH_OPT_REGEX].number);
add_dlg_radio(dlg, _("Extended regexp search", term), 1, 2, &hop->values[SEARCH_OPT_REGEX].number);
#endif
add_dlg_radio(dlg, _("Case sensitive", term), 2, 1, &hop->values[SEARCH_OPT_CASE].number);
add_dlg_radio(dlg, _("Case insensitive", term), 2, 0, &hop->values[SEARCH_OPT_CASE].number);

View File

@ -45,14 +45,20 @@ init_vs(struct view_state *vs, struct uri *uri, int plain)
void
destroy_vs(struct view_state *vs, int blast_ecmascript)
{
int i;
struct form_view *fv, *next;
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);
foreachsafe (fv, next, vs->forms) {
del_from_list(fv);
done_form_view(fv);
}
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)
ecmascript_put_interpreter(vs->ecmascript);
@ -79,6 +85,12 @@ copy_vs(struct view_state *dst, struct view_state *src)
dst->ecmascript_fragile = 1;
#endif
/* destroy_vs(vs) does mem_free_if(vs->form_info), so each
* view_state must have its own form_info. Normally we make a
* copy below, but not if src->form_info_len is 0, which it
* can be even if src->form_info is not NULL. */
dst->form_info = NULL;
/* Clean as a baby. */
dst->doc_view = NULL;
@ -108,6 +120,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). */

View File

@ -1,11 +1,9 @@
#!/usr/bin/env python
import os, time
from zlib import *
import os, time, zlib
data1 = '<html><body>Two lines should be visible.<br/>The second line.</body></html>'
ob = compressobj(Z_DEFAULT_COMPRESSION, DEFLATED, -MAX_WBITS)
cd1 = ob.compress(data1)
cd1 += ob.flush()
cd1 = zlib.compress(data1)
length = len(cd1)
next_chunk = hex(length - 10)[2:]

22
test/cgi/chunked_raw_deflate.py Executable file
View File

@ -0,0 +1,22 @@
#!/usr/bin/env python
import os, time
from zlib import *
# According to section 3.5 of RFC 2616, "Content-Encoding: deflate"
# requires a ZLIB header. However, Microsoft-IIS/6.0 sends a raw
# DEFLATE stream instead. This CGI tests how ELinks handles that.
data1 = '<html><body>Two lines should be visible.<br/>The second line.</body></html>'
ob = compressobj(Z_DEFAULT_COMPRESSION, DEFLATED, -MAX_WBITS)
cd1 = ob.compress(data1)
cd1 += ob.flush()
length = len(cd1)
next_chunk = hex(length - 10)[2:]
os.write(1, "Date: Sun, 20 Jan 2008 15:24:00 GMT\r\nServer: ddd\r\nTransfer-Encoding: chunked\r\nContent-Encoding: deflate\r\nConnection: close\r\nContent-Type: text/html; charset=ISO-8859-1\r\n")
os.write(1, "\r\na\r\n")
os.write(1, cd1[:10])
time.sleep(2)
os.write(1, "\r\n%s\r\n" % next_chunk)
os.write(1, cd1[10:])
os.write(1, "\r\n0\r\n")