Subversion Repositories Kolibri OS

Rev

Rev 4364 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
3584 sourcerer 1
/*
2
 * Copyright 2007 James Bursa 
3
 * Copyright 2010 Michael Drake 
4
 *
5
 * This file is part of NetSurf, http://www.netsurf-browser.org/
6
 *
7
 * NetSurf is free software; you can redistribute it and/or modify
8
 * it under the terms of the GNU General Public License as published by
9
 * the Free Software Foundation; version 2 of the License.
10
 *
11
 * NetSurf is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
 * GNU General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU General Public License
17
 * along with this program.  If not, see .
18
 */
19
 
20
/** \file
21
 * Content for text/html (implementation).
22
 */
23
 
24
#include 
25
#include 
26
#include 
27
#include 
28
#include 
29
#include 
30
 
31
#include "utils/config.h"
32
#include "content/content_protected.h"
33
#include "content/fetch.h"
34
#include "content/hlcache.h"
35
#include "desktop/options.h"
36
#include "desktop/selection.h"
37
#include "desktop/scrollbar.h"
38
#include "image/bitmap.h"
39
#include "render/box.h"
40
#include "render/font.h"
41
#include "render/form.h"
42
#include "render/html_internal.h"
43
#include "render/imagemap.h"
44
#include "render/layout.h"
45
#include "render/search.h"
46
#include "javascript/js.h"
47
#include "utils/corestrings.h"
48
#include "utils/http.h"
49
#include "utils/libdom.h"
50
#include "utils/log.h"
51
#include "utils/messages.h"
52
#include "utils/schedule.h"
53
#include "utils/talloc.h"
54
#include "utils/url.h"
55
#include "utils/utf8.h"
56
#include "utils/utils.h"
57
 
58
#define CHUNK 4096
59
 
60
/* Change these to 1 to cause a dump to stderr of the frameset or box
61
 * when the trees have been built.
62
 */
63
#define ALWAYS_DUMP_FRAMESET 0
64
#define ALWAYS_DUMP_BOX 0
65
 
66
static const char *html_types[] = {
67
	"application/xhtml+xml",
68
	"text/html"
69
};
70
 
71
/* forward declared functions */
72
static void html_object_refresh(void *p);
73
 
74
/* pre-interned character set */
75
static lwc_string *html_charset;
76
 
77
static nsurl *html_default_stylesheet_url;
78
static nsurl *html_adblock_stylesheet_url;
79
static nsurl *html_quirks_stylesheet_url;
80
static nsurl *html_user_stylesheet_url;
81
 
82
static nserror css_error_to_nserror(css_error error)
83
{
84
	switch (error) {
85
	case CSS_OK:
86
		return NSERROR_OK;
87
 
88
	case CSS_NOMEM:
89
		return NSERROR_NOMEM;
90
 
91
	case CSS_BADPARM:
92
		return NSERROR_BAD_PARAMETER;
93
 
94
	case CSS_INVALID:
95
		return NSERROR_INVALID;
96
 
97
	case CSS_FILENOTFOUND:
98
		return NSERROR_NOT_FOUND;
99
 
100
	case CSS_NEEDDATA:
101
		return NSERROR_NEED_DATA;
102
 
103
	case CSS_BADCHARSET:
104
		return NSERROR_BAD_ENCODING;
105
 
106
	case CSS_EOF:
107
	case CSS_IMPORTS_PENDING:
108
	case CSS_PROPERTY_NOT_SET:
109
	default:
110
		break;
111
	}
112
	return NSERROR_CSS;
113
}
114
 
115
 
116
static void html_destroy_objects(html_content *html)
117
{
118
	while (html->object_list != NULL) {
119
		struct content_html_object *victim = html->object_list;
120
 
121
		if (victim->content != NULL) {
122
			LOG(("object %p", victim->content));
123
 
124
			if (content_get_type(victim->content) == CONTENT_HTML)
125
				schedule_remove(html_object_refresh, victim);
126
 
127
			hlcache_handle_release(victim->content);
128
		}
129
 
130
		html->object_list = victim->next;
131
		free(victim);
132
	}
133
}
134
 
135
/**
136
 * Perform post-box-creation conversion of a document
137
 *
138
 * \param c        HTML content to complete conversion of
139
 * \param success  Whether box tree construction was successful
140
 */
141
static void html_box_convert_done(html_content *c, bool success)
142
{
143
	nserror err;
144
	dom_exception exc; /* returned by libdom functions */
145
	dom_node *html;
146
 
147
	LOG(("Done XML to box (%p)", c));
148
 
149
	/* Clean up and report error if unsuccessful or aborted */
150
	if ((success == false) || (c->aborted)) {
151
		html_destroy_objects(c);
152
 
153
		if (success == false) {
154
			content_broadcast_errorcode(&c->base, NSERROR_BOX_CONVERT);
155
		} else {
156
			content_broadcast_errorcode(&c->base, NSERROR_STOPPED);
157
		}
158
 
159
		content_set_error(&c->base);
160
		return;
161
	}
162
 
163
 
164
#if ALWAYS_DUMP_BOX
165
	box_dump(stderr, c->layout->children, 0);
166
#endif
167
#if ALWAYS_DUMP_FRAMESET
168
	if (c->frameset)
169
		html_dump_frameset(c->frameset, 0);
170
#endif
171
 
172
	exc = dom_document_get_document_element(c->document, (void *) &html);
173
	if ((exc != DOM_NO_ERR) || (html == NULL)) {
174
		/** @todo should this call html_destroy_objects(c);
175
		 * like the other error paths
176
		 */
177
		LOG(("error retrieving html element from dom"));
178
		content_broadcast_errorcode(&c->base, NSERROR_DOM);
179
		content_set_error(&c->base);
180
		return;
181
	}
182
 
183
	/* extract image maps - can't do this sensibly in dom_to_box */
184
	err = imagemap_extract(c);
185
	if (err != NSERROR_OK) {
186
		LOG(("imagemap extraction failed"));
187
		html_destroy_objects(c);
188
		content_broadcast_errorcode(&c->base, err);
189
		content_set_error(&c->base);
190
		dom_node_unref(html);
191
		return;
192
	}
193
	/*imagemap_dump(c);*/
194
 
195
	/* Destroy the parser binding */
196
	dom_hubbub_parser_destroy(c->parser);
197
	c->parser = NULL;
198
 
199
	content_set_ready(&c->base);
200
 
201
	if (c->base.active == 0) {
202
		content_set_done(&c->base);
203
	}
204
 
205
	html_set_status(c, "");
206
	dom_node_unref(html);
207
}
208
 
209
 
210
/**
211
 * Complete conversion of an HTML document
212
 *
213
 * \param c  Content to convert
214
 */
215
void html_finish_conversion(html_content *c)
216
{
217
	union content_msg_data msg_data;
218
	dom_exception exc; /* returned by libdom functions */
219
	dom_node *html;
220
	uint32_t i;
221
	css_error css_ret;
222
	nserror error;
223
 
224
	/* Bail out if we've been aborted */
225
	if (c->aborted) {
226
		content_broadcast_errorcode(&c->base, NSERROR_STOPPED);
227
		content_set_error(&c->base);
228
		return;
229
	}
230
 
231
	/* check that the base stylesheet loaded; layout fails without it */
232
	if (c->stylesheets[STYLESHEET_BASE].data.external == NULL) {
233
		content_broadcast_errorcode(&c->base, NSERROR_CSS_BASE);
234
		content_set_error(&c->base);
235
		return;
236
	}
237
 
238
	/* Create selection context */
239
	css_ret = css_select_ctx_create(ns_realloc, c, &c->select_ctx);
240
	if (css_ret != CSS_OK) {
241
		content_broadcast_errorcode(&c->base,
242
					    css_error_to_nserror(css_ret));
243
		content_set_error(&c->base);
244
		return;
245
	}
246
 
247
	/* Add sheets to it */
248
	for (i = STYLESHEET_BASE; i != c->stylesheet_count; i++) {
249
		const struct html_stylesheet *hsheet = &c->stylesheets[i];
250
		css_stylesheet *sheet;
251
		css_origin origin = CSS_ORIGIN_AUTHOR;
252
 
253
		if (i < STYLESHEET_USER)
254
			origin = CSS_ORIGIN_UA;
255
		else if (i < STYLESHEET_START)
256
			origin = CSS_ORIGIN_USER;
257
 
258
		if (hsheet->type == HTML_STYLESHEET_EXTERNAL &&
259
				hsheet->data.external != NULL) {
260
			sheet = nscss_get_stylesheet(hsheet->data.external);
261
		} else if (hsheet->type == HTML_STYLESHEET_INTERNAL) {
262
			sheet = hsheet->data.internal->sheet;
263
		} else {
264
			sheet = NULL;
265
		}
266
 
267
		if (sheet != NULL) {
268
			css_ret = css_select_ctx_append_sheet(c->select_ctx,
269
							      sheet,
270
							      origin,
271
							      CSS_MEDIA_SCREEN);
272
			if (css_ret != CSS_OK) {
273
				content_broadcast_errorcode(&c->base,
274
						css_error_to_nserror(css_ret));
275
				content_set_error(&c->base);
276
				return;
277
			}
278
		}
279
	}
280
 
281
	/* fire a simple event named load at the Document's Window
282
	 * object, but with its target set to the Document object (and
283
	 * the currentTarget set to the Window object)
284
	 */
285
	js_fire_event(c->jscontext, "load", c->document, NULL);
286
 
287
	/* convert dom tree to box tree */
288
	LOG(("DOM to box (%p)", c));
289
	content_set_status(&c->base, messages_get("Processing"));
5043 ashmew2 290
	/* LOG(("After content_set_status")); */
3584 sourcerer 291
	msg_data.explicit_status_text = NULL;
292
	content_broadcast(&c->base, CONTENT_MSG_STATUS, msg_data);
5043 ashmew2 293
	/* LOG(("After content_broadcast")); */
3584 sourcerer 294
 
295
	exc = dom_document_get_document_element(c->document, (void *) &html);
5043 ashmew2 296
	/* LOG(("After get_document_element")); */
297
 
3584 sourcerer 298
	if ((exc != DOM_NO_ERR) || (html == NULL)) {
299
		LOG(("error retrieving html element from dom"));
300
		content_broadcast_errorcode(&c->base, NSERROR_DOM);
301
		content_set_error(&c->base);
302
		return;
303
	}
5043 ashmew2 304
 
305
	error = dom_to_box(html, c, html_box_convert_done);
306
	/* LOG(("After dom_to_box")); */
3584 sourcerer 307
 
308
	if (error != NSERROR_OK) {
309
		dom_node_unref(html);
310
		html_destroy_objects(c);
311
		content_broadcast_errorcode(&c->base, error);
312
		content_set_error(&c->base);
313
		return;
314
	}
315
 
316
	dom_node_unref(html);
317
}
318
 
319
 
320
 
321
static nserror
322
html_create_html_data(html_content *c, const http_parameter *params)
323
{
324
	lwc_string *charset;
325
	nserror nerror;
326
	dom_hubbub_parser_params parse_params;
327
	dom_hubbub_error error;
328
 
329
	c->parser = NULL;
330
	c->document = NULL;
331
	c->quirks = DOM_DOCUMENT_QUIRKS_MODE_NONE;
332
	c->encoding = NULL;
333
	c->base_url = nsurl_ref(content_get_url(&c->base));
334
	c->base_target = NULL;
335
	c->aborted = false;
336
	c->bctx = NULL;
337
	c->layout = NULL;
338
	c->background_colour = NS_TRANSPARENT;
339
	c->stylesheet_count = 0;
340
	c->stylesheets = NULL;
341
	c->select_ctx = NULL;
342
	c->universal = NULL;
343
	c->num_objects = 0;
344
	c->object_list = NULL;
345
	c->forms = NULL;
346
	c->imagemaps = NULL;
347
	c->bw = NULL;
348
	c->frameset = NULL;
349
	c->iframe = NULL;
350
	c->page = NULL;
351
	c->font_func = &nsfont;
352
	c->scrollbar = NULL;
353
	c->scripts_count = 0;
354
	c->scripts = NULL;
355
	c->jscontext = NULL;
356
 
357
	c->base.active = 1; /* The html content itself is active */
358
 
359
	if (lwc_intern_string("*", SLEN("*"), &c->universal) != lwc_error_ok) {
360
		return NSERROR_NOMEM;
361
	}
362
 
363
	selection_prepare(&c->sel, (struct content *)c, true);
364
 
365
	nerror = http_parameter_list_find_item(params, html_charset, &charset);
366
	if (nerror == NSERROR_OK) {
367
		c->encoding = strdup(lwc_string_data(charset));
368
 
369
		lwc_string_unref(charset);
370
 
371
		if (c->encoding == NULL) {
372
			lwc_string_unref(c->universal);
373
			c->universal = NULL;
374
			return NSERROR_NOMEM;
375
 
376
		}
377
		c->encoding_source = DOM_HUBBUB_ENCODING_SOURCE_HEADER;
378
	}
379
 
380
	/* Create the parser binding */
381
	parse_params.enc = c->encoding;
382
	parse_params.fix_enc = true;
383
	parse_params.enable_script = nsoption_bool(enable_javascript);
384
	parse_params.msg = NULL;
385
	parse_params.script = html_process_script;
386
	parse_params.ctx = c;
387
	parse_params.daf = NULL;
388
 
389
	error = dom_hubbub_parser_create(&parse_params,
390
					 &c->parser,
391
					 &c->document);
392
	if ((error != DOM_HUBBUB_OK) && (c->encoding != NULL)) {
393
		/* Ok, we don't support the declared encoding. Bailing out
394
		 * isn't exactly user-friendly, so fall back to autodetect */
395
		free(c->encoding);
396
		c->encoding = NULL;
397
 
398
		parse_params.enc = c->encoding;
399
 
400
		error = dom_hubbub_parser_create(&parse_params,
401
						 &c->parser,
402
						 &c->document);
403
	}
404
 
405
	if (error != DOM_HUBBUB_OK) {
406
		nsurl_unref(c->base_url);
407
		c->base_url = NULL;
408
 
409
		lwc_string_unref(c->universal);
410
		c->universal = NULL;
411
 
412
		return libdom_hubbub_error_to_nserror(error);
413
	}
414
 
415
	return NSERROR_OK;
416
 
417
}
418
 
