13 #include "cache/cache.h"
14 #include "config/options.h"
15 #include "document/docdata.h"
16 #include "document/document.h"
17 #include "document/html/frames.h"
18 #include "document/html/parser.h"
19 #include "document/html/parser/parse.h"
20 #include "document/html/renderer.h"
21 #include "document/html/tables.h"
22 #include "document/options.h"
23 #include "document/refresh.h"
24 #include "document/renderer.h"
25 #include "intl/charsets.h"
26 #include "osdep/types.h"
27 #include "protocol/uri.h"
28 #include "session/session.h"
29 #include "terminal/color.h"
30 #include "terminal/draw.h"
31 #include "util/color.h"
32 #include "util/conv.h"
33 #include "util/error.h"
34 #include "util/hash.h"
35 #include "util/lists.h"
36 #include "util/memory.h"
37 #include "util/string.h"
38 #include "util/time.h"
39 #include "viewer/text/form.h"
40 #include "viewer/text/view.h"
41 #include "viewer/text/vs.h"
44 #include "document/html/internal.h"
46 /* Types and structs */
48 /* Tags are used for ``id''s or anchors in the document referenced by the
49 * fragment part of the URI. */
50 /* FIXME: This and find_tag() should be part of the general infrastructure
51 * in document/document.*. --pasky */
53 LIST_HEAD(struct tag
);
56 unsigned char name
[1]; /* must be last of struct. --Zas */
65 struct link_state_info
{
67 unsigned char *target
;
69 struct form_control
*form
;
72 struct table_cache_entry_key
{
82 struct table_cache_entry
{
83 LIST_HEAD(struct table_cache_entry
);
85 struct table_cache_entry_key key
;
89 /* Max. entries in table cache used for nested tables. */
90 #define MAX_TABLE_CACHE_ENTRIES 16384
92 /* Global variables */
93 static int table_cache_entries
;
94 static struct hash
*table_cache
;
97 struct renderer_context
{
98 int last_link_to_move
;
99 struct tag
*last_tag_to_move
;
100 /* All tags between document->tags and this tag (inclusive) should
101 * be aligned to the next line break, unless some real content follows
102 * the tag. Therefore, this virtual tags list accumulates new tags as
103 * they arrive and empties when some real content is written; if a line
104 * break is inserted in the meanwhile, the tags follow it (ie. imagine
105 * <a name="x"> <p>, then the "x" tag follows the line breaks inserted
106 * by the <p> tag). */
107 struct tag
*last_tag_for_newline
;
109 struct link_state_info link_state_info
;
111 struct conv_table
*convert_table
;
113 /* Used for setting cache info from HTTP-EQUIV meta tags. */
114 struct cache_entry
*cached
;
117 int subscript
; /* Count stacked subscripts */
118 int supscript
; /* Count stacked supscripts */
120 unsigned int empty_format
:1;
121 unsigned int nobreak
:1;
122 unsigned int nosearchable
:1;
123 unsigned int nowrap
:1; /* Activated/deactivated by SP_NOWRAP. */
126 static struct renderer_context renderer_context
;
130 static void line_break(struct html_context
*);
131 static void put_chars(struct html_context
*, unsigned char *, int);
133 #define X(x_) (part->box.x + (x_))
134 #define Y(y_) (part->box.y + (y_))
136 #define SPACES_GRANULARITY 0x7F
138 #define ALIGN_SPACES(x, o, n) mem_align_alloc(x, o, n, SPACES_GRANULARITY)
141 set_screen_char_color(struct screen_char
*schar
,
142 color_T bgcolor
, color_T fgcolor
,
143 enum color_flags color_flags
,
144 enum color_mode color_mode
)
146 struct color_pair colors
= INIT_COLOR_PAIR(bgcolor
, fgcolor
);
148 set_term_color(schar
, &colors
, color_flags
, color_mode
);
152 realloc_line(struct html_context
*html_context
, struct document
*document
,
155 struct screen_char
*pos
, *end
;
158 if (!realloc_lines(document
, y
))
161 line
= &document
->data
[y
];
163 if (length
< line
->length
)
166 if (!ALIGN_LINE(&line
->chars
, line
->length
, length
+ 1))
169 /* We cannot rely on the aligned allocation to clear the members for us
170 * since for line splitting we simply trim the length. Question is if
171 * it is better to to clear the line after the splitting or here. */
172 end
= &line
->chars
[length
];
175 set_screen_char_color(end
, par_format
.bgcolor
, 0x0,
176 0, document
->options
.color_mode
);
178 for (pos
= &line
->chars
[line
->length
]; pos
< end
; pos
++) {
179 copy_screen_chars(pos
, end
, 1);
182 line
->length
= length
+ 1;
188 expand_lines(struct html_context
*html_context
, struct part
*part
,
189 int x
, int y
, int lines
, color_T bgcolor
)
193 assert(part
&& part
->document
);
194 if_assert_failed
return;
196 if (!use_document_bg_colors(&part
->document
->options
))
199 par_format
.bgcolor
= bgcolor
;
201 for (line
= 0; line
< lines
; line
++)
202 realloc_line(html_context
, part
->document
, Y(y
+ line
), X(x
));
206 realloc_spaces(struct part
*part
, int length
)
208 if (length
< part
->spaces_len
)
211 if (!ALIGN_SPACES(&part
->spaces
, part
->spaces_len
, length
))
214 if (!ALIGN_SPACES(&part
->char_width
, part
->spaces_len
, length
))
218 part
->spaces_len
= length
;
224 #define LINE(y_) part->document->data[Y(y_)]
225 #define POS(x_, y_) LINE(y_).chars[X(x_)]
226 #define LEN(y_) int_max(LINE(y_).length - part->box.x, 0)
229 /* When we clear chars we want to preserve and use the background colors
230 * already in place else we could end up ``staining'' the background especial
231 * when drawing table cells. So make the cleared chars share the colors in
234 clear_hchars(struct html_context
*html_context
, int x
, int y
, int width
)
237 struct screen_char
*pos
, *end
;
239 assert(html_context
);
240 if_assert_failed
return;
242 part
= html_context
->part
;
244 assert(part
&& part
->document
&& width
> 0);
245 if_assert_failed
return;
247 if (realloc_line(html_context
, part
->document
, Y(y
), X(x
) + width
- 1))
250 assert(part
->document
->data
);
251 if_assert_failed
return;
254 end
= pos
+ width
- 1;
257 set_screen_char_color(end
, par_format
.bgcolor
, 0x0,
258 0, part
->document
->options
.color_mode
);
261 copy_screen_chars(pos
++, end
, 1);
264 /* TODO: Merge parts with get_format_screen_char(). --jonas */
265 /* Allocates the required chars on the given line and returns the char at
266 * position (x, y) ready to be used as a template char. */
267 static inline struct screen_char
*
268 get_frame_char(struct html_context
*html_context
, struct part
*part
,
269 int x
, int y
, unsigned char data
,
270 color_T bgcolor
, color_T fgcolor
)
272 struct screen_char
*template;
274 assert(html_context
);
275 if_assert_failed
return NULL
;
277 assert(part
&& part
->document
&& x
>= 0 && y
>= 0);
278 if_assert_failed
return NULL
;
280 if (realloc_line(html_context
, part
->document
, Y(y
), X(x
)))
283 assert(part
->document
->data
);
284 if_assert_failed
return NULL
;
286 template = &POS(x
, y
);
287 template->data
= data
;
288 template->attr
= SCREEN_ATTR_FRAME
;
289 set_screen_char_color(template, bgcolor
, fgcolor
,
290 part
->document
->options
.color_flags
,
291 part
->document
->options
.color_mode
);
297 draw_frame_hchars(struct part
*part
, int x
, int y
, int width
,
298 unsigned char data
, color_T bgcolor
, color_T fgcolor
,
299 struct html_context
*html_context
)
301 struct screen_char
*template;
304 if_assert_failed
return;
306 template = get_frame_char(html_context
, part
, x
+ width
- 1, y
, data
, bgcolor
, fgcolor
);
307 if (!template) return;
309 /* The template char is the last we need to draw so only decrease @width. */
310 for (width
-= 1; width
; width
--, x
++) {
311 copy_screen_chars(&POS(x
, y
), template, 1);
316 draw_frame_vchars(struct part
*part
, int x
, int y
, int height
,
317 unsigned char data
, color_T bgcolor
, color_T fgcolor
,
318 struct html_context
*html_context
)
320 struct screen_char
*template = get_frame_char(html_context
, part
, x
, y
,
321 data
, bgcolor
, fgcolor
);
323 if (!template) return;
325 /* The template char is the first vertical char to be drawn. So
326 * copy it to the rest. */
327 for (height
-= 1, y
+= 1; height
; height
--, y
++) {
328 if (realloc_line(html_context
, part
->document
, Y(y
), X(x
)))
331 copy_screen_chars(&POS(x
, y
), template, 1);
335 static inline struct screen_char
*
336 get_format_screen_char(struct html_context
*html_context
,
337 enum link_state link_state
)
339 static struct text_attrib_style ta_cache
= { -1, 0x0, 0x0 };
340 static struct screen_char schar_cache
;
342 if (memcmp(&ta_cache
, &format
.style
, sizeof(ta_cache
))) {
343 copy_struct(&ta_cache
, &format
.style
);
345 schar_cache
.attr
= 0;
346 if (format
.style
.attr
) {
347 if (format
.style
.attr
& AT_UNDERLINE
) {
348 schar_cache
.attr
|= SCREEN_ATTR_UNDERLINE
;
351 if (format
.style
.attr
& AT_BOLD
) {
352 schar_cache
.attr
|= SCREEN_ATTR_BOLD
;
355 if (format
.style
.attr
& AT_ITALIC
) {
356 schar_cache
.attr
|= SCREEN_ATTR_ITALIC
;
359 if (format
.style
.attr
& AT_GRAPHICS
) {
360 schar_cache
.attr
|= SCREEN_ATTR_FRAME
;
364 if (link_state
!= LINK_STATE_NONE
365 && html_context
->options
->underline_links
) {
366 schar_cache
.attr
|= SCREEN_ATTR_UNDERLINE
;
369 set_screen_char_color(&schar_cache
, format
.style
.bg
, format
.style
.fg
,
370 html_context
->options
->color_flags
,
371 html_context
->options
->color_mode
);
374 if (!!(schar_cache
.attr
& SCREEN_ATTR_UNSEARCHABLE
)
375 ^ !!renderer_context
.nosearchable
) {
376 schar_cache
.attr
^= SCREEN_ATTR_UNSEARCHABLE
;
383 /* First possibly do the format change and then find out what coordinates
384 * to use since sub- or superscript might change them */
386 set_hline(struct html_context
*html_context
, unsigned char *chars
, int charslen
,
387 enum link_state link_state
)
389 struct part
*part
= html_context
->part
;
390 struct screen_char
*schar
= get_format_screen_char(html_context
,
396 int utf8
= html_context
->options
->utf8
;
399 if_assert_failed
return len
;
401 assert(charslen
>= 0);
403 if (realloc_spaces(part
, x
+ charslen
))
406 if (part
->document
) {
407 if (realloc_line(html_context
, part
->document
,
408 Y(y
), X(x
) + charslen
- 1))
411 unsigned char *end
= chars
+ charslen
;
414 if (part
->document
->buf_length
) {
415 /* previous char was broken in the middle */
416 int length
= utf8charlen(part
->document
->buf
);
418 unsigned char *buf_ptr
= part
->document
->buf
;
420 for (i
= part
->document
->buf_length
; i
< length
&& chars
< end
;) {
421 part
->document
->buf
[i
++] = *chars
++;
423 part
->document
->buf_length
= i
;
424 part
->document
->buf
[i
] = '\0';
425 data
= utf_8_to_unicode(&buf_ptr
, buf_ptr
+ i
);
426 if (data
!= UCS_NO_CHAR
) {
427 part
->document
->buf_length
= 0;
430 /* Still not full char */
435 for (; chars
< end
; x
++) {
436 if (*chars
== NBSP_CHAR
) {
438 part
->spaces
[x
] = html_context
->options
->wrap_nbsp
;
439 part
->char_width
[x
] = 1;
442 part
->spaces
[x
] = (*chars
== ' ');
443 data
= utf_8_to_unicode(&chars
, end
);
444 if (data
== UCS_NO_CHAR
) {
447 unsigned char attr
= schar
->attr
;
449 schar
->data
= *chars
++;
450 schar
->attr
= SCREEN_ATTR_FRAME
;
451 copy_screen_chars(&POS(x
, y
), schar
, 1);
453 part
->char_width
[x
] = 0;
458 for (i
= 0; chars
< end
;i
++) {
459 part
->document
->buf
[i
] = *chars
++;
461 part
->document
->buf_length
= i
;
466 if (unicode_to_cell(data
) == 2) {
467 schar
->data
= (unicode_val_T
)data
;
468 part
->char_width
[x
] = 2;
469 copy_screen_chars(&POS(x
++, y
), schar
, 1);
470 schar
->data
= UCS_NO_CHAR
;
472 part
->char_width
[x
] = 0;
474 part
->char_width
[x
] = unicode_to_cell(data
);
475 schar
->data
= (unicode_val_T
)data
;
479 copy_screen_chars(&POS(x
, y
), schar
, 1);
482 for (; charslen
> 0; charslen
--, x
++, chars
++) {
483 part
->char_width
[x
] = 1;
484 if (*chars
== NBSP_CHAR
) {
486 part
->spaces
[x
] = html_context
->options
->wrap_nbsp
;
488 part
->spaces
[x
] = (*chars
== ' ');
489 schar
->data
= *chars
;
491 copy_screen_chars(&POS(x
, y
), schar
, 1);
500 for (end
= chars
+ charslen
; chars
< end
; x
++) {
503 part
->spaces
[x
] = (*chars
== ' ');
504 data
= utf_8_to_unicode(&chars
, end
);
505 part
->char_width
[x
] = unicode_to_cell(data
);
506 if (part
->char_width
[x
] == 2) {
509 part
->char_width
[x
] = 0;
511 if (data
== UCS_NO_CHAR
) {
513 part
->char_width
[x
] = 0;
519 for (; charslen
> 0; charslen
--, x
++, chars
++) {
520 part
->spaces
[x
] = (*chars
== ' ');
521 part
->char_width
[x
] = 1;
529 /* First possibly do the format change and then find out what coordinates
530 * to use since sub- or superscript might change them */
532 set_hline(struct html_context
*html_context
, unsigned char *chars
, int charslen
,
533 enum link_state link_state
)
535 struct part
*part
= html_context
->part
;
536 struct screen_char
*schar
= get_format_screen_char(html_context
,
542 if_assert_failed
return;
544 if (realloc_spaces(part
, x
+ charslen
))
547 if (part
->document
) {
548 if (realloc_line(html_context
, part
->document
,
549 Y(y
), X(x
) + charslen
- 1))
552 for (; charslen
> 0; charslen
--, x
++, chars
++) {
553 if (*chars
== NBSP_CHAR
) {
555 part
->spaces
[x
] = html_context
->options
->wrap_nbsp
;
557 part
->spaces
[x
] = (*chars
== ' ');
558 schar
->data
= *chars
;
560 copy_screen_chars(&POS(x
, y
), schar
, 1);
563 for (; charslen
> 0; charslen
--, x
++, chars
++) {
564 part
->spaces
[x
] = (*chars
== ' ');
568 #endif /* CONFIG_UTF_8 */
571 move_links(struct html_context
*html_context
, int xf
, int yf
, int xt
, int yt
)
575 int nlink
= renderer_context
.last_link_to_move
;
578 assert(html_context
);
579 if_assert_failed
return;
581 part
= html_context
->part
;
583 assert(part
&& part
->document
);
584 if_assert_failed
return;
586 if (!realloc_lines(part
->document
, Y(yt
)))
589 for (; nlink
< part
->document
->nlinks
; nlink
++) {
590 struct link
*link
= &part
->document
->links
[nlink
];
593 for (i
= 0; i
< link
->npoints
; i
++) {
594 /* Fix for bug 479 (part one) */
595 /* The scenario that triggered it:
597 * Imagine a centered element containing a really long
598 * word (over half of the screen width long) followed
599 * by a few links with no spaces between them where all
600 * the link text combined with the really long word
601 * will force the line to be wrapped. When rendering
602 * the line first words (including link text words) are
603 * put on one line. Then wrapping is performed moving
604 * all links from current line to the one below. Then
605 * the current line (now only containing the really
606 * long word) is centered. This will trigger a call to
607 * move_links() which will increment.
609 * Without the fix below the centering of the current
610 * line will increment last_link_to_move to that of the
611 * last link which means centering of the next line
612 * with all the links will only move the last link
613 * leaving all the other links' points dangling and
614 * causing buggy link highlighting.
616 * Even links like textareas will be correctly handled
617 * because @last_link_to_move is a way to optimize how
618 * many links move_links() will have to iterate and
619 * this little fix will only decrease the effect of the
620 * optimization by always ensuring it is never
621 * incremented too far. */
622 if (!matched
&& link
->points
[i
].y
> Y(yf
)) {
627 if (link
->points
[i
].y
!= Y(yf
))
632 if (link
->points
[i
].x
< X(xf
))
636 link
->points
[i
].y
= Y(yt
);
637 link
->points
[i
].x
+= -xf
+ xt
;
639 int to_move
= link
->npoints
- (i
+ 1);
641 assert(to_move
>= 0);
644 memmove(&link
->points
[i
],
645 &link
->points
[i
+ 1],
647 sizeof(*link
->points
));
656 renderer_context
.last_link_to_move
= nlink
;
660 /* Don't move tags when removing links. */
664 tag
= renderer_context
.last_tag_to_move
;
666 while (list_has_next(part
->document
->tags
, tag
)) {
669 if (tag
->y
== Y(yf
)) {
671 if (tag
->x
>= X(xf
)) {
676 } else if (!matched
&& tag
->y
> Y(yf
)) {
677 /* Fix for bug 479 (part two) */
681 if (!matched
) renderer_context
.last_tag_to_move
= tag
;
686 copy_chars(struct html_context
*html_context
, int x
, int y
, int width
, struct screen_char
*d
)
690 assert(html_context
);
691 if_assert_failed
return;
693 part
= html_context
->part
;
695 assert(width
> 0 && part
&& part
->document
&& part
->document
->data
);
696 if_assert_failed
return;
698 if (realloc_line(html_context
, part
->document
, Y(y
), X(x
) + width
- 1))
701 copy_screen_chars(&POS(x
, y
), d
, width
);
705 move_chars(struct html_context
*html_context
, int x
, int y
, int nx
, int ny
)
709 assert(html_context
);
710 if_assert_failed
return;
712 part
= html_context
->part
;
714 assert(part
&& part
->document
&& part
->document
->data
);
715 if_assert_failed
return;
717 if (LEN(y
) - x
<= 0) return;
718 copy_chars(html_context
, nx
, ny
, LEN(y
) - x
, &POS(x
, y
));
720 LINE(y
).length
= X(x
);
721 move_links(html_context
, x
, y
, nx
, ny
);
725 shift_chars(struct html_context
*html_context
, int y
, int shift
)
728 struct screen_char
*a
;
731 assert(html_context
);
732 if_assert_failed
return;
734 part
= html_context
->part
;
736 assert(part
&& part
->document
&& part
->document
->data
);
737 if_assert_failed
return;
741 a
= fmem_alloc(len
* sizeof(*a
));
744 copy_screen_chars(a
, &POS(0, y
), len
);
746 clear_hchars(html_context
, 0, y
, shift
);
747 copy_chars(html_context
, shift
, y
, len
, a
);
750 move_links(html_context
, 0, y
, shift
, y
);
754 del_chars(struct html_context
*html_context
, int x
, int y
)
758 assert(html_context
);
759 if_assert_failed
return;
761 part
= html_context
->part
;
763 assert(part
&& part
->document
&& part
->document
->data
);
764 if_assert_failed
return;
766 LINE(y
).length
= X(x
);
767 move_links(html_context
, x
, y
, -1, -1);
770 #if TABLE_LINE_PADDING < 0
771 # define overlap_width(x) (x).width
773 # define overlap_width(x) int_min((x).width, \
774 html_context->options->box.width - TABLE_LINE_PADDING)
776 #define overlap(x) int_max(overlap_width(x) - (x).rightmargin, 0)
779 split_line_at(struct html_context
*html_context
, int width
)
783 int new_width
= width
+ par_format
.rightmargin
;
785 assert(html_context
);
786 if_assert_failed
return 0;
788 part
= html_context
->part
;
791 if_assert_failed
return 0;
793 /* Make sure that we count the right margin to the total
794 * actual box width. */
795 int_lower_bound(&part
->box
.width
, new_width
);
797 if (part
->document
) {
798 assert(part
->document
->data
);
799 if_assert_failed
return 0;
801 if (html_context
->options
->utf8
802 && width
< part
->spaces_len
&& part
->char_width
[width
] == 2) {
803 move_chars(html_context
, width
, part
->cy
, par_format
.leftmargin
, part
->cy
+ 1);
804 del_chars(html_context
, width
, part
->cy
);
808 assertm(POS(width
, part
->cy
).data
== ' ',
809 "bad split: %c", POS(width
, part
->cy
).data
);
810 move_chars(html_context
, width
+ 1, part
->cy
, par_format
.leftmargin
, part
->cy
+ 1);
811 del_chars(html_context
, width
, part
->cy
);
817 if (!(html_context
->options
->utf8
818 && width
< part
->spaces_len
819 && part
->char_width
[width
] == 2))
821 width
++; /* Since we were using (x + 1) only later... */
823 tmp
= part
->spaces_len
- width
;
825 /* 0 is possible and I'm paranoid ... --Zas */
826 memmove(part
->spaces
, part
->spaces
+ width
, tmp
);
828 memmove(part
->char_width
, part
->char_width
+ width
, tmp
);
833 if_assert_failed tmp
= 0;
834 memset(part
->spaces
+ tmp
, 0, width
);
836 memset(part
->char_width
+ tmp
, 0, width
);
839 if (par_format
.leftmargin
> 0) {
840 tmp
= part
->spaces_len
- par_format
.leftmargin
;
841 assertm(tmp
> 0, "part->spaces_len - par_format.leftmargin == %d", tmp
);
842 /* So tmp is zero, memmove() should survive that. Don't recover. */
843 memmove(part
->spaces
+ par_format
.leftmargin
, part
->spaces
, tmp
);
845 memmove(part
->char_width
+ par_format
.leftmargin
, part
->char_width
, tmp
);
851 if (part
->cx
== width
) {
853 int_lower_bound(&part
->box
.height
, part
->cy
);
856 part
->cx
-= width
- par_format
.leftmargin
;
857 int_lower_bound(&part
->box
.height
, part
->cy
+ 1);
862 /* Here, we scan the line for a possible place where we could split it into two
863 * (breaking it, because it is too long), if it is overlapping from the maximal
865 /* Returns 0 if there was found no spot suitable for breaking the line.
866 * 1 if the line was split into two.
867 * 2 if the (second) splitted line is blank (that is useful to determine
868 * ie. if the next line_break() should really break the line; we don't
869 * want to see any blank lines to pop up, do we?). */
871 split_line(struct html_context
*html_context
)
876 assert(html_context
);
877 if_assert_failed
return 0;
879 part
= html_context
->part
;
882 if_assert_failed
return 0;
885 if (html_context
->options
->utf8
) {
886 for (x
= overlap(par_format
); x
>= par_format
.leftmargin
; x
--) {
888 if (x
< part
->spaces_len
&& (part
->spaces
[x
]
889 || (part
->char_width
[x
] == 2
890 /* Ugly hack. If we haven't place for
891 * double-width characters we print two
892 * double-width characters. */
893 && x
!= par_format
.leftmargin
)))
894 return split_line_at(html_context
, x
);
897 for (x
= par_format
.leftmargin
; x
< part
->cx
; x
++) {
898 if (x
< part
->spaces_len
&& (part
->spaces
[x
]
899 || (part
->char_width
[x
] == 2
900 /* We want to break line after _second_
901 * double-width character. */
902 && x
> par_format
.leftmargin
)))
903 return split_line_at(html_context
, x
);
908 for (x
= overlap(par_format
); x
>= par_format
.leftmargin
; x
--)
909 if (x
< part
->spaces_len
&& part
->spaces
[x
])
910 return split_line_at(html_context
, x
);
912 for (x
= par_format
.leftmargin
; x
< part
->cx
; x
++)
913 if (x
< part
->spaces_len
&& part
->spaces
[x
])
914 return split_line_at(html_context
, x
);
917 /* Make sure that we count the right margin to the total
918 * actual box width. */
919 int_lower_bound(&part
->box
.width
, part
->cx
+ par_format
.rightmargin
);
924 /* Insert @new_spaces spaces before the coordinates @x and @y,
925 * adding those spaces to whatever link is at those coordinates. */
926 /* TODO: Integrate with move_links. */
928 insert_spaces_in_link(struct part
*part
, int x
, int y
, int new_spaces
)
930 int i
= part
->document
->nlinks
;
936 struct link
*link
= &part
->document
->links
[i
];
937 int j
= link
->npoints
;
940 struct point
*point
= &link
->points
[j
];
942 if (point
->x
!= x
|| point
->y
!= y
)
945 if (!realloc_points(link
, link
->npoints
+ new_spaces
))
948 link
->npoints
+= new_spaces
;
949 point
= &link
->points
[link
->npoints
- 1];
951 while (new_spaces
--) {
962 /* This function is very rare exemplary of clean and beautyful code here.
963 * Please handle with care. --pasky */
965 justify_line(struct html_context
*html_context
, int y
)
968 struct screen_char
*line
; /* we save original line here */
975 assert(html_context
);
976 if_assert_failed
return;
978 part
= html_context
->part
;
980 assert(part
&& part
->document
&& part
->document
->data
);
981 if_assert_failed
return;
985 if_assert_failed
return;
987 line
= fmem_alloc(len
* sizeof(*line
));
990 /* It may sometimes happen that the line is only one char long and that
991 * char is space - then we're going to write to both [0] and [1], but
992 * we allocated only one field. Thus, we've to do (len + 1). --pasky */
993 space_list
= fmem_alloc((len
+ 1) * sizeof(*space_list
));
999 copy_screen_chars(line
, &POS(0, y
), len
);
1001 /* Skip leading spaces */
1006 while (line
[pos
].data
== ' ')
1009 /* Yes, this can be negative, we know. But we add one to it always
1010 * anyway, so it's ok. */
1011 space_list
[spaces
++] = pos
- 1;
1015 for (; pos
< len
; pos
++)
1016 if (line
[pos
].data
== ' ')
1017 space_list
[spaces
++] = pos
;
1019 space_list
[spaces
] = len
;
1023 /* Diff is the difference between the width of the paragraph
1024 * and the current length of the line. */
1025 diff
= overlap(par_format
) - len
;
1027 /* We check diff > 0 because diff can be negative (i.e., we have
1028 * an unbroken line of length > overlap(par_format))
1029 * even when spaces > 1 if the line has only non-breaking spaces. */
1030 if (spaces
> 1 && diff
> 0) {
1034 clear_hchars(html_context
, 0, y
, overlap(par_format
));
1036 for (word
= 0; word
< spaces
; word
++) {
1037 /* We have to increase line length by 'diff' num. of
1038 * characters, so we move 'word'th word 'word_shift'
1039 * characters right. */
1040 int word_start
= space_list
[word
] + 1;
1041 int word_len
= space_list
[word
+ 1] - word_start
;
1046 assert(word_len
>= 0);
1047 if_assert_failed
continue;
1048 if (!word_len
) continue;
1050 word_shift
= (word
* diff
) / (spaces
- 1);
1051 new_start
= word_start
+ word_shift
;
1053 copy_chars(html_context
, new_start
, y
, word_len
,
1056 new_spaces
= new_start
- prev_end
- 1;
1057 if (word
&& new_spaces
) {
1058 move_links(html_context
, prev_end
+ 1, y
, new_start
, y
);
1059 insert_spaces_in_link(part
,
1060 new_start
, y
, new_spaces
);
1063 prev_end
= new_start
+ word_len
;
1067 fmem_free(space_list
);
1072 align_line(struct html_context
*html_context
, int y
, int last
)
1078 assert(html_context
);
1079 if_assert_failed
return;
1081 part
= html_context
->part
;
1083 assert(part
&& part
->document
&& part
->document
->data
);
1084 if_assert_failed
return;
1088 if (!len
|| par_format
.align
== ALIGN_LEFT
)
1091 if (par_format
.align
== ALIGN_JUSTIFY
) {
1093 justify_line(html_context
, y
);
1097 shift
= overlap(par_format
) - len
;
1098 if (par_format
.align
== ALIGN_CENTER
)
1101 shift_chars(html_context
, y
, shift
);
1105 init_link_event_hooks(struct html_context
*html_context
, struct link
*link
)
1107 link
->event_hooks
= mem_calloc(1, sizeof(*link
->event_hooks
));
1108 if (!link
->event_hooks
) return;
1110 #define add_evhook(list_, type_, src_) \
1112 struct script_event_hook *evhook; \
1116 evhook = mem_calloc(1, sizeof(*evhook)); \
1117 if (!evhook) break; \
1119 evhook->type = type_; \
1120 evhook->src = stracpy(src_); \
1121 add_to_list(*(list_), evhook); \
1124 init_list(*link
->event_hooks
);
1125 add_evhook(link
->event_hooks
, SEVHOOK_ONCLICK
, format
.onclick
);
1126 add_evhook(link
->event_hooks
, SEVHOOK_ONDBLCLICK
, format
.ondblclick
);
1127 add_evhook(link
->event_hooks
, SEVHOOK_ONMOUSEOVER
, format
.onmouseover
);
1128 add_evhook(link
->event_hooks
, SEVHOOK_ONHOVER
, format
.onhover
);
1129 add_evhook(link
->event_hooks
, SEVHOOK_ONFOCUS
, format
.onfocus
);
1130 add_evhook(link
->event_hooks
, SEVHOOK_ONMOUSEOUT
, format
.onmouseout
);
1131 add_evhook(link
->event_hooks
, SEVHOOK_ONBLUR
, format
.onblur
);
1136 static struct link
*
1137 new_link(struct html_context
*html_context
, unsigned char *name
, int namelen
)
1139 struct document
*document
;
1144 assert(html_context
);
1145 if_assert_failed
return NULL
;
1147 part
= html_context
->part
;
1150 if_assert_failed
return NULL
;
1152 document
= part
->document
;
1155 if_assert_failed
return NULL
;
1157 link_number
= part
->link_num
;
1159 if (!ALIGN_LINK(&document
->links
, document
->nlinks
, document
->nlinks
+ 1))
1162 link
= &document
->links
[document
->nlinks
++];
1163 link
->number
= link_number
- 1;
1164 if (document
->options
.use_tabindex
) link
->number
+= format
.tabindex
;
1165 link
->accesskey
= format
.accesskey
;
1166 link
->title
= null_or_stracpy(format
.title
);
1167 link
->where_img
= null_or_stracpy(format
.image
);
1170 link
->target
= null_or_stracpy(format
.target
);
1171 link
->data
.name
= memacpy(name
, namelen
);
1172 /* if (strlen(url) > 4 && !strncasecmp(url, "MAP@", 4)) { */
1174 && ((format
.link
[0]|32) == 'm')
1175 && ((format
.link
[1]|32) == 'a')
1176 && ((format
.link
[2]|32) == 'p')
1177 && (format
.link
[3] == '@')
1178 && format
.link
[4]) {
1179 link
->type
= LINK_MAP
;
1180 link
->where
= stracpy(format
.link
+ 4);
1182 link
->type
= LINK_HYPERTEXT
;
1183 link
->where
= null_or_stracpy(format
.link
);
1187 struct form_control
*fc
= format
.form
;
1194 link
->type
= LINK_FIELD
;
1197 link
->type
= LINK_AREA
;
1201 link
->type
= LINK_CHECKBOX
;
1204 link
->type
= LINK_SELECT
;
1211 link
->type
= LINK_BUTTON
;
1213 link
->data
.form_control
= fc
;
1214 /* At this point, format.form might already be set but
1215 * the form_control not registered through SP_CONTROL
1216 * yet, therefore without fc->form set. It is always
1217 * after the "good" last form was already processed,
1218 * though, so we can safely just take that. */
1220 if (!form
&& !list_empty(document
->forms
))
1221 form
= document
->forms
.next
;
1222 link
->target
= null_or_stracpy(form
? form
->target
: NULL
);
1225 link
->color
.background
= format
.style
.bg
;
1226 link
->color
.foreground
= link_is_textinput(link
)
1227 ? format
.style
.fg
: format
.clink
;
1229 init_link_event_hooks(html_context
, link
);
1231 document
->links_sorted
= 0;
1236 html_special_tag(struct document
*document
, unsigned char *t
, int x
, int y
)
1242 if_assert_failed
return;
1244 tag_len
= strlen(t
);
1245 /* One byte is reserved for name in struct tag. */
1246 tag
= mem_alloc(sizeof(*tag
) + tag_len
);
1251 memcpy(tag
->name
, t
, tag_len
+ 1);
1252 add_to_list(document
->tags
, tag
);
1253 if (renderer_context
.last_tag_for_newline
== (struct tag
*) &document
->tags
)
1254 renderer_context
.last_tag_for_newline
= tag
;
1259 put_chars_conv(struct html_context
*html_context
,
1260 unsigned char *chars
, int charslen
)
1264 assert(html_context
);
1265 if_assert_failed
return;
1267 part
= html_context
->part
;
1269 assert(part
&& chars
&& charslen
);
1270 if_assert_failed
return;
1272 if (format
.style
.attr
& AT_GRAPHICS
) {
1273 put_chars(html_context
, chars
, charslen
);
1277 convert_string(renderer_context
.convert_table
, chars
, charslen
,
1278 html_context
->options
->cp
,
1279 CSM_DEFAULT
, NULL
, (void (*)(void *, unsigned char *, int)) put_chars
, html_context
);
1283 put_link_number(struct html_context
*html_context
)
1285 struct part
*part
= html_context
->part
;
1286 unsigned char s
[64];
1287 unsigned char *fl
= format
.link
;
1288 unsigned char *ft
= format
.target
;
1289 unsigned char *fi
= format
.image
;
1290 struct form_control
*ff
= format
.form
;
1293 format
.link
= format
.target
= format
.image
= NULL
;
1297 ulongcat(s
, &slen
, part
->link_num
, sizeof(s
) - 3, 0);
1301 renderer_context
.nosearchable
= 1;
1302 put_chars(html_context
, s
, slen
);
1303 renderer_context
.nosearchable
= 0;
1305 if (ff
&& ff
->type
== FC_TEXTAREA
) line_break(html_context
);
1307 /* We might have ended up on a new line after the line breaking
1308 * or putting the link number chars. */
1309 if (part
->cx
== -1) part
->cx
= par_format
.leftmargin
;
1317 #define assert_link_variable(old, new) \
1318 assertm(!(old), "Old link value [%s]. New value [%s]", old, new);
1321 init_link_state_info(unsigned char *link
, unsigned char *target
,
1322 unsigned char *image
, struct form_control
*form
)
1324 assert_link_variable(renderer_context
.link_state_info
.image
, image
);
1325 assert_link_variable(renderer_context
.link_state_info
.target
, target
);
1326 assert_link_variable(renderer_context
.link_state_info
.link
, link
);
1328 renderer_context
.link_state_info
.link
= null_or_stracpy(link
);
1329 renderer_context
.link_state_info
.target
= null_or_stracpy(target
);
1330 renderer_context
.link_state_info
.image
= null_or_stracpy(image
);
1331 renderer_context
.link_state_info
.form
= form
;
1335 done_link_state_info(void)
1337 mem_free_if(renderer_context
.link_state_info
.link
);
1338 mem_free_if(renderer_context
.link_state_info
.target
);
1339 mem_free_if(renderer_context
.link_state_info
.image
);
1340 memset(&renderer_context
.link_state_info
, 0,
1341 sizeof(renderer_context
.link_state_info
));
1346 process_link(struct html_context
*html_context
, enum link_state link_state
,
1347 unsigned char *chars
, int charslen
, int cells
)
1350 process_link(struct html_context
*html_context
, enum link_state link_state
,
1351 unsigned char *chars
, int charslen
)
1352 #endif /* CONFIG_UTF_8 */
1354 struct part
*part
= html_context
->part
;
1358 switch (link_state
) {
1359 case LINK_STATE_SAME
: {
1360 unsigned char *name
;
1362 if (!part
->document
) return;
1364 assertm(part
->document
->nlinks
> 0, "no link");
1365 if_assert_failed
return;
1367 link
= &part
->document
->links
[part
->document
->nlinks
- 1];
1369 name
= get_link_name(link
);
1371 unsigned char *new_name
;
1373 new_name
= straconcat(name
, chars
, NULL
);
1376 link
->data
.name
= new_name
;
1380 /* FIXME: Concatenating two adjectent <a> elements to a single
1381 * link is broken since we lose the event handlers for the
1382 * second one. OTOH simply appending them here won't fly since
1383 * we may get here multiple times for even a single link. We
1384 * will probably need some SP_ for creating a new link or so.
1390 case LINK_STATE_NEW
:
1393 init_link_state_info(format
.link
, format
.target
,
1394 format
.image
, format
.form
);
1395 if (!part
->document
) return;
1397 /* Trim leading space from the link text */
1398 while (x_offset
< charslen
&& chars
[x_offset
] <= ' ')
1402 charslen
-= x_offset
;
1406 #endif /* CONFIG_UTF_8 */
1409 link
= new_link(html_context
, chars
, charslen
);
1414 case LINK_STATE_NONE
:
1416 INTERNAL("bad link_state %i", (int) link_state
);
1420 /* Add new canvas positions to the link. */
1422 if (realloc_points(link
, link
->npoints
+ cells
))
1424 if (realloc_points(link
, link
->npoints
+ charslen
))
1425 #endif /* CONFIG_UTF_8 */
1427 struct point
*point
= &link
->points
[link
->npoints
];
1428 int x
= X(part
->cx
) + x_offset
;
1429 int y
= Y(part
->cy
);
1432 link
->npoints
+= cells
;
1434 for (; cells
> 0; cells
--, point
++, x
++)
1436 link
->npoints
+= charslen
;
1438 for (; charslen
> 0; charslen
--, point
++, x
++)
1439 #endif /* CONFIG_UTF_8 */
1447 static inline enum link_state
1448 get_link_state(struct html_context
*html_context
)
1450 enum link_state state
;
1452 if (!(format
.link
|| format
.image
|| format
.form
)) {
1453 state
= LINK_STATE_NONE
;
1455 } else if ((renderer_context
.link_state_info
.link
1456 || renderer_context
.link_state_info
.image
1457 || renderer_context
.link_state_info
.form
)
1458 && !xstrcmp(format
.link
, renderer_context
.link_state_info
.link
)
1459 && !xstrcmp(format
.target
, renderer_context
.link_state_info
.target
)
1460 && !xstrcmp(format
.image
, renderer_context
.link_state_info
.image
)
1461 && format
.form
== renderer_context
.link_state_info
.form
) {
1463 return LINK_STATE_SAME
;
1466 state
= LINK_STATE_NEW
;
1469 done_link_state_info();
1475 html_has_non_space_chars(unsigned char *chars
, int charslen
)
1479 while (pos
< charslen
)
1480 if (!isspace(chars
[pos
++]))
1487 put_chars(struct html_context
*html_context
, unsigned char *chars
, int charslen
)
1489 enum link_state link_state
;
1493 #endif /* CONFIG_UTF_8 */
1495 assert(html_context
);
1496 if_assert_failed
return;
1498 part
= html_context
->part
;
1501 if_assert_failed
return;
1503 assert(chars
&& charslen
);
1504 if_assert_failed
return;
1506 /* If we are not handling verbatim aligning and we are at the begining
1507 * of a line trim whitespace. */
1508 if (part
->cx
== -1) {
1509 /* If we are not handling verbatim aligning trim leading
1511 if (!html_is_preformatted()) {
1512 while (charslen
&& *chars
== ' ') {
1517 if (charslen
< 1) return;
1520 part
->cx
= par_format
.leftmargin
;
1523 /* For preformatted html always update 'the last tag' so we never end
1524 * up moving tags to the wrong line (Fixes bug 324). For all other html
1525 * it is moved only when the line being rendered carry some real
1526 * non-whitespace content. */
1527 if (html_is_preformatted()
1528 || html_has_non_space_chars(chars
, charslen
)) {
1529 renderer_context
.last_tag_for_newline
= (struct tag
*) &part
->document
->tags
;
1532 int_lower_bound(&part
->box
.height
, part
->cy
+ 1);
1534 link_state
= get_link_state(html_context
);
1536 if (link_state
== LINK_STATE_NEW
) {
1539 /* Don't add inaccessible links. It seems to be caused
1540 * by the parser putting a space char after stuff like
1541 * <img>-tags or comments wrapped in <a>-tags. See bug
1542 * 30 for test case. */
1543 while (x_offset
< charslen
&& chars
[x_offset
] <= ' ')
1546 /* For pure spaces reset the link state */
1547 if (x_offset
== charslen
)
1548 link_state
= LINK_STATE_NONE
;
1549 else if (html_context
->options
->links_numbering
)
1550 put_link_number(html_context
);
1554 #endif /* CONFIG_UTF_8 */
1555 set_hline(html_context
, chars
, charslen
, link_state
);
1557 if (link_state
!= LINK_STATE_NONE
) {
1559 /* This all code is from utf8 branch */
1560 #define is_drawing_subs_or_sups() \
1561 ((format.style.attr & AT_SUBSCRIPT \
1562 && html_context->options->display_subs) \
1563 || (format.style.attr & AT_SUPERSCRIPT \
1564 && html_context->options->display_sups))
1566 /* We need to update the current @link_state because <sub> and
1567 * <sup> tags will output to the canvas using an inner
1568 * put_chars() call which results in their process_link() call
1569 * will ``update'' the @link_state. */
1570 if (link_state
== LINK_STATE_NEW
1571 && (is_drawing_subs_or_sups()
1572 || update_after_subscript
!= renderer_context
.subscript
)) {
1573 link_state
= get_link_state(html_context
);
1576 #undef is_drawing_subs_or_sups
1580 process_link(html_context
, link_state
, chars
, charslen
,
1583 process_link(html_context
, link_state
, chars
, charslen
);
1584 #endif /* CONFIG_UTF_8 */
1588 if (renderer_context
.nowrap
1589 && part
->cx
+ cells
> overlap(par_format
))
1594 if (renderer_context
.nowrap
1595 && part
->cx
+ charslen
> overlap(par_format
))
1598 part
->cx
+= charslen
;
1599 #endif /* CONFIG_UTF_8 */
1601 renderer_context
.nobreak
= 0;
1603 if (!(html_context
->options
->wrap
|| html_is_preformatted())) {
1604 while (part
->cx
> overlap(par_format
)
1605 && part
->cx
> par_format
.leftmargin
) {
1606 int x
= split_line(html_context
);
1610 align_line(html_context
, part
->cy
- 1, 0);
1611 renderer_context
.nobreak
= !!(x
- 1);
1615 assert(charslen
> 0);
1619 part
->xa
+= charslen
;
1620 #endif /* CONFIG_UTF_8 */
1621 int_lower_bound(&part
->max_width
, part
->xa
1622 + par_format
.leftmargin
+ par_format
.rightmargin
1623 - (chars
[charslen
- 1] == ' '
1624 && !html_is_preformatted()));
1632 line_break(struct html_context
*html_context
)
1637 assert(html_context
);
1638 if_assert_failed
return;
1640 part
= html_context
->part
;
1643 if_assert_failed
return;
1645 int_lower_bound(&part
->box
.width
, part
->cx
+ par_format
.rightmargin
);
1647 if (renderer_context
.nobreak
) {
1648 renderer_context
.nobreak
= 0;
1654 if (!part
->document
|| !part
->document
->data
) goto end
;
1656 if (!realloc_lines(part
->document
, part
->box
.height
+ part
->cy
+ 1))
1659 if (part
->cx
> par_format
.leftmargin
&& LEN(part
->cy
) > part
->cx
- 1
1660 && POS(part
->cx
- 1, part
->cy
).data
== ' ') {
1661 del_chars(html_context
, part
->cx
- 1, part
->cy
);
1665 if (part
->cx
> 0) align_line(html_context
, part
->cy
, 1);
1667 for (tag
= renderer_context
.last_tag_for_newline
;
1668 tag
&& tag
!= (struct tag
*) &part
->document
->tags
;
1671 tag
->y
= Y(part
->cy
+ 1);
1678 memset(part
->spaces
, 0, part
->spaces_len
);
1680 memset(part
->char_width
, 0, part
->spaces_len
);
1685 html_special_form(struct part
*part
, struct form
*form
)
1687 assert(part
&& form
);
1688 if_assert_failed
return;
1690 if (!part
->document
) {
1695 if (!list_empty(part
->document
->forms
)) {
1698 /* Make sure the new form ``claims'' its slice of the form range
1699 * maintained in the form_num and form_end variables. */
1700 foreach (nform
, part
->document
->forms
) {
1701 if (form
->form_num
< nform
->form_num
1702 || nform
->form_end
< form
->form_num
)
1705 /* First check if the form has identical form numbers.
1706 * That should only be the case when the form being
1707 * added is in fact the same form in which case it
1708 * should be dropped. The fact that this can happen
1709 * suggests that the table renderering can be confused.
1710 * See bug 647 for a test case. */
1711 if (nform
->form_num
== form
->form_num
1712 && nform
->form_end
== form
->form_end
) {
1717 /* The form start is inside an already added form, so
1718 * partition the space of the existing form and get
1720 nform
->form_end
= form
->form_num
- 1;
1721 assertm(nform
->form_num
<= nform
->form_end
,
1722 "[%d:%d] [%d:%d]", nform
->form_num
, nform
->form_end
,
1723 form
->form_num
, form
->form_end
);
1727 /* If it is the first form make sure it eats the whole form
1730 /* Disabled because in tables the parse order may lead to a
1731 * later form being parsed before a preceeding one causing the
1732 * wrong order if we set it to zero. Let's hope it doesn't break
1738 add_to_list(part
->document
->forms
, form
);
1742 html_special_form_control(struct part
*part
, struct form_control
*fc
)
1747 if_assert_failed
return;
1749 if (!part
->document
) {
1750 done_form_control(fc
);
1755 fc
->g_ctrl_num
= renderer_context
.g_ctrl_num
++;
1757 /* We don't want to recode hidden fields. */
1758 if (fc
->type
== FC_TEXT
|| fc
->type
== FC_PASSWORD
||
1759 fc
->type
== FC_TEXTAREA
) {
1760 unsigned char *dv
= convert_string(renderer_context
.convert_table
,
1762 strlen(fc
->default_value
),
1763 part
->document
->options
.cp
,
1764 CSM_QUERY
, NULL
, NULL
, NULL
);
1766 if (dv
) mem_free_set(&fc
->default_value
, dv
);
1769 if (list_empty(part
->document
->forms
)) {
1770 /* No forms encountered yet, that means a homeless form
1771 * control. Generate a dummy form for those Flying
1775 add_to_list(part
->document
->forms
, form
);
1777 /* Attach this form control to the last form encountered. */
1778 form
= part
->document
->forms
.next
;
1780 add_to_list(form
->items
, fc
);
1783 /* Reparents form items based on position in the source. */
1785 check_html_form_hierarchy(struct part
*part
)
1787 struct document
*document
= part
->document
;
1788 INIT_LIST_HEAD(form_controls
);
1790 struct form_control
*fc
, *next
;
1792 if (list_empty(document
->forms
))
1795 /* Take out all badly placed form items. */
1797 foreach (form
, document
->forms
) {
1799 assertm(form
->form_num
<= form
->form_end
,
1800 "%p [%d : %d]", form
, form
->form_num
, form
->form_end
);
1802 foreachsafe (fc
, next
, form
->items
) {
1803 if (form
->form_num
<= fc
->position
1804 && fc
->position
<= form
->form_end
)
1807 move_to_top_of_list(form_controls
, fc
);
1811 /* Re-insert the form items the correct places. */
1813 foreachsafe (fc
, next
, form_controls
) {
1815 foreach (form
, document
->forms
) {
1816 if (fc
->position
< form
->form_num
1817 || form
->form_end
< fc
->position
)
1821 move_to_top_of_list(form
->items
, fc
);
1826 assert(list_empty(form_controls
));
1830 color_link_lines(struct html_context
*html_context
)
1832 struct document
*document
= html_context
->part
->document
;
1833 struct color_pair colors
= INIT_COLOR_PAIR(par_format
.bgcolor
, 0x0);
1834 enum color_mode color_mode
= document
->options
.color_mode
;
1835 enum color_flags color_flags
= document
->options
.color_flags
;
1838 for (y
= 0; y
< document
->height
; y
++) {
1841 for (x
= 0; x
< document
->data
[y
].length
; x
++) {
1842 struct screen_char
*schar
= &document
->data
[y
].chars
[x
];
1844 set_term_color(schar
, &colors
, color_flags
, color_mode
);
1846 /* XXX: Entering hack zone! Change to clink color after
1847 * link text has been recolored. */
1848 if (schar
->data
== ':' && colors
.foreground
== 0x0)
1849 colors
.foreground
= format
.clink
;
1852 colors
.foreground
= 0x0;
1857 html_special(struct html_context
*html_context
, enum html_special_type c
, ...)
1861 struct document
*document
;
1862 void *ret_val
= NULL
;
1864 assert(html_context
);
1865 if_assert_failed
return NULL
;
1867 part
= html_context
->part
;
1870 if_assert_failed
return NULL
;
1872 document
= part
->document
;
1878 unsigned char *t
= va_arg(l
, unsigned char *);
1880 html_special_tag(document
, t
, X(part
->cx
), Y(part
->cy
));
1885 struct form
*form
= va_arg(l
, struct form
*);
1887 html_special_form(part
, form
);
1892 struct form_control
*fc
= va_arg(l
, struct form_control
*);
1894 html_special_form_control(part
, fc
);
1898 ret_val
= renderer_context
.convert_table
;
1901 ret_val
= (void *) (long) !!document
;
1903 case SP_CACHE_CONTROL
:
1905 struct cache_entry
*cached
= renderer_context
.cached
;
1907 cached
->cache_mode
= CACHE_MODE_NEVER
;
1911 case SP_CACHE_EXPIRES
:
1913 time_t expires
= va_arg(l
, time_t);
1914 struct cache_entry
*cached
= renderer_context
.cached
;
1916 if (!expires
|| cached
->cache_mode
== CACHE_MODE_NEVER
)
1919 timeval_from_seconds(&cached
->max_age
, expires
);
1925 struct frameset_param
*fsp
= va_arg(l
, struct frameset_param
*);
1926 struct frameset_desc
*frameset_desc
;
1928 if (!fsp
->parent
&& document
->frame_desc
)
1931 frameset_desc
= create_frameset(fsp
);
1932 if (!fsp
->parent
&& !document
->frame_desc
)
1933 document
->frame_desc
= frameset_desc
;
1935 ret_val
= frameset_desc
;
1940 struct frameset_desc
*parent
= va_arg(l
, struct frameset_desc
*);
1941 unsigned char *name
= va_arg(l
, unsigned char *);
1942 unsigned char *url
= va_arg(l
, unsigned char *);
1944 add_frameset_entry(parent
, NULL
, name
, url
);
1948 renderer_context
.nowrap
= !!va_arg(l
, int);
1952 unsigned long seconds
= va_arg(l
, unsigned long);
1953 unsigned char *t
= va_arg(l
, unsigned char *);
1955 document
->refresh
= init_document_refresh(t
, seconds
);
1958 case SP_COLOR_LINK_LINES
:
1959 if (document
&& use_document_bg_colors(&document
->options
))
1960 color_link_lines(html_context
);
1965 struct uri
*uri
= va_arg(l
, struct uri
*);
1967 add_to_uri_list(&document
->css_imports
, uri
);
1972 #ifdef CONFIG_ECMASCRIPT
1974 struct uri
*uri
= va_arg(l
, struct uri
*);
1976 add_to_uri_list(&document
->ecmascript_imports
, uri
);
1988 free_table_cache(void)
1991 struct hash_item
*item
;
1994 /* We do not free key here. */
1995 foreach_hash_item (item
, *table_cache
, i
) {
1996 mem_free_if(item
->value
);
1999 free_hash(&table_cache
);
2000 table_cache_entries
= 0;
2005 format_html_part(struct html_context
*html_context
,
2006 unsigned char *start
, unsigned char *end
,
2007 int align
, int margin
, int width
, struct document
*document
,
2008 int x
, int y
, unsigned char *head
,
2012 struct html_element
*html_state
;
2013 int llm
= renderer_context
.last_link_to_move
;
2014 struct tag
*ltm
= renderer_context
.last_tag_to_move
;
2015 int ef
= renderer_context
.empty_format
;
2016 int lm
= html_context
->margin
;
2018 /* Hash creation if needed. */
2020 table_cache
= init_hash8();
2021 } else if (!document
) {
2022 /* Search for cached entry. */
2023 struct table_cache_entry_key key
;
2024 struct hash_item
*item
;
2026 /* Clear key to prevent potential alignment problem
2027 * when keys are compared. */
2028 memset(&key
, 0, sizeof(key
));
2033 key
.margin
= margin
;
2036 key
.link_num
= link_num
;
2038 item
= get_hash_item(table_cache
,
2039 (unsigned char *) &key
,
2041 if (item
) { /* We found it in cache, so just copy and return. */
2042 part
= mem_alloc(sizeof(*part
));
2044 copy_struct(part
, &((struct table_cache_entry
*)
2045 item
->value
)->part
);
2051 assertm(y
>= 0, "format_html_part: y == %d", y
);
2052 if_assert_failed
return NULL
;
2055 struct node
*node
= mem_alloc(sizeof(*node
));
2058 int node_width
= !html_context
->table_level
? INT_MAX
: width
;
2060 set_box(&node
->box
, x
, y
, node_width
, 1);
2061 add_to_list(document
->nodes
, node
);
2064 renderer_context
.last_link_to_move
= document
->nlinks
;
2065 renderer_context
.last_tag_to_move
= (struct tag
*) &document
->tags
;
2066 renderer_context
.last_tag_for_newline
= (struct tag
*) &document
->tags
;
2068 renderer_context
.last_link_to_move
= 0;
2069 renderer_context
.last_tag_to_move
= (struct tag
*) NULL
;
2070 renderer_context
.last_tag_for_newline
= (struct tag
*) NULL
;
2073 html_context
->margin
= margin
;
2074 renderer_context
.empty_format
= !document
;
2076 done_link_state_info();
2077 renderer_context
.nobreak
= 1;
2079 part
= mem_calloc(1, sizeof(*part
));
2080 if (!part
) goto ret
;
2082 part
->document
= document
;
2087 part
->link_num
= link_num
;
2089 html_state
= init_html_parser_state(html_context
, ELEMENT_IMMORTAL
, align
, margin
, width
);
2091 parse_html(start
, end
, part
, head
, html_context
);
2093 done_html_parser_state(html_context
, html_state
);
2095 int_lower_bound(&part
->max_width
, part
->box
.width
);
2097 renderer_context
.nobreak
= 0;
2099 done_link_state_info();
2100 mem_free_if(part
->spaces
);
2102 mem_free_if(part
->char_width
);
2106 struct node
*node
= document
->nodes
.next
;
2108 node
->box
.height
= y
- node
->box
.y
+ part
->box
.height
;
2112 renderer_context
.last_link_to_move
= llm
;
2113 renderer_context
.last_tag_to_move
= ltm
;
2114 renderer_context
.empty_format
= ef
;
2116 html_context
->margin
= lm
;
2118 if (html_context
->table_level
> 1 && !document
2120 && table_cache_entries
< MAX_TABLE_CACHE_ENTRIES
) {
2121 /* Create a new entry. */
2122 /* Clear memory to prevent bad key comparaison due to alignment
2124 struct table_cache_entry
*tce
= mem_calloc(1, sizeof(*tce
));
2125 /* A goto is used here to prevent a test or code
2129 tce
->key
.start
= start
;
2131 tce
->key
.align
= align
;
2132 tce
->key
.margin
= margin
;
2133 tce
->key
.width
= width
;
2135 tce
->key
.link_num
= link_num
;
2136 copy_struct(&tce
->part
, part
);
2138 if (!add_hash_item(table_cache
,
2139 (unsigned char *) &tce
->key
,
2140 sizeof(tce
->key
), tce
)) {
2143 table_cache_entries
++;
2153 render_html_document(struct cache_entry
*cached
, struct document
*document
,
2154 struct string
*buffer
)
2156 struct html_context
*html_context
;
2158 unsigned char *start
;
2160 struct string title
;
2163 assert(cached
&& document
);
2164 if_assert_failed
return;
2166 if (!init_string(&head
)) return;
2168 if (cached
->head
) add_to_string(&head
, cached
->head
);
2170 start
= buffer
->source
;
2171 end
= buffer
->source
+ buffer
->length
;
2173 html_context
= init_html_parser(cached
->uri
, &document
->options
,
2174 start
, end
, &head
, &title
,
2175 put_chars_conv
, line_break
,
2177 if (!html_context
) return;
2179 renderer_context
.g_ctrl_num
= 0;
2180 renderer_context
.cached
= cached
;
2181 renderer_context
.convert_table
= get_convert_table(head
.source
,
2182 document
->options
.cp
,
2183 document
->options
.assume_cp
,
2185 &document
->cp_status
,
2186 document
->options
.hard_assume
);
2188 html_context
->options
->utf8
= is_cp_utf8(document
->options
.cp
);
2189 #endif /* CONFIG_UTF_8 */
2192 document
->title
= convert_string(renderer_context
.convert_table
,
2193 title
.source
, title
.length
,
2194 document
->options
.cp
,
2195 CSM_DEFAULT
, NULL
, NULL
, NULL
);
2197 done_string(&title
);
2199 part
= format_html_part(html_context
, start
, end
, par_format
.align
,
2200 par_format
.leftmargin
,
2201 document
->options
.box
.width
, document
,
2202 0, 0, head
.source
, 1);
2204 /* Drop empty allocated lines at end of document if any
2205 * and adjust document height. */
2206 while (document
->height
&& !document
->data
[document
->height
- 1].length
)
2207 mem_free_if(document
->data
[--document
->height
].chars
);
2209 /* Calculate document width. */
2213 document
->width
= 0;
2214 for (i
= 0; i
< document
->height
; i
++)
2215 int_lower_bound(&document
->width
, document
->data
[i
].length
);
2219 document
->options
.needs_width
= 1;
2221 /* FIXME: This needs more tuning since if we are centering stuff it
2223 document
->options
.needs_width
=
2224 (document
->width
+ (document
->options
.margin
2225 >= document
->options
.width
));
2228 document
->bgcolor
= par_format
.bgcolor
;
2230 done_html_parser(html_context
);
2232 /* Drop forms which has been serving as a placeholder for form items
2233 * added in the wrong order due to the ordering of table rendering. */
2237 foreach (form
, document
->forms
) {
2241 if (list_empty(form
->items
))
2248 /* @part was residing in html_context so it has to stay alive until
2249 * done_html_parser(). */
2253 #if 0 /* debug purpose */
2255 FILE *f
= fopen("forms", "ab");
2256 struct form_control
*form
;
2258 fprintf(f
,"FORM:\n");
2259 foreach (form
, document
->forms
) {
2260 fprintf(f
, "g=%d f=%d c=%d t:%d\n",
2261 form
->g_ctrl_num
, form
->form_num
,
2262 form
->ctrl_num
, form
->type
);
2264 fprintf(f
,"fragment: \n");
2265 for (qq
= start
; qq
< end
; qq
++) fprintf(f
, "%c", *qq
);
2266 fprintf(f
,"----------\n\n");
2273 find_tag(struct document
*document
, unsigned char *name
, int namelen
)
2277 foreach (tag
, document
->tags
)
2278 if (!strlcasecmp(tag
->name
, -1, name
, namelen
))