1 /** Generic renderer multiplexer
15 #include "cache/cache.h"
16 #include "config/options.h"
17 #include "document/document.h"
18 #include "document/dom/renderer.h"
19 #include "document/html/frames.h"
20 #include "document/html/renderer.h"
21 #include "document/plain/renderer.h"
22 #include "document/renderer.h"
23 #include "document/view.h"
24 #include "ecmascript/ecmascript.h"
25 #include "encoding/encoding.h"
26 #include "intl/charsets.h"
27 #include "main/main.h"
28 #include "main/object.h"
29 #include "protocol/header.h"
30 #include "protocol/protocol.h"
31 #include "protocol/uri.h"
32 #include "session/location.h"
33 #include "session/session.h"
34 #include "terminal/terminal.h"
35 #include "terminal/window.h"
36 #include "util/error.h"
37 #include "util/memory.h"
38 #include "util/string.h"
39 #include "viewer/text/form.h"
40 #include "viewer/text/view.h"
41 #include "viewer/text/vs.h"
44 #ifdef CONFIG_ECMASCRIPT
45 /** @todo XXX: This function is de facto obsolete, since we do not need to copy
46 * snippets around anymore (we process them in one go after the document is
47 * loaded; gradual processing was practically impossible because the snippets
48 * could reorder randomly during the loading - consider i.e.
49 * @<body onLoad>@<script>@</body>: first just @<body> is loaded, but then the
50 * rest of the document is loaded and @<script> gets before @<body>; do not even
51 * imagine the trouble with rewritten (through scripting hooks) documents;
52 * besides, implementing document.write() will be much simpler).
53 * But I want to take no risk by reworking that now. --pasky */
55 add_snippets(struct ecmascript_interpreter
*interpreter
,
56 LIST_OF(struct string_list_item
) *doc_snippets
,
57 LIST_OF(struct string_list_item
) *queued_snippets
)
59 struct string_list_item
*doc_current
= doc_snippets
->next
;
62 if (list_empty(*queued_snippets
) && interpreter
->vs
->doc_view
->session
)
63 unset_led_value(interpreter
->vs
->doc_view
->session
->status
.ecmascript_led
);
66 if (list_empty(*doc_snippets
)
67 || !get_opt_bool("ecmascript.enable", NULL
))
70 /* We do this all only once per view_state now. */
71 if (!list_empty(*queued_snippets
)) {
72 /* So if we already did it, we shouldn't need to do it again.
73 * This is the case of moving around in history - we have all
74 * what happenned recorded in the view_state and needn't bother
78 struct string_list_item
*iterator
= queued_snippets
->next
;
80 while (iterator
!= (struct string_list_item
*) queued_snippets
) {
81 if (doc_current
== (struct string_list_item
*) doc_snippets
) {
82 INTERNAL("add_snippets(): doc_snippets shorter than queued_snippets!");
86 DBG("Comparing snippets\n%.*s\n###### vs #####\n%.*s\n #####",
87 iterator
->string
.length
, iterator
->string
.source
,
88 doc_current
->string
.length
, doc_current
->string
.source
);
90 assert(!strlcmp(iterator
->string
.source
,
91 iterator
->string
.length
,
92 doc_current
->string
.source
,
93 doc_current
->string
.length
));
95 doc_current
= doc_current
->next
;
96 iterator
= iterator
->next
;
103 for (; doc_current
!= (struct string_list_item
*) doc_snippets
;
104 doc_current
= doc_current
->next
) {
105 add_to_string_list(queued_snippets
, doc_current
->string
.source
,
106 doc_current
->string
.length
);
108 DBG("Adding snippet\n%.*s\n #####",
109 doc_current
->string
.length
,
110 doc_current
->string
.source
);
116 process_snippets(struct ecmascript_interpreter
*interpreter
,
117 LIST_OF(struct string_list_item
) *snippets
,
118 struct string_list_item
**current
)
121 *current
= snippets
->next
;
122 for (; *current
!= (struct string_list_item
*) snippets
;
123 (*current
) = (*current
)->next
) {
124 struct string
*string
= &(*current
)->string
;
125 unsigned char *uristring
;
127 struct cache_entry
*cached
;
128 struct fragment
*fragment
;
130 if (string
->length
== 0)
133 if (*string
->source
!= '^') {
134 /* Evaluate <script>code</script> snippet */
135 ecmascript_eval(interpreter
, string
, NULL
);
139 /* Eval external <script src="reference"></script> snippet */
140 uristring
= string
->source
+ 1;
141 if (!*uristring
) continue;
143 uri
= get_uri(uristring
, URI_BASE
);
146 cached
= get_redirected_cache_entry(uri
);
150 /* At this time (!gradual_rerendering), we should've
151 * already retrieved this though. So it must've been
152 * that it went away because unused and the cache was
153 * already too full. */
155 /* Disabled because gradual rerendering can be triggered
156 * by numerous events other than a ecmascript reference
157 * completing like the original document and CSS. Problem
158 * is that we should never continue this loop but rather
159 * break out if that is the case. Somehow we need to
160 * be able to derive URI loading problems at this point
161 * or maybe remove reference snippets if they fail to load.
163 * This FIFO queue handling should be used for also CSS
164 * imports so it would be cool if it could be general
165 * enough for that. Using it for frames with the FIFOing
166 * disabled probably wouldn't hurt either.
168 * To top this thing off it would be nice if it also
169 * handled dependency tracking between references so that
170 * CSS documents will not disappear from the cache
171 * before all referencing HTML documents has been deleted
174 * Reported as bug 533. */
175 /* Pasky's explanation: If we get the doc in a single
176 * shot, before calling draw_formatted() we didn't have
177 * anything additional queued for loading and the cache
178 * entry was already loaded, so we didn't get
179 * gradual_loading set. But then while parsing the
180 * document we got some external references and trying
181 * to process them right now. Boom.
183 * The obvious solution would be to always call
184 * draw_formatted() with gradual_loading in
185 * doc_loading_callback() and if we are sure the
186 * loading is really over, call it one more time
187 * without gradual_loading set. I'm not sure about
188 * the implications though so I won't do it before
190 ERROR("The script of %s was lost in too full a cache!",
196 fragment
= get_cache_fragment(cached
);
198 struct string code
= INIT_STRING(fragment
->data
, fragment
->length
);
200 ecmascript_eval(interpreter
, &code
, NULL
);
207 render_encoded_document(struct cache_entry
*cached
, struct document
*document
)
209 struct uri
*uri
= cached
->uri
;
210 enum stream_encoding encoding
= ENCODING_NONE
;
211 struct fragment
*fragment
= get_cache_fragment(cached
);
212 struct string buffer
= INIT_STRING("", 0);
214 /* Even empty documents have to be rendered so that info in the protocol
215 * header, such as refresh info, get processed. (bug 625) */
217 buffer
.source
= fragment
->data
;
218 buffer
.length
= fragment
->length
;
221 if (uri
->protocol
!= PROTOCOL_FILE
) {
222 unsigned char *extension
= get_extension_from_uri(uri
);
225 encoding
= guess_encoding(extension
);
229 if (encoding
!= ENCODING_NONE
) {
231 unsigned char *source
;
232 struct stream_encoded
*stream
= open_encoded(-1, encoding
);
235 encoding
= ENCODING_NONE
;
237 source
= decode_encoded_buffer(stream
, encoding
, buffer
.source
,
238 buffer
.length
, &length
);
239 close_encoded(stream
);
242 buffer
.source
= source
;
243 buffer
.length
= length
;
245 encoding
= ENCODING_NONE
;
251 if (document
->options
.plain
) {
253 if (cached
->content_type
254 && (!c_strcasecmp("text/html", cached
->content_type
)
255 || !c_strcasecmp("application/xhtml+xml", cached
->content_type
)
256 || !c_strcasecmp("application/docbook+xml", cached
->content_type
)
257 || !c_strcasecmp("application/rss+xml", cached
->content_type
)
258 || !c_strcasecmp("application/xbel+xml", cached
->content_type
)
259 || !c_strcasecmp("application/x-xbel", cached
->content_type
)
260 || !c_strcasecmp("application/xbel", cached
->content_type
)))
261 render_dom_document(cached
, document
, &buffer
);
264 render_plain_document(cached
, document
, &buffer
);
268 if (cached
->content_type
269 && (!c_strlcasecmp("application/rss+xml", 19, cached
->content_type
, -1)))
270 render_dom_document(cached
, document
, &buffer
);
273 render_html_document(cached
, document
, &buffer
);
276 if (encoding
!= ENCODING_NONE
) {
277 done_string(&buffer
);
282 render_document(struct view_state
*vs
, struct document_view
*doc_view
,
283 struct document_options
*options
)
286 struct document
*document
;
287 struct cache_entry
*cached
;
289 assert(vs
&& doc_view
&& options
);
290 if_assert_failed
return;
293 DBG("(Re%u)Rendering %s on doc_view %p [%s] while attaching it to %p",
294 options
->gradual_rerendering
, struri(vs
->uri
),
295 doc_view
, doc_view
->name
, vs
);
298 name
= doc_view
->name
;
299 doc_view
->name
= NULL
;
301 detach_formatted(doc_view
);
303 doc_view
->name
= name
;
305 doc_view
->last_x
= doc_view
->last_y
= -1;
308 /* This is a nice idea, but doesn't always work: in particular when
309 * there's a frame name conflict. You loaded something to the vs'
310 * frame, but later something tried to get loaded to a frame with
311 * the same name and we got back this frame again, so we are now
312 * overriding the original document with a cuckoo. This assert()ion
313 * should be re-enabled when we start to get this right (which is
314 * very complex, but someone should rewrite the frames support
315 * anyway). --pasky */
316 assert(!vs
->doc_view
);
319 /* It will be still detached, no worries - hopefully it still
320 * resides in ses->scrn_frames. */
321 assert(vs
->doc_view
->vs
== vs
);
322 vs
->doc_view
->used
= 0; /* A bit risky, but... */
323 vs
->doc_view
->vs
= NULL
;
325 #ifdef CONFIG_ECMASCRIPT
326 vs
->ecmascript_fragile
= 1; /* And is this good? ;-) */
330 vs
->doc_view
= doc_view
;
332 cached
= find_in_cache(vs
->uri
);
334 INTERNAL("document %s to format not found", struri(vs
->uri
));
338 document
= get_cached_document(cached
, options
);
340 doc_view
->document
= document
;
342 document
= init_document(cached
, options
);
343 if (!document
) return;
344 doc_view
->document
= document
;
346 if (doc_view
->session
347 && doc_view
->session
->reloadlevel
> CACHE_MODE_NORMAL
)
348 for (; vs
->form_info_len
> 0; vs
->form_info_len
--)
349 done_form_state(&vs
->form_info
[vs
->form_info_len
- 1]);
353 render_encoded_document(cached
, document
);
354 sort_links(document
);
355 if (!document
->title
) {
356 enum uri_component components
;
358 if (document
->uri
->protocol
== PROTOCOL_FILE
) {
359 components
= URI_PATH
;
361 components
= URI_PUBLIC
;
364 document
->title
= get_uri_string(document
->uri
, components
);
365 if (document
->title
) {
367 if (doc_view
->document
->options
.utf8
)
368 decode_uri(document
->title
);
370 #endif /* CONFIG_UTF8 */
371 decode_uri_for_display(document
->title
);
376 document
->css_magic
= get_document_css_magic(document
);
379 document
->images_magic
= get_document_images_magic(document
);
382 #ifdef CONFIG_ECMASCRIPT
383 if (!vs
->ecmascript_fragile
)
384 assert(vs
->ecmascript
);
385 if (!options
->gradual_rerendering
) {
386 /* We also reset the state if the underlying document changed
387 * from the last time we did the snippets. This may be
388 * triggered i.e. when redrawing a document which has been
389 * reloaded in a different tab meanwhile (OTOH we don't want
390 * to reset the state if we are redrawing a document we have
391 * already drawn before).
393 * (vs->ecmascript->onload_snippets_owner) check may be
394 * superfluous since we should always have
395 * vs->ecmascript_fragile set in those cases; that's why we
396 * don't ever bother to re-zero it if we are suddenly doing
397 * gradual rendering again.
399 * XXX: What happens if a document is still loading in the
400 * other tab when we press ^L here? */
401 if (vs
->ecmascript_fragile
403 && vs
->ecmascript
->onload_snippets_cache_id
404 && document
->cache_id
!= vs
->ecmascript
->onload_snippets_cache_id
))
405 ecmascript_reset_state(vs
);
406 /* If ecmascript_reset_state cannot construct a new
407 * ECMAScript interpreter, it sets vs->ecmascript =
408 * NULL and vs->ecmascript_fragile = 1. */
409 if (vs
->ecmascript
) {
410 vs
->ecmascript
->onload_snippets_cache_id
= document
->cache_id
;
412 /* Passing of the onload_snippets pointers
413 * gives *_snippets() some feeling of
414 * universality, shall we ever get any other
416 add_snippets(vs
->ecmascript
,
417 &document
->onload_snippets
,
418 &vs
->ecmascript
->onload_snippets
);
419 process_snippets(vs
->ecmascript
,
420 &vs
->ecmascript
->onload_snippets
,
421 &vs
->ecmascript
->current_onload_snippet
);
426 /* If we do not care about the height and width of the document
427 * just use the setup values. */
429 copy_box(&doc_view
->box
, &document
->options
.box
);
431 if (!document
->options
.needs_width
)
432 doc_view
->box
.width
= options
->box
.width
;
434 if (!document
->options
.needs_height
)
435 doc_view
->box
.height
= options
->box
.height
;
440 render_document_frames(struct session
*ses
, int no_cache
)
442 struct document_options doc_opts
;
443 struct document_view
*doc_view
;
444 struct document_view
*current_doc_view
= NULL
;
445 struct view_state
*vs
= NULL
;
447 if (!ses
->doc_view
) {
448 ses
->doc_view
= mem_calloc(1, sizeof(*ses
->doc_view
));
449 if (!ses
->doc_view
) return;
450 ses
->doc_view
->session
= ses
;
451 ses
->doc_view
->search_word
= &ses
->search_word
;
454 if (have_location(ses
)) vs
= &cur_loc(ses
)->vs
;
456 init_document_options(ses
, &doc_opts
);
458 set_box(&doc_opts
.box
, 0, 0,
459 ses
->tab
->term
->width
, ses
->tab
->term
->height
);
461 if (ses
->status
.show_title_bar
) {
463 doc_opts
.box
.height
--;
465 if (ses
->status
.show_status_bar
) doc_opts
.box
.height
--;
466 if (ses
->status
.show_tabs_bar
) {
467 doc_opts
.box
.height
--;
468 if (ses
->status
.show_tabs_bar_at_top
) doc_opts
.box
.y
++;
471 doc_opts
.color_mode
= get_opt_int_tree(ses
->tab
->term
->spec
, "colors",
473 if (!get_opt_bool_tree(ses
->tab
->term
->spec
, "underline", NULL
))
474 doc_opts
.color_flags
|= COLOR_ENHANCE_UNDERLINE
;
476 doc_opts
.cp
= get_terminal_codepage(ses
->tab
->term
);
477 doc_opts
.no_cache
= no_cache
& 1;
478 doc_opts
.gradual_rerendering
= !!(no_cache
& 2);
481 if (vs
->plain
< 0) vs
->plain
= 0;
482 doc_opts
.plain
= vs
->plain
;
483 doc_opts
.wrap
= vs
->wrap
;
488 foreach (doc_view
, ses
->scrn_frames
) doc_view
->used
= 0;
490 if (vs
) render_document(vs
, ses
->doc_view
, &doc_opts
);
492 if (document_has_frames(ses
->doc_view
->document
)) {
493 current_doc_view
= current_frame(ses
);
494 format_frames(ses
, ses
->doc_view
->document
->frame_desc
, &doc_opts
, 0);
497 foreach (doc_view
, ses
->scrn_frames
) {
498 struct document_view
*prev_doc_view
= doc_view
->prev
;
500 if (doc_view
->used
) continue;
502 detach_formatted(doc_view
);
503 del_from_list(doc_view
);
505 doc_view
= prev_doc_view
;
508 if (current_doc_view
) {
511 foreach (doc_view
, ses
->scrn_frames
) {
512 if (document_has_frames(doc_view
->document
)) continue;
513 if (doc_view
== current_doc_view
) {
514 cur_loc(ses
)->vs
.current_link
= n
;
522 /* comparison function for qsort() */
524 comp_links(const void *v1
, const void *v2
)
526 const struct link
*l1
= v1
, *l2
= v2
;
529 if_assert_failed
return 0;
530 return (l1
->number
- l2
->number
);
534 sort_links(struct document
*document
)
539 if_assert_failed
return;
540 if (!document
->nlinks
) return;
542 if (document
->links_sorted
) return;
543 assert(document
->links
);
544 if_assert_failed
return;
546 qsort(document
->links
, document
->nlinks
, sizeof(*document
->links
),
549 if (!document
->height
) return;
551 mem_free_if(document
->lines1
);
552 document
->lines1
= mem_calloc(document
->height
, sizeof(*document
->lines1
));
553 mem_free_if(document
->lines2
);
554 if (!document
->lines1
) return;
555 document
->lines2
= mem_calloc(document
->height
, sizeof(*document
->lines2
));
556 if (!document
->lines2
) {
557 mem_free(document
->lines1
);
561 for (i
= 0; i
< document
->nlinks
; i
++) {
562 struct link
*link
= &document
->links
[i
];
565 if (!link
->npoints
) {
566 done_link_members(link
);
567 memmove(link
, link
+ 1,
568 (document
->nlinks
- i
- 1) * sizeof(*link
));
573 p
= link
->points
[0].y
;
574 q
= link
->points
[link
->npoints
- 1].y
;
575 if (p
> q
) j
= p
, p
= q
, q
= j
;
576 for (j
= p
; j
<= q
; j
++) {
577 assertm(j
< document
->height
, "link out of screen");
578 if_assert_failed
continue;
579 document
->lines2
[j
] = &document
->links
[i
];
580 if (!document
->lines1
[j
])
581 document
->lines1
[j
] = &document
->links
[i
];
584 document
->links_sorted
= 1;
588 get_convert_table(unsigned char *head
, int to_cp
,
589 int default_cp
, int *from_cp
,
590 enum cp_status
*cp_status
, int ignore_server_cp
)
592 unsigned char *part
= head
;
596 if_assert_failed
return NULL
;
598 if (ignore_server_cp
) {
599 if (cp_status
) *cp_status
= CP_STATUS_IGNORED
;
600 if (from_cp
) *from_cp
= default_cp
;
601 return get_translation_table(default_cp
, to_cp
);
604 while (cp_index
== -1) {
605 unsigned char *ct_charset
;
606 /* scan_http_equiv() appends the meta http-equiv directives to
607 * the protocol header before this function is called, but the
608 * HTTP Content-Type header has precedence, so the HTTP header
609 * will be used if it exists and the meta header is only used
610 * as a fallback. See bug 983. */
611 unsigned char *a
= parse_header(part
, "Content-Type", &part
);
615 parse_header_param(a
, "charset", &ct_charset
);
617 cp_index
= get_cp_index(ct_charset
);
618 mem_free(ct_charset
);
623 if (cp_index
== -1) {
624 unsigned char *a
= parse_header(head
, "Content-Charset", NULL
);
627 cp_index
= get_cp_index(a
);
632 if (cp_index
== -1) {
633 unsigned char *a
= parse_header(head
, "Charset", NULL
);
636 cp_index
= get_cp_index(a
);
641 if (cp_index
== -1) {
642 cp_index
= default_cp
;
643 if (cp_status
) *cp_status
= CP_STATUS_ASSUMED
;
645 if (cp_status
) *cp_status
= CP_STATUS_SERVER
;
648 if (from_cp
) *from_cp
= cp_index
;
650 return get_translation_table(cp_index
, to_cp
);