419
/**
420
 * Create a CONTENT_HTML.
421
 *
422
 * The content_html_data structure is initialized and the HTML parser is
423
 * created.
424
 */
425
 
426
static nserror
427
html_create(const content_handler *handler,
428
	    lwc_string *imime_type,
429
	    const http_parameter *params,
430
	    llcache_handle *llcache,
431
	    const char *fallback_charset,
432
	    bool quirks,
433
	    struct content **c)
434
{
435
	html_content *html;
436
	nserror error;
437
 
438
	html = calloc(1, sizeof(html_content));
439
	if (html == NULL)
440
		return NSERROR_NOMEM;
441
 
442
	error = content__init(&html->base, handler, imime_type, params,
443
			llcache, fallback_charset, quirks);
444
	if (error != NSERROR_OK) {
445
		free(html);
446
		return error;
447
	}
448
 
449
	error = html_create_html_data(html, params);
450
	if (error != NSERROR_OK) {
451
		content_broadcast_errorcode(&html->base, error);
452
		free(html);
453
		return error;
454
	}
455
 
456
	*c = (struct content *) html;
457
 
458
	return NSERROR_OK;
459
}
460
 
461
 
462
 
463
static nserror
464
html_process_encoding_change(struct content *c,
465
			     const char *data,
466
			     unsigned int size)
467
{
468
	html_content *html = (html_content *) c;
469
	dom_hubbub_parser_params parse_params;
470
	dom_hubbub_error error;
471
	const char *encoding;
472
	const char *source_data;
473
	unsigned long source_size;
474
 
475
	/* Retrieve new encoding */
476
	encoding = dom_hubbub_parser_get_encoding(html->parser,
477
						  &html->encoding_source);
478
	if (encoding == NULL) {
479
		return NSERROR_NOMEM;
480
	}
481
 
482
	if (html->encoding != NULL) {
483
		free(html->encoding);
484
	}
485
 
486
	html->encoding = strdup(encoding);
487
	if (html->encoding == NULL) {
488
		return NSERROR_NOMEM;
489
	}
490
 
491
	/* Destroy binding */
492
	dom_hubbub_parser_destroy(html->parser);
493
	html->parser = NULL;
494
 
495
	if (html->document != NULL) {
496
		dom_node_unref(html->document);
497
	}
498
 
499
	parse_params.enc = html->encoding;
500
	parse_params.fix_enc = true;
501
	parse_params.enable_script = nsoption_bool(enable_javascript);
502
	parse_params.msg = NULL;
503
	parse_params.script = html_process_script;
504
	parse_params.ctx = html;
505
	parse_params.daf = NULL;
506
 
507
	/* Create new binding, using the new encoding */
508
	error = dom_hubbub_parser_create(&parse_params,
509
					 &html->parser,
510
					 &html->document);
511
	if (error != DOM_HUBBUB_OK) {
512
		/* Ok, we don't support the declared encoding. Bailing out
513
		 * isn't exactly user-friendly, so fall back to Windows-1252 */
514
		free(html->encoding);
515
		html->encoding = strdup("Windows-1252");
516
		if (html->encoding == NULL) {
517
			return NSERROR_NOMEM;
518
		}
519
		parse_params.enc = html->encoding;
520
 
521
		error = dom_hubbub_parser_create(&parse_params,
522
						 &html->parser,
523
						 &html->document);
524
 
525
		if (error != DOM_HUBBUB_OK) {
526
			return libdom_hubbub_error_to_nserror(error);
527
		}
528
 
529
	}
530
 
531
	source_data = content__get_source_data(c, &source_size);
532
 
533
	/* Reprocess all the data.  This is safe because
534
	 * the encoding is now specified at parser start which means
535
	 * it cannot be changed again.
536
	 */
537
	error = dom_hubbub_parser_parse_chunk(html->parser,
538
					      (const uint8_t *)source_data,
539
					      source_size);
540
 
541
	return libdom_hubbub_error_to_nserror(error);
542
}
543
 
544
 
545
/**
546
 * Process data for CONTENT_HTML.
547
 */
548
 
549
static bool
550
html_process_data(struct content *c, const char *data, unsigned int size)
551
{
552
	html_content *html = (html_content *) c;
553
	dom_hubbub_error dom_ret;
554
	nserror err = NSERROR_OK; /* assume its all going to be ok */
555
 
556
	dom_ret = dom_hubbub_parser_parse_chunk(html->parser,
557
					      (const uint8_t *) data,
558
					      size);
559
 
560
	err = libdom_hubbub_error_to_nserror(dom_ret);
561
 
562
	/* deal with encoding change */
563
	if (err == NSERROR_ENCODING_CHANGE) {
564
		 err = html_process_encoding_change(c, data, size);
565
	}
566
 
567
	/* broadcast the error if necessary */
568
	if (err != NSERROR_OK) {
569
		content_broadcast_errorcode(c, err);
570
		return false;
571
	}
572
 
573
	return true;
574
}
575
 
576
 
577
/** process link node */
578
static bool html_process_link(html_content *c, dom_node *node)
579
{
580
	struct content_rfc5988_link link; /* the link added to the content */
581
	dom_exception exc; /* returned by libdom functions */
582
	dom_string *atr_string;
583
	nserror error;
584
 
585
	memset(&link, 0, sizeof(struct content_rfc5988_link));
586
 
587
	/* check that the relation exists - w3c spec says must be present */
588
	exc = dom_element_get_attribute(node, corestring_dom_rel, &atr_string);
589
	if ((exc != DOM_NO_ERR) || (atr_string == NULL)) {
590
		return false;
591
	}
592
	/* get a lwc string containing the link relation */
593
	exc = dom_string_intern(atr_string, &link.rel);
594
	dom_string_unref(atr_string);
595
	if (exc != DOM_NO_ERR) {
596
		return false;
597
	}
598
 
599
	/* check that the href exists - w3c spec says must be present */
600
	exc = dom_element_get_attribute(node, corestring_dom_href, &atr_string);
601
	if ((exc != DOM_NO_ERR) || (atr_string == NULL)) {
602
		lwc_string_unref(link.rel);
603
		return false;
604
	}
605
 
606
	/* get nsurl */
607
	error = nsurl_join(c->base_url, dom_string_data(atr_string),
608
			&link.href);
609
	dom_string_unref(atr_string);
610
	if (error != NSERROR_OK) {
611
		lwc_string_unref(link.rel);
612
		return false;
613
	}
614
 
615
	/* look for optional properties -- we don't care if internment fails */
616
 
617
	exc = dom_element_get_attribute(node,
618
			corestring_dom_hreflang, &atr_string);
619
	if ((exc == DOM_NO_ERR) && (atr_string != NULL)) {
620
		/* get a lwc string containing the href lang */
621
		exc = dom_string_intern(atr_string, &link.hreflang);
622
		dom_string_unref(atr_string);
623
	}
624
 
625
	exc = dom_element_get_attribute(node,
626
			corestring_dom_type, &atr_string);
627
	if ((exc == DOM_NO_ERR) && (atr_string != NULL)) {
628
		/* get a lwc string containing the type */
629
		exc = dom_string_intern(atr_string, &link.type);
630
		dom_string_unref(atr_string);
631
	}
632
 
633
	exc = dom_element_get_attribute(node,
634
			corestring_dom_media, &atr_string);
635
	if ((exc == DOM_NO_ERR) && (atr_string != NULL)) {
636
		/* get a lwc string containing the media */
637
		exc = dom_string_intern(atr_string, &link.media);
638
		dom_string_unref(atr_string);
639
	}
640
 
641
	exc = dom_element_get_attribute(node,
642
			corestring_dom_sizes, &atr_string);
643
	if ((exc == DOM_NO_ERR) && (atr_string != NULL)) {
644
		/* get a lwc string containing the sizes */
645
		exc = dom_string_intern(atr_string, &link.sizes);
646
		dom_string_unref(atr_string);
647
	}
648
 
649
	/* add to content */
650
	content__add_rfc5988_link(&c->base, &link);
651
 
652
	if (link.sizes != NULL)
653
		lwc_string_unref(link.sizes);
654
	if (link.media != NULL)
655
		lwc_string_unref(link.media);
656
	if (link.type != NULL)
657
		lwc_string_unref(link.type);
658
	if (link.hreflang != NULL)
659
		lwc_string_unref(link.hreflang);
660
 
661
	nsurl_unref(link.href);
662
	lwc_string_unref(link.rel);
663
 
664
	return true;
665
}
666
 
667
/** process title node */
668
static bool html_process_title(html_content *c, dom_node *node)
669
{
670
	dom_exception exc; /* returned by libdom functions */
671
	dom_string *title;
672
	char *title_str;
673
	bool success;
674
 
675
	if (c->base.title != NULL)
676
		return true;
677
 
678
	exc = dom_node_get_text_content(node, &title);
679
	if ((exc != DOM_NO_ERR) || (title == NULL)) {
680
		return false;
681
	}
682
 
683
	title_str = squash_whitespace(dom_string_data(title));
684
	dom_string_unref(title);
685
 
686
	if (title_str == NULL) {
687
		return false;
688
	}
689
 
690
	success = content__set_title(&c->base, title_str);
691
 
692
	free(title_str);
693
 
694
	return success;
695
}
696
 
697
static bool html_process_base(html_content *c, dom_node *node)
698
{
699
	dom_exception exc; /* returned by libdom functions */
700
	dom_string *atr_string;
701
 
702
	/* get href attribute if present */
703
	exc = dom_element_get_attribute(node,
704
			corestring_dom_href, &atr_string);
705
	if ((exc == DOM_NO_ERR) && (atr_string != NULL)) {
706
		nsurl *url;
707
		nserror error;
708
 
709
		/* get url from string */
710
		error = nsurl_create(dom_string_data(atr_string), &url);
711
		dom_string_unref(atr_string);
712
		if (error == NSERROR_OK) {
713
			if (c->base_url != NULL)
714
				nsurl_unref(c->base_url);
715
			c->base_url = url;
716
		}
717
	}
718
 
719
 
720
	/* get target attribute if present and not already set */
721
	if (c->base_target != NULL) {
722
		return true;
723
	}
724
 
725
	exc = dom_element_get_attribute(node,
726
			corestring_dom_target, &atr_string);
727
	if ((exc == DOM_NO_ERR) && (atr_string != NULL)) {
728
		/* Validation rules from the HTML5 spec for the base element:
729
		 *  The target must be one of _blank, _self, _parent, or
730
		 *  _top or any identifier which does not begin with an
731
		 *  underscore
732
		 */
733
		if (*dom_string_data(atr_string) != '_' ||
734
		    dom_string_caseless_lwc_isequal(atr_string,
735
		    		corestring_lwc__blank) ||
736
		    dom_string_caseless_lwc_isequal(atr_string,
737
		    		corestring_lwc__self) ||
738
		    dom_string_caseless_lwc_isequal(atr_string,
739
		    		corestring_lwc__parent) ||
740
		    dom_string_caseless_lwc_isequal(atr_string,
741
		    		corestring_lwc__top)) {
742
			c->base_target = strdup(dom_string_data(atr_string));
743
		}
744
		dom_string_unref(atr_string);
745
	}
746
 
747
	return true;
748
}
749
 
750
/**
751
 * Process elements in .
752
 *
753
 * \param  c     content structure
754
 * \param  head  xml node of head element
755
 * \return  true on success, false on memory exhaustion
756
 *
757
 * The title and base href are extracted if present.
758
 */
759
 
760
static nserror html_head(html_content *c, dom_node *head)
761
{
762
	dom_node *node;
763
	dom_exception exc; /* returned by libdom functions */
764
	dom_string *node_name;
765
	dom_node_type node_type;
766
	dom_node *next_node;
767
 
768
	exc = dom_node_get_first_child(head, &node);
769
	if (exc != DOM_NO_ERR) {
770
		return NSERROR_DOM;
771
	}
772
 
773
	while (node != NULL) {
774
		exc = dom_node_get_node_type(node, &node_type);
775
 
776
		if ((exc == DOM_NO_ERR) && (node_type == DOM_ELEMENT_NODE)) {
777
			exc = dom_node_get_node_name(node, &node_name);
778
 
779
			if ((exc == DOM_NO_ERR) && (node_name != NULL)) {
780
				if (dom_string_caseless_lwc_isequal(
781
						node_name,
782
						corestring_lwc_title)) {
783
					html_process_title(c, node);
784
				} else if (dom_string_caseless_lwc_isequal(
785
						node_name,
786
						corestring_lwc_base)) {
787
					html_process_base(c, node);
788
				} else if (dom_string_caseless_lwc_isequal(
789
						node_name,
790
						corestring_lwc_link)) {
791
					html_process_link(c, node);
792
				}
793
			}
794
			if (node_name != NULL) {
795
				dom_string_unref(node_name);
796
			}
797
		}
798
 
799
		/* move to next node */
800
		exc = dom_node_get_next_sibling(node, &next_node);
801
		dom_node_unref(node);
802
		if (exc == DOM_NO_ERR) {
803
			node = next_node;
804
		} else {
805
			node = NULL;
806
		}
807
	}
808
 
809
	return NSERROR_OK;
810
}
811
 
