NetSurf
forms.c
Go to the documentation of this file.
1/*
2 * Copyright 2011 Vincent Sanders <vince@netsurf-browser.org>
3 *
4 * This file is part of NetSurf, http://www.netsurf-browser.org/
5 *
6 * NetSurf is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; version 2 of the License.
9 *
10 * NetSurf is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19/**
20 * \file
21 * HTML form handling implementation
22 */
23
24#include <string.h>
25
26#include "utils/config.h"
27#include "utils/corestrings.h"
28#include "utils/log.h"
29
30#include "html/form_internal.h"
31#include "html/private.h"
32
33/**
34 * process form element from dom
35 */
36static struct form *
37parse_form_element(const char *docenc, dom_node *node)
38{
39 dom_string *ds_action = NULL;
40 dom_string *ds_charset = NULL;
41 dom_string *ds_target = NULL;
42 dom_string *ds_method = NULL;
43 dom_string *ds_enctype = NULL;
44 char *action = NULL, *charset = NULL, *target = NULL;
46 dom_html_form_element *formele = (dom_html_form_element *)(node);
47 struct form * ret = NULL;
48
49 /* Retrieve the attributes from the node */
50 if (dom_html_form_element_get_action(formele,
51 &ds_action) != DOM_NO_ERR)
52 goto out;
53
54 if (dom_html_form_element_get_accept_charset(formele,
55 &ds_charset) != DOM_NO_ERR)
56 goto out;
57
58 if (dom_html_form_element_get_target(formele,
59 &ds_target) != DOM_NO_ERR)
60 goto out;
61
62 if (dom_html_form_element_get_method(formele,
63 &ds_method) != DOM_NO_ERR)
64 goto out;
65
66 if (dom_html_form_element_get_enctype(formele,
67 &ds_enctype) != DOM_NO_ERR)
68 goto out;
69
70 /* Extract the plain attributes ready for use. We have to do this
71 * because we cannot guarantee that the dom_strings are NULL terminated
72 * and thus we copy them.
73 */
74 if (ds_action != NULL)
75 action = strndup(dom_string_data(ds_action),
76 dom_string_byte_length(ds_action));
77
78 if (ds_charset != NULL)
79 charset = strndup(dom_string_data(ds_charset),
80 dom_string_byte_length(ds_charset));
81
82 if (ds_target != NULL)
83 target = strndup(dom_string_data(ds_target),
84 dom_string_byte_length(ds_target));
85
86 /* Determine the method */
88 if (ds_method != NULL) {
89 if (dom_string_caseless_lwc_isequal(ds_method,
90 corestring_lwc_post)) {
92 if (ds_enctype != NULL) {
93 if (dom_string_caseless_lwc_isequal(ds_enctype,
94 corestring_lwc_multipart_form_data)) {
95
97 }
98 }
99 }
100 }
101
102 /* Construct the form object */
103 ret = form_new(node, action, target, method, charset, docenc);
104
105out:
106 if (ds_action != NULL)
107 dom_string_unref(ds_action);
108 if (ds_charset != NULL)
109 dom_string_unref(ds_charset);
110 if (ds_target != NULL)
111 dom_string_unref(ds_target);
112 if (ds_method != NULL)
113 dom_string_unref(ds_method);
114 if (ds_enctype != NULL)
115 dom_string_unref(ds_enctype);
116 if (action != NULL)
117 free(action);
118 if (charset != NULL)
119 free(charset);
120 if (target != NULL)
121 free(target);
122 return ret;
123}
124
125/* documented in html_internal.h */
126struct form *html_forms_get_forms(const char *docenc, dom_html_document *doc)
127{
128 dom_html_collection *forms;
129 struct form *ret = NULL, *newf;
130 dom_node *node;
131 unsigned long n;
132 uint32_t nforms;
133
134 if (doc == NULL)
135 return NULL;
136
137 /* Attempt to build a set of all the forms */
138 if (dom_html_document_get_forms(doc, &forms) != DOM_NO_ERR)
139 return NULL;
140
141 /* Count the number of forms so we can iterate */
142 if (dom_html_collection_get_length(forms, &nforms) != DOM_NO_ERR)
143 goto out;
144
145 /* Iterate the forms collection, making form structs for returning */
146 for (n = 0; n < nforms; ++n) {
147 if (dom_html_collection_item(forms, n, &node) != DOM_NO_ERR) {
148 goto out;
149 }
150 newf = parse_form_element(docenc, node);
151 dom_node_unref(node);
152 if (newf == NULL) {
153 goto err;
154 }
155 newf->prev = ret;
156 ret = newf;
157 }
158
159 /* All went well */
160 goto out;
161err:
162 while (ret != NULL) {
163 struct form *prev = ret->prev;
164 /* Destroy ret */
165 free(ret);
166 ret = prev;
167 }
168out:
169 /* Finished with the collection, return it */
170 dom_html_collection_unref(forms);
171
172 return ret;
173}
174
175static struct form *
176find_form(struct form *forms, dom_html_form_element *form)
177{
178 while (forms != NULL) {
179 if (forms->node == form)
180 break;
181 forms = forms->prev;
182 }
183
184 return forms;
185}
186
187static struct form_control *
188parse_button_element(struct form *forms, dom_html_button_element *button)
189{
190 struct form_control *control = NULL;
191 dom_exception err;
192 dom_html_form_element *form = NULL;
193 dom_string *ds_type = NULL;
194 dom_string *ds_value = NULL;
195 dom_string *ds_name = NULL;
196
197 err = dom_html_button_element_get_form(button, &form);
198 if (err != DOM_NO_ERR)
199 goto out;
200
201 err = dom_html_button_element_get_type(button, &ds_type);
202 if (err != DOM_NO_ERR)
203 goto out;
204
205 if (ds_type == NULL) {
206 control = form_new_control(button, GADGET_SUBMIT);
207 } else {
208 if (dom_string_caseless_lwc_isequal(ds_type,
209 corestring_lwc_submit)) {
210 control = form_new_control(button, GADGET_SUBMIT);
211 } else if (dom_string_caseless_lwc_isequal(ds_type,
212 corestring_lwc_reset)) {
213 control = form_new_control(button, GADGET_RESET);
214 } else {
215 control = form_new_control(button, GADGET_BUTTON);
216 }
217 }
218
219 if (control == NULL)
220 goto out;
221
222 err = dom_html_button_element_get_value(button, &ds_value);
223 if (err != DOM_NO_ERR)
224 goto out;
225 err = dom_html_button_element_get_name(button, &ds_name);
226 if (err != DOM_NO_ERR)
227 goto out;
228
229 if (ds_value != NULL) {
230 control->value = strndup(
231 dom_string_data(ds_value),
232 dom_string_byte_length(ds_value));
233
234 if (control->value == NULL) {
235 form_free_control(control);
236 control = NULL;
237 goto out;
238 }
239 }
240
241 if (ds_name != NULL) {
242 control->name = strndup(
243 dom_string_data(ds_name),
244 dom_string_byte_length(ds_name));
245
246 if (control->name == NULL) {
247 form_free_control(control);
248 control = NULL;
249 goto out;
250 }
251 }
252
253 if (form != NULL && control != NULL)
254 form_add_control(find_form(forms, form), control);
255
256out:
257 if (form != NULL)
258 dom_node_unref(form);
259 if (ds_type != NULL)
260 dom_string_unref(ds_type);
261 if (ds_value != NULL)
262 dom_string_unref(ds_value);
263 if (ds_name != NULL)
264 dom_string_unref(ds_name);
265
266 return control;
267}
268
269static struct form_control *
270parse_input_element(struct form *forms, dom_html_input_element *input)
271{
272 struct form_control *control = NULL;
273 dom_html_form_element *form = NULL;
274 dom_string *ds_type = NULL;
275 dom_string *ds_name = NULL;
276 dom_string *ds_value = NULL;
277
278 char *name = NULL;
279
280 if (dom_html_input_element_get_form(input, &form) != DOM_NO_ERR)
281 goto out;
282
283 if (dom_html_input_element_get_type(input, &ds_type) != DOM_NO_ERR)
284 goto out;
285
286 if (dom_html_input_element_get_name(input, &ds_name) != DOM_NO_ERR)
287 goto out;
288
289 if (ds_name != NULL)
290 name = strndup(dom_string_data(ds_name),
291 dom_string_byte_length(ds_name));
292
293 if (ds_type != NULL && dom_string_caseless_lwc_isequal(ds_type,
294 corestring_lwc_password)) {
295 control = form_new_control(input, GADGET_PASSWORD);
296 } else if (ds_type != NULL && dom_string_caseless_lwc_isequal(ds_type,
297 corestring_lwc_file)) {
298 control = form_new_control(input, GADGET_FILE);
299 } else if (ds_type != NULL && dom_string_caseless_lwc_isequal(ds_type,
300 corestring_lwc_hidden)) {
301 control = form_new_control(input, GADGET_HIDDEN);
302 } else if (ds_type != NULL && dom_string_caseless_lwc_isequal(ds_type,
303 corestring_lwc_checkbox)) {
304 control = form_new_control(input, GADGET_CHECKBOX);
305 } else if (ds_type != NULL && dom_string_caseless_lwc_isequal(ds_type,
306 corestring_lwc_radio)) {
307 control = form_new_control(input, GADGET_RADIO);
308 } else if (ds_type != NULL && dom_string_caseless_lwc_isequal(ds_type,
309 corestring_lwc_submit)) {
310 control = form_new_control(input, GADGET_SUBMIT);
311 } else if (ds_type != NULL && dom_string_caseless_lwc_isequal(ds_type,
312 corestring_lwc_reset)) {
313 control = form_new_control(input, GADGET_RESET);
314 } else if (ds_type != NULL && dom_string_caseless_lwc_isequal(ds_type,
315 corestring_lwc_button)) {
316 control = form_new_control(input, GADGET_BUTTON);
317 } else if (ds_type != NULL && dom_string_caseless_lwc_isequal(ds_type,
318 corestring_lwc_image)) {
319 control = form_new_control(input, GADGET_IMAGE);
320 } else {
321 control = form_new_control(input, GADGET_TEXTBOX);
322 }
323
324 if (control == NULL)
325 goto out;
326
327 if (name != NULL) {
328 /* Hand the name string over */
329 control->name = name;
330 name = NULL;
331 }
332
333 if (control->type == GADGET_CHECKBOX || control->type == GADGET_RADIO) {
334 bool selected;
335 if (dom_html_input_element_get_checked(
336 input, &selected) == DOM_NO_ERR) {
337 control->selected = selected;
338 }
339 }
340
341 if (control->type == GADGET_PASSWORD ||
342 control->type == GADGET_TEXTBOX) {
343 int32_t maxlength;
344 if (dom_html_input_element_get_max_length(
345 input, &maxlength) != DOM_NO_ERR) {
346 maxlength = -1;
347 }
348
349 if (maxlength >= 0) {
350 /* Got valid maxlength */
351 control->maxlength = maxlength;
352 } else {
353 /* Input has no maxlength attr, or
354 * dom_html_input_element_get_max_length failed.
355 *
356 * Set it to something insane. */
357 control->maxlength = UINT_MAX;
358 }
359 }
360
361 if (control->type != GADGET_FILE && control->type != GADGET_IMAGE) {
362 if (dom_html_input_element_get_value(
363 input, &ds_value) == DOM_NO_ERR) {
364 if (ds_value != NULL) {
365 control->value = strndup(
366 dom_string_data(ds_value),
367 dom_string_byte_length(ds_value));
368 if (control->value == NULL) {
369 form_free_control(control);
370 control = NULL;
371 goto out;
372 }
373 control->length = strlen(control->value);
374 }
375 }
376
377 if (control->type == GADGET_TEXTBOX ||
378 control->type == GADGET_PASSWORD) {
379 if (control->value == NULL) {
380 control->value = strdup("");
381 if (control->value == NULL) {
382 form_free_control(control);
383 control = NULL;
384 goto out;
385 }
386
387 control->length = 0;
388 }
389
390 control->initial_value = strdup(control->value);
391 if (control->initial_value == NULL) {
392 form_free_control(control);
393 control = NULL;
394 goto out;
395 }
396
397 control->last_synced_value = strdup(control->value);
398 if (control->last_synced_value == NULL) {
399 form_free_control(control);
400 control = NULL;
401 goto out;
402 }
403
404 control->node_value = dom_string_ref(ds_value);
405 }
406 /* Force the gadget and DOM to be in sync */
408 }
409
410 if (form != NULL && control != NULL)
411 form_add_control(find_form(forms, form), control);
412
413out:
414 if (form != NULL)
415 dom_node_unref(form);
416 if (ds_type != NULL)
417 dom_string_unref(ds_type);
418 if (ds_name != NULL)
419 dom_string_unref(ds_name);
420 if (ds_value != NULL)
421 dom_string_unref(ds_value);
422
423 if (name != NULL)
424 free(name);
425
426 return control;
427}
428
429static struct form_control *
430parse_textarea_element(struct form *forms, dom_html_text_area_element *ta)
431{
432 struct form_control *control = NULL;
433 dom_html_form_element *form = NULL;
434 dom_string *ds_name = NULL;
435
436 char *name = NULL;
437
438 if (dom_html_text_area_element_get_form(ta, &form) != DOM_NO_ERR)
439 goto out;
440
441 if (dom_html_text_area_element_get_name(ta, &ds_name) != DOM_NO_ERR)
442 goto out;
443
444 if (ds_name != NULL)
445 name = strndup(dom_string_data(ds_name),
446 dom_string_byte_length(ds_name));
447
449
450 if (control == NULL)
451 goto out;
452
453 if (name != NULL) {
454 /* Hand the name string over */
455 control->name = name;
456 name = NULL;
457 }
458
459 if (form != NULL && control != NULL)
460 form_add_control(find_form(forms, form), control);
461
462out:
463 if (form != NULL)
464 dom_node_unref(form);
465 if (ds_name != NULL)
466 dom_string_unref(ds_name);
467
468 if (name != NULL)
469 free(name);
470
471
472 return control;
473}
474
475static struct form_control *
476parse_select_element(struct form *forms, dom_html_select_element *select)
477{
478 struct form_control *control = NULL;
479 dom_html_form_element *form = NULL;
480 dom_string *ds_name = NULL;
481
482 char *name = NULL;
483
484 if (dom_html_select_element_get_form(select, &form) != DOM_NO_ERR)
485 goto out;
486
487 if (dom_html_select_element_get_name(select, &ds_name) != DOM_NO_ERR)
488 goto out;
489
490 if (ds_name != NULL)
491 name = strndup(dom_string_data(ds_name),
492 dom_string_byte_length(ds_name));
493
495
496 if (control == NULL)
497 goto out;
498
499 if (name != NULL) {
500 /* Hand the name string over */
501 control->name = name;
502 name = NULL;
503 }
504
505 dom_html_select_element_get_multiple(select,
506 &(control->data.select.multiple));
507
508 if (form != NULL && control != NULL)
509 form_add_control(find_form(forms, form), control);
510
511out:
512 if (form != NULL)
513 dom_node_unref(form);
514 if (ds_name != NULL)
515 dom_string_unref(ds_name);
516
517 if (name != NULL)
518 free(name);
519
520
521 return control;
522}
523
524
525static struct form_control *
527{
529 if (ctl != NULL) {
530 ctl->value = strdup("");
531 ctl->initial_value = strdup("");
532 ctl->name = strdup("foo");
533
534 if (ctl->value == NULL || ctl->initial_value == NULL ||
535 ctl->name == NULL) {
537 ctl = NULL;
538 }
539 }
540 return ctl;
541}
542
543/* documented in html_internal.h */
544struct form_control *
546{
547 struct form *f;
548 struct form_control *ctl = NULL;
549 dom_exception err;
550 dom_string *ds_name = NULL;
551
552 /* Step one, see if we already have a control */
553 for (f = forms; f != NULL; f = f->prev) {
554 for (ctl = f->controls; ctl != NULL; ctl = ctl->next) {
555 if (ctl->node == node)
556 return ctl;
557 }
558 }
559
560 /* Step two, extract the node's name so we can construct a gadget. */
561 err = dom_element_get_tag_name(node, &ds_name);
562 if (err == DOM_NO_ERR && ds_name != NULL) {
563
564 /* Step three, attempt to work out what gadget to make */
565 if (dom_string_caseless_lwc_isequal(ds_name,
566 corestring_lwc_button)) {
567 ctl = parse_button_element(forms,
568 (dom_html_button_element *) node);
569 } else if (dom_string_caseless_lwc_isequal(ds_name,
570 corestring_lwc_input)) {
571 ctl = parse_input_element(forms,
572 (dom_html_input_element *) node);
573 } else if (dom_string_caseless_lwc_isequal(ds_name,
574 corestring_lwc_textarea)) {
575 ctl = parse_textarea_element(forms,
576 (dom_html_text_area_element *) node);
577 } else if (dom_string_caseless_lwc_isequal(ds_name,
578 corestring_lwc_select)) {
579 ctl = parse_select_element(forms,
580 (dom_html_select_element *) node);
581 }
582 }
583
584 /* If all else fails, fake gadget time */
585 if (ctl == NULL)
587
588 if (ds_name != NULL)
589 dom_string_unref(ds_name);
590
591 return ctl;
592}
char * strndup(const char *s, size_t n)
Duplicate up to n characters of a string.
Definition: utils.c:332
Useful interned string pointers (interface).
void form_gadget_sync_with_dom(struct form_control *control)
Synchronise this gadget with its associated DOM node.
Definition: form.c:2170
struct form_control * form_new_control(void *node, form_control_type type)
Create a struct form_control.
Definition: form.c:2349
void form_add_control(struct form *form, struct form_control *control)
Add a control to the list of controls in a form.
Definition: form.c:1433
struct form * form_new(void *node, const char *action, const char *target, form_method method, const char *charset, const char *doc_charset)
Create a struct form.
Definition: form.c:2276
void form_free_control(struct form_control *control)
Free a struct form_control.
Definition: form.c:1455
Interface to form handling functions internal to HTML content handler.
@ GADGET_SUBMIT
Definition: form_internal.h:56
@ GADGET_TEXTAREA
Definition: form_internal.h:53
@ GADGET_HIDDEN
Definition: form_internal.h:48
@ GADGET_RESET
Definition: form_internal.h:57
@ GADGET_BUTTON
Definition: form_internal.h:59
@ GADGET_IMAGE
Definition: form_internal.h:54
@ GADGET_PASSWORD
Definition: form_internal.h:55
@ GADGET_CHECKBOX
Definition: form_internal.h:51
@ GADGET_FILE
Definition: form_internal.h:58
@ GADGET_SELECT
Definition: form_internal.h:52
@ GADGET_TEXTBOX
Definition: form_internal.h:49
@ GADGET_RADIO
Definition: form_internal.h:50
form_method
Form submit method.
@ method_POST_URLENC
POST, url encoded.
@ method_POST_MULTIPART
POST, multipart/form-data.
@ method_GET
GET, always url encoded.
struct form * html_forms_get_forms(const char *docenc, dom_html_document *doc)
Definition: forms.c:126
static struct form_control * invent_fake_gadget(dom_node *node)
Definition: forms.c:526
static struct form * parse_form_element(const char *docenc, dom_node *node)
process form element from dom
Definition: forms.c:37
static struct form_control * parse_textarea_element(struct form *forms, dom_html_text_area_element *ta)
Definition: forms.c:430
static struct form_control * parse_select_element(struct form *forms, dom_html_select_element *select)
Definition: forms.c:476
struct form_control * html_forms_get_control_for_node(struct form *forms, dom_node *node)
Definition: forms.c:545
static struct form_control * parse_button_element(struct form *forms, dom_html_button_element *button)
Definition: forms.c:188
static struct form_control * parse_input_element(struct form *forms, dom_html_input_element *input)
Definition: forms.c:270
static struct form * find_form(struct form *forms, dom_html_form_element *form)
Definition: forms.c:176
Private data for text/html content.
Interface to utility string handling.
Form control.
Definition: form_internal.h:73
char * last_synced_value
The last value sync'd to the DOM.
Definition: form_internal.h:86
struct form_textarea_data data
struct form_control::@132::@134 select
form_control_type type
Type of control.
Definition: form_internal.h:79
void * node
Corresponding DOM node.
Definition: form_internal.h:74
struct dom_string * node_value
The last value sync'd with the DOM.
Definition: form_internal.h:75
char * name
Control name.
Definition: form_internal.h:83
struct form_control * next
Next control in this form.
unsigned int length
Number of characters in control.
Definition: form_internal.h:91
struct textarea * ta
char * initial_value
Initial value of control.
Definition: form_internal.h:85
char * value
Current value of control.
Definition: form_internal.h:84
bool selected
Whether control is selected.
Definition: form_internal.h:94
unsigned int maxlength
Maximum characters permitted.
Definition: form_internal.h:92
HTML form.
form_method method
Method and enctype.
void * node
Corresponding DOM node.
char * target
Target to submit to.
struct form_control * controls
Linked list of controls.
char * action
Absolute URL to submit to.
struct form * prev
Previous form in doc.