~ubuntu-branches/ubuntu/feisty/elinks/feisty-updates

« back to all changes in this revision

Viewing changes to src/ecmascript/spidermonkey/form.c

  • Committer: Bazaar Package Importer
  • Author(s): Martin Pitt
  • Date: 2006-06-30 08:57:43 UTC
  • mfrom: (1.1.2 upstream)
  • Revision ID: james.westby@ubuntu.com-20060630085743-l81fgbw9dehvl1ds
Tags: 0.11.1-1ubuntu1
* Merge to Debian unstable.
* Removed gnutls12 porting, this is upstream now.
* Only Ubuntu changes left:
  - Killed type-handling.
  - Add X-Ubuntu-Gettext-Domain to .desktop files.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* The SpiderMonkey window object implementation. */
 
2
 
 
3
#ifdef HAVE_CONFIG_H
 
4
#include "config.h"
 
5
#endif
 
6
 
 
7
#include <stdio.h>
 
8
#include <stdlib.h>
 
9
#include <string.h>
 
10
 
 
11
#include "elinks.h"
 
12
 
 
13
#include "ecmascript/spidermonkey/util.h"
 
14
 
 
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"
 
45
 
 
46
 
 
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 */
 
51
 
 
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);
 
54
 
 
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
 
61
};
 
62
 
 
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
 
79
};
 
80
 
 
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 }
 
103
};
 
104
 
 
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);
 
109
 
 
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 }
 
116
};
 
117
 
 
118
static JSBool
 
119
input_get_property(JSContext *ctx, JSObject *obj, jsval id, jsval *vp)
 
120
{
 
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;
 
131
 
 
132
        assert(fc);
 
133
        assert(fc->form && fs);
 
134
 
 
135
        if (!JSVAL_IS_INT(id))
 
136
                return JS_TRUE;
 
137
 
 
138
        linknum = get_form_control_link(document, fc);
 
139
        /* Hiddens have no link. */
 
140
        if (linknum >= 0) link = &document->links[linknum];
 
141
 
 
142
        undef_to_jsval(ctx, vp);
 
143
 
 
144
        switch (JSVAL_TO_INT(id)) {
 
145
        case JSP_INPUT_ACCESSKEY:
 
146
        {
 
147
                struct string keystr;
 
148
 
 
149
                if (!link) break;
 
150
 
 
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;
 
156
        }
 
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:
 
199
        {
 
200
                unsigned char *s = NULL;
 
201
 
 
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;
 
214
                }
 
215
                string_to_jsval(ctx, vp, s);
 
216
                break;
 
217
        }
 
218
        case JSP_INPUT_VALUE:
 
219
                string_to_jsval(ctx, vp, fs->value);
 
220
                break;
 
221
 
 
222
        default:
 
223
                INTERNAL("Invalid ID %d in input_get_property().", JSVAL_TO_INT(id));
 
224
                break;
 
225
        }
 
226
 
 
227
        return JS_TRUE;
 
228
}
 
229
 
 
230
static JSBool
 
231
input_set_property(JSContext *ctx, JSObject *obj, jsval id, jsval *vp)
 
232
{
 
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;
 
243
 
 
244
        assert(fc);
 
245
        assert(fc->form && fs);
 
246
 
 
247
        if (!JSVAL_IS_INT(id))
 
248
                return JS_TRUE;
 
249
 
 
250
        linknum = get_form_control_link(document, fc);
 
251
        /* Hiddens have no link. */
 
252
        if (linknum >= 0) link = &document->links[linknum];
 
253
 
 
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)));
 
288
                }
 
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;
 
297
 
 
298
        default:
 
299
                INTERNAL("Invalid ID %d in input_set_property().", JSVAL_TO_INT(id));
 
300
                return JS_TRUE;
 
301
        }
 
302
 
 
303
        return JS_TRUE;
 
304
}
 
305
 
 
306
static JSBool
 
