Chromium Code Reviews| Index: Source/core/html/HTMLSelectElement.cpp |
| diff --git a/Source/core/html/HTMLSelectElement.cpp b/Source/core/html/HTMLSelectElement.cpp |
| index b0dc747ff14ab9cee6caa8a9264b2d0c2834ad1f..9e6f98db466e369cdde8bd05283aee7271c6dbc6 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 |
| @@ -334,7 +345,7 @@ void HTMLSelectElement::parseAttribute(const QualifiedName& name, const AtomicSt |
| m_size = size; |
| setNeedsValidityCheck(); |
| - if (m_size != oldSize && inActiveDocument()) { |
| + if (m_size != oldSize && document().isActive() && inDocument()) { |
| lazyReattachIfAttached(); |
| setRecalcListItems(); |
| } |
| @@ -366,7 +377,7 @@ bool HTMLSelectElement::canSelectAll() const |
| return !usesMenuList(); |
| } |
| -RenderObject* HTMLSelectElement::createRenderer(RenderStyle*) |
| +RenderObject* HTMLSelectElement::createRenderer(RenderStyle* style) |
| { |
| if (usesMenuList()) |
| return new RenderMenuList(this); |
| @@ -396,7 +407,6 @@ void HTMLSelectElement::childrenChanged(bool changedByParser, Node* beforeChange |
| { |
| setRecalcListItems(); |
| setNeedsValidityCheck(); |
| - m_lastOnChangeSelection.clear(); |
| HTMLFormControlElementWithState::childrenChanged(changedByParser, beforeChange, afterChange, childCountDelta); |
| } |
| @@ -523,8 +533,15 @@ 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)) { |
| + const HTMLOptionElement& option = toHTMLOptionElement(*element); |
| + if (option.isDisabledFormControl()) |
| + continue; |
| + if (!usesMenuList() && !option.renderer()) |
|
keishi
2014/07/01 05:06:35
I was able to get rid of isDisplayNone
|
| + continue; |
| + } else { |
| continue; |
| + } |
| lastGoodIndex = listIndex; |
| if (skip <= 0) |
| break; |
| @@ -640,7 +657,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) |
| @@ -662,25 +679,24 @@ void HTMLSelectElement::listBoxOnChange() |
| const WillBeHeapVector<RawPtrWillBeMember<HTMLElement> >& items = listItems(); |
| - // If the cached selection list is empty, or the size has changed, then fire |
| - // dispatchFormControlChangeEvent, and return early. |
| - // FIXME: Why? This looks unreasonable. |
| - if (m_lastOnChangeSelection.isEmpty() || m_lastOnChangeSelection.size() != items.size()) { |
| - dispatchFormControlChangeEvent(); |
| - return; |
| - } |
| - |
| // 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(); |
| - if (selected != m_lastOnChangeSelection[i]) |
| + if (i < m_lastOnChangeSelection.size()) { |
| + if (selected != m_lastOnChangeSelection[i]) { |
| + fireOnChange = true; |
| + break; |
| + } |
| + } else if (selected) { |
| fireOnChange = true; |
| - m_lastOnChangeSelection[i] = selected; |
| + break; |
| + } |
| } |
| if (fireOnChange) { |
| + saveLastSelection(); |
| RefPtrWillBeRawPtr<HTMLSelectElement> protector(this); |
| dispatchInputEvent(); |
| dispatchFormControlChangeEvent(); |
| @@ -703,11 +719,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 +733,6 @@ void HTMLSelectElement::setOptionsChangedOnRenderer() |
| if (RenderObject* renderer = this->renderer()) { |
| if (usesMenuList()) |
| toRenderMenuList(renderer)->setOptionsChanged(true); |
| - else |
| - toRenderListBox(renderer)->setOptionsChanged(true); |
| } |
| } |
| @@ -858,11 +874,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 +900,17 @@ void HTMLSelectElement::optionSelectionStateChanged(HTMLOptionElement* option, b |
| selectOption(nextSelectableListIndex(-1)); |
| } |
| +void HTMLSelectElement::optionRemoved(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 +946,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 +1092,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) |
| @@ -1335,6 +1373,32 @@ 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] == &option) |
| + return i; |
| + } |
| + return -1; |
| +} |
| + |
| +AutoscrollController* HTMLSelectElement::autoscrollController() const |
| +{ |
| + if (Page* page = document().page()) |
| + return &page->autoscrollController(); |
| + return 0; |
| +} |
| + |
| void HTMLSelectElement::listBoxDefaultEventHandler(Event* event) |
| { |
| const WillBeHeapVector<RawPtrWillBeMember<HTMLElement> >& listItems = this->listItems(); |
| @@ -1346,8 +1410,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()); |
| @@ -1359,10 +1422,12 @@ void HTMLSelectElement::listBoxDefaultEventHandler(Event* event) |
| if (!renderer() || !renderer()->isListBox() || isDisabledFormControl()) |
| return; |
| + if (Page* page = document().page()) |
| + page->autoscrollController().startAutoscrollForSelection(renderer()); |
| + |
| // 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) |
| @@ -1376,13 +1441,11 @@ 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)); |
| + int listIndex = listIndexForEventTargetOption(*mouseEvent); |
| if (listIndex >= 0) { |
| if (!isDisabledFormControl()) { |
| if (m_multiple) { |
| @@ -1399,7 +1462,7 @@ void HTMLSelectElement::listBoxDefaultEventHandler(Event* event) |
| } |
| } |
| } |
| - } else if (event->type() == EventTypeNames::mouseup && event->isMouseEvent() && toMouseEvent(event)->button() == LeftButton && renderer() && !toRenderBox(renderer())->autoscrollInProgress()) { |
| + } else if (event->type() == EventTypeNames::mouseup && event->isMouseEvent() && toMouseEvent(event)->button() == LeftButton && renderer()) { |
| // We didn't start this click/drag on any options. |
| if (m_lastOnChangeSelection.isEmpty()) |
| return; |
| @@ -1478,7 +1541,7 @@ void HTMLSelectElement::listBoxDefaultEventHandler(Event* event) |
| setActiveSelectionAnchorIndex(m_activeSelectionEndIndex); |
| } |
| - toRenderListBox(renderer())->scrollToRevealElementAtListIndex(endIndex); |
| + scrollTo(endIndex); |
| if (selectNewItem) { |
| updateListBoxSelection(deselectOthers); |
| listBoxOnChange(); |
| @@ -1629,6 +1692,7 @@ void HTMLSelectElement::finishParsingChildren() |
| { |
| HTMLFormControlElementWithState::finishParsingChildren(); |
| updateListItemSelectedStates(); |
| + scrollToSelection(); |
| } |
| bool HTMLSelectElement::anonymousIndexedSetter(unsigned index, PassRefPtrWillBeRawPtr<HTMLOptionElement> value, ExceptionState& exceptionState) |
| @@ -1664,4 +1728,11 @@ 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); |
| +} |
| + |
| } // namespace |