Rewrite form_successful_controls() to use the DOM.

This stops image inputs from working temporarily, but makes
inputs inside display:none work correctly.  As such, this is
a net-win.
This commit is contained in:
Daniel Silverstone 2014-01-05 15:50:23 +00:00
parent 32468516e3
commit b1e6c63a2f
4 changed files with 563 additions and 5 deletions

View File

@ -88,7 +88,7 @@ static plot_font_style_t plot_fstyle_entry = {
};
static char *form_acceptable_charset(struct form *form);
static char *form_encode_item(const char *item, const char *charset,
static char *form_encode_item(const char *item, uint32_t len, const char *charset,
const char *fallback);
static void form_select_menu_clicked(struct form_control *control,
int x, int y);
@ -320,6 +320,509 @@ bool form_add_option(struct form_control *control, char *value, char *text,
}
/**
* Identify 'successful' controls via the DOM.
*
* All text strings in the successful controls list will be in the charset most
* appropriate for submission. Therefore, no utf8_to_* processing should be
* performed upon them.
*
* \todo The chosen charset needs to be made available such that it can be
* included in the submission request (e.g. in the fetch's Content-Type header)
*
* \param form form to search for successful controls
* \param submit_button control used to submit the form, if any
* \param successful_controls updated to point to linked list of
* fetch_multipart_data, 0 if no controls
* \return true on success, false on memory exhaustion
*
* See HTML 4.01 section 17.13.2.
*/
bool form_successful_controls_dom(struct form *_form,
struct form_control *_submit_button,
struct fetch_multipart_data **successful_controls)
{
dom_html_form_element *form = _form->node;
dom_html_element *submit_button = (_submit_button != NULL) ? _submit_button->node : NULL;
dom_html_collection *form_elements = NULL;
dom_html_options_collection *options = NULL;
dom_node *form_element = NULL, *option_element = NULL;
dom_exception err;
dom_string *nodename = NULL, *inputname = NULL, *inputvalue = NULL, *inputtype = NULL;
struct fetch_multipart_data sentinel, *last_success, *success_new;
bool had_submit = false, element_disabled;
char *charset, *rawfile_temp = NULL, *basename;
uint32_t index, element_count;
last_success = &sentinel;
sentinel.next = NULL;
LOG(("XYZZY: Yay, let's look for a form"));
/** \todo Replace this call with something DOMish */
charset = form_acceptable_charset(_form);
if (charset == NULL) {
LOG(("failed to find charset"));
return false;
}
#define ENCODE_ITEM(i) (((i) == NULL) ? ( \
form_encode_item("", 0, charset, _form->document_charset) \
):( \
form_encode_item(dom_string_data(i), dom_string_byte_length(i), \
charset, _form->document_charset) \
))
err = dom_html_form_element_get_elements(form, &form_elements);
if (err != DOM_NO_ERR) {
LOG(("Could not get form elements"));
goto dom_no_memory;
}
LOG(("Reffed %p", form_elements));
err = dom_html_collection_get_length(form_elements, &element_count);
if (err != DOM_NO_ERR) {
LOG(("Could not get form element count"));
goto dom_no_memory;
}
for (index = 0; index < element_count; index++) {
if (form_element != NULL) {
LOG(("Unreffed %p", form_element));
dom_node_unref(form_element);
form_element = NULL;
}
if (nodename != NULL) {
dom_string_unref(nodename);
nodename = NULL;
}
if (inputname != NULL) {
dom_string_unref(inputname);
inputname = NULL;
}
if (inputvalue != NULL) {
dom_string_unref(inputvalue);
inputvalue = NULL;
}
if (inputtype != NULL) {
dom_string_unref(inputtype);
inputtype = NULL;
}
if (options != NULL) {
dom_html_options_collection_unref(options);
options = NULL;
}
err = dom_html_collection_item(form_elements,
index, &form_element);
if (err != DOM_NO_ERR) {
LOG(("Could not retrieve form element %d", index));
goto dom_no_memory;
}
LOG(("Reffed %p", form_element));
/* Form elements are one of:
* HTMLInputElement
* HTMLTextAreaElement
* HTMLSelectElement
*/
err = dom_node_get_node_name(form_element, &nodename);
if (err != DOM_NO_ERR) {
LOG(("Could not get node name"));
goto dom_no_memory;
}
LOG(("Found a node(%p): `%*s`", nodename,
dom_string_byte_length(nodename),
dom_string_data(nodename)));
if (dom_string_isequal(nodename, corestring_dom_TEXTAREA)) {
err = dom_html_text_area_element_get_disabled(
(dom_html_text_area_element *)form_element,
&element_disabled);
if (err != DOM_NO_ERR) {
LOG(("Could not get text area disabled property"));
goto dom_no_memory;
}
err = dom_html_text_area_element_get_name(
(dom_html_text_area_element *)form_element,
&inputname);
if (err != DOM_NO_ERR) {
LOG(("Could not get text area name property"));
goto dom_no_memory;
}
} else if (dom_string_isequal(nodename, corestring_dom_SELECT)) {
err = dom_html_select_element_get_disabled(
(dom_html_select_element *)form_element,
&element_disabled);
if (err != DOM_NO_ERR) {
LOG(("Could not get select disabled property"));
goto dom_no_memory;
}
err = dom_html_select_element_get_name(
(dom_html_select_element *)form_element,
&inputname);
if (err != DOM_NO_ERR) {
LOG(("Could not get select name property"));
goto dom_no_memory;
}
} else if (dom_string_isequal(nodename, corestring_dom_INPUT)) {
err = dom_html_input_element_get_disabled(
(dom_html_input_element *)form_element,
&element_disabled);
if (err != DOM_NO_ERR) {
LOG(("Could not get input disabled property"));
goto dom_no_memory;
}
err = dom_html_input_element_get_name(
(dom_html_input_element *)form_element,
&inputname);
if (err != DOM_NO_ERR) {
LOG(("Could not get input name property"));
goto dom_no_memory;
}
} else if (dom_string_isequal(nodename, corestring_dom_BUTTON)) {
/* It was a button, no fair */
continue;
} else {
/* Unknown element type came through! */
LOG(("Unknown element type: %*s",
dom_string_byte_length(nodename),
dom_string_data(nodename)));
goto dom_no_memory;
}
if (element_disabled)
continue;
if (inputname == NULL)
continue;
if (dom_string_isequal(nodename, corestring_dom_TEXTAREA)) {
err = dom_html_text_area_element_get_value(
(dom_html_text_area_element *)form_element,
&inputvalue);
if (err != DOM_NO_ERR) {
LOG(("Could not get text area content"));
goto dom_no_memory;
}
} else if (dom_string_isequal(nodename, corestring_dom_SELECT)) {
uint32_t options_count, option_index;
err = dom_html_select_element_get_options(
(dom_html_select_element *)form_element,
&options);
if (err != DOM_NO_ERR) {
LOG(("Could not get select options collection"));
goto dom_no_memory;
}
err = dom_html_options_collection_get_length(
options, &options_count);
if (err != DOM_NO_ERR) {
LOG(("Could not get select options collection length"));
goto dom_no_memory;
}
for(option_index = 0; option_index < options_count;
++option_index) {
bool selected;
if (option_element != NULL) {
dom_node_unref(option_element);
option_element = NULL;
}
if (inputvalue != NULL) {
dom_string_unref(inputvalue);
inputvalue = NULL;
}
err = dom_html_options_collection_item(
options, option_index, &option_element);
if (err != DOM_NO_ERR) {
LOG(("Could not get options item %d", option_index));
goto dom_no_memory;
}
err = dom_html_option_element_get_selected(
(dom_html_option_element *)option_element,
&selected);
if (err != DOM_NO_ERR) {
LOG(("Could not get option selected property"));
goto dom_no_memory;
}
if (!selected)
continue;
err = dom_html_option_element_get_value(
(dom_html_option_element *)option_element,
&inputvalue);
if (err != DOM_NO_ERR) {
LOG(("Could not get option value"));
goto dom_no_memory;
}
success_new = calloc(1, sizeof(*success_new));
if (success_new == NULL) {
LOG(("Could not allocate data for option"));
goto dom_no_memory;
}
last_success->next = success_new;
last_success = success_new;
success_new->name = ENCODE_ITEM(inputname);
if (success_new->name == NULL) {
LOG(("Could not encode name for option"));
goto dom_no_memory;
}
success_new->value = ENCODE_ITEM(inputvalue);
if (success_new->value == NULL) {
LOG(("Could not encode value for option"));
goto dom_no_memory;
}
}
continue;
} else if (dom_string_isequal(nodename, corestring_dom_INPUT)) {
/* Things to consider here */
/* Buttons -- only if the successful control */
/* radio and checkbox -- only if selected */
/* file -- also get the rawfile */
/* everything else -- just value */
err = dom_html_input_element_get_type(
(dom_html_input_element *) form_element,
&inputtype);
if (err != DOM_NO_ERR) {
LOG(("Could not get input element type"));
goto dom_no_memory;
}
if (dom_string_caseless_isequal(
inputtype, corestring_dom_submit)) {
LOG(("Examining submit button"));
if (submit_button == NULL && !had_submit)
/* no button used, and first submit
* node found, so use it
*/
had_submit = true;
else if ((dom_node *)submit_button !=
(dom_node *)form_element)
continue;
err = dom_html_input_element_get_value(
(dom_html_input_element *)form_element,
&inputvalue);
if (err != DOM_NO_ERR) {
LOG(("Could not get submit button value"));
goto dom_no_memory;
}
/* Drop through to report the successful button */
} else if (dom_string_caseless_isequal(
inputtype, corestring_dom_image)) {
/* We *ONLY* use an image input if it was the
* thing which activated us
*/
LOG(("Examining image button"));
if ((dom_node *)submit_button !=
(dom_node *)form_element)
continue;
basename = ENCODE_ITEM(inputname);
success_new = calloc(1, sizeof(*success_new));
if (success_new == NULL) {
free(basename);
LOG(("Could not allocate data for image.x"));
goto dom_no_memory;
}
last_success->next = success_new;
last_success = success_new;
success_new->name = malloc(strlen(basename) + 3);
if (success_new->name == NULL) {
free(basename);
LOG(("Could not allocate name for image.x"));
goto dom_no_memory;
}
success_new->value = malloc(20);
if (success_new->value == NULL) {
free(basename);
LOG(("Could not allocate value for image.x"));
goto dom_no_memory;
}
sprintf(success_new->name, "%s.x", basename);
/** \todo Store this on the node and
* retrieve it here
*/
sprintf(success_new->value, "%d", 0);
success_new = calloc(1, sizeof(*success_new));
if (success_new == NULL) {
free(basename);
LOG(("Could not allocate data for image.y"));
goto dom_no_memory;
}
last_success->next = success_new;
last_success = success_new;
success_new->name = malloc(strlen(basename) + 3);
if (success_new->name == NULL) {
free(basename);
LOG(("Could not allocate name for image.y"));
goto dom_no_memory;
}
success_new->value = malloc(20);
if (success_new->value == NULL) {
free(basename);
LOG(("Could not allocate value for image.y"));
goto dom_no_memory;
}
sprintf(success_new->name, "%s.y", basename);
/** \todo Store this on the node and
* retrieve it here
*/
sprintf(success_new->value, "%d", 0);
free(basename);
continue;
} else if (dom_string_caseless_isequal(
inputtype, corestring_dom_radio) ||
dom_string_caseless_isequal(
inputtype, corestring_dom_checkbox)) {
LOG(("Examining radio or checkbox"));
err = dom_html_input_element_get_value(
(dom_html_input_element *)form_element,
&inputvalue);
if (err != DOM_NO_ERR) {
LOG(("Could not get input element value"));
goto dom_no_memory;
}
if (inputvalue == NULL)
inputvalue = dom_string_ref(
corestring_dom_on);
/* Fall through to simple allocation */
} else if (dom_string_caseless_isequal(
inputtype, corestring_dom_file)) {
LOG(("Examining file input"));
err = dom_html_input_element_get_value(
(dom_html_input_element *)form_element,
&inputvalue);
if (err != DOM_NO_ERR) {
LOG(("Could not get file value"));
goto dom_no_memory;
}
err = dom_node_get_user_data(
form_element,
corestring_dom___ns_key_file_name_node_data,
&rawfile_temp);
if (err != DOM_NO_ERR) {
LOG(("Could not get file rawname"));
goto dom_no_memory;
}
rawfile_temp = strdup(rawfile_temp != NULL ?
rawfile_temp :
"");
if (rawfile_temp == NULL) {
LOG(("Could not copy file rawname"));
goto dom_no_memory;
}
/* Fall out to the allocation */
} else if (dom_string_caseless_isequal(
inputtype, corestring_dom_reset) ||
dom_string_caseless_isequal(
inputtype, corestring_dom_button)) {
/* Skip these */
LOG(("Skipping RESET and BUTTON"));
continue;
} else {
/* Everything else is treated as text values */
LOG(("Retrieving generic input text"));
err = dom_html_input_element_get_value(
(dom_html_input_element *)form_element,
&inputvalue);
if (err != DOM_NO_ERR) {
LOG(("Could not get input value"));
goto dom_no_memory;
}
/* Fall out to the allocation */
}
}
success_new = calloc(1, sizeof(*success_new));
if (success_new == NULL) {
LOG(("Could not allocate data for generic"));
goto dom_no_memory;
}
last_success->next = success_new;
last_success = success_new;
success_new->name = ENCODE_ITEM(inputname);
if (success_new->name == NULL) {
LOG(("Could not encode name for generic"));
goto dom_no_memory;
}
success_new->value = ENCODE_ITEM(inputvalue);
if (success_new->value == NULL) {
LOG(("Could not encode value for generic"));
goto dom_no_memory;
}
if (rawfile_temp != NULL) {
success_new->file = true;
success_new->rawfile = rawfile_temp;
rawfile_temp = NULL;
}
}
free(charset);
if (form_element != NULL) {
LOG(("Unreffed %p", form_element));
dom_node_unref(form_element);
}
if (form_elements != NULL) {
LOG(("Unreffed %p", form_elements));
dom_html_collection_unref(form_elements);
}
if (nodename != NULL)
dom_string_unref(nodename);
if (inputname != NULL)
dom_string_unref(inputname);
if (inputvalue != NULL)
dom_string_unref(inputvalue);
if (options != NULL)
dom_html_options_collection_unref(options);
if (option_element != NULL)
dom_node_unref(option_element);
if (inputtype != NULL)
dom_string_unref(inputtype);
if (rawfile_temp != NULL)
free(rawfile_temp);
*successful_controls = sentinel.next;
for (success_new = *successful_controls; success_new != NULL;
success_new = success_new->next) {
LOG(("%p -> %s=%s", success_new, success_new->name, success_new->value));
LOG(("%p -> file=%s rawfile=%s", success_new,
success_new->file ? "yes" : "no", success_new->rawfile));
}
return true;
dom_no_memory:
free(charset);
fetch_multipart_data_destroy(sentinel.next);
if (form_elements != NULL)
dom_html_collection_unref(form_elements);
if (form_element != NULL)
dom_node_unref(form_element);
if (nodename != NULL)
dom_string_unref(nodename);
if (inputname != NULL)
dom_string_unref(inputname);
if (inputvalue != NULL)
dom_string_unref(inputvalue);
if (options != NULL)
dom_html_options_collection_unref(options);
if (option_element != NULL)
dom_node_unref(option_element);
if (inputtype != NULL)
dom_string_unref(inputtype);
if (rawfile_temp != NULL)
free(rawfile_temp);
return false;
}
#undef ENCODE_ITEM
/**
* Identify 'successful' controls.
*
@ -356,7 +859,7 @@ bool form_successful_controls(struct form *form,
if (charset == NULL)
return false;
#define ENCODE_ITEM(i) form_encode_item((i), charset, form->document_charset)
#define ENCODE_ITEM(i) form_encode_item((i), 0, charset, form->document_charset)
for (control = form->controls; control; control = control->next) {
/* ignore disabled controls */
@ -812,12 +1315,13 @@ char *form_acceptable_charset(struct form *form)
* \todo Return charset used?
*
* \param item String to convert
* \param len Length of string to convert
* \param charset Destination charset
* \param fallback Fallback charset (may be NULL),
* used iff converting to charset fails
* \return Pointer to converted string (on heap, caller frees), or NULL
*/
char *form_encode_item(const char *item, const char *charset,
char *form_encode_item(const char *item, uint32_t len, const char *charset,
const char *fallback)
{
utf8_convert_ret err;
@ -833,7 +1337,7 @@ char *form_encode_item(const char *item, const char *charset,
if (err == UTF8_CONVERT_BADENC) {
/* charset not understood, try without transliteration */
snprintf(cset, sizeof cset, "%s", charset);
err = utf8_to_enc(item, cset, 0, &ret);
err = utf8_to_enc(item, cset, len, &ret);
if (err == UTF8_CONVERT_BADENC) {
/* nope, try fallback charset (if any) */
@ -1490,7 +1994,7 @@ void form_submit(nsurl *page_url, struct browser_window *target,
assert(form != NULL);
if (form_successful_controls(form, submit_button, &success) == false) {
if (form_successful_controls_dom(form, submit_button, &success) == false) {
warn_user("NoMemory", 0);
return;
}

View File

@ -159,6 +159,9 @@ bool form_add_option(struct form_control *control, char *value, char *text,
bool form_successful_controls(struct form *form,
struct form_control *submit_button,
struct fetch_multipart_data **successful_controls);
bool form_successful_controls_dom(struct form *form,
struct form_control *submit_button,
struct fetch_multipart_data **successful_controls);
bool form_open_select_menu(void *client_data,
struct form_control *control,

View File

@ -232,6 +232,16 @@ dom_string *corestring_dom_volumechange;
dom_string *corestring_dom_vspace;
dom_string *corestring_dom_waiting;
dom_string *corestring_dom_width;
dom_string *corestring_dom_BUTTON;
dom_string *corestring_dom_INPUT;
dom_string *corestring_dom_SELECT;
dom_string *corestring_dom_TEXTAREA;
dom_string *corestring_dom_button;
dom_string *corestring_dom_image;
dom_string *corestring_dom_radio;
dom_string *corestring_dom_checkbox;
dom_string *corestring_dom_file;
dom_string *corestring_dom_on;
dom_string *corestring_dom___ns_key_libcss_node_data;
dom_string *corestring_dom___ns_key_file_name_node_data;
@ -463,6 +473,19 @@ void corestrings_fini(void)
CSS_DOM_STRING_UNREF(vspace);
CSS_DOM_STRING_UNREF(waiting);
CSS_DOM_STRING_UNREF(width);
/* DOM node names, not really CSS */
CSS_DOM_STRING_UNREF(BUTTON);
CSS_DOM_STRING_UNREF(INPUT);
CSS_DOM_STRING_UNREF(SELECT);
CSS_DOM_STRING_UNREF(TEXTAREA);
/* DOM input types, not really CSS */
CSS_DOM_STRING_UNREF(button);
CSS_DOM_STRING_UNREF(image);
CSS_DOM_STRING_UNREF(radio);
CSS_DOM_STRING_UNREF(checkbox);
CSS_DOM_STRING_UNREF(file);
CSS_DOM_STRING_UNREF(on);
/* DOM userdata keys, not really CSS */
CSS_DOM_STRING_UNREF(__ns_key_libcss_node_data);
CSS_DOM_STRING_UNREF(__ns_key_file_name_node_data);
#undef CSS_DOM_STRING_UNREF
@ -725,6 +748,19 @@ nserror corestrings_init(void)
CSS_DOM_STRING_INTERN(vspace);
CSS_DOM_STRING_INTERN(waiting);
CSS_DOM_STRING_INTERN(width);
/* DOM node names, not really CSS */
CSS_DOM_STRING_INTERN(BUTTON);
CSS_DOM_STRING_INTERN(INPUT);
CSS_DOM_STRING_INTERN(SELECT);
CSS_DOM_STRING_INTERN(TEXTAREA);
/* DOM input types, not really CSS */
CSS_DOM_STRING_INTERN(button);
CSS_DOM_STRING_INTERN(image);
CSS_DOM_STRING_INTERN(radio);
CSS_DOM_STRING_INTERN(checkbox);
CSS_DOM_STRING_INTERN(file);
CSS_DOM_STRING_INTERN(on);
/* DOM userdata keys, not really CSS */
CSS_DOM_STRING_INTERN(__ns_key_libcss_node_data);
CSS_DOM_STRING_INTERN(__ns_key_file_name_node_data);
#undef CSS_DOM_STRING_INTERN

View File

@ -238,6 +238,21 @@ extern struct dom_string *corestring_dom_volumechange;
extern struct dom_string *corestring_dom_vspace;
extern struct dom_string *corestring_dom_waiting;
extern struct dom_string *corestring_dom_width;
/* DOM node types */
extern struct dom_string *corestring_dom_BUTTON;
extern struct dom_string *corestring_dom_INPUT;
extern struct dom_string *corestring_dom_SELECT;
extern struct dom_string *corestring_dom_TEXTAREA;
/* DOM input node types */
extern struct dom_string *corestring_dom_button;
/* extern struct dom_string *corestring_dom_submit; */
/* extern struct dom_string *corestring_dom_reset; */
extern struct dom_string *corestring_dom_image;
extern struct dom_string *corestring_dom_radio;
extern struct dom_string *corestring_dom_checkbox;
extern struct dom_string *corestring_dom_file;
extern struct dom_string *corestring_dom_on;
/* DOM userdata keys */
extern struct dom_string *corestring_dom___ns_key_libcss_node_data;
extern struct dom_string *corestring_dom___ns_key_file_name_node_data;