1 /* The SpiderMonkey window object implementation. */
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.h"
26 #include "ecmascript/spidermonkey/document.h"
27 #include "ecmascript/spidermonkey/form.h"
28 #include "ecmascript/spidermonkey/window.h"
29 #include "intl/gettext/libintl.h"
30 #include "main/select.h"
31 #include "osdep/newwin.h"
32 #include "osdep/sysname.h"
33 #include "protocol/http/http.h"
34 #include "protocol/uri.h"
35 #include "session/history.h"
36 #include "session/location.h"
37 #include "session/session.h"
38 #include "session/task.h"
39 #include "terminal/tab.h"
40 #include "terminal/terminal.h"
41 #include "util/conv.h"
42 #include "util/memory.h"
43 #include "util/string.h"
44 #include "viewer/text/draw.h"
45 #include "viewer/text/form.h"
46 #include "viewer/text/link.h"
47 #include "viewer/text/vs.h"
50 static const JSClass form_class
; /* defined below */
53 /* Accordingly to the JS specs, each input type should own object. That'd be a
54 * huge PITA though, however DOM comes to the rescue and defines just a single
55 * HTMLInputElement. The difference could be spotted only by some clever tricky
56 * JS code, but I hope it doesn't matter anywhere. --pasky */
58 static JSBool
input_get_property(JSContext
*ctx
, JSObject
*obj
, jsid id
, jsval
*vp
);
59 static JSBool
input_set_property(JSContext
*ctx
, JSObject
*obj
, jsid id
, JSBool strict
, jsval
*vp
);
60 static void input_finalize(JSContext
*ctx
, JSObject
*obj
);
62 /* Each @input_class object must have a @form_class parent. */
63 static const JSClass input_class
= {
64 "input", /* here, we unleash ourselves */
65 JSCLASS_HAS_PRIVATE
, /* struct form_state *, or NULL if detached */
66 JS_PropertyStub
, JS_PropertyStub
,
67 input_get_property
, input_set_property
,
68 JS_EnumerateStub
, JS_ResolveStub
, JS_ConvertStub
, input_finalize
71 /* Tinyids of properties. Use negative values to distinguish these
72 * from array indexes (even though this object has no array elements).
73 * ECMAScript code should not use these directly as in input[-1];
74 * future versions of ELinks may change the numbers. */
76 JSP_INPUT_ACCESSKEY
= -1,
78 JSP_INPUT_CHECKED
= -3,
79 JSP_INPUT_DEFAULT_CHECKED
= -4,
80 JSP_INPUT_DEFAULT_VALUE
= -5,
81 JSP_INPUT_DISABLED
= -6,
83 JSP_INPUT_MAX_LENGTH
= -8,
85 JSP_INPUT_READONLY
= -10,
86 JSP_INPUT_SELECTED_INDEX
= -11,
89 JSP_INPUT_TABINDEX
= -14,
91 JSP_INPUT_VALUE
= -16,
94 /* XXX: Some of those are marked readonly just because we can't change them
95 * safely now. Changing default* values would affect all open instances of the
96 * document, leading to a potential security risk. Changing size and type would
97 * require re-rendering the document (TODO), tabindex would require renumbering
98 * of all links and whatnot. --pasky */
99 static const JSPropertySpec input_props
[] = {
100 { "accessKey", JSP_INPUT_ACCESSKEY
, JSPROP_ENUMERATE
},
101 { "alt", JSP_INPUT_ALT
, JSPROP_ENUMERATE
},
102 { "checked", JSP_INPUT_CHECKED
, JSPROP_ENUMERATE
},
103 { "defaultChecked",JSP_INPUT_DEFAULT_CHECKED
,JSPROP_ENUMERATE
},
104 { "defaultValue",JSP_INPUT_DEFAULT_VALUE
,JSPROP_ENUMERATE
},
105 { "disabled", JSP_INPUT_DISABLED
, JSPROP_ENUMERATE
},
106 { "form", JSP_INPUT_FORM
, JSPROP_ENUMERATE
| JSPROP_READONLY
},
107 { "maxLength", JSP_INPUT_MAX_LENGTH
, JSPROP_ENUMERATE
},
108 { "name", JSP_INPUT_NAME
, JSPROP_ENUMERATE
},
109 { "readonly", JSP_INPUT_READONLY
, JSPROP_ENUMERATE
},
110 { "selectedIndex",JSP_INPUT_SELECTED_INDEX
,JSPROP_ENUMERATE
},
111 { "size", JSP_INPUT_SIZE
, JSPROP_ENUMERATE
| JSPROP_READONLY
},
112 { "src", JSP_INPUT_SRC
, JSPROP_ENUMERATE
},
113 { "tabindex", JSP_INPUT_TABINDEX
, JSPROP_ENUMERATE
| JSPROP_READONLY
},
114 { "type", JSP_INPUT_TYPE
, JSPROP_ENUMERATE
| JSPROP_READONLY
},
115 { "value", JSP_INPUT_VALUE
, JSPROP_ENUMERATE
},
119 static JSBool
input_blur(JSContext
*ctx
, uintN argc
, jsval
*rval
);
120 static JSBool
input_click(JSContext
*ctx
, uintN argc
, jsval
*rval
);
121 static JSBool
input_focus(JSContext
*ctx
, uintN argc
, jsval
*rval
);
122 static JSBool
input_select(JSContext
*ctx
, uintN argc
, jsval
*rval
);
124 static const spidermonkeyFunctionSpec input_funcs
[] = {
125 { "blur", input_blur
, 0 },
126 { "click", input_click
, 0 },
127 { "focus", input_focus
, 0 },
128 { "select", input_select
, 0 },
132 static JSString
*unicode_to_jsstring(JSContext
*ctx
, unicode_val_T u
);
133 static unicode_val_T
jsval_to_accesskey(JSContext
*ctx
, jsval
*vp
);
136 static struct form_state
*
137 input_get_form_state(JSContext
*ctx
, JSObject
*jsinput
)
139 struct form_state
*fs
= JS_GetInstancePrivate(ctx
, jsinput
,
140 (JSClass
*) &input_class
,
143 if (!fs
) return NULL
; /* detached */
145 assert(fs
->ecmascript_obj
== jsinput
);
146 if_assert_failed
return NULL
;
151 /* @input_class.getProperty */
153 input_get_property(JSContext
*ctx
, JSObject
*obj
, jsid id
, jsval
*vp
)
155 JSObject
*parent_form
; /* instance of @form_class */
156 JSObject
*parent_doc
; /* instance of @document_class */
157 JSObject
*parent_win
; /* instance of @window_class */
158 struct view_state
*vs
;
159 struct document_view
*doc_view
;
160 struct document
*document
;
161 struct form_state
*fs
;
162 struct form_control
*fc
;
164 struct link
*link
= NULL
;
166 /* This can be called if @obj if not itself an instance of the
167 * appropriate class but has one in its prototype chain. Fail
169 if (!JS_InstanceOf(ctx
, obj
, (JSClass
*) &input_class
, NULL
))
171 parent_form
= JS_GetParent(ctx
, obj
);
172 assert(JS_InstanceOf(ctx
, parent_form
, (JSClass
*) &form_class
, NULL
));
173 if_assert_failed
return JS_FALSE
;
174 parent_doc
= JS_GetParent(ctx
, parent_form
);
175 assert(JS_InstanceOf(ctx
, parent_doc
, (JSClass
*) &document_class
, NULL
));
176 if_assert_failed
return JS_FALSE
;
177 parent_win
= JS_GetParent(ctx
, parent_doc
);
178 assert(JS_InstanceOf(ctx
, parent_win
, (JSClass
*) &window_class
, NULL
));
179 if_assert_failed
return JS_FALSE
;
181 vs
= JS_GetInstancePrivate(ctx
, parent_win
,
182 (JSClass
*) &window_class
, NULL
);
183 doc_view
= vs
->doc_view
;
184 document
= doc_view
->document
;
185 fs
= input_get_form_state(ctx
, obj
);
186 if (!fs
) return JS_FALSE
; /* detached */
187 fc
= find_form_control(document
, fs
);
190 assert(fc
->form
&& fs
);
192 if (!JSID_IS_INT(id
))
195 linknum
= get_form_control_link(document
, fc
);
196 /* Hiddens have no link. */
197 if (linknum
>= 0) link
= &document
->links
[linknum
];
199 undef_to_jsval(ctx
, vp
);
201 switch (JSID_TO_INT(id
)) {
202 case JSP_INPUT_ACCESSKEY
:
208 if (!link
->accesskey
) {
209 *vp
= JS_GetEmptyStringValue(ctx
);
211 keystr
= unicode_to_jsstring(ctx
, link
->accesskey
);
213 *vp
= STRING_TO_JSVAL(keystr
);
220 string_to_jsval(ctx
, vp
, fc
->alt
);
222 case JSP_INPUT_CHECKED
:
223 boolean_to_jsval(ctx
, vp
, fs
->state
);
225 case JSP_INPUT_DEFAULT_CHECKED
:
226 boolean_to_jsval(ctx
, vp
, fc
->default_state
);
228 case JSP_INPUT_DEFAULT_VALUE
:
229 /* FIXME (bug 805): convert from the charset of the document */
230 string_to_jsval(ctx
, vp
, fc
->default_value
);
232 case JSP_INPUT_DISABLED
:
233 /* FIXME: <input readonly disabled> --pasky */
234 boolean_to_jsval(ctx
, vp
, fc
->mode
== FORM_MODE_DISABLED
);
237 object_to_jsval(ctx
, vp
, parent_form
);
239 case JSP_INPUT_MAX_LENGTH
:
240 int_to_jsval(ctx
, vp
, fc
->maxlength
);
243 string_to_jsval(ctx
, vp
, fc
->name
);
245 case JSP_INPUT_READONLY
:
246 /* FIXME: <input readonly disabled> --pasky */
247 boolean_to_jsval(ctx
, vp
, fc
->mode
== FORM_MODE_READONLY
);
250 int_to_jsval(ctx
, vp
, fc
->size
);
253 if (link
&& link
->where_img
)
254 string_to_jsval(ctx
, vp
, link
->where_img
);
256 case JSP_INPUT_TABINDEX
:
258 /* FIXME: This is WRONG. --pasky */
259 int_to_jsval(ctx
, vp
, link
->number
);
263 unsigned char *s
= NULL
;
266 case FC_TEXT
: s
= "text"; break;
267 case FC_PASSWORD
: s
= "password"; break;
268 case FC_FILE
: s
= "file"; break;
269 case FC_CHECKBOX
: s
= "checkbox"; break;
270 case FC_RADIO
: s
= "radio"; break;
271 case FC_SUBMIT
: s
= "submit"; break;
272 case FC_IMAGE
: s
= "image"; break;
273 case FC_RESET
: s
= "reset"; break;
274 case FC_BUTTON
: s
= "button"; break;
275 case FC_HIDDEN
: s
= "hidden"; break;
276 case FC_SELECT
: s
= "select"; break;
277 default: INTERNAL("input_get_property() upon a non-input item."); break;
279 string_to_jsval(ctx
, vp
, s
);
282 case JSP_INPUT_VALUE
:
283 string_to_jsval(ctx
, vp
, fs
->value
);
286 case JSP_INPUT_SELECTED_INDEX
:
287 if (fc
->type
== FC_SELECT
) int_to_jsval(ctx
, vp
, fs
->state
);
290 /* Unrecognized integer property ID; someone is using
291 * the object as an array. SMJS builtin classes (e.g.
292 * js_RegExpClass) just return JS_TRUE in this case
293 * and leave *@vp unchanged. Do the same here.
294 * (Actually not quite the same, as we already used
295 * @undef_to_jsval.) */
302 /* @input_class.setProperty */
304 input_set_property(JSContext
*ctx
, JSObject
*obj
, jsid id
, JSBool strict
, jsval
*vp
)
306 JSObject
*parent_form
; /* instance of @form_class */
307 JSObject
*parent_doc
; /* instance of @document_class */
308 JSObject
*parent_win
; /* instance of @window_class */
309 struct view_state
*vs
;
310 struct document_view
*doc_view
;
311 struct document
*document
;
312 struct form_state
*fs
;
313 struct form_control
*fc
;
315 struct link
*link
= NULL
;
316 unicode_val_T accesskey
;
318 /* This can be called if @obj if not itself an instance of the
319 * appropriate class but has one in its prototype chain. Fail
321 if (!JS_InstanceOf(ctx
, obj
, (JSClass
*) &input_class
, NULL
))
323 parent_form
= JS_GetParent(ctx
, obj
);
324 assert(JS_InstanceOf(ctx
, parent_form
, (JSClass
*) &form_class
, NULL
));
325 if_assert_failed
return JS_FALSE
;
326 parent_doc
= JS_GetParent(ctx
, parent_form
);
327 assert(JS_InstanceOf(ctx
, parent_doc
, (JSClass
*) &document_class
, NULL
));
328 if_assert_failed
return JS_FALSE
;
329 parent_win
= JS_GetParent(ctx
, parent_doc
);
330 assert(JS_InstanceOf(ctx
, parent_win
, (JSClass
*) &window_class
, NULL
));
331 if_assert_failed
return JS_FALSE
;
333 vs
= JS_GetInstancePrivate(ctx
, parent_win
,
334 (JSClass
*) &window_class
, NULL
);
335 doc_view
= vs
->doc_view
;
336 document
= doc_view
->document
;
337 fs
= input_get_form_state(ctx
, obj
);
338 if (!fs
) return JS_FALSE
; /* detached */
339 fc
= find_form_control(document
, fs
);
342 assert(fc
->form
&& fs
);
344 if (!JSID_IS_INT(id
))
347 linknum
= get_form_control_link(document
, fc
);
348 /* Hiddens have no link. */
349 if (linknum
>= 0) link
= &document
->links
[linknum
];
351 switch (JSID_TO_INT(id
)) {
352 case JSP_INPUT_ACCESSKEY
:
353 accesskey
= jsval_to_accesskey(ctx
, vp
);
354 if (accesskey
== UCS_NO_CHAR
)
357 link
->accesskey
= accesskey
;
360 mem_free_set(&fc
->alt
, stracpy(jsval_to_string(ctx
, vp
)));
362 case JSP_INPUT_CHECKED
:
363 if (fc
->type
!= FC_CHECKBOX
&& fc
->type
!= FC_RADIO
)
365 fs
->state
= jsval_to_boolean(ctx
, vp
);
367 case JSP_INPUT_DISABLED
:
368 /* FIXME: <input readonly disabled> --pasky */
369 fc
->mode
= (jsval_to_boolean(ctx
, vp
) ? FORM_MODE_DISABLED
370 : fc
->mode
== FORM_MODE_READONLY
? FORM_MODE_READONLY
373 case JSP_INPUT_MAX_LENGTH
:
374 if (!JS_ValueToInt32(ctx
, *vp
, &fc
->maxlength
))
378 mem_free_set(&fc
->name
, stracpy(jsval_to_string(ctx
, vp
)));
380 case JSP_INPUT_READONLY
:
381 /* FIXME: <input readonly disabled> --pasky */
382 fc
->mode
= (jsval_to_boolean(ctx
, vp
) ? FORM_MODE_READONLY
383 : fc
->mode
== FORM_MODE_DISABLED
? FORM_MODE_DISABLED
388 mem_free_set(&link
->where_img
, stracpy(jsval_to_string(ctx
, vp
)));
391 case JSP_INPUT_VALUE
:
392 if (fc
->type
== FC_FILE
)
393 break; /* A huge security risk otherwise. */
394 mem_free_set(&fs
->value
, stracpy(jsval_to_string(ctx
, vp
)));
395 if (fc
->type
== FC_TEXT
|| fc
->type
== FC_PASSWORD
)
396 fs
->state
= strlen(fs
->value
);
398 case JSP_INPUT_SELECTED_INDEX
:
399 if (fc
->type
== FC_SELECT
) {
402 if (!JS_ValueToInt32(ctx
, *vp
, &item
))
405 if (item
>= 0 && item
< fc
->nvalues
) {
407 mem_free_set(&fs
->value
, stracpy(fc
->values
[item
]));
413 /* Unrecognized integer property ID; someone is using
414 * the object as an array. SMJS builtin classes (e.g.
415 * js_RegExpClass) just return JS_TRUE in this case.
416 * Do the same here. */
423 /* @input_funcs{"blur"} */
425 input_blur(JSContext
*ctx
, uintN argc
, jsval
*rval
)
427 /* We are a text-mode browser and there *always* has to be something
428 * selected. So we do nothing for now. (That was easy.) */
432 /* @input_funcs{"click"} */
434 input_click(JSContext
*ctx
, uintN argc
, jsval
*rval
)
437 JSObject
*parent_form
; /* instance of @form_class */
438 JSObject
*parent_doc
; /* instance of @document_class */
439 JSObject
*parent_win
; /* instance of @window_class */
440 JSObject
*obj
= JS_THIS_OBJECT(ctx
, rval
);
441 jsval
*argv
= JS_ARGV(ctx
, rval
);
442 struct view_state
*vs
;
443 struct document_view
*doc_view
;
444 struct document
*document
;
446 struct form_state
*fs
;
447 struct form_control
*fc
;
450 if (!JS_InstanceOf(ctx
, obj
, (JSClass
*) &input_class
, argv
)) return JS_FALSE
;
451 parent_form
= JS_GetParent(ctx
, obj
);
452 assert(JS_InstanceOf(ctx
, parent_form
, (JSClass
*) &form_class
, NULL
));
453 if_assert_failed
return JS_FALSE
;
454 parent_doc
= JS_GetParent(ctx
, parent_form
);
455 assert(JS_InstanceOf(ctx
, parent_doc
, (JSClass
*) &document_class
, NULL
));
456 if_assert_failed
return JS_FALSE
;
457 parent_win
= JS_GetParent(ctx
, parent_doc
);
458 assert(JS_InstanceOf(ctx
, parent_win
, (JSClass
*) &window_class
, NULL
));
459 if_assert_failed
return JS_FALSE
;
461 vs
= JS_GetInstancePrivate(ctx
, parent_win
,
462 (JSClass
*) &window_class
, NULL
);
463 doc_view
= vs
->doc_view
;
464 document
= doc_view
->document
;
465 ses
= doc_view
->session
;
466 fs
= input_get_form_state(ctx
, obj
);
467 if (!fs
) return JS_FALSE
; /* detached */
470 fc
= find_form_control(document
, fs
);
473 linknum
= get_form_control_link(document
, fc
);
474 /* Hiddens have no link. */
478 /* Restore old current_link afterwards? */
479 jump_to_link_number(ses
, doc_view
, linknum
);
480 if (enter(ses
, doc_view
, 0) == FRAME_EVENT_REFRESH
)
481 refresh_view(ses
, doc_view
, 0);
483 print_screen_status(ses
);
485 boolean_to_jsval(ctx
, &val
, 0);
486 JS_SET_RVAL(ctx
, rval
, val
);
490 /* @input_funcs{"focus"} */
492 input_focus(JSContext
*ctx
, uintN argc
, jsval
*rval
)
495 JSObject
*parent_form
; /* instance of @form_class */
496 JSObject
*parent_doc
; /* instance of @document_class */
497 JSObject
*parent_win
; /* instance of @window_class */
498 JSObject
*obj
= JS_THIS_OBJECT(ctx
, rval
);
499 jsval
*argv
= JS_ARGV(ctx
, rval
);
500 struct view_state
*vs
;
501 struct document_view
*doc_view
;
502 struct document
*document
;
504 struct form_state
*fs
;
505 struct form_control
*fc
;
508 if (!JS_InstanceOf(ctx
, obj
, (JSClass
*) &input_class
, argv
)) return JS_FALSE
;
509 parent_form
= JS_GetParent(ctx
, obj
);
510 assert(JS_InstanceOf(ctx
, parent_form
, (JSClass
*) &form_class
, NULL
));
511 if_assert_failed
return JS_FALSE
;
512 parent_doc
= JS_GetParent(ctx
, parent_form
);
513 assert(JS_InstanceOf(ctx
, parent_doc
, (JSClass
*) &document_class
, NULL
));
514 if_assert_failed
return JS_FALSE
;
515 parent_win
= JS_GetParent(ctx
, parent_doc
);
516 assert(JS_InstanceOf(ctx
, parent_win
, (JSClass
*) &window_class
, NULL
));
517 if_assert_failed
return JS_FALSE
;
519 vs
= JS_GetInstancePrivate(ctx
, parent_win
,
520 (JSClass
*) &window_class
, NULL
);
521 doc_view
= vs
->doc_view
;
522 document
= doc_view
->document
;
523 ses
= doc_view
->session
;
524 fs
= input_get_form_state(ctx
, obj
);
525 if (!fs
) return JS_FALSE
; /* detached */
528 fc
= find_form_control(document
, fs
);
531 linknum
= get_form_control_link(document
, fc
);
532 /* Hiddens have no link. */
536 jump_to_link_number(ses
, doc_view
, linknum
);
538 boolean_to_jsval(ctx
, &val
, 0);
539 JS_SET_RVAL(ctx
, rval
, val
);
543 /* @input_funcs{"select"} */
545 input_select(JSContext
*ctx
, uintN argc
, jsval
*rval
)
547 /* We support no text selecting yet. So we do nothing for now.
548 * (That was easy, too.) */
553 get_input_object(JSContext
*ctx
, JSObject
*jsform
, struct form_state
*fs
)
555 JSObject
*jsinput
= fs
->ecmascript_obj
;
558 /* This assumes JS_GetInstancePrivate cannot GC. */
559 assert(JS_GetInstancePrivate(ctx
, jsinput
,
560 (JSClass
*) &input_class
, NULL
)
562 if_assert_failed
return NULL
;
567 /* jsform ('form') is input's parent */
568 /* FIXME: That is NOT correct since the real containing element
569 * should be its parent, but gimme DOM first. --pasky */
570 jsinput
= JS_NewObject(ctx
, (JSClass
*) &input_class
, NULL
, jsform
);
573 JS_DefineProperties(ctx
, jsinput
, (JSPropertySpec
*) input_props
);
574 spidermonkey_DefineFunctions(ctx
, jsinput
, input_funcs
);
576 if (!JS_SetPrivate(ctx
, jsinput
, fs
)) /* to @input_class */
578 fs
->ecmascript_obj
= jsinput
;
583 input_finalize(JSContext
*ctx
, JSObject
*jsinput
)
585 struct form_state
*fs
= JS_GetInstancePrivate(ctx
, jsinput
,
586 (JSClass
*) &input_class
,
590 /* If this assertion fails, leave fs->ecmascript_obj
591 * unchanged, because it may point to a different
592 * JSObject whose private pointer will later have to
593 * be updated to avoid crashes. */
594 assert(fs
->ecmascript_obj
== jsinput
);
595 if_assert_failed
return;
597 fs
->ecmascript_obj
= NULL
;
598 /* No need to JS_SetPrivate, because jsinput is being
604 spidermonkey_detach_form_state(struct form_state
*fs
)
606 JSObject
*jsinput
= fs
->ecmascript_obj
;
609 /* This assumes JS_GetInstancePrivate and JS_SetPrivate
612 /* If this assertion fails, it is not clear whether
613 * the private pointer of jsinput should be reset;
614 * crashes seem possible either way. Resetting it is
616 assert(JS_GetInstancePrivate(spidermonkey_empty_context
,
618 (JSClass
*) &input_class
, NULL
)
622 JS_SetPrivate(spidermonkey_empty_context
, jsinput
, NULL
);
623 fs
->ecmascript_obj
= NULL
;
628 spidermonkey_moved_form_state(struct form_state
*fs
)
630 JSObject
*jsinput
= fs
->ecmascript_obj
;
633 /* This assumes JS_SetPrivate cannot GC. If it could,
634 * then the GC might call input_finalize for some
635 * other object whose struct form_state has also been
636 * reallocated, and an assertion would fail in
638 JS_SetPrivate(spidermonkey_empty_context
, jsinput
, fs
);
644 get_form_control_object(JSContext
*ctx
, JSObject
*jsform
,
645 enum form_type type
, struct form_state
*fs
)
659 return get_input_object(ctx
, jsform
, fs
);
666 INTERNAL("Weird fc->type %d", type
);
672 static struct form_view
*form_get_form_view(JSContext
*ctx
, JSObject
*jsform
, jsval
*argv
);
673 static JSBool
form_elements_get_property(JSContext
*ctx
, JSObject
*obj
, jsid id
, jsval
*vp
);
675 /* Each @form_elements_class object must have a @form_class parent. */
676 static const JSClass form_elements_class
= {
679 JS_PropertyStub
, JS_PropertyStub
,
680 form_elements_get_property
, JS_StrictPropertyStub
,
681 JS_EnumerateStub
, JS_ResolveStub
, JS_ConvertStub
, JS_FinalizeStub
684 static JSBool
form_elements_item2(JSContext
*ctx
, JSObject
*obj
, uintN argc
, jsval
*argv
, jsval
*rval
);
685 static JSBool
form_elements_namedItem2(JSContext
*ctx
, JSObject
*obj
, uintN argc
, jsval
*argv
, jsval
*rval
);
686 static JSBool
form_elements_item(JSContext
*ctx
, uintN argc
, jsval
*rval
);
687 static JSBool
form_elements_namedItem(JSContext
*ctx
, uintN argc
, jsval
*rval
);
690 static const spidermonkeyFunctionSpec form_elements_funcs
[] = {
691 { "item", form_elements_item
, 1 },
692 { "namedItem", form_elements_namedItem
, 1 },
696 /* Tinyids of properties. Use negative values to distinguish these
697 * from array indexes (elements[INT] for INT>=0 is equivalent to
698 * elements.item(INT)). ECMAScript code should not use these directly
699 * as in elements[-1]; future versions of ELinks may change the numbers. */
700 enum form_elements_prop
{
701 JSP_FORM_ELEMENTS_LENGTH
= -1,
703 static const JSPropertySpec form_elements_props
[] = {
704 { "length", JSP_FORM_ELEMENTS_LENGTH
, JSPROP_ENUMERATE
| JSPROP_READONLY
},
708 /* @form_elements_class.getProperty */
710 form_elements_get_property(JSContext
*ctx
, JSObject
*obj
, jsid id
, jsval
*vp
)
713 JSObject
*parent_form
; /* instance of @form_class */
714 JSObject
*parent_doc
; /* instance of @document_class */
715 JSObject
*parent_win
; /* instance of @window_class */
716 struct view_state
*vs
;
717 struct document_view
*doc_view
;
718 struct document
*document
;
719 struct form_view
*form_view
;
722 /* This can be called if @obj if not itself an instance of the
723 * appropriate class but has one in its prototype chain. Fail
725 if (!JS_InstanceOf(ctx
, obj
, (JSClass
*) &form_elements_class
, NULL
))
727 parent_form
= JS_GetParent(ctx
, obj
);
728 assert(JS_InstanceOf(ctx
, parent_form
, (JSClass
*) &form_class
, NULL
));
729 if_assert_failed
return JS_FALSE
;
730 parent_doc
= JS_GetParent(ctx
, parent_form
);
731 assert(JS_InstanceOf(ctx
, parent_doc
, (JSClass
*) &document_class
, NULL
));
732 if_assert_failed
return JS_FALSE
;
733 parent_win
= JS_GetParent(ctx
, parent_doc
);
734 assert(JS_InstanceOf(ctx
, parent_win
, (JSClass
*) &window_class
, NULL
));
735 if_assert_failed
return JS_FALSE
;
737 vs
= JS_GetInstancePrivate(ctx
, parent_win
,
738 (JSClass
*) &window_class
, NULL
);
739 doc_view
= vs
->doc_view
;
740 document
= doc_view
->document
;
741 form_view
= form_get_form_view(ctx
, parent_form
, NULL
);
742 if (!form_view
) return JS_FALSE
; /* detached */
743 form
= find_form_by_form_view(document
, form_view
);
745 if (JSID_IS_STRING(id
)) {
746 JS_IdToValue(ctx
, id
, &idval
);
747 form_elements_namedItem2(ctx
, obj
, 1, &idval
, vp
);
751 if (!JSID_IS_INT(id
))
754 undef_to_jsval(ctx
, vp
);
756 switch (JSID_TO_INT(id
)) {
757 case JSP_FORM_ELEMENTS_LENGTH
:
758 int_to_jsval(ctx
, vp
, list_size(&form
->items
));
762 JS_IdToValue(ctx
, id
, &idval
);
763 form_elements_item2(ctx
, obj
, 1, &idval
, vp
);
770 /* @form_elements_funcs{"item"} */
772 form_elements_item(JSContext
*ctx
, uintN argc
, jsval
*rval
)
774 jsval val
= JSVAL_VOID
;
775 JSObject
*obj
= JS_THIS_OBJECT(ctx
, rval
);
776 jsval
*argv
= JS_ARGV(ctx
, rval
);
777 JSBool ret
= form_elements_item2(ctx
, obj
, argc
, argv
, &val
);
779 JS_SET_RVAL(ctx
, rval
, val
);
784 form_elements_item2(JSContext
*ctx
, JSObject
*obj
, uintN argc
, jsval
*argv
, jsval
*rval
)
786 JSObject
*parent_form
; /* instance of @form_class */
787 JSObject
*parent_doc
; /* instance of @document_class */
788 JSObject
*parent_win
; /* instance of @window_class */
789 struct view_state
*vs
;
790 struct document_view
*doc_view
;
791 struct document
*document
;
792 struct form_view
*form_view
;
794 struct form_control
*fc
;
798 if (!JS_InstanceOf(ctx
, obj
, (JSClass
*) &form_elements_class
, argv
)) return JS_FALSE
;
799 parent_form
= JS_GetParent(ctx
, obj
);
800 assert(JS_InstanceOf(ctx
, parent_form
, (JSClass
*) &form_class
, NULL
));
801 if_assert_failed
return JS_FALSE
;
802 parent_doc
= JS_GetParent(ctx
, parent_form
);
803 assert(JS_InstanceOf(ctx
, parent_doc
, (JSClass
*) &document_class
, NULL
));
804 if_assert_failed
return JS_FALSE
;
805 parent_win
= JS_GetParent(ctx
, parent_doc
);
806 assert(JS_InstanceOf(ctx
, parent_win
, (JSClass
*) &window_class
, NULL
));
807 if_assert_failed
return JS_FALSE
;
809 vs
= JS_GetInstancePrivate(ctx
, parent_win
,
810 (JSClass
*) &window_class
, NULL
);
811 doc_view
= vs
->doc_view
;
812 document
= doc_view
->document
;
813 form_view
= form_get_form_view(ctx
, parent_form
, NULL
);
814 if (!form_view
) return JS_FALSE
; /* detached */
815 form
= find_form_by_form_view(document
, form_view
);
820 if (!JS_ValueToInt32(ctx
, argv
[0], &index
))
822 undef_to_jsval(ctx
, rval
);
824 foreach (fc
, form
->items
) {
826 if (counter
== index
) {
827 struct form_state
*fs
= find_form_state(doc_view
, fc
);
830 JSObject
*fcobj
= get_form_control_object(ctx
, parent_form
, fc
->type
, fs
);
833 object_to_jsval(ctx
, rval
, fcobj
);
842 /* @form_elements_funcs{"namedItem"} */
844 form_elements_namedItem(JSContext
*ctx
, uintN argc
, jsval
*rval
)
846 jsval val
= JSVAL_VOID
;
847 JSObject
*obj
= JS_THIS_OBJECT(ctx
, rval
);
848 jsval
*argv
= JS_ARGV(ctx
, rval
);
849 JSBool ret
= form_elements_namedItem2(ctx
, obj
, argc
, argv
, &val
);
851 JS_SET_RVAL(ctx
, rval
, val
);
856 form_elements_namedItem2(JSContext
*ctx
, JSObject
*obj
, uintN argc
, jsval
*argv
, jsval
*rval
)
858 JSObject
*parent_form
; /* instance of @form_class */
859 JSObject
*parent_doc
; /* instance of @document_class */
860 JSObject
*parent_win
; /* instance of @window_class */
861 struct view_state
*vs
;
862 struct document_view
*doc_view
;
863 struct document
*document
;
864 struct form_view
*form_view
;
866 struct form_control
*fc
;
867 unsigned char *string
;
869 if (!JS_InstanceOf(ctx
, obj
, (JSClass
*) &form_elements_class
, argv
)) return JS_FALSE
;
870 parent_form
= JS_GetParent(ctx
, obj
);
871 assert(JS_InstanceOf(ctx
, parent_form
, (JSClass
*) &form_class
, NULL
));
872 if_assert_failed
return JS_FALSE
;
873 parent_doc
= JS_GetParent(ctx
, parent_form
);
874 assert(JS_InstanceOf(ctx
, parent_doc
, (JSClass
*) &document_class
, NULL
));
875 if_assert_failed
return JS_FALSE
;
876 parent_win
= JS_GetParent(ctx
, parent_doc
);
877 assert(JS_InstanceOf(ctx
, parent_win
, (JSClass
*) &window_class
, NULL
));
878 if_assert_failed
return JS_FALSE
;
880 vs
= JS_GetInstancePrivate(ctx
, parent_win
,
881 (JSClass
*) &window_class
, NULL
);
882 doc_view
= vs
->doc_view
;
883 document
= doc_view
->document
;
884 form_view
= form_get_form_view(ctx
, parent_form
, NULL
);
885 if (!form_view
) return JS_FALSE
; /* detached */
886 form
= find_form_by_form_view(document
, form_view
);
891 string
= jsval_to_string(ctx
, &argv
[0]);
895 undef_to_jsval(ctx
, rval
);
897 foreach (fc
, form
->items
) {
898 if ((fc
->id
&& !c_strcasecmp(string
, fc
->id
))
899 || (fc
->name
&& !c_strcasecmp(string
, fc
->name
))) {
900 struct form_state
*fs
= find_form_state(doc_view
, fc
);
903 JSObject
*fcobj
= get_form_control_object(ctx
, parent_form
, fc
->type
, fs
);
906 object_to_jsval(ctx
, rval
, fcobj
);
917 static JSBool
form_get_property(JSContext
*ctx
, JSObject
*obj
, jsid id
, jsval
*vp
);
918 static JSBool
form_set_property(JSContext
*ctx
, JSObject
*obj
, jsid id
, JSBool strict
, jsval
*vp
);
919 static void form_finalize(JSContext
*ctx
, JSObject
*obj
);
921 /* Each @form_class object must have a @document_class parent. */
922 static const JSClass form_class
= {
924 JSCLASS_HAS_PRIVATE
, /* struct form_view *, or NULL if detached */
925 JS_PropertyStub
, JS_PropertyStub
,
926 form_get_property
, form_set_property
,
927 JS_EnumerateStub
, JS_ResolveStub
, JS_ConvertStub
, form_finalize
930 /* Tinyids of properties. Use negative values to distinguish these
931 * from array indexes (even though this object has no array elements).
932 * ECMAScript code should not use these directly as in form[-1];
933 * future versions of ELinks may change the numbers. */
935 JSP_FORM_ACTION
= -1,
936 JSP_FORM_ELEMENTS
= -2,
937 JSP_FORM_ENCODING
= -3,
938 JSP_FORM_LENGTH
= -4,
939 JSP_FORM_METHOD
= -5,
941 JSP_FORM_TARGET
= -7,
944 static const JSPropertySpec form_props
[] = {
945 { "action", JSP_FORM_ACTION
, JSPROP_ENUMERATE
},
946 { "elements", JSP_FORM_ELEMENTS
, JSPROP_ENUMERATE
},
947 { "encoding", JSP_FORM_ENCODING
, JSPROP_ENUMERATE
},
948 { "length", JSP_FORM_LENGTH
, JSPROP_ENUMERATE
| JSPROP_READONLY
},
949 { "method", JSP_FORM_METHOD
, JSPROP_ENUMERATE
},
950 { "name", JSP_FORM_NAME
, JSPROP_ENUMERATE
},
951 { "target", JSP_FORM_TARGET
, JSPROP_ENUMERATE
},
955 static JSBool
form_reset(JSContext
*ctx
, uintN argc
, jsval
*rval
);
956 static JSBool
form_submit(JSContext
*ctx
, uintN argc
, jsval
*rval
);
958 static const spidermonkeyFunctionSpec form_funcs
[] = {
959 { "reset", form_reset
, 0 },
960 { "submit", form_submit
, 0 },
964 static struct form_view
*
965 form_get_form_view(JSContext
*ctx
, JSObject
*jsform
, jsval
*argv
)
967 struct form_view
*fv
= JS_GetInstancePrivate(ctx
, jsform
,
968 (JSClass
*) &form_class
,
971 if (!fv
) return NULL
; /* detached */
973 assert(fv
->ecmascript_obj
== jsform
);
974 if_assert_failed
return NULL
;
979 /* @form_class.getProperty */
981 form_get_property(JSContext
*ctx
, JSObject
*obj
, jsid id
, jsval
*vp
)
983 /* DBG("doc %p %s\n", parent_doc, JS_GetStringBytes(JS_ValueToString(ctx, OBJECT_TO_JSVAL(parent_doc)))); */
984 JSObject
*parent_doc
; /* instance of @document_class */
985 JSObject
*parent_win
; /* instance of @window_class */
986 struct view_state
*vs
;
987 struct document_view
*doc_view
;
988 struct form_view
*fv
;
991 /* This can be called if @obj if not itself an instance of the
992 * appropriate class but has one in its prototype chain. Fail
994 if (!JS_InstanceOf(ctx
, obj
, (JSClass
*) &form_class
, NULL
))
996 parent_doc
= JS_GetParent(ctx
, obj
);
997 assert(JS_InstanceOf(ctx
, parent_doc
, (JSClass
*) &document_class
, NULL
));
998 if_assert_failed
return JS_FALSE
;
999 parent_win
= JS_GetParent(ctx
, parent_doc
);
1000 assert(JS_InstanceOf(ctx
, parent_win
, (JSClass
*) &window_class
, NULL
));
1001 if_assert_failed
return JS_FALSE
;
1003 vs
= JS_GetInstancePrivate(ctx
, parent_win
,
1004 (JSClass
*) &window_class
, NULL
);
1005 doc_view
= vs
->doc_view
;
1006 fv
= form_get_form_view(ctx
, obj
, NULL
);
1007 if (!fv
) return JS_FALSE
; /* detached */
1008 form
= find_form_by_form_view(doc_view
->document
, fv
);
1012 if (JSID_IS_STRING(id
)) {
1013 struct form_control
*fc
;
1014 unsigned char *string
;
1016 string
= jsid_to_string(ctx
, &id
);
1017 foreach (fc
, form
->items
) {
1018 JSObject
*fcobj
= NULL
;
1019 struct form_state
*fs
;
1021 if ((!fc
->id
|| c_strcasecmp(string
, fc
->id
))
1022 && (!fc
->name
|| c_strcasecmp(string
, fc
->name
)))
1025 undef_to_jsval(ctx
, vp
);
1026 fs
= find_form_state(doc_view
, fc
);
1028 fcobj
= get_form_control_object(ctx
, obj
, fc
->type
, fs
);
1030 object_to_jsval(ctx
, vp
, fcobj
);
1037 if (!JSID_IS_INT(id
))
1040 undef_to_jsval(ctx
, vp
);
1042 switch (JSID_TO_INT(id
)) {
1043 case JSP_FORM_ACTION
:
1044 string_to_jsval(ctx
, vp
, form
->action
);
1047 case JSP_FORM_ELEMENTS
:
1049 /* jsform ('form') is form_elements' parent; who knows is that's correct */
1050 JSObject
*jsform_elems
= JS_NewObject(ctx
, (JSClass
*) &form_elements_class
, NULL
, obj
);
1052 JS_DefineProperties(ctx
, jsform_elems
, (JSPropertySpec
*) form_elements_props
);
1053 spidermonkey_DefineFunctions(ctx
, jsform_elems
,
1054 form_elements_funcs
);
1055 object_to_jsval(ctx
, vp
, jsform_elems
);
1056 /* SM will cache this property value for us so we create this
1057 * just once per form. */
1061 case JSP_FORM_ENCODING
:
1062 switch (form
->method
) {
1063 case FORM_METHOD_GET
:
1064 case FORM_METHOD_POST
:
1065 string_to_jsval(ctx
, vp
, "application/x-www-form-urlencoded");
1067 case FORM_METHOD_POST_MP
:
1068 string_to_jsval(ctx
, vp
, "multipart/form-data");
1070 case FORM_METHOD_POST_TEXT_PLAIN
:
1071 string_to_jsval(ctx
, vp
, "text/plain");
1076 case JSP_FORM_LENGTH
:
1077 int_to_jsval(ctx
, vp
, list_size(&form
->items
));
1080 case JSP_FORM_METHOD
:
1081 switch (form
->method
) {
1082 case FORM_METHOD_GET
:
1083 string_to_jsval(ctx
, vp
, "GET");
1086 case FORM_METHOD_POST
:
1087 case FORM_METHOD_POST_MP
:
1088 case FORM_METHOD_POST_TEXT_PLAIN
:
1089 string_to_jsval(ctx
, vp
, "POST");
1095 string_to_jsval(ctx
, vp
, form
->name
);
1098 case JSP_FORM_TARGET
:
1099 string_to_jsval(ctx
, vp
, form
->target
);
1103 /* Unrecognized integer property ID; someone is using
1104 * the object as an array. SMJS builtin classes (e.g.
1105 * js_RegExpClass) just return JS_TRUE in this case
1106 * and leave *@vp unchanged. Do the same here.
1107 * (Actually not quite the same, as we already used
1108 * @undef_to_jsval.) */
1115 /* @form_class.setProperty */
1117 form_set_property(JSContext
*ctx
, JSObject
*obj
, jsid id
, JSBool strict
, jsval
*vp
)
1119 JSObject
*parent_doc
; /* instance of @document_class */
1120 JSObject
*parent_win
; /* instance of @window_class */
1121 struct view_state
*vs
;
1122 struct document_view
*doc_view
;
1123 struct form_view
*fv
;
1125 unsigned char *string
;
1127 /* This can be called if @obj if not itself an instance of the
1128 * appropriate class but has one in its prototype chain. Fail
1130 if (!JS_InstanceOf(ctx
, obj
, (JSClass
*) &form_class
, NULL
))
1132 parent_doc
= JS_GetParent(ctx
, obj
);
1133 assert(JS_InstanceOf(ctx
, parent_doc
, (JSClass
*) &document_class
, NULL
));
1134 if_assert_failed
return JS_FALSE
;
1135 parent_win
= JS_GetParent(ctx
, parent_doc
);
1136 assert(JS_InstanceOf(ctx
, parent_win
, (JSClass
*) &window_class
, NULL
));
1137 if_assert_failed
return JS_FALSE
;
1139 vs
= JS_GetInstancePrivate(ctx
, parent_win
,
1140 (JSClass
*) &window_class
, NULL
);
1141 doc_view
= vs
->doc_view
;
1142 fv
= form_get_form_view(ctx
, obj
, NULL
);
1143 if (!fv
) return JS_FALSE
; /* detached */
1144 form
= find_form_by_form_view(doc_view
->document
, fv
);
1148 if (!JSID_IS_INT(id
))
1151 switch (JSID_TO_INT(id
)) {
1152 case JSP_FORM_ACTION
:
1153 string
= stracpy(jsval_to_string(ctx
, vp
));
1155 ecmascript_set_action(&form
->action
, string
);
1157 mem_free_set(&form
->action
, string
);
1161 case JSP_FORM_ENCODING
:
1162 string
= jsval_to_string(ctx
, vp
);
1163 if (!c_strcasecmp(string
, "application/x-www-form-urlencoded")) {
1164 form
->method
= form
->method
== FORM_METHOD_GET
? FORM_METHOD_GET
1166 } else if (!c_strcasecmp(string
, "multipart/form-data")) {
1167 form
->method
= FORM_METHOD_POST_MP
;
1168 } else if (!c_strcasecmp(string
, "text/plain")) {
1169 form
->method
= FORM_METHOD_POST_TEXT_PLAIN
;
1173 case JSP_FORM_METHOD
:
1174 string
= jsval_to_string(ctx
, vp
);
1175 if (!c_strcasecmp(string
, "GET")) {
1176 form
->method
= FORM_METHOD_GET
;
1177 } else if (!c_strcasecmp(string
, "POST")) {
1178 form
->method
= FORM_METHOD_POST
;
1183 mem_free_set(&form
->name
, stracpy(jsval_to_string(ctx
, vp
)));
1186 case JSP_FORM_TARGET
:
1187 mem_free_set(&form
->target
, stracpy(jsval_to_string(ctx
, vp
)));
1191 /* Unrecognized integer property ID; someone is using
1192 * the object as an array. SMJS builtin classes (e.g.
1193 * js_RegExpClass) just return JS_TRUE in this case.
1194 * Do the same here. */
1201 /* @form_funcs{"reset"} */
1203 form_reset(JSContext
*ctx
, uintN argc
, jsval
*rval
)
1206 JSObject
*parent_doc
; /* instance of @document_class */
1207 JSObject
*parent_win
; /* instance of @window_class */
1208 JSObject
*obj
= JS_THIS_OBJECT(ctx
, rval
);
1209 jsval
*argv
= JS_ARGV(ctx
, rval
);
1210 struct view_state
*vs
;
1211 struct document_view
*doc_view
;
1212 struct form_view
*fv
;
1215 if (!JS_InstanceOf(ctx
, obj
, (JSClass
*) &form_class
, argv
)) return JS_FALSE
;
1216 parent_doc
= JS_GetParent(ctx
, obj
);
1217 assert(JS_InstanceOf(ctx
, parent_doc
, (JSClass
*) &document_class
, NULL
));
1218 if_assert_failed
return JS_FALSE
;
1219 parent_win
= JS_GetParent(ctx
, parent_doc
);
1220 assert(JS_InstanceOf(ctx
, parent_win
, (JSClass
*) &window_class
, NULL
));
1221 if_assert_failed
return JS_FALSE
;
1223 vs
= JS_GetInstancePrivate(ctx
, parent_win
,
1224 (JSClass
*) &window_class
, NULL
);
1225 doc_view
= vs
->doc_view
;
1226 fv
= form_get_form_view(ctx
, obj
, argv
);
1227 if (!fv
) return JS_FALSE
; /* detached */
1228 form
= find_form_by_form_view(doc_view
->document
, fv
);
1232 do_reset_form(doc_view
, form
);
1233 draw_forms(doc_view
->session
->tab
->term
, doc_view
);
1235 boolean_to_jsval(ctx
, &val
, 0);
1236 JS_SET_RVAL(ctx
, rval
, val
);
1241 /* @form_funcs{"submit"} */
1243 form_submit(JSContext
*ctx
, uintN argc
, jsval
*rval
)
1246 JSObject
*parent_doc
; /* instance of @document_class */
1247 JSObject
*parent_win
; /* instance of @window_class */
1248 JSObject
*obj
= JS_THIS_OBJECT(ctx
, rval
);
1249 jsval
*argv
= JS_ARGV(ctx
, rval
);
1250 struct view_state
*vs
;
1251 struct document_view
*doc_view
;
1252 struct session
*ses
;
1253 struct form_view
*fv
;
1256 if (!JS_InstanceOf(ctx
, obj
, (JSClass
*) &form_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_GetInstancePrivate(ctx
, parent_win
,
1265 (JSClass
*) &window_class
, NULL
);
1266 doc_view
= vs
->doc_view
;
1267 ses
= doc_view
->session
;
1268 fv
= form_get_form_view(ctx
, obj
, argv
);
1269 if (!fv
) return JS_FALSE
; /* detached */
1270 form
= find_form_by_form_view(doc_view
->document
, fv
);
1273 submit_given_form(ses
, doc_view
, form
, 0);
1275 boolean_to_jsval(ctx
, &val
, 0);
1276 JS_SET_RVAL(ctx
, rval
, val
);
1282 get_form_object(JSContext
*ctx
, JSObject
*jsdoc
, struct form_view
*fv
)
1284 JSObject
*jsform
= fv
->ecmascript_obj
;
1287 /* This assumes JS_GetInstancePrivate cannot GC. */
1288 assert(JS_GetInstancePrivate(ctx
, jsform
,
1289 (JSClass
*) &form_class
, NULL
)
1291 if_assert_failed
return NULL
;
1296 /* jsdoc ('document') is fv's parent */
1297 /* FIXME: That is NOT correct since the real containing element
1298 * should be its parent, but gimme DOM first. --pasky */
1299 jsform
= JS_NewObject(ctx
, (JSClass
*) &form_class
, NULL
, jsdoc
);
1302 JS_DefineProperties(ctx
, jsform
, (JSPropertySpec
*) form_props
);
1303 spidermonkey_DefineFunctions(ctx
, jsform
, form_funcs
);
1305 if (!JS_SetPrivate(ctx
, jsform
, fv
)) /* to @form_class */
1307 fv
->ecmascript_obj
= jsform
;
1312 form_finalize(JSContext
*ctx
, JSObject
*jsform
)
1314 struct form_view
*fv
= JS_GetInstancePrivate(ctx
, jsform
,
1315 (JSClass
*) &form_class
,
1319 /* If this assertion fails, leave fv->ecmascript_obj
1320 * unchanged, because it may point to a different
1321 * JSObject whose private pointer will later have to
1322 * be updated to avoid crashes. */
1323 assert(fv
->ecmascript_obj
== jsform
);
1324 if_assert_failed
return;
1326 fv
->ecmascript_obj
= NULL
;
1327 /* No need to JS_SetPrivate, because the object is
1328 * being destroyed. */
1333 spidermonkey_detach_form_view(struct form_view
*fv
)
1335 JSObject
*jsform
= fv
->ecmascript_obj
;
1338 /* This assumes JS_GetInstancePrivate and JS_SetPrivate
1341 /* If this assertion fails, it is not clear whether
1342 * the private pointer of jsform should be reset;
1343 * crashes seem possible either way. Resetting it is
1345 assert(JS_GetInstancePrivate(spidermonkey_empty_context
,
1347 (JSClass
*) &form_class
, NULL
)
1351 JS_SetPrivate(spidermonkey_empty_context
, jsform
, NULL
);
1352 fv
->ecmascript_obj
= NULL
;
1357 static JSBool
forms_get_property(JSContext
*ctx
, JSObject
*obj
, jsid id
, jsval
*vp
);
1359 /* Each @forms_class object must have a @document_class parent. */
1360 const JSClass forms_class
= {
1362 JSCLASS_HAS_PRIVATE
,
1363 JS_PropertyStub
, JS_PropertyStub
,
1364 forms_get_property
, JS_StrictPropertyStub
,
1365 JS_EnumerateStub
, JS_ResolveStub
, JS_ConvertStub
, JS_FinalizeStub
1368 static JSBool
forms_item(JSContext
*ctx
, uintN argc
, jsval
*rval
);
1369 static JSBool
forms_item2(JSContext
*ctx
, JSObject
*obj
, uintN argc
, jsval
*argv
, jsval
*rval
);
1370 static JSBool
forms_namedItem(JSContext
*ctx
, uintN argc
, jsval
*rval
);
1372 const spidermonkeyFunctionSpec forms_funcs
[] = {
1373 { "item", forms_item
, 1 },
1374 { "namedItem", forms_namedItem
, 1 },
1378 /* Tinyids of properties. Use negative values to distinguish these from
1379 * array indexes (forms[INT] for INT>=0 is equivalent to forms.item(INT)).
1380 * ECMAScript code should not use these directly as in forms[-1];
1381 * future versions of ELinks may change the numbers. */
1383 JSP_FORMS_LENGTH
= -1,
1385 const JSPropertySpec forms_props
[] = {
1386 { "length", JSP_FORMS_LENGTH
, JSPROP_ENUMERATE
| JSPROP_READONLY
},
1390 /* Find the form whose name is @name, which should normally be a
1391 * string (but might not be). If found, set *rval = the DOM
1392 * object. If not found, leave *rval unchanged. */
1394 find_form_by_name(JSContext
*ctx
, JSObject
*jsdoc
,
1395 struct document_view
*doc_view
,
1396 jsval name
, jsval
*rval
)
1398 unsigned char *string
= jsval_to_string(ctx
, &name
);
1404 foreach (form
, doc_view
->document
->forms
) {
1405 if (form
->name
&& !c_strcasecmp(string
, form
->name
)) {
1406 object_to_jsval(ctx
, rval
, get_form_object(ctx
, jsdoc
,
1407 find_form_view(doc_view
, form
)));
1413 /* @forms_class.getProperty */
1415 forms_get_property(JSContext
*ctx
, JSObject
*obj
, jsid id
, jsval
*vp
)
1418 JSObject
*parent_doc
; /* instance of @document_class */
1419 JSObject
*parent_win
; /* instance of @window_class */
1420 struct view_state
*vs
;
1421 struct document_view
*doc_view
;
1422 struct document
*document
;
1424 /* This can be called if @obj if not itself an instance of the
1425 * appropriate class but has one in its prototype chain. Fail
1427 if (!JS_InstanceOf(ctx
, obj
, (JSClass
*) &forms_class
, NULL
))
1429 parent_doc
= JS_GetParent(ctx
, obj
);
1430 assert(JS_InstanceOf(ctx
, parent_doc
, (JSClass
*) &document_class
, NULL
));
1431 if_assert_failed
return JS_FALSE
;
1432 parent_win
= JS_GetParent(ctx
, parent_doc
);
1433 assert(JS_InstanceOf(ctx
, parent_win
, (JSClass
*) &window_class
, NULL
));
1434 if_assert_failed
return JS_FALSE
;
1436 vs
= JS_GetInstancePrivate(ctx
, parent_win
,
1437 (JSClass
*) &window_class
, NULL
);
1438 doc_view
= vs
->doc_view
;
1439 document
= doc_view
->document
;
1441 if (JSID_IS_STRING(id
)) {
1442 /* When SMJS evaluates forms.namedItem("foo"), it first
1443 * calls forms_get_property with id = JSString "namedItem"
1444 * and *vp = JSObject JSFunction forms_namedItem.
1445 * If we don't find a form whose name is id,
1446 * we must leave *vp unchanged here, to avoid
1447 * "TypeError: forms.namedItem is not a function". */
1448 JS_IdToValue(ctx
, id
, &idval
);
1449 find_form_by_name(ctx
, parent_doc
, doc_view
, idval
, vp
);
1453 if (!JSID_IS_INT(id
))
1456 switch (JSID_TO_INT(id
)) {
1457 case JSP_FORMS_LENGTH
:
1458 int_to_jsval(ctx
, vp
, list_size(&document
->forms
));
1462 JS_IdToValue(ctx
, id
, &idval
);
1463 forms_item2(ctx
, obj
, 1, &idval
, vp
);
1470 /* @forms_funcs{"item"} */
1472 forms_item(JSContext
*ctx
, uintN argc
, jsval
*rval
)
1474 jsval val
= JSVAL_VOID
;
1475 JSObject
*obj
= JS_THIS_OBJECT(ctx
, rval
);
1476 jsval
*argv
= JS_ARGV(ctx
, rval
);
1477 JSBool ret
= forms_item2(ctx
, obj
, argc
, argv
, &val
);
1479 JS_SET_RVAL(ctx
, rval
, val
);
1484 forms_item2(JSContext
*ctx
, JSObject
*obj
, uintN argc
, jsval
*argv
, jsval
*rval
)
1486 JSObject
*parent_doc
; /* instance of @document_class */
1487 JSObject
*parent_win
; /* instance of @window_class */
1488 struct view_state
*vs
;
1489 struct form_view
*fv
;
1493 if (!JS_InstanceOf(ctx
, obj
, (JSClass
*) &forms_class
, argv
)) return JS_FALSE
;
1494 parent_doc
= JS_GetParent(ctx
, obj
);
1495 assert(JS_InstanceOf(ctx
, parent_doc
, (JSClass
*) &document_class
, NULL
));
1496 if_assert_failed
return JS_FALSE
;
1497 parent_win
= JS_GetParent(ctx
, parent_doc
);
1498 assert(JS_InstanceOf(ctx
, parent_win
, (JSClass
*) &window_class
, NULL
));
1499 if_assert_failed
return JS_FALSE
;
1501 vs
= JS_GetInstancePrivate(ctx
, parent_win
,
1502 (JSClass
*) &window_class
, NULL
);
1507 if (!JS_ValueToInt32(ctx
, argv
[0], &index
))
1509 undef_to_jsval(ctx
, rval
);
1511 foreach (fv
, vs
->forms
) {
1513 if (counter
== index
) {
1514 object_to_jsval(ctx
, rval
, get_form_object(ctx
, parent_doc
, fv
));
1522 /* @forms_funcs{"namedItem"} */
1524 forms_namedItem(JSContext
*ctx
, uintN argc
, jsval
*rval
)
1527 JSObject
*parent_doc
; /* instance of @document_class */
1528 JSObject
*parent_win
; /* instance of @window_class */
1529 JSObject
*obj
= JS_THIS_OBJECT(ctx
, rval
);
1530 jsval
*argv
= JS_ARGV(ctx
, rval
);
1531 struct view_state
*vs
;
1532 struct document_view
*doc_view
;
1534 if (!JS_InstanceOf(ctx
, obj
, (JSClass
*) &forms_class
, argv
)) return JS_FALSE
;
1535 parent_doc
= JS_GetParent(ctx
, obj
);
1536 assert(JS_InstanceOf(ctx
, parent_doc
, (JSClass
*) &document_class
, NULL
));
1537 if_assert_failed
return JS_FALSE
;
1538 parent_win
= JS_GetParent(ctx
, parent_doc
);
1539 assert(JS_InstanceOf(ctx
, parent_win
, (JSClass
*) &window_class
, NULL
));
1540 if_assert_failed
return JS_FALSE
;
1542 vs
= JS_GetInstancePrivate(ctx
, parent_win
,
1543 (JSClass
*) &window_class
, NULL
);
1544 doc_view
= vs
->doc_view
;
1549 undef_to_jsval(ctx
, &val
);
1550 find_form_by_name(ctx
, parent_doc
, doc_view
, argv
[0], &val
);
1551 JS_SET_RVAL(ctx
, rval
, val
);
1557 unicode_to_jsstring(JSContext
*ctx
, unicode_val_T u
)
1561 /* This is supposed to make a string from which
1562 * jsval_to_accesskey() can get the original @u back.
1563 * If @u is a surrogate, then that is not possible, so
1564 * return NULL to indicate an error instead.
1566 * If JS_NewUCStringCopyN hits a null character, it truncates
1567 * the string there and pads it with more nulls. However,
1568 * that is not a problem here, because if there is a null
1569 * character in buf[], then it must be the only character. */
1570 if (u
<= 0xFFFF && !is_utf16_surrogate(u
)) {
1572 return JS_NewUCStringCopyN(ctx
, buf
, 1);
1573 } else if (needs_utf16_surrogates(u
)) {
1574 buf
[0] = get_utf16_high_surrogate(u
);
1575 buf
[1] = get_utf16_low_surrogate(u
);
1576 return JS_NewUCStringCopyN(ctx
, buf
, 2);
1582 /* Convert the string *@vp to an access key. Return 0 for no access
1583 * key, UCS_NO_CHAR on error, or the access key otherwise. */
1584 static unicode_val_T
1585 jsval_to_accesskey(JSContext
*ctx
, jsval
*vp
)
1590 /* Convert the value in place, to protect the result from GC. */
1591 if (JS_ConvertValue(ctx
, *vp
, JSTYPE_STRING
, vp
) == JS_FALSE
)
1593 len
= JS_GetStringEncodingLength(ctx
, JSVAL_TO_STRING(*vp
));
1594 chr
= JS_EncodeString(ctx
, JSVAL_TO_STRING(*vp
));
1596 /* This implementation ignores extra characters in the string. */
1598 return 0; /* which means no access key */
1599 if (!is_utf16_surrogate(chr
[0]))
1602 && is_utf16_high_surrogate(chr
[0])
1603 && is_utf16_low_surrogate(chr
[1]))
1604 return join_utf16_surrogates(chr
[0], chr
[1]);
1605 JS_ReportError(ctx
, "Invalid UTF-16 sequence");
1606 return UCS_NO_CHAR
; /* which the caller will reject */