summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Silverstone <dsilvers@digital-scurf.org>2019-08-04 22:07:42 +0100
committerDaniel Silverstone <dsilvers@digital-scurf.org>2019-08-04 22:07:42 +0100
commit05c6ee02d99ea04dde6687d94508b40f4393f77b (patch)
tree47ead2087a29ecf2c89a22cc0281099142cb0e9f
parente78dc4f5a945f2035f48c8dc01cd3b1d76f03c81 (diff)
downloadnetsurf-05c6ee02d99ea04dde6687d94508b40f4393f77b.tar.gz
netsurf-05c6ee02d99ea04dde6687d94508b40f4393f77b.tar.bz2
html: Mirror gadget values in and out of the DOM
Currently only supporting text input, password input, and hidden input, along with text areas, this mirrors the text values in and out of the DOM, allowing JS to adjust the gadget values and for the gadget values to be interrogated from JS. Signed-off-by: Daniel Silverstone <dsilvers@digital-scurf.org>
-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)