NetSurf
form.c
Go to the documentation of this file.
1/*
2 * Copyright 2004 James Bursa <bursa@users.sourceforge.net>
3 * Copyright 2003 Phil Mellor <monkeyson@users.sourceforge.net>
4 * Copyright 2004 John Tytgat <joty@netsurf-browser.org>
5 * Copyright 2005-9 John-Mark Bell <jmb@netsurf-browser.org>
6 * Copyright 2009 Paul Blokus <paul_pl@users.sourceforge.net>
7 * Copyright 2010 Michael Drake <tlsa@netsurf-browser.org>
8 *
9 * This file is part of NetSurf, http://www.netsurf-browser.org/
10 *
11 * NetSurf is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; version 2 of the License.
14 *
15 * NetSurf is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program. If not, see <http://www.gnu.org/licenses/>.
22 */
23
24/**
25 * \file
26 * Form handling functions (implementation).
27 */
28
29#include <assert.h>
30#include <limits.h>
31#include <stdbool.h>
32#include <stdio.h>
33#include <string.h>
34#include <dom/dom.h>
35
36#include "utils/corestrings.h"
37#include "utils/log.h"
38#include "utils/messages.h"
39#include "utils/talloc.h"
40#include "utils/url.h"
41#include "utils/utf8.h"
42#include "utils/ascii.h"
44#include "netsurf/inttypes.h"
45#include "netsurf/mouse.h"
46#include "netsurf/plotters.h"
47#include "netsurf/misc.h"
48#include "content/fetch.h"
49#include "content/hlcache.h"
50#include "css/utils.h"
51#include "desktop/knockout.h"
52#include "desktop/scrollbar.h"
53#include "desktop/textarea.h"
54
55#include "html/html.h"
56#include "html/private.h"
57#include "html/layout.h"
58#include "html/box.h"
59#include "html/box_inspect.h"
60#include "html/font.h"
61#include "html/form_internal.h"
62
63#define MAX_SELECT_HEIGHT 210
64#define SELECT_LINE_SPACING 0.2
65#define SELECT_BORDER_WIDTH 1
66#define SELECT_SELECTED_COLOUR 0xDB9370
67
72 int f_size;
76 struct content *c;
77};
78
81 .fill_colour = SELECT_SELECTED_COLOUR,
82};
83
86 .weight = 400,
87 .flags = FONTF_NONE,
88 .background = 0xffffff,
89 .foreground = 0x000000,
90};
91
92
93/**
94 * Convert a string from UTF-8 to the specified charset
95 * As a final fallback, this will attempt to convert to ISO-8859-1.
96 *
97 * \todo Return charset used?
98 *
99 * \param item String to convert
100 * \param len Length of string to convert
101 * \param charset Destination charset
102 * \param fallback Fallback charset (may be NULL),
103 * used iff converting to charset fails
104 * \return Pointer to converted string (on heap, caller frees), or NULL
105 */
106static char *
107form_encode_item(const char *item,
108 uint32_t len,
109 const char *charset,
110 const char *fallback)
111{
112 nserror err;
113 char *ret = NULL;
114 char cset[256];
115
116 if (!item || !charset)
117 return NULL;
118
119 snprintf(cset, sizeof cset, "%s//TRANSLIT", charset);
120
121 err = utf8_to_enc(item, cset, 0, &ret);
122 if (err == NSERROR_BAD_ENCODING) {
123 /* charset not understood, try without transliteration */
124 snprintf(cset, sizeof cset, "%s", charset);
125 err = utf8_to_enc(item, cset, len, &ret);
126
127 if (err == NSERROR_BAD_ENCODING) {
128 /* nope, try fallback charset (if any) */
129 if (fallback) {
130 snprintf(cset, sizeof cset,
131 "%s//TRANSLIT", fallback);
132 err = utf8_to_enc(item, cset, 0, &ret);
133
134 if (err == NSERROR_BAD_ENCODING) {
135 /* and without transliteration */
136 snprintf(cset, sizeof cset,
137 "%s", fallback);
138 err = utf8_to_enc(item, cset, 0, &ret);
139 }
140 }
141
142 if (err == NSERROR_BAD_ENCODING) {
143 /* that also failed, use 8859-1 */
144 err = utf8_to_enc(item, "ISO-8859-1//TRANSLIT",
145 0, &ret);
146 if (err == NSERROR_BAD_ENCODING) {
147 /* and without transliteration */
148 err = utf8_to_enc(item, "ISO-8859-1",
149 0, &ret);
150 }
151 }
152 }
153 }
154 if (err == NSERROR_NOMEM) {
155 return NULL;
156 }
157
158 return ret;
159}
160
161
162/**
163 * string allocation size for numeric values in multipart data
164 */
165#define FETCH_DATA_INT_VALUE_SIZE 20
166
167
168/**
169 * append split key name and integer value to a multipart data list
170 *
171 * \param name key name
172 * \param ksfx key name suffix
173 * \param value The value to encode
174 * \param fetch_data_next_ptr The multipart data list to append to.
175 */
176static nserror
178 const char *ksfx,
179 int value,
180 struct fetch_multipart_data ***fetch_data_next_ptr)
181{
182 struct fetch_multipart_data *fetch_data;
183 int keysize;
184
185 fetch_data = calloc(1, sizeof(*fetch_data));
186 if (fetch_data == NULL) {
187 NSLOG(netsurf, INFO, "failed allocation for fetch data");
188 return NSERROR_NOMEM;
189 }
190
191 /* key name */
192 keysize = snprintf(fetch_data->name, 0, "%s%s", name, ksfx);
193 fetch_data->name = malloc(keysize + 1); /* allow for null */
194 if (fetch_data->name == NULL) {
195 free(fetch_data);
196 NSLOG(netsurf, INFO,
197 "keyname allocation failure for %s%s", name, ksfx);
198 return NSERROR_NOMEM;
199 }
200 snprintf(fetch_data->name, keysize + 1, "%s%s", name, ksfx);
201
202 /* value */
203 fetch_data->value = malloc(FETCH_DATA_INT_VALUE_SIZE);
204 if (fetch_data->value == NULL) {
205 free(fetch_data->name);
206 free(fetch_data);
207 NSLOG(netsurf, INFO, "value allocation failure");
208 return NSERROR_NOMEM;
209 }
210 snprintf(fetch_data->value, FETCH_DATA_INT_VALUE_SIZE, "%d", value);
211
212 /* link into list */
213 **fetch_data_next_ptr = fetch_data;
214 *fetch_data_next_ptr = &fetch_data->next;
215
216 return NSERROR_OK;
217}
218
219
220/**
221 * append DOM string name/value pair to a multipart data list
222 *
223 * \param name key name
224 * \param value the value to associate with the key
225 * \param rawfile the raw file value to associate with the key.
226 * \param form_charset The form character set
227 * \param docu_charset The document character set for fallback
228 * \param fetch_data_next_ptr The multipart data list being constructed.
229 * \return NSERROR_OK on success or appropriate error code.
230 */
231static nserror
233 dom_string *value,
234 const char *rawfile,
235 const char *form_charset,
236 const char *docu_charset,
237 struct fetch_multipart_data ***fetch_data_next_ptr)
238{
239 struct fetch_multipart_data *fetch_data;
240
241 assert(name != NULL);
242
243 fetch_data = calloc(1, sizeof(*fetch_data));
244 if (fetch_data == NULL) {
245 NSLOG(netsurf, INFO, "failed allocation for fetch data");
246 return NSERROR_NOMEM;
247 }
248
249 fetch_data->name = form_encode_item(dom_string_data(name),
250 dom_string_byte_length(name),
251 form_charset,
252 docu_charset);
253 if (fetch_data->name == NULL) {
254 NSLOG(netsurf, INFO, "Could not encode name for fetch data");
255 free(fetch_data);
256 return NSERROR_NOMEM;
257 }
258
259 if (value == NULL) {
260 fetch_data->value = strdup("");
261 } else {
262 fetch_data->value = form_encode_item(dom_string_data(value),
263 dom_string_byte_length(value),
264 form_charset,
265 docu_charset);
266 }
267 if (fetch_data->value == NULL) {
268 NSLOG(netsurf, INFO, "Could not encode value for fetch data");
269 free(fetch_data->name);
270 free(fetch_data);
271 return NSERROR_NOMEM;
272 }
273
274 /* deal with raw file name */
275 if (rawfile != NULL) {
276 fetch_data->file = true;
277 fetch_data->rawfile = strdup(rawfile);
278 if (fetch_data->rawfile == NULL) {
279 NSLOG(netsurf, INFO,
280 "Could not encode rawfile value for fetch data");
281 free(fetch_data->value);
282 free(fetch_data->name);
283 free(fetch_data);
284 return NSERROR_NOMEM;
285 }
286 }
287
288 /* link into list */
289 **fetch_data_next_ptr = fetch_data;
290 *fetch_data_next_ptr = &fetch_data->next;
291
292 return NSERROR_OK;
293}
294
295
296/**
297 * process form HTMLTextAreaElement into multipart data.
298 *
299 * \param text_area_element The form select DOM element to convert.
300 * \param form_charset The form character set
301 * \param doc_charset The document character set for fallback
302 * \param fetch_data_next_ptr The multipart data list being constructed.
303 * \return NSERROR_OK on success or appropriate error code.
304 */
305static nserror
306form_dom_to_data_textarea(dom_html_text_area_element *text_area_element,
307 const char *form_charset,
308 const char *doc_charset,
309 struct fetch_multipart_data ***fetch_data_next_ptr)
310{
311 dom_exception exp; /* the result from DOM operations */
312 bool element_disabled;
313 dom_string *inputname;
314 dom_string *inputvalue;
315 nserror res;
316
317 /* check if element is disabled */
318 exp = dom_html_text_area_element_get_disabled(text_area_element,
319 &element_disabled);
320 if (exp != DOM_NO_ERR) {
321 NSLOG(netsurf, INFO,
322 "Could not get text area disabled property. exp %d", exp);
323 return NSERROR_DOM;
324 }
325
326 if (element_disabled) {
327 /* allow enumeration to continue after disabled element */
328 return NSERROR_OK;
329 }
330
331 /* obtain name property */
332 exp = dom_html_text_area_element_get_name(text_area_element,
333 &inputname);
334 if (exp != DOM_NO_ERR) {
335 NSLOG(netsurf, INFO,
336 "Could not get text area name property. exp %d", exp);
337 return NSERROR_DOM;
338 }
339
340 if (inputname == NULL) {
341 /* allow enumeration to continue after element with no name */
342 return NSERROR_OK;
343 }
344
345 /* obtain text area value */
346 exp = dom_html_text_area_element_get_value(text_area_element,
347 &inputvalue);
348 if (exp != DOM_NO_ERR) {
349 NSLOG(netsurf, INFO,
350 "Could not get text area content. exp %d", exp);
351 dom_string_unref(inputname);
352 return NSERROR_DOM;
353 }
354
355 /* add key/value pair to fetch data list */
356 res = fetch_data_list_add(inputname,
357 inputvalue,
358 NULL,
359 form_charset,
360 doc_charset,
361 fetch_data_next_ptr);
362
363 dom_string_unref(inputvalue);
364 dom_string_unref(inputname);
365
366 return res;
367}
368
369
370static nserror
371form_dom_to_data_select_option(dom_html_option_element *option_element,
372 dom_string *keyname,
373 const char *form_charset,
374 const char *docu_charset,
375 struct fetch_multipart_data ***fetch_data_next_ptr)
376{
377 nserror res;
378 dom_exception exp; /* the result from DOM operations */
379 dom_string *value;
380 bool selected;
381
382 exp = dom_html_option_element_get_selected(option_element, &selected);
383 if (exp != DOM_NO_ERR) {
384 NSLOG(netsurf, INFO, "Could not get option selected property");
385 return NSERROR_DOM;
386 }
387
388 if (!selected) {
389 /* unselected options do not add fetch data entries */
390 return NSERROR_OK;
391 }
392
393 exp = dom_html_option_element_get_value(option_element, &value);
394 if (exp != DOM_NO_ERR) {
395 NSLOG(netsurf, INFO, "Could not get option value");
396 return NSERROR_DOM;
397 }
398
399 /* add key/value pair to fetch data list */
400 res = fetch_data_list_add(keyname,
401 value,
402 NULL,
403 form_charset,
404 docu_charset,
405 fetch_data_next_ptr);
406
407 dom_string_unref(value);
408
409 return res;
410}
411
412
413/**
414 * process form HTMLSelectElement into multipart data.
415 *
416 * \param select_element The form select DOM element to convert.
417 * \param form_charset The form character set
418 * \param doc_charset The document character set for fallback
419 * \param fetch_data_next_ptr The multipart data list being constructed.
420 * \return NSERROR_OK on success or appropriate error code.
421 */
422static nserror
423form_dom_to_data_select(dom_html_select_element *select_element,
424 const char *form_charset,
425 const char *doc_charset,
426 struct fetch_multipart_data ***fetch_data_next_ptr)
427{
428 nserror res = NSERROR_OK;
429 dom_exception exp; /* the result from DOM operations */
430 bool element_disabled;
431 dom_string *inputname;
432 dom_html_options_collection *options = NULL;
433 uint32_t options_count;
434 uint32_t option_index;
435 dom_node *option_element = NULL;
436
437 /* check if element is disabled */
438 exp = dom_html_select_element_get_disabled(select_element,
439 &element_disabled);
440 if (exp != DOM_NO_ERR) {
441 NSLOG(netsurf, INFO,
442 "Could not get select disabled property. exp %d", exp);
443 return NSERROR_DOM;
444 }
445
446 if (element_disabled) {
447 /* allow enumeration to continue after disabled element */
448 return NSERROR_OK;
449 }
450
451 /* obtain name property */
452 exp = dom_html_select_element_get_name(select_element, &inputname);
453 if (exp != DOM_NO_ERR) {
454 NSLOG(netsurf, INFO,
455 "Could not get select name property. exp %d", exp);
456 return NSERROR_DOM;
457 }
458
459 if (inputname == NULL) {
460 /* allow enumeration to continue after element with no name */
461 return NSERROR_OK;
462 }
463
464 /* get options collection */
465 exp = dom_html_select_element_get_options(select_element, &options);
466 if (exp != DOM_NO_ERR) {
467 NSLOG(netsurf, INFO,
468 "Could not get select options collection");
469 dom_string_unref(inputname);
470 return NSERROR_DOM;
471 }
472
473 /* get options collection length */
474 exp = dom_html_options_collection_get_length(options, &options_count);
475 if (exp != DOM_NO_ERR) {
476 NSLOG(netsurf, INFO,
477 "Could not get select options collection length");
478 dom_html_options_collection_unref(options);
479 dom_string_unref(inputname);
480 return NSERROR_DOM;
481 }
482
483 /* iterate over options collection */
484 for (option_index = 0; option_index < options_count; ++option_index) {
485 exp = dom_html_options_collection_item(options,
486 option_index,
487 &option_element);
488 if (exp != DOM_NO_ERR) {
489 NSLOG(netsurf, INFO,
490 "Could not get options item %"PRId32, option_index);
491 res = NSERROR_DOM;
492 } else {
494 (dom_html_option_element *)option_element,
495 inputname,
496 form_charset,
497 doc_charset,
498 fetch_data_next_ptr);
499
500 dom_node_unref(option_element);
501 }
502
503 if (res != NSERROR_OK) {
504 break;
505 }
506 }
507
508 dom_html_options_collection_unref(options);
509 dom_string_unref(inputname);
510
511 return res;
512}
513
514
515static nserror
516form_dom_to_data_input_submit(dom_html_input_element *input_element,
517 dom_string *inputname,
518 const char *charset,
519 const char *document_charset,
520 dom_html_element **submit_button,
521 struct fetch_multipart_data ***fetch_data_next_ptr)
522{
523 dom_exception exp; /* the result from DOM operations */
524 dom_string *inputvalue;
525 nserror res;
526
527 if (*submit_button == NULL) {
528 /* caller specified no button so use this one */
529 *submit_button = (dom_html_element *)input_element;
530 } else if (*submit_button != (dom_html_element *)input_element) {
531 return NSERROR_OK;
532 }
533
534 /* matched button used to submit form */
535 exp = dom_html_input_element_get_value(input_element, &inputvalue);
536 if (exp != DOM_NO_ERR) {
537 NSLOG(netsurf, INFO, "Could not get submit button value");
538 return NSERROR_DOM;
539 }
540
541 /* add key/value pair to fetch data list */
542 res = fetch_data_list_add(inputname,
543 inputvalue,
544 NULL,
545 charset,
546 document_charset,
547 fetch_data_next_ptr);
548
549 dom_string_unref(inputvalue);
550
551 return res;
552}
553
554
555static nserror
556form_dom_to_data_input_image(dom_html_input_element *input_element,
557 dom_string *inputname,
558 const char *charset,
559 const char *document_charset,
560 dom_html_element **submit_button,
561 struct fetch_multipart_data ***fetch_data_next_ptr)
562{
563 nserror res;
564 dom_exception exp; /* the result from DOM operations */
565 struct image_input_coords *coords;
566 char *basename;
567
568 /* Only use an image input if it was the thing which activated us */
569 if (*submit_button != (dom_html_element *)input_element) {
570 return NSERROR_OK;
571 }
572
573 exp = dom_node_get_user_data((dom_node *)input_element,
574 corestring_dom___ns_key_image_coords_node_data,
575 &coords);
576 if (exp != DOM_NO_ERR) {
577 NSLOG(netsurf, INFO, "Could not get image XY data");
578 return NSERROR_DOM;
579 }
580
581 if (coords == NULL) {
582 NSLOG(netsurf, INFO, "No XY data on the image input");
583 return NSERROR_DOM;
584 }
585
586 /* encode input name once */
587 basename = form_encode_item(dom_string_data(inputname),
588 dom_string_byte_length(inputname),
589 charset,
590 document_charset);
591 if (basename == NULL) {
592 NSLOG(netsurf, INFO, "Could not encode basename");
593 return NSERROR_NOMEM;
594 }
595
596 res = fetch_data_list_add_sname(basename, ".x",
597 coords->x,
598 fetch_data_next_ptr);
599
600 if (res == NSERROR_OK) {
601 res = fetch_data_list_add_sname(basename, ".y",
602 coords->y,
603 fetch_data_next_ptr);
604 }
605
606 free(basename);
607
608 return res;
609}
610
611
612static nserror
613form_dom_to_data_input_checkbox(dom_html_input_element *input_element,
614 dom_string *inputname,
615 const char *charset,
616 const char *document_charset,
617 struct fetch_multipart_data ***fetch_data_next_ptr)
618{
619 nserror res;
620 dom_exception exp; /* the result from DOM operations */
621 bool checked;
622 dom_string *inputvalue;
623
624 exp = dom_html_input_element_get_checked(input_element, &checked);
625 if (exp != DOM_NO_ERR) {
626 NSLOG(netsurf, INFO,
627 "Could not get input element checked");
628 return NSERROR_DOM;
629 }
630
631 if (!checked) {
632 /* unchecked items do not generate a data entry */
633 return NSERROR_OK;
634 }
635
636 exp = dom_html_input_element_get_value(input_element, &inputvalue);
637 if (exp != DOM_NO_ERR) {
638 NSLOG(netsurf, INFO,
639 "Could not get input element value");
640 return NSERROR_DOM;
641 }
642
643 /* ensure a default value */
644 if (inputvalue == NULL) {
645 inputvalue = dom_string_ref(corestring_dom_on);
646 }
647
648 /* add key/value pair to fetch data list */
649 res = fetch_data_list_add(inputname,
650 inputvalue,
651 NULL,
652 charset,
653 document_charset,
654 fetch_data_next_ptr);
655
656 dom_string_unref(inputvalue);
657
658 return res;
659}
660
661
662static nserror
663form_dom_to_data_input_file(dom_html_input_element *input_element,
664 dom_string *inputname,
665 const char *charset,
666 const char *document_charset,
667 struct fetch_multipart_data ***fetch_data_next_ptr)
668{
669 nserror res;
670 dom_exception exp; /* the result from DOM operations */
671 dom_string *inputvalue;
672 const char *rawfile = NULL;
673
674 exp = dom_html_input_element_get_value(input_element, &inputvalue);
675 if (exp != DOM_NO_ERR) {
676 NSLOG(netsurf, INFO, "Could not get file value");
677 return NSERROR_DOM;
678 }
679
680 exp = dom_node_get_user_data((dom_node *)input_element,
681 corestring_dom___ns_key_file_name_node_data,
682 &rawfile);
683 if (exp != DOM_NO_ERR) {
684 NSLOG(netsurf, INFO, "Could not get file rawname");
685 return NSERROR_DOM;
686 }
687
688 if (rawfile == NULL) {
689 rawfile = "";
690 }
691
692 /* add key/value pair to fetch data list */
693 res = fetch_data_list_add(inputname,
694 inputvalue,
695 rawfile,
696 charset,
697 document_charset,
698 fetch_data_next_ptr);
699
700 dom_string_unref(inputvalue);
701
702 return res;
703}
704
705
706static nserror
707form_dom_to_data_input_text(dom_html_input_element *input_element,
708 dom_string *inputname,
709 const char *charset,
710 const char *document_charset,
711 struct fetch_multipart_data ***fetch_data_next_ptr)
712{
713 nserror res;
714 dom_exception exp; /* the result from DOM operations */
715 dom_string *inputvalue;
716
717 exp = dom_html_input_element_get_value(input_element, &inputvalue);
718 if (exp != DOM_NO_ERR) {
719 NSLOG(netsurf, INFO, "Could not get input value");
720 return NSERROR_DOM;
721 }
722
723 /* add key/value pair to fetch data list */
724 res = fetch_data_list_add(inputname,
725 inputvalue,
726 NULL,
727 charset,
728 document_charset,
729 fetch_data_next_ptr);
730
731 dom_string_unref(inputvalue);
732
733 return res;
734}
735
736
737/**
738 * process form input element into multipart data.
739 *
740 * \param input_element The form input DOM element to convert.
741 * \param charset The form character set
742 * \param document_charset The document character set for fallback
743 * \param submit_button The DOM element of the button submitting the form
744 * \param had_submit A boolean value indicating if the submit button
745 * has already been processed in the form element enumeration.
746 * \param fetch_data_next_ptr The multipart data list being constructed.
747 * \return NSERROR_OK on success or appropriate error code.
748 */
749static nserror
750form_dom_to_data_input(dom_html_input_element *input_element,
751 const char *charset,
752 const char *document_charset,
753 dom_html_element **submit_button,
754 struct fetch_multipart_data ***fetch_data_next_ptr)
755{
756 dom_exception exp; /* the result from DOM operations */
757 bool element_disabled;
758 dom_string *inputname;
759 dom_string *inputtype;
760 nserror res;
761
762 /* check if element is disabled */
763 exp = dom_html_input_element_get_disabled(input_element,
764 &element_disabled);
765 if (exp != DOM_NO_ERR) {
766 NSLOG(netsurf, INFO,
767 "Could not get input disabled property. exp %d", exp);
768 return NSERROR_DOM;
769 }
770
771 if (element_disabled) {
772 /* disabled element requires no more processing */
773 return NSERROR_OK;
774 }
775
776 /* obtain name property */
777 exp = dom_html_input_element_get_name(input_element, &inputname);
778 if (exp != DOM_NO_ERR) {
779 NSLOG(netsurf, INFO,
780 "Could not get input name property. exp %d", exp);
781 return NSERROR_DOM;
782 }
783
784 if (inputname == NULL) {
785 /* element with no name is not converted */
786 return NSERROR_OK;
787 }
788
789 /* get input type */
790 exp = dom_html_input_element_get_type(input_element, &inputtype);
791 if (exp != DOM_NO_ERR) {
792 NSLOG(netsurf, INFO, "Could not get input element type");
793 dom_string_unref(inputname);
794 return NSERROR_DOM;
795 }
796
797 /* process according to input element type */
798 if (dom_string_caseless_isequal(inputtype, corestring_dom_submit)) {
799
800 res = form_dom_to_data_input_submit(input_element,
801 inputname,
802 charset,
803 document_charset,
804 submit_button,
805 fetch_data_next_ptr);
806
807 } else if (dom_string_caseless_isequal(inputtype,
808 corestring_dom_image)) {
809
810 res = form_dom_to_data_input_image(input_element,
811 inputname,
812 charset,
813 document_charset,
814 submit_button,
815 fetch_data_next_ptr);
816
817 } else if (dom_string_caseless_isequal(inputtype,
818 corestring_dom_radio) ||
819 dom_string_caseless_isequal(inputtype,
820 corestring_dom_checkbox)) {
821
822 res = form_dom_to_data_input_checkbox(input_element,
823 inputname,
824 charset,
825 document_charset,
826 fetch_data_next_ptr);
827
828 } else if (dom_string_caseless_isequal(inputtype,
829 corestring_dom_file)) {
830
831 res = form_dom_to_data_input_file(input_element,
832 inputname,
833 charset,
834 document_charset,
835 fetch_data_next_ptr);
836
837 } else if (dom_string_caseless_isequal(inputtype,
838 corestring_dom_reset) ||
839 dom_string_caseless_isequal(inputtype,
840 corestring_dom_button)) {
841 /* Skip these */
842 NSLOG(netsurf, INFO, "Skipping RESET and BUTTON");
843 res = NSERROR_OK;
844
845 } else {
846 /* Everything else is treated as text values */
847 res = form_dom_to_data_input_text(input_element,
848 inputname,
849 charset,
850 document_charset,
851 fetch_data_next_ptr);
852
853 }
854
855 dom_string_unref(inputtype);
856 dom_string_unref(inputname);
857
858 return res;
859}
860
861
862/**
863 * process form HTMLButtonElement into multipart data.
864 *
865 * https://html.spec.whatwg.org/multipage/form-elements.html#the-button-element
866 *
867 * \param button_element The form button DOM element to convert.
868 * \param form_charset The form character set
869 * \param doc_charset The document character set for fallback
870 * \param submit_button The DOM element of the button submitting the form
871 * \param fetch_data_next_ptr The multipart data list being constructed.
872 * \return NSERROR_OK on success or appropriate error code.
873 */
874static nserror
875form_dom_to_data_button(dom_html_button_element *button_element,
876 const char *form_charset,
877 const char *doc_charset,
878 dom_html_element **submit_button,
879 struct fetch_multipart_data ***fetch_data_next_ptr)
880{
881 dom_exception exp; /* the result from DOM operations */
882 bool element_disabled;
883 dom_string *inputname;
884 dom_string *inputvalue;
885 dom_string *inputtype;
886 nserror res = NSERROR_OK;
887
888 /* check if element is disabled */
889 exp = dom_html_button_element_get_disabled(button_element,
890 &element_disabled);
891 if (exp != DOM_NO_ERR) {
892 NSLOG(netsurf, INFO,
893 "Unable to get disabled property. exp %d", exp);
894 return NSERROR_DOM;
895 }
896
897 if (element_disabled) {
898 /* allow enumeration to continue after disabled element */
899 return NSERROR_OK;
900 }
901
902 /* get the type attribute */
903 exp = dom_html_button_element_get_type(button_element, &inputtype);
904 if (exp != DOM_NO_ERR) {
905 NSLOG(netsurf, INFO, "Could not get button element type");
906 return NSERROR_DOM;
907 }
908
909 /* If the type attribute is "reset" or "button" the element is
910 * barred from constraint validation. Specification says
911 * default and invalid values result in submit which will
912 * be considered.
913 */
914 if (dom_string_caseless_isequal(inputtype, corestring_dom_reset)) {
915 /* multipart data entry not required for reset type */
916 dom_string_unref(inputtype);
917 return NSERROR_OK;
918 }
919 if (dom_string_caseless_isequal(inputtype, corestring_dom_button)) {
920 /* multipart data entry not required for button type */
921 dom_string_unref(inputtype);
922 return NSERROR_OK;
923 }
924 dom_string_unref(inputtype);
925
926 /* only submision button generates an element */
927 if (*submit_button == NULL) {
928 /* no submission button selected yet so use this one */
929 *submit_button = (dom_html_element *)button_element;
930 }
931 if (*submit_button != (dom_html_element *)button_element) {
932 return NSERROR_OK;
933 }
934
935 /* obtain name property */
936 exp = dom_html_button_element_get_name(button_element, &inputname);
937 if (exp != DOM_NO_ERR) {
938 NSLOG(netsurf, INFO,
939 "Could not get button name property. exp %d", exp);
940 return NSERROR_DOM;
941 }
942
943 if (inputname == NULL) {
944 /* allow enumeration to continue after element with no name */
945 return NSERROR_OK;
946 }
947
948 /* get button value and add to fetch data list */
949 exp = dom_html_button_element_get_value(button_element, &inputvalue);
950 if (exp != DOM_NO_ERR) {
951 NSLOG(netsurf, INFO, "Could not get submit button value");
952 res = NSERROR_DOM;
953 } else {
954 res = fetch_data_list_add(inputname,
955 inputvalue,
956 NULL,
957 form_charset,
958 doc_charset,
959 fetch_data_next_ptr);
960
961 dom_string_unref(inputvalue);
962 }
963
964 dom_string_unref(inputname);
965
966 return res;
967}
968
969
970/**
971 * Find an acceptable character set encoding with which to submit the form
972 *
973 * \param form The form
974 * \return Pointer to charset name (on heap, caller should free) or NULL
975 */
976static char *form_acceptable_charset(struct form *form)
977{
978 char *temp, *c;
979
980 if (!form->accept_charsets) {
981 /* no accept-charsets attribute for this form */
982 if (form->document_charset) {
983 /* document charset present, so use it */
984 return strdup(form->document_charset);
985 } else {
986 /* no document charset, so default to 8859-1 */
987 return strdup("ISO-8859-1");
988 }
989 }
990
991 /* make temporary copy of accept-charsets attribute */
992 temp = strdup(form->accept_charsets);
993 if (!temp)
994 return NULL;
995
996 /* make it upper case */
997 for (c = temp; *c; c++) {
998 *c = ascii_to_upper(*c);
999 }
1000
1001 /* is UTF-8 specified? */
1002 c = strstr(temp, "UTF-8");
1003 if (c) {
1004 free(temp);
1005 return strdup("UTF-8");
1006 }
1007
1008 /* dispense with temporary copy */
1009 free(temp);
1010
1011 /* according to RFC2070, the accept-charsets attribute of the
1012 * form element contains a space and/or comma separated list */
1013 c = form->accept_charsets;
1014
1015 /** \todo an improvement would be to choose an encoding
1016 * acceptable to the server which covers as much of the input
1017 * values as possible. Additionally, we need to handle the
1018 * case where none of the acceptable encodings cover all the
1019 * textual input values. For now, we just extract the first
1020 * element of the charset list
1021 */
1022 while (*c && !ascii_is_space(*c)) {
1023 if (*c == ',')
1024 break;
1025 c++;
1026 }
1027
1029}
1030
1031
1032/**
1033 * Construct multipart data list from 'successful' controls via the DOM.
1034 *
1035 * All text strings in the successful controls list will be in the charset most
1036 * appropriate for submission. Therefore, no utf8_to_* processing should be
1037 * performed upon them.
1038 *
1039 * \todo The chosen charset needs to be made available such that it can be
1040 * included in the submission request (e.g. in the fetch's Content-Type header)
1041 *
1042 * See HTML 4.01 section 17.13.2.
1043 *
1044 * \note care is taken to abort even if the error is recoverable as it
1045 * is not desirable to submit incomplete form data.
1046 *
1047 * \param[in] form form to search for successful controls
1048 * \param[in] submit_button control used to submit the form, if any
1049 * \param[out] fetch_data_out updated to point to linked list of
1050 * fetch_multipart_data, NULL if no controls
1051 * \return NSERROR_OK on success or appropriate error code
1052 */
1053static nserror
1055 struct form_control *submit_control,
1056 struct fetch_multipart_data **fetch_data_out)
1057{
1058 nserror res = NSERROR_OK;
1059 char *charset; /* form characterset */
1060 dom_exception exp; /* the result from DOM operations */
1061 dom_html_collection *elements = NULL; /* the dom form elements */
1062 uint32_t element_count; /* the number of elements in the DOM form */
1063 uint32_t element_idx; /* the index of thr enumerated element */
1064 dom_node *element = NULL; /* the DOM form element */
1065 dom_string *nodename = NULL; /* the DOM node name of the element */
1066 struct fetch_multipart_data *fetch_data = NULL; /* fetch data list */
1067 struct fetch_multipart_data **fetch_data_next = &fetch_data;
1068 dom_html_element *submit_button;
1069
1070 /* obtain the submit_button DOM node from the control */
1071 if (submit_control != NULL) {
1072 submit_button = submit_control->node;
1073 } else {
1074 submit_button = NULL;
1075 }
1076
1077 /** \todo Replace this call with something DOMish */
1078 charset = form_acceptable_charset(form);
1079 if (charset == NULL) {
1080 NSLOG(netsurf, INFO, "failed to find charset");
1081 return NSERROR_NOMEM;
1082 }
1083
1084 /* obtain the form elements and count */
1085 exp = dom_html_form_element_get_elements(form->node, &elements);
1086 if (exp != DOM_NO_ERR) {
1087 NSLOG(netsurf, INFO, "Could not get form elements");
1088 free(charset);
1089 return NSERROR_DOM;
1090 }
1091
1092 exp = dom_html_collection_get_length(elements, &element_count);
1093 if (exp != DOM_NO_ERR) {
1094 NSLOG(netsurf, INFO, "Could not get form element count");
1095 res = NSERROR_DOM;
1096 goto form_dom_to_data_error;
1097 }
1098
1099 for (element_idx = 0; element_idx < element_count; element_idx++) {
1100 /* obtain a form element */
1101 exp = dom_html_collection_item(elements, element_idx, &element);
1102 if (exp != DOM_NO_ERR) {
1103 NSLOG(netsurf, INFO,
1104 "retrieving form element %"PRIu32" failed with %d",
1105 element_idx, exp);
1106 res = NSERROR_DOM;
1107 goto form_dom_to_data_error;
1108 }
1109
1110 /* node name from element */
1111 exp = dom_node_get_node_name(element, &nodename);
1112 if (exp != DOM_NO_ERR) {
1113 NSLOG(netsurf, INFO,
1114 "getting element node name %"PRIu32" failed with %d",
1115 element_idx, exp);
1116 dom_node_unref(element);
1117 res = NSERROR_DOM;
1118 goto form_dom_to_data_error;
1119 }
1120
1121 if (dom_string_isequal(nodename, corestring_dom_TEXTAREA)) {
1122 /* Form element is HTMLTextAreaElement */
1124 (dom_html_text_area_element *)element,
1125 charset,
1127 &fetch_data_next);
1128
1129 } else if (dom_string_isequal(nodename, corestring_dom_SELECT)) {
1130 /* Form element is HTMLSelectElement */
1132 (dom_html_select_element *)element,
1133 charset,
1135 &fetch_data_next);
1136
1137 } else if (dom_string_isequal(nodename, corestring_dom_INPUT)) {
1138 /* Form element is HTMLInputElement */
1140 (dom_html_input_element *)element,
1141 charset,
1143 &submit_button,
1144 &fetch_data_next);
1145
1146 } else if (dom_string_isequal(nodename, corestring_dom_BUTTON)) {
1147 /* Form element is HTMLButtonElement */
1149 (dom_html_button_element *)element,
1150 charset,
1152 &submit_button,
1153 &fetch_data_next);
1154
1155 } else {
1156 /* Form element is not handled */
1157 NSLOG(netsurf, INFO,
1158 "Unhandled element type: %*s",
1159 (int)dom_string_byte_length(nodename),
1160 dom_string_data(nodename));
1161 res = NSERROR_DOM;
1162
1163 }
1164
1165 dom_string_unref(nodename);
1166 dom_node_unref(element);
1167
1168 /* abort form element enumeration on error */
1169 if (res != NSERROR_OK) {
1170 goto form_dom_to_data_error;
1171 }
1172 }
1173
1174 *fetch_data_out = fetch_data;
1175 dom_html_collection_unref(elements);
1176 free(charset);
1177
1178 return NSERROR_OK;
1179
1180form_dom_to_data_error:
1181 fetch_multipart_data_destroy(fetch_data);
1182 dom_html_collection_unref(elements);
1183 free(charset);
1184
1185 return res;
1186}
1187
1188/**
1189 * Encode controls using application/x-www-form-urlencoded.
1190 *
1191 * \param[in] form form to which successful controls relate
1192 * \param[in] control linked list of fetch_multipart_data
1193 * \param[out] encoded_out URL-encoded form data
1194 * \return NSERROR_OK on success and \a encoded_out updated else appropriate error code
1195 */
1196static nserror
1198 struct fetch_multipart_data *control,
1199 char **encoded_out)
1200{
1201 char *name, *value;
1202 char *s, *s2;
1203 unsigned int len, len1, len_init;
1204 nserror res;
1205
1206 s = malloc(1);
1207
1208 if (s == NULL) {
1209 return NSERROR_NOMEM;
1210 }
1211
1212 s[0] = '\0';
1213 len_init = len = 0;
1214
1215 for (; control; control = control->next) {
1216 res = url_escape(control->name, true, NULL, &name);
1217 if (res != NSERROR_OK) {
1218 free(s);
1219 return res;
1220 }
1221
1222 res = url_escape(control->value, true, NULL, &value);
1223 if (res != NSERROR_OK) {
1224 free(name);
1225 free(s);
1226 return res;
1227 }
1228
1229 /* resize string to allow for new key/value pair,
1230 * equals, amphersand and terminator
1231 */
1232 len1 = len + strlen(name) + strlen(value) + 2;
1233 s2 = realloc(s, len1 + 1);
1234 if (s2 == NULL) {
1235 free(value);
1236 free(name);
1237 free(s);
1238 return NSERROR_NOMEM;
1239 }
1240 s = s2;
1241
1242 snprintf(s + len, (len1 + 1) - len, "%s=%s&", name, value);
1243 len = len1;
1244 free(name);
1245 free(value);
1246 }
1247
1248 if (len > len_init) {
1249 /* Replace trailing '&' */
1250 s[len - 1] = '\0';
1251 }
1252
1253 *encoded_out = s;
1254
1255 return NSERROR_OK;
1256}
1257
1258
1259/**
1260 * Callback for the select menus scroll
1261 */
1262static void
1264 struct scrollbar_msg_data *scrollbar_data)
1265{
1266 struct form_control *control = client_data;
1267 struct form_select_menu *menu = control->data.select.menu;
1268 html_content *html = (html_content *)menu->c;
1269
1270 switch (scrollbar_data->msg) {
1272 menu->callback(menu->client_data,
1273 0, 0,
1274 menu->width,
1275 menu->height);
1276 break;
1278 {
1279 struct rect rect = {
1280 .x0 = scrollbar_data->x0,
1281 .y0 = scrollbar_data->y0,
1282 .x1 = scrollbar_data->x1,
1283 .y1 = scrollbar_data->y1
1284 };
1285
1288
1289 menu->scroll_capture = true;
1290 }
1291 break;
1293 menu->scroll_capture = false;
1294
1296 DRAGGING_NONE, NULL);
1297 break;
1298 default:
1299 break;
1300 }
1301}
1302
1303
1304/**
1305 * Process a selection from a form select menu.
1306 *
1307 * \param html The html content handle for the form
1308 * \param control form control with menu
1309 * \param item index of item selected from the menu
1310 * \return NSERROR_OK or appropriate error code.
1311 */
1312static nserror
1314 struct form_control *control,
1315 int item)
1316{
1317 struct box *inline_box;
1318 struct form_option *o;
1319 int count;
1320 nserror ret = NSERROR_OK;
1321
1322 assert(control != NULL);
1323 assert(html != NULL);
1324
1325 /**
1326 * \todo Even though the form code is effectively part of the html
1327 * content handler, poking around inside contents is not good
1328 */
1329
1330 inline_box = control->box->children->children;
1331
1332 for (count = 0, o = control->data.select.items;
1333 o != NULL;
1334 count++, o = o->next) {
1335 if (!control->data.select.multiple && o->selected) {
1336 o->selected = false;
1337 dom_html_option_element_set_selected(o->node, false);
1338 }
1339
1340 if (count == item) {
1341 if (control->data.select.multiple) {
1342 if (o->selected) {
1343 o->selected = false;
1344 dom_html_option_element_set_selected(
1345 o->node, false);
1346 control->data.select.num_selected--;
1347 } else {
1348 o->selected = true;
1349 dom_html_option_element_set_selected(
1350 o->node, true);
1351 control->data.select.num_selected++;
1352 }
1353 } else {
1354 dom_html_option_element_set_selected(
1355 o->node, true);
1356 o->selected = true;
1357 }
1358 }
1359
1360 if (o->selected) {
1361 control->data.select.current = o;
1362 }
1363 }
1364
1365 talloc_free(inline_box->text);
1366 inline_box->text = 0;
1367
1368 if (control->data.select.num_selected == 0) {
1369 inline_box->text = talloc_strdup(html->bctx,
1370 messages_get("Form_None"));
1371 } else if (control->data.select.num_selected == 1) {
1372 inline_box->text = talloc_strdup(html->bctx,
1373 control->data.select.current->text);
1374 } else {
1375 inline_box->text = talloc_strdup(html->bctx,
1376 messages_get("Form_Many"));
1377 }
1378
1379 if (!inline_box->text) {
1380 ret = NSERROR_NOMEM;
1381 inline_box->length = 0;
1382 } else {
1383 inline_box->length = strlen(inline_box->text);
1384 }
1385 inline_box->width = control->box->width;
1386
1387 html__redraw_a_box(html, control->box);
1388
1389 return ret;
1390}
1391
1392
1393/**
1394 * Handle a click on the area of the currently opened select menu.
1395 *
1396 * \param control the select menu which received the click
1397 * \param x X coordinate of click
1398 * \param y Y coordinate of click
1399 */
1400static void form_select_menu_clicked(struct form_control *control, int x, int y)
1401{
1402 struct form_select_menu *menu = control->data.select.menu;
1403 struct form_option *option;
1404 html_content *html = (html_content *)menu->c;
1405 int line_height, line_height_with_spacing;
1406 int item_bottom_y;
1407 int scroll, i;
1408
1409 scroll = scrollbar_get_offset(menu->scrollbar);
1410
1411 line_height = menu->line_height;
1412 line_height_with_spacing = line_height +
1414
1415 option = control->data.select.items;
1416 item_bottom_y = line_height_with_spacing;
1417 i = 0;
1418 while (option && item_bottom_y < scroll + y) {
1419 item_bottom_y += line_height_with_spacing;
1420 option = option->next;
1421 i++;
1422 }
1423
1424 if (option != NULL) {
1425 form__select_process_selection(html, control, i);
1426 }
1427
1428 menu->callback(menu->client_data, 0, 0, menu->width, menu->height);
1429}
1430
1431
1432/* exported interface documented in html/form_internal.h */
1433void form_add_control(struct form *form, struct form_control *control)
1434{
1435 if (form == NULL) {
1436 return;
1437 }
1438
1439 control->form = form;
1440
1441 if (form->controls != NULL) {
1442 assert(form->last_control);
1443
1444 form->last_control->next = control;
1445 control->prev = form->last_control;
1446 control->next = NULL;
1447 form->last_control = control;
1448 } else {
1449 form->controls = form->last_control = control;
1450 }
1451}
1452
1453
1454/* exported interface documented in html/form_internal.h */
1455void form_free_control(struct form_control *control)
1456{
1457 struct form_control *c;
1458 assert(control != NULL);
1459
1460 NSLOG(netsurf, INFO, "Control:%p name:%p value:%p initial:%p",
1461 control, control->name, control->value, control->initial_value);
1462 free(control->name);
1463 free(control->value);
1464 free(control->initial_value);
1465 if (control->last_synced_value != NULL) {
1466 free(control->last_synced_value);
1467 }
1468
1469 if (control->type == GADGET_SELECT) {
1470 struct form_option *option, *next;
1471
1472 for (option = control->data.select.items; option;
1473 option = next) {
1474 next = option->next;
1475 NSLOG(netsurf, INFO,
1476 "select option:%p text:%p value:%p", option,
1477 option->text, option->value);
1478 free(option->text);
1479 free(option->value);
1480 free(option);
1481 }
1482 if (control->data.select.menu != NULL) {
1483 form_free_select_menu(control);
1484 }
1485 }
1486
1487 if (control->type == GADGET_TEXTAREA ||
1488 control->type == GADGET_TEXTBOX ||
1489 control->type == GADGET_PASSWORD) {
1490
1491 if (control->data.text.initial != NULL) {
1492 dom_string_unref(control->data.text.initial);
1493 }
1494
1495 if (control->data.text.ta != NULL) {
1496 textarea_destroy(control->data.text.ta);
1497 }
1498 }
1499
1500 /* unlink the control from the form */
1501 if (control->form != NULL) {
1502 for (c = control->form->controls; c != NULL; c = c->next) {
1503 if (c->next == control) {
1504 c->next = control->next;
1505 if (control->form->last_control == control)
1506 control->form->last_control = c;
1507 break;
1508 }
1509 if (c == control) {
1510 /* can only happen if control was first control */
1511 control->form->controls = control->next;
1512 if (control->form->last_control == control)
1513 control->form->controls =
1514 control->form->last_control = NULL;
1515 break;
1516 }
1517 }
1518 }
1519
1520 if (control->node_value != NULL) {
1521 dom_string_unref(control->node_value);
1522 }
1523
1524 free(control);
1525}
1526
1527
1528/* exported interface documented in html/form_internal.h */
1529bool form_add_option(struct form_control *control, char *value, char *text,
1530 bool selected, void *node)
1531{
1532 struct form_option *option;
1533
1534 assert(control);
1535 assert(control->type == GADGET_SELECT);
1536
1537 option = calloc(1, sizeof *option);
1538 if (!option)
1539 return false;
1540
1541 option->value = value;
1542 option->text = text;
1543
1544 /* add to linked list */
1545 if (control->data.select.items == 0)
1546 control->data.select.items = option;
1547 else
1548 control->data.select.last_item->next = option;
1549 control->data.select.last_item = option;
1550
1551 /* set selected */
1552 if (selected && (control->data.select.num_selected == 0 ||
1553 control->data.select.multiple)) {
1554 option->selected = option->initial_selected = true;
1555 control->data.select.num_selected++;
1556 control->data.select.current = option;
1557 }
1558
1559 control->data.select.num_items++;
1560
1561 option->node = node;
1562
1563 return true;
1564}
1565
1566
1567/* exported interface documented in html/form_internal.h */
1568nserror
1569form_open_select_menu(void *client_data,
1570 struct form_control *control,
1572 struct content *c)
1573{
1574 int line_height_with_spacing;
1575 struct box *box;
1576 plot_font_style_t fstyle;
1577 int total_height;
1578 struct form_select_menu *menu;
1579 html_content *html = (html_content *)c;
1580 nserror res;
1581
1582 /* if the menu is opened for the first time */
1583 if (control->data.select.menu == NULL) {
1584
1585 menu = calloc(1, sizeof (struct form_select_menu));
1586 if (menu == NULL) {
1587 return NSERROR_NOMEM;
1588 }
1589
1590 control->data.select.menu = menu;
1591
1592 box = control->box;
1593
1594 menu->width = box->width +
1597
1599 control->box->style, &fstyle);
1600 menu->f_size = fstyle.size;
1601
1602 menu->line_height = FIXTOINT(FDIV((FMUL(FLTTOFIX(1.2),
1603 FMUL(html->unit_len_ctx.device_dpi,
1604 INTTOFIX(fstyle.size / PLOT_STYLE_SCALE)))),
1605 F_72));
1606
1607 line_height_with_spacing = menu->line_height +
1608 menu->line_height *
1610
1611 total_height = control->data.select.num_items *
1612 line_height_with_spacing;
1613 menu->height = total_height;
1614
1615 if (menu->height > MAX_SELECT_HEIGHT) {
1616 menu->height = MAX_SELECT_HEIGHT;
1617 }
1618
1619 menu->client_data = client_data;
1620 menu->callback = callback;
1621 res = scrollbar_create(false,
1622 menu->height,
1623 total_height,
1624 menu->height,
1625 control,
1627 &(menu->scrollbar));
1628 if (res != NSERROR_OK) {
1629 control->data.select.menu = NULL;
1630 free(menu);
1631 return res;
1632 }
1633 menu->c = c;
1634 } else {
1635 menu = control->data.select.menu;
1636 }
1637
1638 menu->callback(client_data, 0, 0, menu->width, menu->height);
1639
1640 return NSERROR_OK;
1641}
1642
1643
1644/* exported interface documented in html/form_internal.h */
1646{
1647 if (control->data.select.menu->scrollbar != NULL)
1648 scrollbar_destroy(control->data.select.menu->scrollbar);
1649 free(control->data.select.menu);
1650 control->data.select.menu = NULL;
1651}
1652
1653
1654/* exported interface documented in html/form_internal.h */
1655bool
1657 int x, int y,
1658 float scale,
1659 const struct rect *clip,
1660 const struct redraw_context *ctx)
1661{
1662 struct box *box;
1663 struct form_select_menu *menu = control->data.select.menu;
1664 struct form_option *option;
1665 int line_height, line_height_with_spacing;
1666 int width, height;
1667 int x0, y0, x1, scrollbar_x, y1, y2, y3;
1668 int item_y;
1669 int text_pos_offset, text_x;
1670 int scrollbar_width = SCROLLBAR_WIDTH;
1671 int i;
1672 int scroll;
1673 int x_cp, y_cp;
1674 struct rect r;
1675 struct rect rect;
1676 nserror res;
1677
1678 box = control->box;
1679
1680 x_cp = x;
1681 y_cp = y;
1682 width = menu->width;
1683 height = menu->height;
1684 line_height = menu->line_height;
1685
1686 line_height_with_spacing = line_height +
1688 scroll = scrollbar_get_offset(menu->scrollbar);
1689
1690 if (scale != 1.0) {
1691 x *= scale;
1692 y *= scale;
1693 width *= scale;
1694 height *= scale;
1695 scrollbar_width *= scale;
1696
1697 i = scroll / line_height_with_spacing;
1698 scroll -= i * line_height_with_spacing;
1699 line_height *= scale;
1700 line_height_with_spacing *= scale;
1701 scroll *= scale;
1702 scroll += i * line_height_with_spacing;
1703 }
1704
1705
1706 x0 = x;
1707 y0 = y;
1708 x1 = x + width - 1;
1709 y1 = y + height - 1;
1710 scrollbar_x = x1 - scrollbar_width;
1711
1712 r.x0 = x0;
1713 r.y0 = y0;
1714 r.x1 = x1 + 1;
1715 r.y1 = y1 + 1;
1716 res = ctx->plot->clip(ctx, &r);
1717 if (res != NSERROR_OK) {
1718 return false;
1719 }
1720
1721 rect.x0 = x0;
1722 rect.y0 = y0;
1723 rect.x1 = x1;
1724 rect.y1 = y1;
1725 res = ctx->plot->rectangle(ctx, plot_style_stroke_darkwbasec, &rect);
1726 if (res != NSERROR_OK) {
1727 return false;
1728 }
1729
1735
1736 r.x0 = x0;
1737 r.y0 = y0;
1738 r.x1 = x1 + 1;
1739 r.y1 = y1 + 1;
1740 res = ctx->plot->clip(ctx, &r);
1741 if (res != NSERROR_OK) {
1742 return false;
1743 }
1744
1745 res = ctx->plot->rectangle(ctx, plot_style_fill_lightwbasec, &r);
1746 if (res != NSERROR_OK) {
1747 return false;
1748 }
1749
1750 option = control->data.select.items;
1751 item_y = line_height_with_spacing;
1752
1753 while (item_y < scroll) {
1754 option = option->next;
1755 item_y += line_height_with_spacing;
1756 }
1757 item_y -= line_height_with_spacing;
1758 text_pos_offset = y - scroll +
1759 (int) (line_height * (0.75 + SELECT_LINE_SPACING));
1760 text_x = x + (box->border[LEFT].width + box->padding[LEFT]) * scale;
1761
1763
1764 while (option && item_y - scroll < height) {
1765
1766 if (option->selected) {
1767 y2 = y + item_y - scroll;
1768 y3 = y + item_y + line_height_with_spacing - scroll;
1769
1770 rect.x0 = x0;
1771 rect.y0 = y0 > y2 ? y0 : y2;
1772 rect.x1 = scrollbar_x + 1;
1773 rect.y1 = y3 < y1 + 1 ? y3 : y1 + 1;
1774 res = ctx->plot->rectangle(ctx, &plot_style_fill_selected, &rect);
1775 if (res != NSERROR_OK) {
1776 return false;
1777 }
1778 }
1779
1780 y2 = text_pos_offset + item_y;
1781 res = ctx->plot->text(ctx,
1783 text_x, y2,
1784 option->text, strlen(option->text));
1785 if (res != NSERROR_OK) {
1786 return false;
1787 }
1788
1789 item_y += line_height_with_spacing;
1790 option = option->next;
1791 }
1792
1793 res = scrollbar_redraw(menu->scrollbar,
1794 x_cp + menu->width - SCROLLBAR_WIDTH,
1795 y_cp,
1796 clip, scale, ctx);
1797 if (res != NSERROR_OK) {
1798 return false;
1799 }
1800
1801 return true;
1802}
1803
1804
1805/* private interface described in html/form_internal.h */
1806bool
1808 float scale,
1809 const struct rect *clip)
1810{
1811 struct form_select_menu *menu = control->data.select.menu;
1812 int width, height;
1813
1814
1815 width = menu->width;
1816 height = menu->height;
1817
1818 if (scale != 1.0) {
1819 width *= scale;
1820 height *= scale;
1821 }
1822
1823 if (clip->x0 >= 0 &&
1824 clip->x1 <= width &&
1825 clip->y0 >= 0 &&
1826 clip->y1 <= height)
1827 return true;
1828
1829 return false;
1830}
1831
1832
1833/* exported interface documented in netsurf/form.h */
1835{
1836 assert(control != NULL);
1837
1838 return form__select_process_selection(control->html, control, item);
1839}
1840
1841
1842/* exported interface documented in netsurf/form.h */
1843struct form_option *
1844form_select_get_option(struct form_control *control, int item)
1845{
1846 struct form_option *opt;
1847
1848 opt = control->data.select.items;
1849 while ((opt != NULL) && (item > 0)) {
1850 opt = opt->next;
1851 item--;
1852 }
1853 return opt;
1854}
1855
1856
1857/* exported interface documented in netsurf/form.h */
1859{
1860 return control->name;
1861}
1862
1863
1864/* exported interface documented in netsurf/form.h */
1866{
1867 box_bounds( control->box, r );
1868 return NSERROR_OK;
1869}
1870
1871
1872/* private interface described in html/form_internal.h */
1873const char *
1875 browser_mouse_state mouse,
1876 int x, int y)
1877{
1878 struct form_select_menu *menu = control->data.select.menu;
1879 int x0, y0, x1, y1, scrollbar_x;
1880 const char *status = NULL;
1881 bool multiple = control->data.select.multiple;
1882
1883 x0 = 0;
1884 y0 = 0;
1885 x1 = menu->width;
1886 y1 = menu->height;
1887 scrollbar_x = x1 - SCROLLBAR_WIDTH;
1888
1889 if (menu->scroll_capture ||
1890 (x > scrollbar_x && x < x1 && y > y0 && y < y1)) {
1891 /* The scroll is currently capturing all events or the mouse
1892 * event is taking place on the scrollbar widget area
1893 */
1894 x -= scrollbar_x;
1897 mouse, x, y));
1898 }
1899
1900
1901 if (x > x0 && x < scrollbar_x && y > y0 && y < y1) {
1902 /* over option area */
1903
1905 /* button 1 or 2 click */
1906 form_select_menu_clicked(control, x, y);
1907
1908 if (!(mouse & BROWSER_MOUSE_CLICK_1 && !multiple))
1909 /* anything but a button 1 click over a single select
1910 menu */
1911 status = messages_get(control->data.select.multiple ?
1912 "SelectMClick" : "SelectClick");
1913
1914 } else if (!(mouse & (BROWSER_MOUSE_CLICK_1 | BROWSER_MOUSE_CLICK_2)))
1915 /* if not a button 1 or 2 click*/
1916 status = messages_get("SelectClose");
1917
1918 return status;
1919}
1920
1921
1922/* private interface described in html/form_internal.h */
1923void
1925 browser_mouse_state mouse,
1926 int x, int y)
1927{
1928 int x0, y0, x1, y1;
1929 int box_x, box_y;
1930 struct box *box;
1931 struct form_select_menu *menu = control->data.select.menu;
1932
1933 box = control->box;
1934
1935 /* Get global coords of scrollbar */
1936 box_coords(box, &box_x, &box_y);
1937 box_x -= box->border[LEFT].width;
1938 box_y += box->height + box->border[BOTTOM].width +
1940
1941 /* Get drag end coords relative to scrollbar */
1942 x = x - box_x;
1943 y = y - box_y;
1944
1945 if (menu->scroll_capture) {
1946 x -= menu->width - SCROLLBAR_WIDTH;
1947 scrollbar_mouse_drag_end(menu->scrollbar, mouse, x, y);
1948 return;
1949 }
1950
1951 x0 = 0;
1952 y0 = 0;
1953 x1 = menu->width;
1954 y1 = menu->height;
1955
1956
1957 if (x > x0 && x < x1 - SCROLLBAR_WIDTH && y > y0 && y < y1) {
1958 /* handle drag end above the option area like a regular click */
1959 form_select_menu_clicked(control, x, y);
1960 }
1961}
1962
1963
1964/* private interface described in html/form_internal.h */
1966 int *width, int *height)
1967{
1968 *width = control->data.select.menu->width;
1969 *height = control->data.select.menu->height;
1970}
1971
1972
1973/* private interface described in html/form_internal.h */
1975 int x, int y, int width, int height)
1976{
1977 html_content *html = client_data;
1978 int menu_x, menu_y;
1979 struct box *box;
1980
1981 box = html->visible_select_menu->box;
1982 box_coords(box, &menu_x, &menu_y);
1983
1984 menu_x -= box->border[LEFT].width;
1985 menu_y += box->height + box->border[BOTTOM].width +
1986 box->padding[BOTTOM] +
1987 box->padding[TOP];
1988 content__request_redraw((struct content *)html, menu_x + x, menu_y + y,
1989 width, height);
1990}
1991
1992
1993/* private interface described in html/form_internal.h */
1994void form_radio_set(struct form_control *radio)
1995{
1996 struct form_control *control;
1997
1998 assert(radio);
1999 if (!radio->form)
2000 return;
2001
2002 if (radio->selected)
2003 return;
2004
2005 /* Clear selected state for other controls in
2006 * the same radio button group */
2007 for (control = radio->form->controls;
2008 control != NULL;
2009 control = control->next) {
2010 /* Only interested in radio inputs */
2011 if (control->type != GADGET_RADIO)
2012 continue;
2013
2014 /* Ignore ourself */
2015 if (control == radio)
2016 continue;
2017
2018 /* Ignore inputs where:
2019 * a) this or the other control have no name attribute
2020 * b) this or the other control have an empty name attribute
2021 * c) the control names do not match
2022 */
2023 if ((control->name == NULL) ||
2024 (radio->name == NULL) ||
2025 (control->name[0] == '\0') ||
2026 (radio->name[0] == '\0') ||
2027 strcmp(control->name, radio->name) != 0)
2028 continue;
2029
2030 /* Other control is in the same radio button group: clear its
2031 * selected state */
2032 if (control->selected) {
2033 control->selected = false;
2034 dom_html_input_element_set_checked(control->node, false);
2035 html__redraw_a_box(radio->html, control->box);
2036 }
2037 }
2038
2039 radio->selected = true;
2040 dom_html_input_element_set_checked(radio->node, true);
2041 html__redraw_a_box(radio->html, radio->box);
2042}
2043
2044
2045/* private interface described in html/form_internal.h */
2046nserror
2048 struct browser_window *target,
2049 struct form *form,
2050 struct form_control *submit_button)
2051{
2052 nserror res;
2053 char *data = NULL; /* encoded form data */
2054 struct fetch_multipart_data *success = NULL; /* gcc is incapable of correctly reasoning about use and generates "maybe used uninitialised" warnings */
2055 nsurl *action_url;
2056 nsurl *query_url;
2057
2058 assert(form != NULL);
2059
2060 /* obtain list of controls from DOM */
2061 res = form_dom_to_data(form, submit_button, &success);
2062 if (res != NSERROR_OK) {
2063 return res;
2064 }
2065
2066 /* Decompose action */
2067 res = nsurl_create(form->action, &action_url);
2068 if (res != NSERROR_OK) {
2070 return res;
2071 }
2072
2073 switch (form->method) {
2074 case method_GET:
2075 res = form_url_encode(form, success, &data);
2076 if (res == NSERROR_OK) {
2077 /* Replace query segment */
2078 res = nsurl_replace_query(action_url, data, &query_url);
2079 if (res == NSERROR_OK) {
2080 res = browser_window_navigate(target,
2081 query_url,
2082 page_url,
2084 NULL,
2085 NULL,
2086 NULL);
2087
2088 nsurl_unref(query_url);
2089 }
2090 free(data);
2091 }
2092 break;
2093
2094 case method_POST_URLENC:
2095 res = form_url_encode(form, success, &data);
2096 if (res == NSERROR_OK) {
2097 res = browser_window_navigate(target,
2098 action_url,
2099 page_url,
2101 data,
2102 NULL,
2103 NULL);
2104 free(data);
2105 }
2106 break;
2107
2109 res = browser_window_navigate(target,
2110 action_url,
2111 page_url,
2113 NULL,
2114 success,
2115 NULL);
2116
2117 break;
2118 }
2119
2120 nsurl_unref(action_url);
2122
2123 return res;
2124}
2125
2126
2127/* exported interface documented in html/form_internal.h */
2128void form_gadget_update_value(struct form_control *control, char *value)
2129{
2130 switch (control->type) {
2131 case GADGET_HIDDEN:
2132 case GADGET_TEXTBOX:
2133 case GADGET_TEXTAREA:
2134 case GADGET_PASSWORD:
2135 case GADGET_FILE:
2136 if (control->value != NULL) {
2137 free(control->value);
2138 }
2139 control->value = value;
2140 if (control->node != NULL) {
2141 dom_exception err;
2142 dom_string *str;
2143 err = dom_string_create((uint8_t *)value,
2144 strlen(value), &str);
2145 if (err == DOM_NO_ERR) {
2146 if (control->type == GADGET_TEXTAREA)
2147 err = dom_html_text_area_element_set_value(
2148 (dom_html_text_area_element *)(control->node),
2149 str);
2150 else
2151 err = dom_html_input_element_set_value(
2152 (dom_html_input_element *)(control->node),
2153 str);
2154 dom_string_unref(str);
2155 }
2156 }
2157 break;
2158 default:
2159 /* Do nothing */
2160 break;
2161 }
2162
2163 /* Finally, sync this with the DOM */
2165}
2166
2167
2168/* Exported API, see html/form_internal.h */
2169void
2171{
2172 dom_exception exc;
2173 dom_string *value = NULL;
2174 bool changed_dom = false;
2175
2176 if (control->syncing ||
2177 (control->type != GADGET_TEXTBOX &&
2178 control->type != GADGET_PASSWORD &&
2179 control->type != GADGET_HIDDEN &&
2180 control->type != GADGET_TEXTAREA)) {
2181 /* Not a control we support, or the control is already
2182 * mid-sync so we don't want to disrupt that
2183 */
2184 return;
2185 }
2186
2187 control->syncing = true;
2188
2189 /* If we've changed value, sync that toward the DOM */
2190 if ((control->last_synced_value == NULL &&
2191 control->value != NULL &&
2192 control->value[0] != '\0') ||
2193 (control->last_synced_value != NULL &&
2194 control->value != NULL &&
2195 strcmp(control->value, control->last_synced_value) != 0)) {
2196 char *dup = strdup(control->value);
2197 if (dup == NULL) {
2198 goto out;
2199 }
2200 if (control->last_synced_value != NULL) {
2201 free(control->last_synced_value);
2202 }
2203 control->last_synced_value = dup;
2204 exc = dom_string_create((uint8_t *)(control->value),
2205 strlen(control->value), &value);
2206 if (exc != DOM_NO_ERR) {
2207 goto out;
2208 }
2209 if (control->node_value != NULL) {
2210 dom_string_unref(control->node_value);
2211 }
2212 control->node_value = value;
2213 value = NULL;
2214 if (control->type == GADGET_TEXTAREA) {
2215 exc = dom_html_text_area_element_set_value(control->node, control->node_value);
2216 } else {
2217 exc = dom_html_input_element_set_value(control->node, control->node_value);
2218 }
2219 if (exc != DOM_NO_ERR) {
2220 goto out;
2221 }
2222 changed_dom = true;
2223 }
2224
2225 /* Now check if the DOM has changed since our last go */
2226 if (control->type == GADGET_TEXTAREA) {
2227 exc = dom_html_text_area_element_get_value(control->node, &value);
2228 } else {
2229 exc = dom_html_input_element_get_value(control->node, &value);
2230 }
2231
2232 if (exc != DOM_NO_ERR) {
2233 /* Nothing much we can do here */
2234 goto out;
2235 }
2236
2237 if (!dom_string_isequal(control->node_value, value)) {
2238 /* The DOM has changed */
2239 if (!changed_dom) {
2240 /* And it wasn't us */
2241 char *value_s = strndup(
2242 dom_string_data(value),
2243 dom_string_byte_length(value));
2244 char *dup = NULL;
2245 if (value_s == NULL) {
2246 goto out;
2247 }
2248 dup = strdup(value_s);
2249 if (dup == NULL) {
2250 free(value_s);
2251 goto out;
2252 }
2253 free(control->value);
2254 control->value = value_s;
2255 free(control->last_synced_value);
2256 control->last_synced_value = dup;
2257 if (control->type != GADGET_HIDDEN &&
2258 control->data.text.ta != NULL) {
2259 textarea_set_text(control->data.text.ta,
2260 value_s);
2261 }
2262 }
2263 control->node_value = value;
2264 value = NULL;
2265 }
2266
2267out:
2268 if (value != NULL)
2269 dom_string_unref(value);
2270 control->syncing = false;
2271}
2272
2273
2274/* exported interface documented in html/form_internal.h */
2275struct form *
2277 const char *action,
2278 const char *target,
2280 const char *charset,
2281 const char *doc_charset)
2282{
2283 struct form *form;
2284
2285 form = calloc(1, sizeof *form);
2286 if (!form)
2287 return NULL;
2288
2289 form->action = strdup(action != NULL ? action : "");
2290 if (form->action == NULL) {
2291 free(form);
2292 return NULL;
2293 }
2294
2295 form->target = target != NULL ? strdup(target) : NULL;
2296 if (target != NULL && form->target == NULL) {
2297 free(form->action);
2298 free(form);
2299 return NULL;
2300 }
2301
2302 form->method = method;
2303
2304 form->accept_charsets = charset != NULL ? strdup(charset) : NULL;
2305 if (charset != NULL && form->accept_charsets == NULL) {
2306 free(form->target);
2307 free(form->action);
2308 free(form);
2309 return NULL;
2310 }
2311
2312 form->document_charset = doc_charset != NULL ? strdup(doc_charset)
2313 : NULL;
2314 if (doc_charset && form->document_charset == NULL) {
2315 free(form->accept_charsets);
2316 free(form->target);
2317 free(form->action);
2318 free(form);
2319 return NULL;
2320 }
2321
2322 form->node = node;
2323
2324 return form;
2325}
2326
2327
2328/* exported interface documented in html/form_internal.h */
2329void form_free(struct form *form)
2330{
2331 struct form_control *c, *d;
2332
2333 for (c = form->controls; c != NULL; c = d) {
2334 d = c->next;
2335
2337 }
2338
2339 free(form->action);
2340 free(form->target);
2341 free(form->accept_charsets);
2342 free(form->document_charset);
2343
2344 free(form);
2345}
2346
2347
2348/* exported interface documented in html/form_internal.h */
2350{
2351 struct form_control *control;
2352
2353 control = calloc(1, sizeof *control);
2354 if (control == NULL)
2355 return NULL;
2356
2357 control->node = node;
2358 control->type = type;
2359
2360 return control;
2361}
Helpers for ASCII string handling.
static char ascii_to_upper(char c)
Convert a lower case character to upper case.
Definition: ascii.h:226
static bool ascii_is_space(char c)
Test whether a character is a whitespace character.
Definition: ascii.h:40
Box interface.
@ TOP
Definition: box.h:98
@ BOTTOM
Definition: box.h:98
@ LEFT
Definition: box.h:98
@ RIGHT
Definition: box.h:98
void box_coords(struct box *box, int *x, int *y)
Find the absolute coordinates of a box.
Definition: box_inspect.c:549
void box_bounds(struct box *box, struct rect *r)
Find the bounds of a box.
Definition: box_inspect.c:567
HTML Box tree inspection interface.
Browser window creation and manipulation interface.
@ DRAGGING_CONTENT_SCROLLBAR
@ DRAGGING_NONE
nserror browser_window_navigate(struct browser_window *bw, struct nsurl *url, struct nsurl *referrer, enum browser_window_nav_flags flags, char *post_urlenc, struct fetch_multipart_data *post_multipart, struct hlcache_handle *parent)
Start fetching a page in a browser window.
void browser_window_set_drag_type(struct browser_window *bw, browser_drag_type type, const struct rect *rect)
Set drag type for a browser window, and inform front end.
@ BW_NAVIGATE_HISTORY
this will form a new history node (don't set for back/reload/etc)
static uint32_t count(const http_directive *list, lwc_string *key)
char * strndup(const char *s, size_t n)
Duplicate up to n characters of a string.
Definition: utils.c:332
void fetch_multipart_data_destroy(struct fetch_multipart_data *list)
Free a linked list of fetch_multipart_data.
Definition: fetch.c:701
Fetching of data from a URL (interface).
void font_plot_style_from_css(const css_unit_ctx *unit_len_ctx, const css_computed_style *css, plot_font_style_t *fstyle)
Populate a font style using data from a computed CSS style.
Definition: font.c:135
Internal font handling interfaces.
static int line_height(const css_unit_ctx *unit_len_ctx, const css_computed_style *style)
Calculate line height from a style.
Definition: layout.c:2677
interface to HTML layout.
void content__request_redraw(struct content *c, int x, int y, int width, int height)
Request a redraw of an area of a content.
Definition: content.c:459
Useful interned string pointers (interface).
char options[PATH_MAX]
Definition: gui.c:90
void textarea_destroy(struct textarea *ta)
Destroy a text area.
Definition: textarea.c:1975
bool textarea_set_text(struct textarea *ta, const char *text)
Set the text in a text area, discarding any current text.
Definition: textarea.c:1995
Single/Multi-line UTF-8 text area interface.
nserror
Enumeration of error codes.
Definition: errors.h:29
@ NSERROR_BAD_ENCODING
The character set is unknown.
Definition: errors.h:45
@ NSERROR_DOM
DOM call returned error.
Definition: errors.h:52
@ NSERROR_NOMEM
Memory exhaustion.
Definition: errors.h:32
@ NSERROR_OK
No error.
Definition: errors.h:30
const char * type
Definition: filetype.cpp:44
bool form_add_option(struct form_control *control, char *value, char *text, bool selected, void *node)
Add an option to a form select control.
Definition: form.c:1529
#define FETCH_DATA_INT_VALUE_SIZE
string allocation size for numeric values in multipart data
Definition: form.c:165
static nserror form_dom_to_data_select_option(dom_html_option_element *option_element, dom_string *keyname, const char *form_charset, const char *docu_charset, struct fetch_multipart_data ***fetch_data_next_ptr)
Definition: form.c:371
static void form_select_menu_scroll_callback(void *client_data, struct scrollbar_msg_data *scrollbar_data)
Callback for the select menus scroll.
Definition: form.c:1263
static void form_select_menu_clicked(struct form_control *control, int x, int y)
Handle a click on the area of the currently opened select menu.
Definition: form.c:1400
#define SELECT_SELECTED_COLOUR
Definition: form.c:66
void form_select_menu_callback(void *client_data, int x, int y, int width, int height)
Callback for the core select menu.
Definition: form.c:1974
#define MAX_SELECT_HEIGHT
Definition: form.c:63
void form_select_get_dimensions(struct form_control *control, int *width, int *height)
Get the dimensions of a select menu.
Definition: form.c:1965
static nserror form_dom_to_data_button(dom_html_button_element *button_element, const char *form_charset, const char *doc_charset, dom_html_element **submit_button, struct fetch_multipart_data ***fetch_data_next_ptr)
process form HTMLButtonElement into multipart data.
Definition: form.c:875
#define SELECT_BORDER_WIDTH
Definition: form.c:65
static char * form_encode_item(const char *item, uint32_t len, const char *charset, const char *fallback)
Convert a string from UTF-8 to the specified charset As a final fallback, this will attempt to conver...
Definition: form.c:107
void form_select_mouse_drag_end(struct form_control *control, browser_mouse_state mouse, int x, int y)
Handle mouse drag end for the currently opened select menu.
Definition: form.c:1924
void form_free_select_menu(struct form_control *control)
Destroy a select menu and free allocated memory.
Definition: form.c:1645
bool form_clip_inside_select_menu(struct form_control *control, float scale, const struct rect *clip)
Check whether a clipping rectangle is completely contained in the select menu.
Definition: form.c:1807
static nserror form__select_process_selection(html_content *html, struct form_control *control, int item)
Process a selection from a form select menu.
Definition: form.c:1313
static nserror form_dom_to_data_input_file(dom_html_input_element *input_element, dom_string *inputname, const char *charset, const char *document_charset, struct fetch_multipart_data ***fetch_data_next_ptr)
Definition: form.c:663
nserror form_control_bounding_rect(struct form_control *control, struct rect *r)
Get a form control bounding rectangle.
Definition: form.c:1865
char * form_control_get_name(struct form_control *control)
Get a form control name.
Definition: form.c:1858
struct form_option * form_select_get_option(struct form_control *control, int item)
get a form select menus option.
Definition: form.c:1844
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
bool form_redraw_select_menu(struct form_control *control, int x, int y, float scale, const struct rect *clip, const struct redraw_context *ctx)
Redraw an opened select menu.
Definition: form.c:1656
static nserror form_dom_to_data_textarea(dom_html_text_area_element *text_area_element, const char *form_charset, const char *doc_charset, struct fetch_multipart_data ***fetch_data_next_ptr)
process form HTMLTextAreaElement into multipart data.
Definition: form.c:306
static nserror form_dom_to_data(struct form *form, struct form_control *submit_control, struct fetch_multipart_data **fetch_data_out)
Construct multipart data list from 'successful' controls via the DOM.
Definition: form.c:1054
static plot_font_style_t plot_fstyle_entry
Definition: form.c:84
static nserror form_url_encode(struct form *form, struct fetch_multipart_data *control, char **encoded_out)
Encode controls using application/x-www-form-urlencoded.
Definition: form.c:1197
static nserror fetch_data_list_add(dom_string *name, dom_string *value, const char *rawfile, const char *form_charset, const char *docu_charset, struct fetch_multipart_data ***fetch_data_next_ptr)
append DOM string name/value pair to a multipart data list
Definition: form.c:232
static nserror form_dom_to_data_input_checkbox(dom_html_input_element *input_element, dom_string *inputname, const char *charset, const char *document_charset, struct fetch_multipart_data ***fetch_data_next_ptr)
Definition: form.c:613
static nserror form_dom_to_data_input_text(dom_html_input_element *input_element, dom_string *inputname, const char *charset, const char *document_charset, struct fetch_multipart_data ***fetch_data_next_ptr)
Definition: form.c:707
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
static nserror form_dom_to_data_input_submit(dom_html_input_element *input_element, dom_string *inputname, const char *charset, const char *document_charset, dom_html_element **submit_button, struct fetch_multipart_data ***fetch_data_next_ptr)
Definition: form.c:516
const char * form_select_mouse_action(struct form_control *control, browser_mouse_state mouse, int x, int y)
Handle mouse action for the currently opened select menu.
Definition: form.c:1874
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
static char * form_acceptable_charset(struct form *form)
Find an acceptable character set encoding with which to submit the form.
Definition: form.c:976
nserror form_select_process_selection(struct form_control *control, int item)
Process a selection from a form select menu.
Definition: form.c:1834
void form_free(struct form *form)
Free a form and any controls it owns.
Definition: form.c:2329
void form_gadget_update_value(struct form_control *control, char *value)
Update gadget value.
Definition: form.c:2128
void form_free_control(struct form_control *control)
Free a struct form_control.
Definition: form.c:1455
nserror form_open_select_menu(void *client_data, struct form_control *control, select_menu_redraw_callback callback, struct content *c)
Open a select menu for a select form control, creating it if necessary.
Definition: form.c:1569
static nserror form_dom_to_data_select(dom_html_select_element *select_element, const char *form_charset, const char *doc_charset, struct fetch_multipart_data ***fetch_data_next_ptr)
process form HTMLSelectElement into multipart data.
Definition: form.c:423
#define SELECT_LINE_SPACING
Definition: form.c:64
static nserror fetch_data_list_add_sname(const char *name, const char *ksfx, int value, struct fetch_multipart_data ***fetch_data_next_ptr)
append split key name and integer value to a multipart data list
Definition: form.c:177
static nserror form_dom_to_data_input(dom_html_input_element *input_element, const char *charset, const char *document_charset, dom_html_element **submit_button, struct fetch_multipart_data ***fetch_data_next_ptr)
process form input element into multipart data.
Definition: form.c:750
void form_radio_set(struct form_control *radio)
Set a radio form control and clear the others in the group.
Definition: form.c:1994
static nserror form_dom_to_data_input_image(dom_html_input_element *input_element, dom_string *inputname, const char *charset, const char *document_charset, dom_html_element **submit_button, struct fetch_multipart_data ***fetch_data_next_ptr)
Definition: form.c:556
static plot_style_t plot_style_fill_selected
Definition: form.c:79
nserror form_submit(nsurl *page_url, struct browser_window *target, struct form *form, struct form_control *submit_button)
navigate browser window based on form submission.
Definition: form.c:2047
Interface to form handling functions internal to HTML content handler.
form_control_type
Type of a struct form_control.
Definition: form_internal.h:47
@ GADGET_TEXTAREA
Definition: form_internal.h:53
@ GADGET_HIDDEN
Definition: form_internal.h:48
@ GADGET_PASSWORD
Definition: form_internal.h:55
@ 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.
void(* select_menu_redraw_callback)(void *client_data, int x, int y, int width, int height)
Called by the select menu when it wants an area to be redrawn.
High-level resource cache interface.
void html__redraw_a_box(struct html_content *html, struct box *box)
Redraw a box.
Definition: html.c:1130
Interface to text/html content handler.
Interface to platform-specific miscellaneous browser operation table.
Core mouse and pointer states.
browser_mouse_state
Mouse state.
Definition: mouse.h:43
@ BROWSER_MOUSE_CLICK_2
button 2 clicked.
Definition: mouse.h:57
@ BROWSER_MOUSE_CLICK_1
button 1 clicked.
Definition: mouse.h:55
Target independent plotting interface.
Netsurf additional integer type formatting macros.
Knockout rendering (interface).
#define NSLOG(catname, level, logmsg, args...)
Definition: log.h:116
const char * messages_get(const char *key)
Fast lookup of a message by key from the standard Messages hash.
Definition: messages.c:241
Localised message support (interface).
nserror nsurl_create(const char *const url_s, nsurl **url)
Create a NetSurf URL object from a URL string.
void nsurl_unref(nsurl *url)
Drop a reference to a NetSurf URL object.
nserror nsurl_replace_query(const nsurl *url, const char *query, nsurl **new_url)
Create a NetSurf URL object, with query string replaced.
struct nsurl nsurl
NetSurf URL object.
Definition: nsurl.h:31
plot_style_t * plot_style_stroke_darkwbasec
Definition: plot_style.c:146
@ PLOT_OP_TYPE_SOLID
Solid colour.
Definition: plot_style.h:67
@ FONTF_NONE
Definition: plot_style.h:102
@ PLOT_FONT_FAMILY_SANS_SERIF
Definition: plot_style.h:89
#define PLOT_STYLE_SCALE
Scaling factor for plot styles.
Definition: plot_style.h:45
plot_style_t * plot_style_fill_lightwbasec
Definition: plot_style.c:123
Private data for text/html content.
int width
Definition: gui.c:159
int height
Definition: gui.c:160
const char * scrollbar_mouse_status_to_message(scrollbar_mouse_status status)
Get a status bar message from a scrollbar mouse input status.
Definition: scrollbar.c:888
scrollbar_mouse_status scrollbar_mouse_action(struct scrollbar *s, browser_mouse_state mouse, int x, int y)
Handle mouse actions other then drag ends.
Definition: scrollbar.c:768
int scrollbar_get_offset(struct scrollbar *s)
Get the current scroll offset to the visible part of the full area.
Definition: scrollbar.c:627
nserror scrollbar_create(bool horizontal, int length, int full_size, int visible_size, void *client_data, scrollbar_client_callback client_callback, struct scrollbar **s)
Create a scrollbar.
Definition: scrollbar.c:93
nserror scrollbar_redraw(struct scrollbar *s, int x, int y, const struct rect *clip, float scale, const struct redraw_context *ctx)
Redraw a part of the scrollbar.
Definition: scrollbar.c:239
void scrollbar_destroy(struct scrollbar *s)
Destroy a scrollbar.
Definition: scrollbar.c:138
void scrollbar_mouse_drag_end(struct scrollbar *s, browser_mouse_state mouse, int x, int y)
Handle end of mouse drags.
Definition: scrollbar.c:932
Scrollbar widget interface.
@ SCROLLBAR_MSG_MOVED
the scroll value has changed
Definition: scrollbar.h:47
@ SCROLLBAR_MSG_SCROLL_START
a scrollbar drag has started, all mouse events should be passed to the scrollbar regardless of the co...
Definition: scrollbar.h:48
@ SCROLLBAR_MSG_SCROLL_FINISHED
cancel a scrollbar drag
Definition: scrollbar.h:53
#define SCROLLBAR_WIDTH
Definition: scrollbar.h:32
Interface to utility string handling.
int width
border-width (pixels)
Definition: box.h:107
Node in box tree.
Definition: box.h:177
struct box_border border[4]
Border: TOP, RIGHT, BOTTOM, LEFT.
Definition: box.h:327
int width
Width of content box (excluding padding etc.).
Definition: box.h:289
struct box * children
First child box, or NULL.
Definition: box.h:226
int height
Height of content box (excluding padding etc.).
Definition: box.h:293
css_computed_style * style
Style for this box.
Definition: box.h:205
size_t length
Length of text.
Definition: box.h:360
char * text
Text, or NULL if none.
Definition: box.h:355
int padding[4]
Padding: TOP, RIGHT, BOTTOM, LEFT.
Definition: box.h:322
int x
Coordinate of left padding edge relative to parent box, or relative to ancestor that contains this bo...
Definition: box.h:280
int y
Coordinate of top padding edge, relative as for x.
Definition: box.h:284
Browser window data.
Content which corresponds to a single URL.
Fetch POST multipart data.
Definition: fetch.h:109
char * rawfile
Raw filename if file is true.
Definition: fetch.h:115
char * value
Item value.
Definition: fetch.h:113
struct fetch_multipart_data * next
Next in linked list.
Definition: fetch.h:110
char * name
Name of item.
Definition: fetch.h:112
bool file
Item is a file.
Definition: fetch.h:116
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 * prev
Previous control in this form.
form_control_type type
Type of control.
Definition: form_internal.h:79
struct form * form
Containing form.
Definition: form_internal.h:81
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.
char * initial_value
Initial value of control.
Definition: form_internal.h:85
struct html_content * html
HTML content containing control.
Definition: form_internal.h:77
char * value
Current value of control.
Definition: form_internal.h:84
bool syncing
Set if a DOM sync is in-progress.
Definition: form_internal.h:76
bool selected
Whether control is selected.
Definition: form_internal.h:94
struct box * box
Box for control.
Definition: form_internal.h:89
Option in a select.
Definition: form.h:33
char * value
Definition: form.h:37
void * node
Corresponding DOM node.
Definition: form.h:34
struct form_option * next
Definition: form.h:39
bool initial_selected
Definition: form.h:36
bool selected
Definition: form.h:35
char * text
NUL terminated.
Definition: form.h:38
select_menu_redraw_callback callback
Definition: form.c:74
int height
Definition: form.c:70
void * client_data
Definition: form.c:75
bool scroll_capture
Definition: form.c:73
struct scrollbar * scrollbar
Definition: form.c:71
int line_height
Definition: form.c:69
struct content * c
Definition: form.c:76
int f_size
Definition: form.c:72
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.
struct form_control * last_control
Last control in list.
char * accept_charsets
Charset to submit form in.
char * action
Absolute URL to submit to.
char * document_charset
Charset of document containing form.
Data specific to CONTENT_HTML.
Definition: private.h:93
int * bctx
A talloc context purely for the render box tree.
Definition: private.h:134
struct browser_window * bw
Browser window containing this document, or NULL if not open.
Definition: private.h:177
struct form_control * visible_select_menu
Open core-handled form SELECT menu, or NULL if none currently open.
Definition: private.h:211
css_unit_ctx unit_len_ctx
CSS length conversion context for document.
Definition: private.h:163
Font style for plotting.
Definition: plot_style.h:111
plot_font_generic_family_t family
Generic family to plot with.
Definition: plot_style.h:118
plot_style_fixed size
Font size, in pt.
Definition: plot_style.h:119
Plot style for stroke/fill plotters.
Definition: plot_style.h:76
plot_operation_type_t fill_type
Fill plot type.
Definition: plot_style.h:80
nserror(* text)(const struct redraw_context *ctx, const plot_font_style_t *fstyle, int x, int y, const char *text, size_t length)
Text plotting.
Definition: plotters.h:278
nserror(* rectangle)(const struct redraw_context *ctx, const plot_style_t *pstyle, const struct rect *rectangle)
Plots a rectangle.
Definition: plotters.h:188
nserror(* clip)(const struct redraw_context *ctx, const struct rect *clip)
Sets a clip rectangle for subsequent plot operations.
Definition: plotters.h:111
Rectangle coordinates.
Definition: types.h:40
int x0
Definition: types.h:41
int y0
Top left.
Definition: types.h:41
int x1
Definition: types.h:42
int y1
Bottom right.
Definition: types.h:42
Redraw context.
Definition: plotters.h:51
const struct plotter_table * plot
Current plot operation table.
Definition: plotters.h:73
scrollbar message context data
Definition: scrollbar.h:59
scrollbar_msg msg
Definition: scrollbar.h:61
Scrollbar context.
Definition: scrollbar.c:44
int talloc_free(void *ptr)
Definition: talloc.c:760
char * talloc_strdup(const void *t, const char *p)
Definition: talloc.c:1118
nserror url_escape(const char *unescaped, bool sptoplus, const char *escexceptions, char **result)
Escape a string suitable for inclusion in an URL.
Definition: url.c:131
Interface to URL parsing and joining operations.
nserror utf8_to_enc(const char *string, const char *encname, size_t len, char **result)
Convert a UTF8 string into the named encoding.
Definition: utf8.c:314
UTF-8 manipulation functions (interface).
static nserror text(const struct redraw_context *ctx, const struct plot_font_style *fstyle, int x, int y, const char *text, size_t length)
Text plotting.
Definition: plot.c:978
static nserror clip(const struct redraw_context *ctx, const struct rect *clip)
Sets a clip rectangle for subsequent plot operations.
Definition: plot.c:357