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 2009 John-Mark Bell 
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 .
17
 */
18
 
19
/** \file
20
 * High-level resource cache (implementation)
21
 */
22
 
23
#include 
24
#include 
25
#include 
26
 
27
#include "content/content.h"
28
#include "content/hlcache.h"
29
#include "content/mimesniff.h"
30
#include "utils/http.h"
31
#include "utils/log.h"
32
#include "utils/messages.h"
33
#include "utils/ring.h"
34
#include "utils/schedule.h"
35
#include "utils/url.h"
36
#include "utils/utils.h"
37
 
38
typedef struct hlcache_entry hlcache_entry;
39
typedef struct hlcache_retrieval_ctx hlcache_retrieval_ctx;
40
 
41
/** High-level cache retrieval context */
42
struct hlcache_retrieval_ctx {
43
	struct hlcache_retrieval_ctx *r_prev; /**< Previous retrieval context in the ring */
44
	struct hlcache_retrieval_ctx *r_next; /**< Next retrieval context in the ring */
45
 
46
	llcache_handle *llcache;	/**< Low-level cache handle */
47
 
48
	hlcache_handle *handle;		/**< High-level handle for object */
49
 
50
	uint32_t flags;			/**< Retrieval flags */
51
 
52
	content_type accepted_types;	/**< Accepted types */
53
 
54
	hlcache_child_context child;	/**< Child context */
55
 
56
	bool migrate_target;		/**< Whether this context is the migration target */
57
};
58
 
59
/** High-level cache handle */
60
struct hlcache_handle {
61
	hlcache_entry *entry;		/**< Pointer to cache entry */
62
 
63
	hlcache_handle_callback cb;	/**< Client callback */
64
	void *pw;			/**< Client data */
65
};
66
 
67
/** Entry in high-level cache */
68
struct hlcache_entry {
69
	struct content *content;	/**< Pointer to associated content */
70
 
71
	hlcache_entry *next;		/**< Next sibling */
72
	hlcache_entry *prev;		/**< Previous sibling */
73
};
74
 
75
/** Current state of the cache.
76
 *
77
 * Global state of the cache.
78
 */
79
struct hlcache_s {
80
	struct hlcache_parameters params;
81
 
82
	/** List of cached content objects */
83
	hlcache_entry *content_list;
84
 
85
	/** Ring of retrieval contexts */
86
	hlcache_retrieval_ctx *retrieval_ctx_ring;
87
 
88
	/* statsistics */
89
	unsigned int hit_count;
90
	unsigned int miss_count;
91
};
92
 
93
/** high level cache state */
94
static struct hlcache_s *hlcache = NULL;
95
 
96
 
97
static void hlcache_clean(void *ignored);
98
 
99
static nserror hlcache_llcache_callback(llcache_handle *handle,
100
		const llcache_event *event, void *pw);
101
static nserror hlcache_migrate_ctx(hlcache_retrieval_ctx *ctx,
102
		lwc_string *effective_type);
103
static bool hlcache_type_is_acceptable(lwc_string *mime_type,
104
		content_type accepted_types, content_type *computed_type);
105
static nserror hlcache_find_content(hlcache_retrieval_ctx *ctx,
106
		lwc_string *effective_type);
107
static void hlcache_content_callback(struct content *c,
108
		content_msg msg, union content_msg_data data, void *pw);
109
 
110
/******************************************************************************
111
 * Public API								      *
112
 ******************************************************************************/
113
 
114
nserror
115
hlcache_initialise(const struct hlcache_parameters *hlcache_parameters)
116
{
117
	nserror ret;
118
 
119
	hlcache = calloc(1, sizeof(struct hlcache_s));
120
	if (hlcache == NULL) {
121
		return NSERROR_NOMEM;
122
	}
123
 
124
	ret = llcache_initialise(hlcache_parameters->cb,
125
				 hlcache_parameters->cb_ctx,
126
				 hlcache_parameters->limit);
127
	if (ret != NSERROR_OK) {
128
		free(hlcache);
129
		hlcache = NULL;
130
		return ret;
131
	}
132
 
133
	hlcache->params = *hlcache_parameters;
134
 
135
	/* Schedule the cache cleanup */
136
	schedule(hlcache->params.bg_clean_time / 10, hlcache_clean, NULL);
137
 
138
	return NSERROR_OK;
139
}
140
 
