diff options
Diffstat (limited to 'Source/WebCore/rendering/RenderListBox.cpp')
-rw-r--r-- | Source/WebCore/rendering/RenderListBox.cpp | 432 |
1 files changed, 285 insertions, 147 deletions
diff --git a/Source/WebCore/rendering/RenderListBox.cpp b/Source/WebCore/rendering/RenderListBox.cpp index edc15e677..05a9ef750 100644 --- a/Source/WebCore/rendering/RenderListBox.cpp +++ b/Source/WebCore/rendering/RenderListBox.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006, 2007, 2008, 2011 Apple Inc. All rights reserved. + * Copyright (C) 2006, 2007, 2008, 2011, 2014-2015 Apple Inc. All rights reserved. * 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/) * * Redistribution and use in source and binary forms, with or without @@ -11,7 +11,7 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * 3. Neither the name of Apple Inc. ("Apple") nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * @@ -36,7 +36,6 @@ #include "DocumentEventQueue.h" #include "EventHandler.h" #include "FocusController.h" -#include "FontCache.h" #include "Frame.h" #include "FrameSelection.h" #include "FrameView.h" @@ -54,10 +53,14 @@ #include "RenderText.h" #include "RenderTheme.h" #include "RenderView.h" +#include "ScrollAnimator.h" #include "Scrollbar.h" #include "ScrollbarTheme.h" +#include "Settings.h" #include "SpatialNavigation.h" #include "StyleResolver.h" +#include "StyleTreeResolver.h" +#include "WheelEventTestTrigger.h" #include <math.h> #include <wtf/StackStats.h> @@ -80,8 +83,8 @@ const int defaultSize = 4; // widget, but I'm not sure this is right for the new control. const int baselineAdjustment = 7; -RenderListBox::RenderListBox(HTMLSelectElement& element, PassRef<RenderStyle> style) - : RenderBlockFlow(element, std::move(style)) +RenderListBox::RenderListBox(HTMLSelectElement& element, RenderStyle&& style) + : RenderBlockFlow(element, WTFMove(style)) , m_optionsChanged(true) , m_scrollToRevealSelectionAfterLayout(false) , m_inAutoscroll(false) @@ -93,19 +96,23 @@ RenderListBox::RenderListBox(HTMLSelectElement& element, PassRef<RenderStyle> st RenderListBox::~RenderListBox() { + // Do not add any code here. Add it to willBeDestroyed() instead. +} + +void RenderListBox::willBeDestroyed() +{ setHasVerticalScrollbar(false); view().frameView().removeScrollableArea(this); + RenderBlockFlow::willBeDestroyed(); } HTMLSelectElement& RenderListBox::selectElement() const { - return toHTMLSelectElement(nodeForNonAnonymous()); + return downcast<HTMLSelectElement>(nodeForNonAnonymous()); } void RenderListBox::updateFromElement() { - FontCachePurgePreventer fontCachePurgePreventer; - if (m_optionsChanged) { const Vector<HTMLElement*>& listItems = selectElement().listItems(); int size = numItems(); @@ -114,22 +121,21 @@ void RenderListBox::updateFromElement() for (int i = 0; i < size; ++i) { HTMLElement* element = listItems[i]; String text; - Font itemFont = style().font(); - if (isHTMLOptionElement(element)) - text = toHTMLOptionElement(element)->textIndentedToRespectGroupLabel(); - else if (isHTMLOptGroupElement(element)) { - text = toHTMLOptGroupElement(element)->groupLabelText(); - FontDescription d = itemFont.fontDescription(); - d.setWeight(d.bolderWeight()); - itemFont = Font(d, itemFont.letterSpacing(), itemFont.wordSpacing()); - itemFont.update(document().ensureStyleResolver().fontSelector()); + FontCascade itemFont = style().fontCascade(); + if (is<HTMLOptionElement>(*element)) + text = downcast<HTMLOptionElement>(*element).textIndentedToRespectGroupLabel(); + else if (is<HTMLOptGroupElement>(*element)) { + text = downcast<HTMLOptGroupElement>(*element).groupLabelText(); + auto description = itemFont.fontDescription(); + description.setWeight(description.bolderWeight()); + itemFont = FontCascade(description, itemFont.letterSpacing(), itemFont.wordSpacing()); + itemFont.update(&document().fontSelector()); } if (!text.isEmpty()) { applyTextTransform(style(), text, ' '); // FIXME: Why is this always LTR? Can't text direction affect the width? - TextRun textRun = constructTextRun(this, itemFont, text, style(), TextRun::AllowTrailingExpansion); - textRun.disableRoundingHacks(); + TextRun textRun = constructTextRun(text, style(), AllowTrailingExpansion); float textWidth = itemFont.width(textRun); width = std::max(width, textWidth); } @@ -139,15 +145,12 @@ void RenderListBox::updateFromElement() setHasVerticalScrollbar(true); + computeFirstIndexesVisibleInPaddingTopBottomAreas(); + setNeedsLayoutAndPrefWidthsRecalc(); } } -bool RenderListBox::canBeReplacedWithInlineRunIn() const -{ - return false; -} - void RenderListBox::selectionChanged() { repaint(); @@ -179,7 +182,7 @@ void RenderListBox::layout() } if (m_scrollToRevealSelectionAfterLayout) { - LayoutStateDisabler layoutStateDisabler(&view()); + LayoutStateDisabler layoutStateDisabler(view()); scrollToRevealSelection(); } } @@ -198,13 +201,14 @@ void RenderListBox::computeIntrinsicLogicalWidths(LayoutUnit& minLogicalWidth, L maxLogicalWidth = m_optionsWidth + 2 * optionsSpacingHorizontal; if (m_vBar) maxLogicalWidth += m_vBar->width(); - if (!style().width().isPercent()) + if (!style().width().isPercentOrCalculated()) minLogicalWidth = maxLogicalWidth; } void RenderListBox::computePreferredLogicalWidths() { - ASSERT(!m_optionsChanged); + // Nested style recal do not fire post recal callbacks. see webkit.org/b/153767 + ASSERT(!m_optionsChanged || Style::postResolutionCallbacksAreSuspended()); m_minPreferredLogicalWidth = 0; m_maxPreferredLogicalWidth = 0; @@ -224,7 +228,7 @@ void RenderListBox::computePreferredLogicalWidths() m_minPreferredLogicalWidth = std::min(m_minPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(style().maxWidth().value())); } - LayoutUnit toAdd = borderAndPaddingWidth(); + LayoutUnit toAdd = horizontalBorderAndPaddingExtent(); m_minPreferredLogicalWidth += toAdd; m_maxPreferredLogicalWidth += toAdd; @@ -240,10 +244,14 @@ int RenderListBox::size() const return defaultSize; } -int RenderListBox::numVisibleItems() const +int RenderListBox::numVisibleItems(ConsiderPadding considerPadding) const { // Only count fully visible rows. But don't return 0 even if only part of a row shows. - return std::max<int>(1, (contentHeight() + rowSpacing) / itemHeight()); + int visibleItemsExcludingPadding = std::max<int>(1, (contentHeight() + rowSpacing) / itemHeight()); + if (considerPadding == ConsiderPadding::No) + return visibleItemsExcludingPadding; + + return numberOfVisibleItemsInPaddingTop() + visibleItemsExcludingPadding + numberOfVisibleItemsInPaddingBottom(); } int RenderListBox::numItems() const @@ -256,10 +264,10 @@ LayoutUnit RenderListBox::listHeight() const return itemHeight() * numItems() - rowSpacing; } -void RenderListBox::computeLogicalHeight(LayoutUnit, LayoutUnit logicalTop, LogicalExtentComputedValues& computedValues) const +RenderBox::LogicalExtentComputedValues RenderListBox::computeLogicalHeight(LayoutUnit, LayoutUnit logicalTop) const { - LayoutUnit height = itemHeight() * size() - rowSpacing + borderAndPaddingHeight(); - RenderBox::computeLogicalHeight(height, logicalTop, computedValues); + LayoutUnit height = itemHeight() * size() - rowSpacing + verticalBorderAndPaddingExtent(); + return RenderBox::computeLogicalHeight(height, logicalTop); } int RenderListBox::baselinePosition(FontBaseline baselineType, bool firstLine, LineDirectionMode lineDirection, LinePositionMode linePositionMode) const @@ -269,24 +277,31 @@ int RenderListBox::baselinePosition(FontBaseline baselineType, bool firstLine, L LayoutRect RenderListBox::itemBoundingBoxRect(const LayoutPoint& additionalOffset, int index) { - return LayoutRect(additionalOffset.x() + borderLeft() + paddingLeft(), - additionalOffset.y() + borderTop() + paddingTop() + itemHeight() * (index - m_indexOffset), - contentWidth(), itemHeight()); + LayoutUnit x = additionalOffset.x() + borderLeft() + paddingLeft(); + if (shouldPlaceBlockDirectionScrollbarOnLeft() && m_vBar) + x += m_vBar->occupiedWidth(); + LayoutUnit y = additionalOffset.y() + borderTop() + paddingTop() + itemHeight() * (index - m_indexOffset); + return LayoutRect(x, y, contentWidth(), itemHeight()); } - + +void RenderListBox::paintItem(PaintInfo& paintInfo, const LayoutPoint& paintOffset, PaintFunction paintFunction) +{ + int listItemsSize = numItems(); + int firstVisibleItem = m_indexOfFirstVisibleItemInsidePaddingTopArea.value_or(m_indexOffset); + int endIndex = firstVisibleItem + numVisibleItems(ConsiderPadding::Yes); + for (int i = firstVisibleItem; i < listItemsSize && i < endIndex; ++i) + paintFunction(paintInfo, paintOffset, i); +} + void RenderListBox::paintObject(PaintInfo& paintInfo, const LayoutPoint& paintOffset) { if (style().visibility() != VISIBLE) return; - int listItemsSize = numItems(); - if (paintInfo.phase == PaintPhaseForeground) { - int index = m_indexOffset; - while (index < listItemsSize && index <= m_indexOffset + numVisibleItems()) { - paintItemForeground(paintInfo, paintOffset, index); - index++; - } + paintItem(paintInfo, paintOffset, [this](PaintInfo& paintInfo, const LayoutPoint& paintOffset, int listItemIndex) { + paintItemForeground(paintInfo, paintOffset, listItemIndex); + }); } // Paint the children. @@ -305,11 +320,9 @@ void RenderListBox::paintObject(PaintInfo& paintInfo, const LayoutPoint& paintOf break; case PaintPhaseChildBlockBackground: case PaintPhaseChildBlockBackgrounds: { - int index = m_indexOffset; - while (index < listItemsSize && index <= m_indexOffset + numVisibleItems()) { - paintItemBackground(paintInfo, paintOffset, index); - index++; - } + paintItem(paintInfo, paintOffset, [this](PaintInfo& paintInfo, const LayoutPoint& paintOffset, int listItemIndex) { + paintItemBackground(paintInfo, paintOffset, listItemIndex); + }); break; } default: @@ -317,7 +330,7 @@ void RenderListBox::paintObject(PaintInfo& paintInfo, const LayoutPoint& paintOf } } -void RenderListBox::addFocusRingRects(Vector<IntRect>& rects, const LayoutPoint& additionalOffset, const RenderLayerModelObject* paintContainer) +void RenderListBox::addFocusRingRects(Vector<LayoutRect>& rects, const LayoutPoint& additionalOffset, const RenderLayerModelObject* paintContainer) { if (!selectElement().allowsNonContiguousSelection()) return RenderBlockFlow::addFocusRingRects(rects, additionalOffset, paintContainer); @@ -325,7 +338,7 @@ void RenderListBox::addFocusRingRects(Vector<IntRect>& rects, const LayoutPoint& // Focus the last selected item. int selectedItem = selectElement().activeSelectionEndListIndex(); if (selectedItem >= 0) { - rects.append(pixelSnappedIntRect(itemBoundingBoxRect(additionalOffset, selectedItem))); + rects.append(snappedIntRect(itemBoundingBoxRect(additionalOffset, selectedItem))); return; } @@ -334,9 +347,9 @@ void RenderListBox::addFocusRingRects(Vector<IntRect>& rects, const LayoutPoint& const Vector<HTMLElement*>& listItems = selectElement().listItems(); for (int i = 0; i < size; ++i) { HTMLElement* element = listItems[i]; - if (isHTMLOptionElement(element) && !element->isDisabledFormControl()) { + if (is<HTMLOptionElement>(*element) && !element->isDisabledFormControl()) { selectElement().setActiveSelectionEndIndex(i); - rects.append(pixelSnappedIntRect(itemBoundingBoxRect(additionalOffset, i))); + rects.append(itemBoundingBoxRect(additionalOffset, i)); return; } } @@ -344,17 +357,19 @@ void RenderListBox::addFocusRingRects(Vector<IntRect>& rects, const LayoutPoint& void RenderListBox::paintScrollbar(PaintInfo& paintInfo, const LayoutPoint& paintOffset) { - if (m_vBar) { - IntRect scrollRect = pixelSnappedIntRect(paintOffset.x() + width() - borderRight() - m_vBar->width(), - paintOffset.y() + borderTop(), - m_vBar->width(), - height() - (borderTop() + borderBottom())); - m_vBar->setFrameRect(scrollRect); - m_vBar->paint(paintInfo.context, pixelSnappedIntRect(paintInfo.rect)); - } + if (!m_vBar) + return; + + LayoutUnit left = paintOffset.x() + (shouldPlaceBlockDirectionScrollbarOnLeft() ? borderLeft() : width() - borderRight() - m_vBar->width()); + LayoutUnit top = paintOffset.y() + borderTop(); + LayoutUnit width = m_vBar->width(); + LayoutUnit height = this->height() - (borderTop() + borderBottom()); + IntRect scrollRect = snappedIntRect(left, top, width, height); + m_vBar->setFrameRect(scrollRect); + m_vBar->paint(paintInfo.context(), snappedIntRect(paintInfo.rect)); } -static LayoutSize itemOffsetForAlignment(TextRun textRun, RenderStyle* itemStyle, Font itemFont, LayoutRect itemBoudingBox) +static LayoutSize itemOffsetForAlignment(TextRun textRun, const RenderStyle* itemStyle, FontCascade itemFont, LayoutRect itemBoudingBox) { ETextAlign actualAlignment = itemStyle->textAlign(); // FIXME: Firefox doesn't respect JUSTIFY. Should we? @@ -376,28 +391,24 @@ static LayoutSize itemOffsetForAlignment(TextRun textRun, RenderStyle* itemStyle void RenderListBox::paintItemForeground(PaintInfo& paintInfo, const LayoutPoint& paintOffset, int listIndex) { - FontCachePurgePreventer fontCachePurgePreventer; - const Vector<HTMLElement*>& listItems = selectElement().listItems(); HTMLElement* listItemElement = listItems[listIndex]; - RenderStyle* itemStyle = listItemElement->renderStyle(); - if (!itemStyle) - itemStyle = &style(); + auto& itemStyle = *listItemElement->computedStyle(); - if (itemStyle->visibility() == HIDDEN) + if (itemStyle.visibility() == HIDDEN) return; String itemText; - bool isOptionElement = isHTMLOptionElement(listItemElement); + bool isOptionElement = is<HTMLOptionElement>(*listItemElement); if (isOptionElement) - itemText = toHTMLOptionElement(listItemElement)->textIndentedToRespectGroupLabel(); - else if (isHTMLOptGroupElement(listItemElement)) - itemText = toHTMLOptGroupElement(listItemElement)->groupLabelText(); + itemText = downcast<HTMLOptionElement>(*listItemElement).textIndentedToRespectGroupLabel(); + else if (is<HTMLOptGroupElement>(*listItemElement)) + itemText = downcast<HTMLOptGroupElement>(*listItemElement).groupLabelText(); applyTextTransform(style(), itemText, ' '); - Color textColor = listItemElement->renderStyle() ? listItemElement->renderStyle()->visitedDependentColor(CSSPropertyColor) : style().visitedDependentColor(CSSPropertyColor); - if (isOptionElement && toHTMLOptionElement(listItemElement)->selected()) { + Color textColor = itemStyle.visitedDependentColor(CSSPropertyColor); + if (isOptionElement && downcast<HTMLOptionElement>(*listItemElement).selected()) { if (frame().selection().isFocusedAndActive() && document().focusedElement() == &selectElement()) textColor = theme().activeListBoxSelectionForegroundColor(); // Honor the foreground color for disabled items @@ -405,46 +416,46 @@ void RenderListBox::paintItemForeground(PaintInfo& paintInfo, const LayoutPoint& textColor = theme().inactiveListBoxSelectionForegroundColor(); } - ColorSpace colorSpace = itemStyle->colorSpace(); - paintInfo.context->setFillColor(textColor, colorSpace); + paintInfo.context().setFillColor(textColor); - TextRun textRun(itemText, 0, 0, TextRun::AllowTrailingExpansion, itemStyle->direction(), isOverride(itemStyle->unicodeBidi()), true, TextRun::NoRounding); - Font itemFont = style().font(); + TextRun textRun(itemText, 0, 0, AllowTrailingExpansion, itemStyle.direction(), isOverride(itemStyle.unicodeBidi()), true); + FontCascade itemFont = style().fontCascade(); LayoutRect r = itemBoundingBoxRect(paintOffset, listIndex); - r.move(itemOffsetForAlignment(textRun, itemStyle, itemFont, r)); + r.move(itemOffsetForAlignment(textRun, &itemStyle, itemFont, r)); - if (isHTMLOptGroupElement(listItemElement)) { - FontDescription d = itemFont.fontDescription(); - d.setWeight(d.bolderWeight()); - itemFont = Font(d, itemFont.letterSpacing(), itemFont.wordSpacing()); - itemFont.update(document().ensureStyleResolver().fontSelector()); + if (is<HTMLOptGroupElement>(*listItemElement)) { + auto description = itemFont.fontDescription(); + description.setWeight(description.bolderWeight()); + itemFont = FontCascade(description, itemFont.letterSpacing(), itemFont.wordSpacing()); + itemFont.update(&document().fontSelector()); } // Draw the item text - paintInfo.context->drawBidiText(itemFont, textRun, roundedIntPoint(r.location())); + paintInfo.context().drawBidiText(itemFont, textRun, roundedIntPoint(r.location())); } void RenderListBox::paintItemBackground(PaintInfo& paintInfo, const LayoutPoint& paintOffset, int listIndex) { const Vector<HTMLElement*>& listItems = selectElement().listItems(); HTMLElement* listItemElement = listItems[listIndex]; + auto& itemStyle = *listItemElement->computedStyle(); Color backColor; - if (isHTMLOptionElement(listItemElement) && toHTMLOptionElement(listItemElement)->selected()) { + if (is<HTMLOptionElement>(*listItemElement) && downcast<HTMLOptionElement>(*listItemElement).selected()) { if (frame().selection().isFocusedAndActive() && document().focusedElement() == &selectElement()) backColor = theme().activeListBoxSelectionBackgroundColor(); else backColor = theme().inactiveListBoxSelectionBackgroundColor(); } else - backColor = listItemElement->renderStyle() ? listItemElement->renderStyle()->visitedDependentColor(CSSPropertyBackgroundColor) : style().visitedDependentColor(CSSPropertyBackgroundColor); + backColor = itemStyle.visitedDependentColor(CSSPropertyBackgroundColor); // Draw the background for this list box item - if (!listItemElement->renderStyle() || listItemElement->renderStyle()->visibility() != HIDDEN) { - ColorSpace colorSpace = listItemElement->renderStyle() ? listItemElement->renderStyle()->colorSpace() : style().colorSpace(); - LayoutRect itemRect = itemBoundingBoxRect(paintOffset, listIndex); - itemRect.intersect(controlClipRect(paintOffset)); - paintInfo.context->fillRect(pixelSnappedIntRect(itemRect), backColor, colorSpace); - } + if (itemStyle.visibility() == HIDDEN) + return; + + LayoutRect itemRect = itemBoundingBoxRect(paintOffset, listIndex); + itemRect.intersect(controlClipRect(paintOffset)); + paintInfo.context().fillRect(snappedIntRect(itemRect), backColor); } bool RenderListBox::isPointInOverflowControl(HitTestResult& result, const LayoutPoint& locationInContainer, const LayoutPoint& accumulatedOffset) @@ -452,16 +463,17 @@ bool RenderListBox::isPointInOverflowControl(HitTestResult& result, const Layout if (!m_vBar || !m_vBar->shouldParticipateInHitTesting()) return false; - LayoutRect vertRect(accumulatedOffset.x() + width() - borderRight() - m_vBar->width(), - accumulatedOffset.y() + borderTop(), - m_vBar->width(), - height() - borderTop() - borderBottom()); + LayoutUnit x = accumulatedOffset.x() + (shouldPlaceBlockDirectionScrollbarOnLeft() ? borderLeft() : width() - borderRight() - m_vBar->width()); + LayoutUnit y = accumulatedOffset.y() + borderTop(); + LayoutUnit width = m_vBar->width(); + LayoutUnit height = this->height() - borderTop() - borderBottom(); + LayoutRect vertRect(x, y, width, height); - if (vertRect.contains(locationInContainer)) { - result.setScrollbar(m_vBar.get()); - return true; - } - return false; + if (!vertRect.contains(locationInContainer)) + return false; + + result.setScrollbar(m_vBar.get()); + return true; } int RenderListBox::listIndexAtOffset(const LayoutSize& offset) @@ -469,11 +481,13 @@ int RenderListBox::listIndexAtOffset(const LayoutSize& offset) if (!numItems()) return -1; - if (offset.height() < borderTop() + paddingTop() || offset.height() > height() - paddingBottom() - borderBottom()) + if (offset.height() < borderTop() || offset.height() > height() - borderBottom()) return -1; int scrollbarWidth = m_vBar ? m_vBar->width() : 0; - if (offset.width() < borderLeft() + paddingLeft() || offset.width() > width() - borderRight() - paddingRight() - scrollbarWidth) + if (shouldPlaceBlockDirectionScrollbarOnLeft() && (offset.width() < borderLeft() + paddingLeft() + scrollbarWidth || offset.width() > width() - borderRight() - paddingRight())) + return -1; + if (!shouldPlaceBlockDirectionScrollbarOnLeft() && (offset.width() < borderLeft() + paddingLeft() || offset.width() > width() - borderRight() - paddingRight() - scrollbarWidth)) return -1; int newOffset = (offset.height() - borderTop() - paddingTop()) / itemHeight() + m_indexOffset; @@ -588,8 +602,13 @@ bool RenderListBox::scrollToRevealElementAtListIndex(int index) } bool RenderListBox::listIndexIsVisible(int index) -{ - return index >= m_indexOffset && index < m_indexOffset + numVisibleItems(); +{ + int firstIndex = m_indexOfFirstVisibleItemInsidePaddingTopArea.value_or(m_indexOffset); + int endIndex = m_indexOfFirstVisibleItemInsidePaddingBottomArea + ? m_indexOfFirstVisibleItemInsidePaddingBottomArea.value() + numberOfVisibleItemsInPaddingBottom() + : m_indexOffset + numVisibleItems(); + + return index >= firstIndex && index < endIndex; } bool RenderListBox::scroll(ScrollDirection direction, ScrollGranularity granularity, float multiplier, Element**, RenderBox*, const IntPoint&) @@ -613,22 +632,73 @@ int RenderListBox::scrollSize(ScrollbarOrientation orientation) const return ((orientation == VerticalScrollbar) && m_vBar) ? (m_vBar->totalSize() - m_vBar->visibleSize()) : 0; } -int RenderListBox::scrollPosition(Scrollbar*) const +int RenderListBox::scrollOffset(ScrollbarOrientation) const { return m_indexOffset; } -void RenderListBox::setScrollOffset(const IntPoint& offset) +ScrollPosition RenderListBox::minimumScrollPosition() const +{ + return { 0, 0 }; +} + +ScrollPosition RenderListBox::maximumScrollPosition() const +{ + return { 0, numItems() - numVisibleItems() }; +} + +void RenderListBox::setScrollOffset(const ScrollOffset& offset) { scrollTo(offset.y()); } +int RenderListBox::maximumNumberOfItemsThatFitInPaddingBottomArea() const +{ + return paddingBottom() / itemHeight(); +} + +int RenderListBox::numberOfVisibleItemsInPaddingTop() const +{ + if (!m_indexOfFirstVisibleItemInsidePaddingTopArea) + return 0; + + return m_indexOffset - m_indexOfFirstVisibleItemInsidePaddingTopArea.value(); +} + +int RenderListBox::numberOfVisibleItemsInPaddingBottom() const +{ + if (!m_indexOfFirstVisibleItemInsidePaddingBottomArea) + return 0; + + return std::min(maximumNumberOfItemsThatFitInPaddingBottomArea(), numItems() - m_indexOffset - numVisibleItems()); +} + +void RenderListBox::computeFirstIndexesVisibleInPaddingTopBottomAreas() +{ + m_indexOfFirstVisibleItemInsidePaddingTopArea = std::nullopt; + m_indexOfFirstVisibleItemInsidePaddingBottomArea = std::nullopt; + + int maximumNumberOfItemsThatFitInPaddingTopArea = paddingTop() / itemHeight(); + if (maximumNumberOfItemsThatFitInPaddingTopArea) { + if (m_indexOffset) + m_indexOfFirstVisibleItemInsidePaddingTopArea = std::max(0, m_indexOffset - maximumNumberOfItemsThatFitInPaddingTopArea); + } + + if (maximumNumberOfItemsThatFitInPaddingBottomArea()) { + if (numItems() > (m_indexOffset + numVisibleItems())) + m_indexOfFirstVisibleItemInsidePaddingBottomArea = m_indexOffset + numVisibleItems(); + } +} + void RenderListBox::scrollTo(int newOffset) { if (newOffset == m_indexOffset) return; m_indexOffset = newOffset; + + computeFirstIndexesVisibleInPaddingTopBottomAreas(); + repaint(); document().eventQueue().enqueueOrDispatchScrollEvent(selectElement()); } @@ -640,7 +710,7 @@ LayoutUnit RenderListBox::itemHeight() const int RenderListBox::verticalScrollbarWidth() const { - return m_vBar && !m_vBar->isOverlayScrollbar() ? m_vBar->width() : 0; + return m_vBar ? m_vBar->occupiedWidth() : 0; } // FIXME: We ignore padding in the vertical direction as far as these values are concerned, since that's @@ -648,12 +718,12 @@ int RenderListBox::verticalScrollbarWidth() const int RenderListBox::scrollWidth() const { // There is no horizontal scrolling allowed. - return pixelSnappedClientWidth(); + return roundToInt(clientWidth()); } int RenderListBox::scrollHeight() const { - return std::max(pixelSnappedClientHeight(), roundToInt(listHeight())); + return roundToInt(std::max(clientHeight(), listHeight())); } int RenderListBox::scrollLeft() const @@ -670,13 +740,21 @@ int RenderListBox::scrollTop() const return m_indexOffset * itemHeight(); } +static void setupWheelEventTestTrigger(RenderListBox& renderer) +{ + if (!renderer.page().expectsWheelEventTriggers()) + return; + + renderer.scrollAnimator().setWheelEventTestTrigger(renderer.page().testTrigger()); +} + void RenderListBox::setScrollTop(int newTop) { // Determine an index and scroll to it. int index = newTop / itemHeight(); if (index < 0 || index >= numItems() || index == m_indexOffset) return; - + setupWheelEventTestTrigger(*this); scrollToOffsetWithoutAnimation(VerticalScrollbar, index); } @@ -689,14 +767,14 @@ bool RenderListBox::nodeAtPoint(const HitTestRequest& request, HitTestResult& re LayoutPoint adjustedLocation = accumulatedOffset + location(); for (int i = 0; i < size; ++i) { - if (itemBoundingBoxRect(adjustedLocation, i).contains(locationInContainer.point())) { - if (Element* node = listItems[i]) { - result.setInnerNode(node); - if (!result.innerNonSharedNode()) - result.setInnerNonSharedNode(node); - result.setLocalPoint(locationInContainer.point() - toLayoutSize(adjustedLocation)); - break; - } + if (!itemBoundingBoxRect(adjustedLocation, i).contains(locationInContainer.point())) + continue; + if (Element* node = listItems[i]) { + result.setInnerNode(node); + if (!result.innerNonSharedNode()) + result.setInnerNonSharedNode(node); + result.setLocalPoint(locationInContainer.point() - toLayoutSize(adjustedLocation)); + break; } } @@ -705,55 +783,58 @@ bool RenderListBox::nodeAtPoint(const HitTestRequest& request, HitTestResult& re LayoutRect RenderListBox::controlClipRect(const LayoutPoint& additionalOffset) const { - LayoutRect clipRect = contentBoxRect(); + // Clip against the padding box, to give <option>s and overlay scrollbar some extra space + // to get painted. + LayoutRect clipRect = paddingBoxRect(); + if (shouldPlaceBlockDirectionScrollbarOnLeft()) + clipRect.move(m_vBar->occupiedWidth(), 0); clipRect.moveBy(additionalOffset); return clipRect; } bool RenderListBox::isActive() const { - Page* page = frame().page(); - return page && page->focusController().isActive(); + return page().focusController().isActive(); } -void RenderListBox::invalidateScrollbarRect(Scrollbar* scrollbar, const IntRect& rect) +void RenderListBox::invalidateScrollbarRect(Scrollbar& scrollbar, const IntRect& rect) { IntRect scrollRect = rect; - scrollRect.move(width() - borderRight() - scrollbar->width(), borderTop()); + scrollRect.move(shouldPlaceBlockDirectionScrollbarOnLeft() ? borderLeft() : width() - borderRight() - scrollbar.width(), borderTop()); repaintRectangle(scrollRect); } -IntRect RenderListBox::convertFromScrollbarToContainingView(const Scrollbar* scrollbar, const IntRect& scrollbarRect) const +IntRect RenderListBox::convertFromScrollbarToContainingView(const Scrollbar& scrollbar, const IntRect& scrollbarRect) const { IntRect rect = scrollbarRect; - int scrollbarLeft = width() - borderRight() - scrollbar->width(); + int scrollbarLeft = shouldPlaceBlockDirectionScrollbarOnLeft() ? borderLeft() : width() - borderRight() - scrollbar.width(); int scrollbarTop = borderTop(); rect.move(scrollbarLeft, scrollbarTop); - return view().frameView().convertFromRenderer(this, rect); + return view().frameView().convertFromRendererToContainingView(this, rect); } -IntRect RenderListBox::convertFromContainingViewToScrollbar(const Scrollbar* scrollbar, const IntRect& parentRect) const +IntRect RenderListBox::convertFromContainingViewToScrollbar(const Scrollbar& scrollbar, const IntRect& parentRect) const { - IntRect rect = view().frameView().convertToRenderer(this, parentRect); - int scrollbarLeft = width() - borderRight() - scrollbar->width(); + IntRect rect = view().frameView().convertFromContainingViewToRenderer(this, parentRect); + int scrollbarLeft = shouldPlaceBlockDirectionScrollbarOnLeft() ? borderLeft() : width() - borderRight() - scrollbar.width(); int scrollbarTop = borderTop(); rect.move(-scrollbarLeft, -scrollbarTop); return rect; } -IntPoint RenderListBox::convertFromScrollbarToContainingView(const Scrollbar* scrollbar, const IntPoint& scrollbarPoint) const +IntPoint RenderListBox::convertFromScrollbarToContainingView(const Scrollbar& scrollbar, const IntPoint& scrollbarPoint) const { IntPoint point = scrollbarPoint; - int scrollbarLeft = width() - borderRight() - scrollbar->width(); + int scrollbarLeft = shouldPlaceBlockDirectionScrollbarOnLeft() ? borderLeft() : width() - borderRight() - scrollbar.width(); int scrollbarTop = borderTop(); point.move(scrollbarLeft, scrollbarTop); - return view().frameView().convertFromRenderer(this, point); + return view().frameView().convertFromRendererToContainingView(this, point); } -IntPoint RenderListBox::convertFromContainingViewToScrollbar(const Scrollbar* scrollbar, const IntPoint& parentPoint) const +IntPoint RenderListBox::convertFromContainingViewToScrollbar(const Scrollbar& scrollbar, const IntPoint& parentPoint) const { - IntPoint point = view().frameView().convertToRenderer(this, parentPoint); - int scrollbarLeft = width() - borderRight() - scrollbar->width(); + IntPoint point = view().frameView().convertFromContainingViewToRenderer(this, parentPoint); + int scrollbarLeft = shouldPlaceBlockDirectionScrollbarOnLeft() ? borderLeft() : width() - borderRight() - scrollbar.width(); int scrollbarTop = borderTop(); point.move(-scrollbarLeft, -scrollbarTop); return point; @@ -779,29 +860,56 @@ bool RenderListBox::shouldSuspendScrollAnimations() const return view().frameView().shouldSuspendScrollAnimations(); } +bool RenderListBox::forceUpdateScrollbarsOnMainThreadForPerformanceTesting() const +{ + return settings().forceUpdateScrollbarsOnMainThreadForPerformanceTesting(); +} + ScrollableArea* RenderListBox::enclosingScrollableArea() const { // FIXME: Return a RenderLayer that's scrollable. - return 0; + return nullptr; +} + +bool RenderListBox::isScrollableOrRubberbandable() +{ + return m_vBar; +} + +bool RenderListBox::hasScrollableOrRubberbandableAncestor() +{ + return enclosingLayer() && enclosingLayer()->hasScrollableOrRubberbandableAncestor(); } -IntRect RenderListBox::scrollableAreaBoundingBox() const +IntRect RenderListBox::scrollableAreaBoundingBox(bool*) const { return absoluteBoundingBoxRect(); } -PassRefPtr<Scrollbar> RenderListBox::createScrollbar() +bool RenderListBox::usesMockScrollAnimator() const +{ + return Settings::usesMockScrollAnimator(); +} + +void RenderListBox::logMockScrollAnimatorMessage(const String& message) const +{ + document().addConsoleMessage(MessageSource::Other, MessageLevel::Debug, "RenderListBox: " + message); +} + +Ref<Scrollbar> RenderListBox::createScrollbar() { RefPtr<Scrollbar> widget; bool hasCustomScrollbarStyle = style().hasPseudoStyle(SCROLLBAR); if (hasCustomScrollbarStyle) - widget = RenderScrollbar::createCustomScrollbar(this, VerticalScrollbar, &selectElement()); + widget = RenderScrollbar::createCustomScrollbar(*this, VerticalScrollbar, &selectElement()); else { - widget = Scrollbar::createNativeScrollbar(this, VerticalScrollbar, theme().scrollbarControlSizeForPart(ListboxPart)); + widget = Scrollbar::createNativeScrollbar(*this, VerticalScrollbar, theme().scrollbarControlSizeForPart(ListboxPart)); didAddScrollbar(widget.get(), VerticalScrollbar); + if (page().expectsWheelEventTriggers()) + scrollAnimator().setWheelEventTestTrigger(page().testTrigger()); } - view().frameView().addChild(widget.get()); - return widget.release(); + view().frameView().addChild(*widget); + return widget.releaseNonNull(); } void RenderListBox::destroyScrollbar() @@ -812,13 +920,12 @@ void RenderListBox::destroyScrollbar() if (!m_vBar->isCustomScrollbar()) ScrollableArea::willRemoveScrollbar(m_vBar.get(), VerticalScrollbar); m_vBar->removeFromParent(); - m_vBar->disconnectFromScrollableArea(); - m_vBar = 0; + m_vBar = nullptr; } void RenderListBox::setHasVerticalScrollbar(bool hasScrollbar) { - if (hasScrollbar == (m_vBar != 0)) + if (hasScrollbar == (m_vBar != nullptr)) return; if (hasScrollbar) @@ -836,4 +943,35 @@ void RenderListBox::setHasVerticalScrollbar(bool hasScrollbar) #endif } +bool RenderListBox::scrolledToTop() const +{ + if (Scrollbar* vbar = verticalScrollbar()) + return vbar->value() <= 0; + + return true; +} + +bool RenderListBox::scrolledToBottom() const +{ + Scrollbar* vbar = verticalScrollbar(); + if (!vbar) + return true; + + return vbar->value() >= vbar->maximum(); +} + +bool RenderListBox::scrolledToLeft() const +{ + // We do not scroll horizontally in a select element, so always report + // that we are at the full extent of the scroll. + return true; +} + +bool RenderListBox::scrolledToRight() const +{ + // We do not scroll horizontally in a select element, so always report + // that we are at the full extent of the scroll. + return true; +} + } // namespace WebCore |