etc. */ HTML_SET_TOP_ATTR (html, text_align, DW_STYLE_TEXT_ALIGN_STRING); if ((charattr = Html_get_attr(html, tag, tagsize, "char"))) { if (charattr[0] == 0) /* todo: ALIGN=" ", and even ALIGN="&32;" will reult in * an empty string (don't know whether the latter is * correct, has to be clarified with the specs), so * that for empty strings, " " is assumed. */ HTML_SET_TOP_ATTR (html, text_align_char, ' '); else HTML_SET_TOP_ATTR (html, text_align_char, charattr[0]); } else /* todo: Examine LANG attr of . */ HTML_SET_TOP_ATTR (html, text_align_char, '.'); } } } /* * Evaluates the VALIGN attribute (top|bottom|middle|baseline) and * sets the style in style_attrs. Returns TRUE when set. */ static gboolean Html_tag_set_valign_attr(DilloHtml *html, char *tag, gint tagsize, DwStyle *style_attrs) { const char *attr; if ((attr = Html_get_attr(html, tag, tagsize, "valign"))) { if (g_strcasecmp (attr, "top") == 0) style_attrs->valign = DW_STYLE_VALIGN_TOP; else if (g_strcasecmp (attr, "bottom") == 0) style_attrs->valign = DW_STYLE_VALIGN_BOTTOM; else if (g_strcasecmp (attr, "baseline") == 0) style_attrs->valign = DW_STYLE_VALIGN_BASELINE; else style_attrs->valign = DW_STYLE_VALIGN_MIDDLE; return TRUE; } else return FALSE; } /* * Add a new DwPage into the current DwPage, for indentation. * left and right are the horizontal indentation amounts, space is the * vertical space around the block. */ static void Html_add_indented_widget(DilloHtml *html, DwWidget *page, int left, int right, int space) { DwStyle style_attrs, *style; style_attrs = *html->stack[html->stack_top].style; a_Dw_style_box_set_val(&style_attrs.margin, 0); a_Dw_style_box_set_val(&style_attrs.border_width, 0); a_Dw_style_box_set_val(&style_attrs.padding, 0); /* Activate this for debugging */ #if 0 a_Dw_style_box_set_val(&style_attrs.border_width, 1); a_Dw_style_box_set_border_color (&style_attrs, a_Dw_style_shaded_color_new(style_attrs.color->color_val, html->bw->main_window->window)); a_Dw_style_box_set_border_style(&style_attrs, DW_STYLE_BORDER_DASHED); #endif style_attrs.margin.left = left; style_attrs.margin.right = right; style = a_Dw_style_new (&style_attrs, html->bw->main_window->window); a_Dw_page_add_parbreak (DW_PAGE (html->dw), space, style); a_Dw_page_add_widget (DW_PAGE (html->dw), page, style); a_Dw_page_add_parbreak (DW_PAGE (html->dw), space, style); html->stack[html->stack_top].page = html->dw = page; html->stack[html->stack_top].hand_over_break = TRUE; a_Dw_style_unref (style); /* Handle it when the user clicks on a link */ Html_connect_signals(html, GTK_OBJECT(page)); } /* * Create and add a new indented DwPage to the current DwPage */ static void Html_add_indented(DilloHtml *html, int left, int right, int space) { DwWidget *page = a_Dw_page_new (); Html_add_indented_widget (html, page, left, right, space); } /* * Given a font_size, this will return the correct 'level'. * (or the closest, if the exact level isn't found). */ static gint Html_fontsize_to_level(gint fontsize) { gint i, level; gdouble normalized_size = fontsize / prefs.font_factor, approximation = FontSizes[FontSizesNum-1] + 1; for (i = level = 0; i < FontSizesNum; i++) if (approximation >= fabs(normalized_size - FontSizes[i])) { approximation = fabs(normalized_size - FontSizes[i]); level = i; } else { break; } return level; } /* * Given a level of a font, this will return the correct 'size'. */ static gint Html_level_to_fontsize(gint level) { level = MAX(0, level); level = MIN(FontSizesNum - 1, level); return rint(FontSizes[level]*prefs.font_factor); } /* * Miscelaneous initializations for a DwPage */ static void Html_set_dwpage(DilloHtml *html) { DwWidget *widget; DwPage *page; DwStyle style_attrs; DwStyleFont font; g_return_if_fail (html->dw == NULL); widget = a_Dw_page_new (); page = DW_PAGE (widget); html->dw = html->stack[0].page = widget; /* Create a dummy font, attribute, and tag for the bottom of the stack. */ font.name = prefs.vw_fontname; /* Helvetica */ font.size = Html_level_to_fontsize(FontSizesBase); font.weight = 400; font.style = DW_STYLE_FONT_STYLE_NORMAL; a_Dw_style_init_values (&style_attrs, html->bw->main_window->window); style_attrs.font = a_Dw_style_font_new (&font); style_attrs.color = a_Dw_style_color_new (prefs.text_color, html->bw->main_window->window); html->stack[0].style = a_Dw_style_new (&style_attrs, html->bw->main_window->window); html->stack[0].table_cell_style = NULL; /* Handle it when the user clicks on a link */ Html_connect_signals(html, GTK_OBJECT(widget)); gtk_signal_connect_while_alive ( GTK_OBJECT(GTK_BIN(html->bw->docwin)->child), "button_press_event", GTK_SIGNAL_FUNC(Html_page_menu), (gpointer)html->bw, GTK_OBJECT (page)); /* Connect the "bug meter" button-press to the linkblock */ gtk_signal_connect_while_alive( GTK_OBJECT (html->bw->status_bug_meter), "clicked", GTK_SIGNAL_FUNC (a_Commands_view_page_bugs_callback), (gpointer)html->linkblock, GTK_OBJECT (page)); gtk_signal_connect_while_alive( GTK_OBJECT (html->bw->status_bug_meter), "clicked1", GTK_SIGNAL_FUNC (a_Commands_view_page_bugs_callback), (gpointer)html->linkblock, GTK_OBJECT (page)); /* also connect with the "View page Bugs" menuitem */ gtk_signal_connect_while_alive( GTK_OBJECT (html->bw->viewbugs_menuitem), "activate", GTK_SIGNAL_FUNC (a_Commands_view_page_bugs_callback), (gpointer)html->linkblock, GTK_OBJECT (page)); /* Destroy the linkblock when the DwPage is destroyed */ gtk_signal_connect_object(GTK_OBJECT(page), "destroy", GTK_SIGNAL_FUNC(Html_lb_free), (gpointer)html->linkblock); } /* * Create and initialize a new DilloHtml structure */ static DilloHtml *Html_new(BrowserWindow *bw, const DilloUrl *url) { DilloHtml *html; html = g_new(DilloHtml, 1); html->Start_Buf = NULL; html->Start_Ofs = 0; html->CurrTagOfs = 0; html->OldTagOfs = 0; html->OldTagLine = 1; html->DocType = DT_NONE; /* assume Tag Soup 0.0! :-) */ html->DocTypeVersion = 0.0f; html->dw = NULL; html->bw = bw; html->linkblock = Html_lb_new(bw, url); html->stack_max = 16; html->stack_top = 0; html->stack = g_new(DilloHtmlState, html->stack_max); html->stack[0].tag_name = g_strdup("none"); html->stack[0].style = NULL; html->stack[0].table_cell_style = NULL; html->stack[0].parse_mode = DILLO_HTML_PARSE_MODE_INIT; html->stack[0].table_mode = DILLO_HTML_TABLE_MODE_NONE; html->stack[0].cell_text_align_set = FALSE; html->stack[0].list_type = HTML_LIST_NONE; /* no
\n");
offset = TAB_SIZE - html->pre_column % TAB_SIZE;
a_Dw_page_add_text(DW_PAGE (html->dw),
g_strnfill(offset, ' '),
html->stack[html->stack_top].style);
html->pre_column += offset;
break;
default:
a_Dw_page_add_text(DW_PAGE (html->dw),
g_strndup(space + i, 1),
html->stack[html->stack_top].style);
html->pre_column++;
break;
}
html->PrevWasCR = (space[i] == '\r');
}
html->SPCPending = FALSE;
} else {
if (SGML_SPCDEL && html->PrevWasOpenTag) {
/* SGML_SPCDEL ignores white space inmediately after an open tag */
html->SPCPending = FALSE;
} else {
g_free(html->SPCBuf);
html->SPCBuf = g_strndup(space, spacesize);
html->SPCPending = TRUE;
}
if ( parse_mode == DILLO_HTML_PARSE_MODE_STASH_AND_BODY )
html->StashSpace = (html->Stash->len > 0);
}
}
/*
* Handles putting the word into its proper place
* > STASH and VERBATIM --> html->Stash
* > otherwise it goes through a_Dw_page_add_text()
*
* Entities are parsed (or not) according to parse_mode.
*/
static void Html_process_word(DilloHtml *html, char *word, gint size)
{
gint i, start;
gchar *Pword;
DilloHtmlParseMode parse_mode = html->stack[html->stack_top].parse_mode;
if ( parse_mode == DILLO_HTML_PARSE_MODE_STASH ||
parse_mode == DILLO_HTML_PARSE_MODE_STASH_AND_BODY ) {
if ( html->StashSpace ) {
g_string_append_c(html->Stash, ' ');
html->StashSpace = FALSE;
}
Pword = Html_parse_entities(html, word, size);
g_string_append(html->Stash, Pword);
g_free(Pword);
} else if ( parse_mode == DILLO_HTML_PARSE_MODE_VERBATIM ) {
/* word goes in untouched, it is not processed here. */
Pword = g_strndup(word, size);
g_string_append(html->Stash, Pword);
g_free(Pword);
}
if ( parse_mode == DILLO_HTML_PARSE_MODE_STASH ||
parse_mode == DILLO_HTML_PARSE_MODE_VERBATIM ) {
/* skip until the closing instructions */
} else if ( parse_mode == DILLO_HTML_PARSE_MODE_PRE ) {
/* all this overhead is to catch white-space entities */
Pword = Html_parse_entities(html, word, size);
for (start = i = 0; Pword[i]; start = i)
if (isspace(Pword[i])) {
while (Pword[++i] && isspace(Pword[i]));
Html_process_space(html, Pword + start, i - start);
} else {
while (Pword[++i] && !isspace(Pword[i]));
a_Dw_page_add_text(DW_PAGE (html->dw),
g_strndup(Pword + start, i - start),
html->stack[html->stack_top].style);
html->pre_column += i - start;
html->PreFirstChar = FALSE;
}
g_free(Pword);
} else {
/* add pending space if present */
if (html->SPCPending && (!SGML_SPCDEL || !html->PrevWasOpenTag))
/* SGML_SPCDEL ignores space after an open tag */
a_Dw_page_add_space(DW_PAGE (html->dw),
html->stack[html->stack_top].style);
/* actually white-space entities inside the word could be
* collapsed (except ), but that's too much overhead
* for a very rare case of ill-formed HTML --Jcid */
Pword = Html_parse_entities(html, word, size);
g_strdelimit(Pword, "\t\f\n\r", ' ');
a_Dw_page_add_text(DW_PAGE (html->dw),
Pword,
html->stack[html->stack_top].style);
}
html->PrevWasOpenTag = FALSE;
html->SPCPending = FALSE;
}
/*
* Does the tag in tagstr (e.g. "p") match the tag in the tag, tagsize
* structure, with the initial < skipped over (e.g. "P align=center>")
*/
static gboolean Html_match_tag(const char *tagstr, char *tag, gint tagsize)
{
gint i;
for (i = 0; i < tagsize && tagstr[i] != '\0'; i++) {
if (tolower(tagstr[i]) != tolower(tag[i]))
return FALSE;
}
/* The test for '/' is for xml compatibility: "empty/>" will be matched. */
if (i < tagsize && (isspace(tag[i]) || tag[i] == '>' || tag[i] == '/'))
return TRUE;
return FALSE;
}
/*
* This function is called after popping the stack, to
* handle nested DwPage widgets.
*/
static void Html_eventually_pop_dw(DilloHtml *html)
{
/* This function is called after popping from the stack, so the
* relevant hand_over_break is at html->stack_top + 1. */
if (html->dw != html->stack[html->stack_top].page) {
if (html->stack[html->stack_top + 1].hand_over_break)
a_Dw_page_hand_over_break(DW_PAGE(html->dw),
html->stack[(html)->stack_top].style);
a_Dw_page_flush(DW_PAGE(html->dw));
html->dw = html->stack[html->stack_top].page;
}
}
/*
* Push the tag (copying attributes from the top of the stack)
*/
static void Html_push_tag(DilloHtml *html, gint tag_idx)
{
char *tagstr;
gint n_items;
/* Save the element's name (no parameters) into tagstr. */
tagstr = g_strdup(Tags[tag_idx].name);
n_items = html->stack_top + 1;
a_List_add(html->stack, n_items, html->stack_max);
/* We'll copy the former stack item and just change the tag and its index
* instead of copying all fields except for tag. --Jcid */
html->stack[n_items] = html->stack[n_items - 1];
html->stack[n_items].tag_name = tagstr;
html->stack[n_items].tag_idx = tag_idx;
html->stack_top = n_items;
/* proper memory management, may be unref'd later */
a_Dw_style_ref (html->stack[html->stack_top].style);
if (html->stack[html->stack_top].table_cell_style)
a_Dw_style_ref (html->stack[html->stack_top].table_cell_style);
html->dw = html->stack[html->stack_top].page;
}
/*
* Push the tag (used to force en element with optional open into the stack)
* Note: now it's the same as Html_push_tag(), but things may change...
*/
static void Html_force_push_tag(DilloHtml *html, gint tag_idx)
{
Html_push_tag(html, tag_idx);
}
/*
* Pop the top tag in the stack
*/
static void Html_real_pop_tag(DilloHtml *html)
{
a_Dw_style_unref (html->stack[html->stack_top].style);
if (html->stack[html->stack_top].table_cell_style)
a_Dw_style_unref (html->stack[html->stack_top].table_cell_style);
g_free(html->stack[html->stack_top--].tag_name);
Html_eventually_pop_dw(html);
}
/*
* Default close function for tags.
* (conditional cleanup of the stack)
* There're several ways of doing it. Considering the HTML 4.01 spec
* which defines optional close tags, and the will to deliver useful diagnose
* messages for bad-formed HTML, it'll go as follows:
* 1.- Search the stack for the first tag that requires a close tag.
* 2.- If it matches, clean all the optional-close tags in between.
* 3.- Cleanup the matching tag. (on error, give a warning message)
*
* If 'w3c_mode' is NOT enabled:
* 1.- Search the stack for a matching tag based on tag level.
* 2.- If it exists, clean all the tags in between.
* 3.- Cleanup the matching tag. (on error, give a warning message)
*/
static void Html_tag_cleanup_at_close(DilloHtml *html, gint TagIdx)
{
gint w3c_mode = !prefs.w3c_plus_heuristics;
gint stack_idx, cmp = 1;
gint new_idx = TagIdx;
if (html->CloseOneTag) {
Html_real_pop_tag(html);
html->CloseOneTag = FALSE;
return;
}
/* Look for the candidate tag to close */
stack_idx = html->stack_top;
while (stack_idx &&
(cmp = (new_idx != html->stack[stack_idx].tag_idx)) &&
((w3c_mode &&
Tags[html->stack[stack_idx].tag_idx].EndTag == 'O') ||
(!w3c_mode &&
Tags[html->stack[stack_idx].tag_idx].TagLevel <
Tags[new_idx].TagLevel))) {
--stack_idx;
}
/* clean, up to the matching tag */
if (cmp == 0 && stack_idx > 0) {
/* There's a valid matching tag in the stack */
while (html->stack_top >= stack_idx) {
gint toptag_idx = html->stack[html->stack_top].tag_idx;
/* Warn when we decide to close an open tag (for !w3c_mode) */
if (html->stack_top > stack_idx &&
Tags[toptag_idx].EndTag != 'O')
MSG_HTML(" - forcing close of open tag: <%s>\n",
Tags[toptag_idx].name);
/* Close this and only this tag */
html->CloseOneTag = TRUE;
Tags[toptag_idx].close (html, toptag_idx);
}
} else {
MSG_HTML("unexpected closing tag: %s>. -- expected %s>\n",
Tags[new_idx].name, html->stack[stack_idx].tag_name);
}
}
/*
* Cleanup (conditional), and Pop the tag (if it matches)
*/
static void Html_pop_tag(DilloHtml *html, gint TagIdx)
{
Html_tag_cleanup_at_close(html, TagIdx);
}
/*
* Some parsing routines.
*/
/*
* Used by Html_parse_length
*/
static DwStyleLength Html_parse_length_or_multi_length (const gchar *attr,
gchar **endptr)
{
DwStyleLength l;
double v;
gchar *end;
v = strtod (attr, &end);
switch (*end) {
case '%':
end++;
l = DW_STYLE_CREATE_PER_LENGTH (v / 100);
break;
case '*':
end++;
l = DW_STYLE_CREATE_REL_LENGTH (v);
break;
/*
The "px" suffix seems not allowed by HTML4.01 SPEC.
case 'p':
if (end[1] == 'x')
end += 2;
*/
default:
l = DW_STYLE_CREATE_ABS_LENGTH ((gint)v);
break;
}
if (endptr)
*endptr = end;
return l;
}
/*
* Returns a length or a percentage, or DW_STYLE_UNDEF_LENGTH in case
* of an error, or if attr is NULL.
*/
static DwStyleLength Html_parse_length (DilloHtml *html, const gchar *attr)
{
DwStyleLength l;
gchar *end;
l = Html_parse_length_or_multi_length (attr, &end);
if (DW_STYLE_IS_REL_LENGTH (l))
/* not allowed as &Length; */
return DW_STYLE_LENGTH_AUTO;
else {
/* allow only whitespaces */
if (*end && !isspace (*end)) {
MSG_HTML("Garbage after length: %s\n", attr);
return DW_STYLE_LENGTH_AUTO;
}
}
return l;
}
/*
* Parse a color attribute.
* Return value: parsed color, or default_color (+ error msg) on error.
*/
static gint32
Html_color_parse(DilloHtml *html, const char *subtag, gint32 default_color)
{
gint err = 1;
gint32 color = a_Color_parse(subtag, default_color, &err);
if (err) {
MSG_HTML("color is not in \"#RRGGBB\" format\n");
}
return color;
}
/*
* Check that 'val' is composed of characters inside [A-Za-z0-9:_.-]
* Note: ID can't have entities, but this check is enough (no '&').
* Return value: 1 if OK, 0 otherwise.
*/
static gint
Html_check_name_val(DilloHtml *html, const char *val, const char *attrname)
{
gint i;
for (i = 0; val[i]; ++i)
if (!(isalnum(val[i]) || strchr(":_.-", val[i])))
break;
if (val[i] || !isalpha(val[0]))
MSG_HTML("'%s' value is not of the form "
"[A-Za-z][A-Za-z0-9:_.-]*\n", attrname);
return !(val[i]);
}
/*
* Handle DOCTYPE declaration
*
* Follows the convention that HTML 4.01
* doctypes which include a full w3c DTD url are treated as
* standards-compliant, but 4.01 without the url and HTML 4.0 and
* earlier are not. XHTML doctypes are always standards-compliant
* whether or not an url is present.
*
* Note: I'm not sure about this convention. The W3C validator
* recognizes the "HTML Level" with or without the URL. The convention
* comes from mozilla (see URLs below), but Dillo doesn't have the same
* rendering modes, so it may be better to chose another behaviour. --Jcid
*
* http://www.mozilla.org/docs/web-developer/quirks/doctypes.html
* http://lists.auriga.wearlab.de/pipermail/dillo-dev/2004-October/002300.html
*
* This is not a full DOCTYPE parser, just enough for what Dillo uses.
*/
static void Html_parse_doctype(DilloHtml *html, char *tag, gint tagsize)
{
char *HTML_sig = "DocType = DT_HTML;
html->DocTypeVersion = 4.01f;
} else if (!strncmp(p, XHTML1, strlen(XHTML1)) &&
a_Misc_stristr(p + strlen(XHTML1), XHTML1_url)) {
html->DocType = DT_XHTML;
html->DocTypeVersion = 1.0f;
} else if (!strncmp(p, XHTML11, strlen(XHTML11)) &&
a_Misc_stristr(p + strlen(XHTML11), XHTML11_url)) {
html->DocType = DT_XHTML;
html->DocTypeVersion = 1.1f;
} else if (!strncmp(p, HTML40, strlen(HTML40))) {
html->DocType = DT_HTML;
html->DocTypeVersion = 4.0f;
} else if (!strncmp(p, HTML32, strlen(HTML32))) {
html->DocType = DT_HTML;
html->DocTypeVersion = 3.2f;
} else if (!strncmp(p, HTML20, strlen(HTML20))) {
html->DocType = DT_HTML;
html->DocTypeVersion = 2.0f;
}
}
g_free(ntag);
}
/*
* Handle open HTML element
*/
static void Html_tag_open_html(DilloHtml *html, char *tag, gint tagsize)
{
if (!(html->InFlags & IN_HTML))
html->InFlags |= IN_HTML;
++html->Num_HTML;
if (html->Num_HTML > 1) {
MSG_HTML("HTML element was already open\n");
}
}
/*
* Handle close HTML element
*/
static void Html_tag_close_html(DilloHtml *html, gint TagIdx)
{
/* todo: may add some checks here */
if (html->Num_HTML == 1) {
/* beware of pages with multiple HTML close tags... :-P */
html->InFlags &= ~IN_HTML;
}
Html_pop_tag(html, TagIdx);
}
/*
* Handle open HEAD element
*/
static void Html_tag_open_head(DilloHtml *html, char *tag, gint tagsize)
{
if (html->InFlags & IN_BODY) {
MSG_HTML("HEAD element must go before the BODY section\n");
html->ReqTagClose = TRUE;
return;
}
if (!(html->InFlags & IN_HEAD))
html->InFlags |= IN_HEAD;
++html->Num_HEAD;
if (html->Num_HEAD > 1) {
MSG_HTML("HEAD element was already open\n");
}
}
/*
* Handle close HEAD element
* Note: as a side effect of Html_test_section() this function is called
* twice when the head element is closed implicitly.
*/
static void Html_tag_close_head(DilloHtml *html, gint TagIdx)
{
if (html->InFlags & IN_HEAD) {
if (html->Num_TITLE == 0)
MSG_HTML("HEAD section lacks the TITLE element\n");
html->InFlags &= ~IN_HEAD;
}
Html_pop_tag(html, TagIdx);
}
/*
* Handle open TITLE
* calls stash init, where the title string will be stored
*/
static void Html_tag_open_title(DilloHtml *html, char *tag, gint tagsize)
{
++html->Num_TITLE;
Html_stash_init(html);
}
/*
* Handle close TITLE
* set page-title in the browser window and in the history.
*/
static void Html_tag_close_title(DilloHtml *html, gint TagIdx)
{
if (html->InFlags & IN_HEAD) {
/* title is only valid inside HEAD */
a_Interface_set_page_title(html->linkblock->bw, html->Stash->str);
a_History_set_title(NAV_TOP(html->linkblock->bw), html->Stash->str);
} else {
MSG_HTML("the TITLE element must be inside the HEAD section\n");
}
Html_pop_tag(html, TagIdx);
}
/*
* Handle open SCRIPT
* initializes stash, where the embedded code will be stored.
* MODE_VERBATIM is used because MODE_STASH catches entities.
*/
static void Html_tag_open_script(DilloHtml *html, char *tag, gint tagsize)
{
Html_stash_init(html);
html->stack[html->stack_top].parse_mode = DILLO_HTML_PARSE_MODE_VERBATIM;
}
/*
* Handle close SCRIPT
*/
static void Html_tag_close_script(DilloHtml *html, gint TagIdx)
{
/* eventually the stash will be sent to an interpreter for parsing */
Html_pop_tag(html, TagIdx);
}
/*
* Handle open STYLE
* store the contents to the stash where (in the future) the style
* sheet interpreter can get it.
*/
static void Html_tag_open_style(DilloHtml *html, char *tag, gint tagsize)
{
Html_stash_init(html);
html->stack[html->stack_top].parse_mode = DILLO_HTML_PARSE_MODE_VERBATIM;
}
/*
* Handle close STYLE
*/
static void Html_tag_close_style(DilloHtml *html, gint TagIdx)
{
/* eventually the stash will be sent to an interpreter for parsing */
Html_pop_tag(html, TagIdx);
}
/*
*
*/
static void Html_tag_open_body(DilloHtml *html, char *tag, gint tagsize)
{
const char *attrbuf;
DwPage *page;
DwStyle style_attrs, *style;
gint32 color;
if (!(html->InFlags & IN_BODY))
html->InFlags |= IN_BODY;
++html->Num_BODY;
if (html->Num_BODY > 1) {
MSG_HTML("BODY element was already open\n");
return;
}
if (html->InFlags & IN_HEAD) {
/* if we're here, it's bad XHTML, no need to recover */
MSG_HTML("unclosed HEAD element\n");
}
page = DW_PAGE (html->dw);
if (!prefs.force_my_colors) {
if ((attrbuf = Html_get_attr(html, tag, tagsize, "bgcolor"))) {
color = Html_color_parse(html, attrbuf, prefs.bg_color);
if ( (color == 0xffffff && !prefs.allow_white_bg) ||
prefs.force_my_colors )
color = prefs.bg_color;
style_attrs = *html->dw->style;
style_attrs.background_color =
a_Dw_style_color_new (color, html->bw->main_window->window);
style = a_Dw_style_new (&style_attrs, html->bw->main_window->window);
a_Dw_widget_set_style (html->dw, style);
a_Dw_style_unref (style);
html->stack[html->stack_top].current_bg_color = color;
}
if ((attrbuf = Html_get_attr(html, tag, tagsize, "text"))) {
color = Html_color_parse(html, attrbuf, prefs.text_color);
HTML_SET_TOP_ATTR
(html, color,
a_Dw_style_color_new (color, html->bw->main_window->window));
}
if ((attrbuf = Html_get_attr(html, tag, tagsize, "link")))
html->linkblock->link_color = Html_color_parse(html, attrbuf,
prefs.link_color);
if ((attrbuf = Html_get_attr(html, tag, tagsize, "vlink")))
html->linkblock->visited_color =
Html_color_parse(html, attrbuf, prefs.visited_color);
if (prefs.contrast_visited_color) {
/* get a color that has a "safe distance" from text, link and bg */
html->linkblock->visited_color =
a_Color_vc(html->linkblock->visited_color,
html->stack[html->stack_top].style->color->color_val,
html->linkblock->link_color,
html->stack[html->stack_top].current_bg_color);
}
}
html->stack[html->stack_top].parse_mode = DILLO_HTML_PARSE_MODE_BODY;
}
/*
* BODY
*/
static void Html_tag_close_body(DilloHtml *html, gint TagIdx)
{
if (html->Num_BODY == 1) {
/* some tag soup pages use multiple BODY tags... */
html->InFlags &= ~IN_BODY;
}
Html_pop_tag(html, TagIdx);
}
/*
*
* todo: what's the point between adding the parbreak before and
* after the push?
*/
static void Html_tag_open_p(DilloHtml *html, char *tag, gint tagsize)
{
a_Dw_page_add_parbreak(DW_PAGE (html->dw), 9,
html->stack[(html)->stack_top].style);
Html_tag_set_align_attr (html, tag, tagsize);
}
/*
*
| and | */ static void Html_tag_open_table_cell(DilloHtml *html, char *tag, gint tagsize, DwStyleTextAlignType text_align) { #ifdef USE_TABLES DwWidget *col_page; gint colspan = 1, rowspan = 1; const char *attrbuf; DwStyle style_attrs, *style, *old_style; gint32 bgcolor; gboolean new_style; switch (html->stack[html->stack_top].table_mode) { case DILLO_HTML_TABLE_MODE_NONE: MSG_HTML(" | or | outside
support! */
{"div", B8(011110),'R',2, Html_tag_open_div, Html_tag_close_div},
{"dl", B8(011010),'R',2, Html_tag_open_dl, Html_tag_close_par},
{"dt", B8(010110),'O',1, Html_tag_open_dt, Html_tag_close_par},
{"em", B8(010101),'R',2, Html_tag_open_em, Html_tag_close_default},
/* fieldset */
{"font", B8(010101),'R',2, Html_tag_open_font, Html_tag_close_default},
{"form", B8(011110),'R',2, Html_tag_open_form, Html_tag_close_form},
{"frame", B8(010010),'F',0, Html_tag_open_frame, Html_tag_close_default},
{"frameset", B8(011110),'R',2,Html_tag_open_frameset, Html_tag_close_default},
{"h1", B8(010110),'R',2, Html_tag_open_h, Html_tag_close_h},
{"h2", B8(010110),'R',2, Html_tag_open_h, Html_tag_close_h},
{"h3", B8(010110),'R',2, Html_tag_open_h, Html_tag_close_h},
{"h4", B8(010110),'R',2, Html_tag_open_h, Html_tag_close_h},
{"h5", B8(010110),'R',2, Html_tag_open_h, Html_tag_close_h},
{"h6", B8(010110),'R',2, Html_tag_open_h, Html_tag_close_h},
{"head", B8(101101),'O',1, Html_tag_open_head, Html_tag_close_head},
{"hr", B8(010010),'F',0, Html_tag_open_hr, Html_tag_close_default},
{"html", B8(001110),'O',1, Html_tag_open_html, Html_tag_close_html},
{"i", B8(010101),'R',2, Html_tag_open_i, Html_tag_close_default},
{"iframe", B8(011110),'R',2, Html_tag_open_frame, Html_tag_close_default},
{"img", B8(010001),'F',0, Html_tag_open_img, Html_tag_close_default},
{"input", B8(010001),'F',0, Html_tag_open_input, Html_tag_close_default},
/* ins */
{"isindex", B8(110001),'F',0, Html_tag_open_isindex, Html_tag_close_default},
{"kbd", B8(010101),'R',2, Html_tag_open_kbd, Html_tag_close_default},
/* label 010101 */
/* legend 01?? */
{"li", B8(011110),'O',1, Html_tag_open_li, Html_tag_close_default},
/* link 100000 'F' */
{"map", B8(011001),'R',2, Html_tag_open_map, Html_tag_close_map},
/* menu 1010 -- todo: not exactly 1010, it can contain LI and inline */
{"menu", B8(011010),'R',2, Html_tag_open_menu, Html_tag_close_par},
{"meta", B8(100001),'F',0, Html_tag_open_meta, Html_tag_close_default},
/* noframes 1011 */
/* noscript 1011 */
/* object 11xxxx */
{"ol", B8(011010),'R',2, Html_tag_open_ol, Html_tag_close_par},
/* optgroup */
{"option", B8(010001),'O',1, Html_tag_open_option, Html_tag_close_default},
{"p", B8(010110),'O',1, Html_tag_open_p, Html_tag_close_par},
/* param 010001 'F' */
{"pre", B8(010110),'R',2, Html_tag_open_pre, Html_tag_close_pre},
/* q 010101 */
{"s", B8(010101),'R',2, Html_tag_open_strike, Html_tag_close_default},
{"samp", B8(010101),'R',2, Html_tag_open_samp, Html_tag_close_default},
{"script", B8(111001),'R',2, Html_tag_open_script, Html_tag_close_script},
{"select", B8(011001),'R',2, Html_tag_open_select, Html_tag_close_select},
{"small", B8(010101),'R',2, Html_tag_open_big_small, Html_tag_close_default},
/* span 0101 */
{"strike", B8(010101),'R',2, Html_tag_open_strike, Html_tag_close_default},
{"strong", B8(010101),'R',2, Html_tag_open_strong, Html_tag_close_default},
{"style", B8(100101),'R',2, Html_tag_open_style, Html_tag_close_style},
{"sub", B8(010101),'R',2, Html_tag_open_sub, Html_tag_close_default},
{"sup", B8(010101),'R',2, Html_tag_open_sup, Html_tag_close_default},
{"table", B8(011010),'R',5, Html_tag_open_table, Html_tag_close_div},
/* tbody */
{"td", B8(011110),'O',3, Html_tag_open_td, Html_tag_close_default},
{"textarea", B8(010101),'R',2,Html_tag_open_textarea,Html_tag_close_textarea},
/* tfoot */
{"th", B8(011110),'O',1, Html_tag_open_th, Html_tag_close_default},
/* thead */
{"title", B8(100101),'R',2, Html_tag_open_title, Html_tag_close_title},
{"tr", B8(011010),'O',4, Html_tag_open_tr, Html_tag_close_default},
{"tt", B8(010101),'R',2, Html_tag_open_tt, Html_tag_close_default},
{"u", B8(010101),'R',2, Html_tag_open_u, Html_tag_close_default},
{"ul", B8(011010),'R',2, Html_tag_open_ul, Html_tag_close_par},
{"var", B8(010101),'R',2, Html_tag_open_var, Html_tag_close_default}
};
#define NTAGS (sizeof(Tags)/sizeof(Tags[0]))
/*
* Compares tag from buffer ('/' or '>' or space-ended string) [p1]
* with tag from taglist (lowercase, zero ended string) [p2]
* Return value: as strcmp()
*/
static gint Html_tag_compare(char *p1, char *p2)
{
while ( *p2 ) {
if ( tolower(*p1) != *p2 )
return(tolower(*p1) - *p2);
++p1;
++p2;
}
return !strchr(" >/\n\r\t", *p1);
}
/*
* Get 'tag' index
* return -1 if tag is not handled yet
*/
static gint Html_tag_index(char *tag)
{
gint low, high, mid, cond;
/* Binary search */
low = 0;
high = NTAGS - 1; /* Last tag index */
while (low <= high) {
mid = (low + high) / 2;
if ((cond = Html_tag_compare(tag, Tags[mid].name)) < 0 )
high = mid - 1;
else if (cond > 0)
low = mid + 1;
else
return mid;
}
return -1;
}
/*
* For elements with optional close, check whether is time to close.
* Return value: (1: Close, 0: Don't close)
* --tuned for speed.
*/
static gint Html_needs_optional_close(gint old_idx, gint cur_idx)
{
static gint i_P = -1, i_LI, i_TD, i_TR, i_TH, i_DD, i_DT, i_OPTION;
/* i_THEAD, i_TFOOT, i_COLGROUP; */
if (i_P == -1) {
/* initialize the indexes of elements with optional close */
i_P = Html_tag_index("p"),
i_LI = Html_tag_index("li"),
i_TD = Html_tag_index("td"),
i_TR = Html_tag_index("tr"),
i_TH = Html_tag_index("th"),
i_DD = Html_tag_index("dd"),
i_DT = Html_tag_index("dt"),
i_OPTION = Html_tag_index("option");
/* i_THEAD = Html_tag_index("thead"); */
/* i_TFOOT = Html_tag_index("tfoot"); */
/* i_COLGROUP = Html_tag_index("colgroup"); */
}
if (old_idx == i_P || old_idx == i_DT) {
/* P and DT are closed by block elements */
return (Tags[cur_idx].Flags & 2);
} else if (old_idx == i_LI) {
/* LI closes LI */
return (cur_idx == i_LI);
} else if (old_idx == i_TD || old_idx == i_TH) {
/* TD and TH are closed by TD, TH and TR */
return (cur_idx == i_TD || cur_idx == i_TH || cur_idx == i_TR);
} else if (old_idx == i_TR) {
/* TR closes TR */
return (cur_idx == i_TR);
} else if (old_idx == i_DD) {
/* DD is closed by DD and DT */
return (cur_idx == i_DD || cur_idx == i_DT);
} else if (old_idx == i_OPTION) {
return 1; /* OPTION always needs close */
}
/* HTML, HEAD, BODY are handled by Html_test_section(), not here. */
/* todo: TBODY is pending */
return 0;
}
/*
* Conditional cleanup of the stack (at open time).
* - This helps catching block elements inside inline containers (a BUG).
* - It also closes elements with "optional" close tag.
*
* This function is called when opening a block element or |
|---|