Handling onsubmit
[elinks.git] / src / ecmascript / spidermonkey / form.c
blobf089c64f93887f693ec0ded70231d9f868068fb9
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/form.h"
26 #include "intl/gettext/libintl.h"
27 #include "main/select.h"
28 #include "osdep/newwin.h"
29 #include "osdep/sysname.h"
30 #include "protocol/http/http.h"
31 #include "protocol/uri.h"
32 #include "session/history.h"
33 #include "session/location.h"
34 #include "session/session.h"
35 #include "session/task.h"
36 #include "terminal/tab.h"
37 #include "terminal/terminal.h"
38 #include "util/conv.h"
39 #include "util/memory.h"
40 #include "util/string.h"
41 #include "viewer/text/draw.h"
42 #include "viewer/text/form.h"
43 #include "viewer/text/link.h"
44 #include "viewer/text/vs.h"
47 /* Accordingly to the JS specs, each input type should own object. That'd be a
48 * huge PITA though, however DOM comes to the rescue and defines just a single
49 * HTMLInputElement. The difference could be spotted only by some clever tricky
50 * JS code, but I hope it doesn't matter anywhere. --pasky */
52 static JSBool input_get_property(JSContext *ctx, JSObject *obj, jsval id, jsval *vp);
53 static JSBool input_set_property(JSContext *ctx, JSObject *obj, jsval id, jsval *vp);
55 static const JSClass input_class = {
56 "input", /* here, we unleash ourselves */
57 JSCLASS_HAS_PRIVATE,
58 JS_PropertyStub, JS_PropertyStub,
59 input_get_property, input_set_property,
60 JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub
63 enum input_prop {
64 JSP_INPUT_ACCESSKEY,
65 JSP_INPUT_ALT,
66 JSP_INPUT_CHECKED,
67 JSP_INPUT_DEFAULT_CHECKED,
68 JSP_INPUT_DEFAULT_VALUE,
69 JSP_INPUT_DISABLED,
70 JSP_INPUT_FORM,
71 JSP_INPUT_MAX_LENGTH,
72 JSP_INPUT_NAME,
73 JSP_INPUT_READONLY,
74 JSP_INPUT_SIZE,
75 JSP_INPUT_SRC,
76 JSP_INPUT_TABINDEX,
77 JSP_INPUT_TYPE,
78 JSP_INPUT_VALUE
81 /* XXX: Some of those are marked readonly just because we can't change them
82 * safely now. Changing default* values would affect all open instances of the
83 * document, leading to a potential security risk. Changing size and type would
84 * require re-rendering the document (TODO), tabindex would require renumbering
85 * of all links and whatnot. --pasky */
86 static const JSPropertySpec input_props[] = {
87 { "accessKey", JSP_INPUT_ACCESSKEY, JSPROP_ENUMERATE },
88 { "alt", JSP_INPUT_ALT, JSPROP_ENUMERATE },
89 { "checked", JSP_INPUT_CHECKED, JSPROP_ENUMERATE },
90 { "defaultChecked",JSP_INPUT_DEFAULT_CHECKED,JSPROP_ENUMERATE },
91 { "defaultValue",JSP_INPUT_DEFAULT_VALUE,JSPROP_ENUMERATE },
92 { "disabled", JSP_INPUT_DISABLED, JSPROP_ENUMERATE },
93 { "form", JSP_INPUT_FORM, JSPROP_ENUMERATE | JSPROP_READONLY },
94 { "maxLength", JSP_INPUT_MAX_LENGTH, JSPROP_ENUMERATE },
95 { "name", JSP_INPUT_NAME, JSPROP_ENUMERATE },
96 { "readonly", JSP_INPUT_READONLY, JSPROP_ENUMERATE },
97 { "size", JSP_INPUT_SIZE, JSPROP_ENUMERATE | JSPROP_READONLY },
98 { "src", JSP_INPUT_SRC, JSPROP_ENUMERATE },
99 { "tabindex", JSP_INPUT_TABINDEX, JSPROP_ENUMERATE | JSPROP_READONLY },
100 { "type", JSP_INPUT_TYPE, JSPROP_ENUMERATE | JSPROP_READONLY },
101 { "value", JSP_INPUT_VALUE, JSPROP_ENUMERATE },
102 { NULL }
105 static JSBool input_blur(JSContext *ctx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);
106 static JSBool input_click(JSContext *ctx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);
107 static JSBool input_focus(JSContext *ctx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);
108 static JSBool input_select(JSContext *ctx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);
110 static const JSFunctionSpec input_funcs[] = {
111 { "blur", input_blur, 0 },
112 { "click", input_click, 0 },
113 { "focus", input_focus, 0 },
114 { "select", input_select, 0 },
115 { NULL }
118 static JSBool
119 input_get_property(JSContext *ctx, JSObject *obj, jsval id, jsval *vp)
121 JSObject *parent_form = JS_GetParent(ctx, obj);
122 JSObject *parent_doc = JS_GetParent(ctx, parent_form);
123 JSObject *parent_win = JS_GetParent(ctx, parent_doc);
124 struct view_state *vs = JS_GetPrivate(ctx, parent_win);
125 struct document_view *doc_view = vs->doc_view;
126 struct document *document = doc_view->document;
127 struct form_state *fs = JS_GetPrivate(ctx, obj);
128 struct form_control *fc = find_form_control(document, fs);
129 int linknum;
130 struct link *link = NULL;
132 assert(fc);
133 assert(fc->form && fs);
135 if (!JSVAL_IS_INT(id))
136 return JS_TRUE;
138 linknum = get_form_control_link(document, fc);
139 /* Hiddens have no link. */
140 if (linknum >= 0) link = &document->links[linknum];
142 undef_to_jsval(ctx, vp);
144 switch (JSVAL_TO_INT(id)) {
145 case JSP_INPUT_ACCESSKEY:
147 struct string keystr;
149 if (!link) break;
151 init_string(&keystr);
152 add_accesskey_to_string(&keystr, link->accesskey);
153 string_to_jsval(ctx, vp, keystr.source);
154 done_string(&keystr);
155 break;
157 case JSP_INPUT_ALT:
158 string_to_jsval(ctx, vp, fc->alt);
159 break;
160 case JSP_INPUT_CHECKED:
161 boolean_to_jsval(ctx, vp, fs->state);
162 break;
163 case JSP_INPUT_DEFAULT_CHECKED:
164 boolean_to_jsval(ctx, vp, fc->default_state);
165 break;
166 case JSP_INPUT_DEFAULT_VALUE:
167 string_to_jsval(ctx, vp, fc->default_value);
168 break;
169 case JSP_INPUT_DISABLED:
170 /* FIXME: <input readonly disabled> --pasky */
171 boolean_to_jsval(ctx, vp, fc->mode == FORM_MODE_DISABLED);
172 break;
173 case JSP_INPUT_FORM:
174 object_to_jsval(ctx, vp, parent_form);
175 break;
176 case JSP_INPUT_MAX_LENGTH:
177 int_to_jsval(ctx, vp, fc->maxlength);
178 break;
179 case JSP_INPUT_NAME:
180 string_to_jsval(ctx, vp, fc->name);
181 break;
182 case JSP_INPUT_READONLY:
183 /* FIXME: <input readonly disabled> --pasky */
184 boolean_to_jsval(ctx, vp, fc->mode == FORM_MODE_READONLY);
185 break;
186 case JSP_INPUT_SIZE:
187 int_to_jsval(ctx, vp, fc->size);
188 break;
189 case JSP_INPUT_SRC:
190 if (link && link->where_img)
191 string_to_jsval(ctx, vp, link->where_img);
192 break;
193 case JSP_INPUT_TABINDEX:
194 if (link)
195 /* FIXME: This is WRONG. --pasky */
196 int_to_jsval(ctx, vp, link->number);
197 break;
198 case JSP_INPUT_TYPE:
200 unsigned char *s = NULL;
202 switch (fc->type) {
203 case FC_TEXT: s = "text"; break;
204 case FC_PASSWORD: s = "password"; break;
205 case FC_FILE: s = "file"; break;
206 case FC_CHECKBOX: s = "checkbox"; break;
207 case FC_RADIO: s = "radio"; break;
208 case FC_SUBMIT: s = "submit"; break;
209 case FC_IMAGE: s = "image"; break;
210 case FC_RESET: s = "reset"; break;
211 case FC_BUTTON: s = "button"; break;
212 case FC_HIDDEN: s = "hidden"; break;
213 default: INTERNAL("input_get_property() upon a non-input item."); break;
215 string_to_jsval(ctx, vp, s);
216 break;
218 case JSP_INPUT_VALUE:
219 string_to_jsval(ctx, vp, fs->value);
220 break;
222 default:
223 INTERNAL("Invalid ID %d in input_get_property().", JSVAL_TO_INT(id));
224 break;
227 return JS_TRUE;
230 static JSBool
231 input_set_property(JSContext *ctx, JSObject *obj, jsval id, jsval *vp)
233 JSObject *parent_form = JS_GetParent(ctx, obj);
234 JSObject *parent_doc = JS_GetParent(ctx, parent_form);
235 JSObject *parent_win = JS_GetParent(ctx, parent_doc);
236 struct view_state *vs = JS_GetPrivate(ctx, parent_win);
237 struct document_view *doc_view = vs->doc_view;
238 struct document *document = doc_view->document;
239 struct form_state *fs = JS_GetPrivate(ctx, obj);
240 struct form_control *fc = find_form_control(document, fs);
241 int linknum;
242 struct link *link = NULL;
244 assert(fc);
245 assert(fc->form && fs);
247 if (!JSVAL_IS_INT(id))
248 return JS_TRUE;
250 linknum = get_form_control_link(document, fc);
251 /* Hiddens have no link. */
252 if (linknum >= 0) link = &document->links[linknum];
254 switch (JSVAL_TO_INT(id)) {
255 case JSP_INPUT_ACCESSKEY:
256 if (link)
257 link->accesskey = accesskey_string_to_unicode(jsval_to_string(ctx, vp));
258 break;
259 case JSP_INPUT_ALT:
260 mem_free_set(&fc->alt, stracpy(jsval_to_string(ctx, vp)));
261 break;
262 case JSP_INPUT_CHECKED:
263 if (fc->type != FC_CHECKBOX && fc->type != FC_RADIO)
264 break;
265 fs->state = jsval_to_boolean(ctx, vp);
266 break;
267 case JSP_INPUT_DISABLED:
268 /* FIXME: <input readonly disabled> --pasky */
269 fc->mode = (jsval_to_boolean(ctx, vp) ? FORM_MODE_DISABLED
270 : fc->mode == FORM_MODE_READONLY ? FORM_MODE_READONLY
271 : FORM_MODE_NORMAL);
272 break;
273 case JSP_INPUT_MAX_LENGTH:
274 fc->maxlength = atol(jsval_to_string(ctx, vp));
275 break;
276 case JSP_INPUT_NAME:
277 mem_free_set(&fc->name, stracpy(jsval_to_string(ctx, vp)));
278 break;
279 case JSP_INPUT_READONLY:
280 /* FIXME: <input readonly disabled> --pasky */
281 fc->mode = (jsval_to_boolean(ctx, vp) ? FORM_MODE_READONLY
282 : fc->mode == FORM_MODE_DISABLED ? FORM_MODE_DISABLED
283 : FORM_MODE_NORMAL);
284 break;
285 case JSP_INPUT_SRC:
286 if (link) {
287 mem_free_set(&link->where_img, stracpy(jsval_to_string(ctx, vp)));
289 break;
290 case JSP_INPUT_VALUE:
291 if (fc->type == FC_FILE)
292 break; /* A huge security risk otherwise. */
293 mem_free_set(&fs->value, stracpy(jsval_to_string(ctx, vp)));
294 if (fc->type == FC_TEXT || fc->type == FC_PASSWORD)
295 fs->state = strlen(fs->value);
296 break;
298 default:
299 INTERNAL("Invalid ID %d in input_set_property().", JSVAL_TO_INT(id));
300 return JS_TRUE;
303 return JS_TRUE;
306 static JSBool
307 input_blur(JSContext *ctx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
309 /* We are a text-mode browser and there *always* has to be something
310 * selected. So we do nothing for now. (That was easy.) */
311 return JS_TRUE;
314 static JSBool
315 input_click(JSContext *ctx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
317 JSObject *parent_form = JS_GetParent(ctx, obj);
318 JSObject *parent_doc = JS_GetParent(ctx, parent_form);
319 JSObject *parent_win = JS_GetParent(ctx, parent_doc);
320 struct view_state *vs = JS_GetPrivate(ctx, parent_win);
321 struct document_view *doc_view = vs->doc_view;
322 struct document *document = doc_view->document;
323 struct session *ses = doc_view->session;
324 struct form_state *fs = JS_GetPrivate(ctx, obj);
325 struct form_control *fc;
326 int linknum;
328 assert(fs);
329 fc = find_form_control(document, fs);
330 assert(fc);
332 linknum = get_form_control_link(document, fc);
333 /* Hiddens have no link. */
334 if (linknum < 0)
335 return JS_TRUE;
337 /* Restore old current_link afterwards? */
338 jump_to_link_number(ses, doc_view, linknum);
339 if (enter(ses, doc_view, 0) == FRAME_EVENT_REFRESH)
340 refresh_view(ses, doc_view, 0);
341 else
342 print_screen_status(ses);
344 boolean_to_jsval(ctx, rval, 0);
345 return JS_TRUE;
348 static JSBool
349 input_focus(JSContext *ctx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
351 JSObject *parent_form = JS_GetParent(ctx, obj);
352 JSObject *parent_doc = JS_GetParent(ctx, parent_form);
353 JSObject *parent_win = JS_GetParent(ctx, parent_doc);
354 struct view_state *vs = JS_GetPrivate(ctx, parent_win);
355 struct document_view *doc_view = vs->doc_view;
356 struct document *document = doc_view->document;
357 struct session *ses = doc_view->session;
358 struct form_state *fs = JS_GetPrivate(ctx, obj);
359 struct form_control *fc;
360 int linknum;
362 assert(fs);
363 fc = find_form_control(document, fs);
364 assert(fc);
366 linknum = get_form_control_link(document, fc);
367 /* Hiddens have no link. */
368 if (linknum < 0)
369 return JS_TRUE;
371 jump_to_link_number(ses, doc_view, linknum);
373 boolean_to_jsval(ctx, rval, 0);
374 return JS_TRUE;
377 static JSBool
378 input_select(JSContext *ctx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
380 /* We support no text selecting yet. So we do nothing for now.
381 * (That was easy, too.) */
382 return JS_TRUE;
385 static JSObject *
386 get_input_object(JSContext *ctx, JSObject *jsform, struct form_state *fs)
388 if (!fs->ecmascript_obj) {
389 /* jsform ('form') is input's parent */
390 /* FIXME: That is NOT correct since the real containing element
391 * should be its parent, but gimme DOM first. --pasky */
392 JSObject *jsinput = JS_NewObject(ctx, (JSClass *) &input_class, NULL, jsform);
394 JS_DefineProperties(ctx, jsinput, (JSPropertySpec *) input_props);
395 JS_DefineFunctions(ctx, jsinput, (JSFunctionSpec *) input_funcs);
396 JS_SetPrivate(ctx, jsinput, fs);
397 fs->ecmascript_obj = jsinput;
399 return fs->ecmascript_obj;
403 static JSObject *
404 get_form_control_object(JSContext *ctx, JSObject *jsform, enum form_type type, struct form_state *fs)
406 switch (type) {
407 case FC_TEXT:
408 case FC_PASSWORD:
409 case FC_FILE:
410 case FC_CHECKBOX:
411 case FC_RADIO:
412 case FC_SUBMIT:
413 case FC_IMAGE:
414 case FC_RESET:
415 case FC_BUTTON:
416 case FC_HIDDEN:
417 return get_input_object(ctx, jsform, fs);
419 case FC_TEXTAREA:
420 case FC_SELECT:
421 /* TODO */
422 return NULL;
424 default:
425 INTERNAL("Weird fc->type %d", type);
426 return NULL;
432 static JSBool form_elements_get_property(JSContext *ctx, JSObject *obj, jsval id, jsval *vp);
434 static const JSClass form_elements_class = {
435 "elements",
436 JSCLASS_HAS_PRIVATE,
437 JS_PropertyStub, JS_PropertyStub,
438 form_elements_get_property, JS_PropertyStub,
439 JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub
442 static JSBool form_elements_item(JSContext *ctx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);
443 static JSBool form_elements_namedItem(JSContext *ctx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);
445 static const JSFunctionSpec form_elements_funcs[] = {
446 { "item", form_elements_item, 1 },
447 { "namedItem", form_elements_namedItem, 1 },
448 { NULL }
451 /* INTs from 0 up are equivalent to item(INT), so we have to stuff length out
452 * of the way. */
453 enum form_elements_prop { JSP_FORM_ELEMENTS_LENGTH = -1 };
454 static const JSPropertySpec form_elements_props[] = {
455 { "length", JSP_FORM_ELEMENTS_LENGTH, JSPROP_ENUMERATE | JSPROP_READONLY},
456 { NULL }
459 static JSBool
460 form_elements_get_property(JSContext *ctx, JSObject *obj, jsval id, jsval *vp)
462 JSObject *parent_form = JS_GetParent(ctx, obj);
463 JSObject *parent_doc = JS_GetParent(ctx, parent_form);
464 JSObject *parent_win = JS_GetParent(ctx, parent_doc);
465 struct view_state *vs = JS_GetPrivate(ctx, parent_win);
466 struct document_view *doc_view = vs->doc_view;
467 struct document *document = doc_view->document;
468 struct form_view *form_view = JS_GetPrivate(ctx, parent_form);
469 struct form *form = find_form_by_form_view(document, form_view);
471 if (JSVAL_IS_STRING(id)) {
472 form_elements_namedItem(ctx, obj, 1, &id, vp);
473 return JS_TRUE;
476 if (!JSVAL_IS_INT(id))
477 return JS_TRUE;
479 undef_to_jsval(ctx, vp);
481 switch (JSVAL_TO_INT(id)) {
482 case JSP_FORM_ELEMENTS_LENGTH:
483 int_to_jsval(ctx, vp, list_size(&form->items));
484 break;
485 default:
486 /* Array index. */
487 form_elements_item(ctx, obj, 1, &id, vp);
488 break;
491 return JS_TRUE;
494 static JSBool
495 form_elements_item(JSContext *ctx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
497 JSObject *parent_form = JS_GetParent(ctx, obj);
498 JSObject *parent_doc = JS_GetParent(ctx, parent_form);
499 JSObject *parent_win = JS_GetParent(ctx, parent_doc);
500 struct view_state *vs = JS_GetPrivate(ctx, parent_win);
501 struct document_view *doc_view = vs->doc_view;
502 struct document *document = doc_view->document;
503 struct form_view *form_view = JS_GetPrivate(ctx, parent_form);
504 struct form *form = find_form_by_form_view(document, form_view);
505 struct form_control *fc;
506 int counter = -1;
507 int index;
509 if (argc != 1)
510 return JS_TRUE;
512 index = atol(jsval_to_string(ctx, &argv[0]));
514 undef_to_jsval(ctx, rval);
516 foreach (fc, form->items) {
517 counter++;
518 if (counter == index) {
519 JSObject *fcobj = get_form_control_object(ctx, parent_form, fc->type, find_form_state(doc_view, fc));
521 if (fcobj) {
522 object_to_jsval(ctx, rval, fcobj);
524 break;
528 return JS_TRUE;
531 static JSBool
532 form_elements_namedItem(JSContext *ctx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
534 JSObject *parent_form = JS_GetParent(ctx, obj);
535 JSObject *parent_doc = JS_GetParent(ctx, parent_form);
536 JSObject *parent_win = JS_GetParent(ctx, parent_doc);
537 struct view_state *vs = JS_GetPrivate(ctx, parent_win);
538 struct document_view *doc_view = vs->doc_view;
539 struct document *document = doc_view->document;
540 struct form_view *form_view = JS_GetPrivate(ctx, parent_form);
541 struct form *form = find_form_by_form_view(document, form_view);
542 struct form_control *fc;
543 unsigned char *string;
545 if (argc != 1)
546 return JS_TRUE;
548 string = jsval_to_string(ctx, &argv[0]);
549 if (!*string)
550 return JS_TRUE;
552 undef_to_jsval(ctx, rval);
554 foreach (fc, form->items) {
555 if (fc->name && !strcasecmp(string, fc->name)) {
556 JSObject *fcobj = get_form_control_object(ctx, parent_form, fc->type, find_form_state(doc_view, fc));
558 if (fcobj) {
559 object_to_jsval(ctx, rval, fcobj);
561 break;
565 return JS_TRUE;
570 static JSBool form_get_property(JSContext *ctx, JSObject *obj, jsval id, jsval *vp);
571 static JSBool form_set_property(JSContext *ctx, JSObject *obj, jsval id, jsval *vp);
573 static const JSClass form_class = {
574 "form",
575 JSCLASS_HAS_PRIVATE,
576 JS_PropertyStub, JS_PropertyStub,
577 form_get_property, form_set_property,
578 JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub
581 enum form_prop {
582 JSP_FORM_ACTION,
583 JSP_FORM_ELEMENTS,
584 JSP_FORM_ENCODING,
585 JSP_FORM_LENGTH,
586 JSP_FORM_METHOD,
587 JSP_FORM_NAME,
588 JSP_FORM_TARGET
591 static const JSPropertySpec form_props[] = {
592 { "action", JSP_FORM_ACTION, JSPROP_ENUMERATE },
593 { "elements", JSP_FORM_ELEMENTS, JSPROP_ENUMERATE },
594 { "encoding", JSP_FORM_ENCODING, JSPROP_ENUMERATE },
595 { "length", JSP_FORM_LENGTH, JSPROP_ENUMERATE | JSPROP_READONLY },
596 { "method", JSP_FORM_METHOD, JSPROP_ENUMERATE },
597 { "name", JSP_FORM_NAME, JSPROP_ENUMERATE },
598 { "target", JSP_FORM_TARGET, JSPROP_ENUMERATE },
599 { NULL }
602 static JSBool form_reset(JSContext *ctx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);
603 static JSBool form_submit(JSContext *ctx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);
605 static const JSFunctionSpec form_funcs[] = {
606 { "reset", form_reset, 0 },
607 { "submit", form_submit, 0 },
608 { NULL }
611 static JSBool
612 form_get_property(JSContext *ctx, JSObject *obj, jsval id, jsval *vp)
614 /* DBG("doc %p %s\n", parent_doc, JS_GetStringBytes(JS_ValueToString(ctx, OBJECT_TO_JSVAL(parent_doc)))); */
615 JSObject *parent_doc = JS_GetParent(ctx, obj);
616 JSObject *parent_win = JS_GetParent(ctx, parent_doc);
617 struct view_state *vs = JS_GetPrivate(ctx, parent_win);
618 struct document_view *doc_view = vs->doc_view;
619 struct form_view *fv = JS_GetPrivate(ctx, obj);
620 struct form *form = find_form_by_form_view(doc_view->document, fv);
622 assert(form);
624 if (JSVAL_IS_STRING(id)) {
625 struct form_control *fc;
626 unsigned char *string;
628 string = jsval_to_string(ctx, &id);
629 foreach (fc, form->items) {
630 JSObject *fcobj = NULL;
632 if (!fc->name || strcasecmp(string, fc->name))
633 continue;
635 fcobj = get_form_control_object(ctx, obj, fc->type, find_form_state(doc_view, fc));
636 if (fcobj) {
637 object_to_jsval(ctx, vp, fcobj);
638 } else {
639 undef_to_jsval(ctx, vp);
641 break;
643 return JS_TRUE;
646 if (!JSVAL_IS_INT(id))
647 return JS_TRUE;
649 undef_to_jsval(ctx, vp);
651 switch (JSVAL_TO_INT(id)) {
652 case JSP_FORM_ACTION:
653 string_to_jsval(ctx, vp, form->action);
654 break;
656 case JSP_FORM_ELEMENTS:
658 /* jsform ('form') is form_elements' parent; who knows is that's correct */
659 JSObject *jsform_elems = JS_NewObject(ctx, (JSClass *) &form_elements_class, NULL, obj);
661 JS_DefineProperties(ctx, jsform_elems, (JSPropertySpec *) form_elements_props);
662 JS_DefineFunctions(ctx, jsform_elems, (JSFunctionSpec *) form_elements_funcs);
663 object_to_jsval(ctx, vp, jsform_elems);
664 /* SM will cache this property value for us so we create this
665 * just once per form. */
667 break;
669 case JSP_FORM_ENCODING:
670 switch (form->method) {
671 case FORM_METHOD_GET:
672 case FORM_METHOD_POST:
673 string_to_jsval(ctx, vp, "application/x-www-form-urlencoded");
674 break;
675 case FORM_METHOD_POST_MP:
676 string_to_jsval(ctx, vp, "multipart/form-data");
677 break;
678 case FORM_METHOD_POST_TEXT_PLAIN:
679 string_to_jsval(ctx, vp, "text/plain");
680 break;
682 break;
684 case JSP_FORM_LENGTH:
685 int_to_jsval(ctx, vp, list_size(&form->items));
686 break;
688 case JSP_FORM_METHOD:
689 switch (form->method) {
690 case FORM_METHOD_GET:
691 string_to_jsval(ctx, vp, "GET");
692 break;
694 case FORM_METHOD_POST:
695 case FORM_METHOD_POST_MP:
696 case FORM_METHOD_POST_TEXT_PLAIN:
697 string_to_jsval(ctx, vp, "POST");
698 break;
700 break;
702 case JSP_FORM_NAME:
703 string_to_jsval(ctx, vp, form->name);
704 break;
706 case JSP_FORM_TARGET:
707 string_to_jsval(ctx, vp, form->target);
708 break;
710 default:
711 INTERNAL("Invalid ID %d in form_get_property().", JSVAL_TO_INT(id));
712 break;
715 return JS_TRUE;
718 static JSBool
719 form_set_property(JSContext *ctx, JSObject *obj, jsval id, jsval *vp)
721 JSObject *parent_doc = JS_GetParent(ctx, obj);
722 JSObject *parent_win = JS_GetParent(ctx, parent_doc);
723 struct view_state *vs = JS_GetPrivate(ctx, parent_win);
724 struct document_view *doc_view = vs->doc_view;
725 struct form_view *fv = JS_GetPrivate(ctx, obj);
726 struct form *form = find_form_by_form_view(doc_view->document, fv);
727 unsigned char *string;
729 assert(form);
731 if (!JSVAL_IS_INT(id))
732 return JS_TRUE;
734 switch (JSVAL_TO_INT(id)) {
735 case JSP_FORM_ACTION:
736 mem_free_set(&form->action, stracpy(jsval_to_string(ctx, vp)));
737 break;
739 case JSP_FORM_ENCODING:
740 string = jsval_to_string(ctx, vp);
741 if (!strcasecmp(string, "application/x-www-form-urlencoded")) {
742 form->method = form->method == FORM_METHOD_GET ? FORM_METHOD_GET
743 : FORM_METHOD_POST;
744 } else if (!strcasecmp(string, "multipart/form-data")) {
745 form->method = FORM_METHOD_POST_MP;
746 } else if (!strcasecmp(string, "text/plain")) {
747 form->method = FORM_METHOD_POST_TEXT_PLAIN;
749 break;
751 case JSP_FORM_METHOD:
752 string = jsval_to_string(ctx, vp);
753 if (!strcasecmp(string, "GET")) {
754 form->method = FORM_METHOD_GET;
755 } else if (!strcasecmp(string, "POST")) {
756 form->method = FORM_METHOD_POST;
758 break;
760 case JSP_FORM_NAME:
761 mem_free_set(&form->name, stracpy(jsval_to_string(ctx, vp)));
762 break;
764 case JSP_FORM_TARGET:
765 mem_free_set(&form->target, stracpy(jsval_to_string(ctx, vp)));
766 break;
768 default:
769 INTERNAL("Invalid ID %d in form_set_property().", JSVAL_TO_INT(id));
770 break;
773 return JS_TRUE;
776 static JSBool
777 form_reset(JSContext *ctx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
779 JSObject *parent_doc = JS_GetParent(ctx, obj);
780 JSObject *parent_win = JS_GetParent(ctx, parent_doc);
781 struct view_state *vs = JS_GetPrivate(ctx, parent_win);
782 struct document_view *doc_view = vs->doc_view;
783 struct form_view *fv = JS_GetPrivate(ctx, obj);
784 struct form *form = find_form_by_form_view(doc_view->document, fv);
786 assert(form);
788 do_reset_form(doc_view, form);
789 draw_forms(doc_view->session->tab->term, doc_view);
791 boolean_to_jsval(ctx, rval, 0);
793 return JS_TRUE;
796 static JSBool
797 form_submit(JSContext *ctx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
799 JSObject *parent_doc = JS_GetParent(ctx, obj);
800 JSObject *parent_win = JS_GetParent(ctx, parent_doc);
801 struct view_state *vs = JS_GetPrivate(ctx, parent_win);
802 struct document_view *doc_view = vs->doc_view;
803 struct session *ses = doc_view->session;
804 struct form_view *fv = JS_GetPrivate(ctx, obj);
805 struct form *form = find_form_by_form_view(doc_view->document, fv);
807 assert(form);
808 submit_given_form(ses, doc_view, form, 0);
810 boolean_to_jsval(ctx, rval, 0);
812 return JS_TRUE;
815 JSObject *
816 get_form_object(JSContext *ctx, JSObject *jsdoc, struct form_view *fv)
818 if (!fv->ecmascript_obj) {
819 /* jsdoc ('document') is fv's parent */
820 /* FIXME: That is NOT correct since the real containing element
821 * should be its parent, but gimme DOM first. --pasky */
822 JSObject *jsform = JS_NewObject(ctx, (JSClass *) &form_class, NULL, jsdoc);
824 JS_DefineProperties(ctx, jsform, (JSPropertySpec *) form_props);
825 JS_DefineFunctions(ctx, jsform, (JSFunctionSpec *) form_funcs);
826 JS_SetPrivate(ctx, jsform, fv);
827 fv->ecmascript_obj = jsform;
829 return fv->ecmascript_obj;
833 static JSBool forms_get_property(JSContext *ctx, JSObject *obj, jsval id, jsval *vp);
835 const JSClass forms_class = {
836 "forms",
837 JSCLASS_HAS_PRIVATE,
838 JS_PropertyStub, JS_PropertyStub,
839 forms_get_property, JS_PropertyStub,
840 JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub
843 static JSBool forms_item(JSContext *ctx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);
844 static JSBool forms_namedItem(JSContext *ctx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);
846 const JSFunctionSpec forms_funcs[] = {
847 { "item", forms_item, 1 },
848 { "namedItem", forms_namedItem, 1 },
849 { NULL }
852 /* INTs from 0 up are equivalent to item(INT), so we have to stuff length out
853 * of the way. */
854 enum forms_prop { JSP_FORMS_LENGTH = -1 };
855 const JSPropertySpec forms_props[] = {
856 { "length", JSP_FORMS_LENGTH, JSPROP_ENUMERATE | JSPROP_READONLY},
857 { NULL }
860 static JSBool
861 forms_get_property(JSContext *ctx, JSObject *obj, jsval id, jsval *vp)
863 JSObject *parent_doc = JS_GetParent(ctx, obj);
864 JSObject *parent_win = JS_GetParent(ctx, parent_doc);
865 struct view_state *vs = JS_GetPrivate(ctx, parent_win);
866 struct document_view *doc_view = vs->doc_view;
867 struct document *document = doc_view->document;
869 if (JSVAL_IS_STRING(id)) {
870 forms_namedItem(ctx, obj, 1, &id, vp);
871 return JS_TRUE;
874 if (!JSVAL_IS_INT(id))
875 return JS_TRUE;
877 switch (JSVAL_TO_INT(id)) {
878 case JSP_FORMS_LENGTH:
879 int_to_jsval(ctx, vp, list_size(&document->forms));
880 break;
881 default:
882 /* Array index. */
883 forms_item(ctx, obj, 1, &id, vp);
884 break;
887 return JS_TRUE;
890 static JSBool
891 forms_item(JSContext *ctx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
893 JSObject *parent_doc = JS_GetParent(ctx, obj);
894 JSObject *parent_win = JS_GetParent(ctx, parent_doc);
895 struct view_state *vs = JS_GetPrivate(ctx, parent_win);
896 struct form_view *fv;
897 int counter = -1;
898 int index;
900 if (argc != 1)
901 return JS_TRUE;
903 index = atol(jsval_to_string(ctx, &argv[0]));
905 undef_to_jsval(ctx, rval);
907 foreach (fv, vs->forms) {
908 counter++;
909 if (counter == index) {
910 object_to_jsval(ctx, rval, get_form_object(ctx, parent_doc, fv));
911 break;
915 return JS_TRUE;
918 static JSBool
919 forms_namedItem(JSContext *ctx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
921 JSObject *parent_doc = JS_GetParent(ctx, obj);
922 JSObject *parent_win = JS_GetParent(ctx, parent_doc);
923 struct view_state *vs = JS_GetPrivate(ctx, parent_win);
924 struct document_view *doc_view = vs->doc_view;
925 struct document *document = doc_view->document;
926 struct form *form;
927 unsigned char *string;
929 if (argc != 1)
930 return JS_TRUE;
932 undef_to_jsval(ctx, rval);
934 string = jsval_to_string(ctx, &argv[0]);
935 if (!*string)
936 return JS_TRUE;
938 foreach (form, document->forms) {
939 if (form->name && !strcasecmp(string, form->name)) {
940 object_to_jsval(ctx, rval, get_form_object(ctx, parent_doc,
941 find_form_view(doc_view, form)));
942 break;
946 return JS_TRUE;