1
0
mirror of https://github.com/rkd77/elinks.git synced 2024-12-04 14:46:47 -05:00

Bug 698: Keep forms contiguous and non-overlapping and start from 0.

In document.forms, each struct form has form_num and form_end members
that reserve a subrange of [0, INT_MAX] to that form.  Previously,
multiple forms in the list could have form_end == INT_MAX and thus
overlap each other.  Prevent that by adjusting form_end of each form
newly added to the list.

Revert 438f039bda,
"check_html_form_hierarchy: Old code was buggy.", which made
check_html_form_hierarchy attach controls to the wrong forms.
Instead, construct the dummy form ("for those Flying Dutchmans") at
form_num == 0 always before adding any real forms to the list.
This prevents the assertion failure by ensuring that every possible
form_control.position is covered by some form, if there are any forms.

Add a function assert_forms_list_ok, which checks that the set of
forms actually covers the [0, INT_MAX] range without overlapping,
as intended.  Call that from check_html_form_hierarchy to detect
any corruption.

I have tested this code (before any cherry-picking) with:
- bug 613 attachment 210: didn't crash
- bug 714 attachment 471: didn't crash
- bug 961 attachment 382: didn't crash
- bug 698 attachment 239: all the submit buttons showed the right URLs
- bug 698 attachment 470: the submit button showed the right URL

(cherry picked from commit 386a5d517b,
 with conflicts)
This commit is contained in:
Kalle Olavi Niemitalo 2008-07-14 15:02:06 +03:00 committed by Kalle Olavi Niemitalo
parent 295c7760f7
commit fd7968e638
2 changed files with 107 additions and 43 deletions

2
NEWS
View File

@ -15,6 +15,8 @@ To be released as 0.11.5.
* critical bug 1030: an assertion used to fail in the search dialog
on systems that lack a usable <regex.h>
* major bug 503: various fixes in parsing and updating of elinks.conf
* bug 698: Attach controls to the intended form even if it is
incorrectly nested in a table. (Was broken in 0.11.4.)
* build bug 1021: fixed uninitialized variable in http_got_header
ELinks 0.11.4:

View File

@ -1468,7 +1468,11 @@ end:
static void
html_special_form(struct part *part, struct form *form)
{
struct form *nform;
assert(part && form);
assert(form->form_num > 0);
assert(form->form_end == INT_MAX);
if_assert_failed return;
if (!part->document) {
@ -1476,50 +1480,60 @@ html_special_form(struct part *part, struct form *form)
return;
}
if (!list_empty(part->document->forms)) {
struct form *nform;
/* Make sure the new form ``claims'' its slice of the form range
* maintained in the form_num and form_end variables. */
foreach (nform, part->document->forms) {
if (form->form_num < nform->form_num
|| nform->form_end < form->form_num)
continue;
/* First check if the form has identical form numbers.
* That should only be the case when the form being
* added is in fact the same form in which case it
* should be dropped. The fact that this can happen
* suggests that the table renderering can be confused.
* See bug 647 for a test case. */
if (nform->form_num == form->form_num
&& nform->form_end == form->form_end) {
done_form(form);
return;
}
/* The form start is inside an already added form, so
* partition the space of the existing form and get
* |old|new|. */
nform->form_end = form->form_num - 1;
assertm(nform->form_num <= nform->form_end,
"[%d:%d] [%d:%d]", nform->form_num, nform->form_end,
form->form_num, form->form_end);
break;
/* Make a fake form with form_num == 0 so that there is
* something to use if form controls appear above the first
* actual FORM element. There can never be a real form with
* form_num == 0 because the form_num is the position after the
* "<form" characters and that's already five characters. The
* fake form does not have a name, and it gets a form_view and
* becomes visible to ECMAScript only if it actually has
* controls in it. */
if (list_empty(part->document->forms)) {
nform = init_form();
if (!nform) {
done_form(form);
return;
}
} else {
/* If it is the first form make sure it eats the whole form
* range. */
#if 0
/* Disabled because in tables the parse order may lead to a
* later form being parsed before a preceeding one causing the
* wrong order if we set it to zero. Let's hope it doesn't break
* anything else. */
form->form_num = 0;
#endif
nform->form_num = 0;
add_to_list(part->document->forms, nform);
}
add_to_list(part->document->forms, form);
/* Make sure the new form ``claims'' its slice of the form range
* maintained in the form_num and form_end variables. */
foreach (nform, part->document->forms) {
if (form->form_num < nform->form_num
|| nform->form_end < form->form_num)
continue;
/* First check if the form has identical form numbers.
* That should only be the case when the form being
* added is in fact the same form in which case it
* should be dropped. The fact that this can happen
* suggests that the table renderering can be confused.
* See bug 647 for a test case.
* Do not compare form->form_end here because it is
* normally set by this function and that has obviously
* not yet been done. */
if (nform->form_num == form->form_num) {
done_form(form);
return;
}
/* The form start is inside an already added form, so
* partition the space of the existing form and get
* |old|new|. */
form->form_end = nform->form_end;
nform->form_end = form->form_num - 1;
assertm(nform->form_num <= nform->form_end,
"[%d:%d] [%d:%d]", nform->form_num, nform->form_end,
form->form_num, form->form_end);
add_to_list(part->document->forms, form);
return;
}
ERROR("hole between forms");
done_form(form);
return;
}
static void
@ -1564,6 +1578,51 @@ html_special_form_control(struct part *part, struct form_control *fc)
add_to_list(form->items, fc);
}
#ifdef CONFIG_DEBUG
/** Assert that each form in the list has a different form.form_num
* ... form.form_end range and that the ranges are contiguous and
* together cover all numbers from 0 to INT_MAX. Alternatively, the
* whole list may be empty. This function can be called from a
* debugger, or automatically from some places.
*
* This function may leave assert_failed = 1; the caller must use
* if_assert_failed. */
static void
assert_forms_list_ok(struct list_head *forms)
{
int saw_form_num_0 = 0;
struct form *outer;
if (list_empty(*forms)) return;
/* O(n^2) algorithm, but it's only for debugging. */
foreach (outer, *forms) {
int followers = 0;
struct form *inner;
if (outer->form_num == 0)
saw_form_num_0++;
foreach (inner, *forms) {
assert(inner == outer
|| inner->form_num > outer->form_end
|| outer->form_num > inner->form_end);
if (outer->form_end == inner->form_num - 1)
followers++;
}
if (outer->form_end == INT_MAX)
assert(followers == 0);
else
assert(followers == 1);
}
assert(saw_form_num_0 == 1);
}
#else /* !CONFIG_DEBUG */
# define assert_forms_list_ok(forms) ((void) 0)
#endif /* !CONFIG_DEBUG */
/* Reparents form items based on position in the source. */
void
check_html_form_hierarchy(struct part *part)
@ -1576,6 +1635,9 @@ check_html_form_hierarchy(struct part *part)
if (list_empty(document->forms))
return;
assert_forms_list_ok(&document->forms);
if_assert_failed {}
/* Take out all badly placed form items. */
foreach (form, document->forms) {
@ -1597,8 +1659,8 @@ check_html_form_hierarchy(struct part *part)
foreachsafe (fc, next, form_controls) {
foreach (form, document->forms) {
if (form->form_num <= fc->position
&& fc->position <= form->form_end)
if (fc->position < form->form_num
|| form->form_end < fc->position)
continue;
fc->form = form;