812
static nserror html_meta_refresh_process_element(html_content *c, dom_node *n)
813
{
814
	union content_msg_data msg_data;
815
	const char *url, *end, *refresh = NULL;
816
	char *new_url;
817
	char quote = '\0';
818
	dom_string *equiv, *content;
819
	dom_exception exc;
820
	nsurl *nsurl;
821
	nserror error = NSERROR_OK;
822
 
823
	exc = dom_element_get_attribute(n, corestring_dom_http_equiv, &equiv);
824
	if (exc != DOM_NO_ERR) {
825
		return NSERROR_DOM;
826
	}
827
 
828
	if (equiv == NULL) {
829
		return NSERROR_OK;
830
	}
831
 
832
	if (!dom_string_caseless_lwc_isequal(equiv, corestring_lwc_refresh)) {
833
		dom_string_unref(equiv);
834
		return NSERROR_OK;
835
	}
836
 
837
	dom_string_unref(equiv);
838
 
839
	exc = dom_element_get_attribute(n, corestring_dom_content, &content);
840
	if (exc != DOM_NO_ERR) {
841
		return NSERROR_DOM;
842
	}
843
 
844
	if (content == NULL) {
845
		return NSERROR_OK;
846
	}
847
 
848
	end = dom_string_data(content) + dom_string_byte_length(content);
849
 
850
	/* content  := *LWS intpart fracpart? *LWS [';' *LWS *1url *LWS]
851
	 * intpart  := 1*DIGIT
852
	 * fracpart := 1*('.' | DIGIT)
853
	 * url      := "url" *LWS '=' *LWS (url-nq | url-sq | url-dq)
854
	 * url-nq   := *urlchar
855
	 * url-sq   := "'" *(urlchar | '"') "'"
856
	 * url-dq   := '"' *(urlchar | "'") '"'
857
	 * urlchar  := [#x9#x21#x23-#x26#x28-#x7E] | nonascii
858
	 * nonascii := [#x80-#xD7FF#xE000-#xFFFD#x10000-#x10FFFF]
859
	 */
860
 
861
	url = dom_string_data(content);
862
 
863
	/* *LWS */
864
	while (url < end && isspace(*url)) {
865
		url++;
866
	}
867
 
868
	/* intpart */
869
	if (url == end || (*url < '0' || '9' < *url)) {
870
		/* Empty content, or invalid timeval */
871
		dom_string_unref(content);
872
		return NSERROR_OK;
873
	}
874
 
875
	msg_data.delay = (int) strtol(url, &new_url, 10);
876
	/* a very small delay and self-referencing URL can cause a loop
877
	 * that grinds machines to a halt. To prevent this we set a
878
	 * minimum refresh delay of 1s. */
879
	if (msg_data.delay < 1) {
880
		msg_data.delay = 1;
881
	}
882
 
883
	url = new_url;
884
 
885
	/* fracpart? (ignored, as delay is integer only) */
886
	while (url < end && (('0' <= *url && *url <= '9') ||
887
			*url == '.')) {
888
		url++;
889
	}
890
 
891
	/* *LWS */
892
	while (url < end && isspace(*url)) {
893
		url++;
894
	}
895
 
896
	/* ';' */
897
	if (url < end && *url == ';')
898
		url++;
899
 
900
	/* *LWS */
901
	while (url < end && isspace(*url)) {
902
		url++;
903
	}
904
 
905
	if (url == end) {
906
		/* Just delay specified, so refresh current page */
907
		dom_string_unref(content);
908
 
909
		c->base.refresh = nsurl_ref(
910
				content_get_url(&c->base));
911
 
912
		content_broadcast(&c->base, CONTENT_MSG_REFRESH, msg_data);
913
 
914
		return NSERROR_OK;
915
	}
916
 
917
	/* "url" */
918
	if (url <= end - 3) {
919
		if (strncasecmp(url, "url", 3) == 0) {
920
			url += 3;
921
		} else {
922
			/* Unexpected input, ignore this header */
923
			dom_string_unref(content);
924
			return NSERROR_OK;
925
		}
926
	} else {
927
		/* Insufficient input, ignore this header */
928
		dom_string_unref(content);
929
		return NSERROR_OK;
930
	}
931
 
932
	/* *LWS */
933
	while (url < end && isspace(*url)) {
934
		url++;
935
	}
936
 
937
	/* '=' */
938
	if (url < end) {
939
		if (*url == '=') {
940
			url++;
941
		} else {
942
			/* Unexpected input, ignore this header */
943
			dom_string_unref(content);
944
			return NSERROR_OK;
945
		}
946
	} else {
947
		/* Insufficient input, ignore this header */
948
		dom_string_unref(content);
949
		return NSERROR_OK;
950
	}
951
 
952
	/* *LWS */
953
	while (url < end && isspace(*url)) {
954
		url++;
955
	}
956
 
957
	/* '"' or "'" */
958
	if (url < end && (*url == '"' || *url == '\'')) {
959
		quote = *url;
960
		url++;
961
	}
962
 
963
	/* Start of URL */
964
	refresh = url;
965
 
966
	if (quote != 0) {
967
		/* url-sq | url-dq */
968
		while (url < end && *url != quote)
969
			url++;
970
	} else {
971
		/* url-nq */
972
		while (url < end && !isspace(*url))
973
			url++;
974
	}
975
 
976
	/* '"' or "'" or *LWS (we don't care) */
977
	if (url > refresh) {
978
		/* There's a URL */
979
		new_url = strndup(refresh, url - refresh);
980
		if (new_url == NULL) {
981
			dom_string_unref(content);
982
			return NSERROR_NOMEM;
983
		}
984
 
985
		error = nsurl_join(c->base_url, new_url, &nsurl);
986
		if (error == NSERROR_OK) {
987
			/* broadcast valid refresh url */
988
 
989
			c->base.refresh = nsurl;
990
 
991
			content_broadcast(&c->base, CONTENT_MSG_REFRESH, msg_data);
992
		}
993
 
994
		free(new_url);
995
 
996
	}
997
 
998
	dom_string_unref(content);
999
 
1000
	return error;
1001
}
1002
 
1003
/**
1004
 * Search for meta refresh
1005
 *
1006
 * http://wp.netscape.com/assist/net_sites/pushpull.html
1007
 *
1008
 * \param c content structure
1009
 * \param head xml node of head element
1010
 * \return true on success, false otherwise (error reported)
1011
 */
1012
 
1013
static nserror html_meta_refresh(html_content *c, dom_node *head)
1014
{
1015
	dom_node *n, *next;
1016
	dom_exception exc;
1017
	nserror ns_error = NSERROR_OK;
1018
 
1019
	if (head == NULL) {
1020
		return ns_error;
1021
	}
1022
 
1023
	exc = dom_node_get_first_child(head, &n);
1024
	if (exc != DOM_NO_ERR) {
1025
		return NSERROR_DOM;
1026
	}
1027
 
1028
	while (n != NULL) {
1029
		dom_node_type type;
1030
 
1031
		exc = dom_node_get_node_type(n, &type);
1032
		if (exc != DOM_NO_ERR) {
1033
			dom_node_unref(n);
1034
			return NSERROR_DOM;
1035
		}
1036
 
1037
		if (type == DOM_ELEMENT_NODE) {
1038
			dom_string *name;
1039
 
1040
			exc = dom_node_get_node_name(n, &name);
1041
			if (exc != DOM_NO_ERR) {
1042
				dom_node_unref(n);
1043
				return NSERROR_DOM;
1044
			}
1045
 
1046
			/* Recurse into noscript elements */
1047
			if (dom_string_caseless_lwc_isequal(name, corestring_lwc_noscript)) {
1048
				ns_error = html_meta_refresh(c, n);
1049
				if (ns_error != NSERROR_OK) {
1050
					/* Some error occurred */
1051
					dom_string_unref(name);
1052
					dom_node_unref(n);
1053
					return ns_error;
1054
				} else if (c->base.refresh != NULL) {
1055
					/* Meta refresh found - stop */
1056
					dom_string_unref(name);
1057
					dom_node_unref(n);
1058
					return NSERROR_OK;
1059
				}
1060
			} else if (dom_string_caseless_lwc_isequal(name, corestring_lwc_meta)) {
1061
				ns_error = html_meta_refresh_process_element(c, n);
1062
				if (ns_error != NSERROR_OK) {
1063
					/* Some error occurred */
1064
					dom_string_unref(name);
1065
					dom_node_unref(n);
1066
					return ns_error;
1067
				} else if (c->base.refresh != NULL) {
1068
					/* Meta refresh found - stop */
1069
					dom_string_unref(name);
1070
					dom_node_unref(n);
1071
					return NSERROR_OK;
1072
				}
1073
			}
1074
			dom_string_unref(name);
1075
		}
1076
 
1077
		exc = dom_node_get_next_sibling(n, &next);
1078
		if (exc != DOM_NO_ERR) {
1079
			dom_node_unref(n);
1080
			return NSERROR_DOM;
1081
		}
1082
 
1083
		dom_node_unref(n);
1084
		n = next;
1085
	}
1086
 
1087
	return ns_error;
1088
}
1089
 
1090
/**
1091
 * Update a box whose content has completed rendering.
1092
 */
1093
 
1094
static void
1095
html_object_done(struct box *box,
1096
		 hlcache_handle *object,
1097
		 bool background)
1098
{
1099
	struct box *b;
1100
 
1101
	if (background) {
1102
		box->background = object;
1103
		return;
1104
	}
1105
 
1106
	box->object = object;
1107
 
1108
	if (!(box->flags & REPLACE_DIM)) {
1109
		/* invalidate parent min, max widths */
1110
		for (b = box; b; b = b->parent)
1111
			b->max_width = UNKNOWN_MAX_WIDTH;
1112
 
1113
		/* delete any clones of this box */
1114
		while (box->next && (box->next->flags & CLONE)) {
1115
			/* box_free_box(box->next); */
1116
			box->next = box->next->next;
1117
		}
1118
	}
1119
}
1120
 
1121
/**
1122
 * Handle object fetching or loading failure.
1123
 *
1124
 * \param  box         box containing object which failed to load
1125
 * \param  content     document of type CONTENT_HTML
1126
 * \param  background  the object was the background image for the box
1127
 */
1128
 
1129
static void
1130
html_object_failed(struct box *box, html_content *content, bool background)
1131
{
1132
	/* Nothing to do */
1133
	return;
1134
}
1135
 
1136
/**
1137
 * Callback for hlcache_handle_retrieve() for objects.
1138
 */
1139
 
1140
static nserror
1141
html_object_callback(hlcache_handle *object,
1142
		     const hlcache_event *event,
1143
		     void *pw)
1144
{
1145
	struct content_html_object *o = pw;
1146
	html_content *c = (html_content *) o->parent;
1147
	int x, y;
1148
	struct box *box;
1149
 
1150
	assert(c->base.status != CONTENT_STATUS_ERROR);
1151
 
1152
	box = o->box;
1153
 
1154
	switch (event->type) {
1155
	case CONTENT_MSG_LOADING:
1156
		if (c->base.status != CONTENT_STATUS_LOADING && c->bw != NULL)
1157
			content_open(object,
1158
					c->bw, &c->base,
1159
					box->object_params);
1160
		break;
1161
 
1162
	case CONTENT_MSG_READY:
1163
		if (content_can_reformat(object)) {
1164
			/* TODO: avoid knowledge of box internals here */
1165
			content_reformat(object, false,
1166
					box->max_width != UNKNOWN_MAX_WIDTH ?
1167
							box->width : 0,
1168
					box->max_width != UNKNOWN_MAX_WIDTH ?
1169
							box->height : 0);
1170
 
1171
			/* Adjust parent content for new object size */
1172
			html_object_done(box, object, o->background);
1173
			if (c->base.status == CONTENT_STATUS_READY ||
1174
					c->base.status == CONTENT_STATUS_DONE)
1175
				content__reformat(&c->base, false,
1176
						c->base.available_width,
1177
						c->base.height);
1178
		}
1179
		break;
1180
 
1181
	case CONTENT_MSG_DONE:
1182
		c->base.active--;
1183
		LOG(("%d fetches active", c->base.active));
1184
 
1185
		html_object_done(box, object, o->background);
1186
 
1187
		if (c->base.status != CONTENT_STATUS_LOADING &&
1188
				box->flags & REPLACE_DIM) {
1189
			union content_msg_data data;
1190
 
1191
			if (!box_visible(box))
1192
				break;
1193
 
1194
			box_coords(box, &x, &y);
1195
 
1196
			data.redraw.x = x + box->padding[LEFT];
1197
			data.redraw.y = y + box->padding[TOP];
1198
			data.redraw.width = box->width;
1199
			data.redraw.height = box->height;
1200
			data.redraw.full_redraw = true;
1201
 
1202
			content_broadcast(&c->base, CONTENT_MSG_REDRAW, data);
1203
		}
1204
		break;
1205
 
1206
	case CONTENT_MSG_ERROR:
1207
		hlcache_handle_release(object);
1208
 
1209
		o->content = NULL;
1210
 
1211
		c->base.active--;
1212
		LOG(("%d fetches active", c->base.active));
1213
 
1214
		content_add_error(&c->base, "?", 0);
1215
		html_object_failed(box, c, o->background);
1216
		break;
1217
 
1218
	case CONTENT_MSG_STATUS:
1219
		if (event->data.explicit_status_text == NULL) {
1220
			/* Object content's status text updated */
1221
			union content_msg_data data;
1222
			data.explicit_status_text =
1223
					content_get_status_message(object);
1224
			html_set_status(c, data.explicit_status_text);
1225
			content_broadcast(&c->base, CONTENT_MSG_STATUS, data);
1226
		} else {
1227
			/* Object content wants to set explicit message */
1228
			content_broadcast(&c->base, CONTENT_MSG_STATUS,
1229
					event->data);
1230
		}
1231
		break;
1232
 
1233
	case CONTENT_MSG_REFORMAT:
1234
		break;
1235
 
1236
	case CONTENT_MSG_REDRAW:
1237
		if (c->base.status != CONTENT_STATUS_LOADING) {
1238
			union content_msg_data data = event->data;
1239
 
1240
			if (!box_visible(box))
1241
				break;
1242
 
1243
			box_coords(box, &x, &y);
1244
 
1245
			if (hlcache_handle_get_content(object) ==
1246
					event->data.redraw.object) {
1247
				data.redraw.x = data.redraw.x *
1248
					box->width / content_get_width(object);
1249
				data.redraw.y = data.redraw.y *
1250
					box->height /
1251
					content_get_height(object);
1252
				data.redraw.width = data.redraw.width *
1253
					box->width / content_get_width(object);
1254
				data.redraw.height = data.redraw.height *
1255
					box->height /
1256
					content_get_height(object);
1257
				data.redraw.object_width = box->width;
1258
				data.redraw.object_height = box->height;
1259
			}
1260
 
1261
			data.redraw.x += x + box->padding[LEFT];
1262
			data.redraw.y += y + box->padding[TOP];
1263
			data.redraw.object_x += x + box->padding[LEFT];
1264
			data.redraw.object_y += y + box->padding[TOP];
1265
 
1266
			content_broadcast(&c->base, CONTENT_MSG_REDRAW, data);
1267
		}
1268
		break;
1269
 
1270
	case CONTENT_MSG_REFRESH:
1271
		if (content_get_type(object) == CONTENT_HTML) {
1272
			/* only for HTML objects */
1273
			schedule(event->data.delay * 100,
1274
					html_object_refresh, o);
1275
		}
1276
 
1277
		break;
1278
 
1279
	case CONTENT_MSG_LINK:
1280
		/* Don't care about favicons that aren't on top level content */
1281
		break;
1282
 
1283
	case CONTENT_MSG_GETCTX:
1284
		*(event->data.jscontext) = NULL;
1285
		break;
1286
 
1287
	case CONTENT_MSG_SCROLL:
1288
		if (box->scroll_x != NULL)
1289
			scrollbar_set(box->scroll_x, event->data.scroll.x0,
1290
					false);
1291
		if (box->scroll_y != NULL)
1292
			scrollbar_set(box->scroll_y, event->data.scroll.y0,
1293
					false);
1294
		break;
1295
 
1296
	case CONTENT_MSG_DRAGSAVE:
1297
	{
1298
		union content_msg_data msg_data;
1299
		if (event->data.dragsave.content == NULL)
1300
			msg_data.dragsave.content = object;
1301
		else
1302
			msg_data.dragsave.content =
1303
					event->data.dragsave.content;
1304
 
1305
		content_broadcast(&c->base, CONTENT_MSG_DRAGSAVE, msg_data);
1306
	}
1307
		break;
1308
 
1309
	case CONTENT_MSG_SAVELINK:
1310
	case CONTENT_MSG_POINTER:
1311
		/* These messages are for browser window layer.
1312
		 * we're not interested, so pass them on. */
1313
		content_broadcast(&c->base, event->type, event->data);
1314
		break;
1315
 
1316
	default:
1317
		assert(0);
1318
	}
1319
 
1320
	if (c->base.status == CONTENT_STATUS_READY && c->base.active == 0 &&
1321
			(event->type == CONTENT_MSG_LOADING ||
1322
			event->type == CONTENT_MSG_DONE ||
1323
			event->type == CONTENT_MSG_ERROR)) {
1324
		/* all objects have arrived */
1325
		content__reformat(&c->base, false, c->base.available_width,
1326
				c->base.height);
1327
		html_set_status(c, "");
1328
		content_set_done(&c->base);
1329
	}
1330
 
1331
	/* If  1) the configuration option to reflow pages while objects are
1332
	 *        fetched is set
1333
	 *     2) an object is newly fetched & converted,
1334
	 *     3) the box's dimensions need to change due to being replaced
1335
	 *     4) the object's parent HTML is ready for reformat,
1336
	 *     5) the time since the previous reformat is more than the
1337
	 *        configured minimum time between reformats
1338
	 * then reformat the page to display newly fetched objects */
1339
	else if (nsoption_bool(incremental_reflow) &&
1340
			event->type == CONTENT_MSG_DONE &&
1341
			!(box->flags & REPLACE_DIM) &&
1342
			(c->base.status == CONTENT_STATUS_READY ||
1343
			 c->base.status == CONTENT_STATUS_DONE) &&
1344
			(wallclock() > c->base.reformat_time)) {
1345
		content__reformat(&c->base, false, c->base.available_width,
1346
				c->base.height);
1347
	}
1348
 
1349
	return NSERROR_OK;
1350
}
1351
 
