summaryrefslogtreecommitdiff
path: root/render
diff options
context:
space:
mode:
Diffstat (limited to 'render')
-rw-r--r--render/box_construct.c197
-rw-r--r--render/form.c161
-rw-r--r--render/form.h58
-rw-r--r--render/html.c39
-rw-r--r--render/hubbub_binding.c338
-rw-r--r--render/parser_binding.h8
6 files changed, 647 insertions, 154 deletions
diff --git a/render/box_construct.c b/render/box_construct.c
index 94ca2ef57..4f9bdfd32 100644
--- a/render/box_construct.c
+++ b/render/box_construct.c
@@ -140,7 +140,9 @@ 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);
+#ifndef WITH_HUBBUB
static bool box_form(BOX_SPECIAL_PARAMS);
+#endif
static bool box_textarea(BOX_SPECIAL_PARAMS);
static bool box_select(BOX_SPECIAL_PARAMS);
static bool box_input(BOX_SPECIAL_PARAMS);
@@ -174,7 +176,9 @@ static const struct element_entry element_table[] = {
{"br", box_br},
{"button", box_button},
{"embed", box_embed},
+#ifndef WITH_HUBBUB
{"form", box_form},
+#endif
{"frameset", box_frameset},
{"iframe", box_iframe},
{"image", box_image},
@@ -2158,13 +2162,14 @@ bool box_iframe(BOX_SPECIAL_PARAMS)
}
+#ifndef WITH_HUBBUB
/**
* Interactive form [17.3].
*/
bool box_form(BOX_SPECIAL_PARAMS)
{
- char *xmlaction, *action, *faction, *method, *enctype, *charset, *target;
+ char *xmlaction, *action, *method, *enctype, *charset, *target;
form_method fmethod;
struct form *form;
url_func_result result;
@@ -2173,26 +2178,22 @@ bool box_form(BOX_SPECIAL_PARAMS)
xmlGetProp(n, (const xmlChar *) "action"))) {
/* the action attribute is required, but many forms fail to
* specify it. In the case where it is _not_ specified,
- * follow other browsers and make the form action the
- * URI of the page the form is contained in. */
- action = strdup("");
+ * follow other browsers and make the form action the URI of
+ * the page the form is contained in. */
+ action = strdup(content->data.html.base_url);
} else {
- action = strdup(xmlaction);
+ result = url_join(xmlaction, content->data.html.base_url,
+ &action);
+
xmlFree(xmlaction);
+
+ if (result != URL_FUNC_OK)
+ return false;
}
if (!action)
return false;
- result = url_join(action, content->data.html.base_url, &faction);
- if (result != URL_FUNC_OK) {
- free(action);
- return false;
- }
-
- /* No longer needed */
- free(action);
-
fmethod = method_GET;
if ((method = (char *) xmlGetProp(n, (const xmlChar *) "method"))) {
if (strcasecmp(method, "post") == 0) {
@@ -2214,20 +2215,24 @@ bool box_form(BOX_SPECIAL_PARAMS)
/* target for form data */
target = (char *) xmlGetProp(n, (const xmlChar *) "target");
- form = form_new(faction, target, fmethod, charset,
- content->data.html.encoding);
- if (!form) {
- free(faction);
- xmlFree(target);
+ form = form_new(n, action, target, fmethod, charset,
+ content->data.html.encoding);
+
+ free(action);
+ if (charset)
xmlFree(charset);
+ if (target)
+ xmlFree(target);
+
+ if (!form)
return false;
- }
+
form->prev = content->data.html.forms;
content->data.html.forms = form;
return true;
}
-
+#endif
/**
* Form control [17.4].
@@ -2241,25 +2246,36 @@ bool box_input(BOX_SPECIAL_PARAMS)
type = (char *) xmlGetProp(n, (const xmlChar *) "type");
+#ifdef WITH_HUBBUB
+ gadget = binding_get_control_for_node(content->data.html.parser_binding,
+ n);
+ if (!gadget)
+ goto no_memory;
+ box->gadget = gadget;
+ gadget->box = box;
+#endif
+
if (type && strcasecmp(type, "password") == 0) {
if (!box_input_text(n, content, box, 0, markup_track, author,
true))
goto no_memory;
+#ifndef WITH_HUBBUB
gadget = box->gadget;
gadget->box = box;
-
+#endif
} else if (type && strcasecmp(type, "file") == 0) {
box->type = BOX_INLINE_BLOCK;
- box->gadget = gadget = form_new_control(GADGET_FILE);
+#ifndef WITH_HUBBUB
+ box->gadget = gadget = form_new_control(n, GADGET_FILE);
if (!gadget)
goto no_memory;
gadget->box = box;
-
+#endif
} else if (type && strcasecmp(type, "hidden") == 0) {
/* no box for hidden inputs */
box->style->display = CSS_DISPLAY_NONE;
-
- gadget = form_new_control(GADGET_HIDDEN);
+#ifndef WITH_HUBBUB
+ gadget = form_new_control(n, GADGET_HIDDEN);
if (!gadget)
goto no_memory;
@@ -2270,11 +2286,12 @@ bool box_input(BOX_SPECIAL_PARAMS)
goto no_memory;
gadget->length = strlen(gadget->value);
}
-
+#endif
} else if (type && (strcasecmp(type, "checkbox") == 0 ||
strcasecmp(type, "radio") == 0)) {
- box->gadget = gadget = form_new_control(type[0] == 'c' ||
- type[0] == 'C' ? GADGET_CHECKBOX :
+#ifndef WITH_HUBBUB
+ box->gadget = gadget = form_new_control(n, type[0] == 'c' ||
+ type[0] == 'C' ? GADGET_CHECKBOX :
GADGET_RADIO);
if (!gadget)
goto no_memory;
@@ -2289,9 +2306,10 @@ bool box_input(BOX_SPECIAL_PARAMS)
goto no_memory;
gadget->length = strlen(gadget->value);
}
-
+#endif
} else if (type && (strcasecmp(type, "submit") == 0 ||
- strcasecmp(type, "reset") == 0)) {
+ strcasecmp(type, "reset") == 0 ||
+ strcasecmp(type, "button") == 0)) {
struct box *inline_container, *inline_box;
if (!box_button(n, content, box, 0, markup_track, author))
goto no_memory;
@@ -2310,30 +2328,9 @@ bool box_input(BOX_SPECIAL_PARAMS)
else if (box->gadget->type == GADGET_SUBMIT)
inline_box->text = talloc_strdup(content,
messages_get("Form_Submit"));
- else
+ else if (box->gadget->type == GADGET_RESET)
inline_box->text = talloc_strdup(content,
messages_get("Form_Reset"));
- if (!inline_box->text)
- goto no_memory;
- inline_box->length = strlen(inline_box->text);
- box_add_child(inline_container, inline_box);
- box_add_child(box, inline_container);
-
- } else if (type && strcasecmp(type, "button") == 0) {
- struct box *inline_container, *inline_box;
- if (!box_button(n, content, box, 0, markup_track, author))
- goto no_memory;
- inline_container = box_create(0, 0, 0, 0, 0, content);
- if (!inline_container)
- goto no_memory;
- inline_container->type = BOX_INLINE_CONTAINER;
- inline_box = box_create(box->style, 0, 0, box->title, 0,
- content);
- if (!inline_box)
- goto no_memory;
- inline_box->type = BOX_TEXT;
- if ((s = (char *) xmlGetProp(n, (const xmlChar *) "value")))
- inline_box->text = talloc_strdup(content, s);
else
inline_box->text = talloc_strdup(content, "Button");
if (!inline_box->text)
@@ -2341,12 +2338,13 @@ bool box_input(BOX_SPECIAL_PARAMS)
inline_box->length = strlen(inline_box->text);
box_add_child(inline_container, inline_box);
box_add_child(box, inline_container);
-
} else if (type && strcasecmp(type, "image") == 0) {
- box->gadget = gadget = form_new_control(GADGET_IMAGE);
+#ifndef WITH_HUBBUB
+ box->gadget = gadget = form_new_control(n, GADGET_IMAGE);
if (!gadget)
goto no_memory;
gadget->box = box;
+#endif
gadget->type = GADGET_IMAGE;
if (box->style && box->style->display != CSS_DISPLAY_NONE) {
@@ -2375,19 +2373,21 @@ bool box_input(BOX_SPECIAL_PARAMS)
free(url);
}
}
-
} else {
/* the default type is "text" */
if (!box_input_text(n, content, box, 0, markup_track, author,
false))
goto no_memory;
+#ifndef WITH_HUBBUB
gadget = box->gadget;
gadget->box = box;
+#endif
}
if (type)
xmlFree(type);
+#ifndef WITH_HUBBUB
if (gadget) {
if (content->data.html.forms)
form_add_control(content->data.html.forms, gadget);
@@ -2399,6 +2399,7 @@ bool box_input(BOX_SPECIAL_PARAMS)
goto no_memory;
}
}
+#endif
*convert_children = false;
return true;
@@ -2406,9 +2407,10 @@ bool box_input(BOX_SPECIAL_PARAMS)
no_memory:
if (type)
xmlFree(type);
+#ifndef WITH_HUBBUB
if (gadget)
form_free_control(gadget);
-
+#endif
return false;
}
@@ -2419,14 +2421,17 @@ no_memory:
bool box_input_text(BOX_SPECIAL_PARAMS, bool password)
{
+#ifndef WITH_HUBBUB
char *s;
+#endif
struct box *inline_container, *inline_box;
box->type = BOX_INLINE_BLOCK;
- box->gadget = form_new_control((password) ? GADGET_PASSWORD :
+#ifndef WITH_HUBBUB
+ box->gadget = form_new_control(n, (password) ? GADGET_PASSWORD :
GADGET_TEXTBOX);
if (!box->gadget)
- return 0;
+ return false;
box->gadget->box = box;
if ((s = (char *) xmlGetProp(n, (const xmlChar *) "maxlength"))) {
@@ -2446,6 +2451,7 @@ bool box_input_text(BOX_SPECIAL_PARAMS, bool password)
return NULL;
}
box->gadget->length = strlen(box->gadget->value);
+#endif
inline_container = box_create(0, 0, 0, 0, 0, content);
if (!inline_container)
@@ -2488,19 +2494,16 @@ bool box_input_text(BOX_SPECIAL_PARAMS, bool password)
bool box_button(BOX_SPECIAL_PARAMS)
{
+#ifndef WITH_HUBBUB
xmlChar *s;
char *type = (char *) xmlGetProp(n, (const xmlChar *) "type");
- box->type = BOX_INLINE_BLOCK;
-
if (!type || strcasecmp(type, "submit") == 0) {
- box->gadget = form_new_control(GADGET_SUBMIT);
+ box->gadget = form_new_control(n, GADGET_SUBMIT);
} else if (strcasecmp(type, "reset") == 0) {
- box->gadget = form_new_control(GADGET_RESET);
+ box->gadget = form_new_control(n, GADGET_RESET);
} else {
- /* type="button" or unknown: just render the contents */
- xmlFree(type);
- return true;
+ box->gadget = form_new_control(n, GADGET_BUTTON);
}
if (type)
@@ -2512,6 +2515,7 @@ bool box_button(BOX_SPECIAL_PARAMS)
if (content->data.html.forms)
form_add_control(content->data.html.forms, box->gadget);
box->gadget->box = box;
+
if ((s = xmlGetProp(n, (const xmlChar *) "name")) != NULL) {
box->gadget->name = strdup((char *) s);
xmlFree(s);
@@ -2524,6 +2528,20 @@ bool box_button(BOX_SPECIAL_PARAMS)
if (!box->gadget->value)
return false;
}
+#else
+ struct form_control *gadget;
+
+ gadget = binding_get_control_for_node(content->data.html.parser_binding,
+ n);
+ if (!gadget)
+ return false;
+
+ box->gadget = gadget;
+ gadget->box = box;
+#endif
+ box->type = BOX_INLINE_BLOCK;
+
+ /* Just render the contents */
return true;
}
@@ -2538,23 +2556,24 @@ bool box_select(BOX_SPECIAL_PARAMS)
struct box *inline_container;
struct box *inline_box;
struct form_control *gadget;
- char* s;
+#ifndef WITH_HUBBUB
+ char *s;
+#endif
xmlNode *c, *c2;
- gadget = form_new_control(GADGET_SELECT);
+#ifndef WITH_HUBBUB
+ gadget = form_new_control(n, GADGET_SELECT);
+#else
+ gadget = binding_get_control_for_node(content->data.html.parser_binding,
+ n);
+#endif
if (!gadget)
return false;
- gadget->data.select.multiple = false;
- if ((s = (char *) xmlGetProp(n, (const xmlChar *) "multiple"))) {
- gadget->data.select.multiple = true;
- xmlFree(s);
- }
-
- gadget->data.select.items = NULL;
- gadget->data.select.last_item = NULL;
- gadget->data.select.num_items = 0;
- gadget->data.select.num_selected = 0;
+#ifndef WITH_HUBBUB
+ gadget->data.select.multiple =
+ xmlHasProp(n, (const xmlChar *) "multiple");
+#endif
for (c = n->children; c; c = c->next) {
if (strcmp((const char *) c->name, "option") == 0) {
@@ -2573,16 +2592,20 @@ bool box_select(BOX_SPECIAL_PARAMS)
if (gadget->data.select.num_items == 0) {
/* no options: ignore entire select */
+#ifndef WITH_HUBBUB
form_free_control(gadget);
+#endif
return true;
}
+#ifndef WITH_HUBBUB
if ((s = (char *) xmlGetProp(n, (const xmlChar *) "name"))) {
gadget->name = strdup(s);
xmlFree(s);
if (!gadget->name)
goto no_memory;
}
+#endif
box->type = BOX_INLINE_BLOCK;
box->gadget = gadget;
@@ -2621,14 +2644,18 @@ bool box_select(BOX_SPECIAL_PARAMS)
inline_box->length = strlen(inline_box->text);
+#ifndef WITH_HUBBUB
if (content->data.html.forms)
form_add_control(content->data.html.forms, box->gadget);
+#endif
*convert_children = false;
return true;
no_memory:
+#ifndef WITH_HUBBUB
form_free_control(gadget);
+#endif
return false;
}
@@ -2709,17 +2736,24 @@ bool box_textarea(BOX_SPECIAL_PARAMS)
size_t len;
box->type = BOX_INLINE_BLOCK;
- box->gadget = form_new_control(GADGET_TEXTAREA);
+#ifndef WITH_HUBBUB
+ box->gadget = form_new_control(n, GADGET_TEXTAREA);
+#else
+ box->gadget = binding_get_control_for_node(
+ content->data.html.parser_binding, n);
+#endif
if (!box->gadget)
return false;
box->gadget->box = box;
+#ifndef WITH_HUBBUB
if ((s = (char *) xmlGetProp(n, (const xmlChar *) "name"))) {
box->gadget->name = strdup(s);
xmlFree(s);
if (!box->gadget->name)
return false;
}
+#endif
inline_container = box_create(0, 0, 0, box->title, 0, content);
if (!inline_container)
@@ -2727,6 +2761,9 @@ bool box_textarea(BOX_SPECIAL_PARAMS)
inline_container->type = BOX_INLINE_CONTAINER;
box_add_child(box, inline_container);
+ /** \todo Is it really necessary to reparse the content of a
+ * textarea element to remove entities? Hubbub will do that for us.
+ */
n2 = n->children;
buf = xmlBufferCreate();
while(n2) {
@@ -2806,8 +2843,10 @@ bool box_textarea(BOX_SPECIAL_PARAMS)
xmlFree(string);
xmlBufferFree(buf);
+#ifndef WITH_HUBBUB
if (content->data.html.forms)
form_add_control(content->data.html.forms, box->gadget);
+#endif
*convert_children = false;
return true;
diff --git a/render/form.c b/render/form.c
index e2dac6d1b..096d5d3dd 100644
--- a/render/form.c
+++ b/render/form.c
@@ -1,7 +1,7 @@
/*
* Copyright 2004 James Bursa <bursa@users.sourceforge.net>
* Copyright 2003 Phil Mellor <monkeyson@users.sourceforge.net>
- * Copyright 2005-7 John M Bell <jmb202@ecs.soton.ac.uk>
+ * Copyright 2005-9 John-Mark Bell <jmb@netsurf-browser.org>
*
* This file is part of NetSurf, http://www.netsurf-browser.org/
*
@@ -45,61 +45,113 @@ static char *form_encode_item(const char *item, const char *charset,
/**
* Create a struct form.
*
- * \param action URL to submit form to, used directly (not copied)
+ * \param node DOM node associated with form
+ * \param action URL to submit form to, or NULL for default
+ * \param target Target frame of form, or NULL for default
* \param method method and enctype
- * \param charset acceptable charactersets for form submission (not copied)
- * \param doc_charset characterset of containing document (not copied)
- * \return a new structure, or 0 on memory exhaustion
+ * \param charset acceptable encodings for form submission, or NULL
+ * \param doc_charset encoding of containing document
+ * \return a new structure, or NULL on memory exhaustion
*/
-
-struct form *form_new(char *action, char *target, form_method method,
- char *charset, char *doc_charset)
+struct form *form_new(void *node, const char *action, const char *target,
+ form_method method, const char *charset,
+ const char *doc_charset)
{
struct form *form;
- form = malloc(sizeof *form);
+ assert(doc_charset != NULL);
+
+ form = calloc(1, sizeof *form);
if (!form)
- return 0;
- form->action = action;
- form->target = target;
+ return NULL;
+
+ form->action = strdup(action != NULL ? action : "");
+ if (form->action == NULL) {
+ free(form);
+ return NULL;
+ }
+
+ form->target = target != NULL ? strdup(target) : NULL;
+ if (target != NULL && form->target == NULL) {
+ free(form->action);
+ free(form);
+ return NULL;
+ }
+
form->method = method;
- form->accept_charsets = charset;
- form->document_charset = doc_charset;
- form->controls = 0;
- form->last_control = 0;
- form->prev = 0;
+
+ form->accept_charsets = charset != NULL ? strdup(charset) : NULL;
+ if (charset != NULL && form->accept_charsets == NULL) {
+ free(form->target);
+ free(form->action);
+ free(form);
+ return NULL;
+ }
+
+ form->document_charset = strdup(doc_charset);
+ if (form->document_charset == NULL) {
+ free(form->accept_charsets);
+ free(form->target);
+ free(form->action);
+ free(form);
+ return NULL;
+ }
+
+ form->node = node;
+
return form;
}
+/**
+ * Free a form, and any controls it owns.
+ *
+ * \param form The form to free
+ *
+ * \note There may exist controls attached to box tree nodes which are not
+ * associated with any form. These will leak at present. Ideally, they will
+ * be cleaned up when the box tree is destroyed. As that currently happens
+ * via talloc, this won't happen. These controls are distinguishable, as their
+ * form field will be NULL.
+ */
+void form_free(struct form *form)
+{
+ struct form_control *c, *d;
+
+ for (c = form->controls; c != NULL; c = d) {
+ d = c->next;
+
+ form_free_control(c);
+ }
+
+ free(form->action);
+ free(form->target);
+ free(form->accept_charsets);
+ free(form->document_charset);
+
+ free(form);
+}
/**
* Create a struct form_control.
*
+ * \param node Associated DOM node
* \param type control type
- * \return a new structure, or 0 on memory exhaustion
+ * \return a new structure, or NULL on memory exhaustion
*/
-
-struct form_control *form_new_control(form_control_type type)
+struct form_control *form_new_control(void *node, form_control_type type)
{
struct form_control *control;
- if ((control = malloc(sizeof *control)) == NULL)
+ control = calloc(1, sizeof *control);
+ if (control == NULL)
return NULL;
+
+ control->node = node;
control->type = type;
- control->name = NULL;
- control->value = NULL;
- control->initial_value = NULL;
- control->disabled = false;
- control->form = NULL;
- control->box = NULL;
- control->caret_inline_container = NULL;
- control->caret_text_box = NULL;
- control->caret_box_offset = control->caret_form_offset = 0;
- control->length = 0;
+
+ /* Default max length of input to something insane */
control->maxlength = UINT_MAX;
- control->selected = false;
- control->prev = NULL;
- control->next = NULL;
+
return control;
}
@@ -110,12 +162,13 @@ struct form_control *form_new_control(form_control_type type)
* \param form The form to add the control to
* \param control The control to add
*/
-
void form_add_control(struct form *form, struct form_control *control)
{
control->form = form;
+
if (form->controls != NULL) {
assert(form->last_control);
+
form->last_control->next = control;
control->prev = form->last_control;
control->next = NULL;
@@ -131,14 +184,15 @@ void form_add_control(struct form *form, struct form_control *control)
*
* \param control structure to free
*/
-
void form_free_control(struct form_control *control)
{
free(control->name);
free(control->value);
free(control->initial_value);
+
if (control->type == GADGET_SELECT) {
struct form_option *option, *next;
+
for (option = control->data.select.items; option;
option = next) {
next = option->next;
@@ -147,6 +201,7 @@ void form_free_control(struct form_control *control)
free(option);
}
}
+
free(control);
}
@@ -160,7 +215,6 @@ void form_free_control(struct form_control *control)
* \param selected this option is selected
* \return true on success, false on memory exhaustion
*/
-
bool form_add_option(struct form_control *control, char *value, char *text,
bool selected)
{
@@ -169,13 +223,12 @@ bool form_add_option(struct form_control *control, char *value, char *text,
assert(control);
assert(control->type == GADGET_SELECT);
- option = malloc(sizeof *option);
+ option = calloc(1, sizeof *option);
if (!option)
return false;
- option->selected = option->initial_selected = false;
+
option->value = value;
option->text = text;
- option->next = 0;
/* add to linked list */
if (control->data.select.items == 0)
@@ -216,7 +269,6 @@ bool form_add_option(struct form_control *control, char *value, char *text,
*
* See HTML 4.01 section 17.13.2.
*/
-
bool form_successful_controls(struct form *form,
struct form_control *submit_button,
struct form_successful_control **successful_controls)
@@ -344,17 +396,23 @@ bool form_successful_controls(struct form *form,
case GADGET_IMAGE: {
/* image */
size_t len;
+ char *name;
if (control != submit_button)
/* only the activated submit button
* is successful */
continue;
- len = strlen(control->name) + 3;
+ name = ENCODE_ITEM(control->name);
+ if (name == NULL)
+ goto no_memory;
+
+ len = strlen(name) + 3;
/* x */
success_new = malloc(sizeof(*success_new));
if (!success_new) {
+ free(name);
LOG(("malloc failed"));
goto no_memory;
}
@@ -366,11 +424,11 @@ bool form_successful_controls(struct form *form,
free(success_new->name);
free(success_new->value);
free(success_new);
+ free(name);
LOG(("malloc failed"));
goto no_memory;
}
- sprintf(success_new->name, "%s.x",
- control->name);
+ sprintf(success_new->name, "%s.x", name);
sprintf(success_new->value, "%i",
control->data.image.mx);
success_new->next = 0;
@@ -380,6 +438,7 @@ bool form_successful_controls(struct form *form,
/* y */
success_new = malloc(sizeof(*success_new));
if (!success_new) {
+ free(name);
LOG(("malloc failed"));
goto no_memory;
}
@@ -391,17 +450,19 @@ bool form_successful_controls(struct form *form,
free(success_new->name);
free(success_new->value);
free(success_new);
+ free(name);
LOG(("malloc failed"));
goto no_memory;
}
- sprintf(success_new->name, "%s.y",
- control->name);
+ sprintf(success_new->name, "%s.y", name);
sprintf(success_new->value, "%i",
control->data.image.my);
success_new->next = 0;
last_success->next = success_new;
last_success = success_new;
+ free(name);
+
continue;
break;
}
@@ -450,7 +511,8 @@ bool form_successful_controls(struct form *form,
}
success_new->file = true;
success_new->name = ENCODE_ITEM(control->name);
- success_new->value = ENCODE_ITEM(control->value ?
+ success_new->value =
+ ENCODE_ITEM(control->value ?
control->value : "");
success_new->next = 0;
last_success->next = success_new;
@@ -464,6 +526,10 @@ bool form_successful_controls(struct form *form,
continue;
break;
+ case GADGET_BUTTON:
+ /* Ignore it */
+ break;
+
default:
assert(0);
break;
@@ -505,7 +571,6 @@ no_memory:
* \param textarea control of type GADGET_TEXTAREA
* \return the value as a UTF-8 string on heap, or 0 on memory exhaustion
*/
-
char *form_textarea_value(struct form_control *textarea)
{
unsigned int len = 0;
diff --git a/render/form.h b/render/form.h
index c69bd467f..eaecf3411 100644
--- a/render/form.h
+++ b/render/form.h
@@ -40,14 +40,17 @@ typedef enum {
/** HTML form. */
struct form {
- char *action; /**< Absolute URL to submit to. */
- char *target; /**< Target to submit to. */
- form_method method; /**< Method and enctype. */
- char *accept_charsets; /**< Charset to submit form in */
- char *document_charset; /**< Charset of document containing form */
- struct form_control *controls; /**< Linked list of controls. */
+ void *node; /**< Corresponding DOM node */
+
+ char *action; /**< Absolute URL to submit to. */
+ char *target; /**< Target to submit to. */
+ form_method method; /**< Method and enctype. */
+ char *accept_charsets; /**< Charset to submit form in */
+ char *document_charset; /**< Charset of document containing form */
+ struct form_control *controls; /**< Linked list of controls. */
struct form_control *last_control; /**< Last control in list. */
- struct form *prev; /**< Previous form in doc. */
+
+ struct form *prev; /**< Previous form in doc. */
};
/** Type of a struct form_control. */
@@ -62,25 +65,35 @@ typedef enum {
GADGET_PASSWORD,
GADGET_SUBMIT,
GADGET_RESET,
- GADGET_FILE
+ GADGET_FILE,
+ GADGET_BUTTON
} form_control_type;
/** Form control. */
struct form_control {
- form_control_type type;
- char *name;
- char *value;
- char *initial_value;
- bool disabled;
- struct form *form;
- struct box *box;
+ void *node; /**< Corresponding DOM node */
+
+ form_control_type type; /**< Type of control */
+
+ struct form *form; /**< Containing form */
+
+ char *name; /**< Control name */
+ char *value; /**< Current value of control */
+ char *initial_value; /**< Initial value of control */
+ bool disabled; /**< Whether control is disabled */
+
+ struct box *box; /**< Box for control */
+ /** Caret details. */
struct box *caret_inline_container;
struct box *caret_text_box;
size_t caret_box_offset, caret_form_offset;
- unsigned int length;
int caret_pixel_offset;
- unsigned int maxlength;
- bool selected;
+
+ unsigned int length; /**< Number of characters in control */
+ unsigned int maxlength; /**< Maximum characters permitted */
+
+ bool selected; /**< Whether control is selected */
+
union {
struct {
int mx, my;
@@ -94,6 +107,7 @@ struct form_control {
struct form_option *current;
} select;
} data;
+
struct form_control *prev; /**< Previous control in this form */
struct form_control *next; /**< Next control in this form. */
};
@@ -115,9 +129,11 @@ struct form_successful_control {
struct form_successful_control *next; /**< Next in linked list. */
};
-struct form *form_new(char *action, char *target, form_method method, char *charset,
- char *doc_charset);
-struct form_control *form_new_control(form_control_type type);
+struct form *form_new(void *node, const char *action, const char *target,
+ form_method method, const char *charset,
+ const char *doc_charset);
+void form_free(struct form *form);
+struct form_control *form_new_control(void *node, form_control_type type);
void form_add_control(struct form *form, struct form_control *control);
void form_free_control(struct form_control *control);
bool form_add_option(struct form_control *control, char *value, char *text,
diff --git a/render/html.c b/render/html.c
index 4063f67ad..91f55b145 100644
--- a/render/html.c
+++ b/render/html.c
@@ -38,6 +38,7 @@
#include "image/bitmap.h"
#include "render/box.h"
#include "render/font.h"
+#include "render/form.h"
#include "render/html.h"
#include "render/imagemap.h"
#include "render/layout.h"
@@ -281,6 +282,9 @@ bool html_convert(struct content *c, int width, int height)
xmlNode *html, *head;
union content_msg_data msg_data;
unsigned int time_before, time_taken;
+#ifdef WITH_HUBBUB
+ struct form *f;
+#endif
/* finish parsing */
if (c->source_size == 0) {
@@ -321,8 +325,6 @@ bool html_convert(struct content *c, int width, int height)
c->data.html.document =
binding_get_document(c->data.html.parser_binding);
/*xmlDebugDumpDocument(stderr, c->data.html.document);*/
- binding_destroy_tree(c->data.html.parser_binding);
- c->data.html.parser_binding = NULL;
if (!c->data.html.document) {
LOG(("Parsing failed"));
@@ -364,6 +366,26 @@ bool html_convert(struct content *c, int width, int height)
if (!html_find_stylesheets(c, html))
return false;
+#ifdef WITH_HUBBUB
+ /* Retrieve forms from parser */
+ c->data.html.forms = binding_get_forms(c->data.html.parser_binding);
+ /* Make all actions absolute */
+ for (f = c->data.html.forms; f != NULL; f = f->prev) {
+ char *action;
+ url_func_result res;
+
+ res = url_join(f->action, c->data.html.base_url, &action);
+ if (res != URL_FUNC_OK) {
+ msg_data.error = messages_get("NoMemory");
+ content_broadcast(c, CONTENT_MSG_ERROR, msg_data);
+ return false;
+ }
+
+ free(f->action);
+ f->action = action;
+ }
+#endif
+
/* convert xml tree to box tree */
LOG(("XML to box"));
content_set_status(c, messages_get("Processing"));
@@ -405,6 +427,10 @@ bool html_convert(struct content *c, int width, int height)
c->reformat_time - wallclock()));
/*box_dump(c->data.html.layout->children, 0);*/
+ /* Destroy the parser binding */
+ binding_destroy_tree(c->data.html.parser_binding);
+ c->data.html.parser_binding = NULL;
+
if (c->active == 0)
c->status = CONTENT_STATUS_DONE;
else
@@ -1654,8 +1680,17 @@ void html_reformat(struct content *c, int width, int height)
void html_destroy(struct content *c)
{
unsigned int i;
+ struct form *f, *g;
+
LOG(("content %p", c));
+ /* Destroy forms */
+ for (f = c->data.html.forms; f != NULL; f = g) {
+ g = f->prev;
+
+ form_free(f);
+ }
+
imagemap_destroy(c);
if (c->bitmap) {
diff --git a/render/hubbub_binding.c b/render/hubbub_binding.c
index 1a368ccc2..79a3dba0f 100644
--- a/render/hubbub_binding.c
+++ b/render/hubbub_binding.c
@@ -30,6 +30,7 @@
#include <hubbub/parser.h>
#include <hubbub/tree.h>
+#include "render/form.h"
#include "render/parser_binding.h"
#include "utils/config.h"
@@ -50,6 +51,8 @@ typedef struct hubbub_ctx {
#undef NUM_NAMESPACES
hubbub_tree_handler tree_handler;
+
+ struct form *forms;
} hubbub_ctx;
static struct {
@@ -91,6 +94,12 @@ static int add_attributes(void *ctx, void *node,
static int set_quirks_mode(void *ctx, hubbub_quirks_mode mode);
static int change_encoding(void *ctx, const char *charset);
+static struct form *parse_form_element(xmlNode *node, const char *docenc);
+static struct form_control *parse_input_element(xmlNode *node);
+static struct form_control *parse_button_element(xmlNode *node);
+static struct form_control *parse_select_element(xmlNode *node);
+static struct form_control *parse_textarea_element(xmlNode *node);
+
static hubbub_tree_handler tree_handler = {
create_comment,
create_doctype,
@@ -133,6 +142,7 @@ binding_error binding_create_tree(void *arena, const char *charset, void **ctx)
c->encoding_source = ENCODING_SOURCE_HEADER;
c->document = NULL;
c->owns_doc = true;
+ c->forms = NULL;
error = hubbub_parser_create(charset, true, myrealloc, arena,
&c->parser);
@@ -152,8 +162,7 @@ binding_error binding_create_tree(void *arena, const char *charset, void **ctx)
}
c->document->_private = (void *) 0;
- for (i = 0;
- i < sizeof(c->namespaces) / sizeof(c->namespaces[0]); i++) {
+ for (i = 0; i < sizeof(c->namespaces) / sizeof(c->namespaces[0]); i++) {
c->namespaces[i] = NULL;
}
@@ -236,6 +245,42 @@ xmlDocPtr binding_get_document(void *ctx)
return doc;
}
+struct form *binding_get_forms(void *ctx)
+{
+ hubbub_ctx *c = (hubbub_ctx *) ctx;
+
+ return c->forms;
+}
+
+struct form_control *binding_get_control_for_node(void *ctx, xmlNodePtr node)
+{
+ hubbub_ctx *c = (hubbub_ctx *) ctx;
+ struct form *f;
+ struct form_control *ctl = NULL;
+
+ for (f = c->forms; f != NULL; f = f->prev) {
+ for (ctl = f->controls; ctl != NULL; ctl = ctl->next) {
+ if (ctl->node == node)
+ return ctl;
+ }
+ }
+
+ /* No control found. This implies that it's not associated
+ * with any form. In this case, we create a control for it
+ * on the fly. */
+ if (strcasecmp((const char *) node->name, "input") == 0) {
+ ctl = parse_input_element(node);
+ } else if (strcasecmp((const char *) node->name, "button") == 0) {
+ ctl = parse_button_element(node);
+ } else if (strcasecmp((const char *) node->name, "select") == 0) {
+ ctl = parse_select_element(node);
+ } else if (strcasecmp((const char *) node->name, "textarea") == 0) {
+ ctl = parse_textarea_element(node);
+ }
+
+ return ctl;
+}
+
/*****************************************************************************/
char *c_string_from_hubbub_string(hubbub_ctx *ctx, const hubbub_string *str)
@@ -246,8 +291,8 @@ char *c_string_from_hubbub_string(hubbub_ctx *ctx, const hubbub_string *str)
void create_namespaces(hubbub_ctx *ctx, xmlNode *root)
{
uint32_t i;
- for (i = 1;
- i < sizeof(namespaces) / sizeof(namespaces[0]); i++) {
+
+ for (i = 1; i < sizeof(namespaces) / sizeof(namespaces[0]); i++) {
ctx->namespaces[i - 1] = xmlNewNs(root,
BAD_CAST namespaces[i].url,
BAD_CAST namespaces[i].prefix);
@@ -367,6 +412,21 @@ int create_element(void *ctx, const hubbub_tag *tag, void **result)
return 1;
}
+ if (strcasecmp(name, "form") == 0) {
+ struct form *form = parse_form_element(n, c->encoding);
+
+ /* Memory exhaustion */
+ if (form == NULL) {
+ xmlFreeNode(n);
+ free(name);
+ return 1;
+ }
+
+ /* Insert into list */
+ form->prev = c->forms;
+ c->forms = form;
+ }
+
*result = (void *) n;
free(name);
@@ -567,6 +627,39 @@ int has_children(void *ctx, void *node, bool *result)
int form_associate(void *ctx, void *form, void *node)
{
+ hubbub_ctx *c = (hubbub_ctx *) ctx;
+ xmlNode *n = (xmlNode *) node;
+ struct form *f;
+ struct form_control *control = NULL;
+
+ /* Find form object to associate with */
+ for (f = c->forms; f != NULL; f = f->prev) {
+ if (f->node == form)
+ break;
+ }
+
+ /* None found -- give up */
+ if (f == NULL)
+ return 0;
+
+ if (strcasecmp((const char *) n->name, "input") == 0) {
+ control = parse_input_element(n);
+ } else if (strcasecmp((const char *) n->name, "button") == 0) {
+ control = parse_button_element(n);
+ } else if (strcasecmp((const char *) n->name, "select") == 0) {
+ control = parse_select_element(n);
+ } else if (strcasecmp((const char *) n->name, "textarea") == 0) {
+ control = parse_textarea_element(n);
+ } else
+ return 0;
+
+ /* Memory exhaustion */
+ if (control == NULL)
+ return 1;
+
+ /* Add the control to the form */
+ form_add_control(f, control);
+
return 0;
}
@@ -654,5 +747,242 @@ int change_encoding(void *ctx, const char *charset)
return (charset == name) ? 0 : 1;
}
+struct form *parse_form_element(xmlNode *node, const char *docenc)
+{
+ struct form *form;
+ form_method method;
+ xmlChar *action, *meth, *charset, *target;
+
+ action = xmlGetProp(node, (const xmlChar *) "action");
+ charset = xmlGetProp(node, (const xmlChar *) "accept-charset");
+ target = xmlGetProp(node, (const xmlChar *) "target");
+
+ method = method_GET;
+ meth = xmlGetProp(node, (const xmlChar *) "method");
+ if (meth != NULL) {
+ if (strcasecmp((char *) meth, "post") == 0) {
+ xmlChar *enctype;
+
+ method = method_POST_URLENC;
+
+ enctype = xmlGetProp(node, (const xmlChar *) "enctype");
+ if (enctype != NULL) {
+ if (strcasecmp((char *) enctype,
+ "multipart/form-data") == 0)
+ method = method_POST_MULTIPART;
+
+ xmlFree(enctype);
+ }
+ }
+ xmlFree(meth);
+ }
+
+ form = form_new(node, (char *) action, (char *) target, method,
+ (char *) charset, docenc);
+
+ if (target != NULL)
+ xmlFree(target);
+ if (charset != NULL)
+ xmlFree(charset);
+ if (action != NULL)
+ xmlFree(action);
+
+ return form;
+}
+
+struct form_control *parse_input_element(xmlNode *node)
+{
+ struct form_control *control = NULL;
+ xmlChar *type = xmlGetProp(node, (const xmlChar *) "type");
+ xmlChar *name;
+
+ if (type != NULL && strcasecmp((char *) type, "password") == 0) {
+ control = form_new_control(node, GADGET_PASSWORD);
+ } else if (type != NULL && strcasecmp((char *) type, "file") == 0) {
+ control = form_new_control(node, GADGET_FILE);
+ } else if (type != NULL && strcasecmp((char *) type, "hidden") == 0) {
+ control = form_new_control(node, GADGET_HIDDEN);
+ } else if (type != NULL && strcasecmp((char *) type, "checkbox") == 0) {
+ control = form_new_control(node, GADGET_CHECKBOX);
+ } else if (type != NULL && strcasecmp((char *) type, "radio") == 0) {
+ control = form_new_control(node, GADGET_RADIO);
+ } else if (type != NULL && strcasecmp((char *) type, "submit") == 0) {
+ control = form_new_control(node, GADGET_SUBMIT);
+ } else if (type != NULL && strcasecmp((char *) type, "reset") == 0) {
+ control = form_new_control(node, GADGET_RESET);
+ } else if (type != NULL && strcasecmp((char *) type, "button") == 0) {
+ control = form_new_control(node, GADGET_BUTTON);
+ } else if (type != NULL && strcasecmp((char *) type, "image") == 0) {
+ control = form_new_control(node, GADGET_IMAGE);
+ } else {
+ control = form_new_control(node, GADGET_TEXTBOX);
+ }
+
+ xmlFree(type);
+
+ if (control == NULL)
+ return NULL;
+
+ if (control->type == GADGET_CHECKBOX || control->type == GADGET_RADIO) {
+ control->selected =
+ xmlHasProp(node, (const xmlChar *) "checked");
+ }
+
+ if (control->type == GADGET_PASSWORD ||
+ control->type == GADGET_TEXTBOX) {
+ xmlChar *len = xmlGetProp(node, (const xmlChar *) "maxlength");
+ if (len != NULL) {
+ if (len[0] != '\0')
+ control->maxlength = atoi((char *) len);
+ xmlFree(len);
+ }
+ }
+
+ if (control->type != GADGET_FILE && control->type != GADGET_IMAGE) {
+ xmlChar *value = xmlGetProp(node, (const xmlChar *) "value");
+ if (value != NULL) {
+ control->value = strdup((char *) value);
+
+ xmlFree(value);
+
+ if (control->value == NULL) {
+ form_free_control(control);
+ return NULL;
+ }
+
+ control->length = strlen(control->value);
+ }
+
+ if (control->type == GADGET_TEXTBOX ||
+ control->type == GADGET_PASSWORD) {
+ if (control->value == NULL) {
+ control->value = strdup("");
+ if (control->value == NULL) {
+ form_free_control(control);
+ return NULL;
+ }
+
+ control->length = 0;
+ }
+
+ control->initial_value = strdup(control->value);
+ if (control->initial_value == NULL) {
+ form_free_control(control);
+ return NULL;
+ }
+ }
+ }
+
+ name = xmlGetProp(node, (const xmlChar *) "name");
+ if (name != NULL) {
+ control->name = strdup((char *) name);
+
+ xmlFree(name);
+
+ if (control->name == NULL) {
+ form_free_control(control);
+ return NULL;
+ }
+ }
+
+ return control;
+}
+
+struct form_control *parse_button_element(xmlNode *node)
+{
+ struct form_control *control;
+ xmlChar *type = xmlGetProp(node, (const xmlChar *) "type");
+ xmlChar *name;
+ xmlChar *value;
+
+ if (type == NULL || strcasecmp((char *) type, "submit") == 0) {
+ control = form_new_control(node, GADGET_SUBMIT);
+ } else if (strcasecmp((char *) type, "reset") == 0) {
+ control = form_new_control(node, GADGET_RESET);
+ } else {
+ control = form_new_control(node, GADGET_BUTTON);
+ }
+
+ xmlFree(type);
+
+ if (control == NULL)
+ return NULL;
+
+ value = xmlGetProp(node, (const xmlChar *) "value");
+ if (value != NULL) {
+ control->value = strdup((char *) value);
+
+ xmlFree(value);
+
+ if (control->value == NULL) {
+ form_free_control(control);
+ return NULL;
+ }
+ }
+
+ name = xmlGetProp(node, (const xmlChar *) "name");
+ if (name != NULL) {
+ control->name = strdup((char *) name);
+
+ xmlFree(name);
+
+ if (control->name == NULL) {
+ form_free_control(control);
+ return NULL;
+ }
+ }
+
+ return control;
+}
+
+struct form_control *parse_select_element(xmlNode *node)
+{
+ struct form_control *control = form_new_control(node, GADGET_SELECT);
+ xmlChar *name;
+
+ if (control == NULL)
+ return NULL;
+
+ control->data.select.multiple =
+ xmlHasProp(node, (const xmlChar *) "multiple");
+
+ name = xmlGetProp(node, (const xmlChar *) "name");
+ if (name != NULL) {
+ control->name = strdup((char *) name);
+
+ xmlFree(name);
+
+ if (control->name == NULL) {
+ form_free_control(control);
+ return NULL;
+ }
+ }
+
+ return control;
+}
+
+struct form_control *parse_textarea_element(xmlNode *node)
+{
+ struct form_control *control = form_new_control(node, GADGET_TEXTAREA);
+ xmlChar *name;
+
+ if (control == NULL)
+ return NULL;
+
+ name = xmlGetProp(node, (const xmlChar *) "name");
+ if (name != NULL) {
+ control->name = strdup((char *) name);
+
+ xmlFree(name);
+
+ if (control->name == NULL) {
+ form_free_control(control);
+ return NULL;
+ }
+ }
+
+ return control;
+}
+
#endif
diff --git a/render/parser_binding.h b/render/parser_binding.h
index 10c0ad334..d23b79359 100644
--- a/render/parser_binding.h
+++ b/render/parser_binding.h
@@ -23,6 +23,9 @@
#include <libxml/tree.h>
+struct form;
+struct form_control;
+
typedef enum binding_error {
BINDING_OK,
BINDING_NOMEM,
@@ -45,5 +48,10 @@ binding_error binding_parse_completed(void *ctx);
const char *binding_get_encoding(void *ctx, binding_encoding_source *source);
xmlDocPtr binding_get_document(void *ctx);
+#ifdef WITH_HUBBUB
+struct form *binding_get_forms(void *ctx);
+struct form_control *binding_get_control_for_node(void *ctx, xmlNodePtr node);
+#endif
+
#endif