141
/* See hlcache.h for documentation */
142
void hlcache_stop(void)
143
{
144
	/* Remove the hlcache_clean schedule */
145
	schedule_remove(hlcache_clean, NULL);
146
}
147
 
148
/* See hlcache.h for documentation */
149
void hlcache_finalise(void)
150
{
151
	uint32_t num_contents, prev_contents;
152
	hlcache_entry *entry;
153
	hlcache_retrieval_ctx *ctx, *next;
154
 
155
	/* Obtain initial count of contents remaining */
156
	for (num_contents = 0, entry = hlcache->content_list;
157
			entry != NULL; entry = entry->next) {
158
		num_contents++;
159
	}
160
 
161
	LOG(("%d contents remain before cache drain", num_contents));
162
 
163
	/* Drain cache */
164
	do {
165
		prev_contents = num_contents;
166
 
167
		hlcache_clean(NULL);
168
 
169
		for (num_contents = 0, entry = hlcache->content_list;
170
				entry != NULL; entry = entry->next) {
171
			num_contents++;
172
		}
173
	} while (num_contents > 0 && num_contents != prev_contents);
174
 
175
	LOG(("%d contents remaining:", num_contents));
176
	for (entry = hlcache->content_list; entry != NULL; entry = entry->next) {
177
		hlcache_handle entry_handle = { entry, NULL, NULL };
178
 
179
		if (entry->content != NULL) {
180
			LOG(("	%p : %s (%d users)", entry,
181
			     nsurl_access(hlcache_handle_get_url(&entry_handle)), content_count_users(entry->content)));
182
		} else {
183
			LOG(("	%p", entry));
184
		}
185
	}
186
 
187
	/* Clean up retrieval contexts */
188
	if (hlcache->retrieval_ctx_ring != NULL) {
189
		ctx = hlcache->retrieval_ctx_ring;
190
 
191
		do {
192
			next = ctx->r_next;
193
 
194
			if (ctx->llcache != NULL)
195
				llcache_handle_release(ctx->llcache);
196
 
197
			if (ctx->handle != NULL)
198
				free(ctx->handle);
199
 
200
			if (ctx->child.charset != NULL)
201
				free((char *) ctx->child.charset);
202
 
203
			free(ctx);
204
 
205
			ctx = next;
206
		} while (ctx != hlcache->retrieval_ctx_ring);
207
 
208
		hlcache->retrieval_ctx_ring = NULL;
209
	}
210
 
211
	LOG(("hit/miss %d/%d", hlcache->hit_count, hlcache->miss_count));
212
 
213
	free(hlcache);
214
	hlcache = NULL;
215
 
216
	LOG(("Finalising low-level cache"));
217
	llcache_finalise();
218
}
219
 
220
/* See hlcache.h for documentation */
221
nserror hlcache_poll(void)
222
{
223
	llcache_poll();
224
 
225
	return NSERROR_OK;
226
}
227
 
228
/* See hlcache.h for documentation */
229
nserror hlcache_handle_retrieve(nsurl *url, uint32_t flags,
230
		nsurl *referer, llcache_post_data *post,
231
		hlcache_handle_callback cb, void *pw,
232
		hlcache_child_context *child,
233
		content_type accepted_types, hlcache_handle **result)
