diff options
Diffstat (limited to 'content')
-rw-r--r-- | content/handlers/html/box.h | 8 | ||||
-rw-r--r-- | content/handlers/html/box_construct.c | 3 | ||||
-rw-r--r-- | content/handlers/html/form.c | 111 | ||||
-rw-r--r-- | content/handlers/html/form_internal.h | 17 | ||||
-rw-r--r-- | content/handlers/html/html.c | 20 | ||||
-rw-r--r-- | content/handlers/html/html_forms.c | 11 |
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) |