1 /* The document base functionality */
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
);
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
);
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
);
58 object_nolock(document
, "document");
59 object_lock(document
);
61 copy_opt(&document
->options
, options
);
63 add_to_list(format_cache
, document
);
69 free_frameset_desc(struct frameset_desc
*frameset_desc
)
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
);
80 done_uri(frame_desc
->uri
);
83 mem_free(frameset_desc
);
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
);
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
);
107 done_document(struct document
*document
)
109 struct cache_entry
*cached
;
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
);
119 INTERNAL("no cache entry for document");
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
) {
131 for (pos
= 0; pos
< document
->nlinks
; pos
++)
132 done_link_members(&document
->links
[pos
]);
134 mem_free(document
->links
);
137 if (document
->data
) {
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
);
155 free_uri_list(&document
->css_imports
);
157 #ifdef CONFIG_ECMASCRIPT
158 free_string_list(&document
->onload_snippets
);
159 free_uri_list(&document
->ecmascript_imports
);
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
);
174 release_document(struct document
*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). */
191 get_document_css_magic(struct document
*document
)
193 unsigned long css_magic
= 0;
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
;
206 #define check_document_css_magic(document) \
207 ((document)->css_magic == get_document_css_magic(document))
209 #define check_document_css_magic(document) 1
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
);
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
))
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
);
251 move_to_top_of_list(format_cache
, document
);
253 object_lock(document
);
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
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
)
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
;
319 foreach (document
, format_cache
)
320 i
+= is_object_used(document
);
325 get_format_cache_refresh_count(void)
327 struct document
*document
;
330 foreach (document
, format_cache
)
331 if (document
->refresh
332 && document
->refresh
->timer
!= TIMER_ID_UNDEF
)
338 init_documents(struct module
*module
)
344 done_documents(struct module
*module
)
350 struct module document_module
= struct_module(
351 /* name: */ "Document",
354 /* submodules: */ NULL
,
356 /* init: */ init_documents
,
357 /* done: */ done_documents