#include "config.h"
#include "core/html/HTMLSelectElement.h"
-#include "HTMLNames.h"
-#include "bindings/v8/ExceptionMessages.h"
-#include "bindings/v8/ExceptionState.h"
-#include "bindings/v8/ExceptionStatePlaceholder.h"
+#include "bindings/core/v8/ExceptionMessages.h"
+#include "bindings/core/v8/ExceptionState.h"
+#include "bindings/core/v8/ExceptionStatePlaceholder.h"
+#include "core/HTMLNames.h"
#include "core/accessibility/AXObjectCache.h"
#include "core/dom/Attribute.h"
#include "core/dom/ElementTraversal.h"
+#include "core/dom/NodeListsNodeData.h"
+#include "core/dom/NodeRenderStyle.h"
#include "core/dom/NodeTraversal.h"
+#include "core/events/GestureEvent.h"
#include "core/events/KeyboardEvent.h"
#include "core/events/MouseEvent.h"
-#include "core/events/ThreadLocalEventNames.h"
+#include "core/frame/FrameView.h"
+#include "core/frame/LocalFrame.h"
#include "core/html/FormDataList.h"
#include "core/html/HTMLFormElement.h"
+#include "core/html/HTMLOptGroupElement.h"
#include "core/html/HTMLOptionElement.h"
#include "core/html/forms/FormController.h"
+#include "core/page/AutoscrollController.h"
#include "core/page/EventHandler.h"
-#include "core/frame/Frame.h"
+#include "core/page/Page.h"
#include "core/page/SpatialNavigation.h"
+#include "core/rendering/HitTestRequest.h"
+#include "core/rendering/HitTestResult.h"
#include "core/rendering/RenderListBox.h"
#include "core/rendering/RenderMenuList.h"
#include "core/rendering/RenderTheme.h"
+#include "core/rendering/RenderView.h"
#include "platform/PlatformMouseEvent.h"
#include "platform/text/PlatformLocale.h"
-using namespace std;
using namespace WTF::Unicode;
-namespace WebCore {
+namespace blink {
using namespace HTMLNames;
, m_multiple(false)
, m_activeSelectionState(false)
, m_shouldRecalcListItems(false)
+ , m_suggestedIndex(-1)
+ , m_isAutofilledByPreview(false)
{
- ScriptWrappable::init(this);
}
-PassRefPtr<HTMLSelectElement> HTMLSelectElement::create(Document& document)
+PassRefPtrWillBeRawPtr<HTMLSelectElement> HTMLSelectElement::create(Document& document)
{
- return adoptRef(new HTMLSelectElement(document, 0));
+ RefPtrWillBeRawPtr<HTMLSelectElement> select = adoptRefWillBeNoop(new HTMLSelectElement(document, 0));
+ select->ensureUserAgentShadowRoot();
+ return select.release();
}
-PassRefPtr<HTMLSelectElement> HTMLSelectElement::create(Document& document, HTMLFormElement* form)
+PassRefPtrWillBeRawPtr<HTMLSelectElement> HTMLSelectElement::create(Document& document, HTMLFormElement* form)
{
- return adoptRef(new HTMLSelectElement(document, form));
+ RefPtrWillBeRawPtr<HTMLSelectElement> select = adoptRefWillBeNoop(new HTMLSelectElement(document, form));
+ select->ensureUserAgentShadowRoot();
+ return select.release();
}
const AtomicString& HTMLSelectElement::formControlType() const
if (optionIndex == selectedIndex())
return;
- selectOption(optionIndex, DeselectOtherOptions | (fireOnChangeNow ? DispatchChangeEvent : 0) | UserDriven);
+ selectOption(optionIndex, DeselectOtherOptions | (fireOnChangeNow ? DispatchInputAndChangeEvent : 0) | UserDriven);
}
bool HTMLSelectElement::hasPlaceholderLabelOption() const
void HTMLSelectElement::add(HTMLElement* element, HTMLElement* before, ExceptionState& exceptionState)
{
// Make sure the element is ref'd and deref'd so we don't leak it.
- RefPtr<HTMLElement> protectNewChild(element);
+ RefPtrWillBeRawPtr<HTMLElement> protectNewChild(element);
- if (!element || !(element->hasLocalName(optionTag) || element->hasLocalName(hrTag)))
+ if (!element || !(isHTMLOptionElement(element) || isHTMLOptGroupElement(element) || isHTMLHRElement(element)))
return;
insertBefore(element, before, exceptionState);
setNeedsValidityCheck();
}
+void HTMLSelectElement::addBeforeOptionAtIndex(HTMLElement* element, int beforeIndex, ExceptionState& exceptionState)
+{
+ HTMLOptionElement* beforeElement = options()->item(beforeIndex);
+ add(element, beforeElement, exceptionState);
+}
+
void HTMLSelectElement::remove(int optionIndex)
{
int listIndex = optionToListIndex(optionIndex);
listItems()[listIndex]->remove(IGNORE_EXCEPTION);
}
-void HTMLSelectElement::remove(HTMLOptionElement* option)
-{
- if (option->ownerSelectElement() != this)
- return;
-
- option->remove(IGNORE_EXCEPTION);
-}
-
String HTMLSelectElement::value() const
{
- const Vector<HTMLElement*>& items = listItems();
+ const WillBeHeapVector<RawPtrWillBeMember<HTMLElement>>& items = listItems();
for (unsigned i = 0; i < items.size(); i++) {
- if (items[i]->hasLocalName(optionTag) && toHTMLOptionElement(items[i])->selected())
+ if (isHTMLOptionElement(items[i]) && toHTMLOptionElement(items[i])->selected())
return toHTMLOptionElement(items[i])->value();
}
return "";
}
-void HTMLSelectElement::setValue(const String &value)
+void HTMLSelectElement::setValue(const String &value, bool sendEvents)
{
// We clear the previously selected option(s) when needed, to guarantee calling setSelectedIndex() only once.
+ int optionIndex = 0;
+ if (value.isNull()) {
+ optionIndex = -1;
+ } else {
+ // Find the option with value() matching the given parameter and make it the current selection.
+ const WillBeHeapVector<RawPtrWillBeMember<HTMLElement>>& items = listItems();
+ for (unsigned i = 0; i < items.size(); i++) {
+ if (isHTMLOptionElement(items[i])) {
+ if (toHTMLOptionElement(items[i])->value() == value)
+ break;
+ optionIndex++;
+ }
+ }
+ if (optionIndex >= static_cast<int>(items.size()))
+ optionIndex = -1;
+ }
+
+ int previousSelectedIndex = selectedIndex();
+ setSuggestedIndex(-1);
+ if (m_isAutofilledByPreview)
+ setAutofilled(false);
+ setSelectedIndex(optionIndex);
+
+ if (sendEvents && previousSelectedIndex != selectedIndex()) {
+ if (usesMenuList())
+ dispatchInputAndChangeEventForMenuList(false);
+ else
+ listBoxOnChange();
+ }
+}
+
+String HTMLSelectElement::suggestedValue() const
+{
+ const WillBeHeapVector<RawPtrWillBeMember<HTMLElement>>& items = listItems();
+ for (unsigned i = 0; i < items.size(); ++i) {
+ if (isHTMLOptionElement(items[i]) && m_suggestedIndex >= 0) {
+ if (i == static_cast<unsigned>(m_suggestedIndex))
+ return toHTMLOptionElement(items[i])->value();
+ }
+ }
+ return "";
+}
+
+void HTMLSelectElement::setSuggestedValue(const String& value)
+{
if (value.isNull()) {
- setSelectedIndex(-1);
+ setSuggestedIndex(-1);
return;
}
- // Find the option with value() matching the given parameter and make it the current selection.
- const Vector<HTMLElement*>& items = listItems();
+ const WillBeHeapVector<RawPtrWillBeMember<HTMLElement>>& items = listItems();
unsigned optionIndex = 0;
- for (unsigned i = 0; i < items.size(); i++) {
- if (items[i]->hasLocalName(optionTag)) {
+ for (unsigned i = 0; i < items.size(); ++i) {
+ if (isHTMLOptionElement(items[i])) {
if (toHTMLOptionElement(items[i])->value() == value) {
- setSelectedIndex(optionIndex);
+ setSuggestedIndex(optionIndex);
+ m_isAutofilledByPreview = true;
return;
}
optionIndex++;
}
}
- setSelectedIndex(-1);
+ setSuggestedIndex(-1);
}
bool HTMLSelectElement::isPresentationAttribute(const QualifiedName& name) const
AtomicString attrSize = AtomicString::number(size);
if (attrSize != value) {
// FIXME: This is horribly factored.
- if (Attribute* sizeAttribute = ensureUniqueElementData()->getAttributeItem(sizeAttr))
+ if (Attribute* sizeAttribute = ensureUniqueElementData().attributes().find(sizeAttr))
sizeAttribute->setValue(attrSize);
}
- size = max(size, 1);
+ size = std::max(size, 0);
// Ensure that we've determined selectedness of the items at least once prior to changing the size.
if (oldSize != size)
else if (name == accesskeyAttr) {
// FIXME: ignore for the moment.
//
+ } else if (name == disabledAttr) {
+ HTMLFormControlElementWithState::parseAttribute(name, value);
+ if (renderer() && renderer()->isMenuList()) {
+ if (RenderMenuList* menuList = toRenderMenuList(renderer())) {
+ if (menuList->popupIsVisible())
+ menuList->hidePopup();
+ }
+ }
+
} else
HTMLFormControlElementWithState::parseAttribute(name, value);
}
return new RenderListBox(this);
}
-PassRefPtr<HTMLCollection> HTMLSelectElement::selectedOptions()
+PassRefPtrWillBeRawPtr<HTMLCollection> HTMLSelectElement::selectedOptions()
{
updateListItemSelectedStates();
- return ensureCachedHTMLCollection(SelectedOptions);
+ return ensureCachedCollection<HTMLCollection>(SelectedOptions);
}
-PassRefPtr<HTMLOptionsCollection> HTMLSelectElement::options()
+PassRefPtrWillBeRawPtr<HTMLOptionsCollection> HTMLSelectElement::options()
{
- return static_cast<HTMLOptionsCollection*>(ensureCachedHTMLCollection(SelectOptions).get());
+ return ensureCachedCollection<HTMLOptionsCollection>(SelectOptions);
}
void HTMLSelectElement::updateListItemSelectedStates()
setNeedsValidityCheck();
}
-void HTMLSelectElement::childrenChanged(bool changedByParser, Node* beforeChange, Node* afterChange, int childCountDelta)
+void HTMLSelectElement::childrenChanged(const ChildrenChange& change)
{
setRecalcListItems();
setNeedsValidityCheck();
m_lastOnChangeSelection.clear();
- HTMLFormControlElementWithState::childrenChanged(changedByParser, beforeChange, afterChange, childCountDelta);
+ HTMLFormControlElementWithState::childrenChanged(change);
}
void HTMLSelectElement::optionElementChildrenChanged()
return options()->namedItem(name);
}
-Element* HTMLSelectElement::item(unsigned index)
+HTMLOptionElement* HTMLSelectElement::item(unsigned index)
{
return options()->item(index);
}
if (index > maxSelectItems - 1)
index = maxSelectItems - 1;
int diff = index - length();
- RefPtr<HTMLElement> before = 0;
+ RefPtrWillBeRawPtr<HTMLOptionElement> before = nullptr;
// Out of array bounds? First insert empty dummies.
if (diff > 0) {
setLength(index, exceptionState);
// Replace an existing entry?
} else if (diff < 0) {
- before = toHTMLElement(options()->item(index+1));
+ before = options()->item(index + 1);
remove(index);
}
// Finally add the new element.
if (diff < 0) { // Add dummy elements.
do {
- RefPtr<Element> option = document().createElement(optionTag, false);
+ RefPtrWillBeRawPtr<Element> option = document().createElement(optionTag, false);
ASSERT(option);
add(toHTMLElement(option), 0, exceptionState);
if (exceptionState.hadException())
break;
} while (++diff);
} else {
- const Vector<HTMLElement*>& items = listItems();
+ const WillBeHeapVector<RawPtrWillBeMember<HTMLElement>>& items = listItems();
// Removing children fires mutation events, which might mutate the DOM further, so we first copy out a list
// of elements that we intend to remove then attempt to remove them one at a time.
- Vector<RefPtr<Element> > itemsToRemove;
+ WillBeHeapVector<RefPtrWillBeMember<Element>> itemsToRemove;
size_t optionIndex = 0;
for (size_t i = 0; i < items.size(); ++i) {
Element* item = items[i];
- if (item->hasLocalName(optionTag) && optionIndex++ >= newLen) {
+ if (isHTMLOptionElement(items[i]) && optionIndex++ >= newLen) {
ASSERT(item->parentNode());
itemsToRemove.append(item);
}
int HTMLSelectElement::nextValidIndex(int listIndex, SkipDirection direction, int skip) const
{
ASSERT(direction == -1 || direction == 1);
- const Vector<HTMLElement*>& listItems = this->listItems();
+ const WillBeHeapVector<RawPtrWillBeMember<HTMLElement>>& listItems = this->listItems();
int lastGoodIndex = listIndex;
int size = listItems.size();
for (listIndex += direction; listIndex >= 0 && listIndex < size; listIndex += direction) {
--skip;
- if (!listItems[listIndex]->isDisabledFormControl() && listItems[listIndex]->hasTagName(optionTag)) {
- lastGoodIndex = listIndex;
- if (skip <= 0)
- break;
- }
+ HTMLElement* element = listItems[listIndex];
+ if (!isHTMLOptionElement(*element))
+ continue;
+ if (toHTMLOptionElement(*element).isDisplayNone())
+ continue;
+ if (element->isDisabledFormControl())
+ continue;
+ if (!usesMenuList() && !element->renderer())
+ continue;
+ lastGoodIndex = listIndex;
+ if (skip <= 0)
+ break;
}
return lastGoodIndex;
}
int HTMLSelectElement::firstSelectableListIndex() const
{
- const Vector<HTMLElement*>& items = listItems();
+ const WillBeHeapVector<RawPtrWillBeMember<HTMLElement>>& items = listItems();
int index = nextValidIndex(items.size(), SkipBackwards, INT_MAX);
if (static_cast<size_t>(index) == items.size())
return -1;
// Returns the index of the next valid item one page away from |startIndex| in direction |direction|.
int HTMLSelectElement::nextSelectableListIndexPageAway(int startIndex, SkipDirection direction) const
{
- const Vector<HTMLElement*>& items = listItems();
+ const WillBeHeapVector<RawPtrWillBeMember<HTMLElement>>& items = listItems();
// Can't use m_size because renderer forces a minimum size.
int pageSize = 0;
if (renderer()->isListBox())
setActiveSelectionAnchorIndex(nextSelectableListIndex(-1));
setActiveSelectionEndIndex(previousSelectableListIndex(-1));
- updateListBoxSelection(false);
+ updateListBoxSelection(false, false);
listBoxOnChange();
setNeedsValidityCheck();
}
}
m_lastOnChangeSelection.clear();
- const Vector<HTMLElement*>& items = listItems();
+ const WillBeHeapVector<RawPtrWillBeMember<HTMLElement>>& items = listItems();
for (unsigned i = 0; i < items.size(); ++i) {
HTMLElement* element = items[i];
- m_lastOnChangeSelection.append(element->hasTagName(optionTag) && toHTMLOptionElement(element)->selected());
+ m_lastOnChangeSelection.append(isHTMLOptionElement(*element) && toHTMLOptionElement(element)->selected());
}
}
// selection pivots around this anchor index.
m_cachedStateForActiveSelection.clear();
- const Vector<HTMLElement*>& items = listItems();
+ const WillBeHeapVector<RawPtrWillBeMember<HTMLElement>>& items = listItems();
for (unsigned i = 0; i < items.size(); ++i) {
HTMLElement* element = items[i];
- m_cachedStateForActiveSelection.append(element->hasTagName(optionTag) && toHTMLOptionElement(element)->selected());
+ m_cachedStateForActiveSelection.append(isHTMLOptionElement(*element) && toHTMLOptionElement(element)->selected());
}
}
void HTMLSelectElement::setActiveSelectionEndIndex(int index)
{
+ if (index == m_activeSelectionEndIndex)
+ return;
m_activeSelectionEndIndex = index;
+ setNeedsStyleRecalc(SubtreeStyleChange, StyleChangeReasonForTracing::create(StyleChangeReason::Control));
}
-void HTMLSelectElement::updateListBoxSelection(bool deselectOtherOptions)
+void HTMLSelectElement::updateListBoxSelection(bool deselectOtherOptions, bool scroll)
{
ASSERT(renderer() && (renderer()->isListBox() || m_multiple));
ASSERT(!listItems().size() || m_activeSelectionAnchorIndex >= 0);
- unsigned start = min(m_activeSelectionAnchorIndex, m_activeSelectionEndIndex);
- unsigned end = max(m_activeSelectionAnchorIndex, m_activeSelectionEndIndex);
+ unsigned start = std::min(m_activeSelectionAnchorIndex, m_activeSelectionEndIndex);
+ unsigned end = std::max(m_activeSelectionAnchorIndex, m_activeSelectionEndIndex);
- const Vector<HTMLElement*>& items = listItems();
+ const WillBeHeapVector<RawPtrWillBeMember<HTMLElement>>& items = listItems();
for (unsigned i = 0; i < items.size(); ++i) {
HTMLElement* element = items[i];
- if (!element->hasTagName(optionTag) || toHTMLOptionElement(element)->isDisabledFormControl())
+ if (!isHTMLOptionElement(*element) || toHTMLOptionElement(element)->isDisabledFormControl() || !toHTMLOptionElement(element)->renderer())
continue;
if (i >= start && i <= end)
toHTMLOptionElement(element)->setSelectedState(m_cachedStateForActiveSelection[i]);
}
- scrollToSelection();
setNeedsValidityCheck();
+ if (scroll)
+ scrollToSelection();
notifyFormStateChanged();
}
{
ASSERT(!usesMenuList() || m_multiple);
- const Vector<HTMLElement*>& items = listItems();
+ const WillBeHeapVector<RawPtrWillBeMember<HTMLElement>>& items = listItems();
// If the cached selection list is empty, or the size has changed, then fire
// dispatchFormControlChangeEvent, and return early.
bool fireOnChange = false;
for (unsigned i = 0; i < items.size(); ++i) {
HTMLElement* element = items[i];
- bool selected = element->hasTagName(optionTag) && toHTMLOptionElement(element)->selected();
+ bool selected = isHTMLOptionElement(*element) && toHTMLOptionElement(element)->selected();
if (selected != m_lastOnChangeSelection[i])
fireOnChange = true;
m_lastOnChangeSelection[i] = selected;
}
- if (fireOnChange)
+ if (fireOnChange) {
+ RefPtrWillBeRawPtr<HTMLSelectElement> protector(this);
+ dispatchInputEvent();
dispatchFormControlChangeEvent();
+ }
}
-void HTMLSelectElement::dispatchChangeEventForMenuList()
+void HTMLSelectElement::dispatchInputAndChangeEventForMenuList(bool requiresUserGesture)
{
ASSERT(usesMenuList());
int selected = selectedIndex();
- if (m_lastOnChangeIndex != selected && m_isProcessingUserDrivenChange) {
+ if (m_lastOnChangeIndex != selected && (!requiresUserGesture || m_isProcessingUserDrivenChange)) {
m_lastOnChangeIndex = selected;
m_isProcessingUserDrivenChange = false;
+ RefPtrWillBeRawPtr<HTMLSelectElement> protector(this);
+ dispatchInputEvent();
dispatchFormControlChangeEvent();
}
}
void HTMLSelectElement::scrollToSelection()
{
+ if (!isFinishedParsingChildren())
+ return;
if (usesMenuList())
return;
-
- if (RenderObject* renderer = this->renderer())
- toRenderListBox(renderer)->selectionChanged();
+ scrollTo(activeSelectionEndListIndex());
+ if (AXObjectCache* cache = document().existingAXObjectCache())
+ cache->selectedChildrenChanged(this);
}
void HTMLSelectElement::setOptionsChangedOnRenderer()
if (RenderObject* renderer = this->renderer()) {
if (usesMenuList())
toRenderMenuList(renderer)->setOptionsChanged(true);
- else
- toRenderListBox(renderer)->setOptionsChanged(true);
}
}
-const Vector<HTMLElement*>& HTMLSelectElement::listItems() const
+const WillBeHeapVector<RawPtrWillBeMember<HTMLElement>>& HTMLSelectElement::listItems() const
{
if (m_shouldRecalcListItems)
recalcListItems();
else {
-#if !ASSERT_DISABLED
- Vector<HTMLElement*> items = m_listItems;
+#if ENABLE(ASSERT)
+ WillBeHeapVector<RawPtrWillBeMember<HTMLElement>> items = m_listItems;
recalcListItems(false);
ASSERT(items == m_listItems);
#endif
void HTMLSelectElement::invalidateSelectedItems()
{
- if (HTMLCollection* collection = cachedHTMLCollection(SelectedOptions))
+ if (HTMLCollection* collection = cachedCollection<HTMLCollection>(SelectedOptions))
collection->invalidateCache();
}
// Manual selection anchor is reset when manipulating the select programmatically.
m_activeSelectionAnchorIndex = -1;
setOptionsChangedOnRenderer();
- setNeedsStyleRecalc(SubtreeStyleChange);
+ setNeedsStyleRecalc(SubtreeStyleChange, StyleChangeReasonForTracing::create(StyleChangeReason::ControlValue));
if (!inDocument()) {
- if (HTMLCollection* collection = cachedHTMLCollection(SelectOptions))
+ if (HTMLOptionsCollection* collection = cachedCollection<HTMLOptionsCollection>(SelectOptions))
collection->invalidateCache();
}
if (!inDocument())
// optgroup tags may not nest. However, both FireFox and IE will
// flatten the tree automatically, so we follow suit.
// (http://www.w3.org/TR/html401/interact/forms.html#h-17.6)
- if (current.hasTagName(optgroupTag)) {
+ if (isHTMLOptGroupElement(current)) {
m_listItems.append(¤t);
if (Element* nextElement = ElementTraversal::firstWithin(current)) {
currentElement = nextElement;
}
}
- if (current.hasTagName(optionTag)) {
+ if (isHTMLOptionElement(current)) {
m_listItems.append(¤t);
if (updateSelectedStates && !m_multiple) {
}
}
- if (current.hasTagName(hrTag))
+ if (isHTMLHRElement(current))
m_listItems.append(¤t);
// In conforming HTML code, only <optgroup> and <option> will be found
unsigned index = 0;
// Return the number of the first option selected.
- const Vector<HTMLElement*>& items = listItems();
+ const WillBeHeapVector<RawPtrWillBeMember<HTMLElement>>& items = listItems();
for (size_t i = 0; i < items.size(); ++i) {
HTMLElement* element = items[i];
- if (element->hasTagName(optionTag)) {
- if (toHTMLOptionElement(element)->selected())
+ if (isHTMLOptionElement(*element)) {
+ if (toHTMLOptionElement(*element).selected())
return index;
++index;
}
selectOption(index, DeselectOtherOptions);
}
+int HTMLSelectElement::suggestedIndex() const
+{
+ return m_suggestedIndex;
+}
+
+void HTMLSelectElement::setSuggestedIndex(int suggestedIndex)
+{
+ m_suggestedIndex = suggestedIndex;
+
+ if (RenderObject* renderer = this->renderer()) {
+ renderer->updateFromElement();
+ scrollTo(suggestedIndex);
+ }
+}
+
+void HTMLSelectElement::scrollTo(int listIndex)
+{
+ if (listIndex < 0)
+ return;
+ if (usesMenuList())
+ return;
+ const WillBeHeapVector<RawPtrWillBeMember<HTMLElement>>& items = listItems();
+ int listSize = static_cast<int>(items.size());
+ if (listIndex >= listSize)
+ return;
+ document().updateLayoutIgnorePendingStylesheets();
+ if (!renderer() || !renderer()->isListBox())
+ return;
+ LayoutRect bounds = items[listIndex]->boundingBox();
+ toRenderListBox(renderer())->scrollToRect(bounds);
+}
+
void HTMLSelectElement::optionSelectionStateChanged(HTMLOptionElement* option, bool optionIsSelected)
{
ASSERT(option->ownerSelectElement() == this);
selectOption(nextSelectableListIndex(-1));
}
+void HTMLSelectElement::optionRemoved(const HTMLOptionElement& option)
+{
+ if (m_activeSelectionAnchorIndex < 0 && m_activeSelectionEndIndex < 0)
+ return;
+ int listIndex = optionToListIndex(option.index());
+ if (listIndex <= m_activeSelectionAnchorIndex)
+ m_activeSelectionAnchorIndex--;
+ if (listIndex <= m_activeSelectionEndIndex)
+ m_activeSelectionEndIndex--;
+ if (listIndex == selectedIndex())
+ setAutofilled(false);
+}
+
void HTMLSelectElement::selectOption(int optionIndex, SelectOptionFlags flags)
{
bool shouldDeselect = !m_multiple || (flags & DeselectOtherOptions);
- const Vector<HTMLElement*>& items = listItems();
+ const WillBeHeapVector<RawPtrWillBeMember<HTMLElement>>& items = listItems();
int listIndex = optionToListIndex(optionIndex);
+ if (selectedIndex() != optionIndex && isAutofilled())
+ setAutofilled(false);
+
HTMLElement* element = 0;
if (listIndex >= 0) {
element = items[listIndex];
- if (element->hasTagName(optionTag)) {
+ if (isHTMLOptionElement(*element)) {
if (m_activeSelectionAnchorIndex < 0 || shouldDeselect)
setActiveSelectionAnchorIndex(listIndex);
if (m_activeSelectionEndIndex < 0 || shouldDeselect)
setActiveSelectionEndIndex(listIndex);
- toHTMLOptionElement(element)->setSelectedState(true);
+ toHTMLOptionElement(*element).setSelectedState(true);
}
}
scrollToSelection();
+ setNeedsValidityCheck();
+
if (usesMenuList()) {
m_isProcessingUserDrivenChange = flags & UserDriven;
- if (flags & DispatchChangeEvent)
- dispatchChangeEventForMenuList();
+ if (flags & DispatchInputAndChangeEvent)
+ dispatchInputAndChangeEventForMenuList();
if (RenderObject* renderer = this->renderer()) {
- if (usesMenuList())
+ if (usesMenuList()) {
toRenderMenuList(renderer)->didSetSelectedIndex(listIndex);
- else if (renderer->isListBox())
- toRenderListBox(renderer)->selectionChanged();
+ } else if (renderer->isListBox()) {
+ if (AXObjectCache* cache = document().existingAXObjectCache())
+ cache->selectedChildrenChanged(this);
+ }
}
}
- setNeedsValidityCheck();
notifyFormStateChanged();
}
int HTMLSelectElement::optionToListIndex(int optionIndex) const
{
- const Vector<HTMLElement*>& items = listItems();
+ const WillBeHeapVector<RawPtrWillBeMember<HTMLElement>>& items = listItems();
int listSize = static_cast<int>(items.size());
if (optionIndex < 0 || optionIndex >= listSize)
return -1;
int optionIndex2 = -1;
for (int listIndex = 0; listIndex < listSize; ++listIndex) {
- if (items[listIndex]->hasTagName(optionTag)) {
+ if (isHTMLOptionElement(*items[listIndex])) {
++optionIndex2;
if (optionIndex2 == optionIndex)
return listIndex;
int HTMLSelectElement::listToOptionIndex(int listIndex) const
{
- const Vector<HTMLElement*>& items = listItems();
- if (listIndex < 0 || listIndex >= static_cast<int>(items.size()) || !items[listIndex]->hasTagName(optionTag))
+ const WillBeHeapVector<RawPtrWillBeMember<HTMLElement>>& items = listItems();
+ if (listIndex < 0 || listIndex >= static_cast<int>(items.size()) || !isHTMLOptionElement(*items[listIndex]))
return -1;
// Actual index of option not counting OPTGROUP entries that may be in list.
int optionIndex = 0;
for (int i = 0; i < listIndex; ++i) {
- if (items[i]->hasTagName(optionTag))
+ if (isHTMLOptionElement(*items[i]))
++optionIndex;
}
// change events for list boxes whenever the selection change is actually made.
// This matches other browsers' behavior.
if (usesMenuList())
- dispatchChangeEventForMenuList();
+ dispatchInputAndChangeEventForMenuList();
HTMLFormControlElementWithState::dispatchBlurEvent(newFocusedElement);
}
void HTMLSelectElement::deselectItemsWithoutValidation(HTMLElement* excludeElement)
{
- const Vector<HTMLElement*>& items = listItems();
+ const WillBeHeapVector<RawPtrWillBeMember<HTMLElement>>& items = listItems();
for (unsigned i = 0; i < items.size(); ++i) {
HTMLElement* element = items[i];
- if (element != excludeElement && element->hasTagName(optionTag))
+ if (element != excludeElement && isHTMLOptionElement(*element))
toHTMLOptionElement(element)->setSelectedState(false);
}
}
FormControlState HTMLSelectElement::saveFormControlState() const
{
- const Vector<HTMLElement*>& items = listItems();
+ const WillBeHeapVector<RawPtrWillBeMember<HTMLElement>>& items = listItems();
size_t length = items.size();
FormControlState state;
for (unsigned i = 0; i < length; ++i) {
- if (!items[i]->hasTagName(optionTag))
+ if (!isHTMLOptionElement(*items[i]))
continue;
HTMLOptionElement* option = toHTMLOptionElement(items[i]);
if (!option->selected())
continue;
state.append(option->value());
+ state.append(String::number(i));
if (!multiple())
break;
}
size_t HTMLSelectElement::searchOptionsForValue(const String& value, size_t listIndexStart, size_t listIndexEnd) const
{
- const Vector<HTMLElement*>& items = listItems();
+ const WillBeHeapVector<RawPtrWillBeMember<HTMLElement>>& items = listItems();
size_t loopEndIndex = std::min(items.size(), listIndexEnd);
for (size_t i = listIndexStart; i < loopEndIndex; ++i) {
- if (!items[i]->hasLocalName(optionTag))
+ if (!isHTMLOptionElement(items[i]))
continue;
if (toHTMLOptionElement(items[i])->value() == value)
return i;
{
recalcListItems();
- const Vector<HTMLElement*>& items = listItems();
+ const WillBeHeapVector<RawPtrWillBeMember<HTMLElement>>& items = listItems();
size_t itemsSize = items.size();
if (!itemsSize)
return;
for (size_t i = 0; i < itemsSize; ++i) {
- if (!items[i]->hasLocalName(optionTag))
+ if (!isHTMLOptionElement(items[i]))
continue;
toHTMLOptionElement(items[i])->setSelectedState(false);
}
+ // The saved state should have at least one value and an index.
+ ASSERT(state.valueSize() >= 2);
if (!multiple()) {
- size_t foundIndex = searchOptionsForValue(state[0], 0, itemsSize);
- if (foundIndex != kNotFound)
- toHTMLOptionElement(items[foundIndex])->setSelectedState(true);
+ size_t index = state[1].toUInt();
+ if (index < itemsSize && isHTMLOptionElement(items[index]) && toHTMLOptionElement(items[index])->value() == state[0]) {
+ toHTMLOptionElement(items[index])->setSelectedState(true);
+ } else {
+ size_t foundIndex = searchOptionsForValue(state[0], 0, itemsSize);
+ if (foundIndex != kNotFound)
+ toHTMLOptionElement(items[foundIndex])->setSelectedState(true);
+ }
} else {
size_t startIndex = 0;
- for (size_t i = 0; i < state.valueSize(); ++i) {
+ for (size_t i = 0; i < state.valueSize(); i+= 2) {
const String& value = state[i];
- size_t foundIndex = searchOptionsForValue(value, startIndex, itemsSize);
- if (foundIndex == kNotFound)
- foundIndex = searchOptionsForValue(value, 0, startIndex);
- if (foundIndex == kNotFound)
- continue;
- toHTMLOptionElement(items[foundIndex])->setSelectedState(true);
- startIndex = foundIndex + 1;
+ const size_t index = state[i + 1].toUInt();
+ if (index < itemsSize && isHTMLOptionElement(items[index]) && toHTMLOptionElement(items[index])->value() == value) {
+ toHTMLOptionElement(items[index])->setSelectedState(true);
+ startIndex = index + 1;
+ } else {
+ size_t foundIndex = searchOptionsForValue(value, startIndex, itemsSize);
+ if (foundIndex == kNotFound)
+ foundIndex = searchOptionsForValue(value, 0, startIndex);
+ if (foundIndex == kNotFound)
+ continue;
+ toHTMLOptionElement(items[foundIndex])->setSelectedState(true);
+ startIndex = foundIndex + 1;
+ }
}
}
void HTMLSelectElement::parseMultipleAttribute(const AtomicString& value)
{
- bool oldUsesMenuList = usesMenuList();
m_multiple = !value.isNull();
setNeedsValidityCheck();
- if (oldUsesMenuList != usesMenuList())
- lazyReattachIfAttached();
+
+ lazyReattachIfAttached();
}
bool HTMLSelectElement::appendFormData(FormDataList& list, bool)
return false;
bool successful = false;
- const Vector<HTMLElement*>& items = listItems();
+ const WillBeHeapVector<RawPtrWillBeMember<HTMLElement>>& items = listItems();
for (unsigned i = 0; i < items.size(); ++i) {
HTMLElement* element = items[i];
- if (element->hasTagName(optionTag) && toHTMLOptionElement(element)->selected() && !toHTMLOptionElement(element)->isDisabledFormControl()) {
- list.appendData(name, toHTMLOptionElement(element)->value());
+ if (isHTMLOptionElement(*element) && toHTMLOptionElement(*element).selected() && !toHTMLOptionElement(*element).isDisabledFormControl()) {
+ list.appendData(name, toHTMLOptionElement(*element).value());
successful = true;
}
}
HTMLOptionElement* firstOption = 0;
HTMLOptionElement* selectedOption = 0;
- const Vector<HTMLElement*>& items = listItems();
+ const WillBeHeapVector<RawPtrWillBeMember<HTMLElement>>& items = listItems();
for (unsigned i = 0; i < items.size(); ++i) {
HTMLElement* element = items[i];
- if (!element->hasTagName(optionTag))
+ if (!isHTMLOptionElement(*element))
continue;
if (items[i]->fastHasAttribute(selectedAttr)) {
firstOption->setSelectedState(true);
setOptionsChangedOnRenderer();
- setNeedsStyleRecalc(SubtreeStyleChange);
+ setNeedsStyleRecalc(SubtreeStyleChange, StyleChangeReasonForTracing::create(StyleChangeReason::ControlValue));
setNeedsValidityCheck();
}
-#if !OS(WIN)
-bool HTMLSelectElement::platformHandleKeydownEvent(KeyboardEvent* event)
+void HTMLSelectElement::handlePopupOpenKeyboardEvent(Event* event)
{
- if (!RenderTheme::theme().popsMenuByArrowKeys())
- return false;
+ focus();
+ // Calling focus() may cause us to lose our renderer. Return true so
+ // that our caller doesn't process the event further, but don't set
+ // the event as handled.
+ if (!renderer() || !renderer()->isMenuList() || isDisabledFormControl())
+ return;
+ // Save the selection so it can be compared to the new selection
+ // when dispatching change events during selectOption, which
+ // gets called from RenderMenuList::valueChanged, which gets called
+ // after the user makes a selection from the menu.
+ saveLastSelection();
+ if (RenderMenuList* menuList = toRenderMenuList(renderer()))
+ menuList->showPopup();
+ int index = selectedIndex();
+ ASSERT(index >= 0);
+ ASSERT_WITH_SECURITY_IMPLICATION(index < static_cast<int>(listItems().size()));
+ setSelectedIndex(index);
+ event->setDefaultHandled();
+ return;
+}
- if (!isSpatialNavigationEnabled(document().frame())) {
- if (event->keyIdentifier() == "Down" || event->keyIdentifier() == "Up") {
- focus();
- // Calling focus() may cause us to lose our renderer. Return true so
- // that our caller doesn't process the event further, but don't set
- // the event as handled.
- if (!renderer())
- return true;
+bool HTMLSelectElement::shouldOpenPopupForKeyDownEvent(KeyboardEvent* keyEvent)
+{
+ const String& keyIdentifier = keyEvent->keyIdentifier();
+ RenderTheme& renderTheme = RenderTheme::theme();
- // Save the selection so it can be compared to the new selection
- // when dispatching change events during selectOption, which
- // gets called from RenderMenuList::valueChanged, which gets called
- // after the user makes a selection from the menu.
- saveLastSelection();
- if (RenderMenuList* menuList = toRenderMenuList(renderer()))
- menuList->showPopup();
- event->setDefaultHandled();
- }
- return true;
- }
+ if (isSpatialNavigationEnabled(document().frame()))
+ return false;
- return false;
+ return ((renderTheme.popsMenuByArrowKeys() && (keyIdentifier == "Down" || keyIdentifier == "Up"))
+ || (renderTheme.popsMenuByAltDownUpOrF4Key() && (keyIdentifier == "Down" || keyIdentifier == "Up") && keyEvent->altKey())
+ || (renderTheme.popsMenuByAltDownUpOrF4Key() && (!keyEvent->altKey() && !keyEvent->ctrlKey() && keyIdentifier == "F4")));
}
-#endif
-void HTMLSelectElement::menuListDefaultEventHandler(Event* event)
+bool HTMLSelectElement::shouldOpenPopupForKeyPressEvent(KeyboardEvent *event)
{
RenderTheme& renderTheme = RenderTheme::theme();
+ int keyCode = event->keyCode();
+
+ return ((renderTheme.popsMenuBySpaceKey() && event->keyCode() == ' ')
+ || (renderTheme.popsMenuByReturnKey() && keyCode == '\r'));
+}
+void HTMLSelectElement::menuListDefaultEventHandler(Event* event)
+{
if (event->type() == EventTypeNames::keydown) {
if (!renderer() || !event->isKeyboardEvent())
return;
- if (platformHandleKeydownEvent(toKeyboardEvent(event)))
+ KeyboardEvent* keyEvent = toKeyboardEvent(event);
+ if (shouldOpenPopupForKeyDownEvent(keyEvent)) {
+ handlePopupOpenKeyboardEvent(event);
return;
+ }
// When using spatial navigation, we want to be able to navigate away
// from the select element when the user hits any of the arrow keys,
return;
}
- const String& keyIdentifier = toKeyboardEvent(event)->keyIdentifier();
+ // The key handling below shouldn't be used for non spatial navigation mode Mac
+ if (RenderTheme::theme().popsMenuByArrowKeys() && !isSpatialNavigationEnabled(document().frame()))
+ return;
+
+ const String& keyIdentifier = keyEvent->keyIdentifier();
bool handled = true;
- const Vector<HTMLElement*>& listItems = this->listItems();
+ const WillBeHeapVector<RawPtrWillBeMember<HTMLElement>>& listItems = this->listItems();
int listIndex = optionToListIndex(selectedIndex());
if (keyIdentifier == "Down" || keyIdentifier == "Right")
handled = false;
if (handled && static_cast<size_t>(listIndex) < listItems.size())
- selectOption(listToOptionIndex(listIndex), DeselectOtherOptions | DispatchChangeEvent | UserDriven);
+ selectOption(listToOptionIndex(listIndex), DeselectOtherOptions | DispatchInputAndChangeEvent | UserDriven);
if (handled)
event->setDefaultHandled();
}
- // Use key press event here since sending simulated mouse events
- // on key down blocks the proper sending of the key press event.
if (event->type() == EventTypeNames::keypress) {
if (!renderer() || !event->isKeyboardEvent())
return;
int keyCode = toKeyboardEvent(event)->keyCode();
- bool handled = false;
-
if (keyCode == ' ' && isSpatialNavigationEnabled(document().frame())) {
// Use space to toggle arrow key handling for selection change or spatial navigation.
m_activeSelectionState = !m_activeSelectionState;
return;
}
- if (renderTheme.popsMenuBySpaceOrReturn()) {
- if (keyCode == ' ' || keyCode == '\r') {
- focus();
-
- // Calling focus() may remove the renderer or change the
- // renderer type.
- if (!renderer() || !renderer()->isMenuList())
- return;
-
- // Save the selection so it can be compared to the new selection
- // when dispatching change events during selectOption, which
- // gets called from RenderMenuList::valueChanged, which gets called
- // after the user makes a selection from the menu.
- saveLastSelection();
- if (RenderMenuList* menuList = toRenderMenuList(renderer()))
- menuList->showPopup();
- handled = true;
- }
- } else if (renderTheme.popsMenuByArrowKeys()) {
- if (keyCode == ' ') {
- focus();
-
- // Calling focus() may remove the renderer or change the
- // renderer type.
- if (!renderer() || !renderer()->isMenuList())
- return;
-
- // Save the selection so it can be compared to the new selection
- // when dispatching change events during selectOption, which
- // gets called from RenderMenuList::valueChanged, which gets called
- // after the user makes a selection from the menu.
- saveLastSelection();
- if (RenderMenuList* menuList = toRenderMenuList(renderer()))
- menuList->showPopup();
- handled = true;
- } else if (keyCode == '\r') {
- if (form())
- form()->submitImplicitly(event, false);
- dispatchChangeEventForMenuList();
- handled = true;
- }
+ KeyboardEvent* keyEvent = toKeyboardEvent(event);
+ if (shouldOpenPopupForKeyPressEvent(keyEvent)) {
+ handlePopupOpenKeyboardEvent(event);
+ return;
}
- if (handled)
+ if (!RenderTheme::theme().popsMenuByReturnKey() && keyCode == '\r') {
+ if (form())
+ form()->submitImplicitly(event, false);
+ dispatchInputAndChangeEventForMenuList();
event->setDefaultHandled();
+ }
}
if (event->type() == EventTypeNames::mousedown && event->isMouseEvent() && toMouseEvent(event)->button() == LeftButton) {
focus();
- if (renderer() && renderer()->isMenuList()) {
+ if (renderer() && renderer()->isMenuList() && !isDisabledFormControl()) {
if (RenderMenuList* menuList = toRenderMenuList(renderer())) {
if (menuList->popupIsVisible())
menuList->hidePopup();
{
ASSERT(listIndex >= 0);
+ HTMLElement* clickedElement = listItems()[listIndex];
+ ASSERT(clickedElement);
+ if (isHTMLOptGroupElement(clickedElement))
+ return;
+
// Save the selection so it can be compared to the new selection when
// dispatching change events during mouseup, or after autoscroll finishes.
saveLastSelection();
bool shiftSelect = m_multiple && shift;
bool multiSelect = m_multiple && multi && !shift;
- HTMLElement* clickedElement = listItems()[listIndex];
- if (clickedElement->hasTagName(optionTag)) {
+ if (isHTMLOptionElement(*clickedElement)) {
// Keep track of whether an active selection (like during drag
// selection), should select or deselect.
- if (toHTMLOptionElement(clickedElement)->selected() && multiSelect)
+ if (toHTMLOptionElement(*clickedElement).selected() && multiSelect)
m_activeSelectionState = false;
if (!m_activeSelectionState)
- toHTMLOptionElement(clickedElement)->setSelectedState(false);
+ toHTMLOptionElement(*clickedElement).setSelectedState(false);
}
// If we're not in any special multiple selection mode, then deselect all
setActiveSelectionAnchorIndex(selectedIndex());
// Set the selection state of the clicked option.
- if (clickedElement->hasTagName(optionTag) && !toHTMLOptionElement(clickedElement)->isDisabledFormControl())
- toHTMLOptionElement(clickedElement)->setSelectedState(true);
+ if (isHTMLOptionElement(*clickedElement) && !toHTMLOptionElement(*clickedElement).isDisabledFormControl())
+ toHTMLOptionElement(*clickedElement).setSelectedState(true);
// If there was no selectedIndex() for the previous initialization, or If
// we're doing a single selection, or a multiple selection (using cmd or
updateListBoxSelection(!multiSelect);
}
+int HTMLSelectElement::listIndexForEventTargetOption(const Event& event)
+{
+ Node* targetNode = event.target()->toNode();
+ if (!targetNode || !isHTMLOptionElement(*targetNode))
+ return -1;
+ return listIndexForOption(toHTMLOptionElement(*targetNode));
+}
+
+int HTMLSelectElement::listIndexForOption(const HTMLOptionElement& option)
+{
+ const WillBeHeapVector<RawPtrWillBeMember<HTMLElement>>& items = this->listItems();
+ size_t length = items.size();
+ for (size_t i = 0; i < length; ++i) {
+ if (items[i].get() == &option)
+ return i;
+ }
+ return -1;
+}
+
+AutoscrollController* HTMLSelectElement::autoscrollController() const
+{
+ if (Page* page = document().page())
+ return &page->autoscrollController();
+ return 0;
+}
+
+void HTMLSelectElement::handleMouseRelease()
+{
+ // We didn't start this click/drag on any options.
+ if (m_lastOnChangeSelection.isEmpty())
+ return;
+ listBoxOnChange();
+}
+
void HTMLSelectElement::listBoxDefaultEventHandler(Event* event)
{
- const Vector<HTMLElement*>& listItems = this->listItems();
- bool dragSelection = false;
- if (event->type() == EventTypeNames::mousedown && event->isMouseEvent() && toMouseEvent(event)->button() == LeftButton) {
+ const WillBeHeapVector<RawPtrWillBeMember<HTMLElement>>& listItems = this->listItems();
+ if (event->type() == EventTypeNames::gesturetap && event->isGestureEvent()) {
+ focus();
+ // Calling focus() may cause us to lose our renderer or change the render type, in which case do not want to handle the event.
+ if (!renderer() || !renderer()->isListBox())
+ return;
+
+ // Convert to coords relative to the list box if needed.
+ GestureEvent& gestureEvent = toGestureEvent(*event);
+ int listIndex = listIndexForEventTargetOption(gestureEvent);
+ if (listIndex >= 0) {
+ if (!isDisabledFormControl()) {
+ updateSelectedState(listIndex, true, gestureEvent.shiftKey());
+ listBoxOnChange();
+ }
+ event->setDefaultHandled();
+ }
+ } else if (event->type() == EventTypeNames::mousedown && event->isMouseEvent() && toMouseEvent(event)->button() == LeftButton) {
focus();
// Calling focus() may cause us to lose our renderer, in which case do not want to handle the event.
- if (!renderer())
+ if (!renderer() || !renderer()->isListBox() || isDisabledFormControl())
return;
// Convert to coords relative to the list box if needed.
MouseEvent* mouseEvent = toMouseEvent(event);
- IntPoint localOffset = roundedIntPoint(renderer()->absoluteToLocal(mouseEvent->absoluteLocation(), UseTransforms));
- int listIndex = toRenderListBox(renderer())->listIndexAtOffset(toIntSize(localOffset));
+ int listIndex = listIndexForEventTargetOption(*mouseEvent);
if (listIndex >= 0) {
if (!isDisabledFormControl()) {
#if OS(MACOSX)
updateSelectedState(listIndex, mouseEvent->ctrlKey(), mouseEvent->shiftKey());
#endif
}
- if (Frame* frame = document().frame())
+ if (LocalFrame* frame = document().frame())
frame->eventHandler().setMouseDownMayStartAutoscroll();
event->setDefaultHandled();
}
- } else if (event->type() == EventTypeNames::mousemove && event->isMouseEvent() && !toRenderBox(renderer())->canBeScrolledAndHasScrollableArea()) {
+ } else if (event->type() == EventTypeNames::mousemove && event->isMouseEvent()) {
MouseEvent* mouseEvent = toMouseEvent(event);
if (mouseEvent->button() != LeftButton || !mouseEvent->buttonDown())
return;
- IntPoint localOffset = roundedIntPoint(renderer()->absoluteToLocal(mouseEvent->absoluteLocation(), UseTransforms));
- int listIndex = toRenderListBox(renderer())->listIndexAtOffset(toIntSize(localOffset));
+ if (Page* page = document().page())
+ page->autoscrollController().startAutoscrollForSelection(renderer());
+
+ int listIndex = listIndexForEventTargetOption(*mouseEvent);
if (listIndex >= 0) {
if (!isDisabledFormControl()) {
if (m_multiple) {
updateListBoxSelection(true);
}
}
- dragSelection = true;
- }
- } else if (event->type() == EventTypeNames::mouseup && event->isMouseEvent() && toMouseEvent(event)->button() == LeftButton && renderer() && !toRenderBox(renderer())->autoscrollInProgress()) {
- // We didn't start this click/drag on any options.
- if (m_lastOnChangeSelection.isEmpty())
- return;
- // This makes sure we fire dispatchFormControlChangeEvent for a single
- // click. For drag selection, onChange will fire when the autoscroll
- // timer stops.
- if (!dragSelection) {
- listBoxOnChange();
}
+ } else if (event->type() == EventTypeNames::mouseup && event->isMouseEvent() && toMouseEvent(event)->button() == LeftButton && renderer()) {
+ if (document().page() && document().page()->autoscrollController().autoscrollInProgress(toRenderBox(renderer())))
+ document().page()->autoscrollController().stopAutoscroll();
+ else
+ handleMouseRelease();
} else if (event->type() == EventTypeNames::keydown) {
if (!event->isKeyboardEvent())
return;
setActiveSelectionAnchorIndex(m_activeSelectionEndIndex);
}
- toRenderListBox(renderer())->scrollToRevealElementAtListIndex(endIndex);
+ scrollTo(endIndex);
if (selectNewItem) {
updateListBoxSelection(deselectOthers);
listBoxOnChange();
int HTMLSelectElement::lastSelectedListIndex() const
{
- const Vector<HTMLElement*>& items = listItems();
+ const WillBeHeapVector<RawPtrWillBeMember<HTMLElement>>& items = listItems();
for (size_t i = items.size(); i;) {
HTMLElement* element = items[--i];
- if (element->hasTagName(optionTag) && toHTMLOptionElement(element)->selected())
+ if (isHTMLOptionElement(*element) && toHTMLOptionElement(element)->selected())
return i;
}
return -1;
String HTMLSelectElement::optionAtIndex(int index) const
{
- const Vector<HTMLElement*>& items = listItems();
+ const WillBeHeapVector<RawPtrWillBeMember<HTMLElement>>& items = listItems();
HTMLElement* element = items[index];
- if (!element->hasTagName(optionTag) || toHTMLOptionElement(element)->isDisabledFormControl())
+ if (!isHTMLOptionElement(*element) || toHTMLOptionElement(element)->isDisabledFormControl())
return String();
return toHTMLOptionElement(element)->textIndentedToRespectGroupLabel();
}
int index = m_typeAhead.handleEvent(event, TypeAhead::MatchPrefix | TypeAhead::CycleFirstChar);
if (index < 0)
return;
- selectOption(listToOptionIndex(index), DeselectOtherOptions | DispatchChangeEvent | UserDriven);
+ selectOption(listToOptionIndex(index), DeselectOtherOptions | DispatchInputAndChangeEvent | UserDriven);
if (!usesMenuList())
listBoxOnChange();
}
accessKeyAction(false);
// If this index is already selected, unselect. otherwise update the selected index.
- const Vector<HTMLElement*>& items = listItems();
+ const WillBeHeapVector<RawPtrWillBeMember<HTMLElement>>& items = listItems();
int listIndex = optionToListIndex(index);
if (listIndex >= 0) {
HTMLElement* element = items[listIndex];
- if (element->hasTagName(optionTag)) {
- if (toHTMLOptionElement(element)->selected())
- toHTMLOptionElement(element)->setSelectedState(false);
+ if (isHTMLOptionElement(*element)) {
+ if (toHTMLOptionElement(*element).selected())
+ toHTMLOptionElement(*element).setSelectedState(false);
else
- selectOption(index, DispatchChangeEvent | UserDriven);
+ selectOption(index, DispatchInputAndChangeEvent | UserDriven);
}
}
if (usesMenuList())
- dispatchChangeEventForMenuList();
+ dispatchInputAndChangeEventForMenuList();
else
listBoxOnChange();
{
unsigned options = 0;
- const Vector<HTMLElement*>& items = listItems();
+ const WillBeHeapVector<RawPtrWillBeMember<HTMLElement>>& items = listItems();
for (unsigned i = 0; i < items.size(); ++i) {
- if (items[i]->hasTagName(optionTag))
+ if (isHTMLOptionElement(*items[i]))
++options;
}
{
HTMLFormControlElementWithState::finishParsingChildren();
updateListItemSelectedStates();
+ if (!usesMenuList())
+ scrollToSelection();
}
-bool HTMLSelectElement::anonymousIndexedSetter(unsigned index, PassRefPtr<HTMLOptionElement> value, ExceptionState& exceptionState)
+bool HTMLSelectElement::anonymousIndexedSetter(unsigned index, PassRefPtrWillBeRawPtr<HTMLOptionElement> value, ExceptionState& exceptionState)
{
if (!value) { // undefined or null
remove(index);
return true;
}
+void HTMLSelectElement::updateListOnRenderer()
+{
+ setOptionsChangedOnRenderer();
+}
+
+void HTMLSelectElement::trace(Visitor* visitor)
+{
+#if ENABLE(OILPAN)
+ visitor->trace(m_listItems);
+#endif
+ HTMLFormControlElementWithState::trace(visitor);
+}
+
+void HTMLSelectElement::didAddUserAgentShadowRoot(ShadowRoot& root)
+{
+ RefPtrWillBeRawPtr<HTMLContentElement> content = HTMLContentElement::create(document());
+ content->setAttribute(selectAttr, "option,optgroup");
+ root.appendChild(content);
+}
+
+HTMLOptionElement* HTMLSelectElement::spatialNavigationFocusedOption()
+{
+ if (!isSpatialNavigationEnabled(document().frame()))
+ return nullptr;
+ int focusedIndex = activeSelectionEndListIndex();
+ if (focusedIndex < 0)
+ focusedIndex = firstSelectableListIndex();
+ if (focusedIndex < 0)
+ return nullptr;
+ HTMLElement* focused = listItems()[focusedIndex];
+ return isHTMLOptionElement(focused) ? toHTMLOptionElement(focused) : nullptr;
+}
+
} // namespace