234
{
235
	hlcache_retrieval_ctx *ctx;
236
	nserror error;
237
 
238
	assert(cb != NULL);
239
 
240
	ctx = calloc(1, sizeof(hlcache_retrieval_ctx));
241
	if (ctx == NULL)
242
		return NSERROR_NOMEM;
243
 
244
	ctx->handle = calloc(1, sizeof(hlcache_handle));
245
	if (ctx->handle == NULL) {
246
		free(ctx);
247
		return NSERROR_NOMEM;
248
	}
249
 
250
	if (child != NULL) {
251
		if (child->charset != NULL) {
252
			ctx->child.charset = strdup(child->charset);
253
			if (ctx->child.charset == NULL) {
254
				free(ctx->handle);
255
				free(ctx);
256
				return NSERROR_NOMEM;
257
			}
258
		}
259
		ctx->child.quirks = child->quirks;
260
	}
261
 
262
	ctx->flags = flags;
263
	ctx->accepted_types = accepted_types;
264
 
265
	ctx->handle->cb = cb;
266
	ctx->handle->pw = pw;
267
 
268
	error = llcache_handle_retrieve(url, flags, referer, post,
269
			hlcache_llcache_callback, ctx,
270
			&ctx->llcache);
271
	if (error != NSERROR_OK) {
272
		free((char *) ctx->child.charset);
273
		free(ctx->handle);
274
		free(ctx);
275
		return error;
276
	}
277
 
278
	RING_INSERT(hlcache->retrieval_ctx_ring, ctx);
279
 
280
	*result = ctx->handle;
281
 
282
	return NSERROR_OK;
283
}
284
 
285
/* See hlcache.h for documentation */
286
nserror hlcache_handle_release(hlcache_handle *handle)
287
{
288
	if (handle->entry != NULL) {
289
		content_remove_user(handle->entry->content,
290
				hlcache_content_callback, handle);
291
	} else {
292
		RING_ITERATE_START(struct hlcache_retrieval_ctx,
293
				   hlcache->retrieval_ctx_ring,
294
				   ictx) {
295
			if (ictx->handle == handle &&
296
					ictx->migrate_target == false) {
297
				/* This is the nascent context for us,
298
				 * so abort the fetch */
299
				llcache_handle_abort(ictx->llcache);
300
				llcache_handle_release(ictx->llcache);
301
				/* Remove us from the ring */
302
				RING_REMOVE(hlcache->retrieval_ctx_ring, ictx);
303
				/* Throw us away */
304
				free((char *) ictx->child.charset);
305
				free(ictx);
306
				/* And stop */
307
				RING_ITERATE_STOP(hlcache->retrieval_ctx_ring,
308
						ictx);
309
			}
310
		} RING_ITERATE_END(hlcache->retrieval_ctx_ring, ictx);
311
	}
312
 
313
	handle->cb = NULL;
314
	handle->pw = NULL;
315
 
316
	free(handle);
317
 
318
	return NSERROR_OK;
319
}
320
 
321
/* See hlcache.h for documentation */
322
struct content *hlcache_handle_get_content(const hlcache_handle *handle)
323
{
324
	assert(handle != NULL);
325
 
326
	if (handle->entry != NULL)
327
		return handle->entry->content;
328
 
329
	return NULL;
330
}
331
 