1352
/**
1353
 * Start a fetch for an object required by a page, replacing an existing object.
1354
 *
1355
 * \param  object          Object to replace
1356
 * \param  url             URL of object to fetch (copied)
1357
 * \return  true on success, false on memory exhaustion
1358
 */
1359
 
1360
static bool html_replace_object(struct content_html_object *object, nsurl *url)
1361
{
1362
	html_content *c;
1363
	hlcache_child_context child;
1364
	html_content *page;
1365
	nserror error;
1366
 
1367
	assert(object != NULL);
1368
 
1369
	c = (html_content *) object->parent;
1370
 
1371
	child.charset = c->encoding;
1372
	child.quirks = c->base.quirks;
1373
 
1374
	if (object->content != NULL) {
1375
		/* remove existing object */
1376
		if (content_get_status(object->content) != CONTENT_STATUS_DONE) {
1377
			c->base.active--;
1378
			LOG(("%d fetches active", c->base.active));
1379
		}
1380
 
1381
		hlcache_handle_release(object->content);
1382
		object->content = NULL;
1383
 
1384
		object->box->object = NULL;
1385
	}
1386
 
1387
	/* initialise fetch */
1388
	error = hlcache_handle_retrieve(url, HLCACHE_RETRIEVE_SNIFF_TYPE,
1389
			content_get_url(&c->base), NULL,
1390
			html_object_callback, object, &child,
1391
			object->permitted_types,
1392
			&object->content);
1393
 
1394
	if (error != NSERROR_OK)
1395
		return false;
1396
 
1397
	for (page = c; page != NULL; page = page->page) {
1398
		page->base.active++;
1399
		LOG(("%d fetches active", c->base.active));
1400
 
1401
		page->base.status = CONTENT_STATUS_READY;
1402
	}
1403
 
1404
	return true;
1405
}
1406
 
1407
/**
1408
 * schedule() callback for object refresh
1409
 */
1410
 
1411
static void html_object_refresh(void *p)
1412
{
1413
	struct content_html_object *object = p;
1414
	nsurl *refresh_url;
1415
 
1416
	assert(content_get_type(object->content) == CONTENT_HTML);
1417
 
1418
	refresh_url = content_get_refresh_url(object->content);
1419
 
1420
	/* Ignore if refresh URL has gone
1421
	 * (may happen if fetch errored) */
1422
	if (refresh_url == NULL)
1423
		return;
1424
 
1425
	content_invalidate_reuse_data(object->content);
1426
 
1427
	if (!html_replace_object(object, refresh_url)) {
1428
		/** \todo handle memory exhaustion */
1429
	}
1430
}
1431
 
1432
 
1433
 
1434
 
1435
 
1436
/**
1437
 * Callback for fetchcache() for linked stylesheets.
1438
 */
1439
 
1440
static nserror
1441
html_convert_css_callback(hlcache_handle *css,
1442
			  const hlcache_event *event,
1443
			  void *pw)
1444
{
1445
	html_content *parent = pw;
1446
	unsigned int i;
1447
	struct html_stylesheet *s;
1448
 
1449
	/* Find sheet */
1450
	for (i = 0, s = parent->stylesheets;
1451
			i != parent->stylesheet_count; i++, s++) {
1452
		if (s->type == HTML_STYLESHEET_EXTERNAL &&
1453
				s->data.external == css)
1454
			break;
1455
	}
1456
 
1457
	assert(i != parent->stylesheet_count);
1458
 
1459
	switch (event->type) {
1460
	case CONTENT_MSG_LOADING:
1461
		break;
1462
 
1463
	case CONTENT_MSG_READY:
1464
		break;
1465
 
1466
	case CONTENT_MSG_DONE:
1467
		LOG(("done stylesheet slot %d '%s'", i,
1468
				nsurl_access(hlcache_handle_get_url(css))));
5043 ashmew2 1469
		LOG(("Decrementing parent"));
3584 sourcerer 1470
		parent->base.active--;
1471
		LOG(("%d fetches active", parent->base.active));
1472
		break;
1473
 
1474
	case CONTENT_MSG_ERROR:
1475
		LOG(("stylesheet %s failed: %s",
1476
				nsurl_access(hlcache_handle_get_url(css)),
1477
				event->data.error));
1478
		hlcache_handle_release(css);
1479
		s->data.external = NULL;
1480
		parent->base.active--;
1481
		LOG(("%d fetches active", parent->base.active));
1482
		content_add_error(&parent->base, "?", 0);
1483
		break;
1484
 
1485
	case CONTENT_MSG_STATUS:
1486
		if (event->data.explicit_status_text == NULL) {
1487
			/* Object content's status text updated */
1488
			html_set_status(parent,
1489
					content_get_status_message(css));
1490
			content_broadcast(&parent->base, CONTENT_MSG_STATUS,
1491
					event->data);
1492
		} else {
1493
			/* Object content wants to set explicit message */
1494
			content_broadcast(&parent->base, CONTENT_MSG_STATUS,
1495
					event->data);
1496
		}
1497
		break;
1498
 
1499
	default:
1500
		assert(0);
1501
	}
1502
 
1503
	if (parent->base.active == 0)
5043 ashmew2 1504
	  {
1505
	    /* LOG(("parent->base.active == 0")); */
1506
	    html_finish_conversion(parent);
1507
	  }
1508
 
1509
	/* LOG(("Returning NSERROR_OK from html_redraw")); */
3584 sourcerer 1510
 
1511
	return NSERROR_OK;
1512
}
1513
 
1514
/**
1515
 * Handle notification of inline style completion
1516
 *
1517
 * \param css  Inline style object
1518
 * \param pw   Private data
1519
 */
1520
static void html_inline_style_done(struct content_css_data *css, void *pw)
1521
{
1522
	html_content *html = pw;
1523
 
1524
	if (--html->base.active == 0)
1525
		html_finish_conversion(html);
1526
}
1527
 
1528
/**
1529
 * Process an inline stylesheet in the document.
1530
 *
1531
 * \param  c      content structure
1532
 * \param  index  Index of stylesheet in stylesheet_content array,
1533
 *                updated if successful
1534
 * \param  style  xml node of style element
1535
 * \return  true on success, false if an error occurred
1536
 */
1537
 
1538
static bool
1539
html_process_style_element(html_content *c,
1540
			   unsigned int *index,
1541
			   dom_node *style)
1542
{
1543
	dom_node *child, *next;
1544
	dom_string *val;
1545
	dom_exception exc;
1546
	struct html_stylesheet *stylesheets;
1547
	struct content_css_data *sheet;
1548
	nserror error;
1549
 
1550
	/* type='text/css', or not present (invalid but common) */
1551
	exc = dom_element_get_attribute(style, corestring_dom_type, &val);
1552
	if (exc == DOM_NO_ERR && val != NULL) {
1553
		if (!dom_string_caseless_lwc_isequal(val,
1554
				corestring_lwc_text_css)) {
1555
			dom_string_unref(val);
1556
			return true;
1557
		}
1558
		dom_string_unref(val);
1559
	}
1560
 
1561
	/* media contains 'screen' or 'all' or not present */
1562
	exc = dom_element_get_attribute(style, corestring_dom_media, &val);
1563
	if (exc == DOM_NO_ERR && val != NULL) {
1564
		if (strcasestr(dom_string_data(val), "screen") == NULL &&
1565
				strcasestr(dom_string_data(val),
1566
						"all") == NULL) {
1567
			dom_string_unref(val);
1568
			return true;
1569
		}
1570
		dom_string_unref(val);
1571
	}
1572
 
1573
	/* Extend array */
1574
	stylesheets = realloc(c->stylesheets,
1575
			      sizeof(struct html_stylesheet) * (*index + 1));
1576
	if (stylesheets == NULL)
1577
		goto no_memory;
1578
 
1579
	c->stylesheets = stylesheets;
1580
	c->stylesheet_count++;
1581
 
1582
	c->stylesheets[(*index)].type = HTML_STYLESHEET_INTERNAL;
1583
	c->stylesheets[(*index)].data.internal = NULL;
1584
 
1585
	/* create stylesheet */
1586
	sheet = calloc(1, sizeof(struct content_css_data));
1587
	if (sheet == NULL) {
1588
		c->stylesheet_count--;
1589
		goto no_memory;
1590
	}
1591
 
1592
	error = nscss_create_css_data(sheet,
1593
		nsurl_access(c->base_url), NULL, c->quirks,
1594
		html_inline_style_done, c);
1595
	if (error != NSERROR_OK) {
1596
		free(sheet);
1597
		c->stylesheet_count--;
1598
		content_broadcast_errorcode(&c->base, error);
1599
		return false;
1600
	}
1601
 
1602
	/* can't just use xmlNodeGetContent(style), because that won't
1603
	 * give the content of comments which may be used to 'hide'
1604
	 * the content */
1605
	exc = dom_node_get_first_child(style, &child);
1606
	if (exc != DOM_NO_ERR) {
1607
		nscss_destroy_css_data(sheet);
1608
		free(sheet);
1609
		c->stylesheet_count--;
1610
		goto no_memory;
1611
	}
1612
 
1613
	while (child != NULL) {
1614
		dom_string *data;
1615
 
1616
		exc = dom_node_get_text_content(child, &data);
1617
		if (exc != DOM_NO_ERR) {
1618
			dom_node_unref(child);
1619
			nscss_destroy_css_data(sheet);
1620
			free(sheet);
1621
			c->stylesheet_count--;
1622
			goto no_memory;
1623
		}
1624
 
1625
		if (nscss_process_css_data(sheet, dom_string_data(data),
1626
				dom_string_byte_length(data)) == false) {
1627
			dom_string_unref(data);
1628
			dom_node_unref(child);
1629
			nscss_destroy_css_data(sheet);
1630
			free(sheet);
1631
			c->stylesheet_count--;
1632
			goto no_memory;
1633
		}
1634
 
1635
		dom_string_unref(data);
1636
 
1637
		exc = dom_node_get_next_sibling(child, &next);
1638
		if (exc != DOM_NO_ERR) {
1639
			dom_node_unref(child);
1640
			nscss_destroy_css_data(sheet);
1641
			free(sheet);
1642
			c->stylesheet_count--;
1643
			goto no_memory;
1644
		}
1645
 
1646
		dom_node_unref(child);
1647
		child = next;
1648
	}
1649
 
1650
	c->base.active++;
1651
	LOG(("%d fetches active", c->base.active));
1652
 
1653
	/* Convert the content -- manually, as we want the result */
1654
	if (nscss_convert_css_data(sheet) != CSS_OK) {
1655
		/* conversion failed */
1656
		c->base.active--;
1657
		LOG(("%d fetches active", c->base.active));
1658
		nscss_destroy_css_data(sheet);
1659
		free(sheet);
1660
		sheet = NULL;
1661
	}
1662
 
1663
	/* Update index */
1664
	c->stylesheets[(*index)].data.internal = sheet;
1665
	(*index)++;
1666
 
1667
	return true;
1668
 
1669
no_memory:
1670
	content_broadcast_errorcode(&c->base, NSERROR_NOMEM);
1671
	return false;
1672
}
1673
 
