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