summaryrefslogtreecommitdiff
path: root/Source/WebCore/html/HTMLSelectElement.cpp
diff options
context:
space:
mode:
authorLorry Tar Creator <lorry-tar-importer@lorry>2017-06-27 06:07:23 +0000
committerLorry Tar Creator <lorry-tar-importer@lorry>2017-06-27 06:07:23 +0000
commit1bf1084f2b10c3b47fd1a588d85d21ed0eb41d0c (patch)
tree46dcd36c86e7fbc6e5df36deb463b33e9967a6f7 /Source/WebCore/html/HTMLSelectElement.cpp
parent32761a6cee1d0dee366b885b7b9c777e67885688 (diff)
downloadWebKitGtk-tarball-master.tar.gz
Diffstat (limited to 'Source/WebCore/html/HTMLSelectElement.cpp')
-rw-r--r--Source/WebCore/html/HTMLSelectElement.cpp767
1 files changed, 378 insertions, 389 deletions
diff --git a/Source/WebCore/html/HTMLSelectElement.cpp b/Source/WebCore/html/HTMLSelectElement.cpp
index 2c2eb89a8..a28e4c05d 100644
--- a/Source/WebCore/html/HTMLSelectElement.cpp
+++ b/Source/WebCore/html/HTMLSelectElement.cpp
@@ -29,24 +29,24 @@
#include "HTMLSelectElement.h"
#include "AXObjectCache.h"
-#include "Attribute.h"
-#include "Chrome.h"
-#include "ChromeClient.h"
#include "ElementTraversal.h"
#include "EventHandler.h"
#include "EventNames.h"
-#include "ExceptionCodePlaceholder.h"
#include "FormController.h"
#include "FormDataList.h"
#include "Frame.h"
+#include "GenericCachedHTMLCollection.h"
#include "HTMLFormElement.h"
+#include "HTMLHRElement.h"
#include "HTMLNames.h"
#include "HTMLOptGroupElement.h"
#include "HTMLOptionElement.h"
#include "HTMLOptionsCollection.h"
+#include "HTMLParserIdioms.h"
#include "KeyboardEvent.h"
#include "LocalizedStrings.h"
#include "MouseEvent.h"
+#include "NodeRareData.h"
#include "Page.h"
#include "PlatformMouseEvent.h"
#include "RenderListBox.h"
@@ -80,10 +80,10 @@ HTMLSelectElement::HTMLSelectElement(const QualifiedName& tagName, Document& doc
ASSERT(hasTagName(selectTag));
}
-PassRefPtr<HTMLSelectElement> HTMLSelectElement::create(const QualifiedName& tagName, Document& document, HTMLFormElement* form)
+Ref<HTMLSelectElement> HTMLSelectElement::create(const QualifiedName& tagName, Document& document, HTMLFormElement* form)
{
ASSERT(tagName.matches(selectTag));
- return adoptRef(new HTMLSelectElement(tagName, document, form));
+ return adoptRef(*new HTMLSelectElement(tagName, document, form));
}
void HTMLSelectElement::didRecalcStyle(Style::Change styleChange)
@@ -96,15 +96,15 @@ void HTMLSelectElement::didRecalcStyle(Style::Change styleChange)
const AtomicString& HTMLSelectElement::formControlType() const
{
- DEFINE_STATIC_LOCAL(const AtomicString, selectMultiple, ("select-multiple", AtomicString::ConstructFromLiteral));
- DEFINE_STATIC_LOCAL(const AtomicString, selectOne, ("select-one", AtomicString::ConstructFromLiteral));
+ static NeverDestroyed<const AtomicString> selectMultiple("select-multiple", AtomicString::ConstructFromLiteral);
+ static NeverDestroyed<const AtomicString> selectOne("select-one", AtomicString::ConstructFromLiteral);
return m_multiple ? selectMultiple : selectOne;
}
void HTMLSelectElement::deselectItems(HTMLOptionElement* excludeElement)
{
deselectItemsWithoutValidation(excludeElement);
- setNeedsValidityCheck();
+ updateValidity();
}
void HTMLSelectElement::optionSelectedByUser(int optionIndex, bool fireOnChangeNow, bool allowMultipleSelection)
@@ -113,7 +113,7 @@ void HTMLSelectElement::optionSelectedByUser(int optionIndex, bool fireOnChangeN
// This produces that same behavior for changes triggered by other code running on behalf of the user.
if (!usesMenuList()) {
updateSelectedState(optionToListIndex(optionIndex), allowMultipleSelection, false);
- setNeedsValidityCheck();
+ updateValidity();
if (fireOnChangeNow)
listBoxOnChange();
return;
@@ -149,8 +149,8 @@ bool HTMLSelectElement::hasPlaceholderLabelOption() const
ASSERT(listIndex >= 0);
if (listIndex < 0)
return false;
- HTMLOptionElement* option = toHTMLOptionElement(listItems()[listIndex]);
- return !listIndex && option->value().isEmpty();
+ HTMLOptionElement& option = downcast<HTMLOptionElement>(*listItems()[listIndex]);
+ return !listIndex && option.value().isEmpty();
}
String HTMLSelectElement::validationMessage() const
@@ -184,7 +184,7 @@ void HTMLSelectElement::listBoxSelectItem(int listIndex, bool allowMultiplySelec
optionSelectedByUser(listToOptionIndex(listIndex), fireOnChangeNow, false);
else {
updateSelectedState(listIndex, allowMultiplySelections, shift);
- setNeedsValidityCheck();
+ updateValidity();
if (fireOnChangeNow)
listBoxOnChange();
}
@@ -193,9 +193,7 @@ void HTMLSelectElement::listBoxSelectItem(int listIndex, bool allowMultiplySelec
bool HTMLSelectElement::usesMenuList() const
{
#if !PLATFORM(IOS)
- const Page* page = document().page();
- RefPtr<RenderTheme> renderTheme = page ? &page->theme() : RenderTheme::defaultTheme();
- if (renderTheme->delegatesMenuListRendering())
+ if (RenderTheme::themeForPage(document().page())->delegatesMenuListRendering())
return true;
return !m_multiple && m_size <= 1;
@@ -218,66 +216,63 @@ int HTMLSelectElement::activeSelectionEndListIndex() const
return lastSelectedListIndex();
}
-void HTMLSelectElement::add(HTMLElement* element, HTMLElement* before, ExceptionCode& ec)
+ExceptionOr<void> HTMLSelectElement::add(const OptionOrOptGroupElement& element, const std::optional<HTMLElementOrInt>& before)
{
- if (!element || !(element->hasLocalName(optionTag) || element->hasLocalName(hrTag)))
- return;
+ HTMLElement* beforeElement = nullptr;
+ if (before) {
+ beforeElement = WTF::switchOn(before.value(),
+ [](const RefPtr<HTMLElement>& element) -> HTMLElement* { return element.get(); },
+ [this](int index) -> HTMLElement* { return item(index); }
+ );
+ }
+ HTMLElement& toInsert = WTF::switchOn(element,
+ [](const auto& htmlElement) -> HTMLElement& { return *htmlElement; }
+ );
- // Make sure the element is ref'd and deref'd so we don't leak it.
- Ref<HTMLElement> protectNewChild(*element);
- insertBefore(element, before, ec);
- setNeedsValidityCheck();
+ return insertBefore(toInsert, beforeElement);
}
-void HTMLSelectElement::removeByIndex(int optionIndex)
+void HTMLSelectElement::remove(int optionIndex)
{
int listIndex = optionToListIndex(optionIndex);
if (listIndex < 0)
return;
- listItems()[listIndex]->remove(IGNORE_EXCEPTION);
+ listItems()[listIndex]->remove();
}
-void HTMLSelectElement::remove(HTMLOptionElement* option)
+ExceptionOr<void> HTMLSelectElement::remove(HTMLOptionElement& option)
{
- if (option->ownerSelectElement() != this)
- return;
+ if (option.ownerSelectElement() != this)
+ return { };
- option->remove(IGNORE_EXCEPTION);
+ return option.remove();
}
String HTMLSelectElement::value() const
{
- const Vector<HTMLElement*>& items = listItems();
- for (unsigned i = 0; i < items.size(); i++) {
- if (items[i]->hasLocalName(optionTag)) {
- HTMLOptionElement* option = toHTMLOptionElement(items[i]);
- if (option->selected())
- return option->value();
+ for (auto* item : listItems()) {
+ if (is<HTMLOptionElement>(*item)) {
+ HTMLOptionElement& option = downcast<HTMLOptionElement>(*item);
+ if (option.selected())
+ return option.value();
}
}
- return "";
+ return emptyString();
}
-void HTMLSelectElement::setValue(const String &value)
+void HTMLSelectElement::setValue(const String& value)
{
- // We clear the previously selected option(s) when needed, to guarantee calling setSelectedIndex() only once.
- if (value.isNull()) {
- setSelectedIndex(-1);
- return;
- }
-
// Find the option with value() matching the given parameter and make it the current selection.
- const Vector<HTMLElement*>& items = listItems();
unsigned optionIndex = 0;
- for (unsigned i = 0; i < items.size(); i++) {
- if (items[i]->hasLocalName(optionTag)) {
- if (toHTMLOptionElement(items[i])->value() == value) {
+ for (auto* item : listItems()) {
+ if (is<HTMLOptionElement>(*item)) {
+ if (downcast<HTMLOptionElement>(*item).value() == value) {
setSelectedIndex(optionIndex);
return;
}
- optionIndex++;
+ ++optionIndex;
}
}
@@ -298,27 +293,19 @@ bool HTMLSelectElement::isPresentationAttribute(const QualifiedName& name) const
void HTMLSelectElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
{
if (name == sizeAttr) {
- int oldSize = m_size;
- // Set the attribute value to a number.
- // This is important since the style rules for this attribute can determine the appearance property.
- int size = value.toInt();
- AtomicString attrSize = AtomicString::number(size);
- if (attrSize != value) {
- // FIXME: This is horribly factored.
- if (Attribute* sizeAttribute = ensureUniqueElementData().findAttributeByName(sizeAttr))
- sizeAttribute->setValue(attrSize);
- }
- size = std::max(size, 1);
+ unsigned oldSize = m_size;
+ unsigned size = limitToOnlyHTMLNonNegative(value);
// Ensure that we've determined selectedness of the items at least once prior to changing the size.
if (oldSize != size)
updateListItemSelectedStates();
m_size = size;
- setNeedsValidityCheck();
+ updateValidity();
if (m_size != oldSize) {
- setNeedsStyleRecalc(ReconstructRenderTree);
+ invalidateStyleAndRenderersForSubtree();
setRecalcListItems();
+ updateValidity();
}
} else if (name == multipleAttr)
parseMultipleAttribute(value);
@@ -329,7 +316,7 @@ void HTMLSelectElement::parseAttribute(const QualifiedName& name, const AtomicSt
HTMLFormControlElementWithState::parseAttribute(name, value);
}
-bool HTMLSelectElement::isKeyboardFocusable(KeyboardEvent* event) const
+bool HTMLSelectElement::isKeyboardFocusable(KeyboardEvent& event) const
{
if (renderer())
return isFocusable();
@@ -348,14 +335,14 @@ bool HTMLSelectElement::canSelectAll() const
return !usesMenuList();
}
-RenderPtr<RenderElement> HTMLSelectElement::createElementRenderer(PassRef<RenderStyle> style)
+RenderPtr<RenderElement> HTMLSelectElement::createElementRenderer(RenderStyle&& style, const RenderTreePosition&)
{
#if !PLATFORM(IOS)
if (usesMenuList())
- return createRenderer<RenderMenuList>(*this, std::move(style));
- return createRenderer<RenderListBox>(*this, std::move(style));
+ return createRenderer<RenderMenuList>(*this, WTFMove(style));
+ return createRenderer<RenderListBox>(*this, WTFMove(style));
#else
- return createRenderer<RenderMenuList>(*this, std::move(style));
+ return createRenderer<RenderMenuList>(*this, WTFMove(style));
#endif
}
@@ -365,19 +352,19 @@ bool HTMLSelectElement::childShouldCreateRenderer(const Node& child) const
return false;
#if !PLATFORM(IOS)
if (!usesMenuList())
- return isHTMLOptionElement(child) || isHTMLOptGroupElement(child) || validationMessageShadowTreeContains(child);
+ return is<HTMLOptionElement>(child) || is<HTMLOptGroupElement>(child) || validationMessageShadowTreeContains(child);
#endif
return validationMessageShadowTreeContains(child);
}
-PassRefPtr<HTMLCollection> HTMLSelectElement::selectedOptions()
+Ref<HTMLCollection> HTMLSelectElement::selectedOptions()
{
- return ensureCachedHTMLCollection(SelectedOptions);
+ return ensureRareData().ensureNodeLists().addCachedCollection<GenericCachedHTMLCollection<CollectionTypeTraits<SelectedOptions>::traversalType>>(*this, SelectedOptions);
}
-PassRefPtr<HTMLOptionsCollection> HTMLSelectElement::options()
+Ref<HTMLOptionsCollection> HTMLSelectElement::options()
{
- return static_cast<HTMLOptionsCollection*>(ensureCachedHTMLCollection(SelectOptions).get());
+ return ensureRareData().ensureNodeLists().addCachedCollection<HTMLOptionsCollection>(*this, SelectOptions);
}
void HTMLSelectElement::updateListItemSelectedStates()
@@ -389,7 +376,7 @@ void HTMLSelectElement::updateListItemSelectedStates()
void HTMLSelectElement::childrenChanged(const ChildChange& change)
{
setRecalcListItems();
- setNeedsValidityCheck();
+ updateValidity();
m_lastOnChangeSelection.clear();
HTMLFormControlElementWithState::childrenChanged(change);
@@ -398,12 +385,9 @@ void HTMLSelectElement::childrenChanged(const ChildChange& change)
void HTMLSelectElement::optionElementChildrenChanged()
{
setRecalcListItems();
- setNeedsValidityCheck();
-
- if (renderer()) {
- if (AXObjectCache* cache = renderer()->document().existingAXObjectCache())
- cache->childrenChanged(this);
- }
+ updateValidity();
+ if (auto* cache = document().existingAXObjectCache())
+ cache->childrenChanged(this);
}
void HTMLSelectElement::accessKeyAction(bool sendMouseEvents)
@@ -416,7 +400,7 @@ void HTMLSelectElement::setMultiple(bool multiple)
{
bool oldMultiple = this->multiple();
int oldSelectedIndex = selectedIndex();
- setAttribute(multipleAttr, multiple ? "" : 0);
+ setAttributeWithoutSynchronization(multipleAttr, multiple ? emptyAtom : nullAtom);
// Restore selectedIndex after changing the multiple flag to preserve
// selection as single-line and multi-line has different defaults.
@@ -424,81 +408,92 @@ void HTMLSelectElement::setMultiple(bool multiple)
setSelectedIndex(oldSelectedIndex);
}
-void HTMLSelectElement::setSize(int size)
+void HTMLSelectElement::setSize(unsigned size)
{
- setIntegralAttribute(sizeAttr, size);
+ setUnsignedIntegralAttribute(sizeAttr, limitToOnlyHTMLNonNegative(size));
}
-Node* HTMLSelectElement::namedItem(const AtomicString& name)
+HTMLOptionElement* HTMLSelectElement::namedItem(const AtomicString& name)
{
return options()->namedItem(name);
}
-Node* HTMLSelectElement::item(unsigned index)
+HTMLOptionElement* HTMLSelectElement::item(unsigned index)
{
return options()->item(index);
}
-void HTMLSelectElement::setOption(unsigned index, HTMLOptionElement* option, ExceptionCode& ec)
+ExceptionOr<void> HTMLSelectElement::setItem(unsigned index, HTMLOptionElement* option)
{
- ec = 0;
+ if (!option) {
+ remove(index);
+ return { };
+ }
+
if (index > maxSelectItems - 1)
index = maxSelectItems - 1;
+
int diff = index - length();
- RefPtr<HTMLElement> before = 0;
+
+ RefPtr<HTMLOptionElement> before;
// Out of array bounds? First insert empty dummies.
if (diff > 0) {
- setLength(index, ec);
+ auto result = setLength(index);
+ if (result.hasException())
+ return result;
// Replace an existing entry?
} else if (diff < 0) {
- before = toHTMLElement(options()->item(index+1));
- removeByIndex(index);
+ before = item(index + 1);
+ remove(index);
}
+
// Finally add the new element.
- if (!ec) {
- add(option, before.get(), ec);
- if (diff >= 0 && option->selected())
- optionSelectionStateChanged(option, true);
- }
+ auto result = add(option, HTMLElementOrInt { before.get() });
+ if (result.hasException())
+ return result;
+
+ if (diff >= 0 && option->selected())
+ optionSelectionStateChanged(*option, true);
+
+ return { };
}
-void HTMLSelectElement::setLength(unsigned newLen, ExceptionCode& ec)
+ExceptionOr<void> HTMLSelectElement::setLength(unsigned newLength)
{
- ec = 0;
- if (newLen > maxSelectItems)
- newLen = maxSelectItems;
- int diff = length() - newLen;
+ if (newLength > length() && newLength > maxSelectItems) {
+ document().addConsoleMessage(MessageSource::Other, MessageLevel::Warning, String::format("Blocked attempt to expand the option list to %u items. The maximum number of items allowed is %u.", newLength, maxSelectItems));
+ return { };
+ }
+
+ int diff = length() - newLength;
if (diff < 0) { // Add dummy elements.
do {
- RefPtr<Element> option = document().createElement(optionTag, false);
- ASSERT(option);
- add(toHTMLElement(option.get()), 0, ec);
- if (ec)
- break;
+ auto result = add(HTMLOptionElement::create(document()).ptr(), std::nullopt);
+ if (result.hasException())
+ return result;
} while (++diff);
} else {
- const Vector<HTMLElement*>& items = listItems();
+ auto& 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;
+ Vector<Ref<HTMLOptionElement>> itemsToRemove;
size_t optionIndex = 0;
- for (size_t i = 0; i < items.size(); ++i) {
- Element* item = items[i];
- if (item->hasLocalName(optionTag) && optionIndex++ >= newLen) {
+ for (auto& item : items) {
+ if (is<HTMLOptionElement>(*item) && optionIndex++ >= newLength) {
ASSERT(item->parentNode());
- itemsToRemove.append(item);
+ itemsToRemove.append(downcast<HTMLOptionElement>(*item));
}
}
- for (size_t i = 0; i < itemsToRemove.size(); ++i) {
- Element* item = itemsToRemove[i].get();
- if (item->parentNode())
- item->parentNode()->removeChild(item, ec);
- }
+ // FIXME: Clients can detect what order we remove the options in; is it good to remove them in ascending order?
+ // FIXME: This ignores exceptions. A previous version passed through the exception only for the last item removed.
+ // What exception behavior do we want?
+ for (auto& item : itemsToRemove)
+ item->remove();
}
- setNeedsValidityCheck();
+ return { };
}
bool HTMLSelectElement::isRequiredFormControl() const
@@ -506,12 +501,14 @@ bool HTMLSelectElement::isRequiredFormControl() const
return isRequired();
}
-#if PLATFORM(IOS)
bool HTMLSelectElement::willRespondToMouseClickEvents()
{
+#if PLATFORM(IOS)
return !isDisabledFormControl();
-}
+#else
+ return HTMLFormControlElementWithState::willRespondToMouseClickEvents();
#endif
+}
// Returns the 1st valid item |skip| items from |listIndex| in direction |direction| if there is one.
// Otherwise, it returns the valid item closest to that boundary which is past |listIndex| if there is one.
@@ -520,12 +517,12 @@ bool HTMLSelectElement::willRespondToMouseClickEvents()
int HTMLSelectElement::nextValidIndex(int listIndex, SkipDirection direction, int skip) const
{
ASSERT(direction == -1 || direction == 1);
- const Vector<HTMLElement*>& listItems = this->listItems();
+ auto& listItems = this->listItems();
int lastGoodIndex = listIndex;
int size = listItems.size();
for (listIndex += direction; listIndex >= 0 && listIndex < size; listIndex += direction) {
--skip;
- if (!listItems[listIndex]->isDisabledFormControl() && isHTMLOptionElement(listItems[listIndex])) {
+ if (!listItems[listIndex]->isDisabledFormControl() && is<HTMLOptionElement>(*listItems[listIndex])) {
lastGoodIndex = listIndex;
if (skip <= 0)
break;
@@ -548,7 +545,7 @@ int HTMLSelectElement::previousSelectableListIndex(int startIndex) const
int HTMLSelectElement::firstSelectableListIndex() const
{
- const Vector<HTMLElement*>& items = listItems();
+ auto& items = listItems();
int index = nextValidIndex(items.size(), SkipBackwards, INT_MAX);
if (static_cast<size_t>(index) == items.size())
return -1;
@@ -563,17 +560,19 @@ int HTMLSelectElement::lastSelectableListIndex() const
// 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();
+ auto& items = listItems();
+
// Can't use m_size because renderer forces a minimum size.
int pageSize = 0;
- if (renderer()->isListBox())
- pageSize = toRenderListBox(renderer())->size() - 1; // -1 so we still show context.
+ auto* renderer = this->renderer();
+ if (is<RenderListBox>(*renderer))
+ pageSize = downcast<RenderListBox>(*renderer).size() - 1; // -1 so we still show context.
// One page away, but not outside valid bounds.
// If there is a valid option item one page away, the index is chosen.
// If there is no exact one page away valid option, returns startIndex or the most far index.
- int edgeIndex = (direction == SkipForwards) ? 0 : (items.size() - 1);
- int skipAmount = pageSize + ((direction == SkipForwards) ? startIndex : (edgeIndex - startIndex));
+ int edgeIndex = direction == SkipForwards ? 0 : items.size() - 1;
+ int skipAmount = pageSize + (direction == SkipForwards ? startIndex : edgeIndex - startIndex);
return nextValidIndex(edgeIndex, direction, skipAmount);
}
@@ -595,7 +594,7 @@ void HTMLSelectElement::selectAll()
updateListBoxSelection(false);
listBoxOnChange();
- setNeedsValidityCheck();
+ updateValidity();
}
void HTMLSelectElement::saveLastSelection()
@@ -606,11 +605,8 @@ void HTMLSelectElement::saveLastSelection()
}
m_lastOnChangeSelection.clear();
- const Vector<HTMLElement*>& items = listItems();
- for (unsigned i = 0; i < items.size(); ++i) {
- HTMLElement* element = items[i];
- m_lastOnChangeSelection.append(isHTMLOptionElement(element) && toHTMLOptionElement(element)->selected());
- }
+ for (auto& element : listItems())
+ m_lastOnChangeSelection.append(is<HTMLOptionElement>(*element) && downcast<HTMLOptionElement>(*element).selected());
}
void HTMLSelectElement::setActiveSelectionAnchorIndex(int index)
@@ -621,11 +617,8 @@ void HTMLSelectElement::setActiveSelectionAnchorIndex(int index)
// selection pivots around this anchor index.
m_cachedStateForActiveSelection.clear();
- const Vector<HTMLElement*>& items = listItems();
- for (unsigned i = 0; i < items.size(); ++i) {
- HTMLElement* element = items[i];
- m_cachedStateForActiveSelection.append(isHTMLOptionElement(element) && toHTMLOptionElement(element)->selected());
- }
+ for (auto& element : listItems())
+ m_cachedStateForActiveSelection.append(is<HTMLOptionElement>(*element) && downcast<HTMLOptionElement>(*element).selected());
}
void HTMLSelectElement::setActiveSelectionEndIndex(int index)
@@ -635,40 +628,42 @@ void HTMLSelectElement::setActiveSelectionEndIndex(int index)
void HTMLSelectElement::updateListBoxSelection(bool deselectOtherOptions)
{
+ ASSERT(renderer());
+
#if !PLATFORM(IOS)
- ASSERT(renderer() && (renderer()->isListBox() || m_multiple));
+ ASSERT(renderer()->isListBox() || m_multiple);
#else
- ASSERT(renderer() && (renderer()->isMenuList() || m_multiple));
+ ASSERT(renderer()->isMenuList() || m_multiple);
#endif
+
ASSERT(!listItems().size() || m_activeSelectionAnchorIndex >= 0);
unsigned start = std::min(m_activeSelectionAnchorIndex, m_activeSelectionEndIndex);
unsigned end = std::max(m_activeSelectionAnchorIndex, m_activeSelectionEndIndex);
- const Vector<HTMLElement*>& items = listItems();
+ auto& items = listItems();
for (unsigned i = 0; i < items.size(); ++i) {
- HTMLElement* element = items[i];
- if (!isHTMLOptionElement(element) || toHTMLOptionElement(element)->isDisabledFormControl())
+ auto& element = *items[i];
+ if (!is<HTMLOptionElement>(element) || downcast<HTMLOptionElement>(element).isDisabledFormControl())
continue;
if (i >= start && i <= end)
- toHTMLOptionElement(element)->setSelectedState(m_activeSelectionState);
+ downcast<HTMLOptionElement>(element).setSelectedState(m_activeSelectionState);
else if (deselectOtherOptions || i >= m_cachedStateForActiveSelection.size())
- toHTMLOptionElement(element)->setSelectedState(false);
+ downcast<HTMLOptionElement>(element).setSelectedState(false);
else
- toHTMLOptionElement(element)->setSelectedState(m_cachedStateForActiveSelection[i]);
+ downcast<HTMLOptionElement>(element).setSelectedState(m_cachedStateForActiveSelection[i]);
}
scrollToSelection();
- setNeedsValidityCheck();
- notifyFormStateChanged();
+ updateValidity();
}
void HTMLSelectElement::listBoxOnChange()
{
ASSERT(!usesMenuList() || m_multiple);
- const Vector<HTMLElement*>& items = listItems();
+ auto& items = listItems();
// If the cached selection list is empty, or the size has changed, then fire
// dispatchFormControlChangeEvent, and return early.
@@ -680,15 +675,17 @@ void HTMLSelectElement::listBoxOnChange()
// Update m_lastOnChangeSelection and fire dispatchFormControlChangeEvent.
bool fireOnChange = false;
for (unsigned i = 0; i < items.size(); ++i) {
- HTMLElement* element = items[i];
- bool selected = isHTMLOptionElement(element) && toHTMLOptionElement(element)->selected();
+ auto& element = *items[i];
+ bool selected = is<HTMLOptionElement>(element) && downcast<HTMLOptionElement>(element).selected();
if (selected != m_lastOnChangeSelection[i])
fireOnChange = true;
m_lastOnChangeSelection[i] = selected;
}
- if (fireOnChange)
+ if (fireOnChange) {
+ dispatchInputEvent();
dispatchFormControlChangeEvent();
+ }
}
void HTMLSelectElement::dispatchChangeEventForMenuList()
@@ -699,6 +696,7 @@ void HTMLSelectElement::dispatchChangeEventForMenuList()
if (m_lastOnChangeIndex != selected && m_isProcessingUserDrivenChange) {
m_lastOnChangeIndex = selected;
m_isProcessingUserDrivenChange = false;
+ dispatchInputEvent();
dispatchFormControlChangeEvent();
}
}
@@ -709,26 +707,26 @@ void HTMLSelectElement::scrollToSelection()
if (usesMenuList())
return;
- auto renderer = this->renderer();
- if (!renderer || !renderer->isListBox())
+ auto* renderer = this->renderer();
+ if (!is<RenderListBox>(renderer))
return;
- toRenderListBox(renderer)->selectionChanged();
+ downcast<RenderListBox>(*renderer).selectionChanged();
#else
- if (auto renderer = this->renderer())
+ if (auto* renderer = this->renderer())
renderer->repaint();
#endif
}
void HTMLSelectElement::setOptionsChangedOnRenderer()
{
- if (auto renderer = this->renderer()) {
+ if (auto* renderer = this->renderer()) {
#if !PLATFORM(IOS)
- if (renderer->isMenuList())
- toRenderMenuList(renderer)->setOptionsChanged(true);
+ if (is<RenderMenuList>(*renderer))
+ downcast<RenderMenuList>(*renderer).setOptionsChanged(true);
else
- toRenderListBox(renderer)->setOptionsChanged(true);
+ downcast<RenderListBox>(*renderer).setOptionsChanged(true);
#else
- toRenderMenuList(renderer)->setOptionsChanged(true);
+ downcast<RenderMenuList>(*renderer).setOptionsChanged(true);
#endif
}
}
@@ -751,7 +749,7 @@ const Vector<HTMLElement*>& HTMLSelectElement::listItems() const
void HTMLSelectElement::invalidateSelectedItems()
{
if (HTMLCollection* collection = cachedHTMLCollection(SelectedOptions))
- collection->invalidateCache();
+ collection->invalidateCache(document());
}
void HTMLSelectElement::setRecalcListItems()
@@ -760,18 +758,15 @@ void HTMLSelectElement::setRecalcListItems()
// Manual selection anchor is reset when manipulating the select programmatically.
m_activeSelectionAnchorIndex = -1;
setOptionsChangedOnRenderer();
- setNeedsStyleRecalc();
- if (!inDocument()) {
+ invalidateStyleForSubtree();
+ if (!isConnected()) {
if (HTMLCollection* collection = cachedHTMLCollection(SelectOptions))
- collection->invalidateCache();
+ collection->invalidateCache(document());
}
- if (!inDocument())
+ if (!isConnected())
invalidateSelectedItems();
-
- if (renderer()) {
- if (AXObjectCache* cache = renderer()->document().existingAXObjectCache())
- cache->childrenChanged(this);
- }
+ if (auto* cache = document().existingAXObjectCache())
+ cache->childrenChanged(this);
}
void HTMLSelectElement::recalcListItems(bool updateSelectedStates) const
@@ -782,44 +777,42 @@ void HTMLSelectElement::recalcListItems(bool updateSelectedStates) const
HTMLOptionElement* foundSelected = 0;
HTMLOptionElement* firstOption = 0;
- for (Element* currentElement = ElementTraversal::firstWithin(this); currentElement; ) {
- if (!currentElement->isHTMLElement()) {
- currentElement = ElementTraversal::nextSkippingChildren(currentElement, this);
+ for (Element* currentElement = ElementTraversal::firstWithin(*this); currentElement; ) {
+ if (!is<HTMLElement>(*currentElement)) {
+ currentElement = ElementTraversal::nextSkippingChildren(*currentElement, this);
continue;
}
- HTMLElement* current = toHTMLElement(currentElement);
+ HTMLElement& current = downcast<HTMLElement>(*currentElement);
- // 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 (isHTMLOptGroupElement(current)) {
- m_listItems.append(current);
+ // Only consider optgroup elements that are direct children of the select element.
+ if (is<HTMLOptGroupElement>(current) && current.parentNode() == this) {
+ m_listItems.append(&current);
if (Element* nextElement = ElementTraversal::firstWithin(current)) {
currentElement = nextElement;
continue;
}
}
- if (isHTMLOptionElement(current)) {
- m_listItems.append(current);
+ if (is<HTMLOptionElement>(current)) {
+ m_listItems.append(&current);
if (updateSelectedStates && !m_multiple) {
- HTMLOptionElement* option = toHTMLOptionElement(current);
+ HTMLOptionElement& option = downcast<HTMLOptionElement>(current);
if (!firstOption)
- firstOption = option;
- if (option->selected()) {
+ firstOption = &option;
+ if (option.selected()) {
if (foundSelected)
foundSelected->setSelectedState(false);
- foundSelected = option;
- } else if (m_size <= 1 && !foundSelected && !option->isDisabledFormControl()) {
- foundSelected = option;
+ foundSelected = &option;
+ } else if (m_size <= 1 && !foundSelected && !option.isDisabledFormControl()) {
+ foundSelected = &option;
foundSelected->setSelectedState(true);
}
}
}
- if (current->hasTagName(hrTag))
- m_listItems.append(current);
+ if (current.hasTagName(hrTag))
+ m_listItems.append(&current);
// In conforming HTML code, only <optgroup> and <option> will be found
// within a <select>. We call NodeTraversal::nextSkippingChildren so that we only step
@@ -827,7 +820,7 @@ void HTMLSelectElement::recalcListItems(bool updateSelectedStates) const
// with the case where odd tags like a <div> have been added but we
// handle this because such tags have already been removed from the
// <select>'s subtree at this point.
- currentElement = ElementTraversal::nextSkippingChildren(currentElement, this);
+ currentElement = ElementTraversal::nextSkippingChildren(*currentElement, this);
}
if (!foundSelected && m_size <= 1 && firstOption && !firstOption->selected())
@@ -839,11 +832,9 @@ int HTMLSelectElement::selectedIndex() const
unsigned index = 0;
// Return the number of the first option selected.
- const Vector<HTMLElement*>& items = listItems();
- for (size_t i = 0; i < items.size(); ++i) {
- HTMLElement* element = items[i];
- if (isHTMLOptionElement(element)) {
- if (toHTMLOptionElement(element)->selected())
+ for (auto& element : listItems()) {
+ if (is<HTMLOptionElement>(*element)) {
+ if (downcast<HTMLOptionElement>(*element).selected())
return index;
++index;
}
@@ -857,11 +848,11 @@ void HTMLSelectElement::setSelectedIndex(int index)
selectOption(index, DeselectOtherOptions);
}
-void HTMLSelectElement::optionSelectionStateChanged(HTMLOptionElement* option, bool optionIsSelected)
+void HTMLSelectElement::optionSelectionStateChanged(HTMLOptionElement& option, bool optionIsSelected)
{
- ASSERT(option->ownerSelectElement() == this);
+ ASSERT(option.ownerSelectElement() == this);
if (optionIsSelected)
- selectOption(option->index());
+ selectOption(option.index());
else if (!usesMenuList())
selectOption(-1);
else
@@ -872,26 +863,28 @@ void HTMLSelectElement::selectOption(int optionIndex, SelectOptionFlags flags)
{
bool shouldDeselect = !m_multiple || (flags & DeselectOtherOptions);
- const Vector<HTMLElement*>& items = listItems();
+ auto& items = listItems();
int listIndex = optionToListIndex(optionIndex);
- HTMLElement* element = 0;
- if (listIndex >= 0) {
+ HTMLElement* element = nullptr;
+ if (listIndex >= 0)
element = items[listIndex];
- if (isHTMLOptionElement(element)) {
- if (m_activeSelectionAnchorIndex < 0 || shouldDeselect)
- setActiveSelectionAnchorIndex(listIndex);
- if (m_activeSelectionEndIndex < 0 || shouldDeselect)
- setActiveSelectionEndIndex(listIndex);
- toHTMLOptionElement(element)->setSelectedState(true);
- }
- }
if (shouldDeselect)
deselectItemsWithoutValidation(element);
+ if (is<HTMLOptionElement>(element)) {
+ if (m_activeSelectionAnchorIndex < 0 || shouldDeselect)
+ setActiveSelectionAnchorIndex(listIndex);
+ if (m_activeSelectionEndIndex < 0 || shouldDeselect)
+ setActiveSelectionEndIndex(listIndex);
+ downcast<HTMLOptionElement>(*element).setSelectedState(true);
+ }
+
+ updateValidity();
+
// For the menu list case, this is what makes the selected element appear.
- if (auto renderer = this->renderer())
+ if (auto* renderer = this->renderer())
renderer->updateFromElement();
scrollToSelection();
@@ -900,28 +893,25 @@ void HTMLSelectElement::selectOption(int optionIndex, SelectOptionFlags flags)
m_isProcessingUserDrivenChange = flags & UserDriven;
if (flags & DispatchChangeEvent)
dispatchChangeEventForMenuList();
- if (auto renderer = this->renderer()) {
- if (renderer->isMenuList())
- toRenderMenuList(renderer)->didSetSelectedIndex(listIndex);
+ if (auto* renderer = this->renderer()) {
+ if (is<RenderMenuList>(*renderer))
+ downcast<RenderMenuList>(*renderer).didSetSelectedIndex(listIndex);
else
- toRenderListBox(renderer)->selectionChanged();
+ downcast<RenderListBox>(*renderer).selectionChanged();
}
}
-
- setNeedsValidityCheck();
- notifyFormStateChanged();
}
int HTMLSelectElement::optionToListIndex(int optionIndex) const
{
- const Vector<HTMLElement*>& items = listItems();
+ auto& 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 (isHTMLOptionElement(items[listIndex])) {
+ if (is<HTMLOptionElement>(*items[listIndex])) {
++optionIndex2;
if (optionIndex2 == optionIndex)
return listIndex;
@@ -933,61 +923,57 @@ int HTMLSelectElement::optionToListIndex(int optionIndex) const
int HTMLSelectElement::listToOptionIndex(int listIndex) const
{
- const Vector<HTMLElement*>& items = listItems();
- if (listIndex < 0 || listIndex >= static_cast<int>(items.size()) || !isHTMLOptionElement(items[listIndex]))
+ auto& items = listItems();
+ if (listIndex < 0 || listIndex >= static_cast<int>(items.size()) || !is<HTMLOptionElement>(*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 (isHTMLOptionElement(items[i]))
+ if (is<HTMLOptionElement>(*items[i]))
++optionIndex;
}
return optionIndex;
}
-void HTMLSelectElement::dispatchFocusEvent(PassRefPtr<Element> oldFocusedElement, FocusDirection direction)
+void HTMLSelectElement::dispatchFocusEvent(RefPtr<Element>&& oldFocusedElement, FocusDirection direction)
{
// Save the selection so it can be compared to the new selection when
// dispatching change events during blur event dispatch.
if (usesMenuList())
saveLastSelection();
- HTMLFormControlElementWithState::dispatchFocusEvent(oldFocusedElement, direction);
+ HTMLFormControlElementWithState::dispatchFocusEvent(WTFMove(oldFocusedElement), direction);
}
-void HTMLSelectElement::dispatchBlurEvent(PassRefPtr<Element> newFocusedElement)
+void HTMLSelectElement::dispatchBlurEvent(RefPtr<Element>&& newFocusedElement)
{
// We only need to fire change events here for menu lists, because we fire
// change events for list boxes whenever the selection change is actually made.
// This matches other browsers' behavior.
if (usesMenuList())
dispatchChangeEventForMenuList();
- HTMLFormControlElementWithState::dispatchBlurEvent(newFocusedElement);
+ HTMLFormControlElementWithState::dispatchBlurEvent(WTFMove(newFocusedElement));
}
void HTMLSelectElement::deselectItemsWithoutValidation(HTMLElement* excludeElement)
{
- const Vector<HTMLElement*>& items = listItems();
- for (unsigned i = 0; i < items.size(); ++i) {
- HTMLElement* element = items[i];
- if (element != excludeElement && isHTMLOptionElement(element))
- toHTMLOptionElement(element)->setSelectedState(false);
+ for (auto& element : listItems()) {
+ if (element != excludeElement && is<HTMLOptionElement>(*element))
+ downcast<HTMLOptionElement>(*element).setSelectedState(false);
}
}
FormControlState HTMLSelectElement::saveFormControlState() const
{
- const Vector<HTMLElement*>& items = listItems();
- size_t length = items.size();
FormControlState state;
- for (unsigned i = 0; i < length; ++i) {
- if (!isHTMLOptionElement(items[i]))
+ for (auto& element : listItems()) {
+ if (!is<HTMLOptionElement>(*element))
continue;
- HTMLOptionElement* option = toHTMLOptionElement(items[i]);
- if (!option->selected())
+ HTMLOptionElement& option = downcast<HTMLOptionElement>(*element);
+ if (!option.selected())
continue;
- state.append(option->value());
+ state.append(option.value());
if (!multiple())
break;
}
@@ -996,12 +982,12 @@ FormControlState HTMLSelectElement::saveFormControlState() const
size_t HTMLSelectElement::searchOptionsForValue(const String& value, size_t listIndexStart, size_t listIndexEnd) const
{
- const Vector<HTMLElement*>& items = listItems();
+ auto& items = listItems();
size_t loopEndIndex = std::min(items.size(), listIndexEnd);
for (size_t i = listIndexStart; i < loopEndIndex; ++i) {
- if (!items[i]->hasLocalName(optionTag))
+ if (!is<HTMLOptionElement>(*items[i]))
continue;
- if (toHTMLOptionElement(items[i])->value() == value)
+ if (downcast<HTMLOptionElement>(*items[i]).value() == value)
return i;
}
return notFound;
@@ -1011,21 +997,21 @@ void HTMLSelectElement::restoreFormControlState(const FormControlState& state)
{
recalcListItems();
- const Vector<HTMLElement*>& items = listItems();
+ auto& items = listItems();
size_t itemsSize = items.size();
if (!itemsSize)
return;
- for (size_t i = 0; i < itemsSize; ++i) {
- if (!items[i]->hasLocalName(optionTag))
+ for (auto& element : items) {
+ if (!is<HTMLOptionElement>(*element))
continue;
- toHTMLOptionElement(items[i])->setSelectedState(false);
+ downcast<HTMLOptionElement>(*element).setSelectedState(false);
}
if (!multiple()) {
size_t foundIndex = searchOptionsForValue(state[0], 0, itemsSize);
if (foundIndex != notFound)
- toHTMLOptionElement(items[foundIndex])->setSelectedState(true);
+ downcast<HTMLOptionElement>(*items[foundIndex]).setSelectedState(true);
} else {
size_t startIndex = 0;
for (size_t i = 0; i < state.valueSize(); ++i) {
@@ -1035,22 +1021,22 @@ void HTMLSelectElement::restoreFormControlState(const FormControlState& state)
foundIndex = searchOptionsForValue(value, 0, startIndex);
if (foundIndex == notFound)
continue;
- toHTMLOptionElement(items[foundIndex])->setSelectedState(true);
+ downcast<HTMLOptionElement>(*items[foundIndex]).setSelectedState(true);
startIndex = foundIndex + 1;
}
}
setOptionsChangedOnRenderer();
- setNeedsValidityCheck();
+ updateValidity();
}
void HTMLSelectElement::parseMultipleAttribute(const AtomicString& value)
{
bool oldUsesMenuList = usesMenuList();
m_multiple = !value.isNull();
- setNeedsValidityCheck();
+ updateValidity();
if (oldUsesMenuList != usesMenuList())
- setNeedsStyleRecalc(ReconstructRenderTree);
+ invalidateStyleAndRenderersForSubtree();
}
bool HTMLSelectElement::appendFormData(FormDataList& list, bool)
@@ -1060,12 +1046,9 @@ bool HTMLSelectElement::appendFormData(FormDataList& list, bool)
return false;
bool successful = false;
- const Vector<HTMLElement*>& items = listItems();
-
- for (unsigned i = 0; i < items.size(); ++i) {
- HTMLElement* element = items[i];
- if (isHTMLOptionElement(element) && toHTMLOptionElement(element)->selected() && !toHTMLOptionElement(element)->isDisabledFormControl()) {
- list.appendData(name, toHTMLOptionElement(element)->value());
+ for (auto& element : listItems()) {
+ if (is<HTMLOptionElement>(*element) && downcast<HTMLOptionElement>(*element).selected() && !downcast<HTMLOptionElement>(*element).isDisabledFormControl()) {
+ list.appendData(name, downcast<HTMLOptionElement>(*element).value());
successful = true;
}
}
@@ -1078,42 +1061,39 @@ bool HTMLSelectElement::appendFormData(FormDataList& list, bool)
void HTMLSelectElement::reset()
{
- HTMLOptionElement* firstOption = 0;
- HTMLOptionElement* selectedOption = 0;
+ HTMLOptionElement* firstOption = nullptr;
+ HTMLOptionElement* selectedOption = nullptr;
- const Vector<HTMLElement*>& items = listItems();
- for (unsigned i = 0; i < items.size(); ++i) {
- HTMLElement* element = items[i];
- if (!isHTMLOptionElement(element))
+ for (auto& element : listItems()) {
+ if (!is<HTMLOptionElement>(*element))
continue;
- if (items[i]->fastHasAttribute(selectedAttr)) {
+ HTMLOptionElement& option = downcast<HTMLOptionElement>(*element);
+ if (option.hasAttributeWithoutSynchronization(selectedAttr)) {
if (selectedOption && !m_multiple)
selectedOption->setSelectedState(false);
- toHTMLOptionElement(element)->setSelectedState(true);
- selectedOption = toHTMLOptionElement(element);
+ option.setSelectedState(true);
+ selectedOption = &option;
} else
- toHTMLOptionElement(element)->setSelectedState(false);
+ option.setSelectedState(false);
if (!firstOption)
- firstOption = toHTMLOptionElement(element);
+ firstOption = &option;
}
if (!selectedOption && firstOption && !m_multiple && m_size <= 1)
firstOption->setSelectedState(true);
setOptionsChangedOnRenderer();
- setNeedsStyleRecalc();
- setNeedsValidityCheck();
+ invalidateStyleForSubtree();
+ updateValidity();
}
#if !PLATFORM(WIN)
+
bool HTMLSelectElement::platformHandleKeydownEvent(KeyboardEvent* event)
{
- const Page* page = document().page();
- RefPtr<RenderTheme> renderTheme = page ? &page->theme() : RenderTheme::defaultTheme();
-
- if (!renderTheme->popsMenuByArrowKeys())
+ if (!RenderTheme::themeForPage(document().page())->popsMenuByArrowKeys())
return false;
if (!isSpatialNavigationEnabled(document().frame())) {
@@ -1122,7 +1102,8 @@ bool HTMLSelectElement::platformHandleKeydownEvent(KeyboardEvent* event)
// 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())
+ auto* renderer = this->renderer();
+ if (!is<RenderMenuList>(renderer))
return true;
// Save the selection so it can be compared to the new selection
@@ -1130,7 +1111,7 @@ bool HTMLSelectElement::platformHandleKeydownEvent(KeyboardEvent* event)
// gets called from RenderMenuList::valueChanged, which gets called
// after the user makes a selection from the menu.
saveLastSelection();
- toRenderMenuList(renderer())->showPopup();
+ downcast<RenderMenuList>(*renderer).showPopup();
event->setDefaultHandled();
}
return true;
@@ -1138,20 +1119,22 @@ bool HTMLSelectElement::platformHandleKeydownEvent(KeyboardEvent* event)
return false;
}
+
#endif
-void HTMLSelectElement::menuListDefaultEventHandler(Event* event)
+void HTMLSelectElement::menuListDefaultEventHandler(Event& event)
{
- ASSERT(renderer() && renderer()->isMenuList());
+ ASSERT(renderer());
+ ASSERT(renderer()->isMenuList());
- const Page* page = document().page();
- RefPtr<RenderTheme> renderTheme = page ? &page->theme() : RenderTheme::defaultTheme();
+ RefPtr<RenderTheme> renderTheme = RenderTheme::themeForPage(document().page());
- if (event->type() == eventNames().keydownEvent) {
- if (!event->isKeyboardEvent())
+ if (event.type() == eventNames().keydownEvent) {
+ if (!is<KeyboardEvent>(event))
return;
- if (platformHandleKeydownEvent(static_cast<KeyboardEvent*>(event)))
+ KeyboardEvent& keyboardEvent = downcast<KeyboardEvent>(event);
+ if (platformHandleKeydownEvent(&keyboardEvent))
return;
// When using spatial navigation, we want to be able to navigate away
@@ -1162,15 +1145,14 @@ void HTMLSelectElement::menuListDefaultEventHandler(Event* event)
return;
}
- const String& keyIdentifier = static_cast<KeyboardEvent*>(event)->keyIdentifier();
+ const String& keyIdentifier = keyboardEvent.keyIdentifier();
bool handled = true;
- const Vector<HTMLElement*>& listItems = this->listItems();
+ auto& listItems = this->listItems();
int listIndex = optionToListIndex(selectedIndex());
// When using caret browsing, we want to be able to move the focus
// out of the select element when user hits a left or right arrow key.
- const Frame* frame = document().frame();
- if (frame && frame->settings().caretBrowsingEnabled()) {
+ if (document().settings().caretBrowsingEnabled()) {
if (keyIdentifier == "Left" || keyIdentifier == "Right")
return;
}
@@ -1194,22 +1176,23 @@ void HTMLSelectElement::menuListDefaultEventHandler(Event* event)
selectOption(listToOptionIndex(listIndex), DeselectOtherOptions | DispatchChangeEvent | UserDriven);
if (handled)
- event->setDefaultHandled();
+ keyboardEvent.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() == eventNames().keypressEvent) {
- if (!event->isKeyboardEvent())
+ if (event.type() == eventNames().keypressEvent) {
+ if (!is<KeyboardEvent>(event))
return;
- int keyCode = static_cast<KeyboardEvent*>(event)->keyCode();
+ KeyboardEvent& keyboardEvent = downcast<KeyboardEvent>(event);
+ int keyCode = keyboardEvent.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;
- event->setDefaultHandled();
+ keyboardEvent.setDefaultHandled();
return;
}
@@ -1217,9 +1200,9 @@ void HTMLSelectElement::menuListDefaultEventHandler(Event* event)
if (keyCode == ' ' || keyCode == '\r') {
focus();
- // Calling focus() may remove the renderer or change the
- // renderer type.
- if (!renderer() || !renderer()->isMenuList())
+ // Calling focus() may remove the renderer or change the renderer type.
+ auto* renderer = this->renderer();
+ if (!is<RenderMenuList>(renderer))
return;
// Save the selection so it can be compared to the new selection
@@ -1227,16 +1210,16 @@ void HTMLSelectElement::menuListDefaultEventHandler(Event* event)
// gets called from RenderMenuList::valueChanged, which gets called
// after the user makes a selection from the menu.
saveLastSelection();
- toRenderMenuList(renderer())->showPopup();
+ downcast<RenderMenuList>(*renderer).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())
+ // Calling focus() may remove the renderer or change the renderer type.
+ auto* renderer = this->renderer();
+ if (!is<RenderMenuList>(renderer))
return;
// Save the selection so it can be compared to the new selection
@@ -1244,44 +1227,42 @@ void HTMLSelectElement::menuListDefaultEventHandler(Event* event)
// gets called from RenderMenuList::valueChanged, which gets called
// after the user makes a selection from the menu.
saveLastSelection();
- toRenderMenuList(renderer())->showPopup();
+ downcast<RenderMenuList>(*renderer).showPopup();
handled = true;
} else if (keyCode == '\r') {
if (form())
- form()->submitImplicitly(event, false);
+ form()->submitImplicitly(keyboardEvent, false);
dispatchChangeEventForMenuList();
handled = true;
}
}
if (handled)
- event->setDefaultHandled();
+ keyboardEvent.setDefaultHandled();
}
- if (event->type() == eventNames().mousedownEvent && event->isMouseEvent() && static_cast<MouseEvent*>(event)->button() == LeftButton) {
+ if (event.type() == eventNames().mousedownEvent && is<MouseEvent>(event) && downcast<MouseEvent>(event).button() == LeftButton) {
focus();
#if !PLATFORM(IOS)
- if (renderer() && renderer()->isMenuList()) {
- auto& menuList = toRenderMenuList(*renderer());
- if (menuList.popupIsVisible())
- menuList.hidePopup();
- else {
- // Save the selection so it can be compared to the new
- // selection when we call onChange during selectOption,
- // which gets called from RenderMenuList::valueChanged,
- // which gets called after the user makes a selection from
- // the menu.
- saveLastSelection();
- menuList.showPopup();
- }
+ auto* renderer = this->renderer();
+ if (is<RenderMenuList>(renderer)) {
+ auto& menuList = downcast<RenderMenuList>(*renderer);
+ ASSERT(!menuList.popupIsVisible());
+ // Save the selection so it can be compared to the new
+ // selection when we call onChange during selectOption,
+ // which gets called from RenderMenuList::valueChanged,
+ // which gets called after the user makes a selection from
+ // the menu.
+ saveLastSelection();
+ menuList.showPopup();
}
#endif
- event->setDefaultHandled();
+ event.setDefaultHandled();
}
#if !PLATFORM(IOS)
- if (event->type() == eventNames().blurEvent && !focused()) {
- auto& menuList = toRenderMenuList(*renderer());
+ if (event.type() == eventNames().blurEvent && !focused()) {
+ auto& menuList = downcast<RenderMenuList>(*renderer());
if (menuList.popupIsVisible())
menuList.hidePopup();
}
@@ -1290,7 +1271,10 @@ void HTMLSelectElement::menuListDefaultEventHandler(Event* event)
void HTMLSelectElement::updateSelectedState(int listIndex, bool multi, bool shift)
{
- ASSERT(listIndex >= 0);
+ auto& items = listItems();
+ int listSize = static_cast<int>(items.size());
+ if (listIndex < 0 || listIndex >= listSize)
+ return;
// Save the selection so it can be compared to the new selection when
// dispatching change events during mouseup, or after autoscroll finishes.
@@ -1301,21 +1285,21 @@ void HTMLSelectElement::updateSelectedState(int listIndex, bool multi, bool shif
bool shiftSelect = m_multiple && shift;
bool multiSelect = m_multiple && multi && !shift;
- HTMLElement* clickedElement = listItems()[listIndex];
- if (isHTMLOptionElement(clickedElement)) {
+ auto& clickedElement = *items[listIndex];
+ if (is<HTMLOptionElement>(clickedElement)) {
// Keep track of whether an active selection (like during drag
// selection), should select or deselect.
- if (toHTMLOptionElement(clickedElement)->selected() && multiSelect)
+ if (downcast<HTMLOptionElement>(clickedElement).selected() && multiSelect)
m_activeSelectionState = false;
if (!m_activeSelectionState)
- toHTMLOptionElement(clickedElement)->setSelectedState(false);
+ downcast<HTMLOptionElement>(clickedElement).setSelectedState(false);
}
// If we're not in any special multiple selection mode, then deselect all
// other items, excluding the clicked option. If no option was clicked, then
// this will deselect all items in the list.
if (!shiftSelect && !multiSelect)
- deselectItemsWithoutValidation(clickedElement);
+ deselectItemsWithoutValidation(&clickedElement);
// If the anchor hasn't been set, and we're doing a single selection or a
// shift selection, then initialize the anchor to the first selected index.
@@ -1323,8 +1307,8 @@ void HTMLSelectElement::updateSelectedState(int listIndex, bool multi, bool shif
setActiveSelectionAnchorIndex(selectedIndex());
// Set the selection state of the clicked option.
- if (isHTMLOptionElement(clickedElement) && !toHTMLOptionElement(clickedElement)->isDisabledFormControl())
- toHTMLOptionElement(clickedElement)->setSelectedState(true);
+ if (is<HTMLOptionElement>(clickedElement) && !downcast<HTMLOptionElement>(clickedElement).isDisabledFormControl())
+ downcast<HTMLOptionElement>(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
@@ -1337,40 +1321,44 @@ void HTMLSelectElement::updateSelectedState(int listIndex, bool multi, bool shif
updateListBoxSelection(!multiSelect);
}
-void HTMLSelectElement::listBoxDefaultEventHandler(Event* event)
+void HTMLSelectElement::listBoxDefaultEventHandler(Event& event)
{
- const Vector<HTMLElement*>& listItems = this->listItems();
+ auto& listItems = this->listItems();
- if (event->type() == eventNames().mousedownEvent && event->isMouseEvent() && static_cast<MouseEvent*>(event)->button() == LeftButton) {
+ if (event.type() == eventNames().mousedownEvent && is<MouseEvent>(event) && downcast<MouseEvent>(event).button() == LeftButton) {
focus();
+
// Calling focus() may remove or change our renderer, in which case we don't want to handle the event further.
- if (!renderer() || !renderer()->isListBox())
+ auto* renderer = this->renderer();
+ if (!is<RenderListBox>(renderer))
return;
+ auto& renderListBox = downcast<RenderListBox>(*renderer);
// Convert to coords relative to the list box if needed.
- MouseEvent* mouseEvent = static_cast<MouseEvent*>(event);
- IntPoint localOffset = roundedIntPoint(renderer()->absoluteToLocal(mouseEvent->absoluteLocation(), UseTransforms));
- int listIndex = toRenderListBox(renderer())->listIndexAtOffset(toIntSize(localOffset));
+ MouseEvent& mouseEvent = downcast<MouseEvent>(event);
+ IntPoint localOffset = roundedIntPoint(renderListBox.absoluteToLocal(mouseEvent.absoluteLocation(), UseTransforms));
+ int listIndex = renderListBox.listIndexAtOffset(toIntSize(localOffset));
if (listIndex >= 0) {
if (!isDisabledFormControl()) {
-#if PLATFORM(MAC)
- updateSelectedState(listIndex, mouseEvent->metaKey(), mouseEvent->shiftKey());
+#if PLATFORM(COCOA)
+ updateSelectedState(listIndex, mouseEvent.metaKey(), mouseEvent.shiftKey());
#else
- updateSelectedState(listIndex, mouseEvent->ctrlKey(), mouseEvent->shiftKey());
+ updateSelectedState(listIndex, mouseEvent.ctrlKey(), mouseEvent.shiftKey());
#endif
}
if (Frame* frame = document().frame())
frame->eventHandler().setMouseDownMayStartAutoscroll();
- event->setDefaultHandled();
+ mouseEvent.setDefaultHandled();
}
- } else if (event->type() == eventNames().mousemoveEvent && event->isMouseEvent() && !toRenderBox(renderer())->canBeScrolledAndHasScrollableArea()) {
- MouseEvent* mouseEvent = static_cast<MouseEvent*>(event);
- if (mouseEvent->button() != LeftButton || !mouseEvent->buttonDown())
+ } else if (event.type() == eventNames().mousemoveEvent && is<MouseEvent>(event) && !downcast<RenderListBox>(*renderer()).canBeScrolledAndHasScrollableArea()) {
+ MouseEvent& mouseEvent = downcast<MouseEvent>(event);
+ if (mouseEvent.button() != LeftButton || !mouseEvent.buttonDown())
return;
- IntPoint localOffset = roundedIntPoint(renderer()->absoluteToLocal(mouseEvent->absoluteLocation(), UseTransforms));
- int listIndex = toRenderListBox(renderer())->listIndexAtOffset(toIntSize(localOffset));
+ auto& renderListBox = downcast<RenderListBox>(*renderer());
+ IntPoint localOffset = roundedIntPoint(renderListBox.absoluteToLocal(mouseEvent.absoluteLocation(), UseTransforms));
+ int listIndex = renderListBox.listIndexAtOffset(toIntSize(localOffset));
if (listIndex >= 0) {
if (!isDisabledFormControl()) {
if (m_multiple) {
@@ -1386,9 +1374,9 @@ void HTMLSelectElement::listBoxDefaultEventHandler(Event* event)
updateListBoxSelection(true);
}
}
- event->setDefaultHandled();
+ mouseEvent.setDefaultHandled();
}
- } else if (event->type() == eventNames().mouseupEvent && event->isMouseEvent() && static_cast<MouseEvent*>(event)->button() == LeftButton && document().frame()->eventHandler().autoscrollRenderer() != renderer()) {
+ } else if (event.type() == eventNames().mouseupEvent && is<MouseEvent>(event) && downcast<MouseEvent>(event).button() == LeftButton && document().frame()->eventHandler().autoscrollRenderer() != renderer()) {
// This click or drag event was not over any of the options.
if (m_lastOnChangeSelection.isEmpty())
return;
@@ -1396,10 +1384,12 @@ void HTMLSelectElement::listBoxDefaultEventHandler(Event* event)
// click. For drag selection, onChange will fire when the autoscroll
// timer stops.
listBoxOnChange();
- } else if (event->type() == eventNames().keydownEvent) {
- if (!event->isKeyboardEvent())
+ } else if (event.type() == eventNames().keydownEvent) {
+ if (!is<KeyboardEvent>(event))
return;
- const String& keyIdentifier = static_cast<KeyboardEvent*>(event)->keyIdentifier();
+
+ KeyboardEvent& keyboardEvent = downcast<KeyboardEvent>(event);
+ const String& keyIdentifier = keyboardEvent.keyIdentifier();
bool handled = false;
int endIndex = 0;
@@ -1458,58 +1448,60 @@ void HTMLSelectElement::listBoxDefaultEventHandler(Event* event)
ASSERT_UNUSED(listItems, !listItems.size() || static_cast<size_t>(endIndex) < listItems.size());
setActiveSelectionEndIndex(endIndex);
-#if PLATFORM(MAC)
+#if PLATFORM(COCOA)
m_allowsNonContiguousSelection = m_multiple && isSpatialNavigationEnabled(document().frame());
#else
- m_allowsNonContiguousSelection = m_multiple && (isSpatialNavigationEnabled(document().frame()) || static_cast<KeyboardEvent*>(event)->ctrlKey());
+ m_allowsNonContiguousSelection = m_multiple && (isSpatialNavigationEnabled(document().frame()) || keyboardEvent.ctrlKey());
#endif
- bool selectNewItem = static_cast<KeyboardEvent*>(event)->shiftKey() || !m_allowsNonContiguousSelection;
+ bool selectNewItem = keyboardEvent.shiftKey() || !m_allowsNonContiguousSelection;
if (selectNewItem)
m_activeSelectionState = true;
// If the anchor is unitialized, or if we're going to deselect all
// other options, then set the anchor index equal to the end index.
- bool deselectOthers = !m_multiple || (!static_cast<KeyboardEvent*>(event)->shiftKey() && selectNewItem);
+ bool deselectOthers = !m_multiple || (!keyboardEvent.shiftKey() && selectNewItem);
if (m_activeSelectionAnchorIndex < 0 || deselectOthers) {
if (deselectOthers)
deselectItemsWithoutValidation();
setActiveSelectionAnchorIndex(m_activeSelectionEndIndex);
}
- toRenderListBox(renderer())->scrollToRevealElementAtListIndex(endIndex);
+ downcast<RenderListBox>(*renderer()).scrollToRevealElementAtListIndex(endIndex);
if (selectNewItem) {
updateListBoxSelection(deselectOthers);
listBoxOnChange();
} else
scrollToSelection();
- event->setDefaultHandled();
+ keyboardEvent.setDefaultHandled();
}
- } else if (event->type() == eventNames().keypressEvent) {
- if (!event->isKeyboardEvent())
+ } else if (event.type() == eventNames().keypressEvent) {
+ if (!is<KeyboardEvent>(event))
return;
- int keyCode = static_cast<KeyboardEvent*>(event)->keyCode();
+ KeyboardEvent& keyboardEvent = downcast<KeyboardEvent>(event);
+ int keyCode = keyboardEvent.keyCode();
if (keyCode == '\r') {
if (form())
- form()->submitImplicitly(event, false);
- event->setDefaultHandled();
+ form()->submitImplicitly(keyboardEvent, false);
+ keyboardEvent.setDefaultHandled();
} else if (m_multiple && keyCode == ' ' && m_allowsNonContiguousSelection) {
// Use space to toggle selection change.
m_activeSelectionState = !m_activeSelectionState;
- ASSERT(m_activeSelectionEndIndex >= 0
- && m_activeSelectionEndIndex < static_cast<int>(listItems.size())
- && listItems[m_activeSelectionEndIndex]->hasTagName(optionTag));
+ ASSERT(m_activeSelectionEndIndex >= 0);
+ ASSERT(m_activeSelectionEndIndex < static_cast<int>(listItems.size()));
+ ASSERT(is<HTMLOptionElement>(*listItems[m_activeSelectionEndIndex]));
updateSelectedState(m_activeSelectionEndIndex, true /*multi*/, false /*shift*/);
listBoxOnChange();
- event->setDefaultHandled();
+ keyboardEvent.setDefaultHandled();
}
}
}
-void HTMLSelectElement::defaultEventHandler(Event* event)
+void HTMLSelectElement::defaultEventHandler(Event& event)
{
- if (!renderer())
+ auto* renderer = this->renderer();
+ if (!renderer)
return;
#if !PLATFORM(IOS)
@@ -1518,21 +1510,21 @@ void HTMLSelectElement::defaultEventHandler(Event* event)
return;
}
- if (renderer()->isMenuList())
+ if (renderer->isMenuList())
menuListDefaultEventHandler(event);
else
listBoxDefaultEventHandler(event);
#else
menuListDefaultEventHandler(event);
#endif
- if (event->defaultHandled())
+ if (event.defaultHandled())
return;
- if (event->type() == eventNames().keypressEvent && event->isKeyboardEvent()) {
- KeyboardEvent* keyboardEvent = static_cast<KeyboardEvent*>(event);
- if (!keyboardEvent->ctrlKey() && !keyboardEvent->altKey() && !keyboardEvent->metaKey() && u_isprint(keyboardEvent->charCode())) {
+ if (event.type() == eventNames().keypressEvent && is<KeyboardEvent>(event)) {
+ KeyboardEvent& keyboardEvent = downcast<KeyboardEvent>(event);
+ if (!keyboardEvent.ctrlKey() && !keyboardEvent.altKey() && !keyboardEvent.metaKey() && u_isprint(keyboardEvent.charCode())) {
typeAheadFind(keyboardEvent);
- event->setDefaultHandled();
+ event.setDefaultHandled();
return;
}
}
@@ -1541,10 +1533,10 @@ void HTMLSelectElement::defaultEventHandler(Event* event)
int HTMLSelectElement::lastSelectedListIndex() const
{
- const Vector<HTMLElement*>& items = listItems();
+ auto& items = listItems();
for (size_t i = items.size(); i;) {
- HTMLElement* element = items[--i];
- if (isHTMLOptionElement(element) && toHTMLOptionElement(element)->selected())
+ auto& element = *items[--i];
+ if (is<HTMLOptionElement>(element) && downcast<HTMLOptionElement>(element).selected())
return i;
}
return -1;
@@ -1562,17 +1554,15 @@ int HTMLSelectElement::optionCount() const
String HTMLSelectElement::optionAtIndex(int index) const
{
- const Vector<HTMLElement*>& items = listItems();
-
- HTMLElement* element = items[index];
- if (!isHTMLOptionElement(element) || toHTMLOptionElement(element)->isDisabledFormControl())
+ auto& element = *listItems()[index];
+ if (!is<HTMLOptionElement>(element) || downcast<HTMLOptionElement>(element).isDisabledFormControl())
return String();
- return toHTMLOptionElement(element)->textIndentedToRespectGroupLabel();
+ return downcast<HTMLOptionElement>(element).textIndentedToRespectGroupLabel();
}
-void HTMLSelectElement::typeAheadFind(KeyboardEvent* event)
+void HTMLSelectElement::typeAheadFind(KeyboardEvent& event)
{
- int index = m_typeAhead.handleEvent(event, TypeAhead::MatchPrefix | TypeAhead::CycleFirstChar);
+ int index = m_typeAhead.handleEvent(&event, TypeAhead::MatchPrefix | TypeAhead::CycleFirstChar);
if (index < 0)
return;
selectOption(listToOptionIndex(index), DeselectOtherOptions | DispatchChangeEvent | UserDriven);
@@ -1586,8 +1576,7 @@ Node::InsertionNotificationRequest HTMLSelectElement::insertedInto(ContainerNode
// items yet - but for innerHTML and related methods, this method is called
// after the whole subtree is constructed.
recalcListItems();
- HTMLFormControlElementWithState::insertedInto(insertionPoint);
- return InsertionDone;
+ return HTMLFormControlElementWithState::insertedInto(insertionPoint);
}
void HTMLSelectElement::accessKeySetSelectedIndex(int index)
@@ -1597,13 +1586,13 @@ void HTMLSelectElement::accessKeySetSelectedIndex(int index)
accessKeyAction(false);
// If this index is already selected, unselect. otherwise update the selected index.
- const Vector<HTMLElement*>& items = listItems();
+ auto& items = listItems();
int listIndex = optionToListIndex(index);
if (listIndex >= 0) {
- HTMLElement* element = items[listIndex];
- if (isHTMLOptionElement(element)) {
- if (toHTMLOptionElement(element)->selected())
- toHTMLOptionElement(element)->setSelectedState(false);
+ auto& element = *items[listIndex];
+ if (is<HTMLOptionElement>(element)) {
+ if (downcast<HTMLOptionElement>(element).selected())
+ downcast<HTMLOptionElement>(element).setSelectedState(false);
else
selectOption(index, DispatchChangeEvent | UserDriven);
}
@@ -1621,9 +1610,9 @@ unsigned HTMLSelectElement::length() const
{
unsigned options = 0;
- const Vector<HTMLElement*>& items = listItems();
+ auto& items = listItems();
for (unsigned i = 0; i < items.size(); ++i) {
- if (isHTMLOptionElement(items[i]))
+ if (is<HTMLOptionElement>(*items[i]))
++options;
}