1674
 
1675
 
1676
struct find_stylesheet_ctx {
1677
	unsigned int count;
1678
	html_content *c;
1679
};
1680
 
1681
/** callback to process stylesheet elements
1682
 */
1683
static bool
1684
html_process_stylesheet(dom_node *node, dom_string *name, void *vctx)
1685
{
1686
	struct find_stylesheet_ctx *ctx = (struct find_stylesheet_ctx *)vctx;
1687
	dom_string *rel, *type_attr, *media, *href;
1688
	struct html_stylesheet *stylesheets;
1689
	nsurl *joined;
1690
	dom_exception exc;
1691
	nserror ns_error;
1692
	hlcache_child_context child;
1693
 
1694
	/* deal with style nodes */
1695
	if (dom_string_caseless_lwc_isequal(name, corestring_lwc_style)) {
1696
		if (!html_process_style_element(ctx->c,	&ctx->count, node))
1697
			return false;
1698
		return true;
1699
	}
1700
 
1701
	/* if it is not a link node skip it */
1702
	if (!dom_string_caseless_lwc_isequal(name, corestring_lwc_link)) {
1703
		return true;
1704
	}
1705
 
1706
	/* rel= */
1707
	exc = dom_element_get_attribute(node,
1708
					corestring_dom_rel, &rel);
1709
	if (exc != DOM_NO_ERR || rel == NULL)
1710
		return true;
1711
 
1712
	if (strcasestr(dom_string_data(rel), "stylesheet") == 0) {
1713
		dom_string_unref(rel);
1714
		return true;
1715
	} else if (strcasestr(dom_string_data(rel), "alternate") != 0) {
1716
		/* Ignore alternate stylesheets */
1717
		dom_string_unref(rel);
1718
		return true;
1719
	}
1720
	dom_string_unref(rel);
1721
 
1722
	/* type='text/css' or not present */
1723
	exc = dom_element_get_attribute(node, corestring_dom_type, &type_attr);
1724
	if (exc == DOM_NO_ERR && type_attr != NULL) {
1725
		if (!dom_string_caseless_lwc_isequal(type_attr,
1726
				corestring_lwc_text_css)) {
1727
			dom_string_unref(type_attr);
1728
			return true;
1729
		}
1730
		dom_string_unref(type_attr);
1731
	}
1732
 
1733
	/* media contains 'screen' or 'all' or not present */
1734
	exc = dom_element_get_attribute(node, corestring_dom_media, &media);
1735
	if (exc == DOM_NO_ERR && media != NULL) {
1736
		if (strcasestr(dom_string_data(media), "screen") == NULL &&
1737
		    strcasestr(dom_string_data(media), "all") == NULL) {
1738
			dom_string_unref(media);
1739
			return true;
1740
		}
1741
		dom_string_unref(media);
1742
	}
1743
 
1744
	/* href='...' */
1745
	exc = dom_element_get_attribute(node, corestring_dom_href, &href);
1746
	if (exc != DOM_NO_ERR || href == NULL)
1747
		return true;
1748
 
1749
	/* TODO: only the first preferred stylesheets (ie.
1750
	 * those with a title attribute) should be loaded
1751
	 * (see HTML4 14.3) */
1752
 
1753
	ns_error = nsurl_join(ctx->c->base_url, dom_string_data(href), &joined);
1754
	if (ns_error != NSERROR_OK) {
1755
		dom_string_unref(href);
1756
		goto no_memory;
1757
	}
1758
	dom_string_unref(href);
1759
 
1760
	LOG(("linked stylesheet %i '%s'", ctx->count, nsurl_access(joined)));
1761
 
1762
	/* start fetch */
1763
	stylesheets = realloc(ctx->c->stylesheets,
1764
			      sizeof(struct html_stylesheet) * (ctx->count + 1));
1765
	if (stylesheets == NULL) {
1766
		nsurl_unref(joined);
1767
		ns_error = NSERROR_NOMEM;
1768
		goto no_memory;
1769
	}
1770
 
1771
	ctx->c->stylesheets = stylesheets;
1772
	ctx->c->stylesheet_count++;
1773
	ctx->c->stylesheets[ctx->count].type = HTML_STYLESHEET_EXTERNAL;
1774
 
1775
	child.charset = ctx->c->encoding;
1776
	child.quirks = ctx->c->base.quirks;
1777
 
1778
	ns_error = hlcache_handle_retrieve(joined,
1779
					   0,
1780
					   content_get_url(&ctx->c->base),
1781
					   NULL,
1782
					   html_convert_css_callback,
1783
					   ctx->c,
1784
					   &child,
1785
					   CONTENT_CSS,
1786
					   &ctx->c->stylesheets[ctx->count].data.external);
1787
 
1788
	nsurl_unref(joined);
1789
 
1790
	if (ns_error != NSERROR_OK)
1791
		goto no_memory;
1792
 
1793
	ctx->c->base.active++;
1794
	LOG(("%d fetches active", ctx->c->base.active));
1795
 
1796
	ctx->count++;
1797
 
1798
	return true;
1799
 
1800
no_memory:
1801
	content_broadcast_errorcode(&ctx->c->base, ns_error);
1802
	return false;
1803
}
1804
 
1805
 
1806
/**
1807
 * Process inline stylesheets and fetch linked stylesheets.
1808
 *
1809
 * Uses STYLE and LINK elements inside and outside HEAD
1810
 *
1811
 * \param c content structure
1812
 * \param html dom node of html element
1813
 * \return true on success, false if an error occurred
1814
 */
1815
 
1816
static bool html_find_stylesheets(html_content *c, dom_node *html)
1817
{
1818
	nserror ns_error;
1819
	bool result;
1820
	struct find_stylesheet_ctx ctx;
1821
	hlcache_child_context child;
1822
 
1823
	/* setup context */
1824
	ctx.c = c;
1825
	ctx.count = STYLESHEET_START;
1826
 
1827
	/* stylesheet 0 is the base style sheet,
1828
	 * stylesheet 1 is the quirks mode style sheet,
1829
	 * stylesheet 2 is the adblocking stylesheet,
1830
	 * stylesheet 3 is the user stylesheet */
1831
	c->stylesheets = calloc(STYLESHEET_START, sizeof(struct html_stylesheet));
1832
	if (c->stylesheets == NULL) {
1833
		ns_error = NSERROR_NOMEM;
1834
		goto html_find_stylesheets_no_memory;
1835
	}
1836
 
1837
	c->stylesheets[STYLESHEET_BASE].type = HTML_STYLESHEET_EXTERNAL;
1838
	c->stylesheets[STYLESHEET_BASE].data.external = NULL;
1839
	c->stylesheets[STYLESHEET_QUIRKS].type = HTML_STYLESHEET_EXTERNAL;
1840
	c->stylesheets[STYLESHEET_QUIRKS].data.external = NULL;
1841
	c->stylesheets[STYLESHEET_ADBLOCK].type = HTML_STYLESHEET_EXTERNAL;
1842
	c->stylesheets[STYLESHEET_ADBLOCK].data.external = NULL;
1843
	c->stylesheets[STYLESHEET_USER].type = HTML_STYLESHEET_EXTERNAL;
1844
	c->stylesheets[STYLESHEET_USER].data.external = NULL;
1845
	c->stylesheet_count = STYLESHEET_START;
1846
 
1847
	child.charset = c->encoding;
1848
	child.quirks = c->base.quirks;
1849
 
1850
	ns_error = hlcache_handle_retrieve(html_default_stylesheet_url, 0,
1851
			content_get_url(&c->base), NULL,
1852
			html_convert_css_callback, c, &child, CONTENT_CSS,
1853
			&c->stylesheets[STYLESHEET_BASE].data.external);
1854
	if (ns_error != NSERROR_OK)
1855
		goto html_find_stylesheets_no_memory;
1856
 
1857
	c->base.active++;
1858
	LOG(("%d fetches active", c->base.active));
1859
 
1860
	if (c->quirks == DOM_DOCUMENT_QUIRKS_MODE_FULL) {
1861
		ns_error = hlcache_handle_retrieve(html_quirks_stylesheet_url,
1862
				0, content_get_url(&c->base), NULL,
1863
				html_convert_css_callback, c, &child,
1864
				CONTENT_CSS,
1865
				&c->stylesheets[STYLESHEET_QUIRKS].data.external);
1866
		if (ns_error != NSERROR_OK)
1867
			goto html_find_stylesheets_no_memory;
1868
 
1869
		c->base.active++;
1870
		LOG(("%d fetches active", c->base.active));
1871
 
1872
	}
1873
 
1874
	if (nsoption_bool(block_ads)) {
1875
		ns_error = hlcache_handle_retrieve(html_adblock_stylesheet_url,
1876
				0, content_get_url(&c->base), NULL,
1877
				html_convert_css_callback, c, &child, CONTENT_CSS,
1878
				&c->stylesheets[STYLESHEET_ADBLOCK].
1879
						data.external);
1880
		if (ns_error != NSERROR_OK)
1881
			goto html_find_stylesheets_no_memory;
1882
 
1883
		c->base.active++;
1884
		LOG(("%d fetches active", c->base.active));
1885
 
1886
	}
1887
 
1888
	ns_error = hlcache_handle_retrieve(html_user_stylesheet_url, 0,
1889
			content_get_url(&c->base), NULL,
1890
			html_convert_css_callback, c, &child, CONTENT_CSS,
1891
			&c->stylesheets[STYLESHEET_USER].data.external);
1892
	if (ns_error != NSERROR_OK)
1893
		goto html_find_stylesheets_no_memory;
1894
 
1895
	c->base.active++;
1896
	LOG(("%d fetches active", c->base.active));
1897
 
1898
	result = libdom_treewalk(html, html_process_stylesheet, &ctx);
1899
 
1900
	assert(c->stylesheet_count == ctx.count);
1901
 
1902
	return result;
1903
 
1904
html_find_stylesheets_no_memory:
1905
	content_broadcast_errorcode(&c->base, ns_error);
1906
	return false;
1907
}
1908
 
1909
/**
1910
 * Convert a CONTENT_HTML for display.
1911
 *
1912
 * The following steps are carried out in order:
1913
 *
1914
 *  - parsing to an XML tree is completed
1915
 *  - stylesheets are fetched
1916
 *  - the XML tree is converted to a box tree and object fetches are started
1917
 *
1918
 * On exit, the content status will be either CONTENT_STATUS_DONE if the
1919
 * document is completely loaded or CONTENT_STATUS_READY if objects are still
1920
 * being fetched.
1921
 */
1922
 
1923
static bool html_convert(struct content *c)
1924
{
1925
	html_content *htmlc = (html_content *) c;
1926
 
1927
	htmlc->base.active--; /* the html fetch is no longer active */
1928
	LOG(("%d fetches active", htmlc->base.active));
1929
 
1930
	/* if there are no active fetches in progress no scripts are
1931
	 * being fetched or they completed already.
1932
	 */
1933
	if (htmlc->base.active == 0) {
1934
		return html_begin_conversion(htmlc);
1935
	}
1936
	return true;
1937
}
1938
 
