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..6c73522ea1ef631a2f930f8c55bb24865a1119a2 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" |
| @@ -73,6 +80,7 @@ HTMLSelectElement::HTMLSelectElement(Document& document, HTMLFormElement* form) |
| , m_activeSelectionState(false) |
| , m_shouldRecalcListItems(false) |
| , m_suggestedIndex(-1) |
| + , m_capturing(false) |
| { |
| ScriptWrappable::init(this); |
| setHasCustomStyleCallbacks(); |
| @@ -334,9 +342,11 @@ void HTMLSelectElement::parseAttribute(const QualifiedName& name, const AtomicSt |
| m_size = size; |
| setNeedsValidityCheck(); |
| - if (m_size != oldSize && inActiveDocument()) { |
| - lazyReattachIfAttached(); |
| - setRecalcListItems(); |
| + if (m_size != oldSize && document().isActive()) { |
| + // ensureShadowDOM needs to be run even when !inDocument(). |
| + updateView(); |
| + if (inDocument()) |
| + setRecalcListItems(); |
| } |
| } else if (name == multipleAttr) |
| parseMultipleAttribute(value); |
| @@ -366,7 +376,7 @@ bool HTMLSelectElement::canSelectAll() const |
| return !usesMenuList(); |
| } |
| -RenderObject* HTMLSelectElement::createRenderer(RenderStyle*) |
| +RenderObject* HTMLSelectElement::createRenderer(RenderStyle* style) |
| { |
| if (usesMenuList()) |
| return new RenderMenuList(this); |
| @@ -703,11 +713,13 @@ void HTMLSelectElement::dispatchInputAndChangeEventForMenuList(bool requiresUser |
| void HTMLSelectElement::scrollToSelection() |
| { |
| + if (!isFinishedParsingChildren()) |
|
keishi
2014/06/22 11:59:55
scrollToSelection is very slow compared to the cur
|
| + 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 +727,6 @@ void HTMLSelectElement::setOptionsChangedOnRenderer() |
| if (RenderObject* renderer = this->renderer()) { |
| if (usesMenuList()) |
| toRenderMenuList(renderer)->setOptionsChanged(true); |
| - else |
| - toRenderListBox(renderer)->setOptionsChanged(true); |
| } |
| } |
| @@ -858,11 +868,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 +894,15 @@ void HTMLSelectElement::optionSelectionStateChanged(HTMLOptionElement* option, b |
| selectOption(nextSelectableListIndex(-1)); |
| } |
| +void HTMLSelectElement::optionRemoved(HTMLOptionElement* option) |
|
keishi
2014/06/22 11:59:56
listbox-clear-restore.html tests that the scroll p
|
| +{ |
| + 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 +938,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 +1084,27 @@ void HTMLSelectElement::restoreFormControlState(const FormControlState& state) |
| void HTMLSelectElement::parseMultipleAttribute(const AtomicString& value) |
| { |
| - bool oldUsesMenuList = usesMenuList(); |
| m_multiple = !value.isNull(); |
| setNeedsValidityCheck(); |
| - if (oldUsesMenuList != usesMenuList()) |
| - lazyReattachIfAttached(); |
| + |
| + updateView(); |
| +} |
| + |
| +void HTMLSelectElement::updateView() |
| +{ |
| + if (!usesMenuList()) |
| + ensureUserAgentShadowRoot(); |
| + else if (userAgentShadowRoot()) |
| + userAgentShadowRoot()->detach(); |
| + const WillBeHeapVector<RawPtrWillBeMember<HTMLElement> >& items = listItems(); |
| + for (unsigned i = 0; i < items.size(); ++i) { |
| + HTMLElement* element = items[i]; |
| + if (isHTMLOptionElement(*element)) |
| + toHTMLOptionElement(*element).updateView(); |
| + else if (isHTMLOptGroupElement(*element)) |
| + toHTMLOptGroupElement(*element).updateView(); |
| + } |
| + lazyReattachIfAttached(); |
| } |
| bool HTMLSelectElement::appendFormData(FormDataList& list, bool) |
| @@ -1335,6 +1382,51 @@ void HTMLSelectElement::updateSelectedState(int listIndex, bool multi, bool shif |
| updateListBoxSelection(!multiSelect); |
| } |
| +int HTMLSelectElement::listIndexForMouseEvent(const MouseEvent& event) |
| +{ |
| + Node* targetNode = event.target()->toNode(); |
| + if (targetNode != this) |
| + return listIndexForEvent(event); |
| + HitTestRequest request(HitTestRequest::ReadOnly | HitTestRequest::Active | HitTestRequest::Move | HitTestRequest::ConfusingAndOftenMisusedDisallowShadowContent); |
| + HitTestResult result(event.absoluteLocation()); |
| + document().renderView()->hitTest(request, result); |
| + Node* hitNode = result.targetNode(); |
| + if (!hitNode) |
| + return -1; |
| + return listIndexForNode(*hitNode); |
| +} |
| + |
| +int HTMLSelectElement::listIndexForEvent(const Event& event) |
| +{ |
| + Node* targetNode = event.target()->toNode(); |
| + if (!targetNode) |
| + return -1; |
| + return listIndexForNode(*targetNode); |
| +} |
| + |
| +int HTMLSelectElement::listIndexForNode(const Node& targetNode) |
| +{ |
| + const WillBeHeapVector<RawPtrWillBeMember<HTMLElement> >& items = this->listItems(); |
| + for (const Node* node = &targetNode; node; node = node->parentOrShadowHostNode()) { |
| + if (node->isElementNode() && (toElement(node)->hasTagName(optionTag) || toElement(node)->hasTagName(optgroupTag))) { |
| + size_t length = items.size(); |
| + for (size_t i = 0; i < length; ++i) { |
| + if (items[i] == node) { |
| + 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 +1438,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 = listIndexForEvent(gestureEvent); |
| if (listIndex >= 0) { |
| if (!isDisabledFormControl()) |
| updateSelectedState(listIndex, true, gestureEvent.shiftKey()); |
| @@ -1359,10 +1450,17 @@ void HTMLSelectElement::listBoxDefaultEventHandler(Event* event) |
| if (!renderer() || !renderer()->isListBox() || isDisabledFormControl()) |
| return; |
| + if (Page* page = document().page()) |
| + page->autoscrollController().startAutoscrollForSelection(renderer()); |
| + |
| + if (LocalFrame* frame = document().frame()) { |
| + frame->eventHandler().setCapturingMouseEventsNode(this); |
| + m_capturing = true; |
| + } |
| + |
| // 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 = listIndexForMouseEvent(*mouseEvent); |
| if (listIndex >= 0) { |
| if (!isDisabledFormControl()) { |
| #if OS(MACOSX) |
| @@ -1376,13 +1474,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 = listIndexForMouseEvent(*mouseEvent); |
| if (listIndex >= 0) { |
| if (!isDisabledFormControl()) { |
| if (m_multiple) { |
| @@ -1399,7 +1495,12 @@ 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()) { |
| + LocalFrame* frame = document().frame(); |
| + if (m_capturing && frame) { |
| + frame->eventHandler().setCapturingMouseEventsNode(nullptr); |
| + m_capturing = false; |
| + } |
| // We didn't start this click/drag on any options. |
| if (m_lastOnChangeSelection.isEmpty()) |
| return; |
| @@ -1478,7 +1579,7 @@ void HTMLSelectElement::listBoxDefaultEventHandler(Event* event) |
| setActiveSelectionAnchorIndex(m_activeSelectionEndIndex); |
| } |
| - toRenderListBox(renderer())->scrollToRevealElementAtListIndex(endIndex); |
| + scrollTo(endIndex); |
| if (selectNewItem) { |
| updateListBoxSelection(deselectOthers); |
| listBoxOnChange(); |
| @@ -1629,6 +1730,7 @@ void HTMLSelectElement::finishParsingChildren() |
| { |
| HTMLFormControlElementWithState::finishParsingChildren(); |
| updateListItemSelectedStates(); |
| + scrollToSelection(); |
| } |
| bool HTMLSelectElement::anonymousIndexedSetter(unsigned index, PassRefPtrWillBeRawPtr<HTMLOptionElement> value, ExceptionState& exceptionState) |
| @@ -1664,4 +1766,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 |