Bug Summary

File:content/handlers/html/form.c
Warning:line 2151, column 6
Value stored to 'err' is never read

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-pc-linux-gnu -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name form.c -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 2 -pic-is-pie -mframe-pointer=none -fmath-errno -ffp-contract=on -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fcoverage-compilation-dir=/var/lib/jenkins/workspace/scan-build-netsurf -resource-dir /usr/lib/llvm-14/lib/clang/14.0.6 -I . -I include -I build/Linux-gtk2 -I frontends -I content/handlers -D WITH_JPEG -U WITH_PDF_EXPORT -D LIBICONV_PLUG -I /var/lib/jenkins/artifacts-x86_64-linux-gnu/include -I /var/lib/jenkins/artifacts-x86_64-linux-gnu/include -I /var/lib/jenkins/artifacts-x86_64-linux-gnu/include -I /usr/include/x86_64-linux-gnu -D WITH_CURL -D WITH_OPENSSL -I /var/lib/jenkins/artifacts-x86_64-linux-gnu/include -D UTF8PROC_EXPORTS -D WITH_UTF8PROC -D WITH_WEBP -I /usr/include/libpng16 -D WITH_PNG -I /var/lib/jenkins/artifacts-x86_64-linux-gnu/include/ -D WITH_BMP -I /var/lib/jenkins/artifacts-x86_64-linux-gnu/include -D WITH_GIF -I /var/lib/jenkins/artifacts-x86_64-linux-gnu/include -D WITH_NS_SVG -I /var/lib/jenkins/artifacts-x86_64-linux-gnu/include -D WITH_NSSPRITE -I /var/lib/jenkins/artifacts-x86_64-linux-gnu/include -D WITH_NSPSL -I /var/lib/jenkins/artifacts-x86_64-linux-gnu/include -D WITH_NSLOG -D NETSURF_UA_FORMAT_STRING="Mozilla/5.0 (%s) NetSurf/%d.%d" -D NETSURF_HOMEPAGE="about:welcome" -D NETSURF_LOG_LEVEL=VERBOSE -D NETSURF_BUILTIN_LOG_FILTER="(level:WARNING || cat:jserrors)" -D NETSURF_BUILTIN_VERBOSE_FILTER="(level:VERBOSE || cat:jserrors)" -D STMTEXPR=1 -I /usr/include/librsvg-2.0 -I /usr/include/glib-2.0 -I /usr/lib/x86_64-linux-gnu/glib-2.0/include -I /usr/include/libmount -I /usr/include/blkid -I /usr/include/gdk-pixbuf-2.0 -I /usr/include/libpng16 -I /usr/include/x86_64-linux-gnu -I /usr/include/cairo -I /usr/include/pixman-1 -I /usr/include/freetype2 -D WITH_RSVG -I /usr/include/gtk-2.0 -I /usr/lib/x86_64-linux-gnu/gtk-2.0/include -I /usr/include/pango-1.0 -I /usr/include/glib-2.0 -I /usr/lib/x86_64-linux-gnu/glib-2.0/include -I /usr/include/harfbuzz -I /usr/include/freetype2 -I /usr/include/libpng16 -I /usr/include/libmount -I /usr/include/blkid -I /usr/include/fribidi -I /usr/include/cairo -I /usr/include/pixman-1 -I /usr/include/gdk-pixbuf-2.0 -I /usr/include/x86_64-linux-gnu -I /usr/include/atk-1.0 -I /usr/include/glib-2.0 -I /usr/lib/x86_64-linux-gnu/glib-2.0/include -I /usr/include/glib-2.0 -I /usr/lib/x86_64-linux-gnu/glib-2.0/include -D gtk -D nsgtk -D G_DISABLE_SINGLE_INCLUDES -D G_DISABLE_DEPRECATED -D GTK_DISABLE_SINGLE_INCLUDES -D GTK_MULTIHEAD_SAFE -D PANGO_DISABLE_DEPRECATED -D GTK_DISABLE_DEPRECATED -D _XOPEN_SOURCE=700 -D _POSIX_C_SOURCE=200809L -D _BSD_SOURCE -D _DEFAULT_SOURCE -D _NETBSD_SOURCE -D GTK_RESPATH="/var/lib/jenkins/artifacts-x86_64-linux-gnu/share/netsurf/:./frontends/gtk/res/" -D WITH_GRESOURCE -D DUK_OPT_HAVE_CUSTOM_H -internal-isystem /usr/lib/llvm-14/lib/clang/14.0.6/include -internal-isystem /usr/local/include -internal-isystem /usr/bin/../lib/gcc/x86_64-linux-gnu/12/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -O2 -Wwrite-strings -Wno-unused-parameter -Wno-unused-but-set-variable -std=c99 -fconst-strings -fdebug-compilation-dir=/var/lib/jenkins/workspace/scan-build-netsurf -ferror-limit 19 -fgnuc-version=4.2.1 -vectorize-loops -vectorize-slp -analyzer-display-progress -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /var/lib/jenkins/workspace/scan-build-netsurf/clangScanBuildReports/2024-12-27-084849-3370695-1 -x c content/handlers/html/form.c
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"
43#include "netsurf/browser_window.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_HEIGHT210 210
64#define SELECT_LINE_SPACING0.2 0.2
65#define SELECT_BORDER_WIDTH1 1
66#define SELECT_SELECTED_COLOUR0xDB9370 0xDB9370
67
68struct form_select_menu {
69 int line_height;
70 int width, height;
71 struct scrollbar *scrollbar;
72 int f_size;
73 bool_Bool scroll_capture;
74 select_menu_redraw_callback callback;
75 void *client_data;
76 struct content *c;
77};
78
79static plot_style_t plot_style_fill_selected = {
80 .fill_type = PLOT_OP_TYPE_SOLID,
81 .fill_colour = SELECT_SELECTED_COLOUR0xDB9370,
82};
83
84static plot_font_style_t plot_fstyle_entry = {
85 .family = PLOT_FONT_FAMILY_SANS_SERIF,
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((void*)0);
114 char cset[256];
115
116 if (!item || !charset)
117 return NULL((void*)0);
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((void*)0);
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_SIZE20 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
177fetch_data_list_add_sname(const char *name,
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((void*)0)) {
187 NSLOG(netsurf, INFO, "failed allocation for fetch data")do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "content/handlers/html/form.c", sizeof("content/handlers/html/form.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 187
, }; nslog__log(&_nslog_ctx, "failed allocation for fetch data"
); } } while(0)
;
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((void*)0)) {
195 free(fetch_data);
196 NSLOG(netsurf, INFO,do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "content/handlers/html/form.c", sizeof("content/handlers/html/form.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 197
, }; nslog__log(&_nslog_ctx, "keyname allocation failure for %s%s"
, name, ksfx); } } while(0)
197 "keyname allocation failure for %s%s", name, ksfx)do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "content/handlers/html/form.c", sizeof("content/handlers/html/form.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 197
, }; nslog__log(&_nslog_ctx, "keyname allocation failure for %s%s"
, name, ksfx); } } while(0)
;
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_SIZE20);
204 if (fetch_data->value == NULL((void*)0)) {
205 free(fetch_data->name);
206 free(fetch_data);
207 NSLOG(netsurf, INFO, "value allocation failure")do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "content/handlers/html/form.c", sizeof("content/handlers/html/form.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 207
, }; nslog__log(&_nslog_ctx, "value allocation failure");
} } while(0)
;
208 return NSERROR_NOMEM;
209 }
210 snprintf(fetch_data->value, FETCH_DATA_INT_VALUE_SIZE20, "%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
232fetch_data_list_add(dom_string *name,
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)((name != ((void*)0)) ? (void) (0) : __assert_fail ("name != NULL"
, "content/handlers/html/form.c", 241, __extension__ __PRETTY_FUNCTION__
))
;
242
243 fetch_data = calloc(1, sizeof(*fetch_data));
244 if (fetch_data == NULL((void*)0)) {
245 NSLOG(netsurf, INFO, "failed allocation for fetch data")do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "content/handlers/html/form.c", sizeof("content/handlers/html/form.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 245
, }; nslog__log(&_nslog_ctx, "failed allocation for fetch data"
); } } while(0)
;
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((void*)0)) {
254 NSLOG(netsurf, INFO, "Could not encode name for fetch data")do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "content/handlers/html/form.c", sizeof("content/handlers/html/form.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 254
, }; nslog__log(&_nslog_ctx, "Could not encode name for fetch data"
); } } while(0)
;
255 free(fetch_data);
256 return NSERROR_NOMEM;
257 }
258
259 if (value == NULL((void*)0)) {
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((void*)0)) {
268 NSLOG(netsurf, INFO, "Could not encode value for fetch data")do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "content/handlers/html/form.c", sizeof("content/handlers/html/form.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 268
, }; nslog__log(&_nslog_ctx, "Could not encode value for fetch data"
); } } while(0)
;
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((void*)0)) {
276 fetch_data->file = true1;
277 fetch_data->rawfile = strdup(rawfile);
278 if (fetch_data->rawfile == NULL((void*)0)) {
279 NSLOG(netsurf, INFO,do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "content/handlers/html/form.c", sizeof("content/handlers/html/form.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 280
, }; nslog__log(&_nslog_ctx, "Could not encode rawfile value for fetch data"
); } } while(0)
280 "Could not encode rawfile value for fetch data")do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "content/handlers/html/form.c", sizeof("content/handlers/html/form.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 280
, }; nslog__log(&_nslog_ctx, "Could not encode rawfile value for fetch data"
); } } while(0)
;
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_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,do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "content/handlers/html/form.c", sizeof("content/handlers/html/form.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 322
, }; nslog__log(&_nslog_ctx, "Could not get text area disabled property. exp %d"
, exp); } } while(0)
322 "Could not get text area disabled property. exp %d", exp)do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "content/handlers/html/form.c", sizeof("content/handlers/html/form.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 322
, }; nslog__log(&_nslog_ctx, "Could not get text area disabled property. exp %d"
, exp); } } while(0)
;
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,do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "content/handlers/html/form.c", sizeof("content/handlers/html/form.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 336
, }; nslog__log(&_nslog_ctx, "Could not get text area name property. exp %d"
, exp); } } while(0)
336 "Could not get text area name property. exp %d", exp)do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "content/handlers/html/form.c", sizeof("content/handlers/html/form.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 336
, }; nslog__log(&_nslog_ctx, "Could not get text area name property. exp %d"
, exp); } } while(0)
;
337 return NSERROR_DOM;
338 }
339
340 if (inputname == NULL((void*)0)) {
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,do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "content/handlers/html/form.c", sizeof("content/handlers/html/form.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 350
, }; nslog__log(&_nslog_ctx, "Could not get text area content. exp %d"
, exp); } } while(0)
350 "Could not get text area content. exp %d", exp)do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "content/handlers/html/form.c", sizeof("content/handlers/html/form.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 350
, }; nslog__log(&_nslog_ctx, "Could not get text area content. exp %d"
, exp); } } while(0)
;
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((void*)0),
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_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")do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "content/handlers/html/form.c", sizeof("content/handlers/html/form.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 384
, }; nslog__log(&_nslog_ctx, "Could not get option selected property"
); } } while(0)
;
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")do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "content/handlers/html/form.c", sizeof("content/handlers/html/form.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 395
, }; nslog__log(&_nslog_ctx, "Could not get option value"
); } } while(0)
;
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((void*)0),
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_Bool element_disabled;
431 dom_string *inputname;
432 dom_html_options_collection *options = NULL((void*)0);
433 uint32_t options_count;
434 uint32_t option_index;
435 dom_node *option_element = NULL((void*)0);
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,do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "content/handlers/html/form.c", sizeof("content/handlers/html/form.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 442
, }; nslog__log(&_nslog_ctx, "Could not get select disabled property. exp %d"
, exp); } } while(0)
442 "Could not get select disabled property. exp %d", exp)do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "content/handlers/html/form.c", sizeof("content/handlers/html/form.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 442
, }; nslog__log(&_nslog_ctx, "Could not get select disabled property. exp %d"
, exp); } } while(0)
;
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,do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "content/handlers/html/form.c", sizeof("content/handlers/html/form.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 455
, }; nslog__log(&_nslog_ctx, "Could not get select name property. exp %d"
, exp); } } while(0)
455 "Could not get select name property. exp %d", exp)do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "content/handlers/html/form.c", sizeof("content/handlers/html/form.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 455
, }; nslog__log(&_nslog_ctx, "Could not get select name property. exp %d"
, exp); } } while(0)
;
456 return NSERROR_DOM;
457 }
458
459 if (inputname == NULL((void*)0)) {
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)dom__html_select_element_get_options((dom_html_select_element
*) (select_element), (struct dom_html_options_collection **)
(&options))
;
466 if (exp != DOM_NO_ERR) {
467 NSLOG(netsurf, INFO,do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "content/handlers/html/form.c", sizeof("content/handlers/html/form.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 468
, }; nslog__log(&_nslog_ctx, "Could not get select options collection"
); } } while(0)
468 "Could not get select options collection")do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "content/handlers/html/form.c", sizeof("content/handlers/html/form.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 468
, }; nslog__log(&_nslog_ctx, "Could not get select options collection"
); } } while(0)
;
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,do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "content/handlers/html/form.c", sizeof("content/handlers/html/form.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 477
, }; nslog__log(&_nslog_ctx, "Could not get select options collection length"
); } } while(0)
477 "Could not get select options collection length")do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "content/handlers/html/form.c", sizeof("content/handlers/html/form.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 477
, }; nslog__log(&_nslog_ctx, "Could not get select options collection length"
); } } while(0)
;
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,do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "content/handlers/html/form.c", sizeof("content/handlers/html/form.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 490
, }; nslog__log(&_nslog_ctx, "Could not get options item %"
"d", option_index); } } while(0)
490 "Could not get options item %"PRId32, option_index)do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "content/handlers/html/form.c", sizeof("content/handlers/html/form.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 490
, }; nslog__log(&_nslog_ctx, "Could not get options item %"
"d", option_index); } } while(0)
;
491 res = NSERROR_DOM;
492 } else {
493 res = form_dom_to_data_select_option(
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)dom_node_unref((dom_node *) (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((void*)0)) {
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")do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "content/handlers/html/form.c", sizeof("content/handlers/html/form.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 537
, }; nslog__log(&_nslog_ctx, "Could not get submit button value"
); } } while(0)
;
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((void*)0),
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,dom_node_get_user_data( (dom_node *) ((dom_node *)input_element
), (corestring_dom___ns_key_image_coords_node_data), (void **
) (&coords))
574 corestring_dom___ns_key_image_coords_node_data,dom_node_get_user_data( (dom_node *) ((dom_node *)input_element
), (corestring_dom___ns_key_image_coords_node_data), (void **
) (&coords))
575 &coords)dom_node_get_user_data( (dom_node *) ((dom_node *)input_element
), (corestring_dom___ns_key_image_coords_node_data), (void **
) (&coords))
;
576 if (exp != DOM_NO_ERR) {
577 NSLOG(netsurf, INFO, "Could not get image XY data")do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "content/handlers/html/form.c", sizeof("content/handlers/html/form.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 577
, }; nslog__log(&_nslog_ctx, "Could not get image XY data"
); } } while(0)
;
578 return NSERROR_DOM;
579 }
580
581 if (coords == NULL((void*)0)) {
582 NSLOG(netsurf, INFO, "No XY data on the image input")do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "content/handlers/html/form.c", sizeof("content/handlers/html/form.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 582
, }; nslog__log(&_nslog_ctx, "No XY data on the image input"
); } } while(0)
;
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((void*)0)) {
592 NSLOG(netsurf, INFO, "Could not encode basename")do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "content/handlers/html/form.c", sizeof("content/handlers/html/form.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 592
, }; nslog__log(&_nslog_ctx, "Could not encode basename")
; } } while(0)
;
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_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,do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "content/handlers/html/form.c", sizeof("content/handlers/html/form.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 627
, }; nslog__log(&_nslog_ctx, "Could not get input element checked"
); } } while(0)
627 "Could not get input element checked")do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "content/handlers/html/form.c", sizeof("content/handlers/html/form.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 627
, }; nslog__log(&_nslog_ctx, "Could not get input element checked"
); } } while(0)
;
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,do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "content/handlers/html/form.c", sizeof("content/handlers/html/form.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 639
, }; nslog__log(&_nslog_ctx, "Could not get input element value"
); } } while(0)
639 "Could not get input element value")do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "content/handlers/html/form.c", sizeof("content/handlers/html/form.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 639
, }; nslog__log(&_nslog_ctx, "Could not get input element value"
); } } while(0)
;
640 return NSERROR_DOM;
641 }
642
643 /* ensure a default value */
644 if (inputvalue == NULL((void*)0)) {
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((void*)0),
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((void*)0);
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")do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "content/handlers/html/form.c", sizeof("content/handlers/html/form.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 676
, }; nslog__log(&_nslog_ctx, "Could not get file value");
} } while(0)
;
677 return NSERROR_DOM;
678 }
679
680 exp = dom_node_get_user_data((dom_node *)input_element,dom_node_get_user_data( (dom_node *) ((dom_node *)input_element
), (corestring_dom___ns_key_file_name_node_data), (void **) (
&rawfile))
681 corestring_dom___ns_key_file_name_node_data,dom_node_get_user_data( (dom_node *) ((dom_node *)input_element
), (corestring_dom___ns_key_file_name_node_data), (void **) (
&rawfile))
682 &rawfile)dom_node_get_user_data( (dom_node *) ((dom_node *)input_element
), (corestring_dom___ns_key_file_name_node_data), (void **) (
&rawfile))
;
683 if (exp != DOM_NO_ERR) {
684 NSLOG(netsurf, INFO, "Could not get file rawname")do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "content/handlers/html/form.c", sizeof("content/handlers/html/form.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 684
, }; nslog__log(&_nslog_ctx, "Could not get file rawname"
); } } while(0)
;
685 return NSERROR_DOM;
686 }
687
688 if (rawfile == NULL((void*)0)) {
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")do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "content/handlers/html/form.c", sizeof("content/handlers/html/form.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 719
, }; nslog__log(&_nslog_ctx, "Could not get input value")
; } } while(0)
;
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((void*)0),
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_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,do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "content/handlers/html/form.c", sizeof("content/handlers/html/form.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 767
, }; nslog__log(&_nslog_ctx, "Could not get input disabled property. exp %d"
, exp); } } while(0)
767 "Could not get input disabled property. exp %d", exp)do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "content/handlers/html/form.c", sizeof("content/handlers/html/form.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 767
, }; nslog__log(&_nslog_ctx, "Could not get input disabled property. exp %d"
, exp); } } while(0)
;
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,do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "content/handlers/html/form.c", sizeof("content/handlers/html/form.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 780
, }; nslog__log(&_nslog_ctx, "Could not get input name property. exp %d"
, exp); } } while(0)
780 "Could not get input name property. exp %d", exp)do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "content/handlers/html/form.c", sizeof("content/handlers/html/form.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 780
, }; nslog__log(&_nslog_ctx, "Could not get input name property. exp %d"
, exp); } } while(0)
;
781 return NSERROR_DOM;
782 }
783
784 if (inputname == NULL((void*)0)) {
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")do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "content/handlers/html/form.c", sizeof("content/handlers/html/form.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 792
, }; nslog__log(&_nslog_ctx, "Could not get input element type"
); } } while(0)
;
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")do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "content/handlers/html/form.c", sizeof("content/handlers/html/form.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 842
, }; nslog__log(&_nslog_ctx, "Skipping RESET and BUTTON")
; } } while(0)
;
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_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,do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "content/handlers/html/form.c", sizeof("content/handlers/html/form.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 893
, }; nslog__log(&_nslog_ctx, "Unable to get disabled property. exp %d"
, exp); } } while(0)
893 "Unable to get disabled property. exp %d", exp)do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "content/handlers/html/form.c", sizeof("content/handlers/html/form.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 893
, }; nslog__log(&_nslog_ctx, "Unable to get disabled property. exp %d"
, exp); } } while(0)
;
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")do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "content/handlers/html/form.c", sizeof("content/handlers/html/form.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 905
, }; nslog__log(&_nslog_ctx, "Could not get button element type"
); } } while(0)
;
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((void*)0)) {
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,do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "content/handlers/html/form.c", sizeof("content/handlers/html/form.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 939
, }; nslog__log(&_nslog_ctx, "Could not get button name property. exp %d"
, exp); } } while(0)
939 "Could not get button name property. exp %d", exp)do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "content/handlers/html/form.c", sizeof("content/handlers/html/form.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 939
, }; nslog__log(&_nslog_ctx, "Could not get button name property. exp %d"
, exp); } } while(0)
;
940 return NSERROR_DOM;
941 }
942
943 if (inputname == NULL((void*)0)) {
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")do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "content/handlers/html/form.c", sizeof("content/handlers/html/form.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 951
, }; nslog__log(&_nslog_ctx, "Could not get submit button value"
); } } while(0)
;
952 res = NSERROR_DOM;
953 } else {
954 res = fetch_data_list_add(inputname,
955 inputvalue,
956 NULL((void*)0),
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((void*)0);
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
1028 return strndup(form->accept_charsets, c - form->accept_charsets);
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
1054form_dom_to_data(struct form *form,
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((void*)0); /* 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((void*)0); /* the DOM form element */
1065 dom_string *nodename = NULL((void*)0); /* the DOM node name of the element */
1066 struct fetch_multipart_data *fetch_data = NULL((void*)0); /* 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((void*)0)) {
1072 submit_button = submit_control->node;
1073 } else {
1074 submit_button = NULL((void*)0);
1075 }
1076
1077 /** \todo Replace this call with something DOMish */
1078 charset = form_acceptable_charset(form);
1079 if (charset == NULL((void*)0)) {
1080 NSLOG(netsurf, INFO, "failed to find charset")do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "content/handlers/html/form.c", sizeof("content/handlers/html/form.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 1080
, }; nslog__log(&_nslog_ctx, "failed to find charset"); }
} while(0)
;
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")do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "content/handlers/html/form.c", sizeof("content/handlers/html/form.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 1087
, }; nslog__log(&_nslog_ctx, "Could not get form elements"
); } } while(0)
;
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")do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "content/handlers/html/form.c", sizeof("content/handlers/html/form.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 1094
, }; nslog__log(&_nslog_ctx, "Could not get form element count"
); } } while(0)
;
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,do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "content/handlers/html/form.c", sizeof("content/handlers/html/form.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 1105
, }; nslog__log(&_nslog_ctx, "retrieving form element %""u"
" failed with %d", element_idx, exp); } } while(0)
1104 "retrieving form element %"PRIu32" failed with %d",do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "content/handlers/html/form.c", sizeof("content/handlers/html/form.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 1105
, }; nslog__log(&_nslog_ctx, "retrieving form element %""u"
" failed with %d", element_idx, exp); } } while(0)
1105 element_idx, exp)do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "content/handlers/html/form.c", sizeof("content/handlers/html/form.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 1105
, }; nslog__log(&_nslog_ctx, "retrieving form element %""u"
" failed with %d", element_idx, exp); } } while(0)
;
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)dom_node_get_node_name((dom_node *) (element), (&nodename
))
;
1112 if (exp != DOM_NO_ERR) {
1113 NSLOG(netsurf, INFO,do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "content/handlers/html/form.c", sizeof("content/handlers/html/form.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 1115
, }; nslog__log(&_nslog_ctx, "getting element node name %"
"u"" failed with %d", element_idx, exp); } } while(0)
1114 "getting element node name %"PRIu32" failed with %d",do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "content/handlers/html/form.c", sizeof("content/handlers/html/form.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 1115
, }; nslog__log(&_nslog_ctx, "getting element node name %"
"u"" failed with %d", element_idx, exp); } } while(0)
1115 element_idx, exp)do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "content/handlers/html/form.c", sizeof("content/handlers/html/form.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 1115
, }; nslog__log(&_nslog_ctx, "getting element node name %"
"u"" failed with %d", element_idx, exp); } } while(0)
;
1116 dom_node_unref(element)dom_node_unref((dom_node *) (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 */
1123 res = form_dom_to_data_textarea(
1124 (dom_html_text_area_element *)element,
1125 charset,
1126 form->document_charset,
1127 &fetch_data_next);
1128
1129 } else if (dom_string_isequal(nodename, corestring_dom_SELECT)) {
1130 /* Form element is HTMLSelectElement */
1131 res = form_dom_to_data_select(
1132 (dom_html_select_element *)element,
1133 charset,
1134 form->document_charset,
1135 &fetch_data_next);
1136
1137 } else if (dom_string_isequal(nodename, corestring_dom_INPUT)) {
1138 /* Form element is HTMLInputElement */
1139 res = form_dom_to_data_input(
1140 (dom_html_input_element *)element,
1141 charset,
1142 form->document_charset,
1143 &submit_button,
1144 &fetch_data_next);
1145
1146 } else if (dom_string_isequal(nodename, corestring_dom_BUTTON)) {
1147 /* Form element is HTMLButtonElement */
1148 res = form_dom_to_data_button(
1149 (dom_html_button_element *)element,
1150 charset,
1151 form->document_charset,
1152 &submit_button,
1153 &fetch_data_next);
1154
1155 } else {
1156 /* Form element is not handled */
1157 NSLOG(netsurf, INFO,do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "content/handlers/html/form.c", sizeof("content/handlers/html/form.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 1160
, }; nslog__log(&_nslog_ctx, "Unhandled element type: %*s"
, (int)dom_string_byte_length(nodename), dom_string_data(nodename
)); } } while(0)
1158 "Unhandled element type: %*s",do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "content/handlers/html/form.c", sizeof("content/handlers/html/form.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 1160
, }; nslog__log(&_nslog_ctx, "Unhandled element type: %*s"
, (int)dom_string_byte_length(nodename), dom_string_data(nodename
)); } } while(0)
1159 (int)dom_string_byte_length(nodename),do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "content/handlers/html/form.c", sizeof("content/handlers/html/form.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 1160
, }; nslog__log(&_nslog_ctx, "Unhandled element type: %*s"
, (int)dom_string_byte_length(nodename), dom_string_data(nodename
)); } } while(0)
1160 dom_string_data(nodename))do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "content/handlers/html/form.c", sizeof("content/handlers/html/form.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 1160
, }; nslog__log(&_nslog_ctx, "Unhandled element type: %*s"
, (int)dom_string_byte_length(nodename), dom_string_data(nodename
)); } } while(0)
;
1161 res = NSERROR_DOM;
1162
1163 }
1164
1165 dom_string_unref(nodename);
1166 dom_node_unref(element)dom_node_unref((dom_node *) (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
1197form_url_encode(struct form *form,
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((void*)0)) {
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, true1, NULL((void*)0), &name);
1217 if (res != NSERROR_OK) {
1218 free(s);
1219 return res;
1220 }
1221
1222 res = url_escape(control->value, true1, NULL((void*)0), &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((void*)0)) {
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
1263form_select_menu_scroll_callback(void *client_data,
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) {
1271 case SCROLLBAR_MSG_MOVED:
1272 menu->callback(menu->client_data,
1273 0, 0,
1274 menu->width,
1275 menu->height);
1276 break;
1277 case SCROLLBAR_MSG_SCROLL_START:
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
1286 browser_window_set_drag_type(html->bw,
1287 DRAGGING_CONTENT_SCROLLBAR, &rect);
1288
1289 menu->scroll_capture = true1;
1290 }
1291 break;
1292 case SCROLLBAR_MSG_SCROLL_FINISHED:
1293 menu->scroll_capture = false0;
1294
1295 browser_window_set_drag_type(html->bw,
1296 DRAGGING_NONE, NULL((void*)0));
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
1313form__select_process_selection(html_content *html,
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)((control != ((void*)0)) ? (void) (0) : __assert_fail ("control != NULL"
, "content/handlers/html/form.c", 1322, __extension__ __PRETTY_FUNCTION__
))
;
1323 assert(html != NULL)((html != ((void*)0)) ? (void) (0) : __assert_fail ("html != NULL"
, "content/handlers/html/form.c", 1323, __extension__ __PRETTY_FUNCTION__
))
;
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((void*)0);
1334 count++, o = o->next) {
1335 if (!control->data.select.multiple && o->selected) {
1336 o->selected = false0;
1337 dom_html_option_element_set_selected(o->node, false0);
1338 }
1339
1340 if (count == item) {
1341 if (control->data.select.multiple) {
1342 if (o->selected) {
1343 o->selected = false0;
1344 dom_html_option_element_set_selected(
1345 o->node, false0);
1346 control->data.select.num_selected--;
1347 } else {
1348 o->selected = true1;
1349 dom_html_option_element_set_selected(
1350 o->node, true1);
1351 control->data.select.num_selected++;
1352 }
1353 } else {
1354 dom_html_option_element_set_selected(
1355 o->node, true1);
1356 o->selected = true1;
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 +
1413 line_height * SELECT_LINE_SPACING0.2;
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((void*)0)) {
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((void*)0)) {
1436 return;
1437 }
1438
1439 control->form = form;
1440
1441 if (form->controls != NULL((void*)0)) {
1442 assert(form->last_control)((form->last_control) ? (void) (0) : __assert_fail ("form->last_control"
, "content/handlers/html/form.c", 1442, __extension__ __PRETTY_FUNCTION__
))
;
1443
1444 form->last_control->next = control;
1445 control->prev = form->last_control;
1446 control->next = NULL((void*)0);
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)((control != ((void*)0)) ? (void) (0) : __assert_fail ("control != NULL"
, "content/handlers/html/form.c", 1458, __extension__ __PRETTY_FUNCTION__
))
;
1459
1460 NSLOG(netsurf, INFO, "Control:%p name:%p value:%p initial:%p",do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "content/handlers/html/form.c", sizeof("content/handlers/html/form.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 1461
, }; nslog__log(&_nslog_ctx, "Control:%p name:%p value:%p initial:%p"
, control, control->name, control->value, control->initial_value
); } } while(0)
1461 control, control->name, control->value, control->initial_value)do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "content/handlers/html/form.c", sizeof("content/handlers/html/form.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 1461
, }; nslog__log(&_nslog_ctx, "Control:%p name:%p value:%p initial:%p"
, control, control->name, control->value, control->initial_value
); } } while(0)
;
1462 free(control->name);
1463 free(control->value);
1464 free(control->initial_value);
1465 if (control->last_synced_value != NULL((void*)0)) {
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,do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "content/handlers/html/form.c", sizeof("content/handlers/html/form.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 1477
, }; nslog__log(&_nslog_ctx, "select option:%p text:%p value:%p"
, option, option->text, option->value); } } while(0)
1476 "select option:%p text:%p value:%p", option,do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "content/handlers/html/form.c", sizeof("content/handlers/html/form.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 1477
, }; nslog__log(&_nslog_ctx, "select option:%p text:%p value:%p"
, option, option->text, option->value); } } while(0)
1477 option->text, option->value)do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static
nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf
, NSLOG_LEVEL_INFO, "content/handlers/html/form.c", sizeof("content/handlers/html/form.c"
) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 1477
, }; nslog__log(&_nslog_ctx, "select option:%p text:%p value:%p"
, option, option->text, option->value); } } while(0)
;
1478 free(option->text);
1479 free(option->value);
1480 free(option);
1481 }
1482 if (control->data.select.menu != NULL((void*)0)) {
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((void*)0)) {
1492 dom_string_unref(control->data.text.initial);
1493 }
1494
1495 if (control->data.text.ta != NULL((void*)0)) {
1496 textarea_destroy(control->data.text.ta);
1497 }
1498 }
1499
1500 /* unlink the control from the form */
1501 if (control->form != NULL((void*)0)) {
1502 for (c = control->form->controls; c != NULL((void*)0); 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((void*)0);
1515 break;
1516 }
1517 }
1518 }
1519
1520 if (control->node_value != NULL((void*)0)) {
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_Bool form_add_option(struct form_control *control, char *value, char *text,
1530 bool_Bool selected, void *node)
1531{
1532 struct form_option *option;
1533
1534 assert(control)((control) ? (void) (0) : __assert_fail ("control", "content/handlers/html/form.c"
, 1534, __extension__ __PRETTY_FUNCTION__))
;
1535 assert(control->type == GADGET_SELECT)((control->type == GADGET_SELECT) ? (void) (0) : __assert_fail
("control->type == GADGET_SELECT", "content/handlers/html/form.c"
, 1535, __extension__ __PRETTY_FUNCTION__))
;
1536
1537 option = calloc(1, sizeof *option);
1538 if (!option)
1539 return false0;
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 = true1;
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 true1;
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,
1571 select_menu_redraw_callback callback,
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((void*)0)) {
1584
1585 menu = calloc(1, sizeof (struct form_select_menu));
1586 if (menu == NULL((void*)0)) {
1587 return NSERROR_NOMEM;
1588 }
1589
1590 control->data.select.menu = menu;
1591
1592 box = control->box;
1593
1594 menu->width = box->width +
1595 box->border[RIGHT].width + box->padding[RIGHT] +
1596 box->border[LEFT].width + box->padding[LEFT];
1597
1598 font_plot_style_from_css(&html->unit_len_ctx,
1599 control->box->style, &fstyle);
1600 menu->f_size = fstyle.size;
1601
1602 menu->line_height = FIXTOINT(FDIV((FMUL(FLTTOFIX(1.2),(((css_divide_fixed((((css_multiply_fixed((((css_fixed) ((1.2
) * (float) (1 << 10)))), ((css_multiply_fixed((html->
unit_len_ctx.device_dpi), ((css_int_to_fixed(fstyle.size / (1
<< (10)))))))))))), (0x00012000)))) >> 10)
1603 FMUL(html->unit_len_ctx.device_dpi,(((css_divide_fixed((((css_multiply_fixed((((css_fixed) ((1.2
) * (float) (1 << 10)))), ((css_multiply_fixed((html->
unit_len_ctx.device_dpi), ((css_int_to_fixed(fstyle.size / (1
<< (10)))))))))))), (0x00012000)))) >> 10)
1604 INTTOFIX(fstyle.size / PLOT_STYLE_SCALE)))),(((css_divide_fixed((((css_multiply_fixed((((css_fixed) ((1.2
) * (float) (1 << 10)))), ((css_multiply_fixed((html->
unit_len_ctx.device_dpi), ((css_int_to_fixed(fstyle.size / (1
<< (10)))))))))))), (0x00012000)))) >> 10)
1605 F_72))(((css_divide_fixed((((css_multiply_fixed((((css_fixed) ((1.2
) * (float) (1 << 10)))), ((css_multiply_fixed((html->
unit_len_ctx.device_dpi), ((css_int_to_fixed(fstyle.size / (1
<< (10)))))))))))), (0x00012000)))) >> 10)
;
1606
1607 line_height_with_spacing = menu->line_height +
1608 menu->line_height *
1609 SELECT_LINE_SPACING0.2;
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_HEIGHT210) {
1616 menu->height = MAX_SELECT_HEIGHT210;
1617 }
1618
1619 menu->client_data = client_data;
1620 menu->callback = callback;
1621 res = scrollbar_create(false0,
1622 menu->height,
1623 total_height,
1624 menu->height,
1625 control,
1626 form_select_menu_scroll_callback,
1627 &(menu->scrollbar));
1628 if (res != NSERROR_OK) {
1629 control->data.select.menu = NULL((void*)0);
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 */
1645void form_free_select_menu(struct form_control *control)
1646{
1647 if (control->data.select.menu->scrollbar != NULL((void*)0))
1648 scrollbar_destroy(control->data.select.menu->scrollbar);
1649 free(control->data.select.menu);
1650 control->data.select.menu = NULL((void*)0);
1651}
1652
1653
1654/* exported interface documented in html/form_internal.h */
1655bool_Bool
1656form_redraw_select_menu(struct form_control *control,
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_WIDTH16;
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 +
1687 line_height * SELECT_LINE_SPACING0.2;
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 false0;
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 false0;
1728 }
1729
1730 x0 = x0 + SELECT_BORDER_WIDTH1;
1731 y0 = y0 + SELECT_BORDER_WIDTH1;
1732 x1 = x1 - SELECT_BORDER_WIDTH1;
1733 y1 = y1 - SELECT_BORDER_WIDTH1;
1734 height = height - 2 * SELECT_BORDER_WIDTH1;
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 false0;
1743 }
1744
1745 res = ctx->plot->rectangle(ctx, plot_style_fill_lightwbasec, &r);
1746 if (res != NSERROR_OK) {
1747 return false0;
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_SPACING0.2));
1760 text_x = x + (box->border[LEFT].width + box->padding[LEFT]) * scale;
1761
1762 plot_fstyle_entry.size = menu->f_size;
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 false0;
1777 }
1778 }
1779
1780 y2 = text_pos_offset + item_y;
1781 res = ctx->plot->text(ctx,
1782 &plot_fstyle_entry,
1783 text_x, y2,
1784 option->text, strlen(option->text));
1785 if (res != NSERROR_OK) {
1786 return false0;
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_WIDTH16,
1795 y_cp,
1796 clip, scale, ctx);
1797 if (res != NSERROR_OK) {
1798 return false0;
1799 }
1800
1801 return true1;
1802}
1803
1804
1805/* private interface described in html/form_internal.h */
1806bool_Bool
1807form_clip_inside_select_menu(struct form_control *control,
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 true1;
1828
1829 return false0;
1830}
1831
1832
1833/* exported interface documented in netsurf/form.h */
1834nserror form_select_process_selection(struct form_control *control, int item)
1835{
1836 assert(control != NULL)((control != ((void*)0)) ? (void) (0) : __assert_fail ("control != NULL"
, "content/handlers/html/form.c", 1836, __extension__ __PRETTY_FUNCTION__
))
;
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((void*)0)) && (item > 0)) {
1850 opt = opt->next;
1851 item--;
1852 }
1853 return opt;
1854}
1855
1856
1857/* exported interface documented in netsurf/form.h */
1858char *form_control_get_name(struct form_control *control)
1859{
1860 return control->name;
1861}
1862
1863
1864/* exported interface documented in netsurf/form.h */
1865nserror form_control_bounding_rect(struct form_control *control, struct rect *r)
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 *
1874form_select_mouse_action(struct form_control *control,
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((void*)0);
1881 bool_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_WIDTH16;
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;
1895 return scrollbar_mouse_status_to_message(
1896 scrollbar_mouse_action(menu->scrollbar,
1897 mouse, x, y));
1898 }
1899
1900
1901 if (x > x0 && x < scrollbar_x && y > y0 && y < y1) {
1902 /* over option area */
1903
1904 if (mouse & (BROWSER_MOUSE_CLICK_1 | BROWSER_MOUSE_CLICK_2))
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
1924form_select_mouse_drag_end(struct form_control *control,
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 +
1939 box->padding[BOTTOM] + box->padding[TOP];
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_WIDTH16;
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_WIDTH16 && 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 */
1965void form_select_get_dimensions(struct form_control *control,
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 */
1974void form_select_menu_callback(void *client_data,
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)((radio) ? (void) (0) : __assert_fail ("radio", "content/handlers/html/form.c"
, 1998, __extension__ __PRETTY_FUNCTION__))
;
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((void*)0);
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((void*)0)) ||
2024 (radio->name == NULL((void*)0)) ||
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 = false0;
2034 dom_html_input_element_set_checked(control->node, false0);
2035 html__redraw_a_box(radio->html, control->box);
2036 }
2037 }
2038
2039 radio->selected = true1;
2040 dom_html_input_element_set_checked(radio->node, true1);
2041 html__redraw_a_box(radio->html, radio->box);
2042}
2043
2044
2045/* private interface described in html/form_internal.h */
2046nserror
2047form_submit(nsurl *page_url,
2048 struct browser_window *target,
2049 struct form *form,
2050 struct form_control *submit_button)
2051{
2052 nserror res;
2053 char *data = NULL((void*)0); /* encoded form data */
2054 struct fetch_multipart_data *success = NULL((void*)0); /* 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)((form != ((void*)0)) ? (void) (0) : __assert_fail ("form != NULL"
, "content/handlers/html/form.c", 2058, __extension__ __PRETTY_FUNCTION__
))
;
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) {
2069 fetch_multipart_data_destroy(success);
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,
2083 BW_NAVIGATE_HISTORY,
2084 NULL((void*)0),
2085 NULL((void*)0),
2086 NULL((void*)0));
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,
2100 BW_NAVIGATE_HISTORY,
2101 data,
2102 NULL((void*)0),
2103 NULL((void*)0));
2104 free(data);
2105 }
2106 break;
2107
2108 case method_POST_MULTIPART:
2109 res = browser_window_navigate(target,
2110 action_url,
2111 page_url,
2112 BW_NAVIGATE_HISTORY,
2113 NULL((void*)0),
2114 success,
2115 NULL((void*)0));
2116
2117 break;
2118 }
2119
2120 nsurl_unref(action_url);
2121 fetch_multipart_data_destroy(success);
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((void*)0)) {
2137 free(control->value);
2138 }
2139 control->value = value;
2140 if (control->node != NULL((void*)0)) {
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(
Value stored to 'err' is never read
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 */
2164 form_gadget_sync_with_dom(control);
2165}
2166
2167
2168/* Exported API, see html/form_internal.h */
2169void
2170form_gadget_sync_with_dom(struct form_control *control)
2171{
2172 dom_exception exc;
2173 dom_string *value = NULL((void*)0);
2174 bool_Bool changed_dom = false0;
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 = true1;
2188
2189 /* If we've changed value, sync that toward the DOM */
2190 if ((control->last_synced_value == NULL((void*)0) &&
2191 control->value != NULL((void*)0) &&
2192 control->value[0] != '\0') ||
2193 (control->last_synced_value != NULL((void*)0) &&
2194 control->value != NULL((void*)0) &&
2195 strcmp(control->value, control->last_synced_value) != 0)) {
2196 char *dup = strdup(control->value);
2197 if (dup == NULL((void*)0)) {
2198 goto out;
2199 }
2200 if (control->last_synced_value != NULL((void*)0)) {
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((void*)0)) {
2210 dom_string_unref(control->node_value);
2211 }
2212 control->node_value = value;
2213 value = NULL((void*)0);
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 = true1;
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((void*)0);
2245 if (value_s == NULL((void*)0)) {
2246 goto out;
2247 }
2248 dup = strdup(value_s);
2249 if (dup == NULL((void*)0)) {
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((void*)0)) {
2259 textarea_set_text(control->data.text.ta,
2260 value_s);
2261 }
2262 }
2263 control->node_value = value;
2264 value = NULL((void*)0);
2265 }
2266
2267out:
2268 if (value != NULL((void*)0))
2269 dom_string_unref(value);
2270 control->syncing = false0;
2271}
2272
2273
2274/* exported interface documented in html/form_internal.h */
2275struct form *
2276form_new(void *node,
2277 const char *action,
2278 const char *target,
2279 form_method method,
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((void*)0);
2288
2289 form->action = strdup(action != NULL((void*)0) ? action : "");
2290 if (form->action == NULL((void*)0)) {
2291 free(form);
2292 return NULL((void*)0);
2293 }
2294
2295 form->target = target != NULL((void*)0) ? strdup(target) : NULL((void*)0);
2296 if (target != NULL((void*)0) && form->target == NULL((void*)0)) {
2297 free(form->action);
2298 free(form);
2299 return NULL((void*)0);
2300 }
2301
2302 form->method = method;
2303
2304 form->accept_charsets = charset != NULL((void*)0) ? strdup(charset) : NULL((void*)0);
2305 if (charset != NULL((void*)0) && form->accept_charsets == NULL((void*)0)) {
2306 free(form->target);
2307 free(form->action);
2308 free(form);
2309 return NULL((void*)0);
2310 }
2311
2312 form->document_charset = doc_charset != NULL((void*)0) ? strdup(doc_charset)
2313 : NULL((void*)0);
2314 if (doc_charset && form->document_charset == NULL((void*)0)) {
2315 free(form->accept_charsets);
2316 free(form->target);
2317 free(form->action);
2318 free(form);
2319 return NULL((void*)0);
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((void*)0); c = d) {
2334 d = c->next;
2335
2336 form_free_control(c);
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 */
2349struct form_control *form_new_control(void *node, form_control_type type)
2350{
2351 struct form_control *control;
2352
2353 control = calloc(1, sizeof *control);
2354 if (control == NULL((void*)0))
2355 return NULL((void*)0);
2356
2357 control->node = node;
2358 control->type = type;
2359
2360 return control;
2361}