1939
bool
1940
html_begin_conversion(html_content *htmlc)
1941
{
1942
	dom_node *html, *head;
1943
	nserror ns_error;
1944
	struct form *f;
1945
	dom_exception exc; /* returned by libdom functions */
1946
	dom_string *node_name = NULL;
1947
	dom_hubbub_error error;
1948
 
1949
	/* complete parsing */
1950
	error = dom_hubbub_parser_completed(htmlc->parser);
1951
	if (error != DOM_HUBBUB_OK) {
1952
		LOG(("Parsing failed"));
1953
 
1954
		content_broadcast_errorcode(&htmlc->base,
1955
					    libdom_hubbub_error_to_nserror(error));
1956
 
1957
		return false;
1958
	}
1959
 
1960
	/* Give up processing if we've been aborted */
1961
	if (htmlc->aborted) {
1962
		content_broadcast_errorcode(&htmlc->base, NSERROR_STOPPED);
1963
		return false;
1964
	}
1965
 
1966
 
1967
	/* complete script execution */
1968
	html_scripts_exec(htmlc);
1969
 
1970
	/* fire a simple event that bubbles named DOMContentLoaded at
1971
	 * the Document.
1972
	 */
1973
 
1974
	/* quirks mode */
1975
	exc = dom_document_get_quirks_mode(htmlc->document, &htmlc->quirks);
1976
	if (exc != DOM_NO_ERR) {
1977
		LOG(("error retrieving quirks"));
1978
		/** @todo should this be fatal to the conversion? */
1979
	}
1980
	LOG(("quirks set to %d", htmlc->quirks));
1981
 
1982
	/* get encoding */
1983
	if (htmlc->encoding == NULL) {
1984
		const char *encoding;
1985
 
1986
		encoding = dom_hubbub_parser_get_encoding(htmlc->parser,
1987
					&htmlc->encoding_source);
1988
		if (encoding == NULL) {
1989
			content_broadcast_errorcode(&htmlc->base,
1990
						    NSERROR_NOMEM);
1991
			return false;
1992
		}
1993
 
1994
		htmlc->encoding = strdup(encoding);
1995
		if (htmlc->encoding == NULL) {
1996
			content_broadcast_errorcode(&htmlc->base,
1997
						    NSERROR_NOMEM);
1998
			return false;
1999
		}
2000
	}
2001
 
2002
	/* locate root element and ensure it is html */
2003
	exc = dom_document_get_document_element(htmlc->document, (void *) &html);
2004
	if ((exc != DOM_NO_ERR) || (html == NULL)) {
2005
		LOG(("error retrieving html element from dom"));
2006
		content_broadcast_errorcode(&htmlc->base, NSERROR_DOM);
2007
		return false;
2008
	}
2009
 
2010
	exc = dom_node_get_node_name(html, &node_name);
2011
	if ((exc != DOM_NO_ERR) ||
2012
	    (node_name == NULL) ||
2013
	    (!dom_string_caseless_lwc_isequal(node_name,
2014
	    		corestring_lwc_html))) {
2015
		LOG(("root element not html"));
2016
		content_broadcast_errorcode(&htmlc->base, NSERROR_DOM);
2017
		dom_node_unref(html);
2018
		return false;
2019
	}
2020
	dom_string_unref(node_name);
2021
 
2022
	head = libdom_find_first_element(html, corestring_lwc_head);
2023
	if (head != NULL) {
2024
		ns_error = html_head(htmlc, head);
2025
		if (ns_error != NSERROR_OK) {
2026
			content_broadcast_errorcode(&htmlc->base, ns_error);
2027
 
2028
			dom_node_unref(html);
2029
			dom_node_unref(head);
2030
			return false;
2031
		}
2032
 
2033
		/* handle meta refresh */
2034
		ns_error = html_meta_refresh(htmlc, head);
2035
		if (ns_error != NSERROR_OK) {
2036
			content_broadcast_errorcode(&htmlc->base, ns_error);
2037
 
2038
			dom_node_unref(html);
2039
			dom_node_unref(head);
2040
			return false;
2041
		}
2042
	}
2043
 
2044
	/* Retrieve forms from parser */
2045
	htmlc->forms = html_forms_get_forms(htmlc->encoding,
2046
			(dom_html_document *) htmlc->document);
2047
	for (f = htmlc->forms; f != NULL; f = f->prev) {
2048
		nsurl *action;
2049
 
2050
		/* Make all actions absolute */
2051
		if (f->action == NULL || f->action[0] == '\0') {
2052
			/* HTML5 4.10.22.3 step 9 */
2053
			nsurl *doc_addr = content_get_url(&htmlc->base);
2054
			ns_error = nsurl_join(htmlc->base_url,
2055
					      nsurl_access(doc_addr),
2056
					      &action);
2057
		} else {
2058
			ns_error = nsurl_join(htmlc->base_url,
2059
					      f->action,
2060
					      &action);
2061
		}
2062
 
2063
		if (ns_error != NSERROR_OK) {
2064
			content_broadcast_errorcode(&htmlc->base, ns_error);
2065
 
2066
			dom_node_unref(html);
2067
			dom_node_unref(head);
2068
			return false;
2069
		}
2070
 
2071
		free(f->action);
2072
		f->action = strdup(nsurl_access(action));
2073
		nsurl_unref(action);
2074
		if (f->action == NULL) {
2075
			content_broadcast_errorcode(&htmlc->base,
2076
						    NSERROR_NOMEM);
2077
 
2078
			dom_node_unref(html);
2079
			dom_node_unref(head);
2080
			return false;
2081
		}
2082
 
2083
		/* Ensure each form has a document encoding */
2084
		if (f->document_charset == NULL) {
2085
			f->document_charset = strdup(htmlc->encoding);
2086
			if (f->document_charset == NULL) {
2087
				content_broadcast_errorcode(&htmlc->base,
2088
							    NSERROR_NOMEM);
2089
				dom_node_unref(html);
2090
				dom_node_unref(head);
2091
				return false;
2092
			}
2093
		}
2094
	}
2095
 
2096
	dom_node_unref(head);
2097
 
2098
	/* get stylesheets */
2099
	if (html_find_stylesheets(htmlc, html) == false) {
2100
		dom_node_unref(html);
2101
		return false;
2102
	}
2103
 
2104
	dom_node_unref(html);
2105
 
2106
	if (htmlc->base.active == 0) {
2107
		html_finish_conversion(htmlc);
2108
	}
2109
 
2110
	return true;
2111
}
2112
 
2113
 
2114
 
2115
 
2116
/**
2117
 * Start a fetch for an object required by a page.
2118
 *
2119
 * \param  c                 content of type CONTENT_HTML
2120
 * \param  url               URL of object to fetch (copied)
2121
 * \param  box               box that will contain the object
2122
 * \param  permitted_types   bitmap of acceptable types
2123
 * \param  available_width   estimate of width of object
2124
 * \param  available_height  estimate of height of object
2125
 * \param  background        this is a background image
2126
 * \return  true on success, false on memory exhaustion
2127
 */
2128
 
2129
bool html_fetch_object(html_content *c, nsurl *url, struct box *box,
2130
		content_type permitted_types,
2131
		int available_width, int available_height,
2132
		bool background)
2133
{
2134
	struct content_html_object *object;
2135
	hlcache_child_context child;
2136
	nserror error;
2137
 
2138
	/* If we've already been aborted, don't bother attempting the fetch */
2139
	if (c->aborted)
2140
		return true;
2141
 
2142
	child.charset = c->encoding;
2143
	child.quirks = c->base.quirks;
2144
 
2145
	object = calloc(1, sizeof(struct content_html_object));
2146
	if (object == NULL) {
2147
		return false;
2148
	}
2149
 
2150
	object->parent = (struct content *) c;
2151
	object->next = NULL;
2152
	object->content = NULL;
2153
	object->box = box;
2154
	object->permitted_types = permitted_types;
2155
	object->background = background;
2156
 
2157
	error = hlcache_handle_retrieve(url,
2158
			HLCACHE_RETRIEVE_SNIFF_TYPE,
2159
			content_get_url(&c->base), NULL,
2160
			html_object_callback, object, &child,
2161
			object->permitted_types, &object->content);
2162
       	if (error != NSERROR_OK) {
2163
		free(object);
2164
		return error != NSERROR_NOMEM;
2165
	}
2166
 
2167
	/* add to content object list */
2168
	object->next = c->object_list;
2169
	c->object_list = object;
2170
 
2171
	c->num_objects++;
2172
	c->base.active++;
2173
	LOG(("%d fetches active", c->base.active));
2174
 
2175
	return true;
2176
}
2177
 
2178
 
2179
 
2180
 
2181
 
2182
/**
2183
 * Stop loading a CONTENT_HTML.
2184
 */
2185
 
2186
static void html_stop(struct content *c)
2187
{
2188
	html_content *htmlc = (html_content *) c;
2189
	struct content_html_object *object;
2190
 
2191
	switch (c->status) {
2192
	case CONTENT_STATUS_LOADING:
2193
		/* Still loading; simply flag that we've been aborted
2194
		 * html_convert/html_finish_conversion will do the rest */
2195
		htmlc->aborted = true;
2196
		break;
2197
	case CONTENT_STATUS_READY:
2198
		for (object = htmlc->object_list; object != NULL;
2199
				object = object->next) {
2200
			if (object->content == NULL)
2201
				continue;
2202
 
2203
			if (content_get_status(object->content) ==
2204
					CONTENT_STATUS_DONE)
2205
				; /* already loaded: do nothing */
2206
			else if (content_get_status(object->content) ==
2207
					CONTENT_STATUS_READY)
2208
				hlcache_handle_abort(object->content);
2209
				/* Active count will be updated when
2210
				 * html_object_callback receives
2211
 				 * CONTENT_MSG_DONE from this object */
2212
			else {
2213
				hlcache_handle_abort(object->content);
2214
				hlcache_handle_release(object->content);
2215
				object->content = NULL;
2216
 
2217
				c->active--;
2218
				LOG(("%d fetches active", c->active));
2219
 
2220
			}
2221
		}
2222
 
2223
		/* If there are no further active fetches and we're still
2224
 		 * in the READY state, transition to the DONE state. */
2225
		if (c->status == CONTENT_STATUS_READY && c->active == 0) {
2226
			html_set_status(htmlc, "");
2227
			content_set_done(c);
2228
		}
2229
 
2230
		break;
2231
	case CONTENT_STATUS_DONE:
2232
		/* Nothing to do */
2233
		break;
2234
	default:
2235
		LOG(("Unexpected status %d", c->status));
2236
		assert(0);
2237
	}
2238
}
2239
 
2240
 
2241
/**
2242
 * Reformat a CONTENT_HTML to a new width.
2243
 */
2244
 
2245
static void html_reformat(struct content *c, int width, int height)
2246
{
2247
	html_content *htmlc = (html_content *) c;
2248
	struct box *layout;
2249
	unsigned int time_before, time_taken;
2250
 
2251
	time_before = wallclock();
2252
 
2253
	layout_document(htmlc, width, height);
2254
	layout = htmlc->layout;
2255
 
2256
	/* width and height are at least margin box of document */
2257
	c->width = layout->x + layout->padding[LEFT] + layout->width +
2258
			layout->padding[RIGHT] + layout->border[RIGHT].width +
2259
			layout->margin[RIGHT];
2260
	c->height = layout->y + layout->padding[TOP] + layout->height +
2261
			layout->padding[BOTTOM] + layout->border[BOTTOM].width +
2262
			layout->margin[BOTTOM];
2263
 
2264
	/* if boxes overflow right or bottom edge, expand to contain it */
2265
	if (c->width < layout->x + layout->descendant_x1)
2266
		c->width = layout->x + layout->descendant_x1;
2267
	if (c->height < layout->y + layout->descendant_y1)
2268
		c->height = layout->y + layout->descendant_y1;
2269
 
2270
	selection_reinit(&htmlc->sel, htmlc->layout);
2271
 
2272
	time_taken = wallclock() - time_before;
2273
	c->reformat_time = wallclock() +
2274
		((time_taken * 3 < nsoption_int(min_reflow_period) ?
2275
		  nsoption_int(min_reflow_period) : time_taken * 3));
2276
}
2277
 
2278
 
2279
/**
2280
 * Redraw a box.
2281
 *
2282
 * \param  h	content containing the box, of type CONTENT_HTML
2283
 * \param  box  box to redraw
2284
 */
2285
 
2286
void html_redraw_a_box(hlcache_handle *h, struct box *box)
2287
{
2288
	int x, y;
2289
 
2290
	box_coords(box, &x, &y);
2291
 
2292
	content_request_redraw(h, x, y,
2293
			box->padding[LEFT] + box->width + box->padding[RIGHT],
2294
			box->padding[TOP] + box->height + box->padding[BOTTOM]);
2295
}
2296
 
2297
 
2298
/**
2299
 * Redraw a box.
2300
 *
2301
 * \param  h	content containing the box, of type CONTENT_HTML
2302
 * \param  box  box to redraw
2303
 */
2304
 
2305
void html__redraw_a_box(struct html_content *html, struct box *box)
2306
{
2307
	int x, y;
2308
 
2309
	box_coords(box, &x, &y);
2310
 
2311
	content__request_redraw((struct content *)html, x, y,
2312
			box->padding[LEFT] + box->width + box->padding[RIGHT],
2313
			box->padding[TOP] + box->height + box->padding[BOTTOM]);
2314
}
2315
 
2316
static void html_destroy_frameset(struct content_html_frames *frameset)
2317
{
2318
	int i;
2319
 
2320
	if (frameset->name) {
2321
		talloc_free(frameset->name);
2322
		frameset->name = NULL;
2323
	}
2324
	if (frameset->url) {
2325
		talloc_free(frameset->url);
2326
		frameset->url = NULL;
2327
	}
2328
	if (frameset->children) {
2329
		for (i = 0; i < (frameset->rows * frameset->cols); i++) {
2330
			if (frameset->children[i].name) {
2331
				talloc_free(frameset->children[i].name);
2332
				frameset->children[i].name = NULL;
2333
			}
2334
			if (frameset->children[i].url) {
2335
				nsurl_unref(frameset->children[i].url);
2336
				frameset->children[i].url = NULL;
2337
			}
2338
		  	if (frameset->children[i].children)
2339
		  		html_destroy_frameset(&frameset->children[i]);
2340
		}
2341
		talloc_free(frameset->children);
2342
		frameset->children = NULL;
2343
	}
2344
}
2345
 
2346
static void html_destroy_iframe(struct content_html_iframe *iframe)
2347
{
2348
	struct content_html_iframe *next;
2349
	next = iframe;
2350
	while ((iframe = next) != NULL) {
2351
		next = iframe->next;
2352
		if (iframe->name)
2353
			talloc_free(iframe->name);
2354
		if (iframe->url) {
2355
			nsurl_unref(iframe->url);
2356
			iframe->url = NULL;
2357
		}
2358
		talloc_free(iframe);
2359
	}
2360
}
2361
 
2362
 
2363
static void html_free_layout(html_content *htmlc)
2364
{
2365
	if (htmlc->bctx != NULL) {
2366
		/* freeing talloc context should let the entire box
2367
		 * set be destroyed
2368
		 */
2369
		talloc_free(htmlc->bctx);
2370
	}
2371
}
2372
 
2373
/**
2374
 * Destroy a CONTENT_HTML and free all resources it owns.
2375
 */
2376
 