332
/* See hlcache.h for documentation */
333
nserror hlcache_handle_abort(hlcache_handle *handle)
334
{
335
	struct hlcache_entry *entry = handle->entry;
336
	struct content *c;
337
 
338
	if (entry == NULL) {
339
		/* This handle is not yet associated with a cache entry.
340
		 * The implication is that the fetch for the handle has
341
		 * not progressed to the point where the entry can be
342
		 * created. */
343
 
344
		RING_ITERATE_START(struct hlcache_retrieval_ctx,
345
				   hlcache->retrieval_ctx_ring,
346
				   ictx) {
347
			if (ictx->handle == handle &&
348
					ictx->migrate_target == false) {
349
				/* This is the nascent context for us,
350
				 * so abort the fetch */
351
				llcache_handle_abort(ictx->llcache);
352
				llcache_handle_release(ictx->llcache);
353
				/* Remove us from the ring */
354
				RING_REMOVE(hlcache->retrieval_ctx_ring, ictx);
355
				/* Throw us away */
356
				free((char *) ictx->child.charset);
357
				free(ictx);
358
				/* And stop */
359
				RING_ITERATE_STOP(hlcache->retrieval_ctx_ring,
360
						ictx);
361
			}
362
		} RING_ITERATE_END(hlcache->retrieval_ctx_ring, ictx);
363
 
364
		return NSERROR_OK;
365
	}
366
 
367
	c = entry->content;
368
 
369
	if (content_count_users(c) > 1) {
370
		/* We are not the only user of 'c' so clone it. */
371
		struct content *clone = content_clone(c);
372
 
373
		if (clone == NULL)
374
			return NSERROR_NOMEM;
375
 
376
		entry = calloc(sizeof(struct hlcache_entry), 1);
377
 
378
		if (entry == NULL) {
379
			content_destroy(clone);
380
			return NSERROR_NOMEM;
381
		}
382
 
383
		if (content_add_user(clone,
384
				hlcache_content_callback, handle) == false) {
385
			content_destroy(clone);
386
			free(entry);
387
			return NSERROR_NOMEM;
388
		}
389
 
390
		content_remove_user(c, hlcache_content_callback, handle);
391
 
392
		entry->content = clone;
393
		handle->entry = entry;
394
		entry->prev = NULL;
395
		entry->next = hlcache->content_list;
396
		if (hlcache->content_list != NULL)
397
			hlcache->content_list->prev = entry;
398
		hlcache->content_list = entry;
399
 
400
		c = clone;
401
	}
402
 
403
	return content_abort(c);
404
}
405
 
406
/* See hlcache.h for documentation */
407
nserror hlcache_handle_replace_callback(hlcache_handle *handle,
408
		hlcache_handle_callback cb, void *pw)
409
{
410
	handle->cb = cb;
411
	handle->pw = pw;
412
 
413
	return NSERROR_OK;
414
}
415
 
416
nserror hlcache_handle_clone(hlcache_handle *handle, hlcache_handle **result)
417
{
418
	*result = NULL;
419
	return NSERROR_CLONE_FAILED;
420
}
421
 
422
/* See hlcache.h for documentation */
423
nsurl *hlcache_handle_get_url(const hlcache_handle *handle)
424
{
425
	nsurl *result = NULL;
426
 
427
	assert(handle != NULL);
428
 
429
	if (handle->entry != NULL) {
430
		result = content_get_url(handle->entry->content);
431
	} else {
432
		RING_ITERATE_START(struct hlcache_retrieval_ctx,
433
				   hlcache->retrieval_ctx_ring,
434
				   ictx) {
435
			if (ictx->handle == handle) {
436
				/* This is the nascent context for us */
437
				result = llcache_handle_get_url(ictx->llcache);
438
 
439
				/* And stop */
440
				RING_ITERATE_STOP(hlcache->retrieval_ctx_ring,
441
						ictx);
442
			}
443
		} RING_ITERATE_END(hlcache->retrieval_ctx_ring, ictx);
444
	}
445
 
446
	return result;
447
}
448
 
449
/******************************************************************************
450
 * High-level cache internals						      *
451
 ******************************************************************************/
452
 
453
/**
454
 * Attempt to clean the cache
455
 */
