Index: Source/core/html/HTMLSelectElement.cpp |
diff --git a/Source/core/html/HTMLSelectElement.cpp b/Source/core/html/HTMLSelectElement.cpp |
index ca38ade6fb7fa1fbbc33ff812aea03f938b759af..96aece297937db2785a10705690c5fbf18a27aa0 100644 |
--- a/Source/core/html/HTMLSelectElement.cpp |
+++ b/Source/core/html/HTMLSelectElement.cpp |
@@ -39,16 +39,23 @@ |
#include "core/events/GestureEvent.h" |
#include "core/events/KeyboardEvent.h" |
#include "core/events/MouseEvent.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/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" |
@@ -80,12 +87,16 @@ HTMLSelectElement::HTMLSelectElement(Document& document, HTMLFormElement* form) |
PassRefPtrWillBeRawPtr<HTMLSelectElement> HTMLSelectElement::create(Document& document) |
{ |
- return adoptRefWillBeNoop(new HTMLSelectElement(document, 0)); |
+ RefPtrWillBeRawPtr<HTMLSelectElement> select = adoptRefWillBeNoop(new HTMLSelectElement(document, 0)); |
+ select->ensureUserAgentShadowRoot(); |
+ return select.release(); |
} |
PassRefPtrWillBeRawPtr<HTMLSelectElement> HTMLSelectElement::create(Document& document, HTMLFormElement* form) |
{ |
- return adoptRefWillBeNoop(new HTMLSelectElement(document, form)); |
+ RefPtrWillBeRawPtr<HTMLSelectElement> select = adoptRefWillBeNoop(new HTMLSelectElement(document, form)); |
+ select->ensureUserAgentShadowRoot(); |
+ return select.release(); |
} |
const AtomicString& HTMLSelectElement::formControlType() const |
@@ -523,7 +534,11 @@ int HTMLSelectElement::nextValidIndex(int listIndex, SkipDirection direction, in |
for (listIndex += direction; listIndex >= 0 && listIndex < size; listIndex += direction) { |
--skip; |
HTMLElement* element = listItems[listIndex]; |
- if (!isHTMLOptionElement(*element) || toHTMLOptionElement(element)->isDisabledFormControl() || toHTMLOptionElement(element)->isDisplayNone()) |
+ if (!isHTMLOptionElement(*element)) |
+ continue; |
+ if (element->isDisabledFormControl()) |
+ continue; |
+ if (!usesMenuList() && !element->renderer()) |
continue; |
lastGoodIndex = listIndex; |
if (skip <= 0) |
@@ -626,7 +641,10 @@ void HTMLSelectElement::setActiveSelectionAnchorIndex(int index) |
void HTMLSelectElement::setActiveSelectionEndIndex(int index) |
{ |
+ if (index == m_activeSelectionEndIndex) |
+ return; |
m_activeSelectionEndIndex = index; |
+ setNeedsStyleRecalc(SubtreeStyleChange); |
} |
void HTMLSelectElement::updateListBoxSelection(bool deselectOtherOptions) |
@@ -640,7 +658,7 @@ void HTMLSelectElement::updateListBoxSelection(bool deselectOtherOptions) |
const WillBeHeapVector<RawPtrWillBeMember<HTMLElement> >& items = listItems(); |
for (unsigned i = 0; i < items.size(); ++i) { |
HTMLElement* element = items[i]; |
- if (!isHTMLOptionElement(*element) || toHTMLOptionElement(element)->isDisabledFormControl() || toHTMLOptionElement(element)->isDisplayNone()) |
+ if (!isHTMLOptionElement(*element) || toHTMLOptionElement(element)->isDisabledFormControl() || !toHTMLOptionElement(element)->renderer()) |
continue; |
if (i >= start && i <= end) |
@@ -651,8 +669,8 @@ void HTMLSelectElement::updateListBoxSelection(bool deselectOtherOptions) |
toHTMLOptionElement(element)->setSelectedState(m_cachedStateForActiveSelection[i]); |
} |
- scrollToSelection(); |
setNeedsValidityCheck(); |
+ scrollToSelection(); |
notifyFormStateChanged(); |
} |
@@ -703,11 +721,13 @@ void HTMLSelectElement::dispatchInputAndChangeEventForMenuList(bool requiresUser |
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() |
@@ -715,8 +735,6 @@ void HTMLSelectElement::setOptionsChangedOnRenderer() |
if (RenderObject* renderer = this->renderer()) { |
if (usesMenuList()) |
toRenderMenuList(renderer)->setOptionsChanged(true); |
- else |
- toRenderListBox(renderer)->setOptionsChanged(true); |
} |
} |
@@ -858,11 +876,21 @@ void HTMLSelectElement::setSuggestedIndex(int suggestedIndex) |
if (RenderObject* renderer = this->renderer()) { |
renderer->updateFromElement(); |
- if (renderer->isListBox()) |
- toRenderListBox(renderer)->scrollToRevealElementAtListIndex(suggestedIndex); |
+ scrollTo(suggestedIndex); |
} |
} |
+void HTMLSelectElement::scrollTo(int listIndex) |
+{ |
+ if (listIndex < 0) |
+ return; |
+ const WillBeHeapVector<RawPtrWillBeMember<HTMLElement> >& items = listItems(); |
+ int listSize = static_cast<int>(items.size()); |
+ if (listIndex >= listSize) |
+ return; |
+ items[listIndex]->scrollIntoViewIfNeeded(false); |
+} |
+ |
void HTMLSelectElement::optionSelectionStateChanged(HTMLOptionElement* option, bool optionIsSelected) |
{ |
ASSERT(option->ownerSelectElement() == this); |
@@ -874,6 +902,17 @@ void HTMLSelectElement::optionSelectionStateChanged(HTMLOptionElement* option, b |
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--; |
+} |
+ |
void HTMLSelectElement::selectOption(int optionIndex, SelectOptionFlags flags) |
{ |
bool shouldDeselect = !m_multiple || (flags & DeselectOtherOptions); |
@@ -909,10 +948,12 @@ void HTMLSelectElement::selectOption(int optionIndex, SelectOptionFlags flags) |
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); |
+ } |
} |
} |
@@ -1053,11 +1094,10 @@ void HTMLSelectElement::restoreFormControlState(const FormControlState& state) |
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) |
@@ -1317,6 +1357,40 @@ void HTMLSelectElement::updateSelectedState(int listIndex, bool multi, bool shif |
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 WillBeHeapVector<RawPtrWillBeMember<HTMLElement> >& listItems = this->listItems(); |
@@ -1328,8 +1402,7 @@ void HTMLSelectElement::listBoxDefaultEventHandler(Event* event) |
// Convert to coords relative to the list box if needed. |
GestureEvent& gestureEvent = toGestureEvent(*event); |
- IntPoint localOffset = roundedIntPoint(renderer()->absoluteToLocal(gestureEvent.absoluteLocation(), UseTransforms)); |
- int listIndex = toRenderListBox(renderer())->listIndexAtOffset(toIntSize(localOffset)); |
+ int listIndex = listIndexForEventTargetOption(gestureEvent); |
if (listIndex >= 0) { |
if (!isDisabledFormControl()) |
updateSelectedState(listIndex, true, gestureEvent.shiftKey()); |
@@ -1343,8 +1416,7 @@ void HTMLSelectElement::listBoxDefaultEventHandler(Event* event) |
// 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) |
@@ -1358,13 +1430,15 @@ void HTMLSelectElement::listBoxDefaultEventHandler(Event* event) |
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) { |
@@ -1381,11 +1455,11 @@ void HTMLSelectElement::listBoxDefaultEventHandler(Event* event) |
} |
} |
} |
- } 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; |
- 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; |
@@ -1460,7 +1534,7 @@ void HTMLSelectElement::listBoxDefaultEventHandler(Event* event) |
setActiveSelectionAnchorIndex(m_activeSelectionEndIndex); |
} |
- toRenderListBox(renderer())->scrollToRevealElementAtListIndex(endIndex); |
+ scrollTo(endIndex); |
if (selectNewItem) { |
updateListBoxSelection(deselectOthers); |
listBoxOnChange(); |
@@ -1611,6 +1685,7 @@ void HTMLSelectElement::finishParsingChildren() |
{ |
HTMLFormControlElementWithState::finishParsingChildren(); |
updateListItemSelectedStates(); |
+ scrollToSelection(); |
} |
bool HTMLSelectElement::anonymousIndexedSetter(unsigned index, PassRefPtrWillBeRawPtr<HTMLOptionElement> value, ExceptionState& exceptionState) |
@@ -1646,4 +1721,24 @@ void HTMLSelectElement::trace(Visitor* visitor) |
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 |