2005-03-26 04:12:27 +03:00
|
|
|
/*
|
|
|
|
* Copyright 2005 James Bursa <bursa@users.sourceforge.net>
|
|
|
|
* Copyright 2003 Phil Mellor <monkeyson@users.sourceforge.net>
|
|
|
|
* Copyright 2005 John M Bell <jmb202@ecs.soton.ac.uk>
|
2006-09-02 19:52:41 +04:00
|
|
|
* Copyright 2006 Richard Wilson <info@tinct.net>
|
2008-02-25 19:37:48 +03:00
|
|
|
* Copyright 2008 Michael Drake <tlsa@netsurf-browser.org>
|
2007-08-08 20:16:03 +04:00
|
|
|
*
|
|
|
|
* This file is part of NetSurf, http://www.netsurf-browser.org/
|
|
|
|
*
|
|
|
|
* NetSurf is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation; version 2 of the License.
|
|
|
|
*
|
|
|
|
* NetSurf is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
2005-03-26 04:12:27 +03:00
|
|
|
*/
|
|
|
|
|
|
|
|
/** \file
|
|
|
|
* Conversion of XML tree to box tree (implementation).
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <assert.h>
|
|
|
|
#include <ctype.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdbool.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
2007-01-30 01:27:15 +03:00
|
|
|
#include <strings.h>
|
2007-05-31 02:39:54 +04:00
|
|
|
#include <libxml/HTMLparser.h>
|
|
|
|
#include <libxml/parserInternals.h>
|
|
|
|
#include "utils/config.h"
|
2010-03-28 16:56:39 +04:00
|
|
|
#include "content/content_protected.h"
|
2007-05-31 02:39:54 +04:00
|
|
|
#include "css/css.h"
|
2009-07-24 03:05:34 +04:00
|
|
|
#include "css/utils.h"
|
|
|
|
#include "css/select.h"
|
2007-05-31 02:39:54 +04:00
|
|
|
#include "desktop/options.h"
|
|
|
|
#include "render/box.h"
|
|
|
|
#include "render/form.h"
|
2011-05-07 00:40:09 +04:00
|
|
|
#include "render/html_internal.h"
|
The core code has always assumed a locale of "C".
Do not change the locale globally, else things will break in weird and
wonderful ways.
Introduce utils/locale.[ch], which provide locale-specific wrappers for various
functions (currently just the <ctype.h> ones).
Fix up the few places I can see that actually require that the underlying
locale is paid attention to.
Some notes:
1) The GTK frontend code has not been touched. It is possible that reading of
numeric values (e.g. from the preferences dialogue) may break with this
change, particularly in locales that use something other than '.' as their
decimal separator.
2) The search code is left unchanged (i.e. assuming a locale of "C").
This may break case insensitive matching of non-ASCII characters.
I doubt that ever actually worked, anyway. In future, it should use
Unicode case conversion to achieve the same effect.
3) The text input handling in the core makes use of isspace() to detect
word boundaries. This is fine for western languages (even in the C locale,
which it's currently assuming). It will, however, break for CJK et. al.
(this has always been the case, rather than being a new issue)
4) text-transform uses locale-specific variants of to{lower,upper}. In future
this should probably be performing Unicode case conversion. This is the
only part of the core code that makes use of locale information.
In future, if you require locale-specific behaviour, do the following:
setlocale(LC_<whatever>, "");
<your operation(s) here>
setlocale(LC_<whatever>, "C");
The first setlocale will change the current locale to the native environment.
The second setlocale will reset the current locale to "C".
Any value other than "" or "C" is probably a bug, unless there's a really
good reason for it.
In the long term, it is expected that all locale-dependent code will reside in
platform frontends -- the core being wholly locale agnostic (though assuming
"C" for things like decimal separators).
svn path=/trunk/netsurf/; revision=4153
2008-05-13 18:37:44 +04:00
|
|
|
#include "utils/locale.h"
|
2007-05-31 02:39:54 +04:00
|
|
|
#include "utils/log.h"
|
|
|
|
#include "utils/messages.h"
|
2011-09-29 23:15:54 +04:00
|
|
|
#include "utils/schedule.h"
|
2007-05-31 02:39:54 +04:00
|
|
|
#include "utils/talloc.h"
|
|
|
|
#include "utils/url.h"
|
|
|
|
#include "utils/utils.h"
|
2005-03-26 04:12:27 +03:00
|
|
|
|
2011-09-29 23:15:54 +04:00
|
|
|
/**
|
|
|
|
* Context for box tree construction
|
|
|
|
*/
|
|
|
|
struct box_construct_ctx {
|
|
|
|
html_content *content; /**< Content we're constructing for */
|
|
|
|
|
|
|
|
xmlNode *n; /**< Current node to process */
|
|
|
|
|
|
|
|
struct box *root_box; /**< Root box in the tree */
|
|
|
|
|
|
|
|
box_construct_complete_cb cb; /**< Callback to invoke on completion */
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Transient properties for construction of current node
|
|
|
|
*/
|
|
|
|
struct box_construct_props {
|
|
|
|
/** Style from which to inherit, or NULL if none */
|
|
|
|
const css_computed_style *parent_style;
|
|
|
|
/** Current link target, or NULL if none */
|
2011-10-04 00:28:29 +04:00
|
|
|
nsurl *href;
|
2011-09-29 23:15:54 +04:00
|
|
|
/** Current frame target, or NULL if none */
|
|
|
|
const char *target;
|
|
|
|
/** Current title attribute, or NULL if none */
|
|
|
|
const char *title;
|
|
|
|
/** Identity of the current block-level container */
|
|
|
|
struct box *containing_block;
|
|
|
|
/** Current container for inlines, or NULL if none
|
|
|
|
* \note If non-NULL, will be the last child of containing_block */
|
|
|
|
struct box *inline_container;
|
|
|
|
/** Whether the current node is the root of the DOM tree */
|
|
|
|
bool node_is_root;
|
|
|
|
};
|
2005-03-26 04:12:27 +03:00
|
|
|
|
2011-05-07 00:40:09 +04:00
|
|
|
static const content_type image_types = CONTENT_IMAGE;
|
2005-03-26 04:12:27 +03:00
|
|
|
|
2005-08-21 02:52:20 +04:00
|
|
|
/* the strings are not important, since we just compare the pointers */
|
|
|
|
const char *TARGET_SELF = "_self";
|
|
|
|
const char *TARGET_PARENT = "_parent";
|
|
|
|
const char *TARGET_TOP = "_top";
|
2006-09-02 19:52:41 +04:00
|
|
|
const char *TARGET_BLANK = "_blank";
|
2005-08-21 02:52:20 +04:00
|
|
|
|
2011-09-29 23:15:54 +04:00
|
|
|
static void convert_xml_to_box(struct box_construct_ctx *ctx);
|
|
|
|
static bool box_construct_element(struct box_construct_ctx *ctx,
|
|
|
|
bool *convert_children);
|
|
|
|
static void box_construct_element_after(xmlNode *n, html_content *content);
|
|
|
|
static bool box_construct_text(struct box_construct_ctx *ctx);
|
2011-05-07 00:40:09 +04:00
|
|
|
static css_select_results * box_get_style(html_content *c,
|
2011-01-05 22:36:05 +03:00
|
|
|
const css_computed_style *parent_style, xmlNode *n);
|
2005-03-26 04:12:27 +03:00
|
|
|
static void box_text_transform(char *s, unsigned int len,
|
2009-08-02 23:19:43 +04:00
|
|
|
enum css_text_transform_e tt);
|
2011-05-07 00:40:09 +04:00
|
|
|
#define BOX_SPECIAL_PARAMS xmlNode *n, html_content *content, \
|
2009-07-24 03:05:34 +04:00
|
|
|
struct box *box, bool *convert_children
|
2005-04-09 13:47:37 +04:00
|
|
|
static bool box_a(BOX_SPECIAL_PARAMS);
|
|
|
|
static bool box_body(BOX_SPECIAL_PARAMS);
|
|
|
|
static bool box_br(BOX_SPECIAL_PARAMS);
|
|
|
|
static bool box_image(BOX_SPECIAL_PARAMS);
|
|
|
|
static bool box_textarea(BOX_SPECIAL_PARAMS);
|
|
|
|
static bool box_select(BOX_SPECIAL_PARAMS);
|
|
|
|
static bool box_input(BOX_SPECIAL_PARAMS);
|
|
|
|
static bool box_input_text(BOX_SPECIAL_PARAMS, bool password);
|
|
|
|
static bool box_button(BOX_SPECIAL_PARAMS);
|
|
|
|
static bool box_frameset(BOX_SPECIAL_PARAMS);
|
2006-09-02 19:52:41 +04:00
|
|
|
static bool box_create_frameset(struct content_html_frames *f, xmlNode *n,
|
2011-05-07 00:40:09 +04:00
|
|
|
html_content *content);
|
2005-03-26 04:12:27 +03:00
|
|
|
static bool box_select_add_option(struct form_control *control, xmlNode *n);
|
2005-04-09 13:47:37 +04:00
|
|
|
static bool box_object(BOX_SPECIAL_PARAMS);
|
|
|
|
static bool box_embed(BOX_SPECIAL_PARAMS);
|
2006-04-04 14:56:21 +04:00
|
|
|
static bool box_pre(BOX_SPECIAL_PARAMS);
|
2005-04-09 13:47:37 +04:00
|
|
|
static bool box_iframe(BOX_SPECIAL_PARAMS);
|
2005-04-10 21:08:49 +04:00
|
|
|
static bool box_get_attribute(xmlNode *n, const char *attribute,
|
|
|
|
void *context, char **value);
|
2006-09-02 19:52:41 +04:00
|
|
|
static struct frame_dimension *box_parse_multi_lengths(const char *s,
|
|
|
|
unsigned int *count);
|
2005-03-26 04:12:27 +03:00
|
|
|
|
|
|
|
/* element_table must be sorted by name */
|
|
|
|
struct element_entry {
|
2006-09-02 19:52:41 +04:00
|
|
|
char name[10]; /* element type */
|
2005-04-09 13:47:37 +04:00
|
|
|
bool (*convert)(BOX_SPECIAL_PARAMS);
|
2005-03-26 04:12:27 +03:00
|
|
|
};
|
|
|
|
static const struct element_entry element_table[] = {
|
|
|
|
{"a", box_a},
|
|
|
|
{"body", box_body},
|
|
|
|
{"br", box_br},
|
|
|
|
{"button", box_button},
|
|
|
|
{"embed", box_embed},
|
|
|
|
{"frameset", box_frameset},
|
|
|
|
{"iframe", box_iframe},
|
2007-04-02 00:21:58 +04:00
|
|
|
{"image", box_image},
|
2005-03-26 04:12:27 +03:00
|
|
|
{"img", box_image},
|
|
|
|
{"input", box_input},
|
|
|
|
{"object", box_object},
|
2006-04-04 14:56:21 +04:00
|
|
|
{"pre", box_pre},
|
2005-03-26 04:12:27 +03:00
|
|
|
{"select", box_select},
|
|
|
|
{"textarea", box_textarea}
|
|
|
|
};
|
|
|
|
#define ELEMENT_TABLE_COUNT (sizeof(element_table) / sizeof(element_table[0]))
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Construct a box tree from an xml tree and stylesheets.
|
|
|
|
*
|
2011-09-29 23:15:54 +04:00
|
|
|
* \param n xml tree
|
|
|
|
* \param c content of type CONTENT_HTML to construct box tree in
|
|
|
|
* \param cb callback to report conversion completion
|
2005-03-26 04:12:27 +03:00
|
|
|
* \return true on success, false on memory exhaustion
|
|
|
|
*/
|
|
|
|
|
2011-09-29 23:15:54 +04:00
|
|
|
bool xml_to_box(xmlNode *n, html_content *c, box_construct_complete_cb cb)
|
2005-03-26 04:12:27 +03:00
|
|
|
{
|
2011-09-29 23:15:54 +04:00
|
|
|
struct box_construct_ctx *ctx;
|
2006-03-27 05:04:56 +04:00
|
|
|
|
2011-09-29 23:15:54 +04:00
|
|
|
ctx = malloc(sizeof(*ctx));
|
|
|
|
if (ctx == NULL)
|
2005-03-26 04:12:27 +03:00
|
|
|
return false;
|
|
|
|
|
2011-09-29 23:15:54 +04:00
|
|
|
ctx->content = c;
|
|
|
|
ctx->n = n;
|
|
|
|
ctx->root_box = NULL;
|
|
|
|
ctx->cb = cb;
|
|
|
|
|
|
|
|
schedule(0, (schedule_callback_fn) convert_xml_to_box, ctx);
|
2005-03-26 04:12:27 +03:00
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* mapping from CSS display to box type
|
2009-07-24 03:05:34 +04:00
|
|
|
* this table must be in sync with libcss' css_display enum */
|
2005-03-26 04:12:27 +03:00
|
|
|
static const box_type box_map[] = {
|
|
|
|
0, /*CSS_DISPLAY_INHERIT,*/
|
|
|
|
BOX_INLINE, /*CSS_DISPLAY_INLINE,*/
|
|
|
|
BOX_BLOCK, /*CSS_DISPLAY_BLOCK,*/
|
2006-11-05 15:58:24 +03:00
|
|
|
BOX_BLOCK, /*CSS_DISPLAY_LIST_ITEM,*/
|
2005-03-26 04:12:27 +03:00
|
|
|
BOX_INLINE, /*CSS_DISPLAY_RUN_IN,*/
|
|
|
|
BOX_INLINE_BLOCK, /*CSS_DISPLAY_INLINE_BLOCK,*/
|
|
|
|
BOX_TABLE, /*CSS_DISPLAY_TABLE,*/
|
|
|
|
BOX_TABLE, /*CSS_DISPLAY_INLINE_TABLE,*/
|
|
|
|
BOX_TABLE_ROW_GROUP, /*CSS_DISPLAY_TABLE_ROW_GROUP,*/
|
|
|
|
BOX_TABLE_ROW_GROUP, /*CSS_DISPLAY_TABLE_HEADER_GROUP,*/
|
|
|
|
BOX_TABLE_ROW_GROUP, /*CSS_DISPLAY_TABLE_FOOTER_GROUP,*/
|
|
|
|
BOX_TABLE_ROW, /*CSS_DISPLAY_TABLE_ROW,*/
|
2009-07-24 03:05:34 +04:00
|
|
|
BOX_NONE, /*CSS_DISPLAY_TABLE_COLUMN_GROUP,*/
|
|
|
|
BOX_NONE, /*CSS_DISPLAY_TABLE_COLUMN,*/
|
2005-03-26 04:12:27 +03:00
|
|
|
BOX_TABLE_CELL, /*CSS_DISPLAY_TABLE_CELL,*/
|
2009-07-24 03:05:34 +04:00
|
|
|
BOX_INLINE, /*CSS_DISPLAY_TABLE_CAPTION,*/
|
|
|
|
BOX_NONE /*CSS_DISPLAY_NONE*/
|
2005-03-26 04:12:27 +03:00
|
|
|
};
|
|
|
|
|
2011-09-29 23:15:54 +04:00
|
|
|
static inline struct box *box_for_node(const xmlNode *n)
|
|
|
|
{
|
|
|
|
return ((binding_private *) n->_private)->box;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline bool box_is_root(const xmlNode *n)
|
|
|
|
{
|
|
|
|
return n->parent == NULL || n->parent->type == XML_HTML_DOCUMENT_NODE;
|
|
|
|
}
|
2005-03-26 04:12:27 +03:00
|
|
|
|
|
|
|
/**
|
2011-09-29 23:15:54 +04:00
|
|
|
* Find the next node in the DOM tree, completing
|
|
|
|
* element construction where appropriate.
|
2005-03-26 04:12:27 +03:00
|
|
|
*
|
2011-09-29 23:15:54 +04:00
|
|
|
* \param n Current node
|
|
|
|
* \param content Containing content
|
|
|
|
* \param convert_children Whether to consider children of \a n
|
|
|
|
* \return Next node to process, or NULL if complete
|
2005-03-26 04:12:27 +03:00
|
|
|
*/
|
2011-09-29 23:15:54 +04:00
|
|
|
static xmlNode *next_node(xmlNode *n, html_content *content,
|
|
|
|
bool convert_children)
|
|
|
|
{
|
|
|
|
xmlNode *next = NULL;
|
|
|
|
|
|
|
|
if (convert_children && n->children != NULL) {
|
|
|
|
next = n->children;
|
|
|
|
} else if (n->next != NULL) {
|
|
|
|
if (box_for_node(n) != NULL)
|
|
|
|
box_construct_element_after(n, content);
|
|
|
|
|
|
|
|
next = n->next;
|
|
|
|
} else {
|
|
|
|
if (box_for_node(n) != NULL)
|
|
|
|
box_construct_element_after(n, content);
|
|
|
|
|
|
|
|
while (box_is_root(n) == false && n->parent->next == NULL) {
|
|
|
|
n = n->parent;
|
2005-03-26 04:12:27 +03:00
|
|
|
|
2011-09-29 23:15:54 +04:00
|
|
|
if (box_for_node(n) != NULL)
|
|
|
|
box_construct_element_after(n, content);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (box_is_root(n) == false) {
|
|
|
|
if (box_for_node(n->parent) != NULL) {
|
|
|
|
box_construct_element_after(n->parent, content);
|
|
|
|
}
|
|
|
|
|
|
|
|
next = n->parent->next;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return next;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Convert an ELEMENT node to a box tree fragment,
|
|
|
|
* then schedule conversion of the next ELEMENT node
|
|
|
|
*/
|
|
|
|
void convert_xml_to_box(struct box_construct_ctx *ctx)
|
2005-03-28 23:17:06 +04:00
|
|
|
{
|
2011-09-29 23:15:54 +04:00
|
|
|
xmlNode *next;
|
|
|
|
bool convert_children = true;
|
|
|
|
|
|
|
|
assert(ctx->n != NULL);
|
|
|
|
assert(ctx->n->type == XML_ELEMENT_NODE);
|
|
|
|
|
|
|
|
if (box_construct_element(ctx, &convert_children) == false) {
|
|
|
|
ctx->cb(ctx->content, false);
|
|
|
|
free(ctx);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Find next element to process, converting text nodes as we go */
|
|
|
|
next = next_node(ctx->n, ctx->content, convert_children);
|
|
|
|
while (next != NULL && next->type != XML_ELEMENT_NODE) {
|
|
|
|
if (next->type == XML_TEXT_NODE) {
|
|
|
|
ctx->n = next;
|
|
|
|
if (box_construct_text(ctx) == false) {
|
|
|
|
ctx->cb(ctx->content, false);
|
|
|
|
free(ctx);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
next = next_node(next, ctx->content, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
ctx->n = next;
|
|
|
|
|
|
|
|
if (next != NULL) {
|
|
|
|
/* More work to do: schedule a continuation */
|
|
|
|
schedule(0, (schedule_callback_fn) convert_xml_to_box, ctx);
|
|
|
|
} else {
|
|
|
|
/* Conversion complete */
|
|
|
|
struct box root;
|
|
|
|
|
|
|
|
memset(&root, 0, sizeof(root));
|
|
|
|
|
|
|
|
root.type = BOX_BLOCK;
|
|
|
|
root.children = root.last = ctx->root_box;
|
|
|
|
root.children->parent = &root;
|
|
|
|
|
|
|
|
/** \todo Remove box_normalise_block */
|
|
|
|
if (box_normalise_block(&root, ctx->content) == false) {
|
|
|
|
ctx->cb(ctx->content, false);
|
|
|
|
} else {
|
|
|
|
ctx->content->layout = root.children;
|
|
|
|
ctx->content->layout->parent = NULL;
|
|
|
|
|
|
|
|
ctx->cb(ctx->content, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
free(ctx);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Construct a list marker box
|
|
|
|
*
|
|
|
|
* \param box Box to attach marker to
|
|
|
|
* \param title Current title attribute
|
|
|
|
* \param content Containing content
|
|
|
|
* \param parent Current block-level container
|
|
|
|
* \return True on success, false on memory exhaustion
|
|
|
|
*/
|
|
|
|
static bool box_construct_marker(struct box *box, const char *title,
|
|
|
|
html_content *content, struct box *parent)
|
|
|
|
{
|
|
|
|
lwc_string *image_uri;
|
|
|
|
struct box *marker;
|
|
|
|
|
|
|
|
marker = box_create(NULL, box->style, false, NULL, NULL, title,
|
|
|
|
NULL, content);
|
|
|
|
if (marker == false)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
marker->type = BOX_BLOCK;
|
|
|
|
|
|
|
|
/** \todo marker content (list-style-type) */
|
|
|
|
switch (css_computed_list_style_type(box->style)) {
|
|
|
|
case CSS_LIST_STYLE_TYPE_DISC:
|
|
|
|
/* 2022 BULLET */
|
|
|
|
marker->text = (char *) "\342\200\242";
|
|
|
|
marker->length = 3;
|
|
|
|
break;
|
|
|
|
case CSS_LIST_STYLE_TYPE_CIRCLE:
|
|
|
|
/* 25CB WHITE CIRCLE */
|
|
|
|
marker->text = (char *) "\342\227\213";
|
|
|
|
marker->length = 3;
|
|
|
|
break;
|
|
|
|
case CSS_LIST_STYLE_TYPE_SQUARE:
|
|
|
|
/* 25AA BLACK SMALL SQUARE */
|
|
|
|
marker->text = (char *) "\342\226\252";
|
|
|
|
marker->length = 3;
|
|
|
|
break;
|
|
|
|
case CSS_LIST_STYLE_TYPE_DECIMAL:
|
|
|
|
case CSS_LIST_STYLE_TYPE_LOWER_ALPHA:
|
|
|
|
case CSS_LIST_STYLE_TYPE_LOWER_ROMAN:
|
|
|
|
case CSS_LIST_STYLE_TYPE_UPPER_ALPHA:
|
|
|
|
case CSS_LIST_STYLE_TYPE_UPPER_ROMAN:
|
2005-03-28 23:17:06 +04:00
|
|
|
default:
|
2011-09-29 23:15:54 +04:00
|
|
|
if (parent->last) {
|
|
|
|
struct box *last = parent->last;
|
|
|
|
|
|
|
|
/* Drill down into last child of parent
|
|
|
|
* to find the list marker (if any)
|
|
|
|
*
|
|
|
|
* Floated list boxes end up as:
|
|
|
|
*
|
|
|
|
* parent
|
|
|
|
* BOX_INLINE_CONTAINER
|
|
|
|
* BOX_FLOAT_{LEFT,RIGHT}
|
|
|
|
* BOX_BLOCK <-- list box
|
|
|
|
* ...
|
|
|
|
*/
|
|
|
|
while (last != NULL) {
|
|
|
|
if (last->list_marker != NULL)
|
|
|
|
break;
|
|
|
|
|
|
|
|
last = last->last;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (last && last->list_marker) {
|
|
|
|
marker->rows = last->list_marker->rows + 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
marker->text = talloc_array(content, char, 20);
|
|
|
|
if (marker->text == NULL)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
snprintf(marker->text, 20, "%u.", marker->rows);
|
|
|
|
marker->length = strlen(marker->text);
|
|
|
|
break;
|
|
|
|
case CSS_LIST_STYLE_TYPE_NONE:
|
|
|
|
marker->text = 0;
|
|
|
|
marker->length = 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (css_computed_list_style_image(box->style, &image_uri) ==
|
|
|
|
CSS_LIST_STYLE_IMAGE_URI && image_uri != NULL) {
|
2011-10-04 14:23:47 +04:00
|
|
|
nsurl *url;
|
|
|
|
nserror error;
|
|
|
|
|
|
|
|
/* TODO: we get a url out of libcss as a lwc string, but
|
|
|
|
* earlier we already had it as a nsurl after we
|
|
|
|
* nsurl_joined it. Can this be improved?
|
|
|
|
* For now, just making another nsurl. */
|
|
|
|
error = nsurl_create(lwc_string_data(image_uri), &url);
|
|
|
|
if (error != NSERROR_OK)
|
2011-09-29 23:15:54 +04:00
|
|
|
return false;
|
2011-10-04 14:23:47 +04:00
|
|
|
|
|
|
|
if (html_fetch_object(content, url, marker, image_types,
|
|
|
|
content->base.available_width, 1000, false) ==
|
|
|
|
false) {
|
|
|
|
nsurl_unref(url);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
nsurl_unref(url);
|
2011-09-29 23:15:54 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
box->list_marker = marker;
|
|
|
|
marker->parent = box;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Construct the box required for a generated element.
|
|
|
|
*
|
|
|
|
* \param n XML node of type XML_ELEMENT_NODE
|
|
|
|
* \param content Content of type CONTENT_HTML that is being processed
|
|
|
|
* \param box Box which may have generated content
|
|
|
|
* \param style Complete computed style for pseudo element, or NULL
|
|
|
|
*
|
|
|
|
* TODO:
|
|
|
|
* This is currently incomplete. It just does enough to support the clearfix
|
|
|
|
* hack. ( http://www.positioniseverything.net/easyclearing.html )
|
|
|
|
*/
|
|
|
|
static void box_construct_generate(xmlNode *n, html_content *content,
|
|
|
|
struct box *box, const css_computed_style *style)
|
|
|
|
{
|
|
|
|
struct box *gen = NULL;
|
|
|
|
const css_computed_content_item *c_item;
|
|
|
|
|
|
|
|
/* Nothing to generate if the parent box is not a block */
|
|
|
|
if (box->type != BOX_BLOCK)
|
|
|
|
return;
|
|
|
|
|
|
|
|
/* To determine if an element has a pseudo element, we select
|
|
|
|
* for it and test to see if the returned style's content
|
|
|
|
* property is set to normal. */
|
|
|
|
if (style == NULL ||
|
|
|
|
css_computed_content(style, &c_item) ==
|
|
|
|
CSS_CONTENT_NORMAL) {
|
|
|
|
/* No pseudo element */
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* create box for this element */
|
|
|
|
if (css_computed_display(style, box_is_root(n)) == CSS_DISPLAY_BLOCK) {
|
|
|
|
/* currently only support block level elements */
|
|
|
|
|
|
|
|
/** \todo Not wise to drop const from the computed style */
|
|
|
|
gen = box_create(NULL, (css_computed_style *) style,
|
|
|
|
false, NULL, NULL, NULL, NULL, content);
|
|
|
|
if (gen == NULL) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* set box type from computed display */
|
|
|
|
gen->type = box_map[css_computed_display(
|
|
|
|
style, box_is_root(n))];
|
|
|
|
|
|
|
|
box_add_child(box, gen);
|
2005-03-28 23:17:06 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-09-29 23:15:54 +04:00
|
|
|
/**
|
|
|
|
* Extract transient construction properties
|
|
|
|
*
|
|
|
|
* \param n Current DOM node to convert
|
|
|
|
* \param props Property object to populate
|
|
|
|
*/
|
|
|
|
static void box_extract_properties(const xmlNode *n,
|
|
|
|
struct box_construct_props *props)
|
|
|
|
{
|
|
|
|
memset(props, 0, sizeof(*props));
|
|
|
|
|
|
|
|
props->node_is_root = box_is_root(n);
|
|
|
|
|
|
|
|
/* Extract properties from containing DOM node */
|
|
|
|
if (props->node_is_root == false) {
|
|
|
|
struct box *parent_box;
|
|
|
|
|
|
|
|
/* Find ancestor node containing parent box */
|
|
|
|
while (n->parent != NULL && box_for_node(n->parent) == NULL)
|
|
|
|
n = n->parent;
|
|
|
|
|
|
|
|
parent_box = box_for_node(n->parent);
|
|
|
|
|
|
|
|
props->parent_style = parent_box->style;
|
|
|
|
props->href = parent_box->href;
|
|
|
|
props->target = parent_box->target;
|
|
|
|
props->title = parent_box->title;
|
|
|
|
|
|
|
|
/* Find containing block (may be parent) */
|
|
|
|
for (n = n->parent; n != NULL; n = n->parent) {
|
|
|
|
struct box *b = box_for_node(n);
|
|
|
|
|
|
|
|
/* Children of nodes that created an inline box
|
|
|
|
* will generate boxes which are attached as
|
|
|
|
* _siblings_ of the box generated for their
|
|
|
|
* parent node. Note, however, that we'll still
|
|
|
|
* use the parent node's styling as the parent
|
|
|
|
* style, above. */
|
|
|
|
if (b != NULL && b->type != BOX_INLINE &&
|
|
|
|
b->type != BOX_BR) {
|
|
|
|
props->containing_block = b;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Compute current inline container, if any */
|
|
|
|
if (props->containing_block != NULL &&
|
|
|
|
props->containing_block->last != NULL &&
|
|
|
|
props->containing_block->last->type ==
|
|
|
|
BOX_INLINE_CONTAINER)
|
|
|
|
props->inline_container = props->containing_block->last;
|
|
|
|
}
|
2005-03-28 23:17:06 +04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Construct the box tree for an XML element.
|
|
|
|
*
|
2011-09-29 23:15:54 +04:00
|
|
|
* \param ctx Tree construction context
|
|
|
|
* \param convert_children Whether to convert children
|
2005-03-28 23:17:06 +04:00
|
|
|
* \return true on success, false on memory exhaustion
|
|
|
|
*/
|
|
|
|
|
2011-09-29 23:15:54 +04:00
|
|
|
bool box_construct_element(struct box_construct_ctx *ctx,
|
|
|
|
bool *convert_children)
|
2005-03-26 04:12:27 +03:00
|
|
|
{
|
2011-09-29 23:15:54 +04:00
|
|
|
xmlChar *title0, *s;
|
|
|
|
char *id = NULL;
|
|
|
|
struct box *box = NULL;
|
2011-01-05 22:36:05 +03:00
|
|
|
css_select_results *styles = NULL;
|
2005-03-28 23:17:06 +04:00
|
|
|
struct element_entry *element;
|
2009-07-24 03:05:34 +04:00
|
|
|
lwc_string *bgimage_uri;
|
2011-09-29 23:15:54 +04:00
|
|
|
struct box_construct_props props;
|
|
|
|
|
|
|
|
assert(ctx->n != NULL);
|
|
|
|
assert(ctx->n->type == XML_ELEMENT_NODE);
|
2005-03-26 04:12:27 +03:00
|
|
|
|
2011-09-29 23:15:54 +04:00
|
|
|
box_extract_properties(ctx->n, &props);
|
2005-03-26 04:12:27 +03:00
|
|
|
|
2011-09-29 23:15:54 +04:00
|
|
|
if (props.containing_block != NULL) {
|
|
|
|
/* In case the containing block is a pre block, we clear
|
|
|
|
* the PRE_STRIP flag since it is not used if we follow
|
|
|
|
* the pre with a tag */
|
|
|
|
props.containing_block->flags &= ~PRE_STRIP;
|
|
|
|
}
|
2006-07-04 01:41:25 +04:00
|
|
|
|
2011-09-29 23:15:54 +04:00
|
|
|
styles = box_get_style(ctx->content, props.parent_style, ctx->n);
|
|
|
|
if (styles == NULL)
|
2005-04-09 13:47:37 +04:00
|
|
|
return false;
|
2005-03-28 23:17:06 +04:00
|
|
|
|
2011-09-29 23:15:54 +04:00
|
|
|
/* Extract title attribute, if present */
|
|
|
|
if ((title0 = xmlGetProp(ctx->n, (const xmlChar *) "title")) != NULL) {
|
|
|
|
char *t = squash_whitespace((char *) title0);
|
2011-01-05 00:52:43 +03:00
|
|
|
|
2005-03-28 23:17:06 +04:00
|
|
|
xmlFree(title0);
|
2011-01-05 00:52:43 +03:00
|
|
|
|
2011-09-29 23:15:54 +04:00
|
|
|
if (t == NULL)
|
2005-04-09 13:47:37 +04:00
|
|
|
return false;
|
2009-07-24 03:05:34 +04:00
|
|
|
|
2011-09-29 23:15:54 +04:00
|
|
|
props.title = talloc_strdup(ctx->content, t);
|
2009-07-24 03:05:34 +04:00
|
|
|
|
2011-09-29 23:15:54 +04:00
|
|
|
free(t);
|
2009-07-24 03:05:34 +04:00
|
|
|
|
2011-09-29 23:15:54 +04:00
|
|
|
if (props.title == NULL)
|
2005-04-09 13:47:37 +04:00
|
|
|
return false;
|
2005-03-28 23:17:06 +04:00
|
|
|
}
|
|
|
|
|
2011-09-29 23:15:54 +04:00
|
|
|
/* Extract id attribute, if present */
|
|
|
|
if (box_get_attribute(ctx->n, "id", ctx->content, &id) == false)
|
2005-04-10 21:08:49 +04:00
|
|
|
return false;
|
2005-03-26 04:12:27 +03:00
|
|
|
|
2011-01-05 22:36:05 +03:00
|
|
|
box = box_create(styles, styles->styles[CSS_PSEUDO_ELEMENT_NONE], false,
|
2011-10-04 00:28:29 +04:00
|
|
|
props.href, props.target, props.title, id,
|
2011-09-29 23:15:54 +04:00
|
|
|
ctx->content);
|
|
|
|
if (box == NULL)
|
2005-04-09 13:47:37 +04:00
|
|
|
return false;
|
2011-09-29 23:15:54 +04:00
|
|
|
|
|
|
|
/* If this is the root box, add it to the context */
|
|
|
|
if (props.node_is_root)
|
|
|
|
ctx->root_box = box;
|
|
|
|
|
|
|
|
/* Deal with colspan/rowspan */
|
|
|
|
if ((s = xmlGetProp(ctx->n, (const xmlChar *) "colspan")) != NULL) {
|
|
|
|
if ('0' <= s[0] && s[0] <= '9')
|
|
|
|
box->columns = strtol((char *) s, NULL, 10);
|
|
|
|
|
|
|
|
xmlFree(s);
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((s = xmlGetProp(ctx->n, (const xmlChar *) "rowspan")) != NULL) {
|
|
|
|
if ('0' <= s[0] && s[0] <= '9')
|
|
|
|
box->rows = strtol((char *) s, NULL, 10);
|
|
|
|
|
|
|
|
xmlFree(s);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Set box type from computed display */
|
2011-01-05 22:36:05 +03:00
|
|
|
if ((css_computed_position(box->style) == CSS_POSITION_ABSOLUTE ||
|
|
|
|
css_computed_position(box->style) ==
|
|
|
|
CSS_POSITION_FIXED) &&
|
2011-09-29 23:15:54 +04:00
|
|
|
(css_computed_display_static(box->style) ==
|
2009-07-24 03:05:34 +04:00
|
|
|
CSS_DISPLAY_INLINE ||
|
2011-09-29 23:15:54 +04:00
|
|
|
css_computed_display_static(box->style) ==
|
2009-07-24 03:05:34 +04:00
|
|
|
CSS_DISPLAY_INLINE_BLOCK ||
|
2011-09-29 23:15:54 +04:00
|
|
|
css_computed_display_static(box->style) ==
|
2009-07-24 03:05:34 +04:00
|
|
|
CSS_DISPLAY_INLINE_TABLE)) {
|
|
|
|
/* Special case for absolute positioning: make absolute inlines
|
2011-09-29 23:15:54 +04:00
|
|
|
* into inline block so that the boxes are constructed in an
|
|
|
|
* inline container as if they were not absolutely positioned.
|
2009-07-24 03:05:34 +04:00
|
|
|
* Layout expects and handles this. */
|
|
|
|
box->type = box_map[CSS_DISPLAY_INLINE_BLOCK];
|
|
|
|
} else {
|
|
|
|
/* Normal mapping */
|
2011-01-05 22:36:05 +03:00
|
|
|
box->type = box_map[css_computed_display(box->style,
|
2011-09-29 23:15:54 +04:00
|
|
|
props.node_is_root)];
|
2009-07-24 03:05:34 +04:00
|
|
|
}
|
2005-04-09 13:47:37 +04:00
|
|
|
|
2011-09-29 23:15:54 +04:00
|
|
|
/* Handle the :before pseudo element */
|
|
|
|
box_construct_generate(ctx->n, ctx->content, box,
|
|
|
|
box->styles->styles[CSS_PSEUDO_ELEMENT_BEFORE]);
|
2011-04-22 18:48:54 +04:00
|
|
|
|
2011-09-29 23:15:54 +04:00
|
|
|
/* Special elements */
|
|
|
|
element = bsearch((const char *) ctx->n->name, element_table,
|
2005-03-28 23:17:06 +04:00
|
|
|
ELEMENT_TABLE_COUNT, sizeof(element_table[0]),
|
|
|
|
(int (*)(const void *, const void *)) strcmp);
|
2011-09-29 23:15:54 +04:00
|
|
|
if (element != NULL) {
|
|
|
|
/* A special convert function exists for this element */
|
|
|
|
if (element->convert(ctx->n, ctx->content, box,
|
|
|
|
convert_children) == false)
|
2005-04-09 13:47:37 +04:00
|
|
|
return false;
|
2005-03-28 23:17:06 +04:00
|
|
|
}
|
2008-02-25 19:37:48 +03:00
|
|
|
|
2009-07-24 03:05:34 +04:00
|
|
|
if (box->type == BOX_NONE || css_computed_display(box->style,
|
2011-09-29 23:15:54 +04:00
|
|
|
props.node_is_root) == CSS_DISPLAY_NONE) {
|
2011-01-05 22:36:05 +03:00
|
|
|
css_select_results_destroy(styles);
|
|
|
|
box->styles = NULL;
|
2008-02-07 03:50:37 +03:00
|
|
|
box->style = NULL;
|
|
|
|
|
2011-09-29 23:15:54 +04:00
|
|
|
/* Invalidate associated gadget, if any */
|
|
|
|
if (box->gadget != NULL) {
|
2008-02-07 03:50:37 +03:00
|
|
|
box->gadget->box = NULL;
|
|
|
|
box->gadget = NULL;
|
|
|
|
}
|
|
|
|
|
2011-09-29 23:15:54 +04:00
|
|
|
/* Can't do this, because the lifetimes of boxes and gadgets
|
|
|
|
* are inextricably linked. Fortunately, talloc will save us
|
|
|
|
* (for now) */
|
2007-04-06 16:05:25 +04:00
|
|
|
/* box_free_box(box); */
|
2011-09-29 23:15:54 +04:00
|
|
|
|
|
|
|
*convert_children = false;
|
|
|
|
|
2005-07-03 00:08:24 +04:00
|
|
|
return true;
|
|
|
|
}
|
2005-03-28 23:17:06 +04:00
|
|
|
|
2011-09-29 23:15:54 +04:00
|
|
|
/* Attach box to DOM node */
|
|
|
|
((binding_private *) ctx->n->_private)->box = box;
|
|
|
|
|
|
|
|
if (props.inline_container == NULL &&
|
2005-04-09 13:47:37 +04:00
|
|
|
(box->type == BOX_INLINE ||
|
2011-09-29 23:15:54 +04:00
|
|
|
box->type == BOX_BR ||
|
|
|
|
box->type == BOX_INLINE_BLOCK ||
|
|
|
|
css_computed_float(box->style) == CSS_FLOAT_LEFT ||
|
|
|
|
css_computed_float(box->style) == CSS_FLOAT_RIGHT)) {
|
|
|
|
/* Found an inline child of a block without a current container
|
|
|
|
* (i.e. this box is the first child of its parent, or was
|
|
|
|
* preceded by block-level siblings) */
|
|
|
|
assert(props.containing_block != NULL &&
|
|
|
|
"Root box must not be inline or floated");
|
|
|
|
|
|
|
|
props.inline_container = box_create(NULL, NULL, false, NULL,
|
|
|
|
NULL, NULL, NULL, ctx->content);
|
|
|
|
if (props.inline_container == NULL)
|
2005-04-09 13:47:37 +04:00
|
|
|
return false;
|
2009-07-24 03:05:34 +04:00
|
|
|
|
2011-09-29 23:15:54 +04:00
|
|
|
props.inline_container->type = BOX_INLINE_CONTAINER;
|
2009-07-24 03:05:34 +04:00
|
|
|
|
2011-09-29 23:15:54 +04:00
|
|
|
box_add_child(props.containing_block, props.inline_container);
|
2005-04-09 13:47:37 +04:00
|
|
|
}
|
2005-03-26 04:12:27 +03:00
|
|
|
|
2011-09-29 23:15:54 +04:00
|
|
|
/* Kick off fetch for any background image */
|
|
|
|
if (css_computed_background_image(box->style, &bgimage_uri) ==
|
|
|
|
CSS_BACKGROUND_IMAGE_IMAGE && bgimage_uri != NULL) {
|
2011-10-04 14:23:47 +04:00
|
|
|
nsurl *url;
|
|
|
|
nserror error;
|
|
|
|
|
|
|
|
/* TODO: we get a url out of libcss as a lwc string, but
|
|
|
|
* earlier we already had it as a nsurl after we
|
|
|
|
* nsurl_joined it. Can this be improved?
|
|
|
|
* For now, just making another nsurl. */
|
|
|
|
error = nsurl_create(lwc_string_data(bgimage_uri), &url);
|
|
|
|
if (error != NSERROR_OK)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (html_fetch_object(ctx->content, url, box, image_types,
|
2011-09-29 23:15:54 +04:00
|
|
|
ctx->content->base.available_width, 1000,
|
2011-10-04 14:23:47 +04:00
|
|
|
true) == false) {
|
|
|
|
nsurl_unref(url);
|
2011-09-29 23:15:54 +04:00
|
|
|
return false;
|
2011-10-04 14:23:47 +04:00
|
|
|
}
|
|
|
|
nsurl_unref(url);
|
2011-09-29 23:15:54 +04:00
|
|
|
}
|
2009-07-24 03:05:34 +04:00
|
|
|
|
2011-09-29 23:15:54 +04:00
|
|
|
if (*convert_children)
|
|
|
|
box->flags |= CONVERT_CHILDREN;
|
2009-07-24 03:05:34 +04:00
|
|
|
|
2011-09-29 23:15:54 +04:00
|
|
|
if (box->type == BOX_INLINE || box->type == BOX_BR ||
|
|
|
|
box->type == BOX_INLINE_BLOCK) {
|
|
|
|
/* Inline container must exist, as we'll have
|
|
|
|
* created it above if it didn't */
|
|
|
|
assert(props.inline_container != NULL);
|
2009-07-24 03:05:34 +04:00
|
|
|
|
2011-09-29 23:15:54 +04:00
|
|
|
box_add_child(props.inline_container, box);
|
2005-04-09 13:47:37 +04:00
|
|
|
} else {
|
2011-09-29 23:15:54 +04:00
|
|
|
if (css_computed_display(box->style, props.node_is_root) ==
|
2009-07-24 03:05:34 +04:00
|
|
|
CSS_DISPLAY_LIST_ITEM) {
|
2011-09-29 23:15:54 +04:00
|
|
|
/* List item: compute marker */
|
|
|
|
if (box_construct_marker(box, props.title, ctx->content,
|
|
|
|
props.containing_block) == false)
|
2006-11-05 15:58:24 +03:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2011-01-05 22:36:05 +03:00
|
|
|
if (css_computed_float(box->style) == CSS_FLOAT_LEFT ||
|
2011-09-29 23:15:54 +04:00
|
|
|
css_computed_float(box->style) ==
|
2011-01-05 22:36:05 +03:00
|
|
|
CSS_FLOAT_RIGHT) {
|
2011-09-29 23:15:54 +04:00
|
|
|
/* Float: insert a float between the parent and box. */
|
|
|
|
struct box *flt = box_create(NULL, NULL, false,
|
|
|
|
props.href, props.target, props.title,
|
|
|
|
NULL, ctx->content);
|
|
|
|
if (flt == NULL)
|
2009-04-29 14:44:20 +04:00
|
|
|
return false;
|
2009-07-24 03:05:34 +04:00
|
|
|
|
2011-01-05 22:36:05 +03:00
|
|
|
if (css_computed_float(box->style) == CSS_FLOAT_LEFT)
|
2011-09-29 23:15:54 +04:00
|
|
|
flt->type = BOX_FLOAT_LEFT;
|
2009-04-29 14:44:20 +04:00
|
|
|
else
|
2011-09-29 23:15:54 +04:00
|
|
|
flt->type = BOX_FLOAT_RIGHT;
|
|
|
|
|
|
|
|
box_add_child(props.inline_container, flt);
|
|
|
|
box_add_child(flt, box);
|
|
|
|
} else {
|
|
|
|
/* Non-floated block-level box: add to containing block
|
|
|
|
* if there is one. If we're the root box, then there
|
|
|
|
* won't be. */
|
|
|
|
if (props.containing_block != NULL)
|
|
|
|
box_add_child(props.containing_block, box);
|
2009-04-29 14:44:20 +04:00
|
|
|
}
|
2011-01-05 00:52:43 +03:00
|
|
|
}
|
|
|
|
|
2005-03-28 23:17:06 +04:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2011-01-05 00:52:43 +03:00
|
|
|
/**
|
2011-09-29 23:15:54 +04:00
|
|
|
* Complete construction of the box tree for an element.
|
2011-01-05 00:52:43 +03:00
|
|
|
*
|
2011-09-29 23:15:54 +04:00
|
|
|
* \param n DOM node to construct for
|
|
|
|
* \param content Containing document
|
2011-01-05 00:52:43 +03:00
|
|
|
*
|
2011-09-29 23:15:54 +04:00
|
|
|
* This will be called after all children of an element have been processed
|
2011-01-05 00:52:43 +03:00
|
|
|
*/
|
2011-09-29 23:15:54 +04:00
|
|
|
void box_construct_element_after(xmlNode *n, html_content *content)
|
2011-01-05 00:52:43 +03:00
|
|
|
{
|
2011-09-29 23:15:54 +04:00
|
|
|
struct box_construct_props props;
|
|
|
|
struct box *box = box_for_node(n);
|
2011-01-05 00:52:43 +03:00
|
|
|
|
2011-09-29 23:15:54 +04:00
|
|
|
assert(box != NULL);
|
2011-01-05 00:52:43 +03:00
|
|
|
|
2011-09-29 23:15:54 +04:00
|
|
|
box_extract_properties(n, &props);
|
2011-01-05 22:36:05 +03:00
|
|
|
|
2011-09-29 23:15:54 +04:00
|
|
|
if (box->type == BOX_INLINE || box->type == BOX_BR) {
|
|
|
|
/* Insert INLINE_END into containing block */
|
|
|
|
struct box *inline_end;
|
|
|
|
|
|
|
|
if (n->children == NULL ||
|
|
|
|
(box->flags & CONVERT_CHILDREN) == 0) {
|
|
|
|
/* No children, or didn't want children converted */
|
2011-01-05 00:52:43 +03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2011-09-29 23:15:54 +04:00
|
|
|
if (props.inline_container == NULL) {
|
|
|
|
/* Create inline container if we don't have one */
|
|
|
|
props.inline_container = box_create(NULL, NULL, false,
|
|
|
|
NULL, NULL, NULL, NULL, content);
|
|
|
|
if (props.inline_container == NULL)
|
|
|
|
return;
|
2011-01-05 00:52:43 +03:00
|
|
|
|
2011-09-29 23:15:54 +04:00
|
|
|
props.inline_container->type = BOX_INLINE_CONTAINER;
|
|
|
|
|
|
|
|
box_add_child(props.containing_block,
|
|
|
|
props.inline_container);
|
|
|
|
}
|
|
|
|
|
|
|
|
inline_end = box_create(NULL, box->style, false,
|
|
|
|
box->href, box->target, box->title,
|
|
|
|
box->id, content);
|
|
|
|
if (inline_end != NULL) {
|
|
|
|
inline_end->type = BOX_INLINE_END;
|
|
|
|
|
|
|
|
assert(props.inline_container != NULL);
|
|
|
|
|
|
|
|
box_add_child(props.inline_container, inline_end);
|
|
|
|
|
|
|
|
box->inline_end = inline_end;
|
|
|
|
inline_end->inline_end = box;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
/* Handle the :after pseudo element */
|
|
|
|
box_construct_generate(n, content, box,
|
|
|
|
box->styles->styles[CSS_PSEUDO_ELEMENT_AFTER]);
|
2011-01-05 00:52:43 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2005-03-28 23:17:06 +04:00
|
|
|
/**
|
|
|
|
* Construct the box tree for an XML text node.
|
|
|
|
*
|
2011-09-29 23:15:54 +04:00
|
|
|
* \param ctx Tree construction context
|
2005-03-28 23:17:06 +04:00
|
|
|
* \return true on success, false on memory exhaustion
|
|
|
|
*/
|
|
|
|
|
2011-09-29 23:15:54 +04:00
|
|
|
bool box_construct_text(struct box_construct_ctx *ctx)
|
2005-03-28 23:17:06 +04:00
|
|
|
{
|
2011-09-29 23:15:54 +04:00
|
|
|
struct box_construct_props props;
|
|
|
|
struct box *box = NULL;
|
2005-03-28 23:17:06 +04:00
|
|
|
|
2011-09-29 23:15:54 +04:00
|
|
|
assert(ctx->n != NULL);
|
|
|
|
assert(ctx->n->type == XML_TEXT_NODE);
|
2005-03-28 23:17:06 +04:00
|
|
|
|
2011-09-29 23:15:54 +04:00
|
|
|
box_extract_properties(ctx->n, &props);
|
|
|
|
|
|
|
|
assert(props.containing_block != NULL);
|
|
|
|
|
|
|
|
if (css_computed_white_space(props.parent_style) ==
|
|
|
|
CSS_WHITE_SPACE_NORMAL ||
|
|
|
|
css_computed_white_space(props.parent_style) ==
|
2009-07-24 03:05:34 +04:00
|
|
|
CSS_WHITE_SPACE_NOWRAP) {
|
2011-09-29 23:15:54 +04:00
|
|
|
char *text = squash_whitespace((char *) ctx->n->content);
|
|
|
|
if (text == NULL)
|
2005-04-09 13:47:37 +04:00
|
|
|
return false;
|
2005-03-26 04:12:27 +03:00
|
|
|
|
|
|
|
/* if the text is just a space, combine it with the preceding
|
|
|
|
* text node, if any */
|
|
|
|
if (text[0] == ' ' && text[1] == 0) {
|
2011-09-29 23:15:54 +04:00
|
|
|
if (props.inline_container != NULL) {
|
|
|
|
assert(props.inline_container->last != NULL);
|
2009-07-24 03:05:34 +04:00
|
|
|
|
2011-09-29 23:15:54 +04:00
|
|
|
props.inline_container->last->space =
|
2011-03-01 23:00:41 +03:00
|
|
|
UNKNOWN_WIDTH;
|
2005-03-26 04:12:27 +03:00
|
|
|
}
|
2009-07-24 03:05:34 +04:00
|
|
|
|
2005-03-26 04:12:27 +03:00
|
|
|
free(text);
|
2009-07-24 03:05:34 +04:00
|
|
|
|
2005-04-09 13:47:37 +04:00
|
|
|
return true;
|
2005-03-26 04:12:27 +03:00
|
|
|
}
|
|
|
|
|
2011-09-29 23:15:54 +04:00
|
|
|
if (props.inline_container == NULL) {
|
|
|
|
/* Child of a block without a current container
|
|
|
|
* (i.e. this box is the first child of its parent, or
|
|
|
|
* was preceded by block-level siblings) */
|
|
|
|
props.inline_container = box_create(NULL, NULL, false,
|
|
|
|
NULL, NULL, NULL, NULL, ctx->content);
|
|
|
|
if (props.inline_container == NULL) {
|
2005-03-26 04:12:27 +03:00
|
|
|
free(text);
|
2005-04-09 13:47:37 +04:00
|
|
|
return false;
|
2005-03-26 04:12:27 +03:00
|
|
|
}
|
2009-07-24 03:05:34 +04:00
|
|
|
|
2011-09-29 23:15:54 +04:00
|
|
|
props.inline_container->type = BOX_INLINE_CONTAINER;
|
2009-07-24 03:05:34 +04:00
|
|
|
|
2011-09-29 23:15:54 +04:00
|
|
|
box_add_child(props.containing_block,
|
|
|
|
props.inline_container);
|
2005-03-26 04:12:27 +03:00
|
|
|
}
|
|
|
|
|
2009-07-24 03:05:34 +04:00
|
|
|
/** \todo Dropping const here is not clever */
|
2011-09-29 23:15:54 +04:00
|
|
|
box = box_create(NULL,
|
|
|
|
(css_computed_style *) props.parent_style,
|
|
|
|
false, props.href, props.target, props.title,
|
|
|
|
NULL, ctx->content);
|
|
|
|
if (box == NULL) {
|
2005-03-26 04:12:27 +03:00
|
|
|
free(text);
|
2005-04-09 13:47:37 +04:00
|
|
|
return false;
|
2005-03-26 04:12:27 +03:00
|
|
|
}
|
2009-07-24 03:05:34 +04:00
|
|
|
|
2005-05-23 01:50:14 +04:00
|
|
|
box->type = BOX_TEXT;
|
2009-07-24 03:05:34 +04:00
|
|
|
|
2011-09-29 23:15:54 +04:00
|
|
|
box->text = talloc_strdup(ctx->content, text);
|
2005-04-09 13:47:37 +04:00
|
|
|
free(text);
|
2011-09-29 23:15:54 +04:00
|
|
|
if (box->text == NULL)
|
2005-04-09 13:47:37 +04:00
|
|
|
return false;
|
2009-07-24 03:05:34 +04:00
|
|
|
|
2005-04-09 13:47:37 +04:00
|
|
|
box->length = strlen(box->text);
|
2009-07-24 03:05:34 +04:00
|
|
|
|
2005-03-26 04:12:27 +03:00
|
|
|
/* strip ending space char off */
|
2005-04-25 01:49:28 +04:00
|
|
|
if (box->length > 1 && box->text[box->length - 1] == ' ') {
|
2011-03-01 23:00:41 +03:00
|
|
|
box->space = UNKNOWN_WIDTH;
|
2005-03-26 04:12:27 +03:00
|
|
|
box->length--;
|
|
|
|
}
|
2009-07-24 03:05:34 +04:00
|
|
|
|
2011-09-29 23:15:54 +04:00
|
|
|
if (css_computed_text_transform(props.parent_style) !=
|
2009-07-24 03:05:34 +04:00
|
|
|
CSS_TEXT_TRANSFORM_NONE)
|
2005-03-26 04:12:27 +03:00
|
|
|
box_text_transform(box->text, box->length,
|
2011-09-29 23:15:54 +04:00
|
|
|
css_computed_text_transform(
|
|
|
|
props.parent_style));
|
2009-07-24 03:05:34 +04:00
|
|
|
|
2011-09-29 23:15:54 +04:00
|
|
|
if (css_computed_white_space(props.parent_style) ==
|
2009-07-24 03:05:34 +04:00
|
|
|
CSS_WHITE_SPACE_NOWRAP) {
|
2005-03-26 04:12:27 +03:00
|
|
|
unsigned int i;
|
2009-07-24 03:05:34 +04:00
|
|
|
|
2005-04-25 01:49:28 +04:00
|
|
|
for (i = 0; i != box->length &&
|
|
|
|
box->text[i] != ' '; ++i)
|
2005-03-26 04:12:27 +03:00
|
|
|
; /* no body */
|
2009-07-24 03:05:34 +04:00
|
|
|
|
2005-03-26 04:12:27 +03:00
|
|
|
if (i != box->length) {
|
|
|
|
/* there is a space in text block and we
|
|
|
|
* want all spaces to be converted to NBSP
|
|
|
|
*/
|
2005-04-09 13:47:37 +04:00
|
|
|
/*box->text = cnv_space2nbsp(text);
|
2005-03-26 04:12:27 +03:00
|
|
|
if (!box->text) {
|
|
|
|
free(text);
|
|
|
|
goto no_memory;
|
|
|
|
}
|
2005-04-09 13:47:37 +04:00
|
|
|
box->length = strlen(box->text);*/
|
2005-03-26 04:12:27 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-09-29 23:15:54 +04:00
|
|
|
box_add_child(props.inline_container, box);
|
2009-07-24 03:05:34 +04:00
|
|
|
|
2005-03-26 04:12:27 +03:00
|
|
|
if (box->text[0] == ' ') {
|
|
|
|
box->length--;
|
2009-07-24 03:05:34 +04:00
|
|
|
|
2005-03-26 04:12:27 +03:00
|
|
|
memmove(box->text, &box->text[1], box->length);
|
2009-07-24 03:05:34 +04:00
|
|
|
|
2005-03-26 04:12:27 +03:00
|
|
|
if (box->prev != NULL)
|
2011-03-01 23:00:41 +03:00
|
|
|
box->prev->space = UNKNOWN_WIDTH;
|
2005-03-26 04:12:27 +03:00
|
|
|
}
|
2005-03-28 23:17:06 +04:00
|
|
|
} else {
|
2005-03-26 04:12:27 +03:00
|
|
|
/* white-space: pre */
|
2011-09-29 23:15:54 +04:00
|
|
|
char *text = cnv_space2nbsp((char *) ctx->n->content);
|
2005-03-26 04:12:27 +03:00
|
|
|
char *current;
|
2009-08-02 23:19:43 +04:00
|
|
|
enum css_white_space_e white_space =
|
2011-09-29 23:15:54 +04:00
|
|
|
css_computed_white_space(props.parent_style);
|
2009-07-24 03:05:34 +04:00
|
|
|
|
2005-03-26 04:12:27 +03:00
|
|
|
/* note: pre-wrap/pre-line are unimplemented */
|
2009-07-24 03:05:34 +04:00
|
|
|
assert(white_space == CSS_WHITE_SPACE_PRE ||
|
|
|
|
white_space == CSS_WHITE_SPACE_PRE_LINE ||
|
|
|
|
white_space == CSS_WHITE_SPACE_PRE_WRAP);
|
|
|
|
|
2011-09-29 23:15:54 +04:00
|
|
|
if (text == NULL)
|
2005-04-09 13:47:37 +04:00
|
|
|
return false;
|
2009-07-24 03:05:34 +04:00
|
|
|
|
2011-09-29 23:15:54 +04:00
|
|
|
if (css_computed_text_transform(props.parent_style) !=
|
2009-07-24 03:05:34 +04:00
|
|
|
CSS_TEXT_TRANSFORM_NONE)
|
2005-03-26 04:12:27 +03:00
|
|
|
box_text_transform(text, strlen(text),
|
2011-09-29 23:15:54 +04:00
|
|
|
css_computed_text_transform(
|
|
|
|
props.parent_style));
|
2009-07-24 03:05:34 +04:00
|
|
|
|
2005-03-26 04:12:27 +03:00
|
|
|
current = text;
|
2006-07-04 01:41:25 +04:00
|
|
|
|
2006-03-27 03:52:22 +04:00
|
|
|
/* swallow a single leading new line */
|
2011-09-29 23:15:54 +04:00
|
|
|
if (props.containing_block->flags & PRE_STRIP) {
|
2006-04-04 14:56:21 +04:00
|
|
|
switch (*current) {
|
|
|
|
case '\n':
|
2011-09-29 23:15:54 +04:00
|
|
|
current++;
|
|
|
|
break;
|
2006-04-04 14:56:21 +04:00
|
|
|
case '\r':
|
|
|
|
current++;
|
2011-09-29 23:15:54 +04:00
|
|
|
if (*current == '\n')
|
|
|
|
current++;
|
2006-04-04 14:56:21 +04:00
|
|
|
break;
|
|
|
|
}
|
2011-09-29 23:15:54 +04:00
|
|
|
props.containing_block->flags &= ~PRE_STRIP;
|
2006-03-27 03:52:22 +04:00
|
|
|
}
|
2006-07-04 01:41:25 +04:00
|
|
|
|
2005-03-26 04:12:27 +03:00
|
|
|
do {
|
|
|
|
size_t len = strcspn(current, "\r\n");
|
|
|
|
char old = current[len];
|
2009-07-24 03:05:34 +04:00
|
|
|
|
2005-03-26 04:12:27 +03:00
|
|
|
current[len] = 0;
|
2009-07-24 03:05:34 +04:00
|
|
|
|
2011-09-29 23:15:54 +04:00
|
|
|
if (props.inline_container == NULL) {
|
|
|
|
/* Child of a block without a current container
|
|
|
|
* (i.e. this box is the first child of its
|
|
|
|
* parent, or was preceded by block-level
|
|
|
|
* siblings) */
|
|
|
|
props.inline_container = box_create(NULL, NULL,
|
|
|
|
false, NULL, NULL, NULL, NULL,
|
|
|
|
ctx->content);
|
|
|
|
if (props.inline_container == NULL) {
|
2005-03-26 04:12:27 +03:00
|
|
|
free(text);
|
2005-04-09 13:47:37 +04:00
|
|
|
return false;
|
2005-03-26 04:12:27 +03:00
|
|
|
}
|
2009-07-24 03:05:34 +04:00
|
|
|
|
2011-09-29 23:15:54 +04:00
|
|
|
props.inline_container->type =
|
2005-03-26 04:12:27 +03:00
|
|
|
BOX_INLINE_CONTAINER;
|
2009-07-24 03:05:34 +04:00
|
|
|
|
2011-09-29 23:15:54 +04:00
|
|
|
box_add_child(props.containing_block,
|
|
|
|
props.inline_container);
|
2005-03-26 04:12:27 +03:00
|
|
|
}
|
2009-07-24 03:05:34 +04:00
|
|
|
|
|
|
|
/** \todo Dropping const isn't clever */
|
2011-01-05 22:36:05 +03:00
|
|
|
box = box_create(NULL,
|
2011-09-29 23:15:54 +04:00
|
|
|
(css_computed_style *) props.parent_style,
|
|
|
|
false, props.href, props.target, props.title,
|
|
|
|
NULL, ctx->content);
|
|
|
|
if (box == NULL) {
|
2005-03-26 04:12:27 +03:00
|
|
|
free(text);
|
2005-04-09 13:47:37 +04:00
|
|
|
return false;
|
2005-03-26 04:12:27 +03:00
|
|
|
}
|
2009-07-24 03:05:34 +04:00
|
|
|
|
2005-05-23 01:50:14 +04:00
|
|
|
box->type = BOX_TEXT;
|
2009-07-24 03:05:34 +04:00
|
|
|
|
2011-09-29 23:15:54 +04:00
|
|
|
box->text = talloc_strdup(ctx->content, current);
|
|
|
|
if (box->text == NULL) {
|
2005-03-26 04:12:27 +03:00
|
|
|
free(text);
|
2005-04-09 13:47:37 +04:00
|
|
|
return false;
|
2005-03-26 04:12:27 +03:00
|
|
|
}
|
2009-07-24 03:05:34 +04:00
|
|
|
|
2005-03-26 04:12:27 +03:00
|
|
|
box->length = strlen(box->text);
|
2009-07-24 03:05:34 +04:00
|
|
|
|
2011-09-29 23:15:54 +04:00
|
|
|
box_add_child(props.inline_container, box);
|
2009-07-24 03:05:34 +04:00
|
|
|
|
2005-03-26 04:12:27 +03:00
|
|
|
current[len] = old;
|
2009-07-24 03:05:34 +04:00
|
|
|
|
2005-03-26 04:12:27 +03:00
|
|
|
current += len;
|
2009-07-24 03:05:34 +04:00
|
|
|
|
2011-10-04 01:32:32 +04:00
|
|
|
if (current[0] != '\0') {
|
|
|
|
/* Linebreak: create new inline container */
|
|
|
|
props.inline_container = box_create(NULL, NULL,
|
|
|
|
false, NULL, NULL, NULL, NULL,
|
|
|
|
ctx->content);
|
|
|
|
if (props.inline_container == NULL) {
|
|
|
|
free(text);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
props.inline_container->type =
|
|
|
|
BOX_INLINE_CONTAINER;
|
|
|
|
|
|
|
|
box_add_child(props.containing_block,
|
|
|
|
props.inline_container);
|
|
|
|
|
|
|
|
if (current[0] == '\r' && current[1] == '\n')
|
|
|
|
current += 2;
|
|
|
|
else
|
|
|
|
current++;
|
2005-03-26 04:12:27 +03:00
|
|
|
}
|
|
|
|
} while (*current);
|
2009-07-24 03:05:34 +04:00
|
|
|
|
2005-03-26 04:12:27 +03:00
|
|
|
free(text);
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get the style for an element.
|
|
|
|
*
|
2011-01-05 00:52:43 +03:00
|
|
|
* \param c content of type CONTENT_HTML that is being processed
|
|
|
|
* \param parent_style style at this point in xml tree, or NULL for root
|
|
|
|
* \param n node in xml tree
|
2009-07-24 03:05:34 +04:00
|
|
|
* \return the new style, or NULL on memory exhaustion
|
2005-03-26 04:12:27 +03:00
|
|
|
*/
|
2011-05-07 00:40:09 +04:00
|
|
|
css_select_results *box_get_style(html_content *c,
|
2011-01-05 22:36:05 +03:00
|
|
|
const css_computed_style *parent_style, xmlNode *n)
|
2005-03-26 04:12:27 +03:00
|
|
|
{
|
|
|
|
char *s;
|
2011-01-05 22:36:05 +03:00
|
|
|
int pseudo_element;
|
2011-01-06 14:11:47 +03:00
|
|
|
css_error error;
|
2009-07-24 03:05:34 +04:00
|
|
|
css_stylesheet *inline_style = NULL;
|
2011-01-05 22:36:05 +03:00
|
|
|
css_select_results *styles;
|
2011-05-07 00:40:09 +04:00
|
|
|
nscss_select_ctx ctx;
|
2005-03-26 04:12:27 +03:00
|
|
|
|
2009-07-24 03:05:34 +04:00
|
|
|
/* Firstly, construct inline stylesheet, if any */
|
2011-01-06 01:38:37 +03:00
|
|
|
if ((s = (char *) xmlGetProp(n, (const xmlChar *) "style"))) {
|
2009-07-24 03:05:34 +04:00
|
|
|
inline_style = nscss_create_inline_style(
|
|
|
|
(uint8_t *) s, strlen(s),
|
2011-10-03 19:56:47 +04:00
|
|
|
c->encoding,
|
|
|
|
nsurl_access(content__get_url(&c->base)),
|
2011-05-07 00:40:09 +04:00
|
|
|
c->quirks != BINDING_QUIRKS_MODE_NONE,
|
2010-04-30 11:00:58 +04:00
|
|
|
box_style_alloc, NULL);
|
2005-03-26 04:12:27 +03:00
|
|
|
|
|
|
|
xmlFree(s);
|
|
|
|
|
2009-07-24 03:05:34 +04:00
|
|
|
if (inline_style == NULL)
|
|
|
|
return NULL;
|
2008-02-25 19:37:48 +03:00
|
|
|
}
|
|
|
|
|
2011-05-07 00:40:09 +04:00
|
|
|
/* Populate selection context */
|
|
|
|
ctx.ctx = c->select_ctx;
|
|
|
|
ctx.quirks = (c->quirks == BINDING_QUIRKS_MODE_FULL);
|
|
|
|
ctx.base_url = c->base_url;
|
|
|
|
|
2009-07-24 03:05:34 +04:00
|
|
|
/* Select partial style for element */
|
2011-05-07 00:40:09 +04:00
|
|
|
styles = nscss_get_style(&ctx, n, CSS_MEDIA_SCREEN, inline_style,
|
2011-01-05 22:36:05 +03:00
|
|
|
box_style_alloc, NULL);
|
2005-04-28 05:17:52 +04:00
|
|
|
|
2009-07-24 03:05:34 +04:00
|
|
|
/* No longer need inline style */
|
|
|
|
if (inline_style != NULL)
|
|
|
|
css_stylesheet_destroy(inline_style);
|
2005-03-26 04:12:27 +03:00
|
|
|
|
2009-07-24 03:05:34 +04:00
|
|
|
/* Failed selecting partial style -- bail out */
|
2011-01-05 23:14:34 +03:00
|
|
|
if (styles == NULL)
|
2009-07-24 03:05:34 +04:00
|
|
|
return NULL;
|
2005-03-28 23:17:06 +04:00
|
|
|
|
2009-07-24 03:05:34 +04:00
|
|
|
/* If there's a parent style, compose with partial to obtain
|
|
|
|
* complete computed style for element */
|
|
|
|
if (parent_style != NULL) {
|
2008-02-25 19:37:48 +03:00
|
|
|
|
2011-01-05 22:36:05 +03:00
|
|
|
/* Complete the computed style, by composing with the parent
|
|
|
|
* element's style */
|
|
|
|
error = css_computed_style_compose(parent_style,
|
|
|
|
styles->styles[CSS_PSEUDO_ELEMENT_NONE],
|
|
|
|
nscss_compute_font_size, NULL,
|
|
|
|
styles->styles[CSS_PSEUDO_ELEMENT_NONE]);
|
2009-07-24 03:05:34 +04:00
|
|
|
if (error != CSS_OK) {
|
2011-01-05 22:36:05 +03:00
|
|
|
css_select_results_destroy(styles);
|
2009-07-24 03:05:34 +04:00
|
|
|
return NULL;
|
2008-02-25 19:37:48 +03:00
|
|
|
}
|
2011-01-05 22:36:05 +03:00
|
|
|
}
|
2008-02-25 19:37:48 +03:00
|
|
|
|
2011-01-05 22:36:05 +03:00
|
|
|
for (pseudo_element = CSS_PSEUDO_ELEMENT_NONE + 1;
|
|
|
|
pseudo_element < CSS_PSEUDO_ELEMENT_COUNT;
|
|
|
|
pseudo_element++) {
|
2011-01-06 14:11:47 +03:00
|
|
|
|
|
|
|
if (pseudo_element == CSS_PSEUDO_ELEMENT_FIRST_LETTER ||
|
|
|
|
pseudo_element == CSS_PSEUDO_ELEMENT_FIRST_LINE)
|
|
|
|
/* TODO: Handle first-line and first-letter pseudo
|
|
|
|
* element computed style completion */
|
|
|
|
continue;
|
2011-01-05 22:36:05 +03:00
|
|
|
|
|
|
|
if (styles->styles[pseudo_element] == NULL)
|
|
|
|
/* There were no rules concerning this pseudo element */
|
|
|
|
continue;
|
|
|
|
|
|
|
|
/* Complete the pseudo element's computed style, by composing
|
|
|
|
* with the base element's style */
|
|
|
|
error = css_computed_style_compose(
|
|
|
|
styles->styles[CSS_PSEUDO_ELEMENT_NONE],
|
|
|
|
styles->styles[pseudo_element],
|
|
|
|
nscss_compute_font_size, NULL,
|
|
|
|
styles->styles[pseudo_element]);
|
|
|
|
if (error != CSS_OK) {
|
|
|
|
/* TODO: perhaps this shouldn't be quite so
|
|
|
|
* catastrophic? */
|
|
|
|
css_select_results_destroy(styles);
|
|
|
|
return NULL;
|
|
|
|
}
|
2008-02-25 19:37:48 +03:00
|
|
|
}
|
|
|
|
|
2011-01-05 22:36:05 +03:00
|
|
|
return styles;
|
2005-03-26 04:12:27 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Apply the CSS text-transform property to given text for its ASCII chars.
|
|
|
|
*
|
2006-09-02 19:52:41 +04:00
|
|
|
* \param s string to transform
|
2005-03-26 04:12:27 +03:00
|
|
|
* \param len length of s
|
2006-09-02 19:52:41 +04:00
|
|
|
* \param tt transform type
|
2005-03-26 04:12:27 +03:00
|
|
|
*/
|
|
|
|
|
2009-08-02 23:19:43 +04:00
|
|
|
void box_text_transform(char *s, unsigned int len, enum css_text_transform_e tt)
|
2005-03-26 04:12:27 +03:00
|
|
|
{
|
|
|
|
unsigned int i;
|
|
|
|
if (len == 0)
|
|
|
|
return;
|
|
|
|
switch (tt) {
|
|
|
|
case CSS_TEXT_TRANSFORM_UPPERCASE:
|
|
|
|
for (i = 0; i < len; ++i)
|
2008-02-25 20:58:00 +03:00
|
|
|
if ((unsigned char) s[i] < 0x80)
|
The core code has always assumed a locale of "C".
Do not change the locale globally, else things will break in weird and
wonderful ways.
Introduce utils/locale.[ch], which provide locale-specific wrappers for various
functions (currently just the <ctype.h> ones).
Fix up the few places I can see that actually require that the underlying
locale is paid attention to.
Some notes:
1) The GTK frontend code has not been touched. It is possible that reading of
numeric values (e.g. from the preferences dialogue) may break with this
change, particularly in locales that use something other than '.' as their
decimal separator.
2) The search code is left unchanged (i.e. assuming a locale of "C").
This may break case insensitive matching of non-ASCII characters.
I doubt that ever actually worked, anyway. In future, it should use
Unicode case conversion to achieve the same effect.
3) The text input handling in the core makes use of isspace() to detect
word boundaries. This is fine for western languages (even in the C locale,
which it's currently assuming). It will, however, break for CJK et. al.
(this has always been the case, rather than being a new issue)
4) text-transform uses locale-specific variants of to{lower,upper}. In future
this should probably be performing Unicode case conversion. This is the
only part of the core code that makes use of locale information.
In future, if you require locale-specific behaviour, do the following:
setlocale(LC_<whatever>, "");
<your operation(s) here>
setlocale(LC_<whatever>, "C");
The first setlocale will change the current locale to the native environment.
The second setlocale will reset the current locale to "C".
Any value other than "" or "C" is probably a bug, unless there's a really
good reason for it.
In the long term, it is expected that all locale-dependent code will reside in
platform frontends -- the core being wholly locale agnostic (though assuming
"C" for things like decimal separators).
svn path=/trunk/netsurf/; revision=4153
2008-05-13 18:37:44 +04:00
|
|
|
s[i] = ls_toupper(s[i]);
|
2005-03-26 04:12:27 +03:00
|
|
|
break;
|
|
|
|
case CSS_TEXT_TRANSFORM_LOWERCASE:
|
|
|
|
for (i = 0; i < len; ++i)
|
2008-02-25 20:58:00 +03:00
|
|
|
if ((unsigned char) s[i] < 0x80)
|
The core code has always assumed a locale of "C".
Do not change the locale globally, else things will break in weird and
wonderful ways.
Introduce utils/locale.[ch], which provide locale-specific wrappers for various
functions (currently just the <ctype.h> ones).
Fix up the few places I can see that actually require that the underlying
locale is paid attention to.
Some notes:
1) The GTK frontend code has not been touched. It is possible that reading of
numeric values (e.g. from the preferences dialogue) may break with this
change, particularly in locales that use something other than '.' as their
decimal separator.
2) The search code is left unchanged (i.e. assuming a locale of "C").
This may break case insensitive matching of non-ASCII characters.
I doubt that ever actually worked, anyway. In future, it should use
Unicode case conversion to achieve the same effect.
3) The text input handling in the core makes use of isspace() to detect
word boundaries. This is fine for western languages (even in the C locale,
which it's currently assuming). It will, however, break for CJK et. al.
(this has always been the case, rather than being a new issue)
4) text-transform uses locale-specific variants of to{lower,upper}. In future
this should probably be performing Unicode case conversion. This is the
only part of the core code that makes use of locale information.
In future, if you require locale-specific behaviour, do the following:
setlocale(LC_<whatever>, "");
<your operation(s) here>
setlocale(LC_<whatever>, "C");
The first setlocale will change the current locale to the native environment.
The second setlocale will reset the current locale to "C".
Any value other than "" or "C" is probably a bug, unless there's a really
good reason for it.
In the long term, it is expected that all locale-dependent code will reside in
platform frontends -- the core being wholly locale agnostic (though assuming
"C" for things like decimal separators).
svn path=/trunk/netsurf/; revision=4153
2008-05-13 18:37:44 +04:00
|
|
|
s[i] = ls_tolower(s[i]);
|
2005-03-26 04:12:27 +03:00
|
|
|
break;
|
|
|
|
case CSS_TEXT_TRANSFORM_CAPITALIZE:
|
2008-02-25 20:58:00 +03:00
|
|
|
if ((unsigned char) s[0] < 0x80)
|
The core code has always assumed a locale of "C".
Do not change the locale globally, else things will break in weird and
wonderful ways.
Introduce utils/locale.[ch], which provide locale-specific wrappers for various
functions (currently just the <ctype.h> ones).
Fix up the few places I can see that actually require that the underlying
locale is paid attention to.
Some notes:
1) The GTK frontend code has not been touched. It is possible that reading of
numeric values (e.g. from the preferences dialogue) may break with this
change, particularly in locales that use something other than '.' as their
decimal separator.
2) The search code is left unchanged (i.e. assuming a locale of "C").
This may break case insensitive matching of non-ASCII characters.
I doubt that ever actually worked, anyway. In future, it should use
Unicode case conversion to achieve the same effect.
3) The text input handling in the core makes use of isspace() to detect
word boundaries. This is fine for western languages (even in the C locale,
which it's currently assuming). It will, however, break for CJK et. al.
(this has always been the case, rather than being a new issue)
4) text-transform uses locale-specific variants of to{lower,upper}. In future
this should probably be performing Unicode case conversion. This is the
only part of the core code that makes use of locale information.
In future, if you require locale-specific behaviour, do the following:
setlocale(LC_<whatever>, "");
<your operation(s) here>
setlocale(LC_<whatever>, "C");
The first setlocale will change the current locale to the native environment.
The second setlocale will reset the current locale to "C".
Any value other than "" or "C" is probably a bug, unless there's a really
good reason for it.
In the long term, it is expected that all locale-dependent code will reside in
platform frontends -- the core being wholly locale agnostic (though assuming
"C" for things like decimal separators).
svn path=/trunk/netsurf/; revision=4153
2008-05-13 18:37:44 +04:00
|
|
|
s[0] = ls_toupper(s[0]);
|
2005-03-26 04:12:27 +03:00
|
|
|
for (i = 1; i < len; ++i)
|
2008-02-27 01:31:38 +03:00
|
|
|
if ((unsigned char) s[i] < 0x80 &&
|
The core code has always assumed a locale of "C".
Do not change the locale globally, else things will break in weird and
wonderful ways.
Introduce utils/locale.[ch], which provide locale-specific wrappers for various
functions (currently just the <ctype.h> ones).
Fix up the few places I can see that actually require that the underlying
locale is paid attention to.
Some notes:
1) The GTK frontend code has not been touched. It is possible that reading of
numeric values (e.g. from the preferences dialogue) may break with this
change, particularly in locales that use something other than '.' as their
decimal separator.
2) The search code is left unchanged (i.e. assuming a locale of "C").
This may break case insensitive matching of non-ASCII characters.
I doubt that ever actually worked, anyway. In future, it should use
Unicode case conversion to achieve the same effect.
3) The text input handling in the core makes use of isspace() to detect
word boundaries. This is fine for western languages (even in the C locale,
which it's currently assuming). It will, however, break for CJK et. al.
(this has always been the case, rather than being a new issue)
4) text-transform uses locale-specific variants of to{lower,upper}. In future
this should probably be performing Unicode case conversion. This is the
only part of the core code that makes use of locale information.
In future, if you require locale-specific behaviour, do the following:
setlocale(LC_<whatever>, "");
<your operation(s) here>
setlocale(LC_<whatever>, "C");
The first setlocale will change the current locale to the native environment.
The second setlocale will reset the current locale to "C".
Any value other than "" or "C" is probably a bug, unless there's a really
good reason for it.
In the long term, it is expected that all locale-dependent code will reside in
platform frontends -- the core being wholly locale agnostic (though assuming
"C" for things like decimal separators).
svn path=/trunk/netsurf/; revision=4153
2008-05-13 18:37:44 +04:00
|
|
|
ls_isspace(s[i - 1]))
|
|
|
|
s[i] = ls_toupper(s[i]);
|
2005-03-26 04:12:27 +03:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2005-04-09 13:47:37 +04:00
|
|
|
/**
|
|
|
|
* \name Special case element handlers
|
2005-03-26 04:12:27 +03:00
|
|
|
*
|
2005-04-10 21:08:49 +04:00
|
|
|
* These functions are called by box_construct_element() when an element is
|
|
|
|
* being converted, according to the entries in element_table.
|
2005-04-09 13:47:37 +04:00
|
|
|
*
|
|
|
|
* The parameters are the xmlNode, the content for the document, and a partly
|
|
|
|
* filled in box structure for the element.
|
2005-03-26 04:12:27 +03:00
|
|
|
*
|
2005-04-09 13:47:37 +04:00
|
|
|
* Return true on success, false on memory exhaustion. Set *convert_children
|
|
|
|
* to false if children of this element in the XML tree should be skipped (for
|
|
|
|
* example, if they have been processed in some special way already).
|
2005-03-26 04:12:27 +03:00
|
|
|
*
|
2005-04-09 13:47:37 +04:00
|
|
|
* Elements ordered as in the HTML 4.01 specification. Section numbers in
|
|
|
|
* brackets [] refer to the spec.
|
|
|
|
*
|
|
|
|
* \{
|
2005-03-26 04:12:27 +03:00
|
|
|
*/
|
|
|
|
|
2005-04-09 13:47:37 +04:00
|
|
|
/**
|
|
|
|
* Document body [7.5.1].
|
|
|
|
*/
|
2005-03-26 04:12:27 +03:00
|
|
|
|
2005-04-09 13:47:37 +04:00
|
|
|
bool box_body(BOX_SPECIAL_PARAMS)
|
|
|
|
{
|
2009-07-24 03:05:34 +04:00
|
|
|
css_color color;
|
|
|
|
|
2011-01-29 22:22:12 +03:00
|
|
|
css_computed_background_color(box->style, &color);
|
|
|
|
if (nscss_color_is_transparent(color))
|
2011-05-07 00:40:09 +04:00
|
|
|
content->background_colour = NS_TRANSPARENT;
|
2009-07-24 03:05:34 +04:00
|
|
|
else
|
2011-05-07 00:40:09 +04:00
|
|
|
content->background_colour = nscss_color_to_ns(color);
|
2009-07-24 03:05:34 +04:00
|
|
|
|
2005-04-09 13:47:37 +04:00
|
|
|
return true;
|
|
|
|
}
|
2005-03-26 04:12:27 +03:00
|
|
|
|
|
|
|
|
2005-04-09 13:47:37 +04:00
|
|
|
/**
|
|
|
|
* Forced line break [9.3.2].
|
|
|
|
*/
|
2005-03-26 04:12:27 +03:00
|
|
|
|
2005-04-09 13:47:37 +04:00
|
|
|
bool box_br(BOX_SPECIAL_PARAMS)
|
|
|
|
{
|
|
|
|
box->type = BOX_BR;
|
|
|
|
return true;
|
|
|
|
}
|
2005-03-26 04:12:27 +03:00
|
|
|
|
2006-04-04 14:56:21 +04:00
|
|
|
/**
|
|
|
|
* Preformatted text [9.3.4].
|
|
|
|
*/
|
|
|
|
|
|
|
|
bool box_pre(BOX_SPECIAL_PARAMS)
|
|
|
|
{
|
2011-03-02 22:16:03 +03:00
|
|
|
box->flags |= PRE_STRIP;
|
2006-04-04 14:56:21 +04:00
|
|
|
return true;
|
|
|
|
}
|
2005-03-26 04:12:27 +03:00
|
|
|
|
2005-04-09 13:47:37 +04:00
|
|
|
/**
|
|
|
|
* Anchor [12.2].
|
|
|
|
*/
|
2005-03-26 04:12:27 +03:00
|
|
|
|
2005-04-09 13:47:37 +04:00
|
|
|
bool box_a(BOX_SPECIAL_PARAMS)
|
2005-03-26 04:12:27 +03:00
|
|
|
{
|
2005-04-10 21:08:49 +04:00
|
|
|
bool ok;
|
2011-10-04 00:28:29 +04:00
|
|
|
nsurl *url;
|
2005-04-10 21:08:49 +04:00
|
|
|
xmlChar *s;
|
2005-03-26 04:12:27 +03:00
|
|
|
|
2005-04-10 21:08:49 +04:00
|
|
|
if ((s = xmlGetProp(n, (const xmlChar *) "href"))) {
|
|
|
|
ok = box_extract_link((const char *) s,
|
2011-10-04 00:28:29 +04:00
|
|
|
content->base_url, &url);
|
2005-04-09 13:47:37 +04:00
|
|
|
xmlFree(s);
|
2005-04-10 21:08:49 +04:00
|
|
|
if (!ok)
|
2005-04-09 13:47:37 +04:00
|
|
|
return false;
|
2005-04-10 21:08:49 +04:00
|
|
|
if (url) {
|
2011-10-04 00:28:29 +04:00
|
|
|
if (box->href != NULL)
|
|
|
|
nsurl_unref(box->href);
|
|
|
|
box->href = url;
|
2005-04-10 21:08:49 +04:00
|
|
|
}
|
2005-04-09 13:47:37 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/* name and id share the same namespace */
|
2005-04-10 21:08:49 +04:00
|
|
|
if (!box_get_attribute(n, "name", content, &box->id))
|
|
|
|
return false;
|
2005-04-09 13:47:37 +04:00
|
|
|
|
2005-08-21 02:52:20 +04:00
|
|
|
/* target frame [16.3] */
|
|
|
|
if ((s = xmlGetProp(n, (const xmlChar *) "target"))) {
|
2008-02-25 20:58:00 +03:00
|
|
|
if (!strcasecmp((const char *) s, "_blank"))
|
2006-09-02 19:52:41 +04:00
|
|
|
box->target = TARGET_BLANK;
|
2008-02-25 20:58:00 +03:00
|
|
|
else if (!strcasecmp((const char *) s, "_top"))
|
2005-08-21 02:52:20 +04:00
|
|
|
box->target = TARGET_TOP;
|
2008-02-25 20:58:00 +03:00
|
|
|
else if (!strcasecmp((const char *) s, "_parent"))
|
2005-08-21 02:52:20 +04:00
|
|
|
box->target = TARGET_PARENT;
|
2008-02-25 20:58:00 +03:00
|
|
|
else if (!strcasecmp((const char *) s, "_self"))
|
2005-08-21 02:52:20 +04:00
|
|
|
/* the default may have been overridden by a
|
|
|
|
* <base target=...>, so this is different to 0 */
|
|
|
|
box->target = TARGET_SELF;
|
2009-06-08 13:34:58 +04:00
|
|
|
else {
|
|
|
|
/* 6.16 says that frame names must begin with [a-zA-Z]
|
|
|
|
* This doesn't match reality, so just take anything */
|
2008-02-25 20:58:00 +03:00
|
|
|
box->target = talloc_strdup(content, (const char *) s);
|
2005-08-21 02:52:20 +04:00
|
|
|
if (!box->target) {
|
|
|
|
xmlFree(s);
|
|
|
|
return false;
|
|
|
|
}
|
2006-09-02 19:52:41 +04:00
|
|
|
}
|
2005-08-21 02:52:20 +04:00
|
|
|
xmlFree(s);
|
|
|
|
}
|
|
|
|
|
2005-04-09 13:47:37 +04:00
|
|
|
return true;
|
2005-03-26 04:12:27 +03:00
|
|
|
}
|
|
|
|
|
2005-04-09 13:47:37 +04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Embedded image [13.2].
|
|
|
|
*/
|
|
|
|
|
|
|
|
bool box_image(BOX_SPECIAL_PARAMS)
|
2005-03-26 04:12:27 +03:00
|
|
|
{
|
2005-04-14 01:58:28 +04:00
|
|
|
bool ok;
|
2011-10-04 00:28:29 +04:00
|
|
|
char *s;
|
|
|
|
nsurl *url;
|
2005-04-14 01:58:28 +04:00
|
|
|
xmlChar *alt, *src;
|
2011-04-27 17:50:49 +04:00
|
|
|
enum css_width_e wtype;
|
|
|
|
enum css_height_e htype;
|
|
|
|
css_fixed value = 0;
|
|
|
|
css_unit wunit = CSS_UNIT_PX;
|
|
|
|
css_unit hunit = CSS_UNIT_PX;
|
2005-03-26 04:12:27 +03:00
|
|
|
|
2009-07-24 03:05:34 +04:00
|
|
|
if (box->style && css_computed_display(box->style,
|
|
|
|
n->parent == NULL) == CSS_DISPLAY_NONE)
|
2007-04-07 03:48:26 +04:00
|
|
|
return true;
|
|
|
|
|
2005-03-26 04:12:27 +03:00
|
|
|
/* handle alt text */
|
2005-04-14 01:58:28 +04:00
|
|
|
if ((alt = xmlGetProp(n, (const xmlChar *) "alt"))) {
|
2008-02-25 20:58:00 +03:00
|
|
|
s = squash_whitespace((const char *) alt);
|
2005-04-14 01:58:28 +04:00
|
|
|
xmlFree(alt);
|
2005-04-09 13:47:37 +04:00
|
|
|
if (!s)
|
|
|
|
return false;
|
|
|
|
box->text = talloc_strdup(content, s);
|
|
|
|
free(s);
|
2005-03-26 04:12:27 +03:00
|
|
|
if (!box->text)
|
2005-04-09 13:47:37 +04:00
|
|
|
return false;
|
2005-03-26 04:12:27 +03:00
|
|
|
box->length = strlen(box->text);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* imagemap associated with this image */
|
2005-04-10 21:08:49 +04:00
|
|
|
if (!box_get_attribute(n, "usemap", content, &box->usemap))
|
|
|
|
return false;
|
|
|
|
if (box->usemap && box->usemap[0] == '#')
|
|
|
|
box->usemap++;
|
2005-03-26 04:12:27 +03:00
|
|
|
|
2005-04-14 01:58:28 +04:00
|
|
|
/* get image URL */
|
|
|
|
if (!(src = xmlGetProp(n, (const xmlChar *) "src")))
|
2005-04-09 13:47:37 +04:00
|
|
|
return true;
|
2011-10-04 00:28:29 +04:00
|
|
|
if (!box_extract_link((char *) src, content->base_url, &url))
|
2005-04-09 13:47:37 +04:00
|
|
|
return false;
|
2005-04-14 01:58:28 +04:00
|
|
|
xmlFree(src);
|
|
|
|
if (!url)
|
2005-04-09 13:47:37 +04:00
|
|
|
return true;
|
2005-03-26 04:12:27 +03:00
|
|
|
|
|
|
|
/* start fetch */
|
2011-10-04 14:23:47 +04:00
|
|
|
ok = html_fetch_object(content, url, box, image_types,
|
2011-05-07 00:40:09 +04:00
|
|
|
content->base.available_width, 1000, false);
|
2011-10-04 00:28:29 +04:00
|
|
|
nsurl_unref(url);
|
2011-04-27 17:50:49 +04:00
|
|
|
|
|
|
|
wtype = css_computed_width(box->style, &value, &wunit);
|
|
|
|
htype = css_computed_height(box->style, &value, &hunit);
|
|
|
|
|
|
|
|
if (wtype == CSS_WIDTH_SET && wunit != CSS_UNIT_PCT &&
|
|
|
|
htype == CSS_HEIGHT_SET && hunit != CSS_UNIT_PCT) {
|
|
|
|
/* We know the dimensions the image will be shown at before it's
|
|
|
|
* fetched. */
|
|
|
|
box->flags |= REPLACE_DIM;
|
|
|
|
}
|
|
|
|
|
2005-04-14 01:58:28 +04:00
|
|
|
return ok;
|
2005-03-26 04:12:27 +03:00
|
|
|
}
|
|
|
|
|
2005-04-09 13:47:37 +04:00
|
|
|
|
2011-10-04 01:04:54 +04:00
|
|
|
/**
|
|
|
|
* Destructor for object_params, for <object> elements
|
|
|
|
*
|
|
|
|
* \param b The object params being destroyed.
|
|
|
|
* \return 0 to allow talloc to continue destroying the tree.
|
|
|
|
*/
|
|
|
|
static int box_object_talloc_destructor(struct object_params *o)
|
|
|
|
{
|
|
|
|
if (o->codebase != NULL)
|
|
|
|
nsurl_unref(o->codebase);
|
|
|
|
if (o->classid != NULL)
|
|
|
|
nsurl_unref(o->classid);
|
|
|
|
if (o->data != NULL)
|
|
|
|
nsurl_unref(o->data);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-04-09 13:47:37 +04:00
|
|
|
/**
|
|
|
|
* Generic embedded object [13.3].
|
|
|
|
*/
|
|
|
|
|
|
|
|
bool box_object(BOX_SPECIAL_PARAMS)
|
2005-03-26 04:12:27 +03:00
|
|
|
{
|
2005-04-14 01:58:28 +04:00
|
|
|
struct object_params *params;
|
|
|
|
struct object_param *param;
|
|
|
|
xmlChar *codebase, *classid, *data;
|
2005-04-09 13:47:37 +04:00
|
|
|
xmlNode *c;
|
2005-03-26 04:12:27 +03:00
|
|
|
|
2009-07-24 03:05:34 +04:00
|
|
|
if (box->style && css_computed_display(box->style,
|
|
|
|
n->parent == NULL) == CSS_DISPLAY_NONE)
|
2007-04-07 03:48:26 +04:00
|
|
|
return true;
|
|
|
|
|
2005-04-10 21:08:49 +04:00
|
|
|
if (!box_get_attribute(n, "usemap", content, &box->usemap))
|
|
|
|
return false;
|
|
|
|
if (box->usemap && box->usemap[0] == '#')
|
|
|
|
box->usemap++;
|
2005-03-26 04:12:27 +03:00
|
|
|
|
2005-04-14 01:58:28 +04:00
|
|
|
params = talloc(content, struct object_params);
|
|
|
|
if (!params)
|
2005-04-10 21:08:49 +04:00
|
|
|
return false;
|
2011-10-04 01:04:54 +04:00
|
|
|
|
|
|
|
talloc_set_destructor(params, box_object_talloc_destructor);
|
|
|
|
|
2005-04-14 01:58:28 +04:00
|
|
|
params->data = 0;
|
|
|
|
params->type = 0;
|
|
|
|
params->codetype = 0;
|
|
|
|
params->codebase = 0;
|
|
|
|
params->classid = 0;
|
|
|
|
params->params = 0;
|
|
|
|
|
2007-03-23 01:47:24 +03:00
|
|
|
/* codebase, classid, and data are URLs
|
|
|
|
* (codebase is the base for the other two) */
|
2005-04-14 01:58:28 +04:00
|
|
|
if ((codebase = xmlGetProp(n, (const xmlChar *) "codebase"))) {
|
2011-10-04 00:28:29 +04:00
|
|
|
if (!box_extract_link((char *) codebase, content->base_url,
|
2007-03-23 01:47:24 +03:00
|
|
|
¶ms->codebase))
|
2005-04-14 01:58:28 +04:00
|
|
|
return false;
|
|
|
|
xmlFree(codebase);
|
|
|
|
}
|
|
|
|
if (!params->codebase)
|
2011-10-04 00:28:29 +04:00
|
|
|
params->codebase = nsurl_ref(content->base_url);
|
2005-04-14 01:58:28 +04:00
|
|
|
|
2007-03-23 01:47:24 +03:00
|
|
|
if ((classid = xmlGetProp(n, (const xmlChar *) "classid"))) {
|
2005-04-14 01:58:28 +04:00
|
|
|
if (!box_extract_link((char *) classid, params->codebase,
|
|
|
|
¶ms->classid))
|
|
|
|
return false;
|
|
|
|
xmlFree(classid);
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((data = xmlGetProp(n, (const xmlChar *) "data"))) {
|
|
|
|
if (!box_extract_link((char *) data, params->codebase,
|
|
|
|
¶ms->data))
|
|
|
|
return false;
|
|
|
|
xmlFree(data);
|
|
|
|
}
|
2007-03-23 01:47:24 +03:00
|
|
|
|
|
|
|
if (!params->classid && !params->data)
|
|
|
|
/* nothing to embed; ignore */
|
2005-04-14 01:58:28 +04:00
|
|
|
return true;
|
|
|
|
|
2006-03-27 05:04:56 +04:00
|
|
|
/* Don't include ourself */
|
2011-10-04 00:28:29 +04:00
|
|
|
if (params->classid && nsurl_compare(content->base_url,
|
|
|
|
params->classid, NSURL_COMPLETE))
|
2007-03-23 01:47:24 +03:00
|
|
|
return true;
|
|
|
|
|
2011-10-04 00:28:29 +04:00
|
|
|
if (params->data && nsurl_compare(content->base_url,
|
|
|
|
params->data, NSURL_COMPLETE))
|
2006-03-27 05:04:56 +04:00
|
|
|
return true;
|
|
|
|
|
2005-04-14 01:58:28 +04:00
|
|
|
/* codetype and type are MIME types */
|
|
|
|
if (!box_get_attribute(n, "codetype", params, ¶ms->codetype))
|
2005-04-10 21:08:49 +04:00
|
|
|
return false;
|
2005-04-14 01:58:28 +04:00
|
|
|
if (!box_get_attribute(n, "type", params, ¶ms->type))
|
2005-04-10 21:08:49 +04:00
|
|
|
return false;
|
2007-03-23 01:47:24 +03:00
|
|
|
|
|
|
|
/* classid && !data => classid is used (consult codetype)
|
|
|
|
* (classid || !classid) && data => data is used (consult type)
|
|
|
|
* !classid && !data => invalid; ignored */
|
|
|
|
|
2011-07-08 12:38:17 +04:00
|
|
|
if (params->classid != NULL && params->data == NULL &&
|
|
|
|
params->codetype != NULL) {
|
|
|
|
lwc_string *icodetype;
|
|
|
|
lwc_error lerror;
|
|
|
|
|
|
|
|
lerror = lwc_intern_string(params->codetype,
|
|
|
|
strlen(params->codetype), &icodetype);
|
|
|
|
if (lerror != lwc_error_ok)
|
|
|
|
return false;
|
2007-03-23 01:47:24 +03:00
|
|
|
|
2011-07-08 12:38:17 +04:00
|
|
|
if (content_factory_type_from_mime_type(icodetype) ==
|
|
|
|
CONTENT_NONE) {
|
|
|
|
/* can't handle this MIME type */
|
|
|
|
lwc_string_unref(icodetype);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
lwc_string_unref(icodetype);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (params->data != NULL && params->type != NULL) {
|
|
|
|
lwc_string *itype;
|
|
|
|
lwc_error lerror;
|
|
|
|
|
|
|
|
lerror = lwc_intern_string(params->type, strlen(params->type),
|
|
|
|
&itype);
|
|
|
|
if (lerror != lwc_error_ok)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (content_factory_type_from_mime_type(itype) ==
|
|
|
|
CONTENT_NONE) {
|
|
|
|
/* can't handle this MIME type */
|
|
|
|
lwc_string_unref(itype);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
lwc_string_unref(itype);
|
|
|
|
}
|
2005-03-26 04:12:27 +03:00
|
|
|
|
2005-04-14 01:58:28 +04:00
|
|
|
/* add parameters to linked list */
|
2005-04-09 13:47:37 +04:00
|
|
|
for (c = n->children; c; c = c->next) {
|
|
|
|
if (c->type != XML_ELEMENT_NODE)
|
|
|
|
continue;
|
|
|
|
if (strcmp((const char *) c->name, "param") != 0)
|
2005-04-14 01:58:28 +04:00
|
|
|
/* The first non-param child is the start of the alt
|
|
|
|
* html. Therefore, we should break out of this loop. */
|
2005-03-26 04:12:27 +03:00
|
|
|
break;
|
|
|
|
|
2005-04-14 01:58:28 +04:00
|
|
|
param = talloc(params, struct object_param);
|
|
|
|
if (!param)
|
2005-04-09 13:47:37 +04:00
|
|
|
return false;
|
2005-04-14 01:58:28 +04:00
|
|
|
param->name = 0;
|
|
|
|
param->value = 0;
|
|
|
|
param->type = 0;
|
|
|
|
param->valuetype = 0;
|
|
|
|
param->next = 0;
|
2005-04-09 13:47:37 +04:00
|
|
|
|
2005-04-14 01:58:28 +04:00
|
|
|
if (!box_get_attribute(c, "name", param, ¶m->name))
|
2005-04-10 21:08:49 +04:00
|
|
|
return false;
|
2005-04-14 01:58:28 +04:00
|
|
|
if (!box_get_attribute(c, "value", param, ¶m->value))
|
2005-04-10 21:08:49 +04:00
|
|
|
return false;
|
2005-04-14 01:58:28 +04:00
|
|
|
if (!box_get_attribute(c, "type", param, ¶m->type))
|
2005-04-10 21:08:49 +04:00
|
|
|
return false;
|
2005-04-14 01:58:28 +04:00
|
|
|
if (!box_get_attribute(c, "valuetype", param,
|
|
|
|
¶m->valuetype))
|
2005-04-10 21:08:49 +04:00
|
|
|
return false;
|
2006-09-11 15:55:17 +04:00
|
|
|
if (!param->valuetype) {
|
|
|
|
param->valuetype = talloc_strdup(param, "data");
|
|
|
|
if (!param->valuetype)
|
|
|
|
return false;
|
|
|
|
}
|
2005-03-26 04:12:27 +03:00
|
|
|
|
2005-04-14 01:58:28 +04:00
|
|
|
param->next = params->params;
|
|
|
|
params->params = param;
|
2005-03-26 04:12:27 +03:00
|
|
|
}
|
|
|
|
|
2005-04-14 01:58:28 +04:00
|
|
|
box->object_params = params;
|
2005-04-09 13:47:37 +04:00
|
|
|
|
2005-04-14 01:58:28 +04:00
|
|
|
/* start fetch (MIME type is ok or not specified) */
|
2007-03-23 01:47:24 +03:00
|
|
|
if (!html_fetch_object(content,
|
2011-10-04 14:23:47 +04:00
|
|
|
params->data ? params->data : params->classid,
|
|
|
|
box, CONTENT_ANY, content->base.available_width, 1000,
|
2011-05-07 00:40:09 +04:00
|
|
|
false))
|
2005-04-14 01:58:28 +04:00
|
|
|
return false;
|
|
|
|
|
|
|
|
*convert_children = false;
|
2005-04-09 13:47:37 +04:00
|
|
|
return true;
|
2005-03-26 04:12:27 +03:00
|
|
|
}
|
|
|
|
|
2005-04-09 13:47:37 +04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Window subdivision [16.2.1].
|
2005-03-26 04:12:27 +03:00
|
|
|
*/
|
|
|
|
|
2005-04-09 13:47:37 +04:00
|
|
|
bool box_frameset(BOX_SPECIAL_PARAMS)
|
2005-03-26 04:12:27 +03:00
|
|
|
{
|
2006-09-02 19:52:41 +04:00
|
|
|
bool ok;
|
|
|
|
|
2011-05-07 00:40:09 +04:00
|
|
|
if (content->frameset) {
|
2006-09-02 19:52:41 +04:00
|
|
|
LOG(("Error: multiple framesets in document."));
|
2008-02-25 11:53:04 +03:00
|
|
|
/* Don't convert children */
|
|
|
|
if (convert_children)
|
|
|
|
*convert_children = false;
|
|
|
|
/* And ignore this spurious frameset */
|
2009-07-24 03:05:34 +04:00
|
|
|
box->type = BOX_NONE;
|
2008-02-25 11:53:04 +03:00
|
|
|
return true;
|
2006-09-02 19:52:41 +04:00
|
|
|
}
|
2006-09-11 15:55:17 +04:00
|
|
|
|
2011-05-07 00:40:09 +04:00
|
|
|
content->frameset = talloc_zero(content, struct content_html_frames);
|
|
|
|
if (!content->frameset)
|
2006-09-02 19:52:41 +04:00
|
|
|
return false;
|
2006-09-11 15:55:17 +04:00
|
|
|
|
2011-05-07 00:40:09 +04:00
|
|
|
ok = box_create_frameset(content->frameset, n, content);
|
2006-09-02 19:52:41 +04:00
|
|
|
if (ok)
|
2009-07-24 03:05:34 +04:00
|
|
|
box->type = BOX_NONE;
|
2006-09-02 19:52:41 +04:00
|
|
|
|
|
|
|
if (convert_children)
|
|
|
|
*convert_children = false;
|
|
|
|
return ok;
|
|
|
|
}
|
|
|
|
|
2011-10-04 01:49:28 +04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Destructor for content_html_frames, for <frame> elements
|
|
|
|
*
|
|
|
|
* \param b The frame params being destroyed.
|
|
|
|
* \return 0 to allow talloc to continue destroying the tree.
|
|
|
|
*/
|
|
|
|
static int box_frames_talloc_destructor(struct content_html_frames *f)
|
|
|
|
{
|
2011-10-04 02:06:47 +04:00
|
|
|
if (f->url != NULL) {
|
2011-10-04 01:49:28 +04:00
|
|
|
nsurl_unref(f->url);
|
2011-10-04 02:06:47 +04:00
|
|
|
f->url = NULL;
|
|
|
|
}
|
2011-10-04 01:49:28 +04:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2006-09-02 19:52:41 +04:00
|
|
|
bool box_create_frameset(struct content_html_frames *f, xmlNode *n,
|
2011-05-07 00:40:09 +04:00
|
|
|
html_content *content) {
|
2006-09-02 19:52:41 +04:00
|
|
|
unsigned int row, col, index, i;
|
2005-04-09 13:47:37 +04:00
|
|
|
unsigned int rows = 1, cols = 1;
|
2011-10-04 00:28:29 +04:00
|
|
|
char *s;
|
|
|
|
nsurl *url;
|
2006-09-02 19:52:41 +04:00
|
|
|
struct frame_dimension *row_height = 0, *col_width = 0;
|
2005-04-09 13:47:37 +04:00
|
|
|
xmlNode *c;
|
2006-09-02 19:52:41 +04:00
|
|
|
struct content_html_frames *frame;
|
|
|
|
bool default_border = true;
|
|
|
|
colour default_border_colour = 0x000000;
|
2005-03-26 04:12:27 +03:00
|
|
|
|
2005-04-09 13:47:37 +04:00
|
|
|
/* parse rows and columns */
|
|
|
|
if ((s = (char *) xmlGetProp(n, (const xmlChar *) "rows"))) {
|
2006-09-02 19:52:41 +04:00
|
|
|
row_height = box_parse_multi_lengths(s, &rows);
|
2005-03-26 04:12:27 +03:00
|
|
|
xmlFree(s);
|
2005-04-09 13:47:37 +04:00
|
|
|
if (!row_height)
|
|
|
|
return false;
|
2006-09-02 19:52:41 +04:00
|
|
|
} else {
|
|
|
|
row_height = calloc(1, sizeof(struct frame_dimension));
|
|
|
|
if (!row_height)
|
|
|
|
return false;
|
|
|
|
row_height->value = 100;
|
|
|
|
row_height->unit = FRAME_DIMENSION_PERCENT;
|
2005-04-09 13:47:37 +04:00
|
|
|
}
|
2005-03-26 04:12:27 +03:00
|
|
|
|
2005-04-09 13:47:37 +04:00
|
|
|
if ((s = (char *) xmlGetProp(n, (const xmlChar *) "cols"))) {
|
2006-09-02 19:52:41 +04:00
|
|
|
col_width = box_parse_multi_lengths(s, &cols);
|
2005-04-09 13:47:37 +04:00
|
|
|
xmlFree(s);
|
|
|
|
if (!col_width)
|
|
|
|
return false;
|
2006-09-02 19:52:41 +04:00
|
|
|
} else {
|
|
|
|
col_width = calloc(1, sizeof(struct frame_dimension));
|
|
|
|
if (!col_width)
|
|
|
|
return false;
|
|
|
|
col_width->value = 100;
|
|
|
|
col_width->unit = FRAME_DIMENSION_PERCENT;
|
2005-04-09 13:47:37 +04:00
|
|
|
}
|
2005-03-26 04:12:27 +03:00
|
|
|
|
2006-09-02 19:52:41 +04:00
|
|
|
/* common extension: border="0|1" to control all children */
|
|
|
|
if ((s = (char *) xmlGetProp(n, (const xmlChar *) "border"))) {
|
|
|
|
if ((s[0] == '0') && (s[1] == '\0'))
|
|
|
|
default_border = false;
|
|
|
|
xmlFree(s);
|
|
|
|
}
|
|
|
|
/* common extension: frameborder="yes|no" to control all children */
|
|
|
|
if ((s = (char *) xmlGetProp(n, (const xmlChar *) "frameborder"))) {
|
|
|
|
if (!strcasecmp(s, "no"))
|
|
|
|
default_border = false;
|
|
|
|
xmlFree(s);
|
|
|
|
}
|
2008-02-25 19:37:48 +03:00
|
|
|
/* common extension: bordercolor="#RRGGBB|<named colour>" to control
|
|
|
|
*all children */
|
2006-09-02 19:52:41 +04:00
|
|
|
if ((s = (char *) xmlGetProp(n, (const xmlChar *) "bordercolor"))) {
|
2009-07-24 03:05:34 +04:00
|
|
|
css_color color;
|
|
|
|
|
|
|
|
if (nscss_parse_colour((const char *) s, &color))
|
|
|
|
default_border_colour = nscss_color_to_ns(color);
|
|
|
|
|
2006-09-02 19:52:41 +04:00
|
|
|
xmlFree(s);
|
|
|
|
}
|
2006-09-11 15:55:17 +04:00
|
|
|
|
2006-09-02 19:52:41 +04:00
|
|
|
/* update frameset and create default children */
|
|
|
|
f->cols = cols;
|
|
|
|
f->rows = rows;
|
|
|
|
f->scrolling = SCROLLING_NO;
|
2008-02-25 19:37:48 +03:00
|
|
|
f->children = talloc_array(content, struct content_html_frames,
|
|
|
|
(rows * cols));
|
2011-10-04 01:49:28 +04:00
|
|
|
|
|
|
|
talloc_set_destructor(f->children, box_frames_talloc_destructor);
|
|
|
|
|
2006-09-02 19:52:41 +04:00
|
|
|
for (row = 0; row < rows; row++) {
|
|
|
|
for (col = 0; col < cols; col++) {
|
|
|
|
index = (row * cols) + col;
|
|
|
|
frame = &f->children[index];
|
|
|
|
frame->cols = 0;
|
|
|
|
frame->rows = 0;
|
|
|
|
frame->width = col_width[col];
|
|
|
|
frame->height = row_height[row];
|
2007-01-25 01:53:09 +03:00
|
|
|
frame->margin_width = 0;
|
|
|
|
frame->margin_height = 0;
|
2006-09-02 19:52:41 +04:00
|
|
|
frame->name = NULL;
|
|
|
|
frame->url = NULL;
|
|
|
|
frame->no_resize = false;
|
|
|
|
frame->scrolling = SCROLLING_AUTO;
|
|
|
|
frame->border = default_border;
|
|
|
|
frame->border_colour = default_border_colour;
|
|
|
|
frame->children = NULL;
|
2005-04-09 13:47:37 +04:00
|
|
|
}
|
|
|
|
}
|
2006-09-02 19:52:41 +04:00
|
|
|
free(col_width);
|
|
|
|
free(row_height);
|
2005-04-09 13:47:37 +04:00
|
|
|
|
2006-09-02 19:52:41 +04:00
|
|
|
/* create the frameset windows */
|
2005-04-09 13:47:37 +04:00
|
|
|
c = n->children;
|
2006-09-02 19:52:41 +04:00
|
|
|
for (row = 0; c && row < rows; row++) {
|
|
|
|
for (col = 0; c && col < cols; col++) {
|
2005-04-09 13:47:37 +04:00
|
|
|
while (c && !(c->type == XML_ELEMENT_NODE && (
|
|
|
|
strcmp((const char *) c->name, "frame") == 0 ||
|
|
|
|
strcmp((const char *) c->name, "frameset") == 0
|
|
|
|
)))
|
|
|
|
c = c->next;
|
|
|
|
if (!c)
|
|
|
|
break;
|
|
|
|
|
2006-09-02 19:52:41 +04:00
|
|
|
/* get current frame */
|
|
|
|
index = (row * cols) + col;
|
|
|
|
frame = &f->children[index];
|
2005-04-09 13:47:37 +04:00
|
|
|
|
2006-09-02 19:52:41 +04:00
|
|
|
/* nest framesets */
|
2005-04-09 13:47:37 +04:00
|
|
|
if (strcmp((const char *) c->name, "frameset") == 0) {
|
2006-09-02 19:52:41 +04:00
|
|
|
frame->border = 0;
|
|
|
|
if (!box_create_frameset(frame, c, content))
|
2005-04-09 13:47:37 +04:00
|
|
|
return false;
|
|
|
|
c = c->next;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2007-01-03 23:11:09 +03:00
|
|
|
/* get frame URL (not required) */
|
|
|
|
url = NULL;
|
|
|
|
if ((s = (char *) xmlGetProp(c,
|
2005-04-09 13:47:37 +04:00
|
|
|
(const xmlChar *) "src"))) {
|
2011-10-04 00:28:29 +04:00
|
|
|
box_extract_link(s, content->base_url, &url);
|
2006-09-02 19:52:41 +04:00
|
|
|
xmlFree(s);
|
|
|
|
}
|
2007-01-25 01:53:09 +03:00
|
|
|
|
2007-01-03 23:11:09 +03:00
|
|
|
/* copy url */
|
|
|
|
if (url) {
|
|
|
|
/* no self-references */
|
2011-10-04 00:28:29 +04:00
|
|
|
if (nsurl_compare(content->base_url, url,
|
2011-10-04 01:49:28 +04:00
|
|
|
NSURL_COMPLETE) == false)
|
2011-10-04 00:28:29 +04:00
|
|
|
frame->url = url;
|
2011-10-04 01:49:28 +04:00
|
|
|
url = NULL;
|
2006-09-02 19:52:41 +04:00
|
|
|
}
|
2007-01-25 01:53:09 +03:00
|
|
|
|
2006-09-02 19:52:41 +04:00
|
|
|
/* fill in specified values */
|
|
|
|
if ((s = (char *) xmlGetProp(c,
|
|
|
|
(const xmlChar *) "name"))) {
|
|
|
|
frame->name = talloc_strdup(content, s);
|
|
|
|
xmlFree(s);
|
|
|
|
}
|
|
|
|
frame->no_resize = xmlHasProp(c,
|
2009-04-16 16:49:49 +04:00
|
|
|
(const xmlChar *) "noresize") != NULL;
|
2006-09-02 19:52:41 +04:00
|
|
|
if ((s = (char *) xmlGetProp(c,
|
|
|
|
(const xmlChar *) "frameborder"))) {
|
|
|
|
i = atoi(s);
|
|
|
|
frame->border = (i != 0);
|
|
|
|
xmlFree(s);
|
|
|
|
}
|
|
|
|
if ((s = (char *) xmlGetProp(c,
|
|
|
|
(const xmlChar *) "scrolling"))) {
|
|
|
|
if (!strcasecmp(s, "yes"))
|
|
|
|
frame->scrolling = SCROLLING_YES;
|
|
|
|
else if (!strcasecmp(s, "no"))
|
|
|
|
frame->scrolling = SCROLLING_NO;
|
|
|
|
xmlFree(s);
|
|
|
|
}
|
|
|
|
if ((s = (char *) xmlGetProp(c,
|
|
|
|
(const xmlChar *) "marginwidth"))) {
|
|
|
|
frame->margin_width = atoi(s);
|
|
|
|
xmlFree(s);
|
|
|
|
}
|
|
|
|
if ((s = (char *) xmlGetProp(c,
|
|
|
|
(const xmlChar *) "marginheight"))) {
|
2007-01-25 01:53:09 +03:00
|
|
|
frame->margin_height = atoi(s);
|
2006-09-02 19:52:41 +04:00
|
|
|
xmlFree(s);
|
|
|
|
}
|
2008-02-25 19:37:48 +03:00
|
|
|
if ((s = (char *) xmlGetProp(c, (const xmlChar *)
|
|
|
|
"bordercolor"))) {
|
2009-07-24 03:05:34 +04:00
|
|
|
css_color color;
|
|
|
|
|
|
|
|
if (nscss_parse_colour((const char *) s,
|
|
|
|
&color))
|
|
|
|
frame->border_colour =
|
|
|
|
nscss_color_to_ns(color);
|
|
|
|
|
2006-09-02 19:52:41 +04:00
|
|
|
xmlFree(s);
|
|
|
|
}
|
2005-04-09 13:47:37 +04:00
|
|
|
|
2007-01-03 23:11:09 +03:00
|
|
|
/* advance */
|
2005-04-09 13:47:37 +04:00
|
|
|
c = c->next;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2005-03-26 04:12:27 +03:00
|
|
|
return true;
|
2005-04-09 13:47:37 +04:00
|
|
|
}
|
2005-03-26 04:12:27 +03:00
|
|
|
|
2005-04-09 13:47:37 +04:00
|
|
|
|
2011-10-04 01:49:28 +04:00
|
|
|
/**
|
|
|
|
* Destructor for content_html_iframe, for <iframe> elements
|
|
|
|
*
|
|
|
|
* \param b The iframe params being destroyed.
|
|
|
|
* \return 0 to allow talloc to continue destroying the tree.
|
|
|
|
*/
|
|
|
|
static int box_iframes_talloc_destructor(struct content_html_iframe *f)
|
|
|
|
{
|
2011-10-04 02:06:47 +04:00
|
|
|
if (f->url != NULL) {
|
2011-10-04 01:49:28 +04:00
|
|
|
nsurl_unref(f->url);
|
2011-10-04 02:06:47 +04:00
|
|
|
f->url = NULL;
|
|
|
|
}
|
2011-10-04 01:49:28 +04:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2005-04-09 13:47:37 +04:00
|
|
|
/**
|
|
|
|
* Inline subwindow [16.5].
|
|
|
|
*/
|
|
|
|
|
|
|
|
bool box_iframe(BOX_SPECIAL_PARAMS)
|
|
|
|
{
|
2011-10-04 00:28:29 +04:00
|
|
|
nsurl *url;
|
|
|
|
char *s;
|
2006-09-02 19:52:41 +04:00
|
|
|
struct content_html_iframe *iframe;
|
|
|
|
int i;
|
2005-04-09 13:47:37 +04:00
|
|
|
|
2011-01-05 04:26:29 +03:00
|
|
|
if (box->style && css_computed_display(box->style,
|
|
|
|
n->parent == NULL) == CSS_DISPLAY_NONE)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
if (box->style && css_computed_visibility(box->style) ==
|
|
|
|
CSS_VISIBILITY_HIDDEN)
|
|
|
|
/* Don't create iframe discriptors for invisible iframes
|
|
|
|
* TODO: handle hidden iframes at browser_window generation
|
|
|
|
* time instead? */
|
|
|
|
return true;
|
|
|
|
|
2005-04-14 01:58:28 +04:00
|
|
|
/* get frame URL */
|
2006-09-02 19:52:41 +04:00
|
|
|
if (!(s = (char *) xmlGetProp(n,
|
|
|
|
(const xmlChar *) "src")))
|
2005-04-14 01:58:28 +04:00
|
|
|
return true;
|
2011-10-04 00:28:29 +04:00
|
|
|
if (!box_extract_link(s, content->base_url, &url)) {
|
2006-09-02 19:52:41 +04:00
|
|
|
xmlFree(s);
|
2005-04-09 13:47:37 +04:00
|
|
|
return false;
|
2006-09-02 19:52:41 +04:00
|
|
|
}
|
|
|
|
xmlFree(s);
|
2005-04-14 01:58:28 +04:00
|
|
|
if (!url)
|
|
|
|
return true;
|
2005-04-09 13:47:37 +04:00
|
|
|
|
2006-09-02 19:52:41 +04:00
|
|
|
/* don't include ourself */
|
2011-10-04 00:28:29 +04:00
|
|
|
if (nsurl_compare(content->base_url, url, NSURL_COMPLETE)) {
|
|
|
|
nsurl_unref(url);
|
2006-03-27 05:04:56 +04:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2006-09-02 19:52:41 +04:00
|
|
|
/* create a new iframe */
|
|
|
|
iframe = talloc(content, struct content_html_iframe);
|
|
|
|
if (!iframe) {
|
2011-10-04 00:28:29 +04:00
|
|
|
nsurl_unref(url);
|
2006-09-02 19:52:41 +04:00
|
|
|
return false;
|
|
|
|
}
|
2011-10-04 01:49:28 +04:00
|
|
|
|
|
|
|
talloc_set_destructor(iframe, box_iframes_talloc_destructor);
|
|
|
|
|
2006-09-02 19:52:41 +04:00
|
|
|
iframe->box = box;
|
2007-01-25 01:53:09 +03:00
|
|
|
iframe->margin_width = 0;
|
|
|
|
iframe->margin_height = 0;
|
2006-09-02 19:52:41 +04:00
|
|
|
iframe->name = NULL;
|
2011-10-04 00:28:29 +04:00
|
|
|
iframe->url = url;
|
2006-09-02 19:52:41 +04:00
|
|
|
iframe->scrolling = SCROLLING_AUTO;
|
|
|
|
iframe->border = true;
|
2010-06-09 13:53:07 +04:00
|
|
|
|
|
|
|
/* Add this iframe to the linked list of iframes */
|
2011-05-07 00:40:09 +04:00
|
|
|
iframe->next = content->iframe;
|
|
|
|
content->iframe = iframe;
|
2006-09-02 19:52:41 +04:00
|
|
|
|
|
|
|
/* fill in specified values */
|
2011-05-07 00:40:09 +04:00
|
|
|
if ((s = (char *) xmlGetProp(n, (const xmlChar *) "name"))) {
|
2006-09-02 19:52:41 +04:00
|
|
|
iframe->name = talloc_strdup(content, s);
|
2006-09-11 15:55:17 +04:00
|
|
|
xmlFree(s);
|
2006-09-02 19:52:41 +04:00
|
|
|
}
|
2011-05-07 00:40:09 +04:00
|
|
|
if ((s = (char *) xmlGetProp(n, (const xmlChar *) "frameborder"))) {
|
2006-09-02 19:52:41 +04:00
|
|
|
i = atoi(s);
|
|
|
|
iframe->border = (i != 0);
|
|
|
|
xmlFree(s);
|
|
|
|
}
|
|
|
|
if ((s = (char *) xmlGetProp(n, (const xmlChar *) "bordercolor"))) {
|
2009-07-24 03:05:34 +04:00
|
|
|
css_color color;
|
|
|
|
|
|
|
|
if (nscss_parse_colour(s, &color))
|
|
|
|
iframe->border_colour = nscss_color_to_ns(color);
|
|
|
|
|
2006-09-02 19:52:41 +04:00
|
|
|
xmlFree(s);
|
|
|
|
}
|
2011-05-07 00:40:09 +04:00
|
|
|
if ((s = (char *) xmlGetProp(n, (const xmlChar *) "scrolling"))) {
|
2006-09-02 19:52:41 +04:00
|
|
|
if (!strcasecmp(s, "yes"))
|
|
|
|
iframe->scrolling = SCROLLING_YES;
|
|
|
|
else if (!strcasecmp(s, "no"))
|
|
|
|
iframe->scrolling = SCROLLING_NO;
|
|
|
|
xmlFree(s);
|
|
|
|
}
|
2011-05-07 00:40:09 +04:00
|
|
|
if ((s = (char *) xmlGetProp(n, (const xmlChar *) "marginwidth"))) {
|
2006-09-02 19:52:41 +04:00
|
|
|
iframe->margin_width = atoi(s);
|
|
|
|
xmlFree(s);
|
|
|
|
}
|
2011-05-07 00:40:09 +04:00
|
|
|
if ((s = (char *) xmlGetProp(n, (const xmlChar *) "marginheight"))) {
|
2007-01-25 01:53:09 +03:00
|
|
|
iframe->margin_height = atoi(s);
|
2006-09-02 19:52:41 +04:00
|
|
|
xmlFree(s);
|
|
|
|
}
|
2006-09-11 15:55:17 +04:00
|
|
|
|
2006-09-02 19:52:41 +04:00
|
|
|
/* box */
|
|
|
|
assert(box->style);
|
2011-06-15 00:00:18 +04:00
|
|
|
box->flags |= IFRAME;
|
2006-08-25 00:02:37 +04:00
|
|
|
|
2010-06-03 19:15:31 +04:00
|
|
|
/* Showing iframe, so don't show alternate content */
|
2006-09-02 19:52:41 +04:00
|
|
|
if (convert_children)
|
|
|
|
*convert_children = false;
|
|
|
|
return true;
|
2005-03-26 04:12:27 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2005-04-09 13:47:37 +04:00
|
|
|
/**
|
|
|
|
* Form control [17.4].
|
|
|
|
*/
|
|
|
|
|
|
|
|
bool box_input(BOX_SPECIAL_PARAMS)
|
2005-03-26 04:12:27 +03:00
|
|
|
{
|
|
|
|
struct form_control *gadget = NULL;
|
2011-10-04 14:23:47 +04:00
|
|
|
char *s, *type;
|
|
|
|
nsurl *url;
|
|
|
|
nserror error;
|
2005-03-26 04:12:27 +03:00
|
|
|
|
|
|
|
type = (char *) xmlGetProp(n, (const xmlChar *) "type");
|
|
|
|
|
2011-05-07 00:40:09 +04:00
|
|
|
gadget = binding_get_control_for_node(content->parser_binding, n);
|
2009-02-20 14:39:25 +03:00
|
|
|
if (!gadget)
|
|
|
|
goto no_memory;
|
|
|
|
box->gadget = gadget;
|
|
|
|
gadget->box = box;
|
|
|
|
|
2005-03-26 04:12:27 +03:00
|
|
|
if (type && strcasecmp(type, "password") == 0) {
|
2009-07-24 03:05:34 +04:00
|
|
|
if (!box_input_text(n, content, box, 0, true))
|
2005-03-26 04:12:27 +03:00
|
|
|
goto no_memory;
|
|
|
|
} else if (type && strcasecmp(type, "file") == 0) {
|
|
|
|
box->type = BOX_INLINE_BLOCK;
|
|
|
|
} else if (type && strcasecmp(type, "hidden") == 0) {
|
|
|
|
/* no box for hidden inputs */
|
2009-07-24 03:05:34 +04:00
|
|
|
box->type = BOX_NONE;
|
2005-03-26 04:12:27 +03:00
|
|
|
} else if (type && (strcasecmp(type, "checkbox") == 0 ||
|
|
|
|
strcasecmp(type, "radio") == 0)) {
|
|
|
|
} else if (type && (strcasecmp(type, "submit") == 0 ||
|
2009-02-20 14:39:25 +03:00
|
|
|
strcasecmp(type, "reset") == 0 ||
|
|
|
|
strcasecmp(type, "button") == 0)) {
|
2005-03-26 04:12:27 +03:00
|
|
|
struct box *inline_container, *inline_box;
|
2009-07-24 03:05:34 +04:00
|
|
|
|
|
|
|
if (!box_button(n, content, box, 0))
|
2005-03-26 04:12:27 +03:00
|
|
|
goto no_memory;
|
2009-07-24 03:05:34 +04:00
|
|
|
|
2011-01-05 22:36:05 +03:00
|
|
|
inline_container = box_create(NULL, 0, false, 0, 0, 0, 0,
|
|
|
|
content);
|
2005-03-26 04:12:27 +03:00
|
|
|
if (!inline_container)
|
|
|
|
goto no_memory;
|
2009-07-24 03:05:34 +04:00
|
|
|
|
2005-03-26 04:12:27 +03:00
|
|
|
inline_container->type = BOX_INLINE_CONTAINER;
|
2009-07-24 03:05:34 +04:00
|
|
|
|
2011-01-05 22:36:05 +03:00
|
|
|
inline_box = box_create(NULL, box->style, false, 0, 0,
|
|
|
|
box->title, 0, content);
|
2005-03-26 04:12:27 +03:00
|
|
|
if (!inline_box)
|
|
|
|
goto no_memory;
|
2009-07-24 03:05:34 +04:00
|
|
|
|
2005-05-25 00:21:47 +04:00
|
|
|
inline_box->type = BOX_TEXT;
|
2009-07-24 03:05:34 +04:00
|
|
|
|
2005-03-26 04:12:27 +03:00
|
|
|
if (box->gadget->value != NULL)
|
2005-04-09 13:47:37 +04:00
|
|
|
inline_box->text = talloc_strdup(content,
|
|
|
|
box->gadget->value);
|
2005-03-26 04:12:27 +03:00
|
|
|
else if (box->gadget->type == GADGET_SUBMIT)
|
2005-04-09 13:47:37 +04:00
|
|
|
inline_box->text = talloc_strdup(content,
|
|
|
|
messages_get("Form_Submit"));
|
2009-02-20 14:39:25 +03:00
|
|
|
else if (box->gadget->type == GADGET_RESET)
|
2005-04-09 13:47:37 +04:00
|
|
|
inline_box->text = talloc_strdup(content,
|
|
|
|
messages_get("Form_Reset"));
|
2005-03-26 04:12:27 +03:00
|
|
|
else
|
2005-04-09 13:47:37 +04:00
|
|
|
inline_box->text = talloc_strdup(content, "Button");
|
2009-07-24 03:05:34 +04:00
|
|
|
|
2005-03-26 04:12:27 +03:00
|
|
|
if (!inline_box->text)
|
|
|
|
goto no_memory;
|
2009-07-24 03:05:34 +04:00
|
|
|
|
2005-03-26 04:12:27 +03:00
|
|
|
inline_box->length = strlen(inline_box->text);
|
2009-07-24 03:05:34 +04:00
|
|
|
|
2005-03-26 04:12:27 +03:00
|
|
|
box_add_child(inline_container, inline_box);
|
2009-07-24 03:05:34 +04:00
|
|
|
|
2005-03-26 04:12:27 +03:00
|
|
|
box_add_child(box, inline_container);
|
|
|
|
} else if (type && strcasecmp(type, "image") == 0) {
|
|
|
|
gadget->type = GADGET_IMAGE;
|
2007-04-07 03:48:26 +04:00
|
|
|
|
2009-07-24 03:05:34 +04:00
|
|
|
if (box->style && css_computed_display(box->style,
|
|
|
|
n->parent == NULL) != CSS_DISPLAY_NONE) {
|
2007-04-07 03:48:26 +04:00
|
|
|
if ((s = (char *) xmlGetProp(n,
|
|
|
|
(const xmlChar*) "src"))) {
|
2011-10-04 14:23:47 +04:00
|
|
|
error = nsurl_join(content->base_url, s, &url);
|
2007-04-07 03:48:26 +04:00
|
|
|
xmlFree(s);
|
2011-10-04 14:23:47 +04:00
|
|
|
if (error != NSERROR_OK)
|
|
|
|
goto no_memory;
|
|
|
|
|
2007-04-07 03:48:26 +04:00
|
|
|
/* if url is equivalent to the parent's url,
|
|
|
|
* we've got infinite inclusion. stop it here
|
|
|
|
*/
|
2011-10-04 14:23:47 +04:00
|
|
|
if (nsurl_compare(url, content->base_url,
|
|
|
|
NSURL_COMPLETE) == false) {
|
2007-04-07 03:48:26 +04:00
|
|
|
if (!html_fetch_object(content, url,
|
|
|
|
box, image_types,
|
2011-05-07 00:40:09 +04:00
|
|
|
content->base.
|
2008-02-25 19:37:48 +03:00
|
|
|
available_width,
|
2007-04-07 03:48:26 +04:00
|
|
|
1000, false)) {
|
2011-10-04 14:23:47 +04:00
|
|
|
nsurl_unref(url);
|
2007-04-07 03:48:26 +04:00
|
|
|
goto no_memory;
|
|
|
|
}
|
2006-09-02 19:52:41 +04:00
|
|
|
}
|
2011-10-04 14:23:47 +04:00
|
|
|
nsurl_unref(url);
|
2006-09-02 19:52:41 +04:00
|
|
|
}
|
2005-03-26 04:12:27 +03:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
/* the default type is "text" */
|
2009-07-24 03:05:34 +04:00
|
|
|
if (!box_input_text(n, content, box, 0, false))
|
2005-03-26 04:12:27 +03:00
|
|
|
goto no_memory;
|
|
|
|
}
|
|
|
|
|
2005-04-09 13:47:37 +04:00
|
|
|
if (type)
|
2005-03-26 04:12:27 +03:00
|
|
|
xmlFree(type);
|
|
|
|
|
2005-04-09 13:47:37 +04:00
|
|
|
*convert_children = false;
|
|
|
|
return true;
|
2005-03-26 04:12:27 +03:00
|
|
|
|
|
|
|
no_memory:
|
|
|
|
if (type)
|
|
|
|
xmlFree(type);
|
2005-04-09 13:47:37 +04:00
|
|
|
return false;
|
2005-03-26 04:12:27 +03:00
|
|
|
}
|
|
|
|
|
2005-04-09 13:47:37 +04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Helper function for box_input().
|
|
|
|
*/
|
|
|
|
|
|
|
|
bool box_input_text(BOX_SPECIAL_PARAMS, bool password)
|
2005-03-26 04:12:27 +03:00
|
|
|
{
|
|
|
|
struct box *inline_container, *inline_box;
|
|
|
|
|
|
|
|
box->type = BOX_INLINE_BLOCK;
|
|
|
|
|
2011-01-05 22:36:05 +03:00
|
|
|
inline_container = box_create(NULL, 0, false, 0, 0, 0, 0, content);
|
2005-03-26 04:12:27 +03:00
|
|
|
if (!inline_container)
|
2009-04-16 16:49:49 +04:00
|
|
|
return false;
|
2005-03-26 04:12:27 +03:00
|
|
|
inline_container->type = BOX_INLINE_CONTAINER;
|
2011-01-05 22:36:05 +03:00
|
|
|
inline_box = box_create(NULL, box->style, false, 0, 0, box->title, 0,
|
|
|
|
content);
|
2005-03-26 04:12:27 +03:00
|
|
|
if (!inline_box)
|
2009-04-16 16:49:49 +04:00
|
|
|
return false;
|
2005-05-25 00:21:47 +04:00
|
|
|
inline_box->type = BOX_TEXT;
|
2005-03-26 04:12:27 +03:00
|
|
|
if (password) {
|
|
|
|
inline_box->length = strlen(box->gadget->value);
|
2005-04-09 13:47:37 +04:00
|
|
|
inline_box->text = talloc_array(content, char,
|
|
|
|
inline_box->length + 1);
|
2005-03-26 04:12:27 +03:00
|
|
|
if (!inline_box->text)
|
2009-04-16 16:49:49 +04:00
|
|
|
return false;
|
2005-03-26 04:12:27 +03:00
|
|
|
memset(inline_box->text, '*', inline_box->length);
|
|
|
|
inline_box->text[inline_box->length] = '\0';
|
|
|
|
} else {
|
2005-04-09 13:47:37 +04:00
|
|
|
/* replace spaces/TABs with hard spaces to prevent line
|
|
|
|
* wrapping */
|
|
|
|
char *text = cnv_space2nbsp(box->gadget->value);
|
|
|
|
if (!text)
|
2009-04-16 16:49:49 +04:00
|
|
|
return false;
|
2005-04-09 13:47:37 +04:00
|
|
|
inline_box->text = talloc_strdup(content, text);
|
|
|
|
free(text);
|
2005-03-26 04:12:27 +03:00
|
|
|
if (!inline_box->text)
|
2009-04-16 16:49:49 +04:00
|
|
|
return false;
|
2005-03-26 04:12:27 +03:00
|
|
|
inline_box->length = strlen(inline_box->text);
|
|
|
|
}
|
|
|
|
box_add_child(inline_container, inline_box);
|
|
|
|
box_add_child(box, inline_container);
|
|
|
|
|
2009-04-16 16:49:49 +04:00
|
|
|
return true;
|
2005-03-26 04:12:27 +03:00
|
|
|
}
|
|
|
|
|
2005-04-09 13:47:37 +04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Push button [17.5].
|
|
|
|
*/
|
|
|
|
|
|
|
|
bool box_button(BOX_SPECIAL_PARAMS)
|
2005-03-26 04:12:27 +03:00
|
|
|
{
|
2009-02-20 14:39:25 +03:00
|
|
|
struct form_control *gadget;
|
|
|
|
|
2011-05-07 00:40:09 +04:00
|
|
|
gadget = binding_get_control_for_node(content->parser_binding, n);
|
2009-02-20 14:39:25 +03:00
|
|
|
if (!gadget)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
box->gadget = gadget;
|
|
|
|
gadget->box = box;
|
2009-04-17 04:54:27 +04:00
|
|
|
|
2009-02-20 14:39:25 +03:00
|
|
|
box->type = BOX_INLINE_BLOCK;
|
2009-04-29 00:13:10 +04:00
|
|
|
|
2009-02-20 14:39:25 +03:00
|
|
|
/* Just render the contents */
|
2005-03-26 04:12:27 +03:00
|
|
|
|
2005-04-09 13:47:37 +04:00
|
|
|
return true;
|
2005-03-26 04:12:27 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2005-04-09 13:47:37 +04:00
|
|
|
* Option selector [17.6].
|
2005-03-26 04:12:27 +03:00
|
|
|
*/
|
|
|
|
|
2005-04-09 13:47:37 +04:00
|
|
|
bool box_select(BOX_SPECIAL_PARAMS)
|
|
|
|
{
|
|
|
|
struct box *inline_container;
|
|
|
|
struct box *inline_box;
|
|
|
|
struct form_control *gadget;
|
|
|
|
xmlNode *c, *c2;
|
2005-03-26 04:12:27 +03:00
|
|
|
|
2011-05-07 00:40:09 +04:00
|
|
|
gadget = binding_get_control_for_node(content->parser_binding, n);
|
2005-04-09 13:47:37 +04:00
|
|
|
if (!gadget)
|
|
|
|
return false;
|
2005-03-26 04:12:27 +03:00
|
|
|
|
2005-04-09 13:47:37 +04:00
|
|
|
for (c = n->children; c; c = c->next) {
|
|
|
|
if (strcmp((const char *) c->name, "option") == 0) {
|
|
|
|
if (!box_select_add_option(gadget, c))
|
|
|
|
goto no_memory;
|
|
|
|
} else if (strcmp((const char *) c->name, "optgroup") == 0) {
|
|
|
|
for (c2 = c->children; c2; c2 = c2->next) {
|
|
|
|
if (strcmp((const char *) c2->name,
|
|
|
|
"option") == 0) {
|
|
|
|
if (!box_select_add_option(gadget, c2))
|
|
|
|
goto no_memory;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2005-03-26 04:12:27 +03:00
|
|
|
}
|
|
|
|
|
2005-04-09 13:47:37 +04:00
|
|
|
if (gadget->data.select.num_items == 0) {
|
|
|
|
/* no options: ignore entire select */
|
|
|
|
return true;
|
2005-03-26 04:12:27 +03:00
|
|
|
}
|
|
|
|
|
2005-04-09 13:47:37 +04:00
|
|
|
box->type = BOX_INLINE_BLOCK;
|
|
|
|
box->gadget = gadget;
|
|
|
|
gadget->box = box;
|
2005-03-26 04:12:27 +03:00
|
|
|
|
2011-01-05 22:36:05 +03:00
|
|
|
inline_container = box_create(NULL, 0, false, 0, 0, 0, 0, content);
|
2005-04-09 13:47:37 +04:00
|
|
|
if (!inline_container)
|
|
|
|
goto no_memory;
|
|
|
|
inline_container->type = BOX_INLINE_CONTAINER;
|
2011-01-05 22:36:05 +03:00
|
|
|
inline_box = box_create(NULL, box->style, false, 0, 0, box->title, 0,
|
|
|
|
content);
|
2005-04-09 13:47:37 +04:00
|
|
|
if (!inline_box)
|
|
|
|
goto no_memory;
|
2005-05-25 00:21:47 +04:00
|
|
|
inline_box->type = BOX_TEXT;
|
2005-04-09 13:47:37 +04:00
|
|
|
box_add_child(inline_container, inline_box);
|
|
|
|
box_add_child(box, inline_container);
|
2005-03-26 04:12:27 +03:00
|
|
|
|
2005-04-09 13:47:37 +04:00
|
|
|
if (!gadget->data.select.multiple &&
|
|
|
|
gadget->data.select.num_selected == 0) {
|
|
|
|
gadget->data.select.current = gadget->data.select.items;
|
|
|
|
gadget->data.select.current->initial_selected =
|
|
|
|
gadget->data.select.current->selected = true;
|
|
|
|
gadget->data.select.num_selected = 1;
|
2005-03-26 04:12:27 +03:00
|
|
|
}
|
|
|
|
|
2005-04-09 13:47:37 +04:00
|
|
|
if (gadget->data.select.num_selected == 0)
|
2005-04-15 03:17:37 +04:00
|
|
|
inline_box->text = talloc_strdup(content,
|
|
|
|
messages_get("Form_None"));
|
2005-04-09 13:47:37 +04:00
|
|
|
else if (gadget->data.select.num_selected == 1)
|
|
|
|
inline_box->text = talloc_strdup(content,
|
|
|
|
gadget->data.select.current->text);
|
|
|
|
else
|
2005-04-15 03:17:37 +04:00
|
|
|
inline_box->text = talloc_strdup(content,
|
|
|
|
messages_get("Form_Many"));
|
2005-04-09 13:47:37 +04:00
|
|
|
if (!inline_box->text)
|
|
|
|
goto no_memory;
|
2005-03-26 04:12:27 +03:00
|
|
|
|
2005-04-09 13:47:37 +04:00
|
|
|
inline_box->length = strlen(inline_box->text);
|
2005-03-26 04:12:27 +03:00
|
|
|
|
2005-04-09 13:47:37 +04:00
|
|
|
*convert_children = false;
|
|
|
|
return true;
|
2005-03-26 04:12:27 +03:00
|
|
|
|
2005-04-09 13:47:37 +04:00
|
|
|
no_memory:
|
|
|
|
return false;
|
2005-03-26 04:12:27 +03:00
|
|
|
}
|
|
|
|
|
2005-04-09 13:47:37 +04:00
|
|
|
|
2005-03-26 04:12:27 +03:00
|
|
|
/**
|
2005-04-09 13:47:37 +04:00
|
|
|
* Add an option to a form select control (helper function for box_select()).
|
|
|
|
*
|
|
|
|
* \param control select containing the option
|
2006-09-02 19:52:41 +04:00
|
|
|
* \param n xml element node for <option>
|
2005-04-09 13:47:37 +04:00
|
|
|
* \return true on success, false on memory exhaustion
|
2005-03-26 04:12:27 +03:00
|
|
|
*/
|
|
|
|
|
2005-04-09 13:47:37 +04:00
|
|
|
bool box_select_add_option(struct form_control *control, xmlNode *n)
|
|
|
|
{
|
|
|
|
char *value = 0;
|
|
|
|
char *text = 0;
|
2008-02-25 19:37:48 +03:00
|
|
|
char *text_nowrap = 0;
|
2005-04-09 13:47:37 +04:00
|
|
|
bool selected;
|
|
|
|
xmlChar *content;
|
2008-02-25 20:58:00 +03:00
|
|
|
char *s;
|
2005-03-26 04:12:27 +03:00
|
|
|
|
2005-04-09 13:47:37 +04:00
|
|
|
content = xmlNodeGetContent(n);
|
|
|
|
if (!content)
|
|
|
|
goto no_memory;
|
2008-02-25 20:58:00 +03:00
|
|
|
text = squash_whitespace((const char *) content);
|
2005-04-09 13:47:37 +04:00
|
|
|
xmlFree(content);
|
|
|
|
if (!text)
|
|
|
|
goto no_memory;
|
2005-03-26 04:12:27 +03:00
|
|
|
|
2005-04-09 13:47:37 +04:00
|
|
|
if ((s = (char *) xmlGetProp(n, (const xmlChar *) "value"))) {
|
|
|
|
value = strdup(s);
|
2005-03-26 04:12:27 +03:00
|
|
|
xmlFree(s);
|
2005-04-09 13:47:37 +04:00
|
|
|
} else
|
|
|
|
value = strdup(text);
|
|
|
|
if (!value)
|
|
|
|
goto no_memory;
|
2005-03-26 04:12:27 +03:00
|
|
|
|
2009-04-16 16:49:49 +04:00
|
|
|
selected = xmlHasProp(n, (const xmlChar *) "selected") != NULL;
|
2005-03-26 04:12:27 +03:00
|
|
|
|
2008-02-25 19:37:48 +03:00
|
|
|
/* replace spaces/TABs with hard spaces to prevent line wrapping */
|
|
|
|
text_nowrap = cnv_space2nbsp(text);
|
|
|
|
if (!text_nowrap)
|
|
|
|
goto no_memory;
|
|
|
|
|
|
|
|
if (!form_add_option(control, value, text_nowrap, selected))
|
2005-04-09 13:47:37 +04:00
|
|
|
goto no_memory;
|
2005-03-26 04:12:27 +03:00
|
|
|
|
2008-02-25 19:37:48 +03:00
|
|
|
free(text);
|
|
|
|
|
2005-04-09 13:47:37 +04:00
|
|
|
return true;
|
2005-03-26 04:12:27 +03:00
|
|
|
|
|
|
|
no_memory:
|
2005-04-09 13:47:37 +04:00
|
|
|
free(value);
|
|
|
|
free(text);
|
2008-02-25 19:37:48 +03:00
|
|
|
free(text_nowrap);
|
2005-04-09 13:47:37 +04:00
|
|
|
return false;
|
2005-03-26 04:12:27 +03:00
|
|
|
}
|
|
|
|
|
2005-04-09 13:47:37 +04:00
|
|
|
|
2005-03-26 04:12:27 +03:00
|
|
|
/**
|
2005-04-09 13:47:37 +04:00
|
|
|
* Multi-line text field [17.7].
|
2005-03-26 04:12:27 +03:00
|
|
|
*/
|
|
|
|
|
2005-04-09 13:47:37 +04:00
|
|
|
bool box_textarea(BOX_SPECIAL_PARAMS)
|
|
|
|
{
|
|
|
|
/* A textarea is an INLINE_BLOCK containing a single INLINE_CONTAINER,
|
2005-05-25 00:21:47 +04:00
|
|
|
* which contains the text as runs of TEXT separated by BR. There is
|
|
|
|
* at least one TEXT. The first and last boxes are TEXT.
|
2005-04-09 13:47:37 +04:00
|
|
|
* Consecutive BR may not be present. These constraints are satisfied
|
2005-05-25 00:21:47 +04:00
|
|
|
* by using a 0-length TEXT for blank lines. */
|
2005-03-26 04:12:27 +03:00
|
|
|
|
2006-09-11 15:55:17 +04:00
|
|
|
xmlChar *current, *string;
|
2006-09-10 17:27:08 +04:00
|
|
|
xmlNode *n2;
|
|
|
|
xmlBufferPtr buf;
|
2006-09-11 15:55:17 +04:00
|
|
|
xmlParserCtxtPtr ctxt;
|
2005-04-09 13:47:37 +04:00
|
|
|
struct box *inline_container, *inline_box, *br_box;
|
|
|
|
char *s;
|
|
|
|
size_t len;
|
2005-03-26 04:12:27 +03:00
|
|
|
|
2005-04-09 13:47:37 +04:00
|
|
|
box->type = BOX_INLINE_BLOCK;
|
2011-05-07 00:40:09 +04:00
|
|
|
box->gadget = binding_get_control_for_node(content->parser_binding, n);
|
2005-04-09 13:47:37 +04:00
|
|
|
if (!box->gadget)
|
|
|
|
return false;
|
|
|
|
box->gadget->box = box;
|
2005-03-26 04:12:27 +03:00
|
|
|
|
2011-01-05 22:36:05 +03:00
|
|
|
inline_container = box_create(NULL, 0, false, 0, 0, box->title, 0,
|
|
|
|
content);
|
2005-04-09 13:47:37 +04:00
|
|
|
if (!inline_container)
|
|
|
|
return false;
|
|
|
|
inline_container->type = BOX_INLINE_CONTAINER;
|
|
|
|
box_add_child(box, inline_container);
|
|
|
|
|
2009-02-20 14:39:25 +03:00
|
|
|
/** \todo Is it really necessary to reparse the content of a
|
|
|
|
* textarea element to remove entities? Hubbub will do that for us.
|
|
|
|
*/
|
2006-09-10 17:27:08 +04:00
|
|
|
n2 = n->children;
|
|
|
|
buf = xmlBufferCreate();
|
|
|
|
while(n2) {
|
2006-09-11 15:55:17 +04:00
|
|
|
int ret = xmlNodeDump(buf, n2->doc, n2, 0, 0);
|
2006-09-10 17:27:08 +04:00
|
|
|
if (ret == -1) {
|
|
|
|
xmlBufferFree(buf);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
n2 = n2->next;
|
|
|
|
}
|
2006-09-11 15:55:17 +04:00
|
|
|
|
|
|
|
ctxt = xmlCreateDocParserCtxt(buf->content);
|
|
|
|
string = current = NULL;
|
|
|
|
if (ctxt) {
|
|
|
|
string = current = xmlStringDecodeEntities(ctxt,
|
|
|
|
buf->content,
|
2007-02-12 02:21:05 +03:00
|
|
|
XML_SUBSTITUTE_REF,
|
2006-09-11 15:55:17 +04:00
|
|
|
0, 0, 0);
|
2006-09-11 12:02:13 +04:00
|
|
|
xmlFreeParserCtxt(ctxt);
|
|
|
|
}
|
2006-09-11 15:55:17 +04:00
|
|
|
|
|
|
|
if (!string) {
|
|
|
|
/* If we get here, either the parser context failed to be
|
|
|
|
* created or we were unable to decode the entities in the
|
|
|
|
* buffer. Therefore, try to create a blank string in order
|
|
|
|
* to recover. */
|
|
|
|
string = current = xmlStrdup((const xmlChar *) "");
|
|
|
|
if (!string) {
|
|
|
|
xmlBufferFree(buf);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2005-04-09 13:47:37 +04:00
|
|
|
while (1) {
|
2005-05-25 00:21:47 +04:00
|
|
|
/* BOX_TEXT */
|
2008-02-25 20:58:00 +03:00
|
|
|
len = strcspn((const char *) current, "\r\n");
|
|
|
|
s = talloc_strndup(content, (const char *) current, len);
|
2005-04-09 13:47:37 +04:00
|
|
|
if (!s) {
|
2006-09-11 15:55:17 +04:00
|
|
|
xmlFree(string);
|
2006-09-10 17:27:08 +04:00
|
|
|
xmlBufferFree(buf);
|
2005-04-09 13:47:37 +04:00
|
|
|
return false;
|
2005-03-26 04:12:27 +03:00
|
|
|
}
|
|
|
|
|
2011-01-05 22:36:05 +03:00
|
|
|
inline_box = box_create(NULL, box->style, false, 0, 0,
|
|
|
|
box->title, 0, content);
|
2006-09-10 17:27:08 +04:00
|
|
|
if (!inline_box) {
|
2006-09-11 15:55:17 +04:00
|
|
|
xmlFree(string);
|
2006-09-10 17:27:08 +04:00
|
|
|
xmlBufferFree(buf);
|
2005-04-09 13:47:37 +04:00
|
|
|
return false;
|
2006-09-10 17:27:08 +04:00
|
|
|
}
|
2005-05-25 00:21:47 +04:00
|
|
|
inline_box->type = BOX_TEXT;
|
2005-04-09 13:47:37 +04:00
|
|
|
inline_box->text = s;
|
|
|
|
inline_box->length = len;
|
|
|
|
box_add_child(inline_container, inline_box);
|
2005-03-26 04:12:27 +03:00
|
|
|
|
2005-04-09 13:47:37 +04:00
|
|
|
current += len;
|
|
|
|
if (current[0] == 0)
|
|
|
|
/* finished */
|
|
|
|
break;
|
2005-03-26 04:12:27 +03:00
|
|
|
|
2005-04-09 13:47:37 +04:00
|
|
|
/* BOX_BR */
|
2011-01-05 22:36:05 +03:00
|
|
|
br_box = box_create(NULL, box->style, false, 0, 0, box->title,
|
|
|
|
0, content);
|
2006-09-10 17:27:08 +04:00
|
|
|
if (!br_box) {
|
2006-09-11 15:55:17 +04:00
|
|
|
xmlFree(string);
|
2006-09-10 17:27:08 +04:00
|
|
|
xmlBufferFree(buf);
|
2005-04-09 13:47:37 +04:00
|
|
|
return false;
|
2006-09-10 17:27:08 +04:00
|
|
|
}
|
2005-04-09 13:47:37 +04:00
|
|
|
br_box->type = BOX_BR;
|
|
|
|
box_add_child(inline_container, br_box);
|
2005-03-26 04:12:27 +03:00
|
|
|
|
2005-04-09 13:47:37 +04:00
|
|
|
if (current[0] == '\r' && current[1] == '\n')
|
|
|
|
current += 2;
|
|
|
|
else
|
|
|
|
current++;
|
2005-03-26 04:12:27 +03:00
|
|
|
}
|
2006-09-11 15:55:17 +04:00
|
|
|
|
|
|
|
xmlFree(string);
|
2006-09-10 17:27:08 +04:00
|
|
|
xmlBufferFree(buf);
|
2005-03-26 04:12:27 +03:00
|
|
|
|
2005-04-09 13:47:37 +04:00
|
|
|
*convert_children = false;
|
|
|
|
return true;
|
2005-03-26 04:12:27 +03:00
|
|
|
}
|
|
|
|
|
2005-04-09 13:47:37 +04:00
|
|
|
|
2005-03-26 04:12:27 +03:00
|
|
|
/**
|
2005-04-09 13:47:37 +04:00
|
|
|
* Embedded object (not in any HTML specification:
|
|
|
|
* see http://wp.netscape.com/assist/net_sites/new_html3_prop.html )
|
2005-03-26 04:12:27 +03:00
|
|
|
*/
|
2005-04-09 13:47:37 +04:00
|
|
|
|
|
|
|
bool box_embed(BOX_SPECIAL_PARAMS)
|
2005-03-26 04:12:27 +03:00
|
|
|
{
|
2005-04-14 01:58:28 +04:00
|
|
|
struct object_params *params;
|
|
|
|
struct object_param *param;
|
|
|
|
xmlChar *src;
|
2005-04-09 13:47:37 +04:00
|
|
|
xmlAttr *a;
|
2005-03-26 04:12:27 +03:00
|
|
|
|
2009-07-24 03:05:34 +04:00
|
|
|
if (box->style && css_computed_display(box->style,
|
|
|
|
n->parent == NULL) == CSS_DISPLAY_NONE)
|
2007-04-07 03:48:26 +04:00
|
|
|
return true;
|
|
|
|
|
2005-04-14 01:58:28 +04:00
|
|
|
params = talloc(content, struct object_params);
|
|
|
|
if (!params)
|
2005-04-09 13:47:37 +04:00
|
|
|
return false;
|
2011-10-04 01:49:28 +04:00
|
|
|
|
|
|
|
talloc_set_destructor(params, box_object_talloc_destructor);
|
|
|
|
|
2005-04-14 01:58:28 +04:00
|
|
|
params->data = 0;
|
|
|
|
params->type = 0;
|
|
|
|
params->codetype = 0;
|
|
|
|
params->codebase = 0;
|
|
|
|
params->classid = 0;
|
|
|
|
params->params = 0;
|
|
|
|
|
|
|
|
/* src is a URL */
|
|
|
|
if (!(src = xmlGetProp(n, (const xmlChar *) "src")))
|
|
|
|
return true;
|
2011-10-04 00:28:29 +04:00
|
|
|
if (!box_extract_link((char *) src, content->base_url, ¶ms->data))
|
2005-04-14 01:58:28 +04:00
|
|
|
return false;
|
|
|
|
xmlFree(src);
|
|
|
|
if (!params->data)
|
|
|
|
return true;
|
2005-03-26 04:12:27 +03:00
|
|
|
|
2006-03-27 05:04:56 +04:00
|
|
|
/* Don't include ourself */
|
2011-10-04 00:28:29 +04:00
|
|
|
if (nsurl_compare(content->base_url, params->data, NSURL_COMPLETE))
|
2006-03-27 05:04:56 +04:00
|
|
|
return true;
|
|
|
|
|
2005-04-14 01:58:28 +04:00
|
|
|
/* add attributes as parameters to linked list */
|
2005-04-09 13:47:37 +04:00
|
|
|
for (a = n->properties; a; a = a->next) {
|
2005-04-14 01:58:28 +04:00
|
|
|
if (strcasecmp((const char *) a->name, "src") == 0)
|
|
|
|
continue;
|
|
|
|
if (!a->children || !a->children->content)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
param = talloc(content, struct object_param);
|
|
|
|
if (!param)
|
|
|
|
return false;
|
|
|
|
param->name = talloc_strdup(content, (const char *) a->name);
|
|
|
|
param->value = talloc_strdup(content,
|
|
|
|
(char *) a->children->content);
|
|
|
|
param->type = 0;
|
2006-09-11 15:55:17 +04:00
|
|
|
param->valuetype = talloc_strdup(content, "data");
|
2005-04-14 01:58:28 +04:00
|
|
|
param->next = 0;
|
|
|
|
|
2006-09-11 15:55:17 +04:00
|
|
|
if (!param->name || !param->value || !param->valuetype)
|
2005-04-09 13:47:37 +04:00
|
|
|
return false;
|
|
|
|
|
2005-04-14 01:58:28 +04:00
|
|
|
param->next = params->params;
|
|
|
|
params->params = param;
|
|
|
|
}
|
2005-04-09 13:47:37 +04:00
|
|
|
|
2005-04-14 01:58:28 +04:00
|
|
|
box->object_params = params;
|
2005-03-26 04:12:27 +03:00
|
|
|
|
|
|
|
/* start fetch */
|
2011-10-04 14:23:47 +04:00
|
|
|
return html_fetch_object(content, params->data, box, CONTENT_ANY,
|
|
|
|
content->base.available_width, 1000, false);
|
2005-04-09 13:47:37 +04:00
|
|
|
}
|
2005-03-26 04:12:27 +03:00
|
|
|
|
2005-04-09 13:47:37 +04:00
|
|
|
/**
|
|
|
|
* \}
|
|
|
|
*/
|
2005-03-26 04:12:27 +03:00
|
|
|
|
|
|
|
|
2005-04-10 21:08:49 +04:00
|
|
|
/**
|
|
|
|
* Get the value of an XML element's attribute.
|
|
|
|
*
|
2006-09-02 19:52:41 +04:00
|
|
|
* \param n xmlNode, of type XML_ELEMENT_NODE
|
2005-04-10 21:08:49 +04:00
|
|
|
* \param attribute name of attribute
|
|
|
|
* \param context talloc context for result buffer
|
|
|
|
* \param value updated to value, if the attribute is present
|
|
|
|
* \return true on success, false if attribute present but memory exhausted
|
|
|
|
*
|
|
|
|
* Note that returning true does not imply that the attribute was found. If the
|
|
|
|
* attribute was not found, *value will be unchanged.
|
|
|
|
*/
|
|
|
|
|
|
|
|
bool box_get_attribute(xmlNode *n, const char *attribute,
|
|
|
|
void *context, char **value)
|
|
|
|
{
|
|
|
|
xmlChar *s = xmlGetProp(n, (const xmlChar *) attribute);
|
|
|
|
if (!s)
|
|
|
|
return true;
|
2008-02-25 20:58:00 +03:00
|
|
|
*value = talloc_strdup(context, (const char *) s);
|
2005-04-10 21:08:49 +04:00
|
|
|
xmlFree(s);
|
|
|
|
if (!*value)
|
|
|
|
return false;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Extract a URL from a relative link, handling junk like whitespace and
|
|
|
|
* attempting to read a real URL from "javascript:" links.
|
|
|
|
*
|
2006-09-02 19:52:41 +04:00
|
|
|
* \param rel relative URL taken from page
|
|
|
|
* \param base base for relative URLs
|
2005-04-10 21:08:49 +04:00
|
|
|
* \param result updated to target URL on heap, unchanged if extract failed
|
|
|
|
* \return true on success, false on memory exhaustion
|
|
|
|
*/
|
|
|
|
|
2011-10-04 00:28:29 +04:00
|
|
|
bool box_extract_link(const char *rel, nsurl *base, nsurl **result)
|
2005-04-10 21:08:49 +04:00
|
|
|
{
|
|
|
|
char *s, *s1, *apos0 = 0, *apos1 = 0, *quot0 = 0, *quot1 = 0;
|
|
|
|
unsigned int i, j, end;
|
2011-10-04 00:28:29 +04:00
|
|
|
nserror error;
|
2005-04-10 21:08:49 +04:00
|
|
|
|
|
|
|
s1 = s = malloc(3 * strlen(rel) + 1);
|
|
|
|
if (!s)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
/* copy to s, removing white space and control characters */
|
|
|
|
for (i = 0; rel[i] && isspace(rel[i]); i++)
|
|
|
|
;
|
|
|
|
for (end = strlen(rel); end != i && isspace(rel[end - 1]); end--)
|
|
|
|
;
|
|
|
|
for (j = 0; i != end; i++) {
|
|
|
|
if ((unsigned char) rel[i] < 0x20) {
|
|
|
|
; /* skip control characters */
|
|
|
|
} else if (rel[i] == ' ') {
|
|
|
|
s[j++] = '%';
|
|
|
|
s[j++] = '2';
|
|
|
|
s[j++] = '0';
|
|
|
|
} else {
|
|
|
|
s[j++] = rel[i];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
s[j] = 0;
|
|
|
|
|
|
|
|
/* extract first quoted string out of "javascript:" link */
|
|
|
|
if (strncmp(s, "javascript:", 11) == 0) {
|
|
|
|
apos0 = strchr(s, '\'');
|
|
|
|
if (apos0)
|
|
|
|
apos1 = strchr(apos0 + 1, '\'');
|
|
|
|
quot0 = strchr(s, '"');
|
|
|
|
if (quot0)
|
|
|
|
quot1 = strchr(quot0 + 1, '"');
|
|
|
|
if (apos0 && apos1 && (!quot0 || !quot1 || apos0 < quot0)) {
|
|
|
|
*apos1 = 0;
|
|
|
|
s1 = apos0 + 1;
|
|
|
|
} else if (quot0 && quot1) {
|
|
|
|
*quot1 = 0;
|
|
|
|
s1 = quot0 + 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* construct absolute URL */
|
2011-10-04 00:28:29 +04:00
|
|
|
error = nsurl_join(base, s1, result);
|
2005-04-10 21:08:49 +04:00
|
|
|
free(s);
|
2011-10-04 00:28:29 +04:00
|
|
|
if (error != NSERROR_OK) {
|
|
|
|
*result = NULL;
|
2005-04-10 21:08:49 +04:00
|
|
|
return false;
|
2011-10-04 00:28:29 +04:00
|
|
|
}
|
2005-04-10 21:08:49 +04:00
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2005-03-26 04:12:27 +03:00
|
|
|
/**
|
|
|
|
* Parse a multi-length-list, as defined by HTML 4.01.
|
|
|
|
*
|
2006-09-02 19:52:41 +04:00
|
|
|
* \param s string to parse
|
2005-04-09 13:47:37 +04:00
|
|
|
* \param count updated to number of entries
|
2005-03-26 04:12:27 +03:00
|
|
|
* \return array of struct box_multi_length, or 0 on memory exhaustion
|
|
|
|
*/
|
|
|
|
|
2006-09-02 19:52:41 +04:00
|
|
|
struct frame_dimension *box_parse_multi_lengths(const char *s,
|
|
|
|
unsigned int *count)
|
2005-03-26 04:12:27 +03:00
|
|
|
{
|
|
|
|
char *end;
|
|
|
|
unsigned int i, n;
|
2006-09-02 19:52:41 +04:00
|
|
|
struct frame_dimension *length;
|
2005-03-26 04:12:27 +03:00
|
|
|
|
|
|
|
for (i = 0, n = 1; s[i]; i++)
|
|
|
|
if (s[i] == ',')
|
|
|
|
n++;
|
|
|
|
|
2006-09-02 19:52:41 +04:00
|
|
|
length = calloc(n, sizeof(struct frame_dimension));
|
2005-04-09 13:47:37 +04:00
|
|
|
if (!length)
|
2005-03-26 04:12:27 +03:00
|
|
|
return NULL;
|
|
|
|
|
|
|
|
for (i = 0; i != n; i++) {
|
|
|
|
while (isspace(*s))
|
|
|
|
s++;
|
|
|
|
length[i].value = strtof(s, &end);
|
|
|
|
if (length[i].value <= 0)
|
|
|
|
length[i].value = 1;
|
|
|
|
s = end;
|
|
|
|
switch (*s) {
|
2006-09-02 19:52:41 +04:00
|
|
|
case '%':
|
|
|
|
length[i].unit = FRAME_DIMENSION_PERCENT;
|
|
|
|
break;
|
|
|
|
case '*':
|
|
|
|
length[i].unit = FRAME_DIMENSION_RELATIVE;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
length[i].unit = FRAME_DIMENSION_PIXELS;
|
|
|
|
break;
|
2005-03-26 04:12:27 +03:00
|
|
|
}
|
|
|
|
while (*s && *s != ',')
|
|
|
|
s++;
|
|
|
|
if (*s == ',')
|
|
|
|
s++;
|
|
|
|
}
|
|
|
|
|
|
|
|
*count = n;
|
|
|
|
return length;
|
|
|
|
}
|
2007-10-09 20:25:25 +04:00
|
|
|
|