bug 755: Fixed.
[elinks.git] / src / ecmascript / spidermonkey / form.c
blob9236aa2ca4f2649629001b8722d9cef88d9f57cc
1 /* The SpiderMonkey window object implementation. */
3 #ifdef HAVE_CONFIG_H
4 #include "config.h"
5 #endif
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <string.h>
11 #include "elinks.h"
13 #include "ecmascript/spidermonkey/util.h"
15 #include "bfu/dialog.h"
16 #include "cache/cache.h"
17 #include "cookies/cookies.h"
18 #include "dialogs/menu.h"
19 #include "dialogs/status.h"
20 #include "document/html/frames.h"
21 #include "document/document.h"
22 #include "document/forms.h"
23 #include "document/view.h"
24 #include "ecmascript/ecmascript.h"
25 #include "ecmascript/spidermonkey/document.h"
26 #include "ecmascript/spidermonkey/form.h"
27 #include "ecmascript/spidermonkey/window.h"
28 #include "intl/gettext/libintl.h"
29 #include "main/select.h"
30 #include "osdep/newwin.h"
31 #include "osdep/sysname.h"
32 #include "protocol/http/http.h"
33 #include "protocol/uri.h"
34 #include "session/history.h"
35 #include "session/location.h"
36 #include "session/session.h"
37 #include "session/task.h"
38 #include "terminal/tab.h"
39 #include "terminal/terminal.h"
40 #include "util/conv.h"
41 #include "util/memory.h"
42 #include "util/string.h"
43 #include "viewer/text/draw.h"
44 #include "viewer/text/form.h"
45 #include "viewer/text/link.h"
46 #include "viewer/text/vs.h"
49 static const JSClass form_class; /* defined below */
52 /* Accordingly to the JS specs, each input type should own object. That'd be a
53 * huge PITA though, however DOM comes to the rescue and defines just a single
54 * HTMLInputElement. The difference could be spotted only by some clever tricky
55 * JS code, but I hope it doesn't matter anywhere. --pasky */
57 static JSBool input_get_property(JSContext *ctx, JSObject *obj, jsval id, jsval *vp);
58 static JSBool input_set_property(JSContext *ctx, JSObject *obj, jsval id, jsval *vp);
60 /* Each @input_class object must have a @form_class parent. */
61 static const JSClass input_class = {
62 "input", /* here, we unleash ourselves */
63 JSCLASS_HAS_PRIVATE, /* struct form_state * */
64 JS_PropertyStub, JS_PropertyStub,
65 input_get_property, input_set_property,
66 JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub
69 /* Tinyids of properties. Use negative values to distinguish these
70 * from array indexes (even though this object has no array elements).
71 * ECMAScript code should not use these directly as in input[-1];
72 * future versions of ELinks may change the numbers. */
73 enum input_prop {
74 JSP_INPUT_ACCESSKEY = -1,
75 JSP_INPUT_ALT = -2,
76 JSP_INPUT_CHECKED = -3,
77 JSP_INPUT_DEFAULT_CHECKED = -4,
78 JSP_INPUT_DEFAULT_VALUE = -5,
79 JSP_INPUT_DISABLED = -6,
80 JSP_INPUT_FORM = -7,
81 JSP_INPUT_MAX_LENGTH = -8,
82 JSP_INPUT_NAME = -9,
83 JSP_INPUT_READONLY = -10,
84 JSP_INPUT_SELECTED_INDEX = -11,
85 JSP_INPUT_SIZE = -12,
86 JSP_INPUT_SRC = -13,
87 JSP_INPUT_TABINDEX = -14,
88 JSP_INPUT_TYPE = -15,
89 JSP_INPUT_VALUE = -16,
92 /* XXX: Some of those are marked readonly just because we can't change them
93 * safely now. Changing default* values would affect all open instances of the
94 * document, leading to a potential security risk. Changing size and type would
95 * require re-rendering the document (TODO), tabindex would require renumbering
96 * of all links and whatnot. --pasky */
97 static const JSPropertySpec input_props[] = {
98 { "accessKey", JSP_INPUT_ACCESSKEY, JSPROP_ENUMERATE },
99 { "alt", JSP_INPUT_ALT, JSPROP_ENUMERATE },
100 { "checked", JSP_INPUT_CHECKED, JSPROP_ENUMERATE },
101 { "defaultChecked",JSP_INPUT_DEFAULT_CHECKED,JSPROP_ENUMERATE },
102 { "defaultValue",JSP_INPUT_DEFAULT_VALUE,JSPROP_ENUMERATE },
103 { "disabled", JSP_INPUT_DISABLED, JSPROP_ENUMERATE },
104 { "form", JSP_INPUT_FORM, JSPROP_ENUMERATE | JSPROP_READONLY },
105 { "maxLength", JSP_INPUT_MAX_LENGTH, JSPROP_ENUMERATE },
106 { "name", JSP_INPUT_NAME, JSPROP_ENUMERATE },
107 { "readonly", JSP_INPUT_READONLY, JSPROP_ENUMERATE },
108 { "selectedIndex",JSP_INPUT_SELECTED_INDEX,JSPROP_ENUMERATE },
109 { "size", JSP_INPUT_SIZE, JSPROP_ENUMERATE | JSPROP_READONLY },
110 { "src", JSP_INPUT_SRC, JSPROP_ENUMERATE },
111 { "tabindex", JSP_INPUT_TABINDEX, JSPROP_ENUMERATE | JSPROP_READONLY },
112 { "type", JSP_INPUT_TYPE, JSPROP_ENUMERATE | JSPROP_READONLY },
113 { "value", JSP_INPUT_VALUE, JSPROP_ENUMERATE },
114 { NULL }
117 static JSBool input_blur(JSContext *ctx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);
118 static JSBool input_click(JSContext *ctx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);
119 static JSBool input_focus(JSContext *ctx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);
120 static JSBool input_select(JSContext *ctx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);
122 static const JSFunctionSpec input_funcs[] = {
123 { "blur", input_blur, 0 },
124 { "click", input_click, 0 },
125 { "focus", input_focus, 0 },
126 { "select", input_select, 0 },
127 { NULL }
130 static JSString *unicode_to_jsstring(JSContext *ctx, unicode_val_T u);
131 static unicode_val_T jsval_to_accesskey(JSContext *ctx, jsval *vp);
134 static struct form_state *
135 input_get_form_state(JSContext *ctx, JSObject *obj, struct view_state *vs)
137 int n = (int)(long)JS_GetPrivate(ctx, obj);
139 return &vs->form_info[n];
142 /* @input_class.getProperty */
143 static JSBool
144 input_get_property(JSContext *ctx, JSObject *obj, jsval id, jsval *vp)
146 JSObject *parent_form; /* instance of @form_class */
147 JSObject *parent_doc; /* instance of @document_class */
148 JSObject *parent_win; /* instance of @window_class */
149 struct view_state *vs;
150 struct document_view *doc_view;
151 struct document *document;
152 struct form_state *fs;
153 struct form_control *fc;
154 int linknum;
155 struct link *link = NULL;
157 /* This can be called if @obj if not itself an instance of the
158 * appropriate class but has one in its prototype chain. Fail
159 * such calls. */
160 if (!JS_InstanceOf(ctx, obj, (JSClass *) &input_class, NULL))
161 return JS_FALSE;
162 parent_form = JS_GetParent(ctx, obj);
163 assert(JS_InstanceOf(ctx, parent_form, (JSClass *) &form_class, NULL));
164 if_assert_failed return JS_FALSE;
165 parent_doc = JS_GetParent(ctx, parent_form);
166 assert(JS_InstanceOf(ctx, parent_doc, (JSClass *) &document_class, NULL));
167 if_assert_failed return JS_FALSE;
168 parent_win = JS_GetParent(ctx, parent_doc);
169 assert(JS_InstanceOf(ctx, parent_win, (JSClass *) &window_class, NULL));
170 if_assert_failed return JS_FALSE;
172 vs = JS_GetPrivate(ctx, parent_win); /* from @window_class */
173 doc_view = vs->doc_view;
174 document = doc_view->document;
175 fs = input_get_form_state(ctx, obj, vs);
176 fc = find_form_control(document, fs);
178 assert(fc);
179 assert(fc->form && fs);
181 if (!JSVAL_IS_INT(id))
182 return JS_TRUE;
184 linknum = get_form_control_link(document, fc);
185 /* Hiddens have no link. */
186 if (linknum >= 0) link = &document->links[linknum];
188 undef_to_jsval(ctx, vp);
190 switch (JSVAL_TO_INT(id)) {
191 case JSP_INPUT_ACCESSKEY:
193 JSString *keystr;
195 if (!link) break;
197 if (!link->accesskey) {
198 *vp = JS_GetEmptyStringValue(ctx);
199 } else {
200 keystr = unicode_to_jsstring(ctx, link->accesskey);
201 if (keystr)
202 *vp = STRING_TO_JSVAL(keystr);
203 else
204 return JS_FALSE;
206 break;
208 case JSP_INPUT_ALT:
209 string_to_jsval(ctx, vp, fc->alt);
210 break;
211 case JSP_INPUT_CHECKED:
212 boolean_to_jsval(ctx, vp, fs->state);
213 break;
214 case JSP_INPUT_DEFAULT_CHECKED:
215 boolean_to_jsval(ctx, vp, fc->default_state);
216 break;
217 case JSP_INPUT_DEFAULT_VALUE:
218 /* FIXME (bug 805): convert from the charset of the document */
219 string_to_jsval(ctx, vp, fc->default_value);
220 break;
221 case JSP_INPUT_DISABLED:
222 /* FIXME: <input readonly disabled> --pasky */
223 boolean_to_jsval(ctx, vp, fc->mode == FORM_MODE_DISABLED);
224 break;
225 case JSP_INPUT_FORM:
226 object_to_jsval(ctx, vp, parent_form);
227 break;
228 case JSP_INPUT_MAX_LENGTH:
229 int_to_jsval(ctx, vp, fc->maxlength);
230 break;
231 case JSP_INPUT_NAME:
232 string_to_jsval(ctx, vp, fc->name);
233 break;
234 case JSP_INPUT_READONLY:
235 /* FIXME: <input readonly disabled> --pasky */
236 boolean_to_jsval(ctx, vp, fc->mode == FORM_MODE_READONLY);
237 break;
238 case JSP_INPUT_SIZE:
239 int_to_jsval(ctx, vp, fc->size);
240 break;
241 case JSP_INPUT_SRC:
242 if (link && link->where_img)
243 string_to_jsval(ctx, vp, link->where_img);
244 break;
245 case JSP_INPUT_TABINDEX:
246 if (link)
247 /* FIXME: This is WRONG. --pasky */
248 int_to_jsval(ctx, vp, link->number);
249 break;
250 case JSP_INPUT_TYPE:
252 unsigned char *s = NULL;
254 switch (fc->type) {
255 case FC_TEXT: s = "text"; break;
256 case FC_PASSWORD: s = "password"; break;
257 case FC_FILE: s = "file"; break;
258 case FC_CHECKBOX: s = "checkbox"; break;
259 case FC_RADIO: s = "radio"; break;
260 case FC_SUBMIT: s = "submit"; break;
261 case FC_IMAGE: s = "image"; break;
262 case FC_RESET: s = "reset"; break;
263 case FC_BUTTON: s = "button"; break;
264 case FC_HIDDEN: s = "hidden"; break;
265 case FC_SELECT: s = "select"; break;
266 default: INTERNAL("input_get_property() upon a non-input item."); break;
268 string_to_jsval(ctx, vp, s);
269 break;
271 case JSP_INPUT_VALUE:
272 string_to_jsval(ctx, vp, fs->value);
273 break;
275 case JSP_INPUT_SELECTED_INDEX:
276 if (fc->type == FC_SELECT) int_to_jsval(ctx, vp, fs->state);
277 break;
278 default:
279 /* Unrecognized property ID; someone is using the
280 * object as an array. SMJS builtin classes (e.g.
281 * js_RegExpClass) just return JS_TRUE in this case
282 * and leave *@vp unchanged. Do the same here.
283 * (Actually not quite the same, as we already used
284 * @undef_to_jsval.) */
285 break;
288 return JS_TRUE;
291 /* @input_class.setProperty */
292 static JSBool
293 input_set_property(JSContext *ctx, JSObject *obj, jsval id, jsval *vp)
295 JSObject *parent_form; /* instance of @form_class */
296 JSObject *parent_doc; /* instance of @document_class */
297 JSObject *parent_win; /* instance of @window_class */
298 struct view_state *vs;
299 struct document_view *doc_view;
300 struct document *document;
301 struct form_state *fs;
302 struct form_control *fc;
303 int linknum;
304 struct link *link = NULL;
305 unicode_val_T accesskey;
307 /* This can be called if @obj if not itself an instance of the
308 * appropriate class but has one in its prototype chain. Fail
309 * such calls. */
310 if (!JS_InstanceOf(ctx, obj, (JSClass *) &input_class, NULL))
311 return JS_FALSE;
312 parent_form = JS_GetParent(ctx, obj);
313 assert(JS_InstanceOf(ctx, parent_form, (JSClass *) &form_class, NULL));
314 if_assert_failed return JS_FALSE;
315 parent_doc = JS_GetParent(ctx, parent_form);
316 assert(JS_InstanceOf(ctx, parent_doc, (JSClass *) &document_class, NULL));
317 if_assert_failed return JS_FALSE;
318 parent_win = JS_GetParent(ctx, parent_doc);
319 assert(JS_InstanceOf(ctx, parent_win, (JSClass *) &window_class, NULL));
320 if_assert_failed return JS_FALSE;
322 vs = JS_GetPrivate(ctx, parent_win); /* from @window_class */
323 doc_view = vs->doc_view;
324 document = doc_view->document;
325 fs = input_get_form_state(ctx, obj, vs);
326 fc = find_form_control(document, fs);
328 assert(fc);
329 assert(fc->form && fs);
331 if (!JSVAL_IS_INT(id))
332 return JS_TRUE;
334 linknum = get_form_control_link(document, fc);
335 /* Hiddens have no link. */
336 if (linknum >= 0) link = &document->links[linknum];
338 switch (JSVAL_TO_INT(id)) {
339 case JSP_INPUT_ACCESSKEY:
340 accesskey = jsval_to_accesskey(ctx, vp);
341 if (accesskey == UCS_NO_CHAR)
342 return JS_FALSE;
343 else if (link)
344 link->accesskey = accesskey;
345 break;
346 case JSP_INPUT_ALT:
347 mem_free_set(&fc->alt, stracpy(jsval_to_string(ctx, vp)));
348 break;
349 case JSP_INPUT_CHECKED:
350 if (fc->type != FC_CHECKBOX && fc->type != FC_RADIO)
351 break;
352 fs->state = jsval_to_boolean(ctx, vp);
353 break;
354 case JSP_INPUT_DISABLED:
355 /* FIXME: <input readonly disabled> --pasky */
356 fc->mode = (jsval_to_boolean(ctx, vp) ? FORM_MODE_DISABLED
357 : fc->mode == FORM_MODE_READONLY ? FORM_MODE_READONLY
358 : FORM_MODE_NORMAL);
359 break;
360 case JSP_INPUT_MAX_LENGTH:
361 JS_ValueToInt32(ctx, *vp, &fc->maxlength);
362 break;
363 case JSP_INPUT_NAME:
364 mem_free_set(&fc->name, stracpy(jsval_to_string(ctx, vp)));
365 break;
366 case JSP_INPUT_READONLY:
367 /* FIXME: <input readonly disabled> --pasky */
368 fc->mode = (jsval_to_boolean(ctx, vp) ? FORM_MODE_READONLY
369 : fc->mode == FORM_MODE_DISABLED ? FORM_MODE_DISABLED
370 : FORM_MODE_NORMAL);
371 break;
372 case JSP_INPUT_SRC:
373 if (link) {
374 mem_free_set(&link->where_img, stracpy(jsval_to_string(ctx, vp)));
376 break;
377 case JSP_INPUT_VALUE:
378 if (fc->type == FC_FILE)
379 break; /* A huge security risk otherwise. */
380 mem_free_set(&fs->value, stracpy(jsval_to_string(ctx, vp)));
381 if (fc->type == FC_TEXT || fc->type == FC_PASSWORD)
382 fs->state = strlen(fs->value);
383 break;
384 case JSP_INPUT_SELECTED_INDEX:
385 if (fc->type == FC_SELECT) {
386 int item;
388 JS_ValueToInt32(ctx, *vp, &item);
390 if (item >= 0 && item < fc->nvalues) {
391 fs->state = item;
392 mem_free_set(&fs->value, stracpy(fc->values[item]));
395 break;
397 default:
398 /* Unrecognized property ID; someone is using the
399 * object as an array. SMJS builtin classes (e.g.
400 * js_RegExpClass) just return JS_TRUE in this case.
401 * Do the same here. */
402 return JS_TRUE;
405 return JS_TRUE;
408 /* @input_funcs{"blur"} */
409 static JSBool
410 input_blur(JSContext *ctx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
412 /* We are a text-mode browser and there *always* has to be something
413 * selected. So we do nothing for now. (That was easy.) */
414 return JS_TRUE;
417 /* @input_funcs{"click"} */
418 static JSBool
419 input_click(JSContext *ctx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
421 JSObject *parent_form; /* instance of @form_class */
422 JSObject *parent_doc; /* instance of @document_class */
423 JSObject *parent_win; /* instance of @window_class */
424 struct view_state *vs;
425 struct document_view *doc_view;
426 struct document *document;
427 struct session *ses;
428 struct form_state *fs;
429 struct form_control *fc;
430 int linknum;
432 if (!JS_InstanceOf(ctx, obj, (JSClass *) &input_class, argv)) return JS_FALSE;
433 parent_form = JS_GetParent(ctx, obj);
434 assert(JS_InstanceOf(ctx, parent_form, (JSClass *) &form_class, NULL));
435 if_assert_failed return JS_FALSE;
436 parent_doc = JS_GetParent(ctx, parent_form);
437 assert(JS_InstanceOf(ctx, parent_doc, (JSClass *) &document_class, NULL));
438 if_assert_failed return JS_FALSE;
439 parent_win = JS_GetParent(ctx, parent_doc);
440 assert(JS_InstanceOf(ctx, parent_win, (JSClass *) &window_class, NULL));
441 if_assert_failed return JS_FALSE;
443 vs = JS_GetPrivate(ctx, parent_win); /* from @window_class */
444 doc_view = vs->doc_view;
445 document = doc_view->document;
446 ses = doc_view->session;
447 fs = input_get_form_state(ctx, obj, vs);
449 assert(fs);
450 fc = find_form_control(document, fs);
451 assert(fc);
453 linknum = get_form_control_link(document, fc);
454 /* Hiddens have no link. */
455 if (linknum < 0)
456 return JS_TRUE;
458 /* Restore old current_link afterwards? */
459 jump_to_link_number(ses, doc_view, linknum);
460 if (enter(ses, doc_view, 0) == FRAME_EVENT_REFRESH)
461 refresh_view(ses, doc_view, 0);
462 else
463 print_screen_status(ses);
465 boolean_to_jsval(ctx, rval, 0);
466 return JS_TRUE;
469 /* @input_funcs{"focus"} */
470 static JSBool
471 input_focus(JSContext *ctx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
473 JSObject *parent_form; /* instance of @form_class */
474 JSObject *parent_doc; /* instance of @document_class */
475 JSObject *parent_win; /* instance of @window_class */
476 struct view_state *vs;
477 struct document_view *doc_view;
478 struct document *document;
479 struct session *ses;
480 struct form_state *fs;
481 struct form_control *fc;
482 int linknum;
484 if (!JS_InstanceOf(ctx, obj, (JSClass *) &input_class, argv)) return JS_FALSE;
485 parent_form = JS_GetParent(ctx, obj);
486 assert(JS_InstanceOf(ctx, parent_form, (JSClass *) &form_class, NULL));
487 if_assert_failed return JS_FALSE;
488 parent_doc = JS_GetParent(ctx, parent_form);
489 assert(JS_InstanceOf(ctx, parent_doc, (JSClass *) &document_class, NULL));
490 if_assert_failed return JS_FALSE;
491 parent_win = JS_GetParent(ctx, parent_doc);
492 assert(JS_InstanceOf(ctx, parent_win, (JSClass *) &window_class, NULL));
493 if_assert_failed return JS_FALSE;
495 vs = JS_GetPrivate(ctx, parent_win); /* from @window_class */
496 doc_view = vs->doc_view;
497 document = doc_view->document;
498 ses = doc_view->session;
499 fs = input_get_form_state(ctx, obj, vs);
501 assert(fs);
502 fc = find_form_control(document, fs);
503 assert(fc);
505 linknum = get_form_control_link(document, fc);
506 /* Hiddens have no link. */
507 if (linknum < 0)
508 return JS_TRUE;
510 jump_to_link_number(ses, doc_view, linknum);
512 boolean_to_jsval(ctx, rval, 0);
513 return JS_TRUE;
516 /* @input_funcs{"select"} */
517 static JSBool
518 input_select(JSContext *ctx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
520 /* We support no text selecting yet. So we do nothing for now.
521 * (That was easy, too.) */
522 return JS_TRUE;
525 static JSObject *
526 get_input_object(JSContext *ctx, JSObject *jsform, long number)
528 #if 0
529 if (fs->ecmascript_obj)
530 return fs->ecmascript_obj;
531 #endif
532 /* jsform ('form') is input's parent */
533 /* FIXME: That is NOT correct since the real containing element
534 * should be its parent, but gimme DOM first. --pasky */
535 JSObject *jsinput = JS_NewObject(ctx, (JSClass *) &input_class, NULL, jsform);
537 JS_DefineProperties(ctx, jsinput, (JSPropertySpec *) input_props);
538 JS_DefineFunctions(ctx, jsinput, (JSFunctionSpec *) input_funcs);
539 JS_SetPrivate(ctx, jsinput, (void *)number); /* to @input_class */
540 return jsinput;;
544 static JSObject *
545 get_form_control_object(JSContext *ctx, JSObject *jsform, enum form_type type, int number)
547 switch (type) {
548 case FC_TEXT:
549 case FC_PASSWORD:
550 case FC_FILE:
551 case FC_CHECKBOX:
552 case FC_RADIO:
553 case FC_SUBMIT:
554 case FC_IMAGE:
555 case FC_RESET:
556 case FC_BUTTON:
557 case FC_HIDDEN:
558 case FC_SELECT:
559 return get_input_object(ctx, jsform, (long)number);
561 case FC_TEXTAREA:
562 /* TODO */
563 return NULL;
565 default:
566 INTERNAL("Weird fc->type %d", type);
567 return NULL;
573 static JSBool form_elements_get_property(JSContext *ctx, JSObject *obj, jsval id, jsval *vp);
575 /* Each @form_elements_class object must have a @form_class parent. */
576 static const JSClass form_elements_class = {
577 "elements",
578 JSCLASS_HAS_PRIVATE,
579 JS_PropertyStub, JS_PropertyStub,
580 form_elements_get_property, JS_PropertyStub,
581 JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub
584 static JSBool form_elements_item(JSContext *ctx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);
585 static JSBool form_elements_namedItem(JSContext *ctx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);
587 static const JSFunctionSpec form_elements_funcs[] = {
588 { "item", form_elements_item, 1 },
589 { "namedItem", form_elements_namedItem, 1 },
590 { NULL }
593 /* Tinyids of properties. Use negative values to distinguish these
594 * from array indexes (elements[INT] for INT>=0 is equivalent to
595 * elements.item(INT)). ECMAScript code should not use these directly
596 * as in elements[-1]; future versions of ELinks may change the numbers. */
597 enum form_elements_prop {
598 JSP_FORM_ELEMENTS_LENGTH = -1,
600 static const JSPropertySpec form_elements_props[] = {
601 { "length", JSP_FORM_ELEMENTS_LENGTH, JSPROP_ENUMERATE | JSPROP_READONLY},
602 { NULL }
605 /* @form_elements_class.getProperty */
606 static JSBool
607 form_elements_get_property(JSContext *ctx, JSObject *obj, jsval id, jsval *vp)
609 JSObject *parent_form; /* instance of @form_class */
610 JSObject *parent_doc; /* instance of @document_class */
611 JSObject *parent_win; /* instance of @window_class */
612 struct view_state *vs;
613 struct document_view *doc_view;
614 struct document *document;
615 struct form_view *form_view;
616 struct form *form;
618 /* This can be called if @obj if not itself an instance of the
619 * appropriate class but has one in its prototype chain. Fail
620 * such calls. */
621 if (!JS_InstanceOf(ctx, obj, (JSClass *) &form_elements_class, NULL))
622 return JS_FALSE;
623 parent_form = JS_GetParent(ctx, obj);
624 assert(JS_InstanceOf(ctx, parent_form, (JSClass *) &form_class, NULL));
625 if_assert_failed return JS_FALSE;
626 parent_doc = JS_GetParent(ctx, parent_form);
627 assert(JS_InstanceOf(ctx, parent_doc, (JSClass *) &document_class, NULL));
628 if_assert_failed return JS_FALSE;
629 parent_win = JS_GetParent(ctx, parent_doc);
630 assert(JS_InstanceOf(ctx, parent_win, (JSClass *) &window_class, NULL));
631 if_assert_failed return JS_FALSE;
633 vs = JS_GetPrivate(ctx, parent_win); /* from @window_class */
634 doc_view = vs->doc_view;
635 document = doc_view->document;
636 form_view = JS_GetPrivate(ctx, parent_form); /* from @form_class */
637 form = find_form_by_form_view(document, form_view);
639 if (JSVAL_IS_STRING(id)) {
640 form_elements_namedItem(ctx, obj, 1, &id, vp);
641 return JS_TRUE;
644 if (!JSVAL_IS_INT(id))
645 return JS_TRUE;
647 undef_to_jsval(ctx, vp);
649 switch (JSVAL_TO_INT(id)) {
650 case JSP_FORM_ELEMENTS_LENGTH:
651 int_to_jsval(ctx, vp, list_size(&form->items));
652 break;
653 default:
654 /* Array index. */
655 form_elements_item(ctx, obj, 1, &id, vp);
656 break;
659 return JS_TRUE;
662 /* @form_elements_funcs{"item"} */
663 static JSBool
664 form_elements_item(JSContext *ctx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
666 JSObject *parent_form; /* instance of @form_class */
667 JSObject *parent_doc; /* instance of @document_class */
668 JSObject *parent_win; /* instance of @window_class */
669 struct view_state *vs;
670 struct document_view *doc_view;
671 struct document *document;
672 struct form_view *form_view;
673 struct form *form;
674 struct form_control *fc;
675 int counter = -1;
676 int index;
678 if (!JS_InstanceOf(ctx, obj, (JSClass *) &form_elements_class, argv)) return JS_FALSE;
679 parent_form = JS_GetParent(ctx, obj);
680 assert(JS_InstanceOf(ctx, parent_form, (JSClass *) &form_class, NULL));
681 if_assert_failed return JS_FALSE;
682 parent_doc = JS_GetParent(ctx, parent_form);
683 assert(JS_InstanceOf(ctx, parent_doc, (JSClass *) &document_class, NULL));
684 if_assert_failed return JS_FALSE;
685 parent_win = JS_GetParent(ctx, parent_doc);
686 assert(JS_InstanceOf(ctx, parent_win, (JSClass *) &window_class, NULL));
687 if_assert_failed return JS_FALSE;
689 vs = JS_GetPrivate(ctx, parent_win); /* from @window_class */
690 doc_view = vs->doc_view;
691 document = doc_view->document;
692 form_view = JS_GetPrivate(ctx, parent_form); /* from @form_class */
693 form = find_form_by_form_view(document, form_view);
695 if (argc != 1)
696 return JS_TRUE;
698 JS_ValueToInt32(ctx, argv[0], &index);
699 undef_to_jsval(ctx, rval);
701 foreach (fc, form->items) {
702 counter++;
703 if (counter == index) {
704 struct form_state *fs = find_form_state(doc_view, fc);
706 if (fs) {
707 JSObject *fcobj = get_form_control_object(ctx, parent_form, fc->type, fc->g_ctrl_num);
709 if (fcobj)
710 object_to_jsval(ctx, rval, fcobj);
712 break;
716 return JS_TRUE;
719 /* @form_elements_funcs{"namedItem"} */
720 static JSBool
721 form_elements_namedItem(JSContext *ctx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
723 JSObject *parent_form; /* instance of @form_class */
724 JSObject *parent_doc; /* instance of @document_class */
725 JSObject *parent_win; /* instance of @window_class */
726 struct view_state *vs;
727 struct document_view *doc_view;
728 struct document *document;
729 struct form_view *form_view;
730 struct form *form;
731 struct form_control *fc;
732 unsigned char *string;
734 if (!JS_InstanceOf(ctx, obj, (JSClass *) &form_elements_class, argv)) return JS_FALSE;
735 parent_form = JS_GetParent(ctx, obj);
736 assert(JS_InstanceOf(ctx, parent_form, (JSClass *) &form_class, NULL));
737 if_assert_failed return JS_FALSE;
738 parent_doc = JS_GetParent(ctx, parent_form);
739 assert(JS_InstanceOf(ctx, parent_doc, (JSClass *) &document_class, NULL));
740 if_assert_failed return JS_FALSE;
741 parent_win = JS_GetParent(ctx, parent_doc);
742 assert(JS_InstanceOf(ctx, parent_win, (JSClass *) &window_class, NULL));
743 if_assert_failed return JS_FALSE;
745 vs = JS_GetPrivate(ctx, parent_win); /* from @window_class */
746 doc_view = vs->doc_view;
747 document = doc_view->document;
748 form_view = JS_GetPrivate(ctx, parent_form); /* from @form_class */
749 form = find_form_by_form_view(document, form_view);
751 if (argc != 1)
752 return JS_TRUE;
754 string = jsval_to_string(ctx, &argv[0]);
755 if (!*string)
756 return JS_TRUE;
758 undef_to_jsval(ctx, rval);
760 foreach (fc, form->items) {
761 if ((fc->id && !strcasecmp(string, fc->id)) || (fc->name && !strcasecmp(string, fc->name))) {
762 struct form_state *fs = find_form_state(doc_view, fc);
764 if (fs) {
765 JSObject *fcobj = get_form_control_object(ctx, parent_form, fc->type, fc->g_ctrl_num);
767 if (fcobj)
768 object_to_jsval(ctx, rval, fcobj);
770 break;
774 return JS_TRUE;
779 static JSBool form_get_property(JSContext *ctx, JSObject *obj, jsval id, jsval *vp);
780 static JSBool form_set_property(JSContext *ctx, JSObject *obj, jsval id, jsval *vp);
782 /* Each @form_class object must have a @document_class parent. */
783 static const JSClass form_class = {
784 "form",
785 JSCLASS_HAS_PRIVATE, /* struct form_view * */
786 JS_PropertyStub, JS_PropertyStub,
787 form_get_property, form_set_property,
788 JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub
791 /* Tinyids of properties. Use negative values to distinguish these
792 * from array indexes (even though this object has no array elements).
793 * ECMAScript code should not use these directly as in form[-1];
794 * future versions of ELinks may change the numbers. */
795 enum form_prop {
796 JSP_FORM_ACTION = -1,
797 JSP_FORM_ELEMENTS = -2,
798 JSP_FORM_ENCODING = -3,
799 JSP_FORM_LENGTH = -4,
800 JSP_FORM_METHOD = -5,
801 JSP_FORM_NAME = -6,
802 JSP_FORM_TARGET = -7,
805 static const JSPropertySpec form_props[] = {
806 { "action", JSP_FORM_ACTION, JSPROP_ENUMERATE },
807 { "elements", JSP_FORM_ELEMENTS, JSPROP_ENUMERATE },
808 { "encoding", JSP_FORM_ENCODING, JSPROP_ENUMERATE },
809 { "length", JSP_FORM_LENGTH, JSPROP_ENUMERATE | JSPROP_READONLY },
810 { "method", JSP_FORM_METHOD, JSPROP_ENUMERATE },
811 { "name", JSP_FORM_NAME, JSPROP_ENUMERATE },
812 { "target", JSP_FORM_TARGET, JSPROP_ENUMERATE },
813 { NULL }
816 static JSBool form_reset(JSContext *ctx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);
817 static JSBool form_submit(JSContext *ctx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);
819 static const JSFunctionSpec form_funcs[] = {
820 { "reset", form_reset, 0 },
821 { "submit", form_submit, 0 },
822 { NULL }
825 /* @form_class.getProperty */
826 static JSBool
827 form_get_property(JSContext *ctx, JSObject *obj, jsval id, jsval *vp)
829 /* DBG("doc %p %s\n", parent_doc, JS_GetStringBytes(JS_ValueToString(ctx, OBJECT_TO_JSVAL(parent_doc)))); */
830 JSObject *parent_doc; /* instance of @document_class */
831 JSObject *parent_win; /* instance of @window_class */
832 struct view_state *vs;
833 struct document_view *doc_view;
834 struct form_view *fv;
835 struct form *form;
837 /* This can be called if @obj if not itself an instance of the
838 * appropriate class but has one in its prototype chain. Fail
839 * such calls. */
840 if (!JS_InstanceOf(ctx, obj, (JSClass *) &form_class, NULL))
841 return JS_FALSE;
842 parent_doc = JS_GetParent(ctx, obj);
843 assert(JS_InstanceOf(ctx, parent_doc, (JSClass *) &document_class, NULL));
844 if_assert_failed return JS_FALSE;
845 parent_win = JS_GetParent(ctx, parent_doc);
846 assert(JS_InstanceOf(ctx, parent_win, (JSClass *) &window_class, NULL));
847 if_assert_failed return JS_FALSE;
849 vs = JS_GetPrivate(ctx, parent_win); /* from @window_class */
850 doc_view = vs->doc_view;
851 fv = JS_GetPrivate(ctx, obj); /* from @form_class */
852 form = find_form_by_form_view(doc_view->document, fv);
854 assert(form);
856 if (JSVAL_IS_STRING(id)) {
857 struct form_control *fc;
858 unsigned char *string;
860 string = jsval_to_string(ctx, &id);
861 foreach (fc, form->items) {
862 JSObject *fcobj = NULL;
863 struct form_state *fs;
865 if ((!fc->id || strcasecmp(string, fc->id)) && (!fc->name || strcasecmp(string, fc->name)))
866 continue;
868 undef_to_jsval(ctx, vp);
869 fs = find_form_state(doc_view, fc);
870 if (fs) {
871 fcobj = get_form_control_object(ctx, obj, fc->type, fc->g_ctrl_num);
872 if (fcobj)
873 object_to_jsval(ctx, vp, fcobj);
875 break;
877 return JS_TRUE;
880 if (!JSVAL_IS_INT(id))
881 return JS_TRUE;
883 undef_to_jsval(ctx, vp);
885 switch (JSVAL_TO_INT(id)) {
886 case JSP_FORM_ACTION:
887 string_to_jsval(ctx, vp, form->action);
888 break;
890 case JSP_FORM_ELEMENTS:
892 /* jsform ('form') is form_elements' parent; who knows is that's correct */
893 JSObject *jsform_elems = JS_NewObject(ctx, (JSClass *) &form_elements_class, NULL, obj);
895 JS_DefineProperties(ctx, jsform_elems, (JSPropertySpec *) form_elements_props);
896 JS_DefineFunctions(ctx, jsform_elems, (JSFunctionSpec *) form_elements_funcs);
897 object_to_jsval(ctx, vp, jsform_elems);
898 /* SM will cache this property value for us so we create this
899 * just once per form. */
901 break;
903 case JSP_FORM_ENCODING:
904 switch (form->method) {
905 case FORM_METHOD_GET:
906 case FORM_METHOD_POST:
907 string_to_jsval(ctx, vp, "application/x-www-form-urlencoded");
908 break;
909 case FORM_METHOD_POST_MP:
910 string_to_jsval(ctx, vp, "multipart/form-data");
911 break;
912 case FORM_METHOD_POST_TEXT_PLAIN:
913 string_to_jsval(ctx, vp, "text/plain");
914 break;
916 break;
918 case JSP_FORM_LENGTH:
919 int_to_jsval(ctx, vp, list_size(&form->items));
920 break;
922 case JSP_FORM_METHOD:
923 switch (form->method) {
924 case FORM_METHOD_GET:
925 string_to_jsval(ctx, vp, "GET");
926 break;
928 case FORM_METHOD_POST:
929 case FORM_METHOD_POST_MP:
930 case FORM_METHOD_POST_TEXT_PLAIN:
931 string_to_jsval(ctx, vp, "POST");
932 break;
934 break;
936 case JSP_FORM_NAME:
937 string_to_jsval(ctx, vp, form->name);
938 break;
940 case JSP_FORM_TARGET:
941 string_to_jsval(ctx, vp, form->target);
942 break;
944 default:
945 /* Unrecognized property ID; someone is using the
946 * object as an array. SMJS builtin classes (e.g.
947 * js_RegExpClass) just return JS_TRUE in this case
948 * and leave *@vp unchanged. Do the same here.
949 * (Actually not quite the same, as we already used
950 * @undef_to_jsval.) */
951 break;
954 return JS_TRUE;
957 /* @form_class.setProperty */
958 static JSBool
959 form_set_property(JSContext *ctx, JSObject *obj, jsval id, jsval *vp)
961 JSObject *parent_doc; /* instance of @document_class */
962 JSObject *parent_win; /* instance of @window_class */
963 struct view_state *vs;
964 struct document_view *doc_view;
965 struct form_view *fv;
966 struct form *form;
967 unsigned char *string;
969 /* This can be called if @obj if not itself an instance of the
970 * appropriate class but has one in its prototype chain. Fail
971 * such calls. */
972 if (!JS_InstanceOf(ctx, obj, (JSClass *) &form_class, NULL))
973 return JS_FALSE;
974 parent_doc = JS_GetParent(ctx, obj);
975 assert(JS_InstanceOf(ctx, parent_doc, (JSClass *) &document_class, NULL));
976 if_assert_failed return JS_FALSE;
977 parent_win = JS_GetParent(ctx, parent_doc);
978 assert(JS_InstanceOf(ctx, parent_win, (JSClass *) &window_class, NULL));
979 if_assert_failed return JS_FALSE;
981 vs = JS_GetPrivate(ctx, parent_win); /* from @window_class */
982 doc_view = vs->doc_view;
983 fv = JS_GetPrivate(ctx, obj); /* from @form_class */
984 form = find_form_by_form_view(doc_view->document, fv);
986 assert(form);
988 if (!JSVAL_IS_INT(id))
989 return JS_TRUE;
991 switch (JSVAL_TO_INT(id)) {
992 case JSP_FORM_ACTION:
993 string = stracpy(jsval_to_string(ctx, vp));
994 if (form->action) {
995 ecmascript_set_action(&form->action, string);
996 } else {
997 mem_free_set(&form->action, string);
999 break;
1001 case JSP_FORM_ENCODING:
1002 string = jsval_to_string(ctx, vp);
1003 if (!strcasecmp(string, "application/x-www-form-urlencoded")) {
1004 form->method = form->method == FORM_METHOD_GET ? FORM_METHOD_GET
1005 : FORM_METHOD_POST;
1006 } else if (!strcasecmp(string, "multipart/form-data")) {
1007 form->method = FORM_METHOD_POST_MP;
1008 } else if (!strcasecmp(string, "text/plain")) {
1009 form->method = FORM_METHOD_POST_TEXT_PLAIN;
1011 break;
1013 case JSP_FORM_METHOD:
1014 string = jsval_to_string(ctx, vp);
1015 if (!strcasecmp(string, "GET")) {
1016 form->method = FORM_METHOD_GET;
1017 } else if (!strcasecmp(string, "POST")) {
1018 form->method = FORM_METHOD_POST;
1020 break;
1022 case JSP_FORM_NAME:
1023 mem_free_set(&form->name, stracpy(jsval_to_string(ctx, vp)));
1024 break;
1026 case JSP_FORM_TARGET:
1027 mem_free_set(&form->target, stracpy(jsval_to_string(ctx, vp)));
1028 break;
1030 default:
1031 /* Unrecognized property ID; someone is using the
1032 * object as an array. SMJS builtin classes (e.g.
1033 * js_RegExpClass) just return JS_TRUE in this case.
1034 * Do the same here. */
1035 break;
1038 return JS_TRUE;
1041 /* @form_funcs{"reset"} */
1042 static JSBool
1043 form_reset(JSContext *ctx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1045 JSObject *parent_doc; /* instance of @document_class */
1046 JSObject *parent_win; /* instance of @window_class */
1047 struct view_state *vs;
1048 struct document_view *doc_view;
1049 struct form_view *fv;
1050 struct form *form;
1052 if (!JS_InstanceOf(ctx, obj, (JSClass *) &form_class, argv)) return JS_FALSE;
1053 parent_doc = JS_GetParent(ctx, obj);
1054 assert(JS_InstanceOf(ctx, parent_doc, (JSClass *) &document_class, NULL));
1055 if_assert_failed return JS_FALSE;
1056 parent_win = JS_GetParent(ctx, parent_doc);
1057 assert(JS_InstanceOf(ctx, parent_win, (JSClass *) &window_class, NULL));
1058 if_assert_failed return JS_FALSE;
1060 vs = JS_GetPrivate(ctx, parent_win); /* from @window_class */
1061 doc_view = vs->doc_view;
1062 fv = JS_GetPrivate(ctx, obj); /* from @form_class */
1063 form = find_form_by_form_view(doc_view->document, fv);
1065 assert(form);
1067 do_reset_form(doc_view, form);
1068 draw_forms(doc_view->session->tab->term, doc_view);
1070 boolean_to_jsval(ctx, rval, 0);
1072 return JS_TRUE;
1075 /* @form_funcs{"submit"} */
1076 static JSBool
1077 form_submit(JSContext *ctx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1079 JSObject *parent_doc; /* instance of @document_class */
1080 JSObject *parent_win; /* instance of @window_class */
1081 struct view_state *vs;
1082 struct document_view *doc_view;
1083 struct session *ses;
1084 struct form_view *fv;
1085 struct form *form;
1087 if (!JS_InstanceOf(ctx, obj, (JSClass *) &form_class, argv)) return JS_FALSE;
1088 parent_doc = JS_GetParent(ctx, obj);
1089 assert(JS_InstanceOf(ctx, parent_doc, (JSClass *) &document_class, NULL));
1090 if_assert_failed return JS_FALSE;
1091 parent_win = JS_GetParent(ctx, parent_doc);
1092 assert(JS_InstanceOf(ctx, parent_win, (JSClass *) &window_class, NULL));
1093 if_assert_failed return JS_FALSE;
1095 vs = JS_GetPrivate(ctx, parent_win); /* from @window_class */
1096 doc_view = vs->doc_view;
1097 ses = doc_view->session;
1098 fv = JS_GetPrivate(ctx, obj); /* from @form_class */
1099 form = find_form_by_form_view(doc_view->document, fv);
1101 assert(form);
1102 submit_given_form(ses, doc_view, form, 0);
1104 boolean_to_jsval(ctx, rval, 0);
1106 return JS_TRUE;
1109 JSObject *
1110 get_form_object(JSContext *ctx, JSObject *jsdoc, struct form_view *fv)
1112 #if 0
1113 if (fv->ecmascript_obj)
1114 return fv->ecmascript_obj;
1115 #endif
1116 /* jsdoc ('document') is fv's parent */
1117 /* FIXME: That is NOT correct since the real containing element
1118 * should be its parent, but gimme DOM first. --pasky */
1119 JSObject *jsform = JS_NewObject(ctx, (JSClass *) &form_class, NULL, jsdoc);
1121 JS_DefineProperties(ctx, jsform, (JSPropertySpec *) form_props);
1122 JS_DefineFunctions(ctx, jsform, (JSFunctionSpec *) form_funcs);
1123 JS_SetPrivate(ctx, jsform, fv); /* to @form_class */
1124 fv->ecmascript_obj = jsform;
1125 return fv->ecmascript_obj;
1127 static JSBool forms_get_property(JSContext *ctx, JSObject *obj, jsval id, jsval *vp);
1129 /* Each @forms_class object must have a @document_class parent. */
1130 const JSClass forms_class = {
1131 "forms",
1132 JSCLASS_HAS_PRIVATE,
1133 JS_PropertyStub, JS_PropertyStub,
1134 forms_get_property, JS_PropertyStub,
1135 JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub
1138 static JSBool forms_item(JSContext *ctx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);
1139 static JSBool forms_namedItem(JSContext *ctx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);
1141 const JSFunctionSpec forms_funcs[] = {
1142 { "item", forms_item, 1 },
1143 { "namedItem", forms_namedItem, 1 },
1144 { NULL }
1147 /* Tinyids of properties. Use negative values to distinguish these from
1148 * array indexes (forms[INT] for INT>=0 is equivalent to forms.item(INT)).
1149 * ECMAScript code should not use these directly as in forms[-1];
1150 * future versions of ELinks may change the numbers. */
1151 enum forms_prop {
1152 JSP_FORMS_LENGTH = -1,
1154 const JSPropertySpec forms_props[] = {
1155 { "length", JSP_FORMS_LENGTH, JSPROP_ENUMERATE | JSPROP_READONLY},
1156 { NULL }
1159 /* @forms_class.getProperty */
1160 static JSBool
1161 forms_get_property(JSContext *ctx, JSObject *obj, jsval id, jsval *vp)
1163 JSObject *parent_doc; /* instance of @document_class */
1164 JSObject *parent_win; /* instance of @window_class */
1165 struct view_state *vs;
1166 struct document_view *doc_view;
1167 struct document *document;
1169 /* This can be called if @obj if not itself an instance of the
1170 * appropriate class but has one in its prototype chain. Fail
1171 * such calls. */
1172 if (!JS_InstanceOf(ctx, obj, (JSClass *) &forms_class, NULL))
1173 return JS_FALSE;
1174 parent_doc = JS_GetParent(ctx, obj);
1175 assert(JS_InstanceOf(ctx, parent_doc, (JSClass *) &document_class, NULL));
1176 if_assert_failed return JS_FALSE;
1177 parent_win = JS_GetParent(ctx, parent_doc);
1178 assert(JS_InstanceOf(ctx, parent_win, (JSClass *) &window_class, NULL));
1179 if_assert_failed return JS_FALSE;
1181 vs = JS_GetPrivate(ctx, parent_win); /* from @window_class */
1182 doc_view = vs->doc_view;
1183 document = doc_view->document;
1185 if (JSVAL_IS_STRING(id)) {
1186 forms_namedItem(ctx, obj, 1, &id, vp);
1187 return JS_TRUE;
1190 if (!JSVAL_IS_INT(id))
1191 return JS_TRUE;
1193 switch (JSVAL_TO_INT(id)) {
1194 case JSP_FORMS_LENGTH:
1195 int_to_jsval(ctx, vp, list_size(&document->forms));
1196 break;
1197 default:
1198 /* Array index. */
1199 forms_item(ctx, obj, 1, &id, vp);
1200 break;
1203 return JS_TRUE;
1206 /* @forms_funcs{"item"} */
1207 static JSBool
1208 forms_item(JSContext *ctx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1210 JSObject *parent_doc; /* instance of @document_class */
1211 JSObject *parent_win; /* instance of @window_class */
1212 struct view_state *vs;
1213 struct form_view *fv;
1214 int counter = -1;
1215 int index;
1217 if (!JS_InstanceOf(ctx, obj, (JSClass *) &forms_class, argv)) return JS_FALSE;
1218 parent_doc = JS_GetParent(ctx, obj);
1219 assert(JS_InstanceOf(ctx, parent_doc, (JSClass *) &document_class, NULL));
1220 if_assert_failed return JS_FALSE;
1221 parent_win = JS_GetParent(ctx, parent_doc);
1222 assert(JS_InstanceOf(ctx, parent_win, (JSClass *) &window_class, NULL));
1223 if_assert_failed return JS_FALSE;
1225 vs = JS_GetPrivate(ctx, parent_win); /* from @window_class */
1227 if (argc != 1)
1228 return JS_TRUE;
1230 JS_ValueToInt32(ctx, argv[0], &index);
1231 undef_to_jsval(ctx, rval);
1233 foreach (fv, vs->forms) {
1234 counter++;
1235 if (counter == index) {
1236 object_to_jsval(ctx, rval, get_form_object(ctx, parent_doc, fv));
1237 break;
1241 return JS_TRUE;
1244 /* @forms_funcs{"namedItem"} */
1245 static JSBool
1246 forms_namedItem(JSContext *ctx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1248 JSObject *parent_doc; /* instance of @document_class */
1249 JSObject *parent_win; /* instance of @window_class */
1250 struct view_state *vs;
1251 struct document_view *doc_view;
1252 struct document *document;
1253 struct form *form;
1254 unsigned char *string;
1256 if (!JS_InstanceOf(ctx, obj, (JSClass *) &forms_class, argv)) return JS_FALSE;
1257 parent_doc = JS_GetParent(ctx, obj);
1258 assert(JS_InstanceOf(ctx, parent_doc, (JSClass *) &document_class, NULL));
1259 if_assert_failed return JS_FALSE;
1260 parent_win = JS_GetParent(ctx, parent_doc);
1261 assert(JS_InstanceOf(ctx, parent_win, (JSClass *) &window_class, NULL));
1262 if_assert_failed return JS_FALSE;
1264 vs = JS_GetPrivate(ctx, parent_win); /* from @window_class */
1265 doc_view = vs->doc_view;
1266 document = doc_view->document;
1268 if (argc != 1)
1269 return JS_TRUE;
1271 undef_to_jsval(ctx, rval);
1273 string = jsval_to_string(ctx, &argv[0]);
1274 if (!*string)
1275 return JS_TRUE;
1277 foreach (form, document->forms) {
1278 if (form->name && !strcasecmp(string, form->name)) {
1279 object_to_jsval(ctx, rval, get_form_object(ctx, parent_doc,
1280 find_form_view(doc_view, form)));
1281 break;
1285 return JS_TRUE;
1289 static JSString *
1290 unicode_to_jsstring(JSContext *ctx, unicode_val_T u)
1292 jschar buf[2];
1294 /* This is supposed to make a string from which
1295 * jsval_to_accesskey() can get the original @u back.
1296 * If @u is a surrogate, then that is not possible, so
1297 * return NULL to indicate an error instead.
1299 * If JS_NewUCStringCopyN hits a null character, it truncates
1300 * the string there and pads it with more nulls. However,
1301 * that is not a problem here, because if there is a null
1302 * character in buf[], then it must be the only character. */
1303 if (u <= 0xFFFF && !is_utf16_surrogate(u)) {
1304 buf[0] = u;
1305 return JS_NewUCStringCopyN(ctx, buf, 1);
1306 } else if (needs_utf16_surrogates(u)) {
1307 buf[0] = get_utf16_high_surrogate(u);
1308 buf[1] = get_utf16_low_surrogate(u);
1309 return JS_NewUCStringCopyN(ctx, buf, 2);
1310 } else {
1311 return NULL;
1315 /* Convert the string *@vp to an access key. Return 0 for no access
1316 * key, UCS_NO_CHAR on error, or the access key otherwise. */
1317 static unicode_val_T
1318 jsval_to_accesskey(JSContext *ctx, jsval *vp)
1320 size_t len;
1321 const jschar *chr;
1323 /* Convert the value in place, to protect the result from GC. */
1324 if (JS_ConvertValue(ctx, *vp, JSTYPE_STRING, vp) == JS_FALSE)
1325 return UCS_NO_CHAR;
1326 len = JS_GetStringLength(JSVAL_TO_STRING(*vp));
1327 chr = JS_GetStringChars(JSVAL_TO_STRING(*vp));
1329 /* This implementation ignores extra characters in the string. */
1330 if (len < 1)
1331 return 0; /* which means no access key */
1332 if (!is_utf16_surrogate(chr[0]))
1333 return chr[0];
1334 if (len >= 2
1335 && is_utf16_high_surrogate(chr[0])
1336 && is_utf16_low_surrogate(chr[1]))
1337 return join_utf16_surrogates(chr[0], chr[1]);
1338 JS_ReportError(ctx, "Invalid UTF-16 sequence");
1339 return UCS_NO_CHAR; /* which the caller will reject */