1 /*
2  * Copyright 2005 James Bursa <bursa@users.sourceforge.net>
3  * Copyright 2003 Phil Mellor <monkeyson@users.sourceforge.net>
4  * Copyright 2005 John M Bell <jmb202@ecs.soton.ac.uk>
5  * Copyright 2006 Richard Wilson <info@tinct.net>
6  * Copyright 2008 Michael Drake <tlsa@netsurf-browser.org>
7  * Copyright 2020 Vincent Sanders <vince@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  * Implementation of special element handling conversion.
27  */
28 
29 #include <stdbool.h>
30 #include <dom/dom.h>
31 
32 #include "utils/nsoption.h"
33 #include "utils/corestrings.h"
34 #include "utils/log.h"
35 #include "utils/messages.h"
36 #include "utils/talloc.h"
37 #include "utils/string.h"
38 #include "utils/ascii.h"
39 #include "utils/nsurl.h"
40 #include "netsurf/plot_style.h"
41 #include "css/hints.h"
42 #include "desktop/frame_types.h"
43 #include "content/content_factory.h"
44 
45 #include "html/html.h"
46 #include "html/private.h"
47 #include "html/object.h"
48 #include "html/box.h"
49 #include "html/box_manipulate.h"
50 #include "html/box_construct.h"
51 #include "html/box_special.h"
52 #include "html/box_textarea.h"
53 #include "html/form_internal.h"
54 
55 
56 static const content_type image_types = CONTENT_IMAGE;
57 
58 
59 /**
60  * determine if a box is the root node
61  *
62  * \param n node to check
63  * \return true if node is root else false.
64  */
box_is_root(dom_node * n)65 static inline bool box_is_root(dom_node *n)
66 {
67 	dom_node *parent;
68 	dom_node_type type;
69 	dom_exception err;
70 
71 	err = dom_node_get_parent_node(n, &parent);
72 	if (err != DOM_NO_ERR)
73 		return false;
74 
75 	if (parent != NULL) {
76 		err = dom_node_get_node_type(parent, &type);
77 
78 		dom_node_unref(parent);
79 
80 		if (err != DOM_NO_ERR)
81 			return false;
82 
83 		if (type != DOM_DOCUMENT_NODE)
84 			return false;
85 	}
86 
87 	return true;
88 }
89 
90 
91 /**
92  * Destructor for object_params, for &lt;object&gt; elements
93  *
94  * \param o  The object params being destroyed.
95  * \return 0 to allow talloc to continue destroying the tree.
96  */
box_object_talloc_destructor(struct object_params * o)97 static int box_object_talloc_destructor(struct object_params *o)
98 {
99 	if (o->codebase != NULL)
100 		nsurl_unref(o->codebase);
101 	if (o->classid != NULL)
102 		nsurl_unref(o->classid);
103 	if (o->data != NULL)
104 		nsurl_unref(o->data);
105 
106 	return 0;
107 }
108 
109 
110 /**
111  * Parse a multi-length-list, as defined by HTML 4.01.
112  *
113  * \param ds dom string to parse
114  * \param count updated to number of entries
115  * \return array of struct box_multi_length, or 0 on memory exhaustion
116  */
117 static struct frame_dimension *
box_parse_multi_lengths(const dom_string * ds,unsigned int * count)118 box_parse_multi_lengths(const dom_string *ds, unsigned int *count)
119 {
120 	char *end;
121 	unsigned int i, n;
122 	struct frame_dimension *length;
123 	const char *s;
124 
125 	s = dom_string_data(ds);
126 
127 	for (i = 0, n = 1; s[i]; i++)
128 		if (s[i] == ',')
129 			n++;
130 
131 	length = calloc(n, sizeof(struct frame_dimension));
132 	if (!length)
133 		return NULL;
134 
135 	for (i = 0; i != n; i++) {
136 		while (ascii_is_space(*s)) {
137 			s++;
138 		}
139 		length[i].value = strtof(s, &end);
140 		if (length[i].value <= 0) {
141 			length[i].value = 1;
142 		}
143 		s = end;
144 		switch (*s) {
145 			case '%':
146 				length[i].unit = FRAME_DIMENSION_PERCENT;
147 				break;
148 			case '*':
149 				length[i].unit = FRAME_DIMENSION_RELATIVE;
150 				break;
151 			default:
152 				length[i].unit = FRAME_DIMENSION_PIXELS;
153 				break;
154 		}
155 		while (*s && *s != ',') {
156 			s++;
157 		}
158 		if (*s == ',') {
159 			s++;
160 		}
161 	}
162 
163 	*count = n;
164 	return length;
165 }
166 
167 
168 /**
169  * Destructor for content_html_frames, for frame elements
170  *
171  * \param f  The frame params being destroyed.
172  * \return 0 to allow talloc to continue destroying the tree.
173  */
box_frames_talloc_destructor(struct content_html_frames * f)174 static int box_frames_talloc_destructor(struct content_html_frames *f)
175 {
176 	if (f->url != NULL) {
177 		nsurl_unref(f->url);
178 		f->url = NULL;
179 	}
180 
181 	return 0;
182 }
183 
184 
185 /**
186  * create a frameset box tree
187  */
188 static bool
box_create_frameset(struct content_html_frames * f,dom_node * n,html_content * content)189 box_create_frameset(struct content_html_frames *f,
190 		    dom_node *n,
191 		    html_content *content)
192 {
193 	unsigned int row, col, index, i;
194 	unsigned int rows = 1, cols = 1;
195 	dom_string *s;
196 	dom_exception err;
197 	nsurl *url;
198 	struct frame_dimension *row_height = 0, *col_width = 0;
199 	dom_node *c, *next;
200 	struct content_html_frames *frame;
201 	bool default_border = true;
202 	colour default_border_colour = 0x000000;
203 
204 	/* parse rows and columns */
205 	err = dom_element_get_attribute(n, corestring_dom_rows, &s);
206 	if (err == DOM_NO_ERR && s != NULL) {
207 		row_height = box_parse_multi_lengths(s, &rows);
208 		dom_string_unref(s);
209 		if (row_height == NULL)
210 			return false;
211 	} else {
212 		row_height = calloc(1, sizeof(struct frame_dimension));
213 		if (row_height == NULL)
214 			return false;
215 		row_height->value = 100;
216 		row_height->unit = FRAME_DIMENSION_PERCENT;
217 	}
218 
219 	err = dom_element_get_attribute(n, corestring_dom_cols, &s);
220 	if (err == DOM_NO_ERR && s != NULL) {
221 		col_width = box_parse_multi_lengths(s, &cols);
222 		dom_string_unref(s);
223 		if (col_width == NULL) {
224 			free(row_height);
225 			return false;
226 		}
227 	} else {
228 		col_width = calloc(1, sizeof(struct frame_dimension));
229 		if (col_width == NULL) {
230 			free(row_height);
231 			return false;
232 		}
233 		col_width->value = 100;
234 		col_width->unit = FRAME_DIMENSION_PERCENT;
235 	}
236 
237 	/* common extension: border="0|1" to control all children */
238 	err = dom_element_get_attribute(n, corestring_dom_border, &s);
239 	if (err == DOM_NO_ERR && s != NULL) {
240 		if ((dom_string_data(s)[0] == '0') &&
241 				(dom_string_data(s)[1] == '\0'))
242 			default_border = false;
243 		dom_string_unref(s);
244 	}
245 
246 	/* common extension: frameborder="yes|no" to control all children */
247 	err = dom_element_get_attribute(n, corestring_dom_frameborder, &s);
248 	if (err == DOM_NO_ERR && s != NULL) {
249 		if (dom_string_caseless_lwc_isequal(s,
250 				corestring_lwc_no) == 0)
251 			default_border = false;
252 		dom_string_unref(s);
253 	}
254 
255 	/* common extension: bordercolor="#RRGGBB|<named colour>" to control
256 	 *all children */
257 	err = dom_element_get_attribute(n, corestring_dom_bordercolor, &s);
258 	if (err == DOM_NO_ERR && s != NULL) {
259 		css_color color;
260 
261 		if (nscss_parse_colour(dom_string_data(s), &color))
262 			default_border_colour = nscss_color_to_ns(color);
263 
264 		dom_string_unref(s);
265 	}
266 
267 	/* update frameset and create default children */
268 	f->cols = cols;
269 	f->rows = rows;
270 	f->scrolling = BW_SCROLLING_NO;
271 	f->children = talloc_array(content->bctx, struct content_html_frames,
272 								(rows * cols));
273 
274 	talloc_set_destructor(f->children, box_frames_talloc_destructor);
275 
276 	for (row = 0; row < rows; row++) {
277 		for (col = 0; col < cols; col++) {
278 			index = (row * cols) + col;
279 			frame = &f->children[index];
280 			frame->cols = 0;
281 			frame->rows = 0;
282 			frame->width = col_width[col];
283 			frame->height = row_height[row];
284 			frame->margin_width = 0;
285 			frame->margin_height = 0;
286 			frame->name = NULL;
287 			frame->url = NULL;
288 			frame->no_resize = false;
289 			frame->scrolling = BW_SCROLLING_AUTO;
290 			frame->border = default_border;
291 			frame->border_colour = default_border_colour;
292 			frame->children = NULL;
293 		}
294 	}
295 	free(col_width);
296 	free(row_height);
297 
298 	/* create the frameset windows */
299 	err = dom_node_get_first_child(n, &c);
300 	if (err != DOM_NO_ERR)
301 		return false;
302 
303 	for (row = 0; c != NULL && row < rows; row++) {
304 		for (col = 0; c != NULL && col < cols; col++) {
305 			while (c != NULL) {
306 				dom_node_type type;
307 				dom_string *name;
308 
309 				err = dom_node_get_node_type(c, &type);
310 				if (err != DOM_NO_ERR) {
311 					dom_node_unref(c);
312 					return false;
313 				}
314 
315 				err = dom_node_get_node_name(c, &name);
316 				if (err != DOM_NO_ERR) {
317 					dom_node_unref(c);
318 					return false;
319 				}
320 
321 				if (type != DOM_ELEMENT_NODE ||
322 					(!dom_string_caseless_lwc_isequal(
323 							name,
324 							corestring_lwc_frame) &&
325 					!dom_string_caseless_lwc_isequal(
326 							name,
327 							corestring_lwc_frameset
328 							))) {
329 					err = dom_node_get_next_sibling(c,
330 							&next);
331 					if (err != DOM_NO_ERR) {
332 						dom_string_unref(name);
333 						dom_node_unref(c);
334 						return false;
335 					}
336 
337 					dom_string_unref(name);
338 					dom_node_unref(c);
339 					c = next;
340 				} else {
341 					/* Got a FRAME or FRAMESET element */
342 					dom_string_unref(name);
343 					break;
344 				}
345 			}
346 
347 			if (c == NULL)
348 				break;
349 
350 			/* get current frame */
351 			index = (row * cols) + col;
352 			frame = &f->children[index];
353 
354 			/* nest framesets */
355 			err = dom_node_get_node_name(c, &s);
356 			if (err != DOM_NO_ERR) {
357 				dom_node_unref(c);
358 				return false;
359 			}
360 
361 			if (dom_string_caseless_lwc_isequal(s,
362 					corestring_lwc_frameset)) {
363 				dom_string_unref(s);
364 				frame->border = 0;
365 				if (box_create_frameset(frame, c,
366 						content) == false) {
367 					dom_node_unref(c);
368 					return false;
369 				}
370 
371 				err = dom_node_get_next_sibling(c, &next);
372 				if (err != DOM_NO_ERR) {
373 					dom_node_unref(c);
374 					return false;
375 				}
376 
377 				dom_node_unref(c);
378 				c = next;
379 				continue;
380 			}
381 
382 			dom_string_unref(s);
383 
384 			/* get frame URL (not required) */
385 			url = NULL;
386 			err = dom_element_get_attribute(c, corestring_dom_src, &s);
387 			if (err == DOM_NO_ERR && s != NULL) {
388 				box_extract_link(content, s, content->base_url,
389 						 &url);
390 				dom_string_unref(s);
391 			}
392 
393 			/* copy url */
394 			if (url != NULL) {
395 				/* no self-references */
396 				if (nsurl_compare(content->base_url, url,
397 						NSURL_COMPLETE) == false)
398 					frame->url = url;
399 				url = NULL;
400 			}
401 
402 			/* fill in specified values */
403 			err = dom_element_get_attribute(c, corestring_dom_name, &s);
404 			if (err == DOM_NO_ERR && s != NULL) {
405 				frame->name = talloc_strdup(content->bctx,
406 						dom_string_data(s));
407 				dom_string_unref(s);
408 			}
409 
410 			if (dom_element_has_attribute(c, corestring_dom_noresize,
411 						      &frame->no_resize) != DOM_NO_ERR) {
412 				/* If we can't read the attribute for some reason,
413 				 * assume we didn't have it.
414 				 */
415 				frame->no_resize = false;
416 			}
417 
418 			err = dom_element_get_attribute(c, corestring_dom_frameborder,
419 					&s);
420 			if (err == DOM_NO_ERR && s != NULL) {
421 				i = atoi(dom_string_data(s));
422 				frame->border = (i != 0);
423 				dom_string_unref(s);
424 			}
425 
426 			err = dom_element_get_attribute(c, corestring_dom_scrolling, &s);
427 			if (err == DOM_NO_ERR && s != NULL) {
428 				if (dom_string_caseless_lwc_isequal(s,
429 						corestring_lwc_yes))
430 					frame->scrolling = BW_SCROLLING_YES;
431 				else if (dom_string_caseless_lwc_isequal(s,
432 						corestring_lwc_no))
433 					frame->scrolling = BW_SCROLLING_NO;
434 				dom_string_unref(s);
435 			}
436 
437 			err = dom_element_get_attribute(c, corestring_dom_marginwidth,
438 					&s);
439 			if (err == DOM_NO_ERR && s != NULL) {
440 				frame->margin_width = atoi(dom_string_data(s));
441 				dom_string_unref(s);
442 			}
443 
444 			err = dom_element_get_attribute(c, corestring_dom_marginheight,
445 					&s);
446 			if (err == DOM_NO_ERR && s != NULL) {
447 				frame->margin_height = atoi(dom_string_data(s));
448 				dom_string_unref(s);
449 			}
450 
451 			err = dom_element_get_attribute(c, corestring_dom_bordercolor,
452 					&s);
453 			if (err == DOM_NO_ERR && s != NULL) {
454 				css_color color;
455 
456 				if (nscss_parse_colour(dom_string_data(s),
457 						&color))
458 					frame->border_colour =
459 						nscss_color_to_ns(color);
460 
461 				dom_string_unref(s);
462 			}
463 
464 			/* advance */
465 			err = dom_node_get_next_sibling(c, &next);
466 			if (err != DOM_NO_ERR) {
467 				dom_node_unref(c);
468 				return false;
469 			}
470 
471 			dom_node_unref(c);
472 			c = next;
473 		}
474 	}
475 
476 	/* If the last child wasn't a frame, we still need to unref it */
477 	if (c != NULL) {
478 		dom_node_unref(c);
479 	}
480 
481 	return true;
482 }
483 
484 
485 /**
486  * Destructor for content_html_iframe, for &lt;iframe&gt; elements
487  *
488  * \param f The iframe params being destroyed.
489  * \return 0 to allow talloc to continue destroying the tree.
490  */
box_iframes_talloc_destructor(struct content_html_iframe * f)491 static int box_iframes_talloc_destructor(struct content_html_iframe *f)
492 {
493 	if (f->url != NULL) {
494 		nsurl_unref(f->url);
495 		f->url = NULL;
496 	}
497 
498 	return 0;
499 }
500 
501 
502 /**
503  * Get the value of a dom node element's attribute.
504  *
505  * \param  n	      dom element node
506  * \param  attribute  name of attribute
507  * \param  context    talloc context for result buffer
508  * \param  value      updated to value, if the attribute is present
509  * \return  true on success, false if attribute present but memory exhausted
510  *
511  * \note returning true does not imply that the attribute was found. If the
512  * attribute was not found, *value will be unchanged.
513  */
514 static bool
box_get_attribute(dom_node * n,const char * attribute,void * context,char ** value)515 box_get_attribute(dom_node *n,
516 		  const char *attribute,
517 		  void *context,
518 		  char **value)
519 {
520 	char *result;
521 	dom_string *attr, *attr_name;
522 	dom_exception err;
523 
524 	err = dom_string_create_interned((const uint8_t *) attribute,
525 			strlen(attribute), &attr_name);
526 	if (err != DOM_NO_ERR)
527 		return false;
528 
529 	err = dom_element_get_attribute(n, attr_name, &attr);
530 	if (err != DOM_NO_ERR) {
531 		dom_string_unref(attr_name);
532 		return false;
533 	}
534 
535 	dom_string_unref(attr_name);
536 
537 	if (attr != NULL) {
538 		result = talloc_strdup(context, dom_string_data(attr));
539 
540 		dom_string_unref(attr);
541 
542 		if (result == NULL)
543 			return false;
544 
545 		*value = result;
546 	}
547 
548 	return true;
549 }
550 
551 
552 /**
553  * Helper function for adding textarea widget to box.
554  *
555  * This is a load of hacks to ensure boxes replaced with textareas
556  * can be handled by the layout code.
557  */
558 static bool
box_input_text(html_content * html,struct box * box,struct dom_node * node)559 box_input_text(html_content *html, struct box *box, struct dom_node *node)
560 {
561 	struct box *inline_container, *inline_box;
562 
563 	box->type = BOX_INLINE_BLOCK;
564 
565 	inline_container = box_create(NULL, 0, false, 0, 0, 0, 0, html->bctx);
566 	if (!inline_container)
567 		return false;
568 	inline_container->type = BOX_INLINE_CONTAINER;
569 	inline_box = box_create(NULL, box->style, false, 0, 0, box->title, 0,
570 			html->bctx);
571 	if (!inline_box)
572 		return false;
573 	inline_box->type = BOX_TEXT;
574 	inline_box->text = talloc_strdup(html->bctx, "");
575 
576 	box_add_child(inline_container, inline_box);
577 	box_add_child(box, inline_container);
578 
579 	return box_textarea_create_textarea(html, box, node);
580 }
581 
582 
583 /**
584  * Add an option to a form select control (helper function for box_select()).
585  *
586  * \param  control  select containing the &lt;option&gt;
587  * \param  n	    xml element node for &lt;option&gt;
588  * \return  true on success, false on memory exhaustion
589  */
box_select_add_option(struct form_control * control,dom_node * n)590 static bool box_select_add_option(struct form_control *control, dom_node *n)
591 {
592 	char *value = NULL;
593 	char *text = NULL;
594 	char *text_nowrap = NULL;
595 	bool selected;
596 	dom_string *content, *s;
597 	dom_exception err;
598 
599 	err = dom_node_get_text_content(n, &content);
600 	if (err != DOM_NO_ERR)
601 		return false;
602 
603 	if (content != NULL) {
604 		text = squash_whitespace(dom_string_data(content));
605 		dom_string_unref(content);
606 	} else {
607 		text = strdup("");
608 	}
609 
610 	if (text == NULL)
611 		goto no_memory;
612 
613 	err = dom_element_get_attribute(n, corestring_dom_value, &s);
614 	if (err == DOM_NO_ERR && s != NULL) {
615 		value = strdup(dom_string_data(s));
616 		dom_string_unref(s);
617 	} else {
618 		value = strdup(text);
619 	}
620 
621 	if (value == NULL)
622 		goto no_memory;
623 
624 	if (dom_element_has_attribute(n, corestring_dom_selected, &selected) != DOM_NO_ERR) {
625 		/* Assume not selected if we can't read the attribute presence */
626 		selected = false;
627 	}
628 
629 	/* replace spaces/TABs with hard spaces to prevent line wrapping */
630 	text_nowrap = cnv_space2nbsp(text);
631 	if (text_nowrap == NULL)
632 		goto no_memory;
633 
634 	if (form_add_option(control, value, text_nowrap, selected, n) == false)
635 		goto no_memory;
636 
637 	free(text);
638 
639 	return true;
640 
641 no_memory:
642 	free(value);
643 	free(text);
644 	free(text_nowrap);
645 	return false;
646 }
647 
648 
649 /**
650  * \name  Special case element handlers
651  *
652  * These functions are called by box_construct_element() when an element is
653  * being converted, according to the entries in element_table.
654  *
655  * The parameters are the xmlNode, the content for the document, and a partly
656  * filled in box structure for the element.
657  *
658  * Return true on success, false on memory exhaustion. Set *convert_children
659  * to false if children of this element in the XML tree should be skipped (for
660  * example, if they have been processed in some special way already).
661  *
662  * Elements ordered as in the HTML 4.01 specification. Section numbers in
663  * brackets [] refer to the spec.
664  *
665  * \{
666  */
667 
668 /**
669  * special element handler for Anchor [12.2].
670  */
671 static bool
box_a(dom_node * n,html_content * content,struct box * box,bool * convert_children)672 box_a(dom_node *n,
673       html_content *content,
674       struct box *box,
675       bool *convert_children)
676 {
677 	bool ok;
678 	nsurl *url;
679 	dom_string *s;
680 	dom_exception err;
681 
682 	err = dom_element_get_attribute(n, corestring_dom_href, &s);
683 	if (err == DOM_NO_ERR && s != NULL) {
684 		ok = box_extract_link(content, s, content->base_url, &url);
685 		dom_string_unref(s);
686 		if (!ok)
687 			return false;
688 		if (url) {
689 			if (box->href != NULL)
690 				nsurl_unref(box->href);
691 			box->href = url;
692 		}
693 	}
694 
695 	/* name and id share the same namespace */
696 	err = dom_element_get_attribute(n, corestring_dom_name, &s);
697 	if (err == DOM_NO_ERR && s != NULL) {
698 		lwc_string *lwc_name;
699 
700 		err = dom_string_intern(s, &lwc_name);
701 
702 		dom_string_unref(s);
703 
704 		if (err == DOM_NO_ERR) {
705 			/* name replaces existing id
706 			 * TODO: really? */
707 			if (box->id != NULL)
708 				lwc_string_unref(box->id);
709 
710 			box->id = lwc_name;
711 		}
712 	}
713 
714 	/* target frame [16.3] */
715 	err = dom_element_get_attribute(n, corestring_dom_target, &s);
716 	if (err == DOM_NO_ERR && s != NULL) {
717 		if (dom_string_caseless_lwc_isequal(s,
718 				corestring_lwc__blank))
719 			box->target = TARGET_BLANK;
720 		else if (dom_string_caseless_lwc_isequal(s,
721 				corestring_lwc__top))
722 			box->target = TARGET_TOP;
723 		else if (dom_string_caseless_lwc_isequal(s,
724 				corestring_lwc__parent))
725 			box->target = TARGET_PARENT;
726 		else if (dom_string_caseless_lwc_isequal(s,
727 				corestring_lwc__self))
728 			/* the default may have been overridden by a
729 			 * <base target=...>, so this is different to 0 */
730 			box->target = TARGET_SELF;
731 		else {
732 			/* 6.16 says that frame names must begin with [a-zA-Z]
733 			 * This doesn't match reality, so just take anything */
734 			box->target = talloc_strdup(content->bctx,
735 					dom_string_data(s));
736 			if (!box->target) {
737 				dom_string_unref(s);
738 				return false;
739 			}
740 		}
741 		dom_string_unref(s);
742 	}
743 
744 	return true;
745 }
746 
747 
748 /**
749  * Document body special element handler [7.5.1].
750  */
751 static bool
box_body(dom_node * n,html_content * content,struct box * box,bool * convert_children)752 box_body(dom_node *n,
753 	 html_content *content,
754 	 struct box *box,
755 	 bool *convert_children)
756 {
757 	css_color color;
758 
759 	css_computed_background_color(box->style, &color);
760 	if (nscss_color_is_transparent(color)) {
761 		content->background_colour = NS_TRANSPARENT;
762 	} else {
763 		content->background_colour = nscss_color_to_ns(color);
764 	}
765 
766 	return true;
767 }
768 
769 
770 /**
771  * special element handler for forced line break [9.3.2].
772  */
773 static bool
box_br(dom_node * n,html_content * content,struct box * box,bool * convert_children)774 box_br(dom_node *n,
775        html_content *content,
776        struct box *box,
777        bool *convert_children)
778 {
779 	box->type = BOX_BR;
780 	return true;
781 }
782 
783 
784 /**
785  * special element handler for Push button [17.5].
786  */
787 static bool
box_button(dom_node * n,html_content * content,struct box * box,bool * convert_children)788 box_button(dom_node *n,
789 	   html_content *content,
790 	   struct box *box,
791 	   bool *convert_children)
792 {
793 	struct form_control *gadget;
794 
795 	gadget = html_forms_get_control_for_node(content->forms, n);
796 	if (!gadget)
797 		return false;
798 
799 	gadget->html = content;
800 	box->gadget = gadget;
801 	box->flags |= IS_REPLACED;
802 	gadget->box = box;
803 
804 	box->type = BOX_INLINE_BLOCK;
805 
806 	/* Just render the contents */
807 
808 	return true;
809 }
810 
811 
812 /**
813  * Canvas element
814  */
815 static bool
box_canvas(dom_node * n,html_content * content,struct box * box,bool * convert_children)816 box_canvas(dom_node *n,
817 	     html_content *content,
818 	     struct box *box,
819 	     bool *convert_children)
820 {
821 	/* If scripting is not enabled display the contents of canvas */
822 	if (!content->enable_scripting) {
823 		return true;
824 	}
825 	*convert_children = false;
826 
827 	if (box->style &&
828 	    ns_computed_display(box->style, box_is_root(n)) == CSS_DISPLAY_NONE)
829 		return true;
830 
831 	/* This is replaced content */
832 	box->flags |= IS_REPLACED | REPLACE_DIM;
833 
834 	return true;
835 }
836 
837 
838 /**
839  * Embedded object (not in any HTML specification:
840  * see http://wp.netscape.com/assist/net_sites/new_html3_prop.html )
841  */
842 static bool
box_embed(dom_node * n,html_content * content,struct box * box,bool * convert_children)843 box_embed(dom_node *n,
844 	  html_content *content,
845 	  struct box *box,
846 	  bool *convert_children)
847 {
848 	struct object_params *params;
849 	struct object_param *param;
850 	dom_namednodemap *attrs;
851 	unsigned long idx;
852 	uint32_t num_attrs;
853 	dom_string *src;
854 	dom_exception err;
855 
856 	if (box->style &&
857 	    ns_computed_display(box->style, box_is_root(n)) == CSS_DISPLAY_NONE)
858 		return true;
859 
860 	params = talloc(content->bctx, struct object_params);
861 	if (params == NULL)
862 		return false;
863 
864 	talloc_set_destructor(params, box_object_talloc_destructor);
865 
866 	params->data = NULL;
867 	params->type = NULL;
868 	params->codetype = NULL;
869 	params->codebase = NULL;
870 	params->classid = NULL;
871 	params->params = NULL;
872 
873 	/* src is a URL */
874 	err = dom_element_get_attribute(n, corestring_dom_src, &src);
875 	if (err != DOM_NO_ERR || src == NULL)
876 		return true;
877 	if (box_extract_link(content, src, content->base_url,
878 			     &params->data) == false) {
879 		dom_string_unref(src);
880 		return false;
881 	}
882 
883 	dom_string_unref(src);
884 
885 	if (params->data == NULL)
886 		return true;
887 
888 	/* Don't include ourself */
889 	if (nsurl_compare(content->base_url, params->data, NSURL_COMPLETE))
890 		return true;
891 
892 	/* add attributes as parameters to linked list */
893 	err = dom_node_get_attributes(n, &attrs);
894 	if (err != DOM_NO_ERR)
895 		return false;
896 
897 	err = dom_namednodemap_get_length(attrs, &num_attrs);
898 	if (err != DOM_NO_ERR) {
899 		dom_namednodemap_unref(attrs);
900 		return false;
901 	}
902 
903 	for (idx = 0; idx < num_attrs; idx++) {
904 		dom_attr *attr;
905 		dom_string *name, *value;
906 
907 		err = dom_namednodemap_item(attrs, idx, (void *) &attr);
908 		if (err != DOM_NO_ERR) {
909 			dom_namednodemap_unref(attrs);
910 			return false;
911 		}
912 
913 		err = dom_attr_get_name(attr, &name);
914 		if (err != DOM_NO_ERR) {
915 			dom_node_unref(attr);
916 			dom_namednodemap_unref(attrs);
917 			return false;
918 		}
919 
920 		if (dom_string_caseless_lwc_isequal(name, corestring_lwc_src)) {
921 			dom_node_unref(attr);
922 			dom_string_unref(name);
923 			continue;
924 		}
925 
926 		err = dom_attr_get_value(attr, &value);
927 		if (err != DOM_NO_ERR) {
928 			dom_node_unref(attr);
929 			dom_string_unref(name);
930 			dom_namednodemap_unref(attrs);
931 			return false;
932 		}
933 
934 		param = talloc(content->bctx, struct object_param);
935 		if (param == NULL) {
936 			dom_node_unref(attr);
937 			dom_string_unref(value);
938 			dom_string_unref(name);
939 			dom_namednodemap_unref(attrs);
940 			return false;
941 		}
942 
943 		param->name = talloc_strdup(content->bctx, dom_string_data(name));
944 		param->value = talloc_strdup(content->bctx, dom_string_data(value));
945 		param->type = NULL;
946 		param->valuetype = talloc_strdup(content->bctx, "data");
947 		param->next = NULL;
948 
949 		dom_string_unref(value);
950 		dom_string_unref(name);
951 		dom_node_unref(attr);
952 
953 		if (param->name == NULL || param->value == NULL ||
954 				param->valuetype == NULL) {
955 			dom_namednodemap_unref(attrs);
956 			return false;
957 		}
958 
959 		param->next = params->params;
960 		params->params = param;
961 	}
962 
963 	dom_namednodemap_unref(attrs);
964 
965 	box->object_params = params;
966 
967 	/* start fetch */
968 	box->flags |= IS_REPLACED;
969 	return html_fetch_object(content, params->data, box, CONTENT_ANY, false);
970 }
971 
972 
973 /**
974  * Window subdivision [16.2.1].
975  */
976 static bool
box_frameset(dom_node * n,html_content * content,struct box * box,bool * convert_children)977 box_frameset(dom_node *n,
978 	     html_content *content,
979 	     struct box *box,
980 	     bool *convert_children)
981 {
982 	bool ok;
983 
984 	if (content->frameset) {
985 		NSLOG(netsurf, INFO, "Error: multiple framesets in document.");
986 		/* Don't convert children */
987 		if (convert_children)
988 			*convert_children = false;
989 		/* And ignore this spurious frameset */
990 		box->type = BOX_NONE;
991 		return true;
992 	}
993 
994 	content->frameset = talloc_zero(content->bctx,
995 					struct content_html_frames);
996 	if (!content->frameset) {
997 		return false;
998 	}
999 
1000 	ok = box_create_frameset(content->frameset, n, content);
1001 	if (ok) {
1002 		box->type = BOX_NONE;
1003 	}
1004 
1005 	if (convert_children) {
1006 		*convert_children = false;
1007 	}
1008 	return ok;
1009 }
1010 
1011 
1012 /**
1013  * Inline subwindow [16.5].
1014  */
1015 static bool
box_iframe(dom_node * n,html_content * content,struct box * box,bool * convert_children)1016 box_iframe(dom_node *n,
1017 	   html_content *content,
1018 	   struct box *box,
1019 	   bool *convert_children)
1020 {
1021 	nsurl *url;
1022 	dom_string *s;
1023 	dom_exception err;
1024 	struct content_html_iframe *iframe;
1025 	int i;
1026 
1027 	if (box->style &&
1028 	    ns_computed_display(box->style, box_is_root(n)) == CSS_DISPLAY_NONE)
1029 		return true;
1030 
1031 	if (box->style &&
1032 	    css_computed_visibility(box->style) == CSS_VISIBILITY_HIDDEN) {
1033 		/* Don't create iframe discriptors for invisible iframes
1034 		 * TODO: handle hidden iframes at browser_window generation
1035 		 * time instead? */
1036 		return true;
1037 	}
1038 
1039 	/* get frame URL */
1040 	err = dom_element_get_attribute(n, corestring_dom_src, &s);
1041 	if (err != DOM_NO_ERR || s == NULL)
1042 		return true;
1043 	if (box_extract_link(content, s, content->base_url, &url) == false) {
1044 		dom_string_unref(s);
1045 		return false;
1046 	}
1047 	dom_string_unref(s);
1048 	if (url == NULL)
1049 		return true;
1050 
1051 	/* don't include ourself */
1052 	if (nsurl_compare(content->base_url, url, NSURL_COMPLETE)) {
1053 		nsurl_unref(url);
1054 		return true;
1055 	}
1056 
1057 	/* create a new iframe */
1058 	iframe = talloc(content->bctx, struct content_html_iframe);
1059 	if (iframe == NULL) {
1060 		nsurl_unref(url);
1061 		return false;
1062 	}
1063 
1064 	talloc_set_destructor(iframe, box_iframes_talloc_destructor);
1065 
1066 	iframe->box = box;
1067 	iframe->margin_width = 0;
1068 	iframe->margin_height = 0;
1069 	iframe->name = NULL;
1070 	iframe->url = url;
1071 	iframe->scrolling = BW_SCROLLING_AUTO;
1072 	iframe->border = true;
1073 
1074 	/* Add this iframe to the linked list of iframes */
1075 	iframe->next = content->iframe;
1076 	content->iframe = iframe;
1077 
1078 	/* fill in specified values */
1079 	err = dom_element_get_attribute(n, corestring_dom_name, &s);
1080 	if (err == DOM_NO_ERR && s != NULL) {
1081 		iframe->name = talloc_strdup(content->bctx, dom_string_data(s));
1082 		dom_string_unref(s);
1083 	}
1084 
1085 	err = dom_element_get_attribute(n, corestring_dom_frameborder, &s);
1086 	if (err == DOM_NO_ERR && s != NULL) {
1087 		i = atoi(dom_string_data(s));
1088 		iframe->border = (i != 0);
1089 		dom_string_unref(s);
1090 	}
1091 
1092 	err = dom_element_get_attribute(n, corestring_dom_bordercolor, &s);
1093 	if (err == DOM_NO_ERR && s != NULL) {
1094 		css_color color;
1095 
1096 		if (nscss_parse_colour(dom_string_data(s), &color))
1097 			iframe->border_colour = nscss_color_to_ns(color);
1098 
1099 		dom_string_unref(s);
1100 	}
1101 
1102 	err = dom_element_get_attribute(n, corestring_dom_scrolling, &s);
1103 	if (err == DOM_NO_ERR && s != NULL) {
1104 		if (dom_string_caseless_lwc_isequal(s,
1105 				corestring_lwc_yes))
1106 			iframe->scrolling = BW_SCROLLING_YES;
1107 		else if (dom_string_caseless_lwc_isequal(s,
1108 				corestring_lwc_no))
1109 			iframe->scrolling = BW_SCROLLING_NO;
1110 		dom_string_unref(s);
1111 	}
1112 
1113 	err = dom_element_get_attribute(n, corestring_dom_marginwidth, &s);
1114 	if (err == DOM_NO_ERR && s != NULL) {
1115 		iframe->margin_width = atoi(dom_string_data(s));
1116 		dom_string_unref(s);
1117 	}
1118 
1119 	err = dom_element_get_attribute(n, corestring_dom_marginheight, &s);
1120 	if (err == DOM_NO_ERR && s != NULL) {
1121 		iframe->margin_height = atoi(dom_string_data(s));
1122 		dom_string_unref(s);
1123 	}
1124 
1125 	/* box */
1126 	assert(box->style);
1127 	box->flags |= IFRAME;
1128 	box->flags |= IS_REPLACED;
1129 
1130 	/* Showing iframe, so don't show alternate content */
1131 	if (convert_children)
1132 		*convert_children = false;
1133 	return true;
1134 }
1135 
1136 
1137 /**
1138  * Embedded image [13.2].
1139  */
1140 static bool
box_image(dom_node * n,html_content * content,struct box * box,bool * convert_children)1141 box_image(dom_node *n,
1142 	  html_content *content,
1143 	  struct box *box,
1144 	  bool *convert_children)
1145 {
1146 	bool ok;
1147 	dom_string *s;
1148 	dom_exception err;
1149 	nsurl *url;
1150 	enum css_width_e wtype;
1151 	enum css_height_e htype;
1152 	css_fixed value = 0;
1153 	css_unit wunit = CSS_UNIT_PX;
1154 	css_unit hunit = CSS_UNIT_PX;
1155 
1156 	if (box->style &&
1157 	    ns_computed_display(box->style, box_is_root(n)) == CSS_DISPLAY_NONE)
1158 		return true;
1159 
1160 	/* handle alt text */
1161 	err = dom_element_get_attribute(n, corestring_dom_alt, &s);
1162 	if (err == DOM_NO_ERR && s != NULL) {
1163 		char *alt = squash_whitespace(dom_string_data(s));
1164 		dom_string_unref(s);
1165 		if (alt == NULL)
1166 			return false;
1167 		box->text = talloc_strdup(content->bctx, alt);
1168 		free(alt);
1169 		if (box->text == NULL)
1170 			return false;
1171 		box->length = strlen(box->text);
1172 	}
1173 
1174 	if (nsoption_bool(foreground_images) == false) {
1175 		return true;
1176 	}
1177 
1178 	/* imagemap associated with this image */
1179 	if (!box_get_attribute(n, "usemap", content->bctx, &box->usemap))
1180 		return false;
1181 	if (box->usemap && box->usemap[0] == '#')
1182 		box->usemap++;
1183 
1184 	/* get image URL */
1185 	err = dom_element_get_attribute(n, corestring_dom_src, &s);
1186 	if (err != DOM_NO_ERR || s == NULL)
1187 		return true;
1188 
1189 	if (box_extract_link(content, s, content->base_url, &url) == false) {
1190 		dom_string_unref(s);
1191 		return false;
1192 	}
1193 
1194 	dom_string_unref(s);
1195 
1196 	if (url == NULL)
1197 		return true;
1198 
1199 	/* start fetch */
1200 	box->flags |= IS_REPLACED;
1201 	ok = html_fetch_object(content, url, box, image_types, false);
1202 	nsurl_unref(url);
1203 
1204 	wtype = css_computed_width(box->style, &value, &wunit);
1205 	htype = css_computed_height(box->style, &value, &hunit);
1206 
1207 	if (wtype == CSS_WIDTH_SET &&
1208 	    wunit != CSS_UNIT_PCT &&
1209 	    htype == CSS_HEIGHT_SET &&
1210 	    hunit != CSS_UNIT_PCT) {
1211 		/* We know the dimensions the image will be shown at
1212 		 * before it's fetched. */
1213 		box->flags |= REPLACE_DIM;
1214 	}
1215 
1216 	return ok;
1217 }
1218 
1219 
1220 /**
1221  * Form control [17.4].
1222  */
1223 static bool
box_input(dom_node * n,html_content * content,struct box * box,bool * convert_children)1224 box_input(dom_node *n,
1225 	  html_content *content,
1226 	  struct box *box,
1227 	  bool *convert_children)
1228 {
1229 	struct form_control *gadget;
1230 	dom_string *type = NULL;
1231 	dom_exception err;
1232 	nsurl *url;
1233 	nserror error;
1234 
1235 	gadget = html_forms_get_control_for_node(content->forms, n);
1236 	if (gadget == NULL) {
1237 		return false;
1238 	}
1239 
1240 	box->gadget = gadget;
1241 	box->flags |= IS_REPLACED;
1242 	gadget->box = box;
1243 	gadget->html = content;
1244 
1245 	/* get entry type */
1246 	err = dom_element_get_attribute(n, corestring_dom_type, &type);
1247 	if ((err != DOM_NO_ERR) || (type == NULL)) {
1248 		/* no type so "text" is assumed */
1249 		if (box_input_text(content, box, n) == false) {
1250 			return false;
1251 		}
1252 		*convert_children = false;
1253 		return true;
1254 	}
1255 
1256 	if (dom_string_caseless_lwc_isequal(type, corestring_lwc_password)) {
1257 		if (box_input_text(content, box, n) == false)
1258 			goto no_memory;
1259 
1260 	} else if (dom_string_caseless_lwc_isequal(type, corestring_lwc_file)) {
1261 		box->type = BOX_INLINE_BLOCK;
1262 
1263 	} else if (dom_string_caseless_lwc_isequal(type,
1264 			corestring_lwc_hidden)) {
1265 		/* no box for hidden inputs */
1266 		box->type = BOX_NONE;
1267 
1268 	} else if ((dom_string_caseless_lwc_isequal(type,
1269 				corestring_lwc_checkbox) ||
1270 			dom_string_caseless_lwc_isequal(type,
1271 				corestring_lwc_radio))) {
1272 
1273 	} else if (dom_string_caseless_lwc_isequal(type,
1274 				corestring_lwc_submit) ||
1275 			dom_string_caseless_lwc_isequal(type,
1276 				corestring_lwc_reset) ||
1277 			dom_string_caseless_lwc_isequal(type,
1278 				corestring_lwc_button)) {
1279 		struct box *inline_container, *inline_box;
1280 
1281 		if (box_button(n, content, box, 0) == false)
1282 			goto no_memory;
1283 
1284 		inline_container = box_create(NULL, 0, false, 0, 0, 0, 0,
1285 				content->bctx);
1286 		if (inline_container == NULL)
1287 			goto no_memory;
1288 
1289 		inline_container->type = BOX_INLINE_CONTAINER;
1290 
1291 		inline_box = box_create(NULL, box->style, false, 0, 0,
1292 				box->title, 0, content->bctx);
1293 		if (inline_box == NULL)
1294 			goto no_memory;
1295 
1296 		inline_box->type = BOX_TEXT;
1297 
1298 		if (box->gadget->value != NULL)
1299 			inline_box->text = talloc_strdup(content->bctx,
1300 					box->gadget->value);
1301 		else if (box->gadget->type == GADGET_SUBMIT)
1302 			inline_box->text = talloc_strdup(content->bctx,
1303 					messages_get("Form_Submit"));
1304 		else if (box->gadget->type == GADGET_RESET)
1305 			inline_box->text = talloc_strdup(content->bctx,
1306 					messages_get("Form_Reset"));
1307 		else
1308 			inline_box->text = talloc_strdup(content->bctx,
1309 							 "Button");
1310 
1311 		if (inline_box->text == NULL)
1312 			goto no_memory;
1313 
1314 		inline_box->length = strlen(inline_box->text);
1315 
1316 		box_add_child(inline_container, inline_box);
1317 
1318 		box_add_child(box, inline_container);
1319 
1320 	} else if (dom_string_caseless_lwc_isequal(type,
1321 						   corestring_lwc_image)) {
1322 		gadget->type = GADGET_IMAGE;
1323 
1324 		if (box->style &&
1325 		    ns_computed_display(box->style,
1326 				box_is_root(n)) != CSS_DISPLAY_NONE &&
1327 		    nsoption_bool(foreground_images) == true) {
1328 			dom_string *s;
1329 
1330 			err = dom_element_get_attribute(n, corestring_dom_src, &s);
1331 			if (err == DOM_NO_ERR && s != NULL) {
1332 				error = nsurl_join(content->base_url,
1333 						dom_string_data(s), &url);
1334 				dom_string_unref(s);
1335 				if (error != NSERROR_OK)
1336 					goto no_memory;
1337 
1338 				/* if url is equivalent to the parent's url,
1339 				 * we've got infinite inclusion. stop it here
1340 				 */
1341 				if (nsurl_compare(url, content->base_url,
1342 						NSURL_COMPLETE) == false) {
1343 					if (!html_fetch_object(content,
1344 							       url,
1345 							       box,
1346 							       image_types,
1347 							       false)) {
1348 						nsurl_unref(url);
1349 						goto no_memory;
1350 					}
1351 				}
1352 				nsurl_unref(url);
1353 			}
1354 		}
1355 	} else {
1356 		/* unhandled type the default is "text" */
1357 		if (box_input_text(content, box, n) == false)
1358 			goto no_memory;
1359 	}
1360 
1361 	dom_string_unref(type);
1362 
1363 	*convert_children = false;
1364 
1365 	return true;
1366 
1367 no_memory:
1368 	dom_string_unref(type);
1369 
1370 	return false;
1371 }
1372 
1373 
1374 /**
1375  * Noscript element
1376  */
1377 static bool
box_noscript(dom_node * n,html_content * content,struct box * box,bool * convert_children)1378 box_noscript(dom_node *n,
1379 	     html_content *content,
1380 	     struct box *box,
1381 	     bool *convert_children)
1382 {
1383 	/* If scripting is enabled, do not display the contents of noscript */
1384 	if (content->enable_scripting) {
1385 		*convert_children = false;
1386 	}
1387 
1388 	return true;
1389 }
1390 
1391 
1392 /**
1393  * Generic embedded object [13.3].
1394  */
1395 static bool
box_object(dom_node * n,html_content * content,struct box * box,bool * convert_children)1396 box_object(dom_node *n,
1397 	   html_content *content,
1398 	   struct box *box,
1399 	   bool *convert_children)
1400 {
1401 	struct object_params *params;
1402 	struct object_param *param;
1403 	dom_string *codebase, *classid, *data;
1404 	dom_node *c;
1405 	dom_exception err;
1406 
1407 	if (box->style &&
1408 	    ns_computed_display(box->style, box_is_root(n)) == CSS_DISPLAY_NONE)
1409 		return true;
1410 
1411 	if (box_get_attribute(n, "usemap", content->bctx, &box->usemap) ==
1412 			false)
1413 		return false;
1414 	if (box->usemap && box->usemap[0] == '#')
1415 		box->usemap++;
1416 
1417 	params = talloc(content->bctx, struct object_params);
1418 	if (params == NULL)
1419 		return false;
1420 
1421 	talloc_set_destructor(params, box_object_talloc_destructor);
1422 
1423 	params->data = NULL;
1424 	params->type = NULL;
1425 	params->codetype = NULL;
1426 	params->codebase = NULL;
1427 	params->classid = NULL;
1428 	params->params = NULL;
1429 
1430 	/* codebase, classid, and data are URLs
1431 	 * (codebase is the base for the other two) */
1432 	err = dom_element_get_attribute(n, corestring_dom_codebase, &codebase);
1433 	if (err == DOM_NO_ERR && codebase != NULL) {
1434 		if (box_extract_link(content, codebase,	content->base_url,
1435 				&params->codebase) == false) {
1436 			dom_string_unref(codebase);
1437 			return false;
1438 		}
1439 		dom_string_unref(codebase);
1440 	}
1441 	if (params->codebase == NULL)
1442 		params->codebase = nsurl_ref(content->base_url);
1443 
1444 	err = dom_element_get_attribute(n, corestring_dom_classid, &classid);
1445 	if (err == DOM_NO_ERR && classid != NULL) {
1446 		if (box_extract_link(content, classid,
1447 				params->codebase, &params->classid) == false) {
1448 			dom_string_unref(classid);
1449 			return false;
1450 		}
1451 		dom_string_unref(classid);
1452 	}
1453 
1454 	err = dom_element_get_attribute(n, corestring_dom_data, &data);
1455 	if (err == DOM_NO_ERR && data != NULL) {
1456 		if (box_extract_link(content, data,
1457 				params->codebase, &params->data) == false) {
1458 			dom_string_unref(data);
1459 			return false;
1460 		}
1461 		dom_string_unref(data);
1462 	}
1463 
1464 	if (params->classid == NULL && params->data == NULL)
1465 		/* nothing to embed; ignore */
1466 		return true;
1467 
1468 	/* Don't include ourself */
1469 	if (params->classid != NULL && nsurl_compare(content->base_url,
1470 			params->classid, NSURL_COMPLETE))
1471 		return true;
1472 
1473 	if (params->data != NULL && nsurl_compare(content->base_url,
1474 			params->data, NSURL_COMPLETE))
1475 		return true;
1476 
1477 	/* codetype and type are MIME types */
1478 	if (box_get_attribute(n, "codetype", params,
1479 			&params->codetype) == false)
1480 		return false;
1481 	if (box_get_attribute(n, "type", params, &params->type) == false)
1482 		return false;
1483 
1484 	/* classid && !data => classid is used (consult codetype)
1485 	 * (classid || !classid) && data => data is used (consult type)
1486 	 * !classid && !data => invalid; ignored */
1487 
1488 	if (params->classid != NULL && params->data == NULL &&
1489 			params->codetype != NULL) {
1490 		lwc_string *icodetype;
1491 		lwc_error lerror;
1492 
1493 		lerror = lwc_intern_string(params->codetype,
1494 				strlen(params->codetype), &icodetype);
1495 		if (lerror != lwc_error_ok)
1496 			return false;
1497 
1498 		if (content_factory_type_from_mime_type(icodetype) ==
1499 				CONTENT_NONE) {
1500 			/* can't handle this MIME type */
1501 			lwc_string_unref(icodetype);
1502 			return true;
1503 		}
1504 
1505 		lwc_string_unref(icodetype);
1506 	}
1507 
1508 	if (params->data != NULL && params->type != NULL) {
1509 		lwc_string *itype;
1510 		lwc_error lerror;
1511 
1512 		lerror = lwc_intern_string(params->type, strlen(params->type),
1513 				&itype);
1514 		if (lerror != lwc_error_ok)
1515 			return false;
1516 
1517 		if (content_factory_type_from_mime_type(itype) ==
1518 				CONTENT_NONE) {
1519 			/* can't handle this MIME type */
1520 			lwc_string_unref(itype);
1521 			return true;
1522 		}
1523 
1524 		lwc_string_unref(itype);
1525 	}
1526 
1527 	/* add parameters to linked list */
1528 	err = dom_node_get_first_child(n, &c);
1529 	if (err != DOM_NO_ERR)
1530 		return false;
1531 
1532 	while (c != NULL) {
1533 		dom_node *next;
1534 		dom_node_type type;
1535 
1536 		err = dom_node_get_node_type(c, &type);
1537 		if (err != DOM_NO_ERR) {
1538 			dom_node_unref(c);
1539 			return false;
1540 		}
1541 
1542 		if (type == DOM_ELEMENT_NODE) {
1543 			dom_string *name;
1544 
1545 			err = dom_node_get_node_name(c, &name);
1546 			if (err != DOM_NO_ERR) {
1547 				dom_node_unref(c);
1548 				return false;
1549 			}
1550 
1551 			if (!dom_string_caseless_lwc_isequal(name,
1552 					corestring_lwc_param)) {
1553 				/* The first non-param child is the start of
1554 				 * the alt html. Therefore, we should break
1555 				 * out of this loop. */
1556 				dom_string_unref(name);
1557 				dom_node_unref(c);
1558 				break;
1559 			}
1560 			dom_string_unref(name);
1561 
1562 			param = talloc(params, struct object_param);
1563 			if (param == NULL) {
1564 				dom_node_unref(c);
1565 				return false;
1566 			}
1567 			param->name = NULL;
1568 			param->value = NULL;
1569 			param->type = NULL;
1570 			param->valuetype = NULL;
1571 			param->next = NULL;
1572 
1573 			if (box_get_attribute(c, "name", param,
1574 					&param->name) == false) {
1575 				dom_node_unref(c);
1576 				return false;
1577 			}
1578 
1579 			if (box_get_attribute(c, "value", param,
1580 					&param->value) == false) {
1581 				dom_node_unref(c);
1582 				return false;
1583 			}
1584 
1585 			if (box_get_attribute(c, "type", param,
1586 					&param->type) == false) {
1587 				dom_node_unref(c);
1588 				return false;
1589 			}
1590 
1591 			if (box_get_attribute(c, "valuetype", param,
1592 					&param->valuetype) == false) {
1593 				dom_node_unref(c);
1594 				return false;
1595 			}
1596 
1597 			if (param->valuetype == NULL) {
1598 				param->valuetype = talloc_strdup(param, "data");
1599 				if (param->valuetype == NULL) {
1600 					dom_node_unref(c);
1601 					return false;
1602 				}
1603 			}
1604 
1605 			param->next = params->params;
1606 			params->params = param;
1607 		}
1608 
1609 		err = dom_node_get_next_sibling(c, &next);
1610 		if (err != DOM_NO_ERR) {
1611 			dom_node_unref(c);
1612 			return false;
1613 		}
1614 
1615 		dom_node_unref(c);
1616 		c = next;
1617 	}
1618 
1619 	box->object_params = params;
1620 
1621 	/* start fetch (MIME type is ok or not specified) */
1622 	box->flags |= IS_REPLACED;
1623 	if (!html_fetch_object(content,
1624 			       params->data ? params->data : params->classid,
1625 			       box,
1626 			       CONTENT_ANY,
1627 			       false))
1628 		return false;
1629 
1630 	*convert_children = false;
1631 	return true;
1632 }
1633 
1634 
1635 /**
1636  * Preformatted text [9.3.4].
1637  */
1638 static bool
box_pre(dom_node * n,html_content * content,struct box * box,bool * convert_children)1639 box_pre(dom_node *n,
1640 	html_content *content,
1641 	struct box *box,
1642 	bool *convert_children)
1643 {
1644 	box->flags |= PRE_STRIP;
1645 	return true;
1646 }
1647 
1648 
1649 /**
1650  * Option selector [17.6].
1651  */
1652 static bool
box_select(dom_node * n,html_content * content,struct box * box,bool * convert_children)1653 box_select(dom_node *n,
1654 	   html_content *content,
1655 	   struct box *box,
1656 	   bool *convert_children)
1657 {
1658 	struct box *inline_container;
1659 	struct box *inline_box;
1660 	struct form_control *gadget;
1661 	dom_node *c, *c2;
1662 	dom_node *next, *next2;
1663 	dom_exception err;
1664 
1665 	gadget = html_forms_get_control_for_node(content->forms, n);
1666 	if (gadget == NULL)
1667 		return false;
1668 
1669 	gadget->html = content;
1670 	err = dom_node_get_first_child(n, &c);
1671 	if (err != DOM_NO_ERR) {
1672 		form_free_control(gadget);
1673 		return false;
1674 	}
1675 
1676 	while (c != NULL) {
1677 		dom_string *name;
1678 
1679 		err = dom_node_get_node_name(c, &name);
1680 		if (err != DOM_NO_ERR) {
1681 			dom_node_unref(c);
1682 			form_free_control(gadget);
1683 			return false;
1684 		}
1685 
1686 		if (dom_string_caseless_lwc_isequal(name,
1687 				corestring_lwc_option)) {
1688 			dom_string_unref(name);
1689 
1690 			if (box_select_add_option(gadget, c) == false) {
1691 				dom_node_unref(c);
1692 				form_free_control(gadget);
1693 				return false;
1694 			}
1695 		} else if (dom_string_caseless_lwc_isequal(name,
1696 				corestring_lwc_optgroup)) {
1697 			dom_string_unref(name);
1698 
1699 			err = dom_node_get_first_child(c, &c2);
1700 			if (err != DOM_NO_ERR) {
1701 				dom_node_unref(c);
1702 				form_free_control(gadget);
1703 				return false;
1704 			}
1705 
1706 			while (c2 != NULL) {
1707 				dom_string *c2_name;
1708 
1709 				err = dom_node_get_node_name(c2, &c2_name);
1710 				if (err != DOM_NO_ERR) {
1711 					dom_node_unref(c2);
1712 					dom_node_unref(c);
1713 					form_free_control(gadget);
1714 					return false;
1715 				}
1716 
1717 				if (dom_string_caseless_lwc_isequal(c2_name,
1718 						corestring_lwc_option)) {
1719 					dom_string_unref(c2_name);
1720 
1721 					if (box_select_add_option(gadget,
1722 							c2) == false) {
1723 						dom_node_unref(c2);
1724 						dom_node_unref(c);
1725 						form_free_control(gadget);
1726 						return false;
1727 					}
1728 				} else {
1729 					dom_string_unref(c2_name);
1730 				}
1731 
1732 				err = dom_node_get_next_sibling(c2, &next2);
1733 				if (err != DOM_NO_ERR) {
1734 					dom_node_unref(c2);
1735 					dom_node_unref(c);
1736 					form_free_control(gadget);
1737 					return false;
1738 				}
1739 
1740 				dom_node_unref(c2);
1741 				c2 = next2;
1742 			}
1743 		} else {
1744 			dom_string_unref(name);
1745 		}
1746 
1747 		err = dom_node_get_next_sibling(c, &next);
1748 		if (err != DOM_NO_ERR) {
1749 			dom_node_unref(c);
1750 			form_free_control(gadget);
1751 			return false;
1752 		}
1753 
1754 		dom_node_unref(c);
1755 		c = next;
1756 	}
1757 
1758 	if (gadget->data.select.num_items == 0) {
1759 		/* no options: ignore entire select */
1760 		form_free_control(gadget);
1761 		return true;
1762 	}
1763 
1764 	box->type = BOX_INLINE_BLOCK;
1765 	box->gadget = gadget;
1766 	box->flags |= IS_REPLACED;
1767 	gadget->box = box;
1768 
1769 	inline_container = box_create(NULL, 0, false, 0, 0, 0, 0, content->bctx);
1770 	if (inline_container == NULL)
1771 		goto no_memory;
1772 	inline_container->type = BOX_INLINE_CONTAINER;
1773 	inline_box = box_create(NULL, box->style, false, 0, 0, box->title, 0,
1774 			content->bctx);
1775 	if (inline_box == NULL)
1776 		goto no_memory;
1777 	inline_box->type = BOX_TEXT;
1778 	box_add_child(inline_container, inline_box);
1779 	box_add_child(box, inline_container);
1780 
1781 	if (gadget->data.select.multiple == false &&
1782 			gadget->data.select.num_selected == 0) {
1783 		gadget->data.select.current = gadget->data.select.items;
1784 		gadget->data.select.current->initial_selected =
1785 			gadget->data.select.current->selected = true;
1786 		gadget->data.select.num_selected = 1;
1787 		dom_html_option_element_set_selected(
1788 				gadget->data.select.current->node, true);
1789 	}
1790 
1791 	if (gadget->data.select.num_selected == 0)
1792 		inline_box->text = talloc_strdup(content->bctx,
1793 				messages_get("Form_None"));
1794 	else if (gadget->data.select.num_selected == 1)
1795 		inline_box->text = talloc_strdup(content->bctx,
1796 				gadget->data.select.current->text);
1797 	else
1798 		inline_box->text = talloc_strdup(content->bctx,
1799 				messages_get("Form_Many"));
1800 	if (inline_box->text == NULL)
1801 		goto no_memory;
1802 
1803 	inline_box->length = strlen(inline_box->text);
1804 
1805 	*convert_children = false;
1806 	return true;
1807 
1808 no_memory:
1809 	return false;
1810 }
1811 
1812 
1813 /**
1814  * Multi-line text field [17.7].
1815  */
box_textarea(dom_node * n,html_content * content,struct box * box,bool * convert_children)1816 static bool box_textarea(dom_node *n,
1817 			html_content *content,
1818 			struct box *box,
1819 			bool *convert_children)
1820 {
1821 	/* Get the form_control for the DOM node */
1822 	box->gadget = html_forms_get_control_for_node(content->forms, n);
1823 	if (box->gadget == NULL)
1824 		return false;
1825 
1826 	box->flags |= IS_REPLACED;
1827 	box->gadget->html = content;
1828 	box->gadget->box = box;
1829 
1830 	if (!box_input_text(content, box, n))
1831 		return false;
1832 
1833 	*convert_children = false;
1834 	return true;
1835 }
1836 
1837 
1838 /**
1839  * \}
1840  */
1841 
1842 
1843 /* exported interface documented in html/box_special.h */
1844 bool
convert_special_elements(dom_node * node,html_content * content,struct box * box,bool * convert_children)1845 convert_special_elements(dom_node *node,
1846 			 html_content *content,
1847 			 struct box *box,
1848 			 bool *convert_children)
1849 {
1850 	dom_exception exc;
1851 	dom_html_element_type tag_type;
1852 	bool res;
1853 
1854 	exc = dom_html_element_get_tag_type(node, &tag_type);
1855 	if (exc != DOM_NO_ERR) {
1856 		tag_type = DOM_HTML_ELEMENT_TYPE__UNKNOWN;
1857 	}
1858 
1859 	switch (tag_type) {
1860 	case DOM_HTML_ELEMENT_TYPE_A:
1861 		res =  box_a(node, content, box, convert_children);
1862 		break;
1863 
1864 	case DOM_HTML_ELEMENT_TYPE_BODY:
1865 		res = box_body(node, content, box, convert_children);
1866 		break;
1867 
1868 	case DOM_HTML_ELEMENT_TYPE_BR:
1869 		res = box_br(node, content, box, convert_children);
1870 		break;
1871 
1872 	case DOM_HTML_ELEMENT_TYPE_BUTTON:
1873 		res = box_button(node, content, box, convert_children);
1874 		break;
1875 
1876 	case DOM_HTML_ELEMENT_TYPE_CANVAS:
1877 		res = box_canvas(node, content, box, convert_children);
1878 		break;
1879 
1880 	case DOM_HTML_ELEMENT_TYPE_EMBED:
1881 		res = box_embed(node, content, box, convert_children);
1882 		break;
1883 
1884 	case DOM_HTML_ELEMENT_TYPE_FRAMESET:
1885 		res = box_frameset(node, content, box, convert_children);
1886 		break;
1887 
1888 	case DOM_HTML_ELEMENT_TYPE_IFRAME:
1889 		res = box_iframe(node, content, box, convert_children);
1890 		break;
1891 
1892 	case DOM_HTML_ELEMENT_TYPE_IMG:
1893 		res = box_image(node, content, box, convert_children);
1894 		break;
1895 
1896 	case DOM_HTML_ELEMENT_TYPE_INPUT:
1897 		res = box_input(node, content, box, convert_children);
1898 		break;
1899 
1900 	case DOM_HTML_ELEMENT_TYPE_NOSCRIPT:
1901 		res = box_noscript(node, content, box, convert_children);
1902 		break;
1903 
1904 	case DOM_HTML_ELEMENT_TYPE_OBJECT:
1905 		res = box_object(node, content, box, convert_children);
1906 		break;
1907 
1908 	case DOM_HTML_ELEMENT_TYPE_PRE:
1909 		res = box_pre(node, content, box, convert_children);
1910 		break;
1911 
1912 	case DOM_HTML_ELEMENT_TYPE_SELECT:
1913 		res = box_select(node, content, box, convert_children);
1914 		break;
1915 
1916 	case DOM_HTML_ELEMENT_TYPE_TEXTAREA:
1917 		res = box_textarea(node, content, box, convert_children);
1918 		break;
1919 
1920 	default:
1921 		res = true;
1922 	}
1923 
1924 	return res;
1925 }
1926