summaryrefslogtreecommitdiff
path: root/Source/WebCore/rendering/RenderListBox.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/rendering/RenderListBox.cpp
parent32761a6cee1d0dee366b885b7b9c777e67885688 (diff)
downloadWebKitGtk-tarball-master.tar.gz
Diffstat (limited to 'Source/WebCore/rendering/RenderListBox.cpp')
-rw-r--r--Source/WebCore/rendering/RenderListBox.cpp432
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