2377
static void html_destroy(struct content *c)
2378
{
2379
	html_content *html = (html_content *) c;
2380
	unsigned int i;
2381
	struct form *f, *g;
2382
 
2383
	LOG(("content %p", c));
2384
 
2385
	/* Destroy forms */
2386
	for (f = html->forms; f != NULL; f = g) {
2387
		g = f->prev;
2388
 
2389
		form_free(f);
2390
	}
2391
 
2392
	imagemap_destroy(html);
2393
 
2394
	if (c->refresh)
2395
		nsurl_unref(c->refresh);
2396
 
2397
	if (html->base_url)
2398
		nsurl_unref(html->base_url);
2399
 
2400
	if (html->parser != NULL) {
2401
		dom_hubbub_parser_destroy(html->parser);
2402
		html->parser = NULL;
2403
	}
2404
 
2405
	if (html->document != NULL) {
2406
		dom_node_unref(html->document);
2407
	}
2408
 
2409
	/* Free base target */
2410
	if (html->base_target != NULL) {
2411
	 	free(html->base_target);
2412
	 	html->base_target = NULL;
2413
	}
2414
 
2415
	/* Free frameset */
2416
	if (html->frameset != NULL) {
2417
		html_destroy_frameset(html->frameset);
2418
		talloc_free(html->frameset);
2419
		html->frameset = NULL;
2420
	}
2421
 
2422
	/* Free iframes */
2423
	if (html->iframe != NULL) {
2424
		html_destroy_iframe(html->iframe);
2425
		html->iframe = NULL;
2426
	}
2427
 
2428
	/* Destroy selection context */
2429
	if (html->select_ctx != NULL) {
2430
		css_select_ctx_destroy(html->select_ctx);
2431
		html->select_ctx = NULL;
2432
	}
2433
 
2434
	if (html->universal != NULL) {
2435
		lwc_string_unref(html->universal);
2436
		html->universal = NULL;
2437
	}
2438
 
2439
	/* Free stylesheets */
2440
	for (i = 0; i != html->stylesheet_count; i++) {
2441
		if (html->stylesheets[i].type == HTML_STYLESHEET_EXTERNAL &&
2442
				html->stylesheets[i].data.external != NULL) {
2443
			hlcache_handle_release(
2444
					html->stylesheets[i].data.external);
2445
		} else if (html->stylesheets[i].type ==
2446
				HTML_STYLESHEET_INTERNAL &&
2447
				html->stylesheets[i].data.internal != NULL) {
2448
			nscss_destroy_css_data(
2449
					html->stylesheets[i].data.internal);
2450
		}
2451
	}
2452
	free(html->stylesheets);
2453
 
2454
	/* Free scripts */
2455
	html_free_scripts(html);
2456
 
2457
	/* Free objects */
2458
	html_destroy_objects(html);
2459
 
2460
	/* free layout */
2461
	html_free_layout(html);
2462
}
2463
 
2464
 
2465
static nserror html_clone(const struct content *old, struct content **newc)
2466
{
2467
	/** \todo Clone HTML specifics */
2468
 
2469
	/* In the meantime, we should never be called, as HTML contents
2470
	 * cannot be shared and we're not intending to fix printing's
2471
	 * cloning of documents. */
2472
	assert(0 && "html_clone should never be called");
2473
 
2474
	return true;
2475
}
2476
 
2477
/**
2478
 * Set the content status.
2479
 */
2480
 
2481
void html_set_status(html_content *c, const char *extra)
2482
{
2483
	content_set_status(&c->base, extra);
2484
}
2485
 
2486
 
2487
/**
2488
 * Handle a window containing a CONTENT_HTML being opened.
2489
 */
2490
 
2491
static void
2492
html_open(struct content *c,
2493
	  struct browser_window *bw,
2494
	  struct content *page,
2495
	  struct object_params *params)
2496
{
2497
	html_content *html = (html_content *) c;
2498
	struct content_html_object *object, *next;
2499
 
2500
	html->bw = bw;
2501
	html->page = (html_content *) page;
2502
 
2503
	/* text selection */
2504
	selection_init(&html->sel, html->layout);
2505
 
2506
	for (object = html->object_list; object != NULL; object = next) {
2507
		next = object->next;
2508
 
2509
		if (object->content == NULL)
2510
			continue;
2511
 
2512
		if (content_get_type(object->content) == CONTENT_NONE)
2513
			continue;
2514
 
2515
		content_open(object->content,
2516
				bw, c,
2517
				object->box->object_params);
2518
	}
2519
}
2520
 
2521
 
2522
/**
2523
 * Handle a window containing a CONTENT_HTML being closed.
2524
 */
2525
 
2526
static void html_close(struct content *c)
2527
{
2528
	html_content *html = (html_content *) c;
2529
	struct content_html_object *object, *next;
2530
 
2531
	if (html->search != NULL)
2532
		search_destroy_context(html->search);
2533
 
2534
	html->bw = NULL;
2535
 
2536
	for (object = html->object_list; object != NULL; object = next) {
2537
		next = object->next;
2538
 
2539
		if (object->content == NULL)
2540
			continue;
2541
 
2542
		if (content_get_type(object->content) == CONTENT_NONE)
2543
			continue;
2544
 
2545
		if (content_get_type(object->content) == CONTENT_HTML)
2546
			schedule_remove(html_object_refresh, object);
2547
 
2548
		content_close(object->content);
2549
	}
2550
}
2551
 
2552
 
2553
/**
2554
 * Return an HTML content's selection context
2555
 */
2556
 
2557
static struct selection *html_get_selection(struct content *c)
2558
{
2559
	html_content *html = (html_content *) c;
2560
 
2561
	return &html->sel;
2562
}
2563
 
2564
 
2565
/**
2566
 * Get access to any content, link URLs and objects (images) currently
2567
 * at the given (x, y) coordinates.
2568
 *
2569
 * \param c	html content to look inside
2570
 * \param x	x-coordinate of point of interest
2571
 * \param y	y-coordinate of point of interest
2572
 * \param data	pointer to contextual_content struct.  Its fields are updated
2573
 *		with pointers to any relevent content, or set to NULL if none.
2574
 */
2575
static void
2576
html_get_contextual_content(struct content *c,
2577
			    int x,
2578
			    int y,
2579
			    struct contextual_content *data)
2580
{
2581
	html_content *html = (html_content *) c;
2582
 
2583
	struct box *box = html->layout;
2584
	struct box *next;
2585
	int box_x = 0, box_y = 0;
2586
 
2587
	while ((next = box_at_point(box, x, y, &box_x, &box_y)) != NULL) {
2588
		box = next;
2589
 
2590
		if (box->style && css_computed_visibility(box->style) ==
2591
				CSS_VISIBILITY_HIDDEN)
2592
			continue;
2593
 
2594
		if (box->iframe)
2595
			browser_window_get_contextual_content(box->iframe,
2596
					x - box_x, y - box_y, data);
2597
 
2598
		if (box->object)
2599
			content_get_contextual_content(box->object,
2600
					x - box_x, y - box_y, data);
2601
 
2602
		if (box->object)
2603
			data->object = box->object;
2604
 
2605
		if (box->href)
2606
			data->link_url = nsurl_access(box->href);
2607
 
2608
		if (box->usemap) {
2609
			const char *target = NULL;
2610
			nsurl *url = imagemap_get(html, box->usemap, box_x,
2611
					box_y, x, y, &target);
2612
			/* Box might have imagemap, but no actual link area
2613
			 * at point */
2614
			if (url != NULL)
2615
				data->link_url = nsurl_access(url);
2616
		}
2617
		if (box->gadget) {
2618
			switch (box->gadget->type) {
2619
			case GADGET_TEXTBOX:
2620
			case GADGET_TEXTAREA:
2621
			case GADGET_PASSWORD:
2622
				data->form_features = CTX_FORM_TEXT;
2623
				break;
2624
 
2625
			case GADGET_FILE:
2626
				data->form_features = CTX_FORM_FILE;
2627
				break;
2628
 
2629
			default:
2630
				data->form_features = CTX_FORM_NONE;
2631
				break;
2632
			}
2633
		}
2634
	}
2635
}
2636
 
2637
 
2638
/**
2639
 * Scroll deepest thing within the content which can be scrolled at given point
2640
 *
2641
 * \param c	html content to look inside
2642
 * \param x	x-coordinate of point of interest
2643
 * \param y	y-coordinate of point of interest
2644
 * \param scrx	x-coordinate of point of interest
2645
 * \param scry	y-coordinate of point of interest
2646
 * \return true iff scroll was consumed by something in the content
2647
 */
2648
static bool
2649
html_scroll_at_point(struct content *c, int x, int y, int scrx, int scry)
2650
{
2651
	html_content *html = (html_content *) c;
2652
 
2653
	struct box *box = html->layout;
2654
	struct box *next;
2655
	int box_x = 0, box_y = 0;
2656
	bool handled_scroll = false;
2657
 
2658
	/* TODO: invert order; visit deepest box first */
2659
 
2660
	while ((next = box_at_point(box, x, y, &box_x, &box_y)) != NULL) {
2661
		box = next;
2662
 
2663
		if (box->style && css_computed_visibility(box->style) ==
2664
				CSS_VISIBILITY_HIDDEN)
2665
			continue;
2666
 
2667
		/* Pass into iframe */
2668
		if (box->iframe && browser_window_scroll_at_point(box->iframe,
2669
				x - box_x, y - box_y, scrx, scry) == true)
2670
			return true;
2671
 
2672
		/* Pass into object */
2673
		if (box->object != NULL && content_scroll_at_point(
2674
				box->object, x - box_x, y - box_y,
2675
				scrx, scry) == true)
2676
			return true;
2677
 
2678
		/* Handle box scrollbars */
2679
		if (box->scroll_y && scrollbar_scroll(box->scroll_y, scry))
2680
			handled_scroll = true;
2681
 
2682
		if (box->scroll_x && scrollbar_scroll(box->scroll_x, scrx))
2683
			handled_scroll = true;
2684
 
2685
		if (handled_scroll == true)
2686
			return true;
2687
	}
2688
 
2689
	return false;
2690
}
2691
 
2692
 
2693
/**
2694
 * Drop a file onto a content at a particular point, or determine if a file
2695
 * may be dropped onto the content at given point.
2696
 *
2697
 * \param c	html content to look inside
2698
 * \param x	x-coordinate of point of interest
2699
 * \param y	y-coordinate of point of interest
2700
 * \param file	path to file to be dropped, or NULL to know if drop allowed
2701
 * \return true iff file drop has been handled, or if drop possible (NULL file)
2702
 */
2703
static bool html_drop_file_at_point(struct content *c, int x, int y, char *file)
2704
{
2705
	html_content *html = (html_content *) c;
2706
 
2707
	struct box *box = html->layout;
2708
	struct box *next;
2709
	struct box *file_box = NULL;
2710
	struct box *text_box = NULL;
2711
	int box_x = 0, box_y = 0;
2712
 
2713
	/* Scan box tree for boxes that can handle drop */
2714
	while ((next = box_at_point(box, x, y, &box_x, &box_y)) != NULL) {
2715
		box = next;
2716
 
2717
		if (box->style && css_computed_visibility(box->style) ==
2718
				CSS_VISIBILITY_HIDDEN)
2719
			continue;
2720
 
2721
		if (box->iframe)
2722
			return browser_window_drop_file_at_point(box->iframe,
2723
					x - box_x, y - box_y, file);
2724
 
2725
		if (box->object && content_drop_file_at_point(box->object,
2726
					x - box_x, y - box_y, file) == true)
2727
			return true;
2728
 
2729
		if (box->gadget) {
2730
			switch (box->gadget->type) {
2731
				case GADGET_FILE:
2732
					file_box = box;
2733
				break;
2734
 
2735
				case GADGET_TEXTBOX:
2736
				case GADGET_TEXTAREA:
2737
				case GADGET_PASSWORD:
2738
					text_box = box;
2739
					break;
2740
 
2741
				default:	/* appease compiler */
2742
					break;
2743
			}
2744
		}
2745
	}
2746
 
2747
	if (!file_box && !text_box)
2748
		/* No box capable of handling drop */
2749
		return false;
2750
 
2751
	if (file == NULL)
2752
		/* There is a box capable of handling drop here */
2753
		return true;
2754
 
2755
	/* Handle the drop */
2756
	if (file_box) {
2757
		/* File dropped on file input */
2758
		utf8_convert_ret ret;
2759
		char *utf8_fn;
2760
 
2761
		ret = utf8_from_local_encoding(file, 0,
2762
				&utf8_fn);
2763
		if (ret != UTF8_CONVERT_OK) {
2764
			/* A bad encoding should never happen */
2765
			assert(ret != UTF8_CONVERT_BADENC);
2766
			LOG(("utf8_from_local_encoding failed"));
2767
			/* Load was for us - just no memory */
2768
			return true;
2769
		}
2770
 
2771
		/* Found: update form input */
2772
		free(file_box->gadget->value);
2773
		file_box->gadget->value = utf8_fn;
2774
 
2775
		/* Redraw box. */
2776
		html__redraw_a_box(html, file_box);
2777
 
2778
	} else if (html->bw != NULL) {
2779
		/* File dropped on text input */
2780
 
2781
		size_t file_len;
2782
		FILE *fp = NULL;
2783
		char *buffer;
2784
		char *utf8_buff;
2785
		utf8_convert_ret ret;
2786
		unsigned int size;
2787
		struct browser_window *bw;
2788
 
2789
		/* Open file */
2790
		fp = fopen(file, "rb");
2791
		if (fp == NULL) {
2792
			/* Couldn't open file, but drop was for us */
2793
			return true;
2794
		}
2795
 
2796
		/* Get filesize */
2797
		fseek(fp, 0, SEEK_END);
2798
		file_len = ftell(fp);
2799
		fseek(fp, 0, SEEK_SET);
2800
 
2801
		/* Allocate buffer for file data */
2802
		buffer = malloc(file_len + 1);
2803
		if (buffer == NULL) {
2804
			/* No memory, but drop was for us */
2805
			fclose(fp);
2806
			return true;
2807
		}
2808
 
2809
		/* Stick file into buffer */
2810
		if (file_len != fread(buffer, 1, file_len, fp)) {
2811
			/* Failed, but drop was for us */
2812
			free(buffer);
2813
			fclose(fp);
2814
			return true;
2815
		}
2816
 
2817
		/* Done with file */
2818
		fclose(fp);
2819
 
2820
		/* Ensure buffer's string termination */
2821
		buffer[file_len] = '\0';
2822
 
2823
		/* TODO: Sniff for text? */
2824
 
2825
		/* Convert to UTF-8 */
2826
		ret = utf8_from_local_encoding(buffer, file_len, &utf8_buff);
2827
		if (ret != UTF8_CONVERT_OK) {
2828
			/* bad encoding shouldn't happen */
2829
			assert(ret != UTF8_CONVERT_BADENC);
2830
			LOG(("utf8_from_local_encoding failed"));
2831
			free(buffer);
2832
			warn_user("NoMemory", NULL);
2833
			return true;
2834
		}
2835
 
2836
		/* Done with buffer */
2837
		free(buffer);
2838
 
2839
		/* Get new length */
2840
		size = strlen(utf8_buff);
2841
 
2842
		/* Simulate a click over the input box, to place caret */
2843
		browser_window_mouse_click(html->bw,
2844
				BROWSER_MOUSE_PRESS_1, x, y);
2845
 
2846
		bw = browser_window_get_root(html->bw);
2847
 
2848
		/* Paste the file as text */
2849
		browser_window_paste_text(bw, utf8_buff, size, true);
2850
 
2851
		free(utf8_buff);
2852
	}
2853
 
2854
	return true;
2855
}
2856
 
