Added UTF-8 char length lookup table
[elinks.git] / src / document / document.c
blob9765197a0db472a19b641716b6522e96a874ac75
1 /* The document base functionality */
3 #ifdef HAVE_CONFIG_H
4 #include "config.h"
5 #endif
7 #include <errno.h>
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <string.h>
12 #include "elinks.h"
14 #include "cache/cache.h"
15 #include "config/options.h"
16 #include "document/document.h"
17 #include "document/forms.h"
18 #include "document/html/frames.h"
19 #include "document/html/parser.h"
20 #include "document/html/parser/parse.h"
21 #include "document/html/renderer.h"
22 #include "document/options.h"
23 #include "document/refresh.h"
24 #include "main/module.h"
25 #include "main/object.h"
26 #include "protocol/uri.h"
27 #include "terminal/draw.h"
28 #include "util/color.h"
29 #include "util/error.h"
30 #include "util/lists.h"
31 #include "util/memory.h"
32 #include "util/string.h"
33 #include "viewer/text/link.h"
36 static INIT_LIST_HEAD(format_cache);
38 struct document *
39 init_document(struct cache_entry *cached, struct document_options *options)
41 struct document *document = mem_calloc(1, sizeof(*document));
43 if (!document) return NULL;
45 document->uri = get_uri_reference(cached->uri);
47 object_lock(cached);
48 document->id = cached->id;
50 init_list(document->forms);
51 init_list(document->tags);
52 init_list(document->nodes);
54 #ifdef CONFIG_ECMASCRIPT
55 init_list(document->onload_snippets);
56 #endif
58 object_nolock(document, "document");
59 object_lock(document);
61 copy_opt(&document->options, options);
63 add_to_list(format_cache, document);
65 return document;
68 static void
69 free_frameset_desc(struct frameset_desc *frameset_desc)
71 int i;
73 for (i = 0; i < frameset_desc->n; i++) {
74 struct frame_desc *frame_desc = &frameset_desc->frame_desc[i];
76 if (frame_desc->subframe)
77 free_frameset_desc(frame_desc->subframe);
78 mem_free_if(frame_desc->name);
79 if (frame_desc->uri)
80 done_uri(frame_desc->uri);
83 mem_free(frameset_desc);
86 void
87 done_link_members(struct link *link)
89 if (link->event_hooks) {
90 struct script_event_hook *evhook, *safety;
92 foreachsafe (evhook, safety, *link->event_hooks) {
93 mem_free_if(evhook->src);
94 mem_free(evhook);
96 mem_free(link->event_hooks);
98 mem_free_if(get_link_name(link));
99 mem_free_if(link->where);
100 mem_free_if(link->target);
101 mem_free_if(link->title);
102 mem_free_if(link->where_img);
103 mem_free_if(link->points);
106 void
107 done_document(struct document *document)
109 struct cache_entry *cached;
111 assert(document);
112 if_assert_failed return;
114 assertm(!is_object_used(document), "Attempt to free locked formatted data.");
115 if_assert_failed return;
117 cached = find_in_cache(document->uri);
118 if (!cached)
119 INTERNAL("no cache entry for document");
120 else
121 object_unlock(cached);
123 if (document->uri) done_uri(document->uri);
124 mem_free_if(document->title);
125 if (document->frame_desc) free_frameset_desc(document->frame_desc);
126 if (document->refresh) done_document_refresh(document->refresh);
128 if (document->links) {
129 int pos;
131 for (pos = 0; pos < document->nlinks; pos++)
132 done_link_members(&document->links[pos]);
134 mem_free(document->links);
137 if (document->data) {
138 int pos;
140 for (pos = 0; pos < document->height; pos++)
141 mem_free_if(document->data[pos].chars);
143 mem_free(document->data);
146 mem_free_if(document->lines1);
147 mem_free_if(document->lines2);
148 done_document_options(&document->options);
150 while (!list_empty(document->forms)) {
151 done_form(document->forms.next);
154 #ifdef CONFIG_CSS
155 free_uri_list(&document->css_imports);
156 #endif
157 #ifdef CONFIG_ECMASCRIPT
158 free_string_list(&document->onload_snippets);
159 free_uri_list(&document->ecmascript_imports);
160 #endif
162 free_list(document->tags);
163 free_list(document->nodes);
165 mem_free_if(document->search);
166 mem_free_if(document->slines1);
167 mem_free_if(document->slines2);
169 del_from_list(document);
170 mem_free(document);
173 void
174 release_document(struct document *document)
176 assert(document);
177 if_assert_failed return;
179 if (document->refresh) kill_document_refresh(document->refresh);
180 object_unlock(document);
181 move_to_top_of_list(format_cache, document);
184 /* Formatted document cache management */
186 /* ECMAScript doesn't like anything like CSS since it doesn't modify the
187 * formatted document (yet). */
189 #if CONFIG_CSS
190 unsigned long
191 get_document_css_magic(struct document *document)
193 unsigned long css_magic = 0;
194 struct uri *uri;
195 int index;
197 foreach_uri (uri, index, &document->css_imports) {
198 struct cache_entry *cached = find_in_cache(uri);
200 if (cached) css_magic += cached->id + cached->data_size;
203 return css_magic;
206 #define check_document_css_magic(document) \
207 ((document)->css_magic == get_document_css_magic(document))
208 #else
209 #define check_document_css_magic(document) 1
210 #endif
212 void
213 update_cached_document_options(void)
215 struct document *document;
216 struct active_link_options active_link;
218 memset(&active_link, 0, sizeof(active_link)); /* Safer. */
219 active_link.fg = get_opt_color("document.browse.links.active_link.colors.text");
220 active_link.bg = get_opt_color("document.browse.links.active_link.colors.background");
221 active_link.color = get_opt_bool("document.browse.links.active_link.enable_color");
222 active_link.invert = get_opt_bool("document.browse.links.active_link.invert");
223 active_link.underline = get_opt_bool("document.browse.links.active_link.underline");
224 active_link.bold = get_opt_bool("document.browse.links.active_link.bold");
226 foreach (document, format_cache) {
227 copy_struct(&document->options.active_link, &active_link);
231 struct document *
232 get_cached_document(struct cache_entry *cached, struct document_options *options)
234 struct document *document, *next;
236 foreachsafe (document, next, format_cache) {
237 if (!compare_uri(document->uri, cached->uri, 0)
238 || compare_opt(&document->options, options))
239 continue;
241 if (options->no_cache
242 || cached->id != document->id
243 || !check_document_css_magic(document)) {
244 if (!is_object_used(document)) {
245 done_document(document);
247 continue;
250 /* Reactivate */
251 move_to_top_of_list(format_cache, document);
253 object_lock(document);
255 return document;
258 return NULL;
261 void
262 shrink_format_cache(int whole)
264 struct document *document, *next;
265 int format_cache_size = get_opt_int("document.cache.format.size");
266 int format_cache_entries = 0;
268 foreachsafe (document, next, format_cache) {
269 struct cache_entry *cached;
271 if (is_object_used(document)) continue;
273 format_cache_entries++;
275 /* Destroy obsolete renderer documents which are already
276 * out-of-sync. */
277 cached = find_in_cache(document->uri);
278 assertm(cached, "cached formatted document has no cache entry");
279 if (cached->id == document->id) continue;
281 done_document(document);
282 format_cache_entries--;
285 assertm(format_cache_entries >= 0, "format_cache_entries underflow on entry");
286 if_assert_failed format_cache_entries = 0;
288 foreachback (document, format_cache) {
289 if (is_object_used(document)) continue;
291 /* If we are not purging the whole format cache, stop
292 * once we are below the maximum number of entries. */
293 if (!whole && format_cache_entries <= format_cache_size)
294 break;
296 /* Jump back to already processed entry (or list head), and let
297 * the foreachback move it to the next entry to go. */
298 document = document->next;
299 done_document(document->prev);
300 format_cache_entries--;
303 assertm(format_cache_entries >= 0, "format_cache_entries underflow");
304 if_assert_failed format_cache_entries = 0;
308 get_format_cache_size(void)
310 return list_size(&format_cache);
314 get_format_cache_used_count(void)
316 struct document *document;
317 int i = 0;
319 foreach (document, format_cache)
320 i += is_object_used(document);
321 return i;
325 get_format_cache_refresh_count(void)
327 struct document *document;
328 int i = 0;
330 foreach (document, format_cache)
331 if (document->refresh
332 && document->refresh->timer != TIMER_ID_UNDEF)
333 i++;
334 return i;
337 static void
338 init_documents(struct module *module)
340 init_tags_lookup();
343 static void
344 done_documents(struct module *module)
346 free_tags_lookup();
347 free_table_cache();
350 struct module document_module = struct_module(
351 /* name: */ "Document",
352 /* options: */ NULL,
353 /* hooks: */ NULL,
354 /* submodules: */ NULL,
355 /* data: */ NULL,
356 /* init: */ init_documents,
357 /* done: */ done_documents