307
input_blur(JSContext *ctx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
 
308
{
 
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;
 
312
}
 
313
 
 
314
static JSBool
 
315
input_click(JSContext *ctx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
 
316
{
 
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;
 
327
 
 
328
        assert(fs);
 
329
        fc = find_form_control(document, fs);
 
330
        assert(fc);
 
331
 
 
332
        linknum = get_form_control_link(document, fc);
 
333
        /* Hiddens have no link. */
 
334
        if (linknum < 0)
 
335
                return JS_TRUE;
 
336
 
 
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);
 
343
 
 
344
        boolean_to_jsval(ctx, rval, 0);
 
345
        return JS_TRUE;
 
346
}
 
347
 
 
348
static JSBool
 
349
input_focus(JSContext *ctx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
 
350
{
 
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;
 
361
 
 
362
        assert(fs);
 
363
        fc = find_form_control(document, fs);
 
364
        assert(fc);
 
365
 
 
366
        linknum = get_form_control_link(document, fc);
 
367
        /* Hiddens have no link. */
 
368
        if (linknum < 0)
 
369
                return JS_TRUE;
 
370
 
 
371
        jump_to_link_number(ses, doc_view, linknum);
 
372
 
 
373
        boolean_to_jsval(ctx, rval, 0);
 
374
        return JS_TRUE;
 
375
}
 
376
 
 
377
static JSBool
 
378
input_select(JSContext *ctx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
 
379
{
 
380
        /* We support no text selecting yet.  So we do nothing for now.
 
381
         * (That was easy, too.) */
 
382
        return JS_TRUE;
 
383
}
 
384
 
 
385
static JSObject *
 
386
get_input_object(JSContext *ctx, JSObject *jsform, struct form_state *fs)
 
387
{
 
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);
 
393
 
 
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;
 
398
        }
 
399
        return fs->ecmascript_obj;
 
400
}
 
401
 
 
402
 
 
403
static JSObject *
 
404
get_form_control_object(JSContext *ctx, JSObject *jsform, enum form_type type, struct form_state *fs)
 
405
{
 
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);
 
418
 
 
419
                case FC_TEXTAREA:
 
420
                case FC_SELECT:
 
421
                        /* TODO */
 
422
                        return NULL;
 
423
 
 
424
                default:
 
425
                        INTERNAL("Weird fc->type %d", type);
 
426
                        return NULL;
 
427
        }
 
428
}
 
429
 
 
430
 
 
431
 
 
432
static JSBool form_elements_get_property(JSContext *ctx, JSObject *obj, jsval id, jsval *vp);
 
433
 
 
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
 
440
};
 
441
 
 
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);
 
444
 
 
445
static const JSFunctionSpec form_elements_funcs[] = {
 
446
        { "item",               form_elements_item,             1 },
 
447
        { "namedItem",          form_elements_namedItem,        1 },
 
448
        { NULL }
 
449
};
 
450
 
 
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 }
 
457
};
 
458
 
 
459
static JSBool
 
460
form_elements_get_property(JSContext *ctx, JSObject *obj, jsval id, jsval *vp)
 
461
{
 
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);
 
470
 
 
471
        if (JSVAL_IS_STRING(id)) {
 
472
                form_elements_namedItem(ctx, obj, 1, &id, vp);
 
473
                return JS_TRUE;
 
474
        }
 
475
 
 
476
        if (!JSVAL_IS_INT(id))
 
477
                return JS_TRUE;
 
478
 
 
479
        undef_to_jsval(ctx, vp);
 
480
 
 
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;
 
489
        }
 
490
 
 
491
        return JS_TRUE;
 
492
}
 
493
 
 
494
static JSBool
 
495
form_elements_item(JSContext *ctx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
 
496
{
 
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;
 
508
 
 
509
        if (argc != 1)
 
510
                return JS_TRUE;
 
511
 
 
512
        index = atol(jsval_to_string(ctx, &argv[0]));
 
513
 
 
514
        undef_to_jsval(ctx, rval);
 
515
 
 
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));
 
520
 
 
521
                        if (fcobj) {
 
522
                                object_to_jsval(ctx, rval, fcobj);
 
523
                        }
 
