summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--content/handlers/html/box.h8
-rw-r--r--content/handlers/html/box_construct.c3
-rw-r--r--content/handlers/html/form.c111
-rw-r--r--content/handlers/html/form_internal.h17
-rw-r--r--content/handlers/html/html.c20
-rw-r--r--content/handlers/html/html_forms.c11
6 files changed, 169 insertions, 1 deletions
diff --git a/content/handlers/html/box.h b/content/handlers/html/box.h
index 0952b841b..6939cfe1a 100644
--- a/content/handlers/html/box.h
+++ b/content/handlers/html/box.h
@@ -377,4 +377,12 @@ static inline bool box_is_first_child(struct box *b)
return (b->parent == NULL || b == b->parent->children);
}
+/**
+ * Retrieve the box for a dom node, if there is one
+ *
+ * \param node The DOM node
+ * \return The box if there is one
+ */
+struct box *box_for_node(struct dom_node *node);
+
#endif
diff --git a/content/handlers/html/box_construct.c b/content/handlers/html/box_construct.c
index a0eb69ed5..1f15d82b7 100644
--- a/content/handlers/html/box_construct.c
+++ b/content/handlers/html/box_construct.c
@@ -211,7 +211,8 @@ static const box_type box_map[] = {
BOX_NONE /*CSS_DISPLAY_NONE*/
};
-static inline struct box *box_for_node(dom_node *n)
+/* Exported function, see box.h */
+struct box *box_for_node(dom_node *n)
{
struct box *box = NULL;
dom_exception err;
diff --git a/content/handlers/html/form.c b/content/handlers/html/form.c
index 3d787e762..e8461cdcf 100644
--- a/content/handlers/html/form.c
+++ b/content/handlers/html/form.c
@@ -223,6 +223,9 @@ void form_free_control(struct form_control *control)
free(control->name);
free(control->value);
free(control->initial_value);
+ if (control->last_synced_value != NULL) {
+ free(control->last_synced_value);
+ }
if (control->type == GADGET_SELECT) {
struct form_option *option, *next;
@@ -275,6 +278,10 @@ void form_free_control(struct form_control *control)
}
}
+ if (control->node_value != NULL) {
+ dom_string_unref(control->node_value);
+ }
+
free(control);
}
@@ -2235,4 +2242,108 @@ void form_gadget_update_value(struct form_control *control, char *value)
/* Do nothing */
break;
}
+
+ /* Finally, sync this with the DOM */
+ form_gadget_sync_with_dom(control);
+}
+
+/* Exported API, see form_internal.h */
+void
+form_gadget_sync_with_dom(struct form_control *control)
+{
+ dom_exception exc;
+ dom_string *value = NULL;
+ bool changed_dom = false;
+
+ if (control->syncing ||
+ (control->type != GADGET_TEXTBOX &&
+ control->type != GADGET_PASSWORD &&
+ control->type != GADGET_HIDDEN &&
+ control->type != GADGET_TEXTAREA)) {
+ /* Not a control we support, or the control is already
+ * mid-sync so we don't want to disrupt that
+ */
+ return;
+ }
+
+ control->syncing = true;
+
+ /* If we've changed value, sync that toward the DOM */
+ if ((control->last_synced_value == NULL && control->value[0] != '\0') ||
+ (control->last_synced_value != NULL && strcmp(control->value, control->last_synced_value) != 0)) {
+ char *dup = strdup(control->value);
+ if (dup == NULL) {
+ goto out;
+ }
+ if (control->last_synced_value != NULL) {
+ free(control->last_synced_value);
+ }
+ control->last_synced_value = dup;
+ exc = dom_string_create((uint8_t *)(control->value),
+ strlen(control->value), &value);
+ if (exc != DOM_NO_ERR) {
+ goto out;
+ }
+ if (control->node_value != NULL) {
+ dom_string_unref(control->node_value);
+ }
+ control->node_value = value;
+ value = NULL;
+ if (control->type == GADGET_TEXTAREA) {
+ exc = dom_html_text_area_element_set_value(control->node, control->node_value);
+ } else {
+ exc = dom_html_input_element_set_value(control->node, control->node_value);
+ }
+ if (exc != DOM_NO_ERR) {
+ goto out;
+ }
+ changed_dom = true;
+ }
+
+ /* Now check if the DOM has changed since our last go */
+ if (control->type == GADGET_TEXTAREA) {
+ exc = dom_html_text_area_element_get_value(control->node, &value);
+ } else {
+ exc = dom_html_input_element_get_value(control->node, &value);
+ }
+
+ if (exc != DOM_NO_ERR) {
+ /* Nothing much we can do here */
+ goto out;
+ }
+
+ if (!dom_string_isequal(control->node_value, value)) {
+ /* The DOM has changed */
+ if (!changed_dom) {
+ /* And it wasn't us */
+ char *value_s = strndup(
+ dom_string_data(value),
+ dom_string_byte_length(value));
+ char *dup = NULL;
+ if (value_s == NULL) {
+ goto out;
+ }
+ dup = strdup(value_s);
+ if (dup == NULL) {
+ free(value_s);
+ goto out;
+ }
+ free(control->value);
+ control->value = value_s;
+ free(control->last_synced_value);
+ control->last_synced_value = dup;
+ if (control->type != GADGET_HIDDEN &&
+ control->data.text.ta != NULL) {
+ textarea_set_text(control->data.text.ta,
+ value_s);
+ }
+ }
+ control->node_value = value;
+ value = NULL;
+ }
+
+out:
+ if (value != NULL)
+ dom_string_unref(value);
+ control->syncing = false;
}
diff --git a/content/handlers/html/form_internal.h b/content/handlers/html/form_internal.h
index f76f126b4..657522488 100644
--- a/content/handlers/html/form_internal.h
+++ b/content/handlers/html/form_internal.h
@@ -72,6 +72,8 @@ struct image_input_coords {
/** Form control. */
struct form_control {
void *node; /**< Corresponding DOM node */
+ struct dom_string *node_value; /**< The last value sync'd with the DOM */
+ bool syncing; /**< Set if a DOM sync is in-progress */
struct html_content *html; /**< HTML content containing control */
form_control_type type; /**< Type of control */
@@ -81,6 +83,7 @@ struct form_control {
char *name; /**< Control name */
char *value; /**< Current value of control */
char *initial_value; /**< Initial value of control */
+ char *last_synced_value; /**< The last value sync'd to the DOM */
bool disabled; /**< Whether control is disabled */
struct box *box; /**< Box for control */
@@ -261,4 +264,18 @@ void form_radio_set(struct form_control *radio);
void form_gadget_update_value(struct form_control *control, char *value);
+/**
+ * Synchronise this gadget with its associated DOM node.
+ *
+ * If the DOM has changed and the gadget has not, the DOM's new value is
+ * imported into the gadget. If the gadget's value has changed and the DOM's
+ * has not, the gadget's value is pushed into the DOM.
+ * If both have changed, the gadget's value wins.
+ *
+ * \param control The form gadget to synchronise
+ *
+ * \note Currently this will only synchronise input gadgets (text/password)
+ */
+void form_gadget_sync_with_dom(struct form_control *control);
+
#endif
diff --git a/content/handlers/html/html.c b/content/handlers/html/html.c
index 7280ff66a..f72284daf 100644
--- a/content/handlers/html/html.c
+++ b/content/handlers/html/html.c
@@ -829,6 +829,23 @@ dom_default_action_DOMNodeInsertedIntoDocument_cb(struct dom_event *evt, void *p
}
}
+/* Deal with input elements being modified by resyncing their gadget
+ * if they have one.
+ */
+static void html_texty_element_update(html_content *htmlc, dom_node *node)
+{
+ struct box *box = box_for_node(node);
+ if (box == NULL) {
+ return; /* No Box (yet?) so no gadget to update */
+ }
+ if (box->gadget == NULL) {
+ return; /* No gadget yet (under construction perhaps?) */
+ }
+ form_gadget_sync_with_dom(box->gadget);
+ /* And schedule a redraw for the box */
+ html__redraw_a_box(htmlc, box);
+}
+
/* callback for DOMSubtreeModified end type */
static void
dom_default_action_DOMSubtreeModified_cb(struct dom_event *evt, void *pw)
@@ -861,6 +878,9 @@ dom_default_action_DOMSubtreeModified_cb(struct dom_event *evt, void *pw)
case DOM_HTML_ELEMENT_TYPE_STYLE:
html_css_update_style(htmlc, (dom_node *)node);
break;
+ case DOM_HTML_ELEMENT_TYPE_TEXTAREA:
+ case DOM_HTML_ELEMENT_TYPE_INPUT:
+ html_texty_element_update(htmlc, (dom_node *)node);
default:
break;
}
diff --git a/content/handlers/html/html_forms.c b/content/handlers/html/html_forms.c
index 915eb002f..896263dd0 100644
--- a/content/handlers/html/html_forms.c
+++ b/content/handlers/html/html_forms.c
@@ -391,7 +391,18 @@ parse_input_element(struct form *forms, dom_html_input_element *input)
control = NULL;
goto out;
}
+
+ control->last_synced_value = strdup(control->value);
+ if (control->last_synced_value == NULL) {
+ form_free_control(control);
+ control = NULL;
+ goto out;
+ }
+
+ control->node_value = dom_string_ref(ds_value);
}
+ /* Force the gadget and DOM to be in sync */
+ form_gadget_sync_with_dom(control);
}
if (form != NULL && control != NULL)