2005-09-15 09:58:31 -04:00
|
|
|
/* The SpiderMonkey window object implementation. */
|
|
|
|
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
|
|
#include "config.h"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
|
|
|
|
#include "elinks.h"
|
|
|
|
|
|
|
|
#include "ecmascript/spidermonkey/util.h"
|
|
|
|
|
|
|
|
#include "bfu/dialog.h"
|
|
|
|
#include "cache/cache.h"
|
|
|
|
#include "cookies/cookies.h"
|
|
|
|
#include "dialogs/menu.h"
|
|
|
|
#include "dialogs/status.h"
|
|
|
|
#include "document/html/frames.h"
|
|
|
|
#include "document/document.h"
|
|
|
|
#include "document/forms.h"
|
|
|
|
#include "document/view.h"
|
|
|
|
#include "ecmascript/ecmascript.h"
|
2006-11-25 01:54:58 -05:00
|
|
|
#include "ecmascript/spidermonkey/document.h"
|
2005-09-15 09:58:31 -04:00
|
|
|
#include "ecmascript/spidermonkey/form.h"
|
2006-11-25 01:54:58 -05:00
|
|
|
#include "ecmascript/spidermonkey/window.h"
|
2005-09-15 09:58:31 -04:00
|
|
|
#include "intl/gettext/libintl.h"
|
|
|
|
#include "main/select.h"
|
|
|
|
#include "osdep/newwin.h"
|
|
|
|
#include "osdep/sysname.h"
|
|
|
|
#include "protocol/http/http.h"
|
|
|
|
#include "protocol/uri.h"
|
|
|
|
#include "session/history.h"
|
|
|
|
#include "session/location.h"
|
|
|
|
#include "session/session.h"
|
|
|
|
#include "session/task.h"
|
|
|
|
#include "terminal/tab.h"
|
|
|
|
#include "terminal/terminal.h"
|
|
|
|
#include "util/conv.h"
|
|
|
|
#include "util/memory.h"
|
|
|
|
#include "util/string.h"
|
|
|
|
#include "viewer/text/draw.h"
|
|
|
|
#include "viewer/text/form.h"
|
|
|
|
#include "viewer/text/link.h"
|
|
|
|
#include "viewer/text/vs.h"
|
|
|
|
|
|
|
|
|
2006-11-25 01:54:58 -05:00
|
|
|
static const JSClass form_class; /* defined below */
|
|
|
|
|
|
|
|
|
2005-09-15 09:58:31 -04:00
|
|
|
/* Accordingly to the JS specs, each input type should own object. That'd be a
|
|
|
|
* huge PITA though, however DOM comes to the rescue and defines just a single
|
|
|
|
* HTMLInputElement. The difference could be spotted only by some clever tricky
|
|
|
|
* JS code, but I hope it doesn't matter anywhere. --pasky */
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
2007-06-24 11:46:22 -04:00
|
|
|
/* 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
|
|
|
|
};
|
|
|
|
|
2006-11-23 16:33:43 -05:00
|
|
|
/* Each @input_class object must have a @form_class parent. */
|
2005-09-15 09:58:31 -04:00
|
|
|
static const JSClass input_class = {
|
|
|
|
"input", /* here, we unleash ourselves */
|
2007-06-24 11:46:22 -04:00
|
|
|
JSCLASS_HAS_RESERVED_SLOTS(JSRS_INPUT_COUNT),
|
2005-09-15 09:58:31 -04:00
|
|
|
JS_PropertyStub, JS_PropertyStub,
|
|
|
|
input_get_property, input_set_property,
|
|
|
|
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub
|
|
|
|
};
|
|
|
|
|
2006-12-06 16:09:14 -05:00
|
|
|
/* Tinyids of properties. Use negative values to distinguish these
|
|
|
|
* from array indexes (even though this object has no array elements).
|
|
|
|
* ECMAScript code should not use these directly as in input[-1];
|
|
|
|
* future versions of ELinks may change the numbers. */
|
2005-09-15 09:58:31 -04:00
|
|
|
enum input_prop {
|
2006-12-06 16:09:14 -05:00
|
|
|
JSP_INPUT_ACCESSKEY = -1,
|
|
|
|
JSP_INPUT_ALT = -2,
|
|
|
|
JSP_INPUT_CHECKED = -3,
|
|
|
|
JSP_INPUT_DEFAULT_CHECKED = -4,
|
|
|
|
JSP_INPUT_DEFAULT_VALUE = -5,
|
|
|
|
JSP_INPUT_DISABLED = -6,
|
|
|
|
JSP_INPUT_FORM = -7,
|
|
|
|
JSP_INPUT_MAX_LENGTH = -8,
|
|
|
|
JSP_INPUT_NAME = -9,
|
|
|
|
JSP_INPUT_READONLY = -10,
|
|
|
|
JSP_INPUT_SELECTED_INDEX = -11,
|
|
|
|
JSP_INPUT_SIZE = -12,
|
|
|
|
JSP_INPUT_SRC = -13,
|
|
|
|
JSP_INPUT_TABINDEX = -14,
|
|
|
|
JSP_INPUT_TYPE = -15,
|
|
|
|
JSP_INPUT_VALUE = -16,
|
2005-09-15 09:58:31 -04:00
|
|
|
};
|
|
|
|
|
|
|
|
/* XXX: Some of those are marked readonly just because we can't change them
|
|
|
|
* safely now. Changing default* values would affect all open instances of the
|
|
|
|
* document, leading to a potential security risk. Changing size and type would
|
|
|
|
* require re-rendering the document (TODO), tabindex would require renumbering
|
|
|
|
* of all links and whatnot. --pasky */
|
|
|
|
static const JSPropertySpec input_props[] = {
|
|
|
|
{ "accessKey", JSP_INPUT_ACCESSKEY, JSPROP_ENUMERATE },
|
|
|
|
{ "alt", JSP_INPUT_ALT, JSPROP_ENUMERATE },
|
|
|
|
{ "checked", JSP_INPUT_CHECKED, JSPROP_ENUMERATE },
|
|
|
|
{ "defaultChecked",JSP_INPUT_DEFAULT_CHECKED,JSPROP_ENUMERATE },
|
|
|
|
{ "defaultValue",JSP_INPUT_DEFAULT_VALUE,JSPROP_ENUMERATE },
|
|
|
|
{ "disabled", JSP_INPUT_DISABLED, JSPROP_ENUMERATE },
|
|
|
|
{ "form", JSP_INPUT_FORM, JSPROP_ENUMERATE | JSPROP_READONLY },
|
|
|
|
{ "maxLength", JSP_INPUT_MAX_LENGTH, JSPROP_ENUMERATE },
|
|
|
|
{ "name", JSP_INPUT_NAME, JSPROP_ENUMERATE },
|
|
|
|
{ "readonly", JSP_INPUT_READONLY, JSPROP_ENUMERATE },
|
2006-05-02 03:36:23 -04:00
|
|
|
{ "selectedIndex",JSP_INPUT_SELECTED_INDEX,JSPROP_ENUMERATE },
|
2005-09-15 09:58:31 -04:00
|
|
|
{ "size", JSP_INPUT_SIZE, JSPROP_ENUMERATE | JSPROP_READONLY },
|
|
|
|
{ "src", JSP_INPUT_SRC, JSPROP_ENUMERATE },
|
|
|
|
{ "tabindex", JSP_INPUT_TABINDEX, JSPROP_ENUMERATE | JSPROP_READONLY },
|
|
|
|
{ "type", JSP_INPUT_TYPE, JSPROP_ENUMERATE | JSPROP_READONLY },
|
|
|
|
{ "value", JSP_INPUT_VALUE, JSPROP_ENUMERATE },
|
|
|
|
{ NULL }
|
|
|
|
};
|
|
|
|
|
|
|
|
static JSBool input_blur(JSContext *ctx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);
|
|
|
|
static JSBool input_click(JSContext *ctx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);
|
|
|
|
static JSBool input_focus(JSContext *ctx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);
|
|
|
|
static JSBool input_select(JSContext *ctx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);
|
|
|
|
|
|
|
|
static const JSFunctionSpec input_funcs[] = {
|
|
|
|
{ "blur", input_blur, 0 },
|
|
|
|
{ "click", input_click, 0 },
|
|
|
|
{ "focus", input_focus, 0 },
|
|
|
|
{ "select", input_select, 0 },
|
|
|
|
{ NULL }
|
|
|
|
};
|
|
|
|
|
2006-08-20 06:37:03 -04:00
|
|
|
static JSString *unicode_to_jsstring(JSContext *ctx, unicode_val_T u);
|
2006-08-20 10:10:08 -04:00
|
|
|
static unicode_val_T jsval_to_accesskey(JSContext *ctx, jsval *vp);
|
2006-08-20 06:37:03 -04:00
|
|
|
|
2007-05-22 10:07:06 -04:00
|
|
|
|
|
|
|
static struct form_state *
|
|
|
|
input_get_form_state(JSContext *ctx, JSObject *obj, struct view_state *vs)
|
|
|
|
{
|
2007-06-24 11:46:22 -04:00
|
|
|
jsval val;
|
|
|
|
int n;
|
|
|
|
JSBool ok;
|
2007-05-22 10:07:06 -04:00
|
|
|
|
2007-06-24 11:46:22 -04:00
|
|
|
ok = JS_GetReservedSlot(ctx, obj, JSRS_INPUT_FSINDEX, &val);
|
|
|
|
assert(ok);
|
|
|
|
assert(JSVAL_IS_INT(val));
|
|
|
|
if_assert_failed return NULL;
|
|
|
|
|
|
|
|
n = JSVAL_TO_INT(val);
|
2007-05-27 12:18:44 -04:00
|
|
|
assert(n >= 0);
|
|
|
|
assert(n < vs->form_info_len);
|
|
|
|
if_assert_failed return NULL;
|
2007-06-24 11:46:22 -04:00
|
|
|
|
2007-05-22 10:07:06 -04:00
|
|
|
return &vs->form_info[n];
|
|
|
|
}
|
|
|
|
|
2006-11-23 16:33:43 -05:00
|
|
|
/* @input_class.getProperty */
|
2005-09-15 09:58:31 -04:00
|
|
|
static JSBool
|
|
|
|
input_get_property(JSContext *ctx, JSObject *obj, jsval id, jsval *vp)
|
|
|
|
{
|
2006-11-24 01:50:12 -05:00
|
|
|
JSObject *parent_form; /* instance of @form_class */
|
|
|
|
JSObject *parent_doc; /* instance of @document_class */
|
|
|
|
JSObject *parent_win; /* instance of @window_class */
|
|
|
|
struct view_state *vs;
|
|
|
|
struct document_view *doc_view;
|
|
|
|
struct document *document;
|
|
|
|
struct form_state *fs;
|
|
|
|
struct form_control *fc;
|
2005-09-15 09:58:31 -04:00
|
|
|
int linknum;
|
|
|
|
struct link *link = NULL;
|
|
|
|
|
2006-12-03 04:14:22 -05:00
|
|
|
/* This can be called if @obj if not itself an instance of the
|
|
|
|
* appropriate class but has one in its prototype chain. Fail
|
|
|
|
* such calls. */
|
|
|
|
if (!JS_InstanceOf(ctx, obj, (JSClass *) &input_class, NULL))
|
|
|
|
return JS_FALSE;
|
2006-11-24 01:50:12 -05:00
|
|
|
parent_form = JS_GetParent(ctx, obj);
|
2006-11-25 01:54:58 -05:00
|
|
|
assert(JS_InstanceOf(ctx, parent_form, (JSClass *) &form_class, NULL));
|
|
|
|
if_assert_failed return JS_FALSE;
|
2006-11-24 01:50:12 -05:00
|
|
|
parent_doc = JS_GetParent(ctx, parent_form);
|
2006-11-25 01:54:58 -05:00
|
|
|
assert(JS_InstanceOf(ctx, parent_doc, (JSClass *) &document_class, NULL));
|
|
|
|
if_assert_failed return JS_FALSE;
|
2006-11-24 01:50:12 -05:00
|
|
|
parent_win = JS_GetParent(ctx, parent_doc);
|
2006-11-25 01:54:58 -05:00
|
|
|
assert(JS_InstanceOf(ctx, parent_win, (JSClass *) &window_class, NULL));
|
|
|
|
if_assert_failed return JS_FALSE;
|
|
|
|
|
2007-05-27 11:32:53 -04:00
|
|
|
vs = JS_GetInstancePrivate(ctx, parent_win,
|
|
|
|
(JSClass *) &window_class, NULL);
|
2006-11-24 01:50:12 -05:00
|
|
|
doc_view = vs->doc_view;
|
|
|
|
document = doc_view->document;
|
2007-05-22 10:07:06 -04:00
|
|
|
fs = input_get_form_state(ctx, obj, vs);
|
2006-11-24 01:50:12 -05:00
|
|
|
fc = find_form_control(document, fs);
|
|
|
|
|
2005-09-15 09:58:31 -04:00
|
|
|
assert(fc);
|
|
|
|
assert(fc->form && fs);
|
|
|
|
|
|
|
|
if (!JSVAL_IS_INT(id))
|
|
|
|
return JS_TRUE;
|
|
|
|
|
|
|
|
linknum = get_form_control_link(document, fc);
|
|
|
|
/* Hiddens have no link. */
|
|
|
|
if (linknum >= 0) link = &document->links[linknum];
|
|
|
|
|
|
|
|
undef_to_jsval(ctx, vp);
|
|
|
|
|
|
|
|
switch (JSVAL_TO_INT(id)) {
|
|
|
|
case JSP_INPUT_ACCESSKEY:
|
|
|
|
{
|
2006-08-20 06:37:03 -04:00
|
|
|
JSString *keystr;
|
2005-09-15 09:58:31 -04:00
|
|
|
|
|
|
|
if (!link) break;
|
|
|
|
|
2006-08-20 06:37:03 -04:00
|
|
|
if (!link->accesskey) {
|
|
|
|
*vp = JS_GetEmptyStringValue(ctx);
|
|
|
|
} else {
|
|
|
|
keystr = unicode_to_jsstring(ctx, link->accesskey);
|
|
|
|
if (keystr)
|
|
|
|
*vp = STRING_TO_JSVAL(keystr);
|
|
|
|
else
|
|
|
|
return JS_FALSE;
|
|
|
|
}
|
2005-09-15 09:58:31 -04:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case JSP_INPUT_ALT:
|
|
|
|
string_to_jsval(ctx, vp, fc->alt);
|
|
|
|
break;
|
|
|
|
case JSP_INPUT_CHECKED:
|
|
|
|
boolean_to_jsval(ctx, vp, fs->state);
|
|
|
|
break;
|
|
|
|
case JSP_INPUT_DEFAULT_CHECKED:
|
|
|
|
boolean_to_jsval(ctx, vp, fc->default_state);
|
|
|
|
break;
|
|
|
|
case JSP_INPUT_DEFAULT_VALUE:
|
Bug 784: Keep form_control.default_value in the document charset.
Previously, html_special_form_control converted
form_control.default_value to the terminal charset, and init_form_state
then copied the value to form_state.value. However, when CONFIG_UTF8
is defined and UTF-8 I/O is enabled, form_state.value is supposed to
be in UTF-8, rather than in the terminal charset.
This mismatch could not be conveniently fixed in
html_special_form_control because that does not know which terminal is
being used and whether UTF-8 I/O is enabled there. Also, constructing
a conversion table from the document charset to form_state.value could
have ruined renderer_context.convert_table, because src/intl/charsets.c
does not support multiple concurrent conversion tables.
So instead, we now keep form_control.default_value in the document
charset, and convert it in the viewer each time it is needed. Because
the result of the conversion is kept in form_state.value between
incremental renderings, this shouldn't even slow things down too much.
I am not implementing the proper charset conversions for the DOM
defaultValue property yet, because the current code doesn't have
them for other string properties either, and bug 805 is already open
for that.
2007-04-29 15:01:13 -04:00
|
|
|
/* FIXME (bug 805): convert from the charset of the document */
|
2005-09-15 09:58:31 -04:00
|
|
|
string_to_jsval(ctx, vp, fc->default_value);
|
|
|
|
break;
|
|
|
|
case JSP_INPUT_DISABLED:
|
|
|
|
/* FIXME: <input readonly disabled> --pasky */
|
|
|
|
boolean_to_jsval(ctx, vp, fc->mode == FORM_MODE_DISABLED);
|
|
|
|
break;
|
|
|
|
case JSP_INPUT_FORM:
|
|
|
|
object_to_jsval(ctx, vp, parent_form);
|
|
|
|
break;
|
|
|
|
case JSP_INPUT_MAX_LENGTH:
|
|
|
|
int_to_jsval(ctx, vp, fc->maxlength);
|
|
|
|
break;
|
|
|
|
case JSP_INPUT_NAME:
|
|
|
|
string_to_jsval(ctx, vp, fc->name);
|
|
|
|
break;
|
|
|
|
case JSP_INPUT_READONLY:
|
|
|
|
/* FIXME: <input readonly disabled> --pasky */
|
|
|
|
boolean_to_jsval(ctx, vp, fc->mode == FORM_MODE_READONLY);
|
|
|
|
break;
|
|
|
|
case JSP_INPUT_SIZE:
|
|
|
|
int_to_jsval(ctx, vp, fc->size);
|
|
|
|
break;
|
|
|
|
case JSP_INPUT_SRC:
|
|
|
|
if (link && link->where_img)
|
|
|
|
string_to_jsval(ctx, vp, link->where_img);
|
|
|
|
break;
|
|
|
|
case JSP_INPUT_TABINDEX:
|
|
|
|
if (link)
|
|
|
|
/* FIXME: This is WRONG. --pasky */
|
|
|
|
int_to_jsval(ctx, vp, link->number);
|
|
|
|
break;
|
|
|
|
case JSP_INPUT_TYPE:
|
|
|
|
{
|
|
|
|
unsigned char *s = NULL;
|
|
|
|
|
|
|
|
switch (fc->type) {
|
|
|
|
case FC_TEXT: s = "text"; break;
|
|
|
|
case FC_PASSWORD: s = "password"; break;
|
|
|
|
case FC_FILE: s = "file"; break;
|
|
|
|
case FC_CHECKBOX: s = "checkbox"; break;
|
|
|
|
case FC_RADIO: s = "radio"; break;
|
|
|
|
case FC_SUBMIT: s = "submit"; break;
|
|
|
|
case FC_IMAGE: s = "image"; break;
|
|
|
|
case FC_RESET: s = "reset"; break;
|
|
|
|
case FC_BUTTON: s = "button"; break;
|
|
|
|
case FC_HIDDEN: s = "hidden"; break;
|
2006-05-02 03:36:23 -04:00
|
|
|
case FC_SELECT: s = "select"; break;
|
2005-09-15 09:58:31 -04:00
|
|
|
default: INTERNAL("input_get_property() upon a non-input item."); break;
|
|
|
|
}
|
|
|
|
string_to_jsval(ctx, vp, s);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case JSP_INPUT_VALUE:
|
|
|
|
string_to_jsval(ctx, vp, fs->value);
|
|
|
|
break;
|
|
|
|
|
2006-05-02 03:36:23 -04:00
|
|
|
case JSP_INPUT_SELECTED_INDEX:
|
|
|
|
if (fc->type == FC_SELECT) int_to_jsval(ctx, vp, fs->state);
|
|
|
|
break;
|
2005-09-15 09:58:31 -04:00
|
|
|
default:
|
2007-05-27 11:36:31 -04:00
|
|
|
/* Unrecognized integer property ID; someone is using
|
|
|
|
* the object as an array. SMJS builtin classes (e.g.
|
2006-12-03 05:07:07 -05:00
|
|
|
* js_RegExpClass) just return JS_TRUE in this case
|
|
|
|
* and leave *@vp unchanged. Do the same here.
|
|
|
|
* (Actually not quite the same, as we already used
|
|
|
|
* @undef_to_jsval.) */
|
2005-09-15 09:58:31 -04:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return JS_TRUE;
|
|
|
|
}
|
|
|
|
|
2006-11-23 16:33:43 -05:00
|
|
|
/* @input_class.setProperty */
|
2005-09-15 09:58:31 -04:00
|
|
|
static JSBool
|
|
|
|
input_set_property(JSContext *ctx, JSObject *obj, jsval id, jsval *vp)
|
|
|
|
{
|
2006-11-24 01:50:12 -05:00
|
|
|
JSObject *parent_form; /* instance of @form_class */
|
|
|
|
JSObject *parent_doc; /* instance of @document_class */
|
|
|
|
JSObject *parent_win; /* instance of @window_class */
|
|
|
|
struct view_state *vs;
|
|
|
|
struct document_view *doc_view;
|
|
|
|
struct document *document;
|
|
|
|
struct form_state *fs;
|
|
|
|
struct form_control *fc;
|
2005-09-15 09:58:31 -04:00
|
|
|
int linknum;
|
|
|
|
struct link *link = NULL;
|
2006-08-20 10:10:08 -04:00
|
|
|
unicode_val_T accesskey;
|
2005-09-15 09:58:31 -04:00
|
|
|
|
2006-12-03 04:14:22 -05:00
|
|
|
/* This can be called if @obj if not itself an instance of the
|
|
|
|
* appropriate class but has one in its prototype chain. Fail
|
|
|
|
* such calls. */
|
|
|
|
if (!JS_InstanceOf(ctx, obj, (JSClass *) &input_class, NULL))
|
|
|
|
return JS_FALSE;
|
2006-11-24 01:50:12 -05:00
|
|
|
parent_form = JS_GetParent(ctx, obj);
|
2006-11-25 01:54:58 -05:00
|
|
|
assert(JS_InstanceOf(ctx, parent_form, (JSClass *) &form_class, NULL));
|
|
|
|
if_assert_failed return JS_FALSE;
|
2006-11-24 01:50:12 -05:00
|
|
|
parent_doc = JS_GetParent(ctx, parent_form);
|
2006-11-25 01:54:58 -05:00
|
|
|
assert(JS_InstanceOf(ctx, parent_doc, (JSClass *) &document_class, NULL));
|
|
|
|
if_assert_failed return JS_FALSE;
|
2006-11-24 01:50:12 -05:00
|
|
|
parent_win = JS_GetParent(ctx, parent_doc);
|
2006-11-25 01:54:58 -05:00
|
|
|
assert(JS_InstanceOf(ctx, parent_win, (JSClass *) &window_class, NULL));
|
|
|
|
if_assert_failed return JS_FALSE;
|
|
|
|
|
2007-05-27 11:32:53 -04:00
|
|
|
vs = JS_GetInstancePrivate(ctx, parent_win,
|
|
|
|
(JSClass *) &window_class, NULL);
|
2006-11-24 01:50:12 -05:00
|
|
|
doc_view = vs->doc_view;
|
|
|
|
document = doc_view->document;
|
2007-05-22 10:07:06 -04:00
|
|
|
fs = input_get_form_state(ctx, obj, vs);
|
2006-11-24 01:50:12 -05:00
|
|
|
fc = find_form_control(document, fs);
|
|
|
|
|
2005-09-15 09:58:31 -04:00
|
|
|
assert(fc);
|
|
|
|
assert(fc->form && fs);
|
|
|
|
|
|
|
|
if (!JSVAL_IS_INT(id))
|
|
|
|
return JS_TRUE;
|
|
|
|
|
|
|
|
linknum = get_form_control_link(document, fc);
|
|
|
|
/* Hiddens have no link. */
|
|
|
|
if (linknum >= 0) link = &document->links[linknum];
|
|
|
|
|
|
|
|
switch (JSVAL_TO_INT(id)) {
|
|
|
|
case JSP_INPUT_ACCESSKEY:
|
2006-08-20 10:10:08 -04:00
|
|
|
accesskey = jsval_to_accesskey(ctx, vp);
|
|
|
|
if (accesskey == UCS_NO_CHAR)
|
|
|
|
return JS_FALSE;
|
|
|
|
else if (link)
|
|
|
|
link->accesskey = accesskey;
|
2005-09-15 09:58:31 -04:00
|
|
|
break;
|
|
|
|
case JSP_INPUT_ALT:
|
|
|
|
mem_free_set(&fc->alt, stracpy(jsval_to_string(ctx, vp)));
|
|
|
|
break;
|
|
|
|
case JSP_INPUT_CHECKED:
|
|
|
|
if (fc->type != FC_CHECKBOX && fc->type != FC_RADIO)
|
|
|
|
break;
|
|
|
|
fs->state = jsval_to_boolean(ctx, vp);
|
|
|
|
break;
|
|
|
|
case JSP_INPUT_DISABLED:
|
|
|
|
/* FIXME: <input readonly disabled> --pasky */
|
|
|
|
fc->mode = (jsval_to_boolean(ctx, vp) ? FORM_MODE_DISABLED
|
|
|
|
: fc->mode == FORM_MODE_READONLY ? FORM_MODE_READONLY
|
|
|
|
: FORM_MODE_NORMAL);
|
|
|
|
break;
|
|
|
|
case JSP_INPUT_MAX_LENGTH:
|
2007-05-29 20:10:54 -04:00
|
|
|
if (!JS_ValueToInt32(ctx, *vp, &fc->maxlength))
|
2007-05-27 11:09:37 -04:00
|
|
|
return JS_FALSE;
|
2005-09-15 09:58:31 -04:00
|
|
|
break;
|
|
|
|
case JSP_INPUT_NAME:
|
|
|
|
mem_free_set(&fc->name, stracpy(jsval_to_string(ctx, vp)));
|
|
|
|
break;
|
|
|
|
case JSP_INPUT_READONLY:
|
|
|
|
/* FIXME: <input readonly disabled> --pasky */
|
|
|
|
fc->mode = (jsval_to_boolean(ctx, vp) ? FORM_MODE_READONLY
|
|
|
|
: fc->mode == FORM_MODE_DISABLED ? FORM_MODE_DISABLED
|
|
|
|
: FORM_MODE_NORMAL);
|
|
|
|
break;
|
|
|
|
case JSP_INPUT_SRC:
|
|
|
|
if (link) {
|
|
|
|
mem_free_set(&link->where_img, stracpy(jsval_to_string(ctx, vp)));
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case JSP_INPUT_VALUE:
|
|
|
|
if (fc->type == FC_FILE)
|
|
|
|
break; /* A huge security risk otherwise. */
|
|
|
|
mem_free_set(&fs->value, stracpy(jsval_to_string(ctx, vp)));
|
|
|
|
if (fc->type == FC_TEXT || fc->type == FC_PASSWORD)
|
|
|
|
fs->state = strlen(fs->value);
|
|
|
|
break;
|
2006-05-02 05:28:41 -04:00
|
|
|
case JSP_INPUT_SELECTED_INDEX:
|
|
|
|
if (fc->type == FC_SELECT) {
|
2007-05-22 03:36:39 -04:00
|
|
|
int item;
|
|
|
|
|
2007-05-27 11:09:37 -04:00
|
|
|
if (!JS_ValueToInt32(ctx, *vp, &item))
|
|
|
|
return JS_FALSE;
|
2006-05-02 05:28:41 -04:00
|
|
|
|
|
|
|
if (item >= 0 && item < fc->nvalues) {
|
|
|
|
fs->state = item;
|
|
|
|
mem_free_set(&fs->value, stracpy(fc->values[item]));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
2005-09-15 09:58:31 -04:00
|
|
|
|
|
|
|
default:
|
2007-05-27 11:36:31 -04:00
|
|
|
/* Unrecognized integer property ID; someone is using
|
|
|
|
* the object as an array. SMJS builtin classes (e.g.
|
2006-12-03 05:07:07 -05:00
|
|
|
* js_RegExpClass) just return JS_TRUE in this case.
|
|
|
|
* Do the same here. */
|
2005-09-15 09:58:31 -04:00
|
|
|
return JS_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
return JS_TRUE;
|
|
|
|
}
|
|
|
|
|
2006-11-23 16:33:43 -05:00
|
|
|
/* @input_funcs{"blur"} */
|
2005-09-15 09:58:31 -04:00
|
|
|
static JSBool
|
|
|
|
input_blur(JSContext *ctx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
|
|
|
|
{
|
|
|
|
/* We are a text-mode browser and there *always* has to be something
|
|
|
|
* selected. So we do nothing for now. (That was easy.) */
|
|
|
|
return JS_TRUE;
|
|
|
|
}
|
|
|
|
|
2006-11-23 16:33:43 -05:00
|
|
|
/* @input_funcs{"click"} */
|
2005-09-15 09:58:31 -04:00
|
|
|
static JSBool
|
|
|
|
input_click(JSContext *ctx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
|
|
|
|
{
|
2006-11-24 01:50:12 -05:00
|
|
|
JSObject *parent_form; /* instance of @form_class */
|
|
|
|
JSObject *parent_doc; /* instance of @document_class */
|
|
|
|
JSObject *parent_win; /* instance of @window_class */
|
|
|
|
struct view_state *vs;
|
|
|
|
struct document_view *doc_view;
|
|
|
|
struct document *document;
|
|
|
|
struct session *ses;
|
|
|
|
struct form_state *fs;
|
2005-09-15 09:58:31 -04:00
|
|
|
struct form_control *fc;
|
|
|
|
int linknum;
|
|
|
|
|
2006-11-25 01:54:58 -05:00
|
|
|
if (!JS_InstanceOf(ctx, obj, (JSClass *) &input_class, argv)) return JS_FALSE;
|
2006-11-24 01:50:12 -05:00
|
|
|
parent_form = JS_GetParent(ctx, obj);
|
2006-11-25 01:54:58 -05:00
|
|
|
assert(JS_InstanceOf(ctx, parent_form, (JSClass *) &form_class, NULL));
|
|
|
|
if_assert_failed return JS_FALSE;
|
2006-11-24 01:50:12 -05:00
|
|
|
parent_doc = JS_GetParent(ctx, parent_form);
|
2006-11-25 01:54:58 -05:00
|
|
|
assert(JS_InstanceOf(ctx, parent_doc, (JSClass *) &document_class, NULL));
|
|
|
|
if_assert_failed return JS_FALSE;
|
2006-11-24 01:50:12 -05:00
|
|
|
parent_win = JS_GetParent(ctx, parent_doc);
|
2006-11-25 01:54:58 -05:00
|
|
|
assert(JS_InstanceOf(ctx, parent_win, (JSClass *) &window_class, NULL));
|
|
|
|
if_assert_failed return JS_FALSE;
|
|
|
|
|
2007-05-27 11:32:53 -04:00
|
|
|
vs = JS_GetInstancePrivate(ctx, parent_win,
|
|
|
|
(JSClass *) &window_class, NULL);
|
2006-11-24 01:50:12 -05:00
|
|
|
doc_view = vs->doc_view;
|
|
|
|
document = doc_view->document;
|
|
|
|
ses = doc_view->session;
|
2007-05-22 10:07:06 -04:00
|
|
|
fs = input_get_form_state(ctx, obj, vs);
|
2006-11-24 01:50:12 -05:00
|
|
|
|
2005-09-15 09:58:31 -04:00
|
|
|
assert(fs);
|
|
|
|
fc = find_form_control(document, fs);
|
|
|
|
assert(fc);
|
|
|
|
|
|
|
|
linknum = get_form_control_link(document, fc);
|
|
|
|
/* Hiddens have no link. */
|
|
|
|
if (linknum < 0)
|
|
|
|
return JS_TRUE;
|
|
|
|
|
|
|
|
/* Restore old current_link afterwards? */
|
|
|
|
jump_to_link_number(ses, doc_view, linknum);
|
|
|
|
if (enter(ses, doc_view, 0) == FRAME_EVENT_REFRESH)
|
|
|
|
refresh_view(ses, doc_view, 0);
|
|
|
|
else
|
|
|
|
print_screen_status(ses);
|
|
|
|
|
|
|
|
boolean_to_jsval(ctx, rval, 0);
|
|
|
|
return JS_TRUE;
|
|
|
|
}
|
|
|
|
|
2006-11-23 16:33:43 -05:00
|
|
|
/* @input_funcs{"focus"} */
|
2005-09-15 09:58:31 -04:00
|
|
|
static JSBool
|
|
|
|
input_focus(JSContext *ctx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
|
|
|
|
{
|
2006-11-24 01:50:12 -05:00
|
|
|
JSObject *parent_form; /* instance of @form_class */
|
|
|
|
JSObject *parent_doc; /* instance of @document_class */
|
|
|
|
JSObject *parent_win; /* instance of @window_class */
|
|
|
|
struct view_state *vs;
|
|
|
|
struct document_view *doc_view;
|
|
|
|
struct document *document;
|
|
|
|
struct session *ses;
|
|
|
|
struct form_state *fs;
|
2005-09-15 09:58:31 -04:00
|
|
|
struct form_control *fc;
|
|
|
|
int linknum;
|
|
|
|
|
2006-11-25 01:54:58 -05:00
|
|
|
if (!JS_InstanceOf(ctx, obj, (JSClass *) &input_class, argv)) return JS_FALSE;
|
2006-11-24 01:50:12 -05:00
|
|
|
parent_form = JS_GetParent(ctx, obj);
|
2006-11-25 01:54:58 -05:00
|
|
|
assert(JS_InstanceOf(ctx, parent_form, (JSClass *) &form_class, NULL));
|
|
|
|
if_assert_failed return JS_FALSE;
|
2006-11-24 01:50:12 -05:00
|
|
|
parent_doc = JS_GetParent(ctx, parent_form);
|
2006-11-25 01:54:58 -05:00
|
|
|
assert(JS_InstanceOf(ctx, parent_doc, (JSClass *) &document_class, NULL));
|
|
|
|
if_assert_failed return JS_FALSE;
|
2006-11-24 01:50:12 -05:00
|
|
|
parent_win = JS_GetParent(ctx, parent_doc);
|
2006-11-25 01:54:58 -05:00
|
|
|
assert(JS_InstanceOf(ctx, parent_win, (JSClass *) &window_class, NULL));
|
|
|
|
if_assert_failed return JS_FALSE;
|
|
|
|
|
2007-05-27 11:32:53 -04:00
|
|
|
vs = JS_GetInstancePrivate(ctx, parent_win,
|
|
|
|
(JSClass *) &window_class, NULL);
|
2006-11-24 01:50:12 -05:00
|
|
|
doc_view = vs->doc_view;
|
|
|
|
document = doc_view->document;
|
|
|
|
ses = doc_view->session;
|
2007-05-22 10:07:06 -04:00
|
|
|
fs = input_get_form_state(ctx, obj, vs);
|
2006-11-24 01:50:12 -05:00
|
|
|
|
2005-09-15 09:58:31 -04:00
|
|
|
assert(fs);
|
|
|
|
fc = find_form_control(document, fs);
|
|
|
|
assert(fc);
|
|
|
|
|
|
|
|
linknum = get_form_control_link(document, fc);
|
|
|
|
/* Hiddens have no link. */
|
|
|
|
if (linknum < 0)
|
|
|
|
return JS_TRUE;
|
|
|
|
|
|
|
|
jump_to_link_number(ses, doc_view, linknum);
|
|
|
|
|
|
|
|
boolean_to_jsval(ctx, rval, 0);
|
|
|
|
return JS_TRUE;
|
|
|
|
}
|
|
|
|
|
2006-11-23 16:33:43 -05:00
|
|
|
/* @input_funcs{"select"} */
|
2005-09-15 09:58:31 -04:00
|
|
|
static JSBool
|
|
|
|
input_select(JSContext *ctx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
|
|
|
|
{
|
|
|
|
/* We support no text selecting yet. So we do nothing for now.
|
|
|
|
* (That was easy, too.) */
|
|
|
|
return JS_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static JSObject *
|
2007-05-22 10:07:06 -04:00
|
|
|
get_input_object(JSContext *ctx, JSObject *jsform, long number)
|
2005-09-15 09:58:31 -04:00
|
|
|
{
|
2006-01-30 05:15:04 -05:00
|
|
|
#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 */
|
|
|
|
JSObject *jsinput = JS_NewObject(ctx, (JSClass *) &input_class, NULL, jsform);
|
|
|
|
|
|
|
|
JS_DefineProperties(ctx, jsinput, (JSPropertySpec *) input_props);
|
|
|
|
JS_DefineFunctions(ctx, jsinput, (JSFunctionSpec *) input_funcs);
|
2007-06-24 11:46:22 -04:00
|
|
|
JS_SetReservedSlot(ctx, jsinput, JSRS_INPUT_FSINDEX, INT_TO_JSVAL(number));
|
2007-05-22 10:07:06 -04:00
|
|
|
return jsinput;;
|
2005-09-15 09:58:31 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static JSObject *
|
2007-05-22 10:07:06 -04:00
|
|
|
get_form_control_object(JSContext *ctx, JSObject *jsform, enum form_type type, int number)
|
2005-09-15 09:58:31 -04:00
|
|
|
{
|
|
|
|
switch (type) {
|
|
|
|
case FC_TEXT:
|
|
|
|
case FC_PASSWORD:
|
|
|
|
case FC_FILE:
|
|
|
|
case FC_CHECKBOX:
|
|
|
|
case FC_RADIO:
|
|
|
|
case FC_SUBMIT:
|
|
|
|
case FC_IMAGE:
|
|
|
|
case FC_RESET:
|
|
|
|
case FC_BUTTON:
|
|
|
|
case FC_HIDDEN:
|
2006-05-02 03:36:23 -04:00
|
|
|
case FC_SELECT:
|
2007-05-22 10:07:06 -04:00
|
|
|
return get_input_object(ctx, jsform, (long)number);
|
2005-09-15 09:58:31 -04:00
|
|
|
|
|
|
|
case FC_TEXTAREA:
|
|
|
|
/* TODO */
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
default:
|
|
|
|
INTERNAL("Weird fc->type %d", type);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static JSBool form_elements_get_property(JSContext *ctx, JSObject *obj, jsval id, jsval *vp);
|
|
|
|
|
2006-11-26 10:35:33 -05:00
|
|
|
/* Each @form_elements_class object must have a @form_class parent. */
|
2005-09-15 09:58:31 -04:00
|
|
|
static const JSClass form_elements_class = {
|
|
|
|
"elements",
|
|
|
|
JSCLASS_HAS_PRIVATE,
|
|
|
|
JS_PropertyStub, JS_PropertyStub,
|
|
|
|
form_elements_get_property, JS_PropertyStub,
|
|
|
|
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub
|
|
|
|
};
|
|
|
|
|
|
|
|
static JSBool form_elements_item(JSContext *ctx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);
|
|
|
|
static JSBool form_elements_namedItem(JSContext *ctx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);
|
|
|
|
|
|
|
|
static const JSFunctionSpec form_elements_funcs[] = {
|
|
|
|
{ "item", form_elements_item, 1 },
|
|
|
|
{ "namedItem", form_elements_namedItem, 1 },
|
|
|
|
{ NULL }
|
|
|
|
};
|
|
|
|
|
2006-12-06 16:09:14 -05:00
|
|
|
/* Tinyids of properties. Use negative values to distinguish these
|
|
|
|
* from array indexes (elements[INT] for INT>=0 is equivalent to
|
|
|
|
* elements.item(INT)). ECMAScript code should not use these directly
|
|
|
|
* as in elements[-1]; future versions of ELinks may change the numbers. */
|
|
|
|
enum form_elements_prop {
|
|
|
|
JSP_FORM_ELEMENTS_LENGTH = -1,
|
|
|
|
};
|
2005-09-15 09:58:31 -04:00
|
|
|
static const JSPropertySpec form_elements_props[] = {
|
|
|
|
{ "length", JSP_FORM_ELEMENTS_LENGTH, JSPROP_ENUMERATE | JSPROP_READONLY},
|
|
|
|
{ NULL }
|
|
|
|
};
|
|
|
|
|
2006-11-23 16:33:43 -05:00
|
|
|
/* @form_elements_class.getProperty */
|
2005-09-15 09:58:31 -04:00
|
|
|
static JSBool
|
|
|
|
form_elements_get_property(JSContext *ctx, JSObject *obj, jsval id, jsval *vp)
|
|
|
|
{
|
2006-11-24 01:50:12 -05:00
|
|
|
JSObject *parent_form; /* instance of @form_class */
|
|
|
|
JSObject *parent_doc; /* instance of @document_class */
|
|
|
|
JSObject *parent_win; /* instance of @window_class */
|
|
|
|
struct view_state *vs;
|
|
|
|
struct document_view *doc_view;
|
|
|
|
struct document *document;
|
|
|
|
struct form_view *form_view;
|
|
|
|
struct form *form;
|
|
|
|
|
2006-12-03 04:14:22 -05:00
|
|
|
/* This can be called if @obj if not itself an instance of the
|
|
|
|
* appropriate class but has one in its prototype chain. Fail
|
|
|
|
* such calls. */
|
|
|
|
if (!JS_InstanceOf(ctx, obj, (JSClass *) &form_elements_class, NULL))
|
|
|
|
return JS_FALSE;
|
2006-11-24 01:50:12 -05:00
|
|
|
parent_form = JS_GetParent(ctx, obj);
|
2006-11-25 01:54:58 -05:00
|
|
|
assert(JS_InstanceOf(ctx, parent_form, (JSClass *) &form_class, NULL));
|
|
|
|
if_assert_failed return JS_FALSE;
|
2006-11-24 01:50:12 -05:00
|
|
|
parent_doc = JS_GetParent(ctx, parent_form);
|
2006-11-25 01:54:58 -05:00
|
|
|
assert(JS_InstanceOf(ctx, parent_doc, (JSClass *) &document_class, NULL));
|
|
|
|
if_assert_failed return JS_FALSE;
|
2006-11-24 01:50:12 -05:00
|
|
|
parent_win = JS_GetParent(ctx, parent_doc);
|
2006-11-25 01:54:58 -05:00
|
|
|
assert(JS_InstanceOf(ctx, parent_win, (JSClass *) &window_class, NULL));
|
|
|
|
if_assert_failed return JS_FALSE;
|
|
|
|
|
2007-05-27 11:32:53 -04:00
|
|
|
vs = JS_GetInstancePrivate(ctx, parent_win,
|
|
|
|
(JSClass *) &window_class, NULL);
|
2006-11-24 01:50:12 -05:00
|
|
|
doc_view = vs->doc_view;
|
|
|
|
document = doc_view->document;
|
2007-05-27 11:32:53 -04:00
|
|
|
form_view = JS_GetInstancePrivate(ctx, parent_form,
|
|
|
|
(JSClass *) &form_class, NULL);
|
2006-11-24 01:50:12 -05:00
|
|
|
form = find_form_by_form_view(document, form_view);
|
2005-09-15 09:58:31 -04:00
|
|
|
|
|
|
|
if (JSVAL_IS_STRING(id)) {
|
|
|
|
form_elements_namedItem(ctx, obj, 1, &id, vp);
|
|
|
|
return JS_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!JSVAL_IS_INT(id))
|
|
|
|
return JS_TRUE;
|
|
|
|
|
|
|
|
undef_to_jsval(ctx, vp);
|
|
|
|
|
|
|
|
switch (JSVAL_TO_INT(id)) {
|
|
|
|
case JSP_FORM_ELEMENTS_LENGTH:
|
|
|
|
int_to_jsval(ctx, vp, list_size(&form->items));
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
/* Array index. */
|
|
|
|
form_elements_item(ctx, obj, 1, &id, vp);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return JS_TRUE;
|
|
|
|
}
|
|
|
|
|
2006-11-23 16:33:43 -05:00
|
|
|
/* @form_elements_funcs{"item"} */
|
2005-09-15 09:58:31 -04:00
|
|
|
static JSBool
|
|
|
|
form_elements_item(JSContext *ctx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
|
|
|
|
{
|
2006-11-24 01:50:12 -05:00
|
|
|
JSObject *parent_form; /* instance of @form_class */
|
|
|
|
JSObject *parent_doc; /* instance of @document_class */
|
|
|
|
JSObject *parent_win; /* instance of @window_class */
|
|
|
|
struct view_state *vs;
|
|
|
|
struct document_view *doc_view;
|
|
|
|
struct document *document;
|
|
|
|
struct form_view *form_view;
|
|
|
|
struct form *form;
|
2005-09-15 09:58:31 -04:00
|
|
|
struct form_control *fc;
|
|
|
|
int counter = -1;
|
|
|
|
int index;
|
|
|
|
|
2006-11-25 01:54:58 -05:00
|
|
|
if (!JS_InstanceOf(ctx, obj, (JSClass *) &form_elements_class, argv)) return JS_FALSE;
|
2006-11-24 01:50:12 -05:00
|
|
|
parent_form = JS_GetParent(ctx, obj);
|
2006-11-25 01:54:58 -05:00
|
|
|
assert(JS_InstanceOf(ctx, parent_form, (JSClass *) &form_class, NULL));
|
|
|
|
if_assert_failed return JS_FALSE;
|
2006-11-24 01:50:12 -05:00
|
|
|
parent_doc = JS_GetParent(ctx, parent_form);
|
2006-11-25 01:54:58 -05:00
|
|
|
assert(JS_InstanceOf(ctx, parent_doc, (JSClass *) &document_class, NULL));
|
|
|
|
if_assert_failed return JS_FALSE;
|
2006-11-24 01:50:12 -05:00
|
|
|
parent_win = JS_GetParent(ctx, parent_doc);
|
2006-11-25 01:54:58 -05:00
|
|
|
assert(JS_InstanceOf(ctx, parent_win, (JSClass *) &window_class, NULL));
|
|
|
|
if_assert_failed return JS_FALSE;
|
|
|
|
|
2007-05-27 11:32:53 -04:00
|
|
|
vs = JS_GetInstancePrivate(ctx, parent_win,
|
|
|
|
(JSClass *) &window_class, NULL);
|
2006-11-24 01:50:12 -05:00
|
|
|
doc_view = vs->doc_view;
|
|
|
|
document = doc_view->document;
|
2007-05-27 11:32:53 -04:00
|
|
|
form_view = JS_GetInstancePrivate(ctx, parent_form,
|
|
|
|
(JSClass *) &form_class, NULL);
|
2006-11-24 01:50:12 -05:00
|
|
|
form = find_form_by_form_view(document, form_view);
|
|
|
|
|
2005-09-15 09:58:31 -04:00
|
|
|
if (argc != 1)
|
|
|
|
return JS_TRUE;
|
|
|
|
|
2007-05-27 11:09:37 -04:00
|
|
|
if (!JS_ValueToInt32(ctx, argv[0], &index))
|
|
|
|
return JS_FALSE;
|
2005-09-15 09:58:31 -04:00
|
|
|
undef_to_jsval(ctx, rval);
|
|
|
|
|
|
|
|
foreach (fc, form->items) {
|
|
|
|
counter++;
|
|
|
|
if (counter == index) {
|
2007-05-22 10:07:06 -04:00
|
|
|
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);
|
2005-09-15 09:58:31 -04:00
|
|
|
|
2007-05-22 10:07:06 -04:00
|
|
|
if (fcobj)
|
|
|
|
object_to_jsval(ctx, rval, fcobj);
|
2005-09-15 09:58:31 -04:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return JS_TRUE;
|
|
|
|
}
|
|
|
|
|
2006-11-23 16:33:43 -05:00
|
|
|
/* @form_elements_funcs{"namedItem"} */
|
2005-09-15 09:58:31 -04:00
|
|
|
static JSBool
|
|
|
|
form_elements_namedItem(JSContext *ctx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
|
|
|
|
{
|
2006-11-24 01:50:12 -05:00
|
|
|
JSObject *parent_form; /* instance of @form_class */
|
|
|
|
JSObject *parent_doc; /* instance of @document_class */
|
|
|
|
JSObject *parent_win; /* instance of @window_class */
|
|
|
|
struct view_state *vs;
|
|
|
|
struct document_view *doc_view;
|
|
|
|
struct document *document;
|
|
|
|
struct form_view *form_view;
|
|
|
|
struct form *form;
|
2005-09-15 09:58:31 -04:00
|
|
|
struct form_control *fc;
|
|
|
|
unsigned char *string;
|
|
|
|
|
2006-11-25 01:54:58 -05:00
|
|
|
if (!JS_InstanceOf(ctx, obj, (JSClass *) &form_elements_class, argv)) return JS_FALSE;
|
2006-11-24 01:50:12 -05:00
|
|
|
parent_form = JS_GetParent(ctx, obj);
|
2006-11-25 01:54:58 -05:00
|
|
|
assert(JS_InstanceOf(ctx, parent_form, (JSClass *) &form_class, NULL));
|
|
|
|
if_assert_failed return JS_FALSE;
|
2006-11-24 01:50:12 -05:00
|
|
|
parent_doc = JS_GetParent(ctx, parent_form);
|
2006-11-25 01:54:58 -05:00
|
|
|
assert(JS_InstanceOf(ctx, parent_doc, (JSClass *) &document_class, NULL));
|
|
|
|
if_assert_failed return JS_FALSE;
|
2006-11-24 01:50:12 -05:00
|
|
|
parent_win = JS_GetParent(ctx, parent_doc);
|
2006-11-25 01:54:58 -05:00
|
|
|
assert(JS_InstanceOf(ctx, parent_win, (JSClass *) &window_class, NULL));
|
|
|
|
if_assert_failed return JS_FALSE;
|
|
|
|
|
2007-05-27 11:32:53 -04:00
|
|
|
vs = JS_GetInstancePrivate(ctx, parent_win,
|
|
|
|
(JSClass *) &window_class, NULL);
|
2006-11-24 01:50:12 -05:00
|
|
|
doc_view = vs->doc_view;
|
|
|
|
document = doc_view->document;
|
2007-05-27 11:32:53 -04:00
|
|
|
form_view = JS_GetInstancePrivate(ctx, parent_form,
|
|
|
|
(JSClass *) &form_class, NULL);
|
2006-11-24 01:50:12 -05:00
|
|
|
form = find_form_by_form_view(document, form_view);
|
|
|
|
|
2005-09-15 09:58:31 -04:00
|
|
|
if (argc != 1)
|
|
|
|
return JS_TRUE;
|
|
|
|
|
|
|
|
string = jsval_to_string(ctx, &argv[0]);
|
|
|
|
if (!*string)
|
|
|
|
return JS_TRUE;
|
|
|
|
|
|
|
|
undef_to_jsval(ctx, rval);
|
|
|
|
|
|
|
|
foreach (fc, form->items) {
|
2006-05-01 13:37:54 -04:00
|
|
|
if ((fc->id && !strcasecmp(string, fc->id)) || (fc->name && !strcasecmp(string, fc->name))) {
|
2007-05-22 10:07:06 -04:00
|
|
|
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);
|
2005-09-15 09:58:31 -04:00
|
|
|
|
2007-05-22 10:07:06 -04:00
|
|
|
if (fcobj)
|
|
|
|
object_to_jsval(ctx, rval, fcobj);
|
2005-09-15 09:58:31 -04:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return JS_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
2006-11-23 16:33:43 -05:00
|
|
|
/* Each @form_class object must have a @document_class parent. */
|
2005-09-15 09:58:31 -04:00
|
|
|
static const JSClass form_class = {
|
|
|
|
"form",
|
2006-11-20 14:49:17 -05:00
|
|
|
JSCLASS_HAS_PRIVATE, /* struct form_view * */
|
2005-09-15 09:58:31 -04:00
|
|
|
JS_PropertyStub, JS_PropertyStub,
|
|
|
|
form_get_property, form_set_property,
|
|
|
|
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub
|
|
|
|
};
|
|
|
|
|
2006-12-06 16:09:14 -05:00
|
|
|
/* Tinyids of properties. Use negative values to distinguish these
|
|
|
|
* from array indexes (even though this object has no array elements).
|
|
|
|
* ECMAScript code should not use these directly as in form[-1];
|
|
|
|
* future versions of ELinks may change the numbers. */
|
2005-09-15 09:58:31 -04:00
|
|
|
enum form_prop {
|
2006-12-06 16:09:14 -05:00
|
|
|
JSP_FORM_ACTION = -1,
|
|
|
|
JSP_FORM_ELEMENTS = -2,
|
|
|
|
JSP_FORM_ENCODING = -3,
|
|
|
|
JSP_FORM_LENGTH = -4,
|
|
|
|
JSP_FORM_METHOD = -5,
|
|
|
|
JSP_FORM_NAME = -6,
|
|
|
|
JSP_FORM_TARGET = -7,
|
2005-09-15 09:58:31 -04:00
|
|
|
};
|
|
|
|
|
|
|
|
static const JSPropertySpec form_props[] = {
|
|
|
|
{ "action", JSP_FORM_ACTION, JSPROP_ENUMERATE },
|
|
|
|
{ "elements", JSP_FORM_ELEMENTS, JSPROP_ENUMERATE },
|
|
|
|
{ "encoding", JSP_FORM_ENCODING, JSPROP_ENUMERATE },
|
|
|
|
{ "length", JSP_FORM_LENGTH, JSPROP_ENUMERATE | JSPROP_READONLY },
|
|
|
|
{ "method", JSP_FORM_METHOD, JSPROP_ENUMERATE },
|
|
|
|
{ "name", JSP_FORM_NAME, JSPROP_ENUMERATE },
|
|
|
|
{ "target", JSP_FORM_TARGET, JSPROP_ENUMERATE },
|
|
|
|
{ NULL }
|
|
|
|
};
|
|
|
|
|
|
|
|
static JSBool form_reset(JSContext *ctx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);
|
|
|
|
static JSBool form_submit(JSContext *ctx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);
|
|
|
|
|
|
|
|
static const JSFunctionSpec form_funcs[] = {
|
|
|
|
{ "reset", form_reset, 0 },
|
|
|
|
{ "submit", form_submit, 0 },
|
|
|
|
{ NULL }
|
|
|
|
};
|
|
|
|
|
2006-11-23 16:33:43 -05:00
|
|
|
/* @form_class.getProperty */
|
2005-09-15 09:58:31 -04:00
|
|
|
static JSBool
|
|
|
|
form_get_property(JSContext *ctx, JSObject *obj, jsval id, jsval *vp)
|
|
|
|
{
|
|
|
|
/* DBG("doc %p %s\n", parent_doc, JS_GetStringBytes(JS_ValueToString(ctx, OBJECT_TO_JSVAL(parent_doc)))); */
|
2006-11-24 01:50:12 -05:00
|
|
|
JSObject *parent_doc; /* instance of @document_class */
|
|
|
|
JSObject *parent_win; /* instance of @window_class */
|
|
|
|
struct view_state *vs;
|
|
|
|
struct document_view *doc_view;
|
|
|
|
struct form_view *fv;
|
|
|
|
struct form *form;
|
|
|
|
|
2006-12-03 04:14:22 -05:00
|
|
|
/* This can be called if @obj if not itself an instance of the
|
|
|
|
* appropriate class but has one in its prototype chain. Fail
|
|
|
|
* such calls. */
|
|
|
|
if (!JS_InstanceOf(ctx, obj, (JSClass *) &form_class, NULL))
|
|
|
|
return JS_FALSE;
|
2006-11-24 01:50:12 -05:00
|
|
|
parent_doc = JS_GetParent(ctx, obj);
|
2006-11-25 01:54:58 -05:00
|
|
|
assert(JS_InstanceOf(ctx, parent_doc, (JSClass *) &document_class, NULL));
|
|
|
|
if_assert_failed return JS_FALSE;
|
2006-11-24 01:50:12 -05:00
|
|
|
parent_win = JS_GetParent(ctx, parent_doc);
|
2006-11-25 01:54:58 -05:00
|
|
|
assert(JS_InstanceOf(ctx, parent_win, (JSClass *) &window_class, NULL));
|
|
|
|
if_assert_failed return JS_FALSE;
|
|
|
|
|
2007-05-27 11:32:53 -04:00
|
|
|
vs = JS_GetInstancePrivate(ctx, parent_win,
|
|
|
|
(JSClass *) &window_class, NULL);
|
2006-11-24 01:50:12 -05:00
|
|
|
doc_view = vs->doc_view;
|
2007-05-27 11:32:53 -04:00
|
|
|
fv = JS_GetInstancePrivate(ctx, obj, (JSClass *) &form_class, NULL);
|
2006-11-24 01:50:12 -05:00
|
|
|
form = find_form_by_form_view(doc_view->document, fv);
|
2005-09-15 09:58:31 -04:00
|
|
|
|
|
|
|
assert(form);
|
|
|
|
|
|
|
|
if (JSVAL_IS_STRING(id)) {
|
|
|
|
struct form_control *fc;
|
|
|
|
unsigned char *string;
|
|
|
|
|
|
|
|
string = jsval_to_string(ctx, &id);
|
|
|
|
foreach (fc, form->items) {
|
|
|
|
JSObject *fcobj = NULL;
|
2007-05-22 10:07:06 -04:00
|
|
|
struct form_state *fs;
|
2005-09-15 09:58:31 -04:00
|
|
|
|
2006-05-01 13:55:43 -04:00
|
|
|
if ((!fc->id || strcasecmp(string, fc->id)) && (!fc->name || strcasecmp(string, fc->name)))
|
2005-09-15 09:58:31 -04:00
|
|
|
continue;
|
|
|
|
|
2007-05-22 10:07:06 -04:00
|
|
|
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);
|
|
|
|
if (fcobj)
|
|
|
|
object_to_jsval(ctx, vp, fcobj);
|
2005-09-15 09:58:31 -04:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return JS_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!JSVAL_IS_INT(id))
|
|
|
|
return JS_TRUE;
|
|
|
|
|
|
|
|
undef_to_jsval(ctx, vp);
|
|
|
|
|
|
|
|
switch (JSVAL_TO_INT(id)) {
|
|
|
|
case JSP_FORM_ACTION:
|
|
|
|
string_to_jsval(ctx, vp, form->action);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case JSP_FORM_ELEMENTS:
|
|
|
|
{
|
|
|
|
/* jsform ('form') is form_elements' parent; who knows is that's correct */
|
|
|
|
JSObject *jsform_elems = JS_NewObject(ctx, (JSClass *) &form_elements_class, NULL, obj);
|
|
|
|
|
|
|
|
JS_DefineProperties(ctx, jsform_elems, (JSPropertySpec *) form_elements_props);
|
|
|
|
JS_DefineFunctions(ctx, jsform_elems, (JSFunctionSpec *) form_elements_funcs);
|
|
|
|
object_to_jsval(ctx, vp, jsform_elems);
|
|
|
|
/* SM will cache this property value for us so we create this
|
|
|
|
* just once per form. */
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case JSP_FORM_ENCODING:
|
|
|
|
switch (form->method) {
|
|
|
|
case FORM_METHOD_GET:
|
|
|
|
case FORM_METHOD_POST:
|
|
|
|
string_to_jsval(ctx, vp, "application/x-www-form-urlencoded");
|
|
|
|
break;
|
|
|
|
case FORM_METHOD_POST_MP:
|
|
|
|
string_to_jsval(ctx, vp, "multipart/form-data");
|
|
|
|
break;
|
|
|
|
case FORM_METHOD_POST_TEXT_PLAIN:
|
|
|
|
string_to_jsval(ctx, vp, "text/plain");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case JSP_FORM_LENGTH:
|
|
|
|
int_to_jsval(ctx, vp, list_size(&form->items));
|
|
|
|
break;
|
|
|
|
|
|
|
|
case JSP_FORM_METHOD:
|
|
|
|
switch (form->method) {
|
|
|
|
case FORM_METHOD_GET:
|
|
|
|
string_to_jsval(ctx, vp, "GET");
|
|
|
|
break;
|
|
|
|
|
|
|
|
case FORM_METHOD_POST:
|
|
|
|
case FORM_METHOD_POST_MP:
|
|
|
|
case FORM_METHOD_POST_TEXT_PLAIN:
|
|
|
|
string_to_jsval(ctx, vp, "POST");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case JSP_FORM_NAME:
|
|
|
|
string_to_jsval(ctx, vp, form->name);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case JSP_FORM_TARGET:
|
|
|
|
string_to_jsval(ctx, vp, form->target);
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
2007-05-27 11:36:31 -04:00
|
|
|
/* Unrecognized integer property ID; someone is using
|
|
|
|
* the object as an array. SMJS builtin classes (e.g.
|
2006-12-03 05:07:07 -05:00
|
|
|
* js_RegExpClass) just return JS_TRUE in this case
|
|
|
|
* and leave *@vp unchanged. Do the same here.
|
|
|
|
* (Actually not quite the same, as we already used
|
|
|
|
* @undef_to_jsval.) */
|
2005-09-15 09:58:31 -04:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return JS_TRUE;
|
|
|
|
}
|
|
|
|
|
2006-11-23 16:33:43 -05:00
|
|
|
/* @form_class.setProperty */
|
2005-09-15 09:58:31 -04:00
|
|
|
static JSBool
|
|
|
|
form_set_property(JSContext *ctx, JSObject *obj, jsval id, jsval *vp)
|
|
|
|
{
|
2006-11-24 01:50:12 -05:00
|
|
|
JSObject *parent_doc; /* instance of @document_class */
|
|
|
|
JSObject *parent_win; /* instance of @window_class */
|
|
|
|
struct view_state *vs;
|
|
|
|
struct document_view *doc_view;
|
|
|
|
struct form_view *fv;
|
|
|
|
struct form *form;
|
2005-09-15 09:58:31 -04:00
|
|
|
unsigned char *string;
|
|
|
|
|
2006-12-03 04:14:22 -05:00
|
|
|
/* This can be called if @obj if not itself an instance of the
|
|
|
|
* appropriate class but has one in its prototype chain. Fail
|
|
|
|
* such calls. */
|
|
|
|
if (!JS_InstanceOf(ctx, obj, (JSClass *) &form_class, NULL))
|
|
|
|
return JS_FALSE;
|
2006-11-24 01:50:12 -05:00
|
|
|
parent_doc = JS_GetParent(ctx, obj);
|
2006-11-25 01:54:58 -05:00
|
|
|
assert(JS_InstanceOf(ctx, parent_doc, (JSClass *) &document_class, NULL));
|
|
|
|
if_assert_failed return JS_FALSE;
|
2006-11-24 01:50:12 -05:00
|
|
|
parent_win = JS_GetParent(ctx, parent_doc);
|
2006-11-25 01:54:58 -05:00
|
|
|
assert(JS_InstanceOf(ctx, parent_win, (JSClass *) &window_class, NULL));
|
|
|
|
if_assert_failed return JS_FALSE;
|
|
|
|
|
2007-05-27 11:32:53 -04:00
|
|
|
vs = JS_GetInstancePrivate(ctx, parent_win,
|
|
|
|
(JSClass *) &window_class, NULL);
|
2006-11-24 01:50:12 -05:00
|
|
|
doc_view = vs->doc_view;
|
2007-05-27 11:32:53 -04:00
|
|
|
fv = JS_GetInstancePrivate(ctx, obj, (JSClass *) &form_class, NULL);
|
2006-11-24 01:50:12 -05:00
|
|
|
form = find_form_by_form_view(doc_view->document, fv);
|
|
|
|
|
2005-09-15 09:58:31 -04:00
|
|
|
assert(form);
|
|
|
|
|
|
|
|
if (!JSVAL_IS_INT(id))
|
|
|
|
return JS_TRUE;
|
|
|
|
|
|
|
|
switch (JSVAL_TO_INT(id)) {
|
|
|
|
case JSP_FORM_ACTION:
|
2006-03-21 12:45:40 -05:00
|
|
|
string = stracpy(jsval_to_string(ctx, vp));
|
2006-02-09 16:50:30 -05:00
|
|
|
if (form->action) {
|
2006-03-21 12:45:40 -05:00
|
|
|
ecmascript_set_action(&form->action, string);
|
|
|
|
} else {
|
|
|
|
mem_free_set(&form->action, string);
|
2006-02-09 16:50:30 -05:00
|
|
|
}
|
2005-09-15 09:58:31 -04:00
|
|
|
break;
|
|
|
|
|
|
|
|
case JSP_FORM_ENCODING:
|
|
|
|
string = jsval_to_string(ctx, vp);
|
|
|
|
if (!strcasecmp(string, "application/x-www-form-urlencoded")) {
|
|
|
|
form->method = form->method == FORM_METHOD_GET ? FORM_METHOD_GET
|
|
|
|
: FORM_METHOD_POST;
|
|
|
|
} else if (!strcasecmp(string, "multipart/form-data")) {
|
|
|
|
form->method = FORM_METHOD_POST_MP;
|
|
|
|
} else if (!strcasecmp(string, "text/plain")) {
|
|
|
|
form->method = FORM_METHOD_POST_TEXT_PLAIN;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case JSP_FORM_METHOD:
|
|
|
|
string = jsval_to_string(ctx, vp);
|
|
|
|
if (!strcasecmp(string, "GET")) {
|
|
|
|
form->method = FORM_METHOD_GET;
|
|
|
|
} else if (!strcasecmp(string, "POST")) {
|
|
|
|
form->method = FORM_METHOD_POST;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case JSP_FORM_NAME:
|
|
|
|
mem_free_set(&form->name, stracpy(jsval_to_string(ctx, vp)));
|
|
|
|
break;
|
|
|
|
|
|
|
|
case JSP_FORM_TARGET:
|
|
|
|
mem_free_set(&form->target, stracpy(jsval_to_string(ctx, vp)));
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
2007-05-27 11:36:31 -04:00
|
|
|
/* Unrecognized integer property ID; someone is using
|
|
|
|
* the object as an array. SMJS builtin classes (e.g.
|
2006-12-03 05:07:07 -05:00
|
|
|
* js_RegExpClass) just return JS_TRUE in this case.
|
|
|
|
* Do the same here. */
|
2005-09-15 09:58:31 -04:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return JS_TRUE;
|
|
|
|
}
|
|
|
|
|
2006-11-23 16:33:43 -05:00
|
|
|
/* @form_funcs{"reset"} */
|
2005-09-15 09:58:31 -04:00
|
|
|
static JSBool
|
|
|
|
form_reset(JSContext *ctx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
|
|
|
|
{
|
2006-11-24 01:50:12 -05:00
|
|
|
JSObject *parent_doc; /* instance of @document_class */
|
|
|
|
JSObject *parent_win; /* instance of @window_class */
|
|
|
|
struct view_state *vs;
|
|
|
|
struct document_view *doc_view;
|
|
|
|
struct form_view *fv;
|
|
|
|
struct form *form;
|
|
|
|
|
2006-11-25 01:54:58 -05:00
|
|
|
if (!JS_InstanceOf(ctx, obj, (JSClass *) &form_class, argv)) return JS_FALSE;
|
2006-11-24 01:50:12 -05:00
|
|
|
parent_doc = JS_GetParent(ctx, obj);
|
2006-11-25 01:54:58 -05:00
|
|
|
assert(JS_InstanceOf(ctx, parent_doc, (JSClass *) &document_class, NULL));
|
|
|
|
if_assert_failed return JS_FALSE;
|
2006-11-24 01:50:12 -05:00
|
|
|
parent_win = JS_GetParent(ctx, parent_doc);
|
2006-11-25 01:54:58 -05:00
|
|
|
assert(JS_InstanceOf(ctx, parent_win, (JSClass *) &window_class, NULL));
|
|
|
|
if_assert_failed return JS_FALSE;
|
|
|
|
|
2007-05-27 11:32:53 -04:00
|
|
|
vs = JS_GetInstancePrivate(ctx, parent_win,
|
|
|
|
(JSClass *) &window_class, NULL);
|
2006-11-24 01:50:12 -05:00
|
|
|
doc_view = vs->doc_view;
|
2007-05-27 11:32:53 -04:00
|
|
|
fv = JS_GetInstancePrivate(ctx, obj, (JSClass *) &form_class, argv);
|
2006-11-24 01:50:12 -05:00
|
|
|
form = find_form_by_form_view(doc_view->document, fv);
|
2005-09-15 09:58:31 -04:00
|
|
|
|
|
|
|
assert(form);
|
|
|
|
|
|
|
|
do_reset_form(doc_view, form);
|
|
|
|
draw_forms(doc_view->session->tab->term, doc_view);
|
|
|
|
|
|
|
|
boolean_to_jsval(ctx, rval, 0);
|
|
|
|
|
|
|
|
return JS_TRUE;
|
|
|
|
}
|
|
|
|
|
2006-11-23 16:33:43 -05:00
|
|
|
/* @form_funcs{"submit"} */
|
2005-09-15 09:58:31 -04:00
|
|
|
static JSBool
|
|
|
|
form_submit(JSContext *ctx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
|
|
|
|
{
|
2006-11-24 01:50:12 -05:00
|
|
|
JSObject *parent_doc; /* instance of @document_class */
|
|
|
|
JSObject *parent_win; /* instance of @window_class */
|
|
|
|
struct view_state *vs;
|
|
|
|
struct document_view *doc_view;
|
|
|
|
struct session *ses;
|
|
|
|
struct form_view *fv;
|
|
|
|
struct form *form;
|
|
|
|
|
2006-11-25 01:54:58 -05:00
|
|
|
if (!JS_InstanceOf(ctx, obj, (JSClass *) &form_class, argv)) return JS_FALSE;
|
2006-11-24 01:50:12 -05:00
|
|
|
parent_doc = JS_GetParent(ctx, obj);
|
2006-11-25 01:54:58 -05:00
|
|
|
assert(JS_InstanceOf(ctx, parent_doc, (JSClass *) &document_class, NULL));
|
|
|
|
if_assert_failed return JS_FALSE;
|
2006-11-24 01:50:12 -05:00
|
|
|
parent_win = JS_GetParent(ctx, parent_doc);
|
2006-11-25 01:54:58 -05:00
|
|
|
assert(JS_InstanceOf(ctx, parent_win, (JSClass *) &window_class, NULL));
|
|
|
|
if_assert_failed return JS_FALSE;
|
|
|
|
|
2007-05-27 11:32:53 -04:00
|
|
|
vs = JS_GetInstancePrivate(ctx, parent_win,
|
|
|
|
(JSClass *) &window_class, NULL);
|
2006-11-24 01:50:12 -05:00
|
|
|
doc_view = vs->doc_view;
|
|
|
|
ses = doc_view->session;
|
2007-05-27 11:32:53 -04:00
|
|
|
fv = JS_GetInstancePrivate(ctx, obj, (JSClass *) &form_class, argv);
|
2006-11-24 01:50:12 -05:00
|
|
|
form = find_form_by_form_view(doc_view->document, fv);
|
2005-09-15 09:58:31 -04:00
|
|
|
|
|
|
|
assert(form);
|
2006-01-28 05:17:22 -05:00
|
|
|
submit_given_form(ses, doc_view, form, 0);
|
2005-09-15 09:58:31 -04:00
|
|
|
|
|
|
|
boolean_to_jsval(ctx, rval, 0);
|
|
|
|
|
|
|
|
return JS_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
JSObject *
|
|
|
|
get_form_object(JSContext *ctx, JSObject *jsdoc, struct form_view *fv)
|
|
|
|
{
|
2006-01-30 05:15:04 -05:00
|
|
|
#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 */
|
|
|
|
JSObject *jsform = JS_NewObject(ctx, (JSClass *) &form_class, NULL, jsdoc);
|
|
|
|
|
|
|
|
JS_DefineProperties(ctx, jsform, (JSPropertySpec *) form_props);
|
|
|
|
JS_DefineFunctions(ctx, jsform, (JSFunctionSpec *) form_funcs);
|
2006-11-25 02:00:59 -05:00
|
|
|
JS_SetPrivate(ctx, jsform, fv); /* to @form_class */
|
2006-01-30 05:15:04 -05:00
|
|
|
fv->ecmascript_obj = jsform;
|
2005-09-15 09:58:31 -04:00
|
|
|
return fv->ecmascript_obj;
|
|
|
|
}
|
|
|
|
static JSBool forms_get_property(JSContext *ctx, JSObject *obj, jsval id, jsval *vp);
|
|
|
|
|
2006-11-23 16:33:43 -05:00
|
|
|
/* Each @forms_class object must have a @document_class parent. */
|
2005-09-15 09:58:31 -04:00
|
|
|
const JSClass forms_class = {
|
|
|
|
"forms",
|
|
|
|
JSCLASS_HAS_PRIVATE,
|
|
|
|
JS_PropertyStub, JS_PropertyStub,
|
|
|
|
forms_get_property, JS_PropertyStub,
|
|
|
|
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub
|
|
|
|
};
|
|
|
|
|
|
|
|
static JSBool forms_item(JSContext *ctx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);
|
|
|
|
static JSBool forms_namedItem(JSContext *ctx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);
|
|
|
|
|
|
|
|
const JSFunctionSpec forms_funcs[] = {
|
|
|
|
{ "item", forms_item, 1 },
|
|
|
|
{ "namedItem", forms_namedItem, 1 },
|
|
|
|
{ NULL }
|
|
|
|
};
|
|
|
|
|
2006-12-06 16:09:14 -05:00
|
|
|
/* Tinyids of properties. Use negative values to distinguish these from
|
|
|
|
* array indexes (forms[INT] for INT>=0 is equivalent to forms.item(INT)).
|
|
|
|
* ECMAScript code should not use these directly as in forms[-1];
|
|
|
|
* future versions of ELinks may change the numbers. */
|
|
|
|
enum forms_prop {
|
|
|
|
JSP_FORMS_LENGTH = -1,
|
|
|
|
};
|
2005-09-15 09:58:31 -04:00
|
|
|
const JSPropertySpec forms_props[] = {
|
|
|
|
{ "length", JSP_FORMS_LENGTH, JSPROP_ENUMERATE | JSPROP_READONLY},
|
|
|
|
{ NULL }
|
|
|
|
};
|
|
|
|
|
2007-06-10 06:11:53 -04:00
|
|
|
/* Find the form whose name is @name, which should normally be a
|
|
|
|
* string (but might not be). If found, set *rval = the DOM
|
|
|
|
* object. If not found, leave *rval unchanged. */
|
|
|
|
static void
|
|
|
|
find_form_by_name(JSContext *ctx, JSObject *jsdoc,
|
|
|
|
struct document_view *doc_view,
|
|
|
|
jsval name, jsval *rval)
|
|
|
|
{
|
|
|
|
unsigned char *string = jsval_to_string(ctx, &name);
|
|
|
|
struct form *form;
|
|
|
|
|
|
|
|
if (!*string)
|
|
|
|
return;
|
|
|
|
|
|
|
|
foreach (form, doc_view->document->forms) {
|
|
|
|
if (form->name && !strcasecmp(string, form->name)) {
|
|
|
|
object_to_jsval(ctx, rval, get_form_object(ctx, jsdoc,
|
|
|
|
find_form_view(doc_view, form)));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-11-23 16:33:43 -05:00
|
|
|
/* @forms_class.getProperty */
|
2005-09-15 09:58:31 -04:00
|
|
|
static JSBool
|
|
|
|
forms_get_property(JSContext *ctx, JSObject *obj, jsval id, jsval *vp)
|
|
|
|
{
|
2006-11-24 01:50:12 -05:00
|
|
|
JSObject *parent_doc; /* instance of @document_class */
|
|
|
|
JSObject *parent_win; /* instance of @window_class */
|
|
|
|
struct view_state *vs;
|
|
|
|
struct document_view *doc_view;
|
|
|
|
struct document *document;
|
|
|
|
|
2006-12-03 04:14:22 -05:00
|
|
|
/* This can be called if @obj if not itself an instance of the
|
|
|
|
* appropriate class but has one in its prototype chain. Fail
|
|
|
|
* such calls. */
|
|
|
|
if (!JS_InstanceOf(ctx, obj, (JSClass *) &forms_class, NULL))
|
|
|
|
return JS_FALSE;
|
2006-11-24 01:50:12 -05:00
|
|
|
parent_doc = JS_GetParent(ctx, obj);
|
2006-11-25 01:54:58 -05:00
|
|
|
assert(JS_InstanceOf(ctx, parent_doc, (JSClass *) &document_class, NULL));
|
|
|
|
if_assert_failed return JS_FALSE;
|
2006-11-24 01:50:12 -05:00
|
|
|
parent_win = JS_GetParent(ctx, parent_doc);
|
2006-11-25 01:54:58 -05:00
|
|
|
assert(JS_InstanceOf(ctx, parent_win, (JSClass *) &window_class, NULL));
|
|
|
|
if_assert_failed return JS_FALSE;
|
|
|
|
|
2007-05-27 11:32:53 -04:00
|
|
|
vs = JS_GetInstancePrivate(ctx, parent_win,
|
|
|
|
(JSClass *) &window_class, NULL);
|
2006-11-24 01:50:12 -05:00
|
|
|
doc_view = vs->doc_view;
|
|
|
|
document = doc_view->document;
|
2005-09-15 09:58:31 -04:00
|
|
|
|
|
|
|
if (JSVAL_IS_STRING(id)) {
|
2007-06-10 06:11:53 -04:00
|
|
|
/* When SMJS evaluates forms.namedItem("foo"), it first
|
|
|
|
* calls forms_get_property with id = JSString "namedItem"
|
|
|
|
* and *vp = JSObject JSFunction forms_namedItem.
|
|
|
|
* If we don't find a form whose name is id,
|
|
|
|
* we must leave *vp unchanged here, to avoid
|
|
|
|
* "TypeError: forms.namedItem is not a function". */
|
|
|
|
find_form_by_name(ctx, parent_doc, doc_view, id, vp);
|
2005-09-15 09:58:31 -04:00
|
|
|
return JS_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!JSVAL_IS_INT(id))
|
|
|
|
return JS_TRUE;
|
|
|
|
|
|
|
|
switch (JSVAL_TO_INT(id)) {
|
|
|
|
case JSP_FORMS_LENGTH:
|
|
|
|
int_to_jsval(ctx, vp, list_size(&document->forms));
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
/* Array index. */
|
|
|
|
forms_item(ctx, obj, 1, &id, vp);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return JS_TRUE;
|
|
|
|
}
|
|
|
|
|
2006-11-23 16:33:43 -05:00
|
|
|
/* @forms_funcs{"item"} */
|
2005-09-15 09:58:31 -04:00
|
|
|
static JSBool
|
|
|
|
forms_item(JSContext *ctx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
|
|
|
|
{
|
2006-11-24 01:50:12 -05:00
|
|
|
JSObject *parent_doc; /* instance of @document_class */
|
|
|
|
JSObject *parent_win; /* instance of @window_class */
|
|
|
|
struct view_state *vs;
|
2005-09-15 09:58:31 -04:00
|
|
|
struct form_view *fv;
|
|
|
|
int counter = -1;
|
|
|
|
int index;
|
|
|
|
|
2006-11-25 01:54:58 -05:00
|
|
|
if (!JS_InstanceOf(ctx, obj, (JSClass *) &forms_class, argv)) return JS_FALSE;
|
2006-11-24 01:50:12 -05:00
|
|
|
parent_doc = JS_GetParent(ctx, obj);
|
2006-11-25 01:54:58 -05:00
|
|
|
assert(JS_InstanceOf(ctx, parent_doc, (JSClass *) &document_class, NULL));
|
|
|
|
if_assert_failed return JS_FALSE;
|
2006-11-24 01:50:12 -05:00
|
|
|
parent_win = JS_GetParent(ctx, parent_doc);
|
2006-11-25 01:54:58 -05:00
|
|
|
assert(JS_InstanceOf(ctx, parent_win, (JSClass *) &window_class, NULL));
|
|
|
|
if_assert_failed return JS_FALSE;
|
|
|
|
|
2007-05-27 11:32:53 -04:00
|
|
|
vs = JS_GetInstancePrivate(ctx, parent_win,
|
|
|
|
(JSClass *) &window_class, NULL);
|
2006-11-24 01:50:12 -05:00
|
|
|
|
2005-09-15 09:58:31 -04:00
|
|
|
if (argc != 1)
|
|
|
|
return JS_TRUE;
|
|
|
|
|
2007-05-27 11:09:37 -04:00
|
|
|
if (!JS_ValueToInt32(ctx, argv[0], &index))
|
|
|
|
return JS_FALSE;
|
2005-09-15 09:58:31 -04:00
|
|
|
undef_to_jsval(ctx, rval);
|
|
|
|
|
|
|
|
foreach (fv, vs->forms) {
|
|
|
|
counter++;
|
|
|
|
if (counter == index) {
|
|
|
|
object_to_jsval(ctx, rval, get_form_object(ctx, parent_doc, fv));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return JS_TRUE;
|
|
|
|
}
|
|
|
|
|
2006-11-23 16:33:43 -05:00
|
|
|
/* @forms_funcs{"namedItem"} */
|
2005-09-15 09:58:31 -04:00
|
|
|
static JSBool
|
|
|
|
forms_namedItem(JSContext *ctx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
|
|
|
|
{
|
2006-11-24 01:50:12 -05:00
|
|
|
JSObject *parent_doc; /* instance of @document_class */
|
|
|
|
JSObject *parent_win; /* instance of @window_class */
|
|
|
|
struct view_state *vs;
|
|
|
|
struct document_view *doc_view;
|
2005-09-15 09:58:31 -04:00
|
|
|
|
2006-11-25 01:54:58 -05:00
|
|
|
if (!JS_InstanceOf(ctx, obj, (JSClass *) &forms_class, argv)) return JS_FALSE;
|
2006-11-24 01:50:12 -05:00
|
|
|
parent_doc = JS_GetParent(ctx, obj);
|
2006-11-25 01:54:58 -05:00
|
|
|
assert(JS_InstanceOf(ctx, parent_doc, (JSClass *) &document_class, NULL));
|
|
|
|
if_assert_failed return JS_FALSE;
|
2006-11-24 01:50:12 -05:00
|
|
|
parent_win = JS_GetParent(ctx, parent_doc);
|
2006-11-25 01:54:58 -05:00
|
|
|
assert(JS_InstanceOf(ctx, parent_win, (JSClass *) &window_class, NULL));
|
|
|
|
if_assert_failed return JS_FALSE;
|
|
|
|
|
2007-05-27 11:32:53 -04:00
|
|
|
vs = JS_GetInstancePrivate(ctx, parent_win,
|
|
|
|
(JSClass *) &window_class, NULL);
|
2006-11-24 01:50:12 -05:00
|
|
|
doc_view = vs->doc_view;
|
|
|
|
|
2005-09-15 09:58:31 -04:00
|
|
|
if (argc != 1)
|
|
|
|
return JS_TRUE;
|
|
|
|
|
|
|
|
undef_to_jsval(ctx, rval);
|
2007-06-10 06:11:53 -04:00
|
|
|
find_form_by_name(ctx, parent_doc, doc_view, argv[0], rval);
|
2005-09-15 09:58:31 -04:00
|
|
|
return JS_TRUE;
|
|
|
|
}
|
2006-08-20 06:37:03 -04:00
|
|
|
|
|
|
|
|
|
|
|
static JSString *
|
|
|
|
unicode_to_jsstring(JSContext *ctx, unicode_val_T u)
|
|
|
|
{
|
|
|
|
jschar buf[2];
|
|
|
|
|
2006-08-27 04:45:11 -04:00
|
|
|
/* This is supposed to make a string from which
|
|
|
|
* jsval_to_accesskey() can get the original @u back.
|
|
|
|
* If @u is a surrogate, then that is not possible, so
|
|
|
|
* return NULL to indicate an error instead.
|
|
|
|
*
|
|
|
|
* If JS_NewUCStringCopyN hits a null character, it truncates
|
2006-08-20 06:37:03 -04:00
|
|
|
* the string there and pads it with more nulls. However,
|
|
|
|
* that is not a problem here, because if there is a null
|
|
|
|
* character in buf[], then it must be the only character. */
|
2006-08-27 04:45:11 -04:00
|
|
|
if (u <= 0xFFFF && !is_utf16_surrogate(u)) {
|
2006-08-20 06:37:03 -04:00
|
|
|
buf[0] = u;
|
|
|
|
return JS_NewUCStringCopyN(ctx, buf, 1);
|
2006-08-24 16:30:41 -04:00
|
|
|
} else if (needs_utf16_surrogates(u)) {
|
|
|
|
buf[0] = get_utf16_high_surrogate(u);
|
|
|
|
buf[1] = get_utf16_low_surrogate(u);
|
2006-08-20 06:37:03 -04:00
|
|
|
return JS_NewUCStringCopyN(ctx, buf, 2);
|
|
|
|
} else {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
2006-08-20 10:10:08 -04:00
|
|
|
|
|
|
|
/* Convert the string *@vp to an access key. Return 0 for no access
|
|
|
|
* key, UCS_NO_CHAR on error, or the access key otherwise. */
|
|
|
|
static unicode_val_T
|
|
|
|
jsval_to_accesskey(JSContext *ctx, jsval *vp)
|
|
|
|
{
|
|
|
|
size_t len;
|
|
|
|
const jschar *chr;
|
|
|
|
|
|
|
|
/* Convert the value in place, to protect the result from GC. */
|
|
|
|
if (JS_ConvertValue(ctx, *vp, JSTYPE_STRING, vp) == JS_FALSE)
|
|
|
|
return UCS_NO_CHAR;
|
|
|
|
len = JS_GetStringLength(JSVAL_TO_STRING(*vp));
|
|
|
|
chr = JS_GetStringChars(JSVAL_TO_STRING(*vp));
|
|
|
|
|
|
|
|
/* This implementation ignores extra characters in the string. */
|
|
|
|
if (len < 1)
|
|
|
|
return 0; /* which means no access key */
|
2006-08-24 16:30:41 -04:00
|
|
|
if (!is_utf16_surrogate(chr[0]))
|
2006-08-20 10:10:08 -04:00
|
|
|
return chr[0];
|
|
|
|
if (len >= 2
|
2006-08-24 16:30:41 -04:00
|
|
|
&& is_utf16_high_surrogate(chr[0])
|
|
|
|
&& is_utf16_low_surrogate(chr[1]))
|
|
|
|
return join_utf16_surrogates(chr[0], chr[1]);
|
2006-08-20 10:10:08 -04:00
|
|
|
JS_ReportError(ctx, "Invalid UTF-16 sequence");
|
|
|
|
return UCS_NO_CHAR; /* which the caller will reject */
|
|
|
|
}
|