456
void hlcache_clean(void *ignored)
457
{
458
	hlcache_entry *entry, *next;
459
 
460
	for (entry = hlcache->content_list; entry != NULL; entry = next) {
461
		next = entry->next;
462
 
463
		if (entry->content == NULL)
464
			continue;
465
 
466
		if (content__get_status(entry->content) ==
467
				CONTENT_STATUS_LOADING)
468
			continue;
469
 
470
		if (content_count_users(entry->content) != 0)
471
			continue;
472
 
473
		/** \todo This is over-zealous: all unused contents
474
		 * will be immediately destroyed. Ideally, we want to
475
		 * purge all unused contents that are using stale
476
		 * source data, and enough fresh contents such that
477
		 * the cache fits in the configured cache size limit.
478
		 */
479
 
480
		/* Remove entry from cache */
481
		if (entry->prev == NULL)
482
			hlcache->content_list = entry->next;
483
		else
484
			entry->prev->next = entry->next;
485
 
486
		if (entry->next != NULL)
487
			entry->next->prev = entry->prev;
488
 
489
		/* Destroy content */
490
		content_destroy(entry->content);
491
 
492
		/* Destroy entry */
493
		free(entry);
494
	}
495
 
496
	/* Attempt to clean the llcache */
497
	llcache_clean();
498
 
499
	/* Re-schedule ourselves */
500
	schedule(hlcache->params.bg_clean_time / 10, hlcache_clean, NULL);
501
}
502
 
503
/**
504
 * Handler for low-level cache events
505
 *
506
 * \param handle  Handle for which event is issued
507
 * \param event	  Event data
508
 * \param pw	  Pointer to client-specific data
509
 * \return NSERROR_OK on success, appropriate error otherwise
510
 */
511
nserror hlcache_llcache_callback(llcache_handle *handle,
512
		const llcache_event *event, void *pw)
513
{
514
	hlcache_retrieval_ctx *ctx = pw;
515
	lwc_string *effective_type = NULL;
516
	nserror error;
5043 ashmew2 517
	/* LOG(("Asserting ctx->llcache == handle")); */
3584 sourcerer 518
	assert(ctx->llcache == handle);
5043 ashmew2 519
	/* LOG(("After Asserting ctx->llcache == handle")); */
3584 sourcerer 520
 
521
	switch (event->type) {
522
	case LLCACHE_EVENT_HAD_HEADERS:
5043 ashmew2 523
	  /* LOG(("LLCACHE_EVENT_HAD_HEADERS")); */
3584 sourcerer 524
		error = mimesniff_compute_effective_type(handle, NULL, 0,
525
				ctx->flags & HLCACHE_RETRIEVE_SNIFF_TYPE,
526
				ctx->accepted_types == CONTENT_IMAGE,
527
				&effective_type);
528
		if (error == NSERROR_OK || error == NSERROR_NOT_FOUND) {
529
			/* If the sniffer was successful or failed to find
530
			 * a Content-Type header when sniffing was
531
			 * prohibited, we must migrate the retrieval context. */
5043 ashmew2 532
		  /* LOG(("hlcache_migraet_ctx from LLCACHE_HAD_HEADERS")); */
3584 sourcerer 533
			error = hlcache_migrate_ctx(ctx, effective_type);
534
 
535
			if (effective_type != NULL)
536
				lwc_string_unref(effective_type);
537
		}
538
 
539
		/* No need to report that we need data:
540
		 * we'll get some anyway if there is any */
541
		if (error == NSERROR_NEED_DATA)
542
			error = NSERROR_OK;
543
 
544
		return error;
545
 
546
		break;
547
	case LLCACHE_EVENT_HAD_DATA:
5043 ashmew2 548
	  	  /* LOG(("LLCACHE_EVENT_HAD_DATA")); */
3584 sourcerer 549
		error = mimesniff_compute_effective_type(handle,
550
				event->data.data.buf, event->data.data.len,
551
				ctx->flags & HLCACHE_RETRIEVE_SNIFF_TYPE,
552
				ctx->accepted_types == CONTENT_IMAGE,
553
				&effective_type);
554
		if (error != NSERROR_OK) {
555
			assert(0 && "MIME sniff failed with data");
556
		}
5043 ashmew2 557
		/* LOG(("hlcache_migraet_ctx from LLCACHE_HAD_DATA")); */
3584 sourcerer 558
		error = hlcache_migrate_ctx(ctx, effective_type);
559
 
560
		lwc_string_unref(effective_type);
561
 
562
		return error;
563
 
564
		break;
565
	case LLCACHE_EVENT_DONE:
5043 ashmew2 566
	  	  /* LOG(("LLCACHE_EVENT_DONE")); */
3584 sourcerer 567
		/* DONE event before we could determine the effective MIME type.
568
		 */
569
		error = mimesniff_compute_effective_type(handle,
570
				NULL, 0, false, false, &effective_type);
571
		if (error == NSERROR_OK) {
5043 ashmew2 572
		  /* LOG(("hlcache_migrate_ctx in LLCACHE_EVENT_DONE")); */
3584 sourcerer 573
			error = hlcache_migrate_ctx(ctx, effective_type);
574
 
575
			lwc_string_unref(effective_type);
576
 
577
			return error;
578
		}
579
 
580
		if (ctx->handle->cb != NULL) {
581
			hlcache_event hlevent;
5043 ashmew2 582
 
3584 sourcerer 583
			hlevent.type = CONTENT_MSG_ERROR;
584
			hlevent.data.error = messages_get("BadType");
585
			ctx->handle->cb(ctx->handle, &hlevent, ctx->handle->pw);
586
		}
587
		break;
588
	case LLCACHE_EVENT_ERROR:
5043 ashmew2 589
	  /* LOG(("LLCACHE_EVENT_ERROR")); */
3584 sourcerer 590
		if (ctx->handle->cb != NULL) {
591
			hlcache_event hlevent;
592
 
593
			hlevent.type = CONTENT_MSG_ERROR;
594
			hlevent.data.error = event->data.msg;
595
 
596
			ctx->handle->cb(ctx->handle, &hlevent, ctx->handle->pw);
597
		}
5043 ashmew2 598
		break;
3584 sourcerer 599
	case LLCACHE_EVENT_PROGRESS:
5043 ashmew2 600
	  	  /* LOG(("LLCACHE_EVENT_PROGRESS")); */
3584 sourcerer 601
		break;
602
	}
5043 ashmew2 603
	/* LOG(("Returning OK from hlcache_llcache_callback")); */
3584 sourcerer 604
	return NSERROR_OK;
605
}
606
 
