| OLD | NEW |
| 1 /* | 1 /* |
| 2 * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). | 2 * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). |
| 3 * Copyright (C) 1999 Lars Knoll (knoll@kde.org) | 3 * Copyright (C) 1999 Lars Knoll (knoll@kde.org) |
| 4 * (C) 1999 Antti Koivisto (koivisto@kde.org) | 4 * (C) 1999 Antti Koivisto (koivisto@kde.org) |
| 5 * (C) 2001 Dirk Mueller (mueller@kde.org) | 5 * (C) 2001 Dirk Mueller (mueller@kde.org) |
| 6 * Copyright (C) 2004, 2005, 2006, 2007, 2009, 2010, 2011 Apple Inc. All rights
reserved. | 6 * Copyright (C) 2004, 2005, 2006, 2007, 2009, 2010, 2011 Apple Inc. All rights
reserved. |
| 7 * (C) 2006 Alexey Proskuryakov (ap@nypop.com) | 7 * (C) 2006 Alexey Proskuryakov (ap@nypop.com) |
| 8 * Copyright (C) 2010 Google Inc. All rights reserved. | 8 * Copyright (C) 2010 Google Inc. All rights reserved. |
| 9 * Copyright (C) 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmo
bile.com/) | 9 * Copyright (C) 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmo
bile.com/) |
| 10 * | 10 * |
| (...skipping 127 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 138 | 138 |
| 139 // Bail out if this index is already the selected one, to avoid running | 139 // Bail out if this index is already the selected one, to avoid running |
| 140 // unnecessary JavaScript that can mess up autofill when there is no actual | 140 // unnecessary JavaScript that can mess up autofill when there is no actual |
| 141 // change (see https://bugs.webkit.org/show_bug.cgi?id=35256 and | 141 // change (see https://bugs.webkit.org/show_bug.cgi?id=35256 and |
| 142 // <rdar://7467917>). The selectOption function does not behave this way, | 142 // <rdar://7467917>). The selectOption function does not behave this way, |
| 143 // possibly because other callers need a change event even in cases where | 143 // possibly because other callers need a change event even in cases where |
| 144 // the selected option is not change. | 144 // the selected option is not change. |
| 145 if (optionIndex == selectedIndex()) | 145 if (optionIndex == selectedIndex()) |
| 146 return; | 146 return; |
| 147 | 147 |
| 148 selectOption(optionIndex, DeselectOtherOptions | MakeOptionDirty | (fireOnCh
angeNow ? DispatchInputAndChangeEvent : 0)); | 148 selectOption(item(optionIndex), DeselectOtherOptions | MakeOptionDirty | (fi
reOnChangeNow ? DispatchInputAndChangeEvent : 0)); |
| 149 } | 149 } |
| 150 | 150 |
| 151 bool HTMLSelectElement::hasPlaceholderLabelOption() const | 151 bool HTMLSelectElement::hasPlaceholderLabelOption() const |
| 152 { | 152 { |
| 153 // The select element has no placeholder label option if it has an attribute | 153 // The select element has no placeholder label option if it has an attribute |
| 154 // "multiple" specified or a display size of non-1. | 154 // "multiple" specified or a display size of non-1. |
| 155 // | 155 // |
| 156 // The condition "size() > 1" is not compliant with the HTML5 spec as of Dec | 156 // The condition "size() > 1" is not compliant with the HTML5 spec as of Dec |
| 157 // 3, 2010. "size() != 1" is correct. Using "size() > 1" here because | 157 // 3, 2010. "size() != 1" is correct. Using "size() > 1" here because |
| 158 // size() may be 0 in WebKit. See the discussion at | 158 // size() may be 0 in WebKit. See the discussion at |
| (...skipping 117 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 276 if (HTMLOptionElement* option = selectedOption()) | 276 if (HTMLOptionElement* option = selectedOption()) |
| 277 return option->value(); | 277 return option->value(); |
| 278 return ""; | 278 return ""; |
| 279 } | 279 } |
| 280 | 280 |
| 281 void HTMLSelectElement::setValue(const String &value, bool sendEvents) | 281 void HTMLSelectElement::setValue(const String &value, bool sendEvents) |
| 282 { | 282 { |
| 283 // We clear the previously selected option(s) when needed, to guarantee | 283 // We clear the previously selected option(s) when needed, to guarantee |
| 284 // calling setSelectedIndex() only once. | 284 // calling setSelectedIndex() only once. |
| 285 int optionIndex = 0; | 285 int optionIndex = 0; |
| 286 HTMLOptionElement* option = nullptr; |
| 286 if (value.isNull()) { | 287 if (value.isNull()) { |
| 287 optionIndex = -1; | 288 optionIndex = -1; |
| 288 } else { | 289 } else { |
| 289 // Find the option with value() matching the given parameter and make it | 290 // Find the option with value() matching the given parameter and make it |
| 290 // the current selection. | 291 // the current selection. |
| 291 for (auto& item : listItems()) { | 292 for (auto& item : listItems()) { |
| 292 if (!isHTMLOptionElement(item)) | 293 if (!isHTMLOptionElement(item)) |
| 293 continue; | 294 continue; |
| 294 if (toHTMLOptionElement(item)->value() == value) | 295 if (toHTMLOptionElement(item)->value() == value) { |
| 296 option = toHTMLOptionElement(item); |
| 295 break; | 297 break; |
| 298 } |
| 296 optionIndex++; | 299 optionIndex++; |
| 297 } | 300 } |
| 298 if (optionIndex >= static_cast<int>(listItems().size())) | 301 if (optionIndex >= static_cast<int>(listItems().size())) |
| 299 optionIndex = -1; | 302 optionIndex = -1; |
| 300 } | 303 } |
| 301 | 304 |
| 302 int previousSelectedIndex = selectedIndex(); | 305 int previousSelectedIndex = selectedIndex(); |
| 303 setSuggestedOption(nullptr); | 306 setSuggestedOption(nullptr); |
| 304 if (m_isAutofilledByPreview) | 307 if (m_isAutofilledByPreview) |
| 305 setAutofilled(false); | 308 setAutofilled(false); |
| 306 SelectOptionFlags flags = DeselectOtherOptions | MakeOptionDirty; | 309 SelectOptionFlags flags = DeselectOtherOptions | MakeOptionDirty; |
| 307 if (sendEvents) | 310 if (sendEvents) |
| 308 flags |= DispatchInputAndChangeEvent; | 311 flags |= DispatchInputAndChangeEvent; |
| 309 selectOption(optionIndex, flags); | 312 selectOption(option, flags); |
| 310 | 313 |
| 311 if (sendEvents && previousSelectedIndex != selectedIndex() && !usesMenuList(
)) | 314 if (sendEvents && previousSelectedIndex != selectedIndex() && !usesMenuList(
)) |
| 312 listBoxOnChange(); | 315 listBoxOnChange(); |
| 313 } | 316 } |
| 314 | 317 |
| 315 String HTMLSelectElement::suggestedValue() const | 318 String HTMLSelectElement::suggestedValue() const |
| 316 { | 319 { |
| 317 return m_suggestedOption ? m_suggestedOption->value() : ""; | 320 return m_suggestedOption ? m_suggestedOption->value() : ""; |
| 318 } | 321 } |
| 319 | 322 |
| (...skipping 553 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 873 currentElement = ElementTraversal::nextSkippingChildren(*currentElement,
this); | 876 currentElement = ElementTraversal::nextSkippingChildren(*currentElement,
this); |
| 874 } | 877 } |
| 875 } | 878 } |
| 876 | 879 |
| 877 void HTMLSelectElement::resetToDefaultSelection(ResetReason reason) | 880 void HTMLSelectElement::resetToDefaultSelection(ResetReason reason) |
| 878 { | 881 { |
| 879 // https://html.spec.whatwg.org/multipage/forms.html#ask-for-a-reset | 882 // https://html.spec.whatwg.org/multipage/forms.html#ask-for-a-reset |
| 880 if (multiple()) | 883 if (multiple()) |
| 881 return; | 884 return; |
| 882 HTMLOptionElement* firstEnabledOption = nullptr; | 885 HTMLOptionElement* firstEnabledOption = nullptr; |
| 883 int firstEnabledOptionIndex = -1; | |
| 884 HTMLOptionElement* lastSelectedOption = nullptr; | 886 HTMLOptionElement* lastSelectedOption = nullptr; |
| 885 bool didChange = false; | 887 bool didChange = false; |
| 886 int optionIndex = 0; | 888 int optionIndex = 0; |
| 887 // We can't use HTMLSelectElement::options here because this function is | 889 // We can't use HTMLSelectElement::options here because this function is |
| 888 // called in Node::insertedInto and Node::removedFrom before invalidating | 890 // called in Node::insertedInto and Node::removedFrom before invalidating |
| 889 // node collections. | 891 // node collections. |
| 890 for (const auto& option : optionList()) { | 892 for (const auto& option : optionList()) { |
| 891 if (option->selected()) { | 893 if (option->selected()) { |
| 892 if (lastSelectedOption) { | 894 if (lastSelectedOption) { |
| 893 lastSelectedOption->setSelectedState(false); | 895 lastSelectedOption->setSelectedState(false); |
| 894 didChange = true; | 896 didChange = true; |
| 895 } | 897 } |
| 896 lastSelectedOption = option; | 898 lastSelectedOption = option; |
| 897 } | 899 } |
| 898 if (!firstEnabledOption && !option->isDisabledFormControl()) { | 900 if (!firstEnabledOption && !option->isDisabledFormControl()) { |
| 899 firstEnabledOption = option; | 901 firstEnabledOption = option; |
| 900 firstEnabledOptionIndex = optionIndex; | |
| 901 if (reason == ResetReasonSelectedOptionRemoved) { | 902 if (reason == ResetReasonSelectedOptionRemoved) { |
| 902 // There must be no selected OPTIONs. | 903 // There must be no selected OPTIONs. |
| 903 break; | 904 break; |
| 904 } | 905 } |
| 905 } | 906 } |
| 906 ++optionIndex; | 907 ++optionIndex; |
| 907 } | 908 } |
| 908 if (!lastSelectedOption && m_size <= 1 && firstEnabledOption && !firstEnable
dOption->selected()) { | 909 if (!lastSelectedOption && m_size <= 1 && firstEnabledOption && !firstEnable
dOption->selected()) { |
| 909 selectOption(firstEnabledOption, firstEnabledOptionIndex, reason == Rese
tReasonSelectedOptionRemoved ? 0 : DeselectOtherOptions); | 910 selectOption(firstEnabledOption, reason == ResetReasonSelectedOptionRemo
ved ? 0 : DeselectOtherOptions); |
| 910 lastSelectedOption = firstEnabledOption; | 911 lastSelectedOption = firstEnabledOption; |
| 911 didChange = true; | 912 didChange = true; |
| 912 } | 913 } |
| 913 if (didChange) | 914 if (didChange) |
| 914 setNeedsValidityCheck(); | 915 setNeedsValidityCheck(); |
| 915 m_lastOnChangeOption = lastSelectedOption; | 916 m_lastOnChangeOption = lastSelectedOption; |
| 916 } | 917 } |
| 917 | 918 |
| 918 HTMLOptionElement* HTMLSelectElement::selectedOption() const | 919 HTMLOptionElement* HTMLSelectElement::selectedOption() const |
| 919 { | 920 { |
| (...skipping 15 matching lines...) Expand all Loading... |
| 935 if (toHTMLOptionElement(*element).selected()) | 936 if (toHTMLOptionElement(*element).selected()) |
| 936 return index; | 937 return index; |
| 937 ++index; | 938 ++index; |
| 938 } | 939 } |
| 939 | 940 |
| 940 return -1; | 941 return -1; |
| 941 } | 942 } |
| 942 | 943 |
| 943 void HTMLSelectElement::setSelectedIndex(int index) | 944 void HTMLSelectElement::setSelectedIndex(int index) |
| 944 { | 945 { |
| 945 selectOption(index, DeselectOtherOptions | MakeOptionDirty); | 946 selectOption(item(index), DeselectOtherOptions | MakeOptionDirty); |
| 946 } | 947 } |
| 947 | 948 |
| 948 void HTMLSelectElement::setSuggestedOption(HTMLOptionElement* option) | 949 void HTMLSelectElement::setSuggestedOption(HTMLOptionElement* option) |
| 949 { | 950 { |
| 950 if (m_suggestedOption == option) | 951 if (m_suggestedOption == option) |
| 951 return; | 952 return; |
| 952 m_suggestedOption = option; | 953 m_suggestedOption = option; |
| 953 | 954 |
| 954 if (LayoutObject* layoutObject = this->layoutObject()) { | 955 if (LayoutObject* layoutObject = this->layoutObject()) { |
| 955 layoutObject->updateFromElement(); | 956 layoutObject->updateFromElement(); |
| (...skipping 88 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1044 setNeedsValidityCheck(); | 1045 setNeedsValidityCheck(); |
| 1045 m_lastOnChangeSelection.clear(); | 1046 m_lastOnChangeSelection.clear(); |
| 1046 } | 1047 } |
| 1047 | 1048 |
| 1048 void HTMLSelectElement::hrInsertedOrRemoved(HTMLHRElement& hr) | 1049 void HTMLSelectElement::hrInsertedOrRemoved(HTMLHRElement& hr) |
| 1049 { | 1050 { |
| 1050 setRecalcListItems(hr); | 1051 setRecalcListItems(hr); |
| 1051 m_lastOnChangeSelection.clear(); | 1052 m_lastOnChangeSelection.clear(); |
| 1052 } | 1053 } |
| 1053 | 1054 |
| 1054 void HTMLSelectElement::selectOption(int optionIndex, SelectOptionFlags flags) | |
| 1055 { | |
| 1056 selectOption(optionIndex < 0 ? nullptr : item(optionIndex), flags); | |
| 1057 } | |
| 1058 | |
| 1059 void HTMLSelectElement::selectOption(HTMLOptionElement* option, SelectOptionFlag
s flags) | |
| 1060 { | |
| 1061 selectOption(option, option ? option->index() : -1, flags); | |
| 1062 } | |
| 1063 | |
| 1064 // TODO(tkent): This function is not efficient. It contains multiple O(N) | 1055 // TODO(tkent): This function is not efficient. It contains multiple O(N) |
| 1065 // operations. crbug.com/577989. | 1056 // operations. crbug.com/577989. |
| 1066 void HTMLSelectElement::selectOption(HTMLOptionElement* element, int optionIndex
, SelectOptionFlags flags) | 1057 void HTMLSelectElement::selectOption(HTMLOptionElement* element, SelectOptionFla
gs flags) |
| 1067 { | 1058 { |
| 1068 TRACE_EVENT0("blink", "HTMLSelectElement::selectOption"); | 1059 TRACE_EVENT0("blink", "HTMLSelectElement::selectOption"); |
| 1069 ASSERT((!element && optionIndex < 0) || (element && optionIndex >= 0)); | |
| 1070 | 1060 |
| 1071 // selectedIndex() is O(N). | 1061 // selectedOption() is O(N). |
| 1072 if (isAutofilled() && selectedIndex() != optionIndex) | 1062 if (isAutofilled() && selectedOption() != element) |
| 1073 setAutofilled(false); | 1063 setAutofilled(false); |
| 1074 | 1064 |
| 1075 if (element) { | 1065 if (element) { |
| 1076 element->setSelectedState(true); | 1066 element->setSelectedState(true); |
| 1077 if (flags & MakeOptionDirty) | 1067 if (flags & MakeOptionDirty) |
| 1078 element->setDirty(true); | 1068 element->setDirty(true); |
| 1079 } | 1069 } |
| 1080 | 1070 |
| 1081 // deselectItemsWithoutValidation() is O(N). | 1071 // deselectItemsWithoutValidation() is O(N). |
| 1082 if (flags & DeselectOtherOptions) | 1072 if (flags & DeselectOtherOptions) |
| (...skipping 196 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1279 } | 1269 } |
| 1280 } | 1270 } |
| 1281 } | 1271 } |
| 1282 | 1272 |
| 1283 setNeedsValidityCheck(); | 1273 setNeedsValidityCheck(); |
| 1284 } | 1274 } |
| 1285 | 1275 |
| 1286 void HTMLSelectElement::parseMultipleAttribute(const AtomicString& value) | 1276 void HTMLSelectElement::parseMultipleAttribute(const AtomicString& value) |
| 1287 { | 1277 { |
| 1288 bool oldMultiple = m_multiple; | 1278 bool oldMultiple = m_multiple; |
| 1289 int oldSelectedIndex = selectedIndex(); | 1279 HTMLOptionElement* oldSelectedOption = selectedOption(); |
| 1290 m_multiple = !value.isNull(); | 1280 m_multiple = !value.isNull(); |
| 1291 setNeedsValidityCheck(); | 1281 setNeedsValidityCheck(); |
| 1292 lazyReattachIfAttached(); | 1282 lazyReattachIfAttached(); |
| 1293 // Restore selectedIndex after changing the multiple flag to preserve | 1283 // Restore selectedIndex after changing the multiple flag to preserve |
| 1294 // selection as single-line and multi-line has different defaults. | 1284 // selection as single-line and multi-line has different defaults. |
| 1295 if (oldMultiple != m_multiple) { | 1285 if (oldMultiple != m_multiple) { |
| 1296 // Preserving the first selection is compatible with Firefox and | 1286 // Preserving the first selection is compatible with Firefox and |
| 1297 // WebKit. However Edge seems to "ask for a reset" simply. As of 2016 | 1287 // WebKit. However Edge seems to "ask for a reset" simply. As of 2016 |
| 1298 // March, the HTML specification says nothing about this. | 1288 // March, the HTML specification says nothing about this. |
| 1299 if (oldSelectedIndex >= 0) | 1289 if (oldSelectedOption) |
| 1300 selectOption(oldSelectedIndex, DeselectOtherOptions); | 1290 selectOption(oldSelectedOption, DeselectOtherOptions); |
| 1301 else | 1291 else |
| 1302 resetToDefaultSelection(); | 1292 resetToDefaultSelection(); |
| 1303 } | 1293 } |
| 1304 } | 1294 } |
| 1305 | 1295 |
| 1306 void HTMLSelectElement::appendToFormData(FormData& formData) | 1296 void HTMLSelectElement::appendToFormData(FormData& formData) |
| 1307 { | 1297 { |
| 1308 const AtomicString& name = this->name(); | 1298 const AtomicString& name = this->name(); |
| 1309 if (name.isEmpty()) | 1299 if (name.isEmpty()) |
| 1310 return; | 1300 return; |
| (...skipping 482 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1793 if (!isHTMLOptionElement(*element) || toHTMLOptionElement(element)->isDisabl
edFormControl()) | 1783 if (!isHTMLOptionElement(*element) || toHTMLOptionElement(element)->isDisabl
edFormControl()) |
| 1794 return String(); | 1784 return String(); |
| 1795 return toHTMLOptionElement(element)->displayLabel(); | 1785 return toHTMLOptionElement(element)->displayLabel(); |
| 1796 } | 1786 } |
| 1797 | 1787 |
| 1798 void HTMLSelectElement::typeAheadFind(KeyboardEvent* event) | 1788 void HTMLSelectElement::typeAheadFind(KeyboardEvent* event) |
| 1799 { | 1789 { |
| 1800 int index = m_typeAhead.handleEvent(event, TypeAhead::MatchPrefix | TypeAhea
d::CycleFirstChar); | 1790 int index = m_typeAhead.handleEvent(event, TypeAhead::MatchPrefix | TypeAhea
d::CycleFirstChar); |
| 1801 if (index < 0) | 1791 if (index < 0) |
| 1802 return; | 1792 return; |
| 1803 selectOption(listToOptionIndex(index), DeselectOtherOptions | MakeOptionDirt
y | DispatchInputAndChangeEvent); | 1793 HTMLOptionElement* option = nullptr; |
| 1794 if (static_cast<size_t>(index) < listItems().size() && isHTMLOptionElement(l
istItems()[index])) |
| 1795 option = toHTMLOptionElement(listItems()[index]); |
| 1796 selectOption(option, DeselectOtherOptions | MakeOptionDirty | DispatchInputA
ndChangeEvent); |
| 1804 if (!usesMenuList()) | 1797 if (!usesMenuList()) |
| 1805 listBoxOnChange(); | 1798 listBoxOnChange(); |
| 1806 } | 1799 } |
| 1807 | 1800 |
| 1808 void HTMLSelectElement::accessKeySetSelectedIndex(int index) | 1801 void HTMLSelectElement::accessKeySetSelectedIndex(int index) |
| 1809 { | 1802 { |
| 1810 // First bring into focus the list box. | 1803 // First bring into focus the list box. |
| 1811 if (!focused()) | 1804 if (!focused()) |
| 1812 accessKeyAction(false); | 1805 accessKeyAction(false); |
| 1813 | 1806 |
| 1814 const ListItems& items = listItems(); | 1807 HTMLOptionElement* option = item(index); |
| 1815 int listIndex = optionToListIndex(index); | 1808 if (!option) |
| 1816 if (listIndex < 0) | |
| 1817 return; | |
| 1818 HTMLElement& element = *items[listIndex]; | |
| 1819 if (!isHTMLOptionElement(element)) | |
| 1820 return; | 1809 return; |
| 1821 EventQueueScope scope; | 1810 EventQueueScope scope; |
| 1822 // If this index is already selected, unselect. otherwise update the | 1811 // If this index is already selected, unselect. otherwise update the |
| 1823 // selected index. | 1812 // selected index. |
| 1824 SelectOptionFlags flags = DispatchInputAndChangeEvent | (multiple() ? 0 : De
selectOtherOptions); | 1813 SelectOptionFlags flags = DispatchInputAndChangeEvent | (multiple() ? 0 : De
selectOtherOptions); |
| 1825 if (toHTMLOptionElement(element).selected()) { | 1814 if (option->selected()) { |
| 1826 if (usesMenuList()) | 1815 if (usesMenuList()) |
| 1827 selectOption(-1, flags); | 1816 selectOption(nullptr, flags); |
| 1828 else | 1817 else |
| 1829 toHTMLOptionElement(element).setSelectedState(false); | 1818 option->setSelectedState(false); |
| 1830 } else { | 1819 } else { |
| 1831 selectOption(index, flags); | 1820 selectOption(option, flags); |
| 1832 } | 1821 } |
| 1833 toHTMLOptionElement(element).setDirty(true); | 1822 option->setDirty(true); |
| 1834 if (usesMenuList()) | 1823 if (usesMenuList()) |
| 1835 return; | 1824 return; |
| 1836 listBoxOnChange(); | 1825 listBoxOnChange(); |
| 1837 scrollToSelection(); | 1826 scrollToSelection(); |
| 1838 } | 1827 } |
| 1839 | 1828 |
| 1840 unsigned HTMLSelectElement::length() const | 1829 unsigned HTMLSelectElement::length() const |
| 1841 { | 1830 { |
| 1842 unsigned options = 0; | 1831 unsigned options = 0; |
| 1843 for (auto& item : listItems()) { | 1832 for (auto& item : listItems()) { |
| (...skipping 294 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2138 } | 2127 } |
| 2139 | 2128 |
| 2140 void HTMLSelectElement::didMutateSubtree() | 2129 void HTMLSelectElement::didMutateSubtree() |
| 2141 { | 2130 { |
| 2142 DCHECK(popupIsVisible()); | 2131 DCHECK(popupIsVisible()); |
| 2143 DCHECK(m_popup); | 2132 DCHECK(m_popup); |
| 2144 m_popup->updateFromElement(PopupMenu::ByDOMChange); | 2133 m_popup->updateFromElement(PopupMenu::ByDOMChange); |
| 2145 } | 2134 } |
| 2146 | 2135 |
| 2147 } // namespace blink | 2136 } // namespace blink |
| OLD | NEW |