⚠ Archived content — this site is no longer maintained.   Current WebKit documentation is at docs.webkit.org.

Changeset 121128 in webkit


Ignore:
Timestamp:
Jun 24, 2012, 6:47:35 PM (14 years ago)
Author:
tkent@chromium.org
Message:

Selected option is not restored correctly
https://bugs.webkit.org/show_bug.cgi?id=89623

Reviewed by Hajime Morita.

Source/WebCore:

Details of the bug:
We saved a state of a <select> element as a string of which length was
the size of <select>'s children. e.g. If a <select> had five children
and the second and the fifth items were selected, the state string was:

".X..X"

This didn't work well if the structure of the children was updated after
parsing. For example,

  1. A page has the following <select> initially: <select multiple>

<option>Banana
<option>Lemon
<option>Orange
<option>Strawberry

<select>

  1. For some reasons, <option>Apple</option> is prepended to the children.
  2. Some items are selected.
  3. The page is unloaded. Selection state is saved.
  4. A user go back to the page again. A browser parses the page again.
  5. Try to restore the <select> state with the saved data at 4. But "Apple" is missing. The <select> has wrong selections.

Solution:
We save the state as a set of selected values. If "Banana" and
"Strawberry" are selected in the above <select>, we save two strings;
"Banana" and "Strawberry", not ".X..X".

Test: fast/forms/select/select-state-restore.html

  • html/HTMLSelectElement.cpp:

(WebCore::HTMLSelectElement::saveFormControlState):
Store selected value strings to a FormControlState object.
(WebCore::HTMLSelectElement::searchOptionsForValue):
A helper function to find an <option> with the specified value.
(WebCore::HTMLSelectElement::restoreFormControlState):
Clear all of selections, then select options with saved values.
In order to avoid O(M x N) loop, we start searching at position we found
the previous value.

  • html/HTMLSelectElement.h: Declare searchOptionsForValue.
  • html/FormController.cpp:

(formStateSignature): Bump up the version because this is a incompatible
change.

LayoutTests:

  • fast/forms/select/select-state-restore-expected.txt: Added.
  • fast/forms/select/select-state-restore.html: Added.