607
/**
608
 * Migrate a retrieval context into its final destination content
609
 *
610
 * \param ctx             Context to migrate
611
 * \param effective_type  The effective MIME type of the content, or NULL
612
 * \return NSERROR_OK on success,
613
 *         NSERROR_NEED_DATA on success where data is needed,
614
 *         appropriate error otherwise
615
 */
616
nserror hlcache_migrate_ctx(hlcache_retrieval_ctx *ctx,
617
		lwc_string *effective_type)
618
{
619
	content_type type = CONTENT_NONE;
620
	nserror error = NSERROR_OK;
621
 
5043 ashmew2 622
	ctx->migrate_target = true;
3584 sourcerer 623
 
624
	if (effective_type != NULL &&
625
			hlcache_type_is_acceptable(effective_type,
626
			ctx->accepted_types, &type)) {
627
		error = hlcache_find_content(ctx, effective_type);
628
		if (error != NSERROR_OK && error != NSERROR_NEED_DATA) {
629
			if (ctx->handle->cb != NULL) {
630
				hlcache_event hlevent;
631
 
632
				hlevent.type = CONTENT_MSG_ERROR;
633
				hlevent.data.error = messages_get("MiscError");
634
 
635
				ctx->handle->cb(ctx->handle, &hlevent,
636
						ctx->handle->pw);
637
			}
638
 
639
			llcache_handle_abort(ctx->llcache);
640
			llcache_handle_release(ctx->llcache);
641
		}
642
	} else if (type == CONTENT_NONE &&
643
			(ctx->flags & HLCACHE_RETRIEVE_MAY_DOWNLOAD)) {
644
		/* Unknown type, and we can download, so convert */
645
		llcache_handle_force_stream(ctx->llcache);
646
 
647
		if (ctx->handle->cb != NULL) {
648
			hlcache_event hlevent;
649
 
650
			hlevent.type = CONTENT_MSG_DOWNLOAD;
651
			hlevent.data.download = ctx->llcache;
652
 
653
			ctx->handle->cb(ctx->handle, &hlevent,
654
					ctx->handle->pw);
655
		}
656
 
657
		/* Ensure caller knows we need data */
658
		error = NSERROR_NEED_DATA;
659
	} else {
660
		/* Unacceptable type: report error */
661
		if (ctx->handle->cb != NULL) {
662
			hlcache_event hlevent;
5043 ashmew2 663
			/* LOG(("Unacceptable Type! CONTENT_MSG_ERROR set")); */
3584 sourcerer 664
			hlevent.type = CONTENT_MSG_ERROR;
665
			hlevent.data.error = messages_get("UnacceptableType");
666
 
667
			ctx->handle->cb(ctx->handle, &hlevent,
668
					ctx->handle->pw);
669
		}
670
 
671
		llcache_handle_abort(ctx->llcache);
672
		llcache_handle_release(ctx->llcache);
673
	}
674
 
675
	ctx->migrate_target = false;
676
 
677
	/* No longer require retrieval context */
678
	RING_REMOVE(hlcache->retrieval_ctx_ring, ctx);
679
	free((char *) ctx->child.charset);
680
	free(ctx);
681
 
682
	return error;
683
}
684
 