2857
 
2858
/**
2859
 * Dump debug info concerning the html_content
2860
 *
2861
 * \param  bw    The browser window
2862
 * \param  bw    The file to dump to
2863
 */
2864
static void html_debug_dump(struct content *c, FILE *f)
2865
{
2866
	html_content *html = (html_content *) c;
2867
 
2868
	assert(html != NULL);
2869
	assert(html->layout != NULL);
2870
 
2871
	box_dump(f, html->layout, 0);
2872
}
2873
 
2874
 
2875
/**
2876
 * Set an HTML content's search context
2877
 *
2878
 * \param c	content of type html
2879
 * \param s	search context, or NULL if none
2880
 */
2881
 
2882
void html_set_search(struct content *c, struct search_context *s)
2883
{
2884
	html_content *html = (html_content *) c;
2885
 
2886
	html->search = s;
2887
}
2888
 
2889
 
2890
/**
2891
 * Return an HTML content's search context
2892
 *
2893
 * \param c	content of type html
2894
 * \return content's search context, or NULL if none
2895
 */
2896
 
2897
struct search_context *html_get_search(struct content *c)
2898
{
2899
	html_content *html = (html_content *) c;
2900
 
2901
	return html->search;
2902
}
2903
 
2904
 
2905
#if ALWAYS_DUMP_FRAMESET
2906
/**
2907
 * Print a frameset tree to stderr.
2908
 */
2909
 
2910
static void
2911
html_dump_frameset(struct content_html_frames *frame, unsigned int depth)
2912
{
2913
	unsigned int i;
2914
	int row, col, index;
2915
	const char *unit[] = {"px", "%", "*"};
2916
	const char *scrolling[] = {"auto", "yes", "no"};
2917
 
2918
	assert(frame);
2919
 
2920
	fprintf(stderr, "%p ", frame);
2921
 
2922
	fprintf(stderr, "(%i %i) ", frame->rows, frame->cols);
2923
 
2924
	fprintf(stderr, "w%g%s ", frame->width.value, unit[frame->width.unit]);
2925
	fprintf(stderr, "h%g%s ", frame->height.value,unit[frame->height.unit]);
2926
	fprintf(stderr, "(margin w%i h%i) ",
2927
			frame->margin_width, frame->margin_height);
2928
 
2929
	if (frame->name)
2930
		fprintf(stderr, "'%s' ", frame->name);
2931
	if (frame->url)
2932
		fprintf(stderr, "<%s> ", frame->url);
2933
 
2934
	if (frame->no_resize)
2935
		fprintf(stderr, "noresize ");
2936
	fprintf(stderr, "(scrolling %s) ", scrolling[frame->scrolling]);
2937
	if (frame->border)
2938
		fprintf(stderr, "border %x ",
2939
				(unsigned int) frame->border_colour);
2940
 
2941
	fprintf(stderr, "\n");
2942
 
2943
	if (frame->children) {
2944
		for (row = 0; row != frame->rows; row++) {
2945
			for (col = 0; col != frame->cols; col++) {
2946
				for (i = 0; i != depth; i++)
2947
					fprintf(stderr, "  ");
2948
				fprintf(stderr, "(%i %i): ", row, col);
2949
				index = (row * frame->cols) + col;
2950
				html_dump_frameset(&frame->children[index],
2951
						depth + 1);
2952
			}
2953
		}
2954
	}
2955
}
2956
 
2957
#endif
2958
 
2959
/**
2960
 * Retrieve HTML document tree
2961
 *
2962
 * \param h  HTML content to retrieve document tree from
2963
 * \return Pointer to document tree
2964
 */
2965
dom_document *html_get_document(hlcache_handle *h)
2966
{
2967
	html_content *c = (html_content *) hlcache_handle_get_content(h);
2968
 
2969
	assert(c != NULL);
2970
 
2971
	return c->document;
2972
}
2973
 
2974
/**
2975
 * Retrieve box tree
2976
 *
2977
 * \param h  HTML content to retrieve tree from
2978
 * \return Pointer to box tree
2979
 *
2980
 * \todo This API must die, as must all use of the box tree outside render/
2981
 */
2982
struct box *html_get_box_tree(hlcache_handle *h)
2983
{
2984
	html_content *c = (html_content *) hlcache_handle_get_content(h);
2985
 
2986
	assert(c != NULL);
2987
 
2988
	return c->layout;
2989
}
2990
 
2991
/**
2992
 * Retrieve the charset of an HTML document
2993
 *
2994
 * \param h  Content to retrieve charset from
2995
 * \return Pointer to charset, or NULL
2996
 */
2997
const char *html_get_encoding(hlcache_handle *h)
2998
{
2999
	html_content *c = (html_content *) hlcache_handle_get_content(h);
3000
 
3001
	assert(c != NULL);
3002
 
3003
	return c->encoding;
3004
}
3005
 
3006
/**
3007
 * Retrieve the charset of an HTML document
3008
 *
3009
 * \param h  Content to retrieve charset from
3010
 * \return Pointer to charset, or NULL
3011
 */
3012
dom_hubbub_encoding_source html_get_encoding_source(hlcache_handle *h)
3013
{
3014
	html_content *c = (html_content *) hlcache_handle_get_content(h);
3015
 
3016
	assert(c != NULL);
3017
 
3018
	return c->encoding_source;
3019
}
3020
 
3021
/**
3022
 * Retrieve framesets used in an HTML document
3023
 *
3024
 * \param h  Content to inspect
3025
 * \return Pointer to framesets, or NULL if none
3026
 */
3027
struct content_html_frames *html_get_frameset(hlcache_handle *h)
3028
{
3029
	html_content *c = (html_content *) hlcache_handle_get_content(h);
3030
 
3031
	assert(c != NULL);
3032
 
3033
	return c->frameset;
3034
}
3035
 
3036
/**
3037
 * Retrieve iframes used in an HTML document
3038
 *
3039
 * \param h  Content to inspect
3040
 * \return Pointer to iframes, or NULL if none
3041
 */
3042
struct content_html_iframe *html_get_iframe(hlcache_handle *h)
3043
{
3044
	html_content *c = (html_content *) hlcache_handle_get_content(h);
3045
 
3046
	assert(c != NULL);
3047
 
3048
	return c->iframe;
3049
}
3050
 
3051
/**
3052
 * Retrieve an HTML content's base URL
3053
 *
3054
 * \param h  Content to retrieve base target from
3055
 * \return Pointer to URL
3056
 */
3057
nsurl *html_get_base_url(hlcache_handle *h)
3058
{
3059
	html_content *c = (html_content *) hlcache_handle_get_content(h);
3060
 
3061
	assert(c != NULL);
3062
 
3063
	return c->base_url;
3064
}
3065
 
3066
/**
3067
 * Retrieve an HTML content's base target
3068
 *
3069
 * \param h  Content to retrieve base target from
3070
 * \return Pointer to target, or NULL if none
3071
 */
3072
const char *html_get_base_target(hlcache_handle *h)
3073
{
3074
	html_content *c = (html_content *) hlcache_handle_get_content(h);
3075
 
3076
	assert(c != NULL);
3077
 
3078
	return c->base_target;
3079
}
3080
 
3081
/**
3082
 * Retrieve stylesheets used by HTML document
3083
 *
3084
 * \param h  Content to retrieve stylesheets from
3085
 * \param n  Pointer to location to receive number of sheets
3086
 * \return Pointer to array of stylesheets
3087
 */
3088
struct html_stylesheet *html_get_stylesheets(hlcache_handle *h, unsigned int *n)
3089
{
3090
	html_content *c = (html_content *) hlcache_handle_get_content(h);
3091
 
3092
	assert(c != NULL);
3093
	assert(n != NULL);
3094
 
3095
	*n = c->stylesheet_count;
3096
 
3097
	return c->stylesheets;
3098
}
3099
 
3100
/**
3101
 * Retrieve objects used by HTML document
3102
 *
3103
 * \param h  Content to retrieve objects from
3104
 * \param n  Pointer to location to receive number of objects
3105
 * \return Pointer to list of objects
3106
 */
3107
struct content_html_object *html_get_objects(hlcache_handle *h, unsigned int *n)
3108
{
3109
	html_content *c = (html_content *) hlcache_handle_get_content(h);
3110
 
3111
	assert(c != NULL);
3112
	assert(n != NULL);
3113
 
3114
	*n = c->num_objects;
3115
 
3116
	return c->object_list;
3117
}
3118
 
3119
/**
3120
 * Retrieve layout coordinates of box with given id
3121
 *
3122
 * \param h        HTML document to search
3123
 * \param frag_id  String containing an element id
3124
 * \param x        Updated to global x coord iff id found
3125
 * \param y        Updated to global y coord iff id found
3126
 * \return  true iff id found
3127
 */
3128
bool html_get_id_offset(hlcache_handle *h, lwc_string *frag_id, int *x, int *y)
3129
{
3130
	struct box *pos;
3131
	struct box *layout;
3132
 
3133
	if (content_get_type(h) != CONTENT_HTML)
3134
		return false;
3135
 
3136
	layout = html_get_box_tree(h);
3137
 
3138
	if ((pos = box_find_by_id(layout, frag_id)) != 0) {
3139
		box_coords(pos, x, y);
3140
		return true;
3141
	}
3142
	return false;
3143
}
3144
 
3145
/**
3146
 * Compute the type of a content
3147
 *
3148
 * \return CONTENT_HTML
3149
 */
3150
static content_type html_content_type(void)
3151
{
3152
	return CONTENT_HTML;
3153
}
3154
 
3155
 
3156
static void html_fini(void)
3157
{
3158
	box_construct_fini();
3159
 
3160
	if (html_user_stylesheet_url != NULL) {
3161
		nsurl_unref(html_user_stylesheet_url);
3162
		html_user_stylesheet_url = NULL;
3163
	}
3164
 
3165
	if (html_quirks_stylesheet_url != NULL) {
3166
		nsurl_unref(html_quirks_stylesheet_url);
3167
		html_quirks_stylesheet_url = NULL;
3168
	}
3169
 
3170
	if (html_adblock_stylesheet_url != NULL) {
3171
		nsurl_unref(html_adblock_stylesheet_url);
3172
		html_adblock_stylesheet_url = NULL;
3173
	}
3174
 
3175
	if (html_default_stylesheet_url != NULL) {
3176
		nsurl_unref(html_default_stylesheet_url);
3177
		html_default_stylesheet_url = NULL;
3178
	}
3179
 
3180
	if (html_charset != NULL) {
3181
		lwc_string_unref(html_charset);
3182
		html_charset = NULL;
3183
	}
3184
}
3185
 
3186
static const content_handler html_content_handler = {
3187
	.fini = html_fini,
3188
	.create = html_create,
3189
	.process_data = html_process_data,
3190
	.data_complete = html_convert,
3191
	.reformat = html_reformat,
3192
	.destroy = html_destroy,
3193
	.stop = html_stop,
3194
	.mouse_track = html_mouse_track,
3195
	.mouse_action = html_mouse_action,
3196
	.redraw = html_redraw,
3197
	.open = html_open,
3198
	.close = html_close,
3199
	.get_selection = html_get_selection,
3200
	.get_contextual_content = html_get_contextual_content,
3201
	.scroll_at_point = html_scroll_at_point,
3202
	.drop_file_at_point = html_drop_file_at_point,
3203
	.debug_dump = html_debug_dump,
3204
	.clone = html_clone,
3205
	.type = html_content_type,
3206
	.no_share = true,
3207
};
3208
 
3209
nserror html_init(void)
3210
{
3211
	uint32_t i;
3212
	lwc_error lerror;
3213
	nserror error;
3214
 
3215
	lerror = lwc_intern_string("charset", SLEN("charset"), &html_charset);
3216
	if (lerror != lwc_error_ok) {
3217
		error = NSERROR_NOMEM;
3218
		goto error;
3219
	}
3220
 
3221
	error = nsurl_create("resource:default.css",
3222
			&html_default_stylesheet_url);
3223
	if (error != NSERROR_OK)
3224
		goto error;
3225
 
3226
	error = nsurl_create("resource:adblock.css",
3227
			&html_adblock_stylesheet_url);
3228
	if (error != NSERROR_OK)
3229
		goto error;
3230
 
3231
	error = nsurl_create("resource:quirks.css",
3232
			&html_quirks_stylesheet_url);
3233
	if (error != NSERROR_OK)
3234
		goto error;
3235
 
3236
	error = nsurl_create("resource:user.css",
3237
			&html_user_stylesheet_url);
3238
	if (error != NSERROR_OK)
3239
		goto error;
3240
 
3241
	error = box_construct_init();
3242
	if (error != NSERROR_OK)
3243
		goto error;
3244
 
3245
	for (i = 0; i < NOF_ELEMENTS(html_types); i++) {
3246
		error = content_factory_register_handler(html_types[i],
3247
				&html_content_handler);
3248
		if (error != NSERROR_OK)
3249
			goto error;
3250
	}
3251
 
3252
	return NSERROR_OK;
3253
 
3254
error:
3255
	html_fini();
3256
 
3257
	return error;
3258
}
3259
 
3260
/**
3261
 * Get the browser window containing an HTML content
3262
 *
3263
 * \param  c	HTML content
3264
 * \return the browser window
3265
 */
3266
struct browser_window *html_get_browser_window(struct content *c)
3267
{
3268
	html_content *html = (html_content *) c;
3269
 
3270
	assert(c != NULL);
3271
	assert(c->handler == &html_content_handler);
3272
 
3273
	return html->bw;
3274
}