Chromium Code Reviews| 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 626 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 637 // selection pivots around this anchor index. | 637 // selection pivots around this anchor index. |
| 638 // Example: | 638 // Example: |
| 639 // 1. Press the mouse button on the second OPTION | 639 // 1. Press the mouse button on the second OPTION |
| 640 // m_activeSelectionAnchorIndex = 1 | 640 // m_activeSelectionAnchorIndex = 1 |
| 641 // 2. Drag the mouse pointer onto the fifth OPTION | 641 // 2. Drag the mouse pointer onto the fifth OPTION |
| 642 // m_activeSelectionEndIndex = 4, options at 1-4 indices are selected. | 642 // m_activeSelectionEndIndex = 4, options at 1-4 indices are selected. |
| 643 // 3. Drag the mouse pointer onto the fourth OPTION | 643 // 3. Drag the mouse pointer onto the fourth OPTION |
| 644 // m_activeSelectionEndIndex = 3, options at 1-3 indices are selected. | 644 // m_activeSelectionEndIndex = 3, options at 1-3 indices are selected. |
| 645 // updateListBoxSelection needs to clear selection of the fifth OPTION. | 645 // updateListBoxSelection needs to clear selection of the fifth OPTION. |
| 646 m_cachedStateForActiveSelection.resize(0); | 646 m_cachedStateForActiveSelection.resize(0); |
| 647 for (auto& element : listItems()) { | 647 for (const auto& option : optionList()) { |
| 648 m_cachedStateForActiveSelection.append(isHTMLOptionElement(*element) && toHTMLOptionElement(element)->selected()); | 648 m_cachedStateForActiveSelection.append(option->selected()); |
| 649 } | 649 } |
| 650 } | 650 } |
| 651 | 651 |
| 652 void HTMLSelectElement::setActiveSelectionEnd(HTMLOptionElement* option) | 652 void HTMLSelectElement::setActiveSelectionEnd(HTMLOptionElement* option) |
| 653 { | 653 { |
| 654 m_activeSelectionEnd = option; | 654 m_activeSelectionEnd = option; |
| 655 } | 655 } |
| 656 | 656 |
| 657 void HTMLSelectElement::updateListBoxSelection(bool deselectOtherOptions, bool s croll) | 657 void HTMLSelectElement::updateListBoxSelection(bool deselectOtherOptions, bool s croll) |
| 658 { | 658 { |
| 659 ASSERT(layoutObject()); | 659 ASSERT(layoutObject()); |
| 660 ASSERT(layoutObject()->isListBox() || m_multiple); | 660 ASSERT(layoutObject()->isListBox() || m_multiple); |
| 661 | 661 |
| 662 int activeSelectionAnchorIndex = m_activeSelectionAnchor ? m_activeSelection Anchor->listIndex() : -1; | 662 int activeSelectionAnchorIndex = m_activeSelectionAnchor ? m_activeSelection Anchor->index() : -1; |
| 663 int activeSelectionEndIndex = m_activeSelectionEnd ? m_activeSelectionEnd->l istIndex() : -1; | 663 int activeSelectionEndIndex = m_activeSelectionEnd ? m_activeSelectionEnd->i ndex() : -1; |
| 664 int start = std::min(activeSelectionAnchorIndex, activeSelectionEndIndex); | 664 int start = std::min(activeSelectionAnchorIndex, activeSelectionEndIndex); |
| 665 int end = std::max(activeSelectionAnchorIndex, activeSelectionEndIndex); | 665 int end = std::max(activeSelectionAnchorIndex, activeSelectionEndIndex); |
| 666 | 666 |
| 667 const ListItems& items = listItems(); | 667 int i = 0; |
| 668 for (int i = 0; i < static_cast<int>(items.size()); ++i) { | 668 for (const auto& option : optionList()) { |
| 669 if (!isHTMLOptionElement(*items[i])) | 669 if (option->isDisabledFormControl() || !option->layoutObject()) { |
| 670 ++i; | |
| 670 continue; | 671 continue; |
| 671 HTMLOptionElement& option = toHTMLOptionElement(*items[i]); | 672 } |
| 672 if (option.isDisabledFormControl() || !option.layoutObject()) | |
| 673 continue; | |
| 674 if (i >= start && i <= end) { | 673 if (i >= start && i <= end) { |
| 675 option.setSelectedState(m_activeSelectionState); | 674 option->setSelectedState(m_activeSelectionState); |
| 676 option.setDirty(true); | 675 option->setDirty(true); |
| 677 } else if (deselectOtherOptions || i >= static_cast<int>(m_cachedStateFo rActiveSelection.size())) { | 676 } else if (deselectOtherOptions || i >= static_cast<int>(m_cachedStateFo rActiveSelection.size())) { |
| 678 option.setSelectedState(false); | 677 option->setSelectedState(false); |
| 679 option.setDirty(true); | 678 option->setDirty(true); |
| 680 } else { | 679 } else { |
| 681 option.setSelectedState(m_cachedStateForActiveSelection[i]); | 680 option->setSelectedState(m_cachedStateForActiveSelection[i]); |
| 682 } | 681 } |
| 682 ++i; | |
| 683 } | 683 } |
| 684 | 684 |
| 685 setNeedsValidityCheck(); | 685 setNeedsValidityCheck(); |
| 686 if (scroll) | 686 if (scroll) |
| 687 scrollToSelection(); | 687 scrollToSelection(); |
| 688 notifyFormStateChanged(); | 688 notifyFormStateChanged(); |
| 689 } | 689 } |
| 690 | 690 |
| 691 void HTMLSelectElement::listBoxOnChange() | 691 void HTMLSelectElement::listBoxOnChange() |
| 692 { | 692 { |
| (...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 763 | 763 |
| 764 return m_listItems; | 764 return m_listItems; |
| 765 } | 765 } |
| 766 | 766 |
| 767 void HTMLSelectElement::invalidateSelectedItems() | 767 void HTMLSelectElement::invalidateSelectedItems() |
| 768 { | 768 { |
| 769 if (HTMLCollection* collection = cachedCollection<HTMLCollection>(SelectedOp tions)) | 769 if (HTMLCollection* collection = cachedCollection<HTMLCollection>(SelectedOp tions)) |
| 770 collection->invalidateCache(); | 770 collection->invalidateCache(); |
| 771 } | 771 } |
| 772 | 772 |
| 773 void HTMLSelectElement::setRecalcListItems(HTMLElement& subject) | 773 void HTMLSelectElement::setRecalcListItems() |
| 774 { | 774 { |
| 775 // FIXME: This function does a bunch of confusing things depending on if it | 775 // FIXME: This function does a bunch of confusing things depending on if it |
| 776 // is in the document or not. | 776 // is in the document or not. |
| 777 | 777 |
| 778 bool shouldRecalc = true; | 778 m_shouldRecalcListItems = true; |
| 779 if (!m_shouldRecalcListItems && !isHTMLOptGroupElement(subject)) { | |
| 780 if (firstChild() == &subject) { | |
| 781 // The subject was prepended. This doesn't handle elements in an | |
| 782 // OPTGROUP. | |
| 783 DCHECK(m_listItems.size() == 0 || m_listItems[0] != &subject); | |
| 784 m_listItems.prepend(&subject); | |
| 785 shouldRecalc = false; | |
| 786 } else if (lastChild() == &subject) { | |
| 787 // The subject was appended. This doesn't handle elements in an | |
| 788 // OPTGROUP. | |
| 789 DCHECK(m_listItems.size() == 0 || m_listItems.last() != &subject); | |
| 790 m_listItems.append(&subject); | |
| 791 shouldRecalc = false; | |
| 792 } else if (!subject.isDescendantOf(this)) { | |
| 793 // |subject| was removed from this. This logic works well with | |
| 794 // SELECT children and OPTGROUP children. | |
| 795 | |
| 796 // m_listItems might be empty, or might not have the OPTION. | |
| 797 // 1. Remove an OPTGROUP with OPTION children from a SELECT. | |
| 798 // 2. This function is called for the OPTGROUP removal. | |
| 799 // 3. m_shouldRecalcListItems becomes true. | |
| 800 // 4. recalcListItems() happens. m_listItems has no OPTGROUP and | |
| 801 // no its children. m_shouldRecalcListItems becomes false. | |
| 802 // 5. This function is called for the removal of an OPTION child | |
| 803 // of the OPTGROUP. | |
| 804 if (m_listItems.size() > 0) { | |
| 805 size_t index; | |
| 806 // Avoid Vector::find() in typical cases. | |
| 807 if (m_listItems.first() == &subject) | |
| 808 index = 0; | |
| 809 else if (m_listItems.last() == &subject) | |
| 810 index = m_listItems.size() - 1; | |
| 811 else | |
| 812 index = m_listItems.find(&subject); | |
| 813 if (index != WTF::kNotFound) { | |
| 814 m_listItems.remove(index); | |
| 815 shouldRecalc = false; | |
| 816 } | |
| 817 } | |
| 818 } | |
| 819 } | |
| 820 m_shouldRecalcListItems = shouldRecalc; | |
| 821 | 779 |
| 822 setOptionsChangedOnLayoutObject(); | 780 setOptionsChangedOnLayoutObject(); |
| 823 if (!inShadowIncludingDocument()) { | 781 if (!inShadowIncludingDocument()) { |
| 824 if (HTMLOptionsCollection* collection = cachedCollection<HTMLOptionsColl ection>(SelectOptions)) | 782 if (HTMLOptionsCollection* collection = cachedCollection<HTMLOptionsColl ection>(SelectOptions)) |
| 825 collection->invalidateCache(); | 783 collection->invalidateCache(); |
| 826 invalidateSelectedItems(); | 784 invalidateSelectedItems(); |
| 827 } | 785 } |
| 828 | 786 |
| 829 if (layoutObject()) { | 787 if (layoutObject()) { |
| 830 if (AXObjectCache* cache = layoutObject()->document().existingAXObjectCa che()) | 788 if (AXObjectCache* cache = layoutObject()->document().existingAXObjectCa che()) |
| (...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 911 lastSelectedOption = firstEnabledOption; | 869 lastSelectedOption = firstEnabledOption; |
| 912 didChange = true; | 870 didChange = true; |
| 913 } | 871 } |
| 914 if (didChange) | 872 if (didChange) |
| 915 setNeedsValidityCheck(); | 873 setNeedsValidityCheck(); |
| 916 m_lastOnChangeOption = lastSelectedOption; | 874 m_lastOnChangeOption = lastSelectedOption; |
| 917 } | 875 } |
| 918 | 876 |
| 919 HTMLOptionElement* HTMLSelectElement::selectedOption() const | 877 HTMLOptionElement* HTMLSelectElement::selectedOption() const |
| 920 { | 878 { |
| 921 for (const auto& element : listItems()) { | 879 for (const auto option : optionList()) { |
|
keishi
2016/07/11 02:00:39
&?
tkent
2016/07/11 02:12:32
& is not used intentionally. If we add & here, com
| |
| 922 if (isHTMLOptionElement(*element) && toHTMLOptionElement(*element).selec ted()) | 880 if (option->selected()) |
| 923 return toHTMLOptionElement(element); | 881 return option; |
| 924 } | 882 } |
| 925 return nullptr; | 883 return nullptr; |
| 926 } | 884 } |
| 927 | 885 |
| 928 int HTMLSelectElement::selectedIndex() const | 886 int HTMLSelectElement::selectedIndex() const |
| 929 { | 887 { |
| 930 unsigned index = 0; | 888 unsigned index = 0; |
| 931 | 889 |
| 932 // Return the number of the first option selected. | 890 // Return the number of the first option selected. |
| 933 for (auto& element : listItems()) { | 891 for (auto& element : listItems()) { |
| (...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 997 selectOption(option, multiple() ? 0 : DeselectOtherOptions); | 955 selectOption(option, multiple() ? 0 : DeselectOtherOptions); |
| 998 else if (!usesMenuList() || multiple()) | 956 else if (!usesMenuList() || multiple()) |
| 999 selectOption(nullptr, multiple() ? 0 : DeselectOtherOptions); | 957 selectOption(nullptr, multiple() ? 0 : DeselectOtherOptions); |
| 1000 else | 958 else |
| 1001 selectOption(nextSelectableOption(nullptr), DeselectOtherOptions); | 959 selectOption(nextSelectableOption(nullptr), DeselectOtherOptions); |
| 1002 } | 960 } |
| 1003 | 961 |
| 1004 void HTMLSelectElement::optionInserted(HTMLOptionElement& option, bool optionIsS elected) | 962 void HTMLSelectElement::optionInserted(HTMLOptionElement& option, bool optionIsS elected) |
| 1005 { | 963 { |
| 1006 ASSERT(option.ownerSelectElement() == this); | 964 ASSERT(option.ownerSelectElement() == this); |
| 1007 setRecalcListItems(option); | 965 setRecalcListItems(); |
| 1008 if (optionIsSelected) { | 966 if (optionIsSelected) { |
| 1009 selectOption(&option, multiple() ? 0 : DeselectOtherOptions); | 967 selectOption(&option, multiple() ? 0 : DeselectOtherOptions); |
| 1010 } else { | 968 } else { |
| 1011 // No need to reset if we already have a selected option. | 969 // No need to reset if we already have a selected option. |
| 1012 if (!m_lastOnChangeOption) | 970 if (!m_lastOnChangeOption) |
| 1013 resetToDefaultSelection(); | 971 resetToDefaultSelection(); |
| 1014 } | 972 } |
| 1015 setNeedsValidityCheck(); | 973 setNeedsValidityCheck(); |
| 1016 m_lastOnChangeSelection.clear(); | 974 m_lastOnChangeSelection.clear(); |
| 1017 } | 975 } |
| 1018 | 976 |
| 1019 void HTMLSelectElement::optionRemoved(HTMLOptionElement& option) | 977 void HTMLSelectElement::optionRemoved(HTMLOptionElement& option) |
| 1020 { | 978 { |
| 1021 setRecalcListItems(option); | 979 setRecalcListItems(); |
| 1022 if (option.selected()) | 980 if (option.selected()) |
| 1023 resetToDefaultSelection(ResetReasonSelectedOptionRemoved); | 981 resetToDefaultSelection(ResetReasonSelectedOptionRemoved); |
| 1024 else if (!m_lastOnChangeOption) | 982 else if (!m_lastOnChangeOption) |
| 1025 resetToDefaultSelection(); | 983 resetToDefaultSelection(); |
| 1026 if (m_lastOnChangeOption == &option) | 984 if (m_lastOnChangeOption == &option) |
| 1027 m_lastOnChangeOption.clear(); | 985 m_lastOnChangeOption.clear(); |
| 1028 if (m_optionToScrollTo == &option) | 986 if (m_optionToScrollTo == &option) |
| 1029 m_optionToScrollTo.clear(); | 987 m_optionToScrollTo.clear(); |
| 1030 if (m_activeSelectionAnchor == &option) | 988 if (m_activeSelectionAnchor == &option) |
| 1031 m_activeSelectionAnchor.clear(); | 989 m_activeSelectionAnchor.clear(); |
| 1032 if (m_activeSelectionEnd == &option) | 990 if (m_activeSelectionEnd == &option) |
| 1033 m_activeSelectionEnd.clear(); | 991 m_activeSelectionEnd.clear(); |
| 1034 if (m_suggestedOption == &option) | 992 if (m_suggestedOption == &option) |
| 1035 setSuggestedOption(nullptr); | 993 setSuggestedOption(nullptr); |
| 1036 if (option.selected()) | 994 if (option.selected()) |
| 1037 setAutofilled(false); | 995 setAutofilled(false); |
| 1038 setNeedsValidityCheck(); | 996 setNeedsValidityCheck(); |
| 1039 m_lastOnChangeSelection.clear(); | 997 m_lastOnChangeSelection.clear(); |
| 1040 } | 998 } |
| 1041 | 999 |
| 1042 void HTMLSelectElement::optGroupInsertedOrRemoved(HTMLOptGroupElement& optgroup) | 1000 void HTMLSelectElement::optGroupInsertedOrRemoved(HTMLOptGroupElement& optgroup) |
| 1043 { | 1001 { |
| 1044 setRecalcListItems(optgroup); | 1002 setRecalcListItems(); |
| 1045 setNeedsValidityCheck(); | 1003 setNeedsValidityCheck(); |
| 1046 m_lastOnChangeSelection.clear(); | 1004 m_lastOnChangeSelection.clear(); |
| 1047 } | 1005 } |
| 1048 | 1006 |
| 1049 void HTMLSelectElement::hrInsertedOrRemoved(HTMLHRElement& hr) | 1007 void HTMLSelectElement::hrInsertedOrRemoved(HTMLHRElement& hr) |
| 1050 { | 1008 { |
| 1051 setRecalcListItems(hr); | 1009 setRecalcListItems(); |
| 1052 m_lastOnChangeSelection.clear(); | 1010 m_lastOnChangeSelection.clear(); |
| 1053 } | 1011 } |
| 1054 | 1012 |
| 1055 // TODO(tkent): This function is not efficient. It contains multiple O(N) | 1013 // TODO(tkent): This function is not efficient. It contains multiple O(N) |
| 1056 // operations. crbug.com/577989. | 1014 // operations. crbug.com/577989. |
| 1057 void HTMLSelectElement::selectOption(HTMLOptionElement* element, SelectOptionFla gs flags) | 1015 void HTMLSelectElement::selectOption(HTMLOptionElement* element, SelectOptionFla gs flags) |
| 1058 { | 1016 { |
| 1059 TRACE_EVENT0("blink", "HTMLSelectElement::selectOption"); | 1017 TRACE_EVENT0("blink", "HTMLSelectElement::selectOption"); |
| 1060 | 1018 |
| 1061 // selectedOption() is O(N). | 1019 // selectedOption() is O(N). |
| (...skipping 105 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1167 m_typeAhead.resetSession(); | 1125 m_typeAhead.resetSession(); |
| 1168 // We only need to fire change events here for menu lists, because we fire | 1126 // We only need to fire change events here for menu lists, because we fire |
| 1169 // change events for list boxes whenever the selection change is actually | 1127 // change events for list boxes whenever the selection change is actually |
| 1170 // made. This matches other browsers' behavior. | 1128 // made. This matches other browsers' behavior. |
| 1171 if (usesMenuList()) | 1129 if (usesMenuList()) |
| 1172 dispatchInputAndChangeEventForMenuList(); | 1130 dispatchInputAndChangeEventForMenuList(); |
| 1173 m_lastOnChangeSelection.clear(); | 1131 m_lastOnChangeSelection.clear(); |
| 1174 HTMLFormControlElementWithState::dispatchBlurEvent(newFocusedElement, type, sourceCapabilities); | 1132 HTMLFormControlElementWithState::dispatchBlurEvent(newFocusedElement, type, sourceCapabilities); |
| 1175 } | 1133 } |
| 1176 | 1134 |
| 1177 void HTMLSelectElement::deselectItemsWithoutValidation(HTMLElement* excludeEleme nt) | 1135 void HTMLSelectElement::deselectItemsWithoutValidation(HTMLOptionElement* exclud eElement) |
| 1178 { | 1136 { |
| 1179 if (!multiple() && usesMenuList() && m_lastOnChangeOption && m_lastOnChangeO ption != excludeElement) { | 1137 if (!multiple() && usesMenuList() && m_lastOnChangeOption && m_lastOnChangeO ption != excludeElement) { |
| 1180 m_lastOnChangeOption->setSelectedState(false); | 1138 m_lastOnChangeOption->setSelectedState(false); |
| 1181 return; | 1139 return; |
| 1182 } | 1140 } |
| 1183 for (auto& element : listItems()) { | 1141 for (const auto& option : optionList()) { |
| 1184 if (element != excludeElement && isHTMLOptionElement(*element)) | 1142 if (option != excludeElement) |
| 1185 toHTMLOptionElement(element)->setSelectedState(false); | 1143 option->setSelectedState(false); |
| 1186 } | 1144 } |
| 1187 } | 1145 } |
| 1188 | 1146 |
| 1189 FormControlState HTMLSelectElement::saveFormControlState() const | 1147 FormControlState HTMLSelectElement::saveFormControlState() const |
| 1190 { | 1148 { |
| 1191 const ListItems& items = listItems(); | 1149 const ListItems& items = listItems(); |
| 1192 size_t length = items.size(); | 1150 size_t length = items.size(); |
| 1193 FormControlState state; | 1151 FormControlState state; |
| 1194 for (unsigned i = 0; i < length; ++i) { | 1152 for (unsigned i = 0; i < length; ++i) { |
| 1195 if (!isHTMLOptionElement(*items[i])) | 1153 if (!isHTMLOptionElement(*items[i])) |
| (...skipping 934 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 2130 } | 2088 } |
| 2131 | 2089 |
| 2132 void HTMLSelectElement::didMutateSubtree() | 2090 void HTMLSelectElement::didMutateSubtree() |
| 2133 { | 2091 { |
| 2134 DCHECK(popupIsVisible()); | 2092 DCHECK(popupIsVisible()); |
| 2135 DCHECK(m_popup); | 2093 DCHECK(m_popup); |
| 2136 m_popup->updateFromElement(PopupMenu::ByDOMChange); | 2094 m_popup->updateFromElement(PopupMenu::ByDOMChange); |
| 2137 } | 2095 } |
| 2138 | 2096 |
| 2139 } // namespace blink | 2097 } // namespace blink |
| OLD | NEW |