685
/**
686
 * Determine if the specified MIME type is acceptable
687
 *
688
 * \param mime_type       MIME type to consider
689
 * \param accepted_types  Array of acceptable types, or NULL for any
690
 * \param computed_type	  Pointer to location to receive computed type of object
691
 * \return True if the type is acceptable, false otherwise
692
 */
693
bool hlcache_type_is_acceptable(lwc_string *mime_type,
694
		content_type accepted_types, content_type *computed_type)
695
{
696
	content_type type;
697
 
698
	type = content_factory_type_from_mime_type(mime_type);
699
 
700
	*computed_type = type;
701
 
702
	return ((accepted_types & type) != 0);
703
}
704
 
705
/**
706
 * Find a content for the high-level cache handle
707
 *
708
 * \param ctx             High-level cache retrieval context
709
 * \param effective_type  Effective MIME type of content
710
 * \return NSERROR_OK on success,
711
 *         NSERROR_NEED_DATA on success where data is needed,
712
 *         appropriate error otherwise
713
 *
714
 * \pre handle::state == HLCACHE_HANDLE_NEW
715
 * \pre Headers must have been received for associated low-level handle
716
 * \post Low-level handle is either released, or associated with new content
717
 * \post High-level handle is registered with content
718
 */
719
nserror hlcache_find_content(hlcache_retrieval_ctx *ctx,
720
		lwc_string *effective_type)