Location:
trunk
Files:
2 added
5 edited

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/ChangeLog

    r121127 r121128  
     12012-06-21  Kent Tamura  <tkent@chromium.org>
     2
     3        Selected option is not restored correctly
     4        https://bugs.webkit.org/show_bug.cgi?id=89623
     5
     6        Reviewed by Hajime Morita.
     7
     8        * fast/forms/select/select-state-restore-expected.txt: Added.
     9        * fast/forms/select/select-state-restore.html: Added.
     10
    1112012-06-24  David Barr  <davidbarr@chromium.org>
    212
  • trunk/Source/WebCore/ChangeLog

    r121127 r121128  
     12012-06-22  Kent Tamura  <tkent@chromium.org>
     2
     3        Selected option is not restored correctly
     4        https://bugs.webkit.org/show_bug.cgi?id=89623
     5
     6        Reviewed by Hajime Morita.
     7
     8        Details of the bug:
     9        We saved a state of a <select> element as a string of which length was
     10        the size of <select>'s children. e.g. If a <select> had five children
     11        and the second and the fifth items were selected, the state string was:
     12                ".X..X"
     13
     14        This didn't work well if the structure of the children was updated after
     15        parsing. For example,
     16        1. A page has the following <select> initially:
     17          <select multiple>
     18            <option>Banana
     19            <option>Lemon
     20            <option>Orange
     21            <option>Strawberry
     22          <select>
     23
     24        2. For some reasons, <option>Apple</option> is prepended to the children.
     25        3. Some items are selected.
     26        4. The page is unloaded. Selection state is saved.
     27        5. A user go back to the page again. A browser parses the page again.
     28        6. Try to restore the <select> state with the saved data at 4.
     29          But "Apple" is missing. The <select> has wrong selections.
     30
     31        Solution:
     32        We save the state as a set of selected values. If "Banana" and
     33        "Strawberry" are selected in the above <select>, we save two strings;
     34        "Banana" and "Strawberry", not ".X..X".
     35
     36        Test: fast/forms/select/select-state-restore.html
     37
     38        * html/HTMLSelectElement.cpp:
     39        (WebCore::HTMLSelectElement::saveFormControlState):
     40        Store selected value strings to a FormControlState object.
     41        (WebCore::HTMLSelectElement::searchOptionsForValue):
     42        A helper function to find an <option> with the specified value.
     43        (WebCore::HTMLSelectElement::restoreFormControlState):
     44        Clear all of selections, then select options with saved values.
     45        In order to avoid O(M x N) loop, we start searching at position we found
     46        the previous value.
     47        * html/HTMLSelectElement.h: Declare searchOptionsForValue.
     48        * html/FormController.cpp:
     49        (formStateSignature): Bump up the version because this is a incompatible
     50        change.
     51
    1522012-06-24  David Barr  <davidbarr@chromium.org>
    253
  • trunk/Source/WebCore/html/FormController.cpp

    r121032 r121128  
    8282    // attribute value of a form control. The following string literal should
    8383    // contain some characters which are rarely used for name attribute values.
    84     DEFINE_STATIC_LOCAL(String, signature, ("\n\r?% WebKit serialized form state version 2 \n\r=&"));
     84    DEFINE_STATIC_LOCAL(String, signature, ("\n\r?% WebKit serialized form state version 3 \n\r=&"));
    8585    return signature;
    8686}
  • trunk/Source/WebCore/html/HTMLSelectElement.cpp

    r121004 r121128  
    920920    const Vector<HTMLElement*>& items = listItems();
    921921    size_t length = items.size();
    922     StringBuilder builder;
    923     builder.reserveCapacity(length);
     922    FormControlState state;
    924923    for (unsigned i = 0; i < length; ++i) {
    925         HTMLElement* element = items[i];
    926         bool selected = element->hasTagName(optionTag) && toHTMLOptionElement(element)->selected();
    927         builder.append(selected ? 'X' : '.');
    928     }
    929     return FormControlState(builder.toString());
     924        if (!items[i]->hasTagName(optionTag))
     925            continue;
     926        HTMLOptionElement* option = toHTMLOptionElement(items[i]);
     927        if (!option->selected())
     928            continue;
     929        state.append(option->value());
     930        if (!multiple())
     931            break;
     932    }
     933    return state;
     934}
     935
     936size_t HTMLSelectElement::searchOptionsForValue(const String& value, size_t listIndexStart, size_t listIndexEnd) const
     937{
     938    const Vector<HTMLElement*>& items = listItems();
     939    size_t loopEndIndex = std::min(items.size(), listIndexEnd);
     940    for (size_t i = listIndexStart; i < loopEndIndex; ++i) {
     941        if (!items[i]->hasLocalName(optionTag))
     942            continue;
     943        if (static_cast<HTMLOptionElement*>(items[i])->value() == value)
     944            return i;
     945    }
     946    return notFound;
    930947}
    931948
     
    935952
    936953    const Vector<HTMLElement*>& items = listItems();
    937     size_t length = items.size();
    938 
    939     String mask = state[0];
    940     for (size_t i = 0; i < length; ++i) {
    941         HTMLElement* element = items[i];
    942         if (element->hasTagName(optionTag))
    943             toHTMLOptionElement(element)->setSelectedState(mask[i] == 'X');
     954    size_t itemsSize = items.size();
     955    if (!itemsSize)
     956        return;
     957
     958    for (size_t i = 0; i < itemsSize; ++i) {
     959        if (!items[i]->hasLocalName(optionTag))
     960            continue;
     961        static_cast<HTMLOptionElement*>(items[i])->setSelectedState(false);
     962    }
     963
     964    if (!multiple()) {
     965        size_t foundIndex = searchOptionsForValue(state[0], 0, itemsSize);
     966        if (foundIndex != notFound)
     967            toHTMLOptionElement(items[foundIndex])->setSelectedState(true);
     968    } else {
     969        size_t startIndex = 0;
     970        for (size_t i = 0; i < state.valueSize(); ++i) {
     971            const String& value = state[i];
     972            size_t foundIndex = searchOptionsForValue(value, startIndex, itemsSize);
     973            if (foundIndex == notFound)
     974                foundIndex = searchOptionsForValue(value, 0, startIndex);
     975            if (foundIndex == notFound)
     976                continue;
     977            toHTMLOptionElement(items[foundIndex])->setSelectedState(true);
     978            startIndex = foundIndex + 1;
     979        }
    944980    }
    945981
  • trunk/Source/WebCore/html/HTMLSelectElement.h

    r120679 r121128  
    165165    void listBoxDefaultEventHandler(Event*);
    166166    void setOptionsChangedOnRenderer();
     167    size_t searchOptionsForValue(const String&, size_t listIndexStart, size_t listIndexEnd) const;
    167168
    168169    enum SkipDirection {
Note: See TracChangeset for help on using the changeset viewer.