1 /*
2  * Copyright 2005-2007 James Bursa <bursa@users.sourceforge.net>
3  *
4  * This file is part of NetSurf, http://www.netsurf-browser.org/
5  *
6  * NetSurf is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; version 2 of the License.
9  *
10  * NetSurf is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
17  */
18 
19 /**
20  * \file
21  * Content handling implementation.
22  */
23 
24 #include <stdint.h>
25 #include <stdlib.h>
26 #include <nsutils/time.h>
27 
28 #include "netsurf/inttypes.h"
29 #include "utils/log.h"
30 #include "utils/messages.h"
31 #include "utils/corestrings.h"
32 #include "netsurf/browser_window.h"
33 #include "netsurf/bitmap.h"
34 #include "netsurf/content.h"
35 #include "desktop/knockout.h"
36 
37 #include "content/content_protected.h"
38 #include "content/textsearch.h"
39 #include "content/content_debug.h"
40 #include "content/hlcache.h"
41 #include "content/urldb.h"
42 
43 #define URL_FMT_SPC "%.140s"
44 
45 const char * const content_status_name[] = {
46 	"LOADING",
47 	"READY",
48 	"DONE",
49 	"ERROR"
50 };
51 
52 
53 /**
54  * All data has arrived, convert for display.
55  *
56  * Calls the convert function for the content.
57  *
58  * - If the conversion succeeds, but there is still some processing required
59  *   (eg. loading images), the content gets status CONTENT_STATUS_READY, and a
60  *   CONTENT_MSG_READY is sent to all users.
61  * - If the conversion succeeds and is complete, the content gets status
62  *   CONTENT_STATUS_DONE, and CONTENT_MSG_READY then CONTENT_MSG_DONE are sent.
63  * - If the conversion fails, CONTENT_MSG_ERROR is sent. The content will soon
64  *   be destroyed and must no longer be used.
65  */
content_convert(struct content * c)66 static void content_convert(struct content *c)
67 {
68 	assert(c);
69 	assert(c->status == CONTENT_STATUS_LOADING ||
70 	       c->status == CONTENT_STATUS_ERROR);
71 
72 	if (c->status != CONTENT_STATUS_LOADING)
73 		return;
74 
75 	if (c->locked == true)
76 		return;
77 
78 	NSLOG(netsurf, INFO, "content "URL_FMT_SPC" (%p)",
79 	      nsurl_access_log(llcache_handle_get_url(c->llcache)), c);
80 
81 	if (c->handler->data_complete != NULL) {
82 		c->locked = true;
83 		if (c->handler->data_complete(c) == false) {
84 			content_set_error(c);
85 		}
86 		/* Conversion to the READY state will unlock the content */
87 	} else {
88 		content_set_ready(c);
89 		content_set_done(c);
90 	}
91 }
92 
93 
94 /**
95  * Handler for low-level cache events
96  *
97  * \param llcache  Low-level cache handle
98  * \param event	   Event details
99  * \param pw	   Pointer to our context
100  * \return NSERROR_OK on success, appropriate error otherwise
101  */
102 static nserror
content_llcache_callback(llcache_handle * llcache,const llcache_event * event,void * pw)103 content_llcache_callback(llcache_handle *llcache,
104 			 const llcache_event *event, void *pw)
105 {
106 	struct content *c = pw;
107 	union content_msg_data msg_data;
108 	nserror error = NSERROR_OK;
109 
110 	switch (event->type) {
111 	case LLCACHE_EVENT_GOT_CERTS:
112 		/* Will never happen: handled in hlcache */
113 		break;
114 	case LLCACHE_EVENT_HAD_HEADERS:
115 		/* Will never happen: handled in hlcache */
116 		break;
117 	case LLCACHE_EVENT_HAD_DATA:
118 		if (c->handler->process_data != NULL) {
119 			if (c->handler->process_data(c,
120 						     (const char *) event->data.data.buf,
121 						     event->data.data.len) == false) {
122 				llcache_handle_abort(c->llcache);
123 				c->status = CONTENT_STATUS_ERROR;
124 				/** \todo It's not clear what error this is */
125 				error = NSERROR_NOMEM;
126 			}
127 		}
128 		break;
129 	case LLCACHE_EVENT_DONE:
130 		{
131 			size_t source_size;
132 
133 			(void) llcache_handle_get_source_data(llcache, &source_size);
134 
135 			content_set_status(c, messages_get("Processing"));
136 			msg_data.explicit_status_text = NULL;
137 			content_broadcast(c, CONTENT_MSG_STATUS, &msg_data);
138 
139 			content_convert(c);
140 		}
141 		break;
142 	case LLCACHE_EVENT_ERROR:
143 		/** \todo Error page? */
144 		c->status = CONTENT_STATUS_ERROR;
145 		msg_data.errordata.errorcode = event->data.error.code;
146 		msg_data.errordata.errormsg = event->data.error.msg;
147 		content_broadcast(c, CONTENT_MSG_ERROR, &msg_data);
148 		break;
149 	case LLCACHE_EVENT_PROGRESS:
150 		content_set_status(c, event->data.progress_msg);
151 		msg_data.explicit_status_text = NULL;
152 		content_broadcast(c, CONTENT_MSG_STATUS, &msg_data);
153 		break;
154 	case LLCACHE_EVENT_REDIRECT:
155 		msg_data.redirect.from = event->data.redirect.from;
156 		msg_data.redirect.to = event->data.redirect.to;
157 		content_broadcast(c, CONTENT_MSG_REDIRECT, &msg_data);
158 		break;
159 	}
160 
161 	return error;
162 }
163 
164 
165 /**
166  * update content status message
167  *
168  * \param c the content to update.
169  */
content_update_status(struct content * c)170 static void content_update_status(struct content *c)
171 {
172 	if (c->status == CONTENT_STATUS_LOADING ||
173 	    c->status == CONTENT_STATUS_READY) {
174 		/* Not done yet */
175 		snprintf(c->status_message, sizeof (c->status_message),
176 			 "%s%s%s", messages_get("Fetching"),
177 			 c->sub_status[0] != '\0' ? ", " : " ",
178 			 c->sub_status);
179 	} else {
180 		snprintf(c->status_message, sizeof (c->status_message),
181 			 "%s (%.1fs)", messages_get("Done"),
182 			 (float) c->time / 1000);
183 	}
184 }
185 
186 
187 /* exported interface documented in content/protected.h */
188 nserror
content__init(struct content * c,const content_handler * handler,lwc_string * imime_type,const struct http_parameter * params,llcache_handle * llcache,const char * fallback_charset,bool quirks)189 content__init(struct content *c,
190 	      const content_handler *handler,
191 	      lwc_string *imime_type,
192 	      const struct http_parameter *params,
193 	      llcache_handle *llcache,
194 	      const char *fallback_charset,
195 	      bool quirks)
196 {
197 	struct content_user *user_sentinel;
198 	nserror error;
199 
200 	NSLOG(netsurf, INFO, "url "URL_FMT_SPC" -> %p",
201 	      nsurl_access_log(llcache_handle_get_url(llcache)), c);
202 
203 	user_sentinel = calloc(1, sizeof(struct content_user));
204 	if (user_sentinel == NULL) {
205 		return NSERROR_NOMEM;
206 	}
207 
208 	if (fallback_charset != NULL) {
209 		c->fallback_charset = strdup(fallback_charset);
210 		if (c->fallback_charset == NULL) {
211 			free(user_sentinel);
212 			return NSERROR_NOMEM;
213 		}
214 	}
215 
216 	c->llcache = llcache;
217 	c->mime_type = lwc_string_ref(imime_type);
218 	c->handler = handler;
219 	c->status = CONTENT_STATUS_LOADING;
220 	c->width = 0;
221 	c->height = 0;
222 	c->available_width = 0;
223 	c->available_height = 0;
224 	c->quirks = quirks;
225 	c->refresh = 0;
226 	nsu_getmonotonic_ms(&c->time);
227 	c->size = 0;
228 	c->title = NULL;
229 	c->active = 0;
230 	user_sentinel->callback = NULL;
231 	user_sentinel->pw = NULL;
232 	user_sentinel->next = NULL;
233 	c->user_list = user_sentinel;
234 	c->sub_status[0] = 0;
235 	c->locked = false;
236 	c->total_size = 0;
237 	c->http_code = 0;
238 
239 	c->textsearch.string = NULL;
240 	c->textsearch.context = NULL;
241 
242 	content_set_status(c, messages_get("Loading"));
243 
244 	/* Finally, claim low-level cache events */
245 	error = llcache_handle_change_callback(llcache,
246 					       content_llcache_callback, c);
247 	if (error != NSERROR_OK) {
248 		lwc_string_unref(c->mime_type);
249 		return error;
250 	}
251 
252 	return NSERROR_OK;
253 }
254 
255 
256 /* exported interface documented in content/content.h */
content_can_reformat(hlcache_handle * h)257 bool content_can_reformat(hlcache_handle *h)
258 {
259 	struct content *c = hlcache_handle_get_content(h);
260 
261 	if (c == NULL)
262 		return false;
263 
264 	return (c->handler->reformat != NULL);
265 }
266 
267 
268 /* exported interface documented in content/protected.h */
content_set_status(struct content * c,const char * status_message)269 void content_set_status(struct content *c, const char *status_message)
270 {
271 	size_t len = strlen(status_message);
272 
273 	if (len >= sizeof(c->sub_status)) {
274 		len = sizeof(c->sub_status) - 1;
275 	}
276 	memcpy(c->sub_status, status_message, len);
277 	c->sub_status[len] = '\0';
278 
279 	content_update_status(c);
280 }
281 
282 
283 /* exported interface documented in content/protected.h */
content_set_ready(struct content * c)284 void content_set_ready(struct content *c)
285 {
286 	/* The content must be locked at this point, as it can only
287 	 * become READY after conversion. */
288 	assert(c->locked);
289 	c->locked = false;
290 
291 	c->status = CONTENT_STATUS_READY;
292 	content_update_status(c);
293 	content_broadcast(c, CONTENT_MSG_READY, NULL);
294 }
295 
296 
297 /* exported interface documented in content/protected.h */
content_set_done(struct content * c)298 void content_set_done(struct content *c)
299 {
300 	uint64_t now_ms;
301 
302 	nsu_getmonotonic_ms(&now_ms);
303 
304 	c->status = CONTENT_STATUS_DONE;
305 	c->time = now_ms - c->time;
306 	content_update_status(c);
307 	content_broadcast(c, CONTENT_MSG_DONE, NULL);
308 }
309 
310 
311 /* exported interface documented in content/protected.h */
content_set_error(struct content * c)312 void content_set_error(struct content *c)
313 {
314 	c->locked = false;
315 	c->status = CONTENT_STATUS_ERROR;
316 }
317 
318 
319 /* exported interface documented in content/content.h */
content_reformat(hlcache_handle * h,bool background,int width,int height)320 void content_reformat(hlcache_handle *h, bool background,
321 		      int width, int height)
322 {
323 	content__reformat(hlcache_handle_get_content(h), background,
324 			  width, height);
325 }
326 
327 
328 /* exported interface documented in content/protected.h */
329 void
content__reformat(struct content * c,bool background,int width,int height)330 content__reformat(struct content *c, bool background, int width, int height)
331 {
332 	union content_msg_data data;
333 	assert(c != 0);
334 	assert(c->status == CONTENT_STATUS_READY ||
335 	       c->status == CONTENT_STATUS_DONE);
336 	assert(c->locked == false);
337 
338 	c->available_width = width;
339 	c->available_height = height;
340 	if (c->handler->reformat != NULL) {
341 
342 		c->locked = true;
343 		c->handler->reformat(c, width, height);
344 		c->locked = false;
345 
346 		data.background = background;
347 		content_broadcast(c, CONTENT_MSG_REFORMAT, &data);
348 	}
349 }
350 
351 
352 /* exported interface documented in content/content.h */
content_destroy(struct content * c)353 void content_destroy(struct content *c)
354 {
355 	struct content_rfc5988_link *link;
356 
357 	assert(c);
358 	NSLOG(netsurf, INFO, "content %p %s", c,
359 	      nsurl_access_log(llcache_handle_get_url(c->llcache)));
360 	assert(c->locked == false);
361 
362 	if (c->handler->destroy != NULL)
363 		c->handler->destroy(c);
364 
365 	llcache_handle_release(c->llcache);
366 	c->llcache = NULL;
367 
368 	lwc_string_unref(c->mime_type);
369 
370 	/* release metadata links */
371 	link = c->links;
372 	while (link != NULL) {
373 		link = content__free_rfc5988_link(link);
374 	}
375 
376 	/* free the user list */
377 	if (c->user_list != NULL) {
378 		free(c->user_list);
379 	}
380 
381 	/* free the title */
382 	if (c->title != NULL) {
383 		free(c->title);
384 	}
385 
386 	/* free the fallback characterset */
387 	if (c->fallback_charset != NULL) {
388 		free(c->fallback_charset);
389 	}
390 
391 	free(c);
392 }
393 
394 
395 /* exported interface documented in content/content.h */
396 void
content_mouse_track(hlcache_handle * h,struct browser_window * bw,browser_mouse_state mouse,int x,int y)397 content_mouse_track(hlcache_handle *h,
398 		    struct browser_window *bw,
399 		    browser_mouse_state mouse,
400 		    int x, int y)
401 {
402 	struct content *c = hlcache_handle_get_content(h);
403 	assert(c != NULL);
404 
405 	if (c->handler->mouse_track != NULL) {
406 		c->handler->mouse_track(c, bw, mouse, x, y);
407 	} else {
408 		union content_msg_data msg_data;
409 		msg_data.pointer = BROWSER_POINTER_AUTO;
410 		content_broadcast(c, CONTENT_MSG_POINTER, &msg_data);
411 	}
412 
413 
414 	return;
415 }
416 
417 
418 /* exported interface documented in content/content.h */
419 void
content_mouse_action(hlcache_handle * h,struct browser_window * bw,browser_mouse_state mouse,int x,int y)420 content_mouse_action(hlcache_handle *h,
421 		     struct browser_window *bw,
422 		     browser_mouse_state mouse,
423 		     int x, int y)
424 {
425 	struct content *c = hlcache_handle_get_content(h);
426 	assert(c != NULL);
427 
428 	if (c->handler->mouse_action != NULL)
429 		c->handler->mouse_action(c, bw, mouse, x, y);
430 
431 	return;
432 }
433 
434 
435 /* exported interface documented in content/content.h */
content_keypress(struct hlcache_handle * h,uint32_t key)436 bool content_keypress(struct hlcache_handle *h, uint32_t key)
437 {
438 	struct content *c = hlcache_handle_get_content(h);
439 	assert(c != NULL);
440 
441 	if (c->handler->keypress != NULL)
442 		return c->handler->keypress(c, key);
443 
444 	return false;
445 }
446 
447 
448 /* exported interface documented in content/content.h */
content_request_redraw(struct hlcache_handle * h,int x,int y,int width,int height)449 void content_request_redraw(struct hlcache_handle *h,
450 			    int x, int y, int width, int height)
451 {
452 	content__request_redraw(hlcache_handle_get_content(h),
453 				x, y, width, height);
454 }
455 
456 
457 /* exported interface, documented in content/protected.h */
content__request_redraw(struct content * c,int x,int y,int width,int height)458 void content__request_redraw(struct content *c,
459 			     int x, int y, int width, int height)
460 {
461 	union content_msg_data data;
462 
463 	if (c == NULL)
464 		return;
465 
466 	data.redraw.x = x;
467 	data.redraw.y = y;
468 	data.redraw.width = width;
469 	data.redraw.height = height;
470 
471 	content_broadcast(c, CONTENT_MSG_REDRAW, &data);
472 }
473 
474 
475 /* exported interface, documented in content/content.h */
content_exec(struct hlcache_handle * h,const char * src,size_t srclen)476 bool content_exec(struct hlcache_handle *h, const char *src, size_t srclen)
477 {
478 	struct content *c = hlcache_handle_get_content(h);
479 
480 	assert(c != NULL);
481 
482 	if (c->locked) {
483 		/* Not safe to do stuff */
484 		NSLOG(netsurf, DEEPDEBUG, "Unable to exec, content locked");
485 		return false;
486 	}
487 
488 	if (c->handler->exec == NULL) {
489 		/* Can't exec something on this content */
490 		NSLOG(netsurf, DEEPDEBUG, "Unable to exec, no exec function");
491 		return false;
492 	}
493 
494 	return c->handler->exec(c, src, srclen);
495 }
496 
497 
498 /* exported interface, documented in content/content.h */
content_saw_insecure_objects(struct hlcache_handle * h)499 bool content_saw_insecure_objects(struct hlcache_handle *h)
500 {
501 	struct content *c = hlcache_handle_get_content(h);
502 	struct nsurl *url = hlcache_handle_get_url(h);
503 	lwc_string *scheme = nsurl_get_component(url, NSURL_SCHEME);
504 	bool match;
505 
506 	/* Is this an internal scheme? If so, we trust here and stop */
507 	if ((lwc_string_isequal(scheme, corestring_lwc_about,
508 				&match) == lwc_error_ok &&
509 	     (match == true)) ||
510 	    (lwc_string_isequal(scheme, corestring_lwc_data,
511 				&match) == lwc_error_ok &&
512 	     (match == true)) ||
513 	    (lwc_string_isequal(scheme, corestring_lwc_resource,
514 				&match) == lwc_error_ok &&
515 	     (match == true)) ||
516 	    /* Our internal x-ns-css scheme is secure */
517 	    (lwc_string_isequal(scheme, corestring_lwc_x_ns_css,
518 				&match) == lwc_error_ok &&
519 	     (match == true)) ||
520 	    /* We also treat file: as "not insecure" here */
521 	    (lwc_string_isequal(scheme, corestring_lwc_file,
522 				&match) == lwc_error_ok &&
523 	     (match == true))) {
524 		/* No insecurity to find */
525 		lwc_string_unref(scheme);
526 		return false;
527 	}
528 
529 	/* Okay, not internal, am *I* secure? */
530 	if ((lwc_string_isequal(scheme, corestring_lwc_https,
531 				&match) == lwc_error_ok)
532 	    && (match == false)) {
533 		/* I did see something insecure -- ME! */
534 		lwc_string_unref(scheme);
535 		return true;
536 	}
537 
538 	lwc_string_unref(scheme);
539 	/* I am supposed to be secure, but was I overridden */
540 	if (urldb_get_cert_permissions(url)) {
541 		/* I was https:// but I was overridden, that's no good */
542 		return true;
543 	}
544 
545 	/* Otherwise try and chain through the handler */
546 	if (c != NULL && c->handler->saw_insecure_objects != NULL) {
547 		return c->handler->saw_insecure_objects(c);
548 	}
549 
550 	/* If we can't see insecure objects, we can't see them */
551 	return false;
552 }
553 
554 
555 /* exported interface, documented in content/content.h */
556 bool
content_redraw(hlcache_handle * h,struct content_redraw_data * data,const struct rect * clip,const struct redraw_context * ctx)557 content_redraw(hlcache_handle *h,
558 	       struct content_redraw_data *data,
559 	       const struct rect *clip,
560 	       const struct redraw_context *ctx)
561 {
562 	struct content *c = hlcache_handle_get_content(h);
563 
564 	assert(c != NULL);
565 
566 	if (c->locked) {
567 		/* not safe to attempt redraw */
568 		return true;
569 	}
570 
571 	/* ensure we have a redrawable content */
572 	if (c->handler->redraw == NULL) {
573 		return true;
574 	}
575 
576 	return c->handler->redraw(c, data, clip, ctx);
577 }
578 
579 
580 /* exported interface, documented in content/content.h */
581 bool
content_scaled_redraw(struct hlcache_handle * h,int width,int height,const struct redraw_context * ctx)582 content_scaled_redraw(struct hlcache_handle *h,
583 		      int width, int height,
584 		      const struct redraw_context *ctx)
585 {
586 	struct content *c = hlcache_handle_get_content(h);
587 	struct redraw_context new_ctx = *ctx;
588 	struct rect clip;
589 	struct content_redraw_data data;
590 	bool plot_ok = true;
591 
592 	assert(c != NULL);
593 
594 	/* ensure it is safe to attempt redraw */
595 	if (c->locked) {
596 		return true;
597 	}
598 
599 	/* ensure we have a redrawable content */
600 	if (c->handler->redraw == NULL) {
601 		return true;
602 	}
603 
604 	NSLOG(netsurf, INFO, "Content %p %dx%d ctx:%p", c, width, height, ctx);
605 
606 	if (ctx->plot->option_knockout) {
607 		knockout_plot_start(ctx, &new_ctx);
608 	}
609 
610 	/* Set clip rectangle to required thumbnail size */
611 	clip.x0 = 0;
612 	clip.y0 = 0;
613 	clip.x1 = width;
614 	clip.y1 = height;
615 
616 	new_ctx.plot->clip(&new_ctx, &clip);
617 
618 	/* Plot white background */
619 	plot_ok &= (new_ctx.plot->rectangle(&new_ctx,
620 					    plot_style_fill_white,
621 					    &clip) == NSERROR_OK);
622 
623 	/* Set up content redraw data */
624 	data.x = 0;
625 	data.y = 0;
626 	data.width = width;
627 	data.height = height;
628 
629 	data.background_colour = 0xFFFFFF;
630 	data.repeat_x = false;
631 	data.repeat_y = false;
632 
633 	/* Find the scale factor to use if the content has a width */
634 	if (c->width) {
635 		data.scale = (float)width / (float)c->width;
636 	} else {
637 		data.scale = 1.0;
638 	}
639 
640 	/* Render the content */
641 	plot_ok &= c->handler->redraw(c, &data, &clip, &new_ctx);
642 
643 	if (ctx->plot->option_knockout) {
644 		knockout_plot_end(ctx);
645 	}
646 
647 	return plot_ok;
648 }
649 
650 
651 /* exported interface documented in content/content.h */
652 bool
content_add_user(struct content * c,void (* callback)(struct content * c,content_msg msg,const union content_msg_data * data,void * pw),void * pw)653 content_add_user(struct content *c,
654 		 void (*callback)(
655 				  struct content *c,
656 				  content_msg msg,
657 				  const union content_msg_data *data,
658 				  void *pw),
659 		 void *pw)
660 {
661 	struct content_user *user;
662 
663 	NSLOG(netsurf, INFO, "content "URL_FMT_SPC" (%p), user %p %p",
664 	      nsurl_access_log(llcache_handle_get_url(c->llcache)),
665 	      c, callback, pw);
666 	user = malloc(sizeof(struct content_user));
667 	if (!user)
668 		return false;
669 	user->callback = callback;
670 	user->pw = pw;
671 	user->next = c->user_list->next;
672 	c->user_list->next = user;
673 
674 	if (c->handler->add_user != NULL)
675 		c->handler->add_user(c);
676 
677 	return true;
678 }
679 
680 
681 /* exported interface documented in content/content.h */
682 void
content_remove_user(struct content * c,void (* callback)(struct content * c,content_msg msg,const union content_msg_data * data,void * pw),void * pw)683 content_remove_user(struct content *c,
684 		    void (*callback)(
685 				     struct content *c,
686 				     content_msg msg,
687 				     const union content_msg_data *data,
688 				     void *pw),
689 		    void *pw)
690 {
691 	struct content_user *user, *next;
692 	NSLOG(netsurf, INFO, "content "URL_FMT_SPC" (%p), user %p %p",
693 	      nsurl_access_log(llcache_handle_get_url(c->llcache)),
694 	      c, callback, pw);
695 
696 	/* user_list starts with a sentinel */
697 	for (user = c->user_list; user->next != 0 &&
698 		     !(user->next->callback == callback &&
699 		       user->next->pw == pw); user = user->next)
700 		;
701 	if (user->next == 0) {
702 		NSLOG(netsurf, INFO, "user not found in list");
703 		assert(0);
704 		return;
705 	}
706 
707 	if (c->handler->remove_user != NULL)
708 		c->handler->remove_user(c);
709 
710 	next = user->next;
711 	user->next = next->next;
712 	free(next);
713 }
714 
715 
716 /* exported interface documented in content/content.h */
content_count_users(struct content * c)717 uint32_t content_count_users(struct content *c)
718 {
719 	struct content_user *user;
720 	uint32_t counter = 0;
721 
722 	assert(c != NULL);
723 
724 	for (user = c->user_list; user != NULL; user = user->next)
725 		counter += 1;
726 
727 	assert(counter > 0);
728 
729 	return counter - 1; /* Subtract 1 for the sentinel */
730 }
731 
732 
733 /* exported interface documented in content/content.h */
content_matches_quirks(struct content * c,bool quirks)734 bool content_matches_quirks(struct content *c, bool quirks)
735 {
736 	if (c->handler->matches_quirks == NULL)
737 		return true;
738 
739 	return c->handler->matches_quirks(c, quirks);
740 }
741 
742 
743 /* exported interface documented in content/content.h */
content_is_shareable(struct content * c)744 bool content_is_shareable(struct content *c)
745 {
746 	return c->handler->no_share == false;
747 }
748 
749 
750 /* exported interface documented in content/protected.h */
content_broadcast(struct content * c,content_msg msg,const union content_msg_data * data)751 void content_broadcast(struct content *c, content_msg msg,
752 		       const union content_msg_data *data)
753 {
754 	struct content_user *user, *next;
755 	assert(c);
756 
757 	NSLOG(netsurf, DEEPDEBUG, "%p -> msg:%d", c, msg);
758 	for (user = c->user_list->next; user != 0; user = next) {
759 		next = user->next;  /* user may be destroyed during callback */
760 		if (user->callback != 0)
761 			user->callback(c, msg, data, user->pw);
762 	}
763 }
764 
765 
766 /* exported interface documented in content_protected.h */
767 void
content_broadcast_error(struct content * c,nserror errorcode,const char * msg)768 content_broadcast_error(struct content *c, nserror errorcode, const char *msg)
769 {
770 	struct content_user *user, *next;
771 	union content_msg_data data;
772 
773 	assert(c);
774 
775 	data.errordata.errorcode = errorcode;
776 	data.errordata.errormsg = msg;
777 
778 	for (user = c->user_list->next; user != 0; user = next) {
779 		next = user->next;  /* user may be destroyed during callback */
780 		if (user->callback != 0) {
781 			user->callback(c, CONTENT_MSG_ERROR,
782 				       &data, user->pw);
783 		}
784 	}
785 }
786 
787 
788 /* exported interface, documented in content/content.h */
789 nserror
content_open(hlcache_handle * h,struct browser_window * bw,struct content * page,struct object_params * params)790 content_open(hlcache_handle *h,
791 	     struct browser_window *bw,
792 	     struct content *page,
793 	     struct object_params *params)
794 {
795 	struct content *c;
796 	nserror res;
797 
798 	c = hlcache_handle_get_content(h);
799 	assert(c != 0);
800 	NSLOG(netsurf, INFO, "content %p %s", c,
801 	      nsurl_access_log(llcache_handle_get_url(c->llcache)));
802 	if (c->handler->open != NULL) {
803 		res = c->handler->open(c, bw, page, params);
804 	} else {
805 		res = NSERROR_OK;
806 	}
807 	return res;
808 }
809 
810 
811 /* exported interface, documented in content/content.h */
content_close(hlcache_handle * h)812 nserror content_close(hlcache_handle *h)
813 {
814 	struct content *c;
815 	nserror res;
816 
817 	c = hlcache_handle_get_content(h);
818 	if (c == NULL) {
819 		return NSERROR_BAD_PARAMETER;
820 	}
821 
822 	if ((c->status != CONTENT_STATUS_READY) &&
823 	    (c->status != CONTENT_STATUS_DONE)) {
824 		/* status is not read or done so nothing to do */
825 		return NSERROR_INVALID;
826 	}
827 
828 	NSLOG(netsurf, INFO, "content %p %s", c,
829 	      nsurl_access_log(llcache_handle_get_url(c->llcache)));
830 
831 	if (c->textsearch.context != NULL) {
832 		content_textsearch_destroy(c->textsearch.context);
833 		c->textsearch.context = NULL;
834 	}
835 
836 	if (c->handler->close != NULL) {
837 		res = c->handler->close(c);
838 	} else {
839 		res = NSERROR_OK;
840 	}
841 	return res;
842 }
843 
844 
845 /* exported interface, documented in content/content.h */
content_clear_selection(hlcache_handle * h)846 void content_clear_selection(hlcache_handle *h)
847 {
848 	struct content *c = hlcache_handle_get_content(h);
849 	assert(c != 0);
850 
851 	if (c->handler->get_selection != NULL)
852 		c->handler->clear_selection(c);
853 }
854 
855 
856 /* exported interface, documented in content/content.h */
content_get_selection(hlcache_handle * h)857 char * content_get_selection(hlcache_handle *h)
858 {
859 	struct content *c = hlcache_handle_get_content(h);
860 	assert(c != 0);
861 
862 	if (c->handler->get_selection != NULL)
863 		return c->handler->get_selection(c);
864 	else
865 		return NULL;
866 }
867 
868 
869 /* exported interface documented in content/content.h */
870 nserror
content_get_contextual_content(struct hlcache_handle * h,int x,int y,struct browser_window_features * data)871 content_get_contextual_content(struct hlcache_handle *h,
872 			       int x, int y,
873 			       struct browser_window_features *data)
874 {
875 	struct content *c = hlcache_handle_get_content(h);
876 	assert(c != 0);
877 
878 	if (c->handler->get_contextual_content != NULL) {
879 		return c->handler->get_contextual_content(c, x, y, data);
880 	}
881 
882 	data->object = h;
883 	return NSERROR_OK;
884 }
885 
886 
887 /* exported interface, documented in content/content.h */
888 bool
content_scroll_at_point(struct hlcache_handle * h,int x,int y,int scrx,int scry)889 content_scroll_at_point(struct hlcache_handle *h,
890 			int x, int y,
891 			int scrx, int scry)
892 {
893 	struct content *c = hlcache_handle_get_content(h);
894 	assert(c != 0);
895 
896 	if (c->handler->scroll_at_point != NULL)
897 		return c->handler->scroll_at_point(c, x, y, scrx, scry);
898 
899 	return false;
900 }
901 
902 
903 /* exported interface, documented in content/content.h */
904 bool
content_drop_file_at_point(struct hlcache_handle * h,int x,int y,char * file)905 content_drop_file_at_point(struct hlcache_handle *h,
906 			   int x, int y,
907 			   char *file)
908 {
909 	struct content *c = hlcache_handle_get_content(h);
910 	assert(c != 0);
911 
912 	if (c->handler->drop_file_at_point != NULL)
913 		return c->handler->drop_file_at_point(c, x, y, file);
914 
915 	return false;
916 }
917 
918 
919 /* exported interface documented in content/content.h */
920 nserror
content_debug_dump(struct hlcache_handle * h,FILE * f,enum content_debug op)921 content_debug_dump(struct hlcache_handle *h, FILE *f, enum content_debug op)
922 {
923 	struct content *c = hlcache_handle_get_content(h);
924 	assert(c != 0);
925 
926 	if (c->handler->debug_dump == NULL) {
927 		return NSERROR_NOT_IMPLEMENTED;
928 	}
929 
930 	return c->handler->debug_dump(c, f, op);
931 }
932 
933 
934 /* exported interface documented in content/content.h */
content_debug(struct hlcache_handle * h,enum content_debug op)935 nserror content_debug(struct hlcache_handle *h, enum content_debug op)
936 {
937 	struct content *c = hlcache_handle_get_content(h);
938 
939 	if (c == NULL) {
940 		return NSERROR_BAD_PARAMETER;
941 	}
942 
943 	if (c->handler->debug == NULL) {
944 		return NSERROR_NOT_IMPLEMENTED;
945 	}
946 
947 	return c->handler->debug(c, op);
948 }
949 
950 
951 /* exported interface documented in content/content.h */
952 struct content_rfc5988_link *
content_find_rfc5988_link(hlcache_handle * h,lwc_string * rel)953 content_find_rfc5988_link(hlcache_handle *h, lwc_string *rel)
954 {
955 	struct content *c = hlcache_handle_get_content(h);
956 	struct content_rfc5988_link *link = c->links;
957 	bool rel_match = false;
958 
959 	while (link != NULL) {
960 		if (lwc_string_caseless_isequal(link->rel, rel,
961 						&rel_match) == lwc_error_ok && rel_match) {
962 			break;
963 		}
964 		link = link->next;
965 	}
966 	return link;
967 }
968 
969 
970 /* exported interface documented in content/protected.h */
971 struct content_rfc5988_link *
content__free_rfc5988_link(struct content_rfc5988_link * link)972 content__free_rfc5988_link(struct content_rfc5988_link *link)
973 {
974 	struct content_rfc5988_link *next;
975 
976 	next = link->next;
977 
978 	lwc_string_unref(link->rel);
979 	nsurl_unref(link->href);
980 	if (link->hreflang != NULL) {
981 		lwc_string_unref(link->hreflang);
982 	}
983 	if (link->type != NULL) {
984 		lwc_string_unref(link->type);
985 	}
986 	if (link->media != NULL) {
987 		lwc_string_unref(link->media);
988 	}
989 	if (link->sizes != NULL) {
990 		lwc_string_unref(link->sizes);
991 	}
992 	free(link);
993 
994 	return next;
995 }
996 
997 
998 /* exported interface documented in content/protected.h */
999 bool
content__add_rfc5988_link(struct content * c,const struct content_rfc5988_link * link)1000 content__add_rfc5988_link(struct content *c,
1001 			  const struct content_rfc5988_link *link)
1002 {
1003 	struct content_rfc5988_link *newlink;
1004 	union content_msg_data msg_data;
1005 
1006 	/* a link relation must be present for it to be a link */
1007 	if (link->rel == NULL) {
1008 		return false;
1009 	}
1010 
1011 	/* a link href must be present for it to be a link */
1012 	if (link->href == NULL) {
1013 		return false;
1014 	}
1015 
1016 	newlink = calloc(1, sizeof(struct content_rfc5988_link));
1017 	if (newlink == NULL) {
1018 		return false;
1019 	}
1020 
1021 	/* copy values */
1022 	newlink->rel = lwc_string_ref(link->rel);
1023 	newlink->href = nsurl_ref(link->href);
1024 	if (link->hreflang != NULL) {
1025 		newlink->hreflang = lwc_string_ref(link->hreflang);
1026 	}
1027 	if (link->type != NULL) {
1028 		newlink->type = lwc_string_ref(link->type);
1029 	}
1030 	if (link->media != NULL) {
1031 		newlink->media = lwc_string_ref(link->media);
1032 	}
1033 	if (link->sizes != NULL) {
1034 		newlink->sizes = lwc_string_ref(link->sizes);
1035 	}
1036 
1037 	/* add to metadata link to list */
1038 	newlink->next = c->links;
1039 	c->links = newlink;
1040 
1041 	/* broadcast the data */
1042 	msg_data.rfc5988_link = newlink;
1043 	content_broadcast(c, CONTENT_MSG_LINK, &msg_data);
1044 
1045 	return true;
1046 }
1047 
1048 
1049 /* exported interface documented in content/content.h */
content_get_url(struct content * c)1050 nsurl *content_get_url(struct content *c)
1051 {
1052 	if (c == NULL)
1053 		return NULL;
1054 
1055 	return llcache_handle_get_url(c->llcache);
1056 }
1057 
1058 
1059 /* exported interface documented in content/content.h */
content_get_type(hlcache_handle * h)1060 content_type content_get_type(hlcache_handle *h)
1061 {
1062 	struct content *c = hlcache_handle_get_content(h);
1063 
1064 	if (c == NULL)
1065 		return CONTENT_NONE;
1066 
1067 	return c->handler->type();
1068 }
1069 
1070 
1071 /* exported interface documented in content/content.h */
content_get_mime_type(hlcache_handle * h)1072 lwc_string *content_get_mime_type(hlcache_handle *h)
1073 {
1074 	return content__get_mime_type(hlcache_handle_get_content(h));
1075 }
1076 
1077 
1078 /* exported interface documented in content/content_protected.h */
content__get_mime_type(struct content * c)1079 lwc_string *content__get_mime_type(struct content *c)
1080 {
1081 	if (c == NULL)
1082 		return NULL;
1083 
1084 	return lwc_string_ref(c->mime_type);
1085 }
1086 
1087 
1088 /* exported interface documented in content/content_protected.h */
content__set_title(struct content * c,const char * title)1089 bool content__set_title(struct content *c, const char *title)
1090 {
1091 	char *new_title = strdup(title);
1092 	if (new_title == NULL)
1093 		return false;
1094 
1095 	if (c->title != NULL)
1096 		free(c->title);
1097 
1098 	c->title = new_title;
1099 
1100 	return true;
1101 }
1102 
1103 
1104 /* exported interface documented in content/content.h */
content_get_title(hlcache_handle * h)1105 const char *content_get_title(hlcache_handle *h)
1106 {
1107 	return content__get_title(hlcache_handle_get_content(h));
1108 }
1109 
1110 
1111 /* exported interface documented in content/content_protected.h */
content__get_title(struct content * c)1112 const char *content__get_title(struct content *c)
1113 {
1114 	if (c == NULL)
1115 		return NULL;
1116 
1117 	return c->title != NULL ? c->title :
1118 		nsurl_access(llcache_handle_get_url(c->llcache));
1119 }
1120 
1121 
1122 /* exported interface documented in content/content.h */
content_get_status(hlcache_handle * h)1123 content_status content_get_status(hlcache_handle *h)
1124 {
1125 	return content__get_status(hlcache_handle_get_content(h));
1126 }
1127 
1128 
1129 /* exported interface documented in content/content_protected.h */
content__get_status(struct content * c)1130 content_status content__get_status(struct content *c)
1131 {
1132 	if (c == NULL)
1133 		return CONTENT_STATUS_ERROR;
1134 
1135 	return c->status;
1136 }
1137 
1138 
1139 /* exported interface documented in content/content.h */
content_get_status_message(hlcache_handle * h)1140 const char *content_get_status_message(hlcache_handle *h)
1141 {
1142 	return content__get_status_message(hlcache_handle_get_content(h));
1143 }
1144 
1145 
1146 /* exported interface documented in content/content_protected.h */
content__get_status_message(struct content * c)1147 const char *content__get_status_message(struct content *c)
1148 {
1149 	if (c == NULL)
1150 		return NULL;
1151 
1152 	return c->status_message;
1153 }
1154 
1155 
1156 /* exported interface documented in content/content.h */
content_get_width(hlcache_handle * h)1157 int content_get_width(hlcache_handle *h)
1158 {
1159 	return content__get_width(hlcache_handle_get_content(h));
1160 }
1161 
1162 
1163 /* exported interface documented in content/content_protected.h */
content__get_width(struct content * c)1164 int content__get_width(struct content *c)
1165 {
1166 	if (c == NULL)
1167 		return 0;
1168 
1169 	return c->width;
1170 }
1171 
1172 
1173 /* exported interface documented in content/content.h */
content_get_height(hlcache_handle * h)1174 int content_get_height(hlcache_handle *h)
1175 {
1176 	return content__get_height(hlcache_handle_get_content(h));
1177 }
1178 
1179 
1180 /* exported interface documented in content/content_protected.h */
content__get_height(struct content * c)1181 int content__get_height(struct content *c)
1182 {
1183 	if (c == NULL)
1184 		return 0;
1185 
1186 	return c->height;
1187 }
1188 
1189 
1190 /* exported interface documented in content/content.h */
content_get_available_width(hlcache_handle * h)1191 int content_get_available_width(hlcache_handle *h)
1192 {
1193 	return content__get_available_width(hlcache_handle_get_content(h));
1194 }
1195 
1196 
1197 /* exported interface documented in content/content_protected.h */
content__get_available_width(struct content * c)1198 int content__get_available_width(struct content *c)
1199 {
1200 	if (c == NULL)
1201 		return 0;
1202 
1203 	return c->available_width;
1204 }
1205 
1206 
1207 /* exported interface documented in content/content.h */
content_get_source_data(hlcache_handle * h,size_t * size)1208 const uint8_t *content_get_source_data(hlcache_handle *h, size_t *size)
1209 {
1210 	return content__get_source_data(hlcache_handle_get_content(h), size);
1211 }
1212 
1213 
1214 /* exported interface documented in content/content_protected.h */
content__get_source_data(struct content * c,size_t * size)1215 const uint8_t *content__get_source_data(struct content *c, size_t *size)
1216 {
1217 	assert(size != NULL);
1218 
1219 	/** \todo check if the content check should be an assert */
1220 	if (c == NULL)
1221 		return NULL;
1222 
1223 	return llcache_handle_get_source_data(c->llcache, size);
1224 }
1225 
1226 
1227 /* exported interface documented in content/content.h */
content_invalidate_reuse_data(hlcache_handle * h)1228 void content_invalidate_reuse_data(hlcache_handle *h)
1229 {
1230 	content__invalidate_reuse_data(hlcache_handle_get_content(h));
1231 }
1232 
1233 
1234 /* exported interface documented in content/content_protected.h */
content__invalidate_reuse_data(struct content * c)1235 void content__invalidate_reuse_data(struct content *c)
1236 {
1237 	if (c == NULL || c->llcache == NULL)
1238 		return;
1239 
1240 	/* Invalidate low-level cache data */
1241 	llcache_handle_invalidate_cache_data(c->llcache);
1242 }
1243 
1244 
1245 /* exported interface documented in content/content.h */
content_get_refresh_url(hlcache_handle * h)1246 nsurl *content_get_refresh_url(hlcache_handle *h)
1247 {
1248 	return content__get_refresh_url(hlcache_handle_get_content(h));
1249 }
1250 
1251 
1252 /* exported interface documented in content/content_protected.h */
content__get_refresh_url(struct content * c)1253 nsurl *content__get_refresh_url(struct content *c)
1254 {
1255 	if (c == NULL)
1256 		return NULL;
1257 
1258 	return c->refresh;
1259 }
1260 
1261 
1262 /* exported interface documented in content/content.h */
content_get_bitmap(hlcache_handle * h)1263 struct bitmap *content_get_bitmap(hlcache_handle *h)
1264 {
1265 	return content__get_bitmap(hlcache_handle_get_content(h));
1266 }
1267 
1268 
1269 /* exported interface documented in content/content_protected.h */
content__get_bitmap(struct content * c)1270 struct bitmap *content__get_bitmap(struct content *c)
1271 {
1272 	struct bitmap *bitmap = NULL;
1273 
1274 	if ((c != NULL) &&
1275 	    (c->handler != NULL) &&
1276 	    (c->handler->type != NULL) &&
1277 	    (c->handler->type() == CONTENT_IMAGE) &&
1278 	    (c->handler->get_internal != NULL) ) {
1279 		bitmap = c->handler->get_internal(c, NULL);
1280 	}
1281 
1282 	return bitmap;
1283 }
1284 
1285 
1286 /* exported interface documented in content/content.h */
content_get_opaque(hlcache_handle * h)1287 bool content_get_opaque(hlcache_handle *h)
1288 {
1289 	return content__get_opaque(hlcache_handle_get_content(h));
1290 }
1291 
1292 
1293 /* exported interface documented in content/content_protected.h */
content__get_opaque(struct content * c)1294 bool content__get_opaque(struct content *c)
1295 {
1296 	if ((c != NULL) &&
1297 	    (c->handler != NULL) &&
1298 	    (c->handler->is_opaque != NULL)) {
1299 		return c->handler->is_opaque(c);
1300 	}
1301 
1302 	return false;
1303 }
1304 
1305 
1306 /* exported interface documented in content/content.h */
content_get_quirks(hlcache_handle * h)1307 bool content_get_quirks(hlcache_handle *h)
1308 {
1309 	struct content *c = hlcache_handle_get_content(h);
1310 
1311 	if (c == NULL)
1312 		return false;
1313 
1314 	return c->quirks;
1315 }
1316 
1317 
1318 /* exported interface documented in content/content.h */
1319 const char *
content_get_encoding(hlcache_handle * h,enum content_encoding_type op)1320 content_get_encoding(hlcache_handle *h, enum content_encoding_type op)
1321 {
1322 	return content__get_encoding(hlcache_handle_get_content(h), op);
1323 }
1324 
1325 
1326 /* exported interface documented in content/content_protected.h */
1327 const char *
content__get_encoding(struct content * c,enum content_encoding_type op)1328 content__get_encoding(struct content *c, enum content_encoding_type op)
1329 {
1330 	const char *encoding = NULL;
1331 
1332 	if ((c != NULL) &&
1333 	    (c->handler != NULL) &&
1334 	    (c->handler->get_encoding != NULL) ) {
1335 		encoding = c->handler->get_encoding(c, op);
1336 	}
1337 
1338 	return encoding;
1339 }
1340 
1341 
1342 /* exported interface documented in content/content.h */
content_is_locked(hlcache_handle * h)1343 bool content_is_locked(hlcache_handle *h)
1344 {
1345 	return content__is_locked(hlcache_handle_get_content(h));
1346 }
1347 
1348 
1349 /* exported interface documented in content/content_protected.h */
content__is_locked(struct content * c)1350 bool content__is_locked(struct content *c)
1351 {
1352 	return c->locked;
1353 }
1354 
1355 
1356 /* exported interface documented in content/content.h */
content_get_llcache_handle(struct content * c)1357 const llcache_handle *content_get_llcache_handle(struct content *c)
1358 {
1359 	if (c == NULL)
1360 		return NULL;
1361 
1362 	return c->llcache;
1363 }
1364 
1365 
1366 /* exported interface documented in content/protected.h */
content_clone(struct content * c)1367 struct content *content_clone(struct content *c)
1368 {
1369 	struct content *nc;
1370 	nserror error;
1371 
1372 	error = c->handler->clone(c, &nc);
1373 	if (error != NSERROR_OK)
1374 		return NULL;
1375 
1376 	return nc;
1377 };
1378 
1379 
1380 /* exported interface documented in content/protected.h */
content__clone(const struct content * c,struct content * nc)1381 nserror content__clone(const struct content *c, struct content *nc)
1382 {
1383 	nserror error;
1384 
1385 	error = llcache_handle_clone(c->llcache, &(nc->llcache));
1386 	if (error != NSERROR_OK) {
1387 		return error;
1388 	}
1389 
1390 	llcache_handle_change_callback(nc->llcache,
1391 				       content_llcache_callback, nc);
1392 
1393 	nc->mime_type = lwc_string_ref(c->mime_type);
1394 	nc->handler = c->handler;
1395 
1396 	nc->status = c->status;
1397 
1398 	nc->width = c->width;
1399 	nc->height = c->height;
1400 	nc->available_width = c->available_width;
1401 	nc->quirks = c->quirks;
1402 
1403 	if (c->fallback_charset != NULL) {
1404 		nc->fallback_charset = strdup(c->fallback_charset);
1405 		if (nc->fallback_charset == NULL) {
1406 			return NSERROR_NOMEM;
1407 		}
1408 	}
1409 
1410 	if (c->refresh != NULL) {
1411 		nc->refresh = nsurl_ref(c->refresh);
1412 		if (nc->refresh == NULL) {
1413 			return NSERROR_NOMEM;
1414 		}
1415 	}
1416 
1417 	nc->time = c->time;
1418 	nc->reformat_time = c->reformat_time;
1419 	nc->size = c->size;
1420 
1421 	if (c->title != NULL) {
1422 		nc->title = strdup(c->title);
1423 		if (nc->title == NULL) {
1424 			return NSERROR_NOMEM;
1425 		}
1426 	}
1427 
1428 	nc->active = c->active;
1429 
1430 	nc->user_list = calloc(1, sizeof(struct content_user));
1431 	if (nc->user_list == NULL) {
1432 		return NSERROR_NOMEM;
1433 	}
1434 
1435 	memcpy(&(nc->status_message), &(c->status_message), 120);
1436 	memcpy(&(nc->sub_status), &(c->sub_status), 80);
1437 
1438 	nc->locked = c->locked;
1439 	nc->total_size = c->total_size;
1440 	nc->http_code = c->http_code;
1441 
1442 	return NSERROR_OK;
1443 }
1444 
1445 
1446 /* exported interface documented in content/content.h */
content_abort(struct content * c)1447 nserror content_abort(struct content *c)
1448 {
1449 	NSLOG(netsurf, INFO, "Aborting %p", c);
1450 
1451 	if (c->handler->stop != NULL)
1452 		c->handler->stop(c);
1453 
1454 	/* And for now, abort our llcache object */
1455 	return llcache_handle_abort(c->llcache);
1456 }
1457