721
{
722
	hlcache_entry *entry;
723
	hlcache_event event;
724
	nserror error = NSERROR_OK;
725
 
726
	/* Search list of cached contents for a suitable one */
727
	for (entry = hlcache->content_list; entry != NULL; entry = entry->next) {
728
		hlcache_handle entry_handle = { entry, NULL, NULL };
729
		const llcache_handle *entry_llcache;
730
 
731
		if (entry->content == NULL)
732
			continue;
733
 
734
		/* Ignore contents in the error state */
735
		if (content_get_status(&entry_handle) == CONTENT_STATUS_ERROR)
736
			continue;
737
 
738
		/* Ensure that content is shareable */
739
		if (content_is_shareable(entry->content) == false)
740
			continue;
741
 
742
		/* Ensure that quirks mode is acceptable */
743
		if (content_matches_quirks(entry->content,
744
				ctx->child.quirks) == false)
745
			continue;
746
 
747
		/* Ensure that content uses same low-level object as
748
		 * low-level handle */
749
		entry_llcache = content_get_llcache_handle(entry->content);
750
 
751
		if (llcache_handle_references_same_object(entry_llcache,
752
				ctx->llcache))
753
			break;
754
	}
755
 
756
	if (entry == NULL) {
757
		/* No existing entry, so need to create one */
758
		entry = malloc(sizeof(hlcache_entry));
759
		if (entry == NULL)
760
			return NSERROR_NOMEM;
761
 
762
		/* Create content using llhandle */
763
		entry->content = content_factory_create_content(ctx->llcache,
764
				ctx->child.charset, ctx->child.quirks,
765
				effective_type);
766
		if (entry->content == NULL) {
767
			free(entry);
768
			return NSERROR_NOMEM;
769
		}
770
 
771
		/* Insert into cache */
772
		entry->prev = NULL;
773
		entry->next = hlcache->content_list;
774
		if (hlcache->content_list != NULL)
775
			hlcache->content_list->prev = entry;
776
		hlcache->content_list = entry;
777
 
778
		/* Signal to caller that we created a content */
779
		error = NSERROR_NEED_DATA;
780
 
781
		hlcache->miss_count++;
782
	} else {
783
		/* Found a suitable content: no longer need low-level handle */
784
		llcache_handle_release(ctx->llcache);
785
		hlcache->hit_count++;
786
	}
787
 
788
	/* Associate handle with content */
789
	if (content_add_user(entry->content,
790
			hlcache_content_callback, ctx->handle) == false)
791
		return NSERROR_NOMEM;
792
 
793
	/* Associate cache entry with handle */
794
	ctx->handle->entry = entry;
795
 
796
	/* Catch handle up with state of content */
797
	if (ctx->handle->cb != NULL) {
798
		content_status status = content_get_status(ctx->handle);
799
 
800
		if (status == CONTENT_STATUS_LOADING) {
801
			event.type = CONTENT_MSG_LOADING;
802
			ctx->handle->cb(ctx->handle, &event, ctx->handle->pw);
803
		} else if (status == CONTENT_STATUS_READY) {
804
			event.type = CONTENT_MSG_LOADING;
805
			ctx->handle->cb(ctx->handle, &event, ctx->handle->pw);
806
 
807
			if (ctx->handle->cb != NULL) {
808
				event.type = CONTENT_MSG_READY;
809
				ctx->handle->cb(ctx->handle, &event,
810
						ctx->handle->pw);
811
			}
812
		} else if (status == CONTENT_STATUS_DONE) {
813
			event.type = CONTENT_MSG_LOADING;
814
			ctx->handle->cb(ctx->handle, &event, ctx->handle->pw);
815
 
816
			if (ctx->handle->cb != NULL) {
817
				event.type = CONTENT_MSG_READY;
818
				ctx->handle->cb(ctx->handle, &event,
819
						ctx->handle->pw);
820
			}
821
 
822
			if (ctx->handle->cb != NULL) {
5043 ashmew2 823
			  /* LOG(("Calling with CONTENT_MSG_DONE")); */
3584 sourcerer 824
				event.type = CONTENT_MSG_DONE;
825
				ctx->handle->cb(ctx->handle, &event,
826
						ctx->handle->pw);
827
			}
828
		}
829
	}
5043 ashmew2 830
	/* LOG(("Returning from "));  */
3584 sourcerer 831
	return error;
832
}
833
 
834
/**
835
 * Veneer between content callback API and hlcache callback API
836
 *
837
 * \param c	Content to emit message for
838
 * \param msg	Message to emit
839
 * \param data	Data for message
840
 * \param pw	Pointer to private data (hlcache_handle)
841
 */
842
void hlcache_content_callback(struct content *c, content_msg msg,
843
		union content_msg_data data, void *pw)
844
{
845
	hlcache_handle *handle = pw;
846
	hlcache_event event;
847
	nserror error = NSERROR_OK;
848
 
849
	event.type = msg;
850
	event.data = data;
851
 
852
	if (handle->cb != NULL)
853
		error = handle->cb(handle, &event, handle->pw);
854
 
855
	if (error != NSERROR_OK)
856
		LOG(("Error in callback: %d", error));
857
}