524
                        break;
 
525
                }
 
526
        }
 
527
 
 
528
        return JS_TRUE;
 
529
}
 
530
 
 
531
static JSBool
 
532
form_elements_namedItem(JSContext *ctx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
 
533
{
 
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;
 
544
 
 
545
        if (argc != 1)
 
546
                return JS_TRUE;
 
547
 
 
548
        string = jsval_to_string(ctx, &argv[0]);
 
549
        if (!*string)
 
550
                return JS_TRUE;
 
551
 
 
552
        undef_to_jsval(ctx, rval);
 
553
 
 
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));
 
557
 
 
558
                        if (fcobj) {
 
559
                                object_to_jsval(ctx, rval, fcobj);
 
560
                        }
 
561
                        break;
 
562
                }
 
563
        }
 
564
 
 
565
        return JS_TRUE;
 
566
}
 
567
 
 
568
 
 
569
 
 
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);
 
572
 
 
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
 
579
};
 
580
 
 
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
 
589
};
 
590
 
 
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 }
 
600
};
 
601
 
 
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);
 
604
 
 
605
static const JSFunctionSpec form_funcs[] = {
 
606
        { "reset",      form_reset,     0 },
 
607
        { "submit",     form_submit,    0 },
 
608
        { NULL }
 
609
};
 
610
 
 
611
static JSBool
 
612
form_get_property(JSContext *ctx, JSObject *obj, jsval id, jsval *vp)
 
613
{
 
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);
 
621
 
 
622
        assert(form);
 
623
 
 
624
        if (JSVAL_IS_STRING(id)) {
 
625
                struct form_control *fc;
 
626
                unsigned char *string;
 
627
 
 
628
                string = jsval_to_string(ctx, &id);
 
629
                foreach (fc, form->items) {
 
630
                        JSObject *fcobj = NULL;
 
631
 
 
632
                        if (!fc->name || strcasecmp(string, fc->name))
 
633
                                continue;
 
634
 
 
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);
 
640
                        }
 
641
                        break;
 
642
                }
 
643
                return JS_TRUE;
 
644
        }
 
645
 
 
646
        if (!JSVAL_IS_INT(id))
 
647
                return JS_TRUE;
 
648
 
 
649
        undef_to_jsval(ctx, vp);
 
650
 
 
651
        switch (JSVAL_TO_INT(id)) {
 
652
        case JSP_FORM_ACTION:
 
653
                string_to_jsval(ctx, vp, form->action);
 
654
                break;
 
655
 
 
656
        case JSP_FORM_ELEMENTS:
 
657
        {
 
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);
 
660
 
 
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. */
 
666
        }
 
667
                break;
 
668
 
 
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;
 
681
                }
 
682
                break;
 
683
 
 
684
        case JSP_FORM_LENGTH:
 
685
                int_to_jsval(ctx, vp, list_size(&form->items));
 
686
                break;
 
687
 
 
688
        case JSP_FORM_METHOD:
 
689
                switch (form->method) {
 
690
                case FORM_METHOD_GET:
 
691
                        string_to_jsval(ctx, vp, "GET");
 
692
                        break;
 
693
 
 
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;
 
699
                }
 
700
                break;
 
701
 
 
702
        case JSP_FORM_NAME:
 
703
                string_to_jsval(ctx, vp, form->name);
 
704
                break;
 
705
 
 
706
        case JSP_FORM_TARGET:
 
707
                string_to_jsval(ctx, vp, form->target);
 
708
                break;
 
709
 
 
710
        default:
 
711
                INTERNAL("Invalid ID %d in form_get_property().", JSVAL_TO_INT(id));
 
712
                break;
 
713
        }
 
714
 
 
715
        return JS_TRUE;
 
716
}
 
717
 
 
718
static JSBool
 
719
form_set_property(JSContext *ctx, JSObject *obj, jsval id, jsval *vp)
 
720
{
 
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;
 
728
 
 
729
        assert(form);
 
730
 
 
731
        if (!JSVAL_IS_INT(id))
 
732
                return JS_TRUE;
 
733
 
 
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;
 
738
 
 
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;
 
748
                }
 
749
                break;
 
750
 
 
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;
 
757
                }
 
758
                break;
 
759
 
 
760
        case JSP_FORM_NAME:
 
761
                mem_free_set(&form->name, stracpy(jsval_to_string(ctx, vp)));
 
762
                break;
 
763
 
 
764
        case JSP_FORM_TARGET:
 
765
                mem_free_set(&form->target, stracpy(jsval_to_string(ctx, vp)));
 
766
                break;
 
767
 
 
768
        default:
 
769
                INTERNAL("Invalid ID %d in form_set_property().", JSVAL_TO_INT(id));
 
770
                break;
 
771
        }
 
772
 
 
773
        return JS_TRUE;
 
774
}
 
775
 
 
776
static JSBool
 
777
form_reset(JSContext *ctx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
 
778
{
 
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);
 
785
 
 
786
        assert(form);
 
787
 
 
788
        do_reset_form(doc_view, form);
 
789
        draw_forms(doc_view->session->tab->term, doc_view);
 
790
 
 
791
        boolean_to_jsval(ctx, rval, 0);
 
792
 
 
793
        return JS_TRUE;
 
794
}
 
795
 
 
796
static JSBool
 
797
form_submit(JSContext *ctx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
 
798
{
 
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);
 
806
 
 
807
        assert(form);
 
808
        submit_given_form(ses, doc_view, form);
 
809
 
 
810
        boolean_to_jsval(ctx, rval, 0);
 
811
 
 
812
        return JS_TRUE;
 
813
}
 
814
 
 
815
JSObject *
 
816
get_form_object(JSContext *ctx, JSObject *jsdoc, struct form_view *fv)
 
817
{
 
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);
 
823
 
 
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;
 
828
        }
 
829
        return fv->ecmascript_obj;
 
830
}
 
831
 
 
832
 
 
833
static JSBool forms_get_property(JSContext *ctx, JSObject *obj, jsval id, jsval *vp);
 
834
 
 
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
 
841
};
 
842
 
 
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);
 
845
 
 
846
const JSFunctionSpec forms_funcs[] = {
 
847
        { "item",               forms_item,             1 },
 
848
        { "namedItem",          forms_namedItem,        1 },
 
849
        { NULL }
 
850
};
 
851
 
 
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 }
 
858
};
 
859
 
 
860
static JSBool
 
861
forms_get_property(JSContext *ctx, JSObject *obj, jsval id, jsval *vp)
 
862
{
 
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;
 
868
 
 
869
        if (JSVAL_IS_STRING(id)) {
 
870
                forms_namedItem(ctx, obj, 1, &id, vp);
 
871
                return JS_TRUE;
 
872
        }
 
873
 
 
874
        if (!JSVAL_IS_INT(id))
 
875
                return JS_TRUE;
 
876
 
 
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;
 
885
        }
 
886
 
 
887
        return JS_TRUE;
 
888
}
 
889
 
 
890
static JSBool
 
891
forms_item(JSContext *ctx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
 
892
{
 
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;
 
899
 
 
900
        if (argc != 1)
 
901
                return JS_TRUE;
 
902
 
 
903
        index = atol(jsval_to_string(ctx, &argv[0]));
 
904
 
 
905
        undef_to_jsval(ctx, rval);
 
906
 
 
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;
 
912
                }
 
913
        }
 
914
 
 
915
        return JS_TRUE;
 
916
}
 
917
 
 
918
static JSBool
 
919
forms_namedItem(JSContext *ctx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
 
920
{
 
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;
 
928
 
 
929
        if (argc != 1)
 
930
                return JS_TRUE;
 
931
 
 
932
        undef_to_jsval(ctx, rval);
 
933
 
 
934
        string = jsval_to_string(ctx, &argv[0]);
 
935
        if (!*string)
 
936
                return JS_TRUE;
 
937
 
 
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;
 
943
                }
 
944
        }
 
945
 
 
946
        return JS_TRUE;
 
947
}