blob: 9dc7221eeb4547cd7a7bec7e18df57ee29d69bed [file] [log] [blame]
/*
* Copyright (C) 2008, 2009, 2013, 2015 Apple Inc. All Rights Reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 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.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "config.h"
#include "RenderScrollbar.h"
#include "Frame.h"
#include "FrameView.h"
#include "RenderScrollbarPart.h"
#include "RenderScrollbarTheme.h"
#include "RenderWidget.h"
#include "StyleInheritedData.h"
#include "StyleResolver.h"
namespace WebCore {
Ref<Scrollbar> RenderScrollbar::createCustomScrollbar(ScrollableArea& scrollableArea, ScrollbarOrientation orientation, Element* ownerElement, Frame* owningFrame)
{
return adoptRef(*new RenderScrollbar(scrollableArea, orientation, ownerElement, owningFrame));
}
RenderScrollbar::RenderScrollbar(ScrollableArea& scrollableArea, ScrollbarOrientation orientation, Element* ownerElement, Frame* owningFrame)
: Scrollbar(scrollableArea, orientation, RegularScrollbar, RenderScrollbarTheme::renderScrollbarTheme(), true)
, m_ownerElement(ownerElement)
, m_owningFrame(owningFrame)
{
ASSERT(ownerElement || owningFrame);
// FIXME: We need to do this because RenderScrollbar::styleChanged is called as soon as the scrollbar is created.
// Update the scrollbar size.
int width = 0;
int height = 0;
updateScrollbarPart(ScrollbarBGPart);
if (RenderScrollbarPart* part = m_parts.get(ScrollbarBGPart)) {
part->layout();
width = part->width();
height = part->height();
} else if (this->orientation() == HorizontalScrollbar)
width = this->width();
else
height = this->height();
setFrameRect(IntRect(0, 0, width, height));
}
RenderScrollbar::~RenderScrollbar() = default;
RenderBox* RenderScrollbar::owningRenderer() const
{
if (m_owningFrame) {
RenderWidget* currentRenderer = m_owningFrame->ownerRenderer();
return currentRenderer;
}
ASSERT(m_ownerElement);
if (m_ownerElement->renderer())
return &m_ownerElement->renderer()->enclosingBox();
return nullptr;
}
void RenderScrollbar::setParent(ScrollView* parent)
{
Scrollbar::setParent(parent);
if (!parent)
m_parts.clear();
}
void RenderScrollbar::setEnabled(bool e)
{
bool wasEnabled = enabled();
Scrollbar::setEnabled(e);
if (wasEnabled != e)
updateScrollbarParts();
}
void RenderScrollbar::styleChanged()
{
updateScrollbarParts();
}
void RenderScrollbar::paint(GraphicsContext& context, const IntRect& damageRect, Widget::SecurityOriginPaintPolicy)
{
if (context.invalidatingControlTints()) {
updateScrollbarParts();
return;
}
Scrollbar::paint(context, damageRect);
}
void RenderScrollbar::setHoveredPart(ScrollbarPart part)
{
if (part == m_hoveredPart)
return;
ScrollbarPart oldPart = m_hoveredPart;
m_hoveredPart = part;
updateScrollbarPart(oldPart);
updateScrollbarPart(m_hoveredPart);
updateScrollbarPart(ScrollbarBGPart);
updateScrollbarPart(TrackBGPart);
}
void RenderScrollbar::setPressedPart(ScrollbarPart part)
{
ScrollbarPart oldPart = m_pressedPart;
Scrollbar::setPressedPart(part);
updateScrollbarPart(oldPart);
updateScrollbarPart(part);
updateScrollbarPart(ScrollbarBGPart);
updateScrollbarPart(TrackBGPart);
}
std::unique_ptr<RenderStyle> RenderScrollbar::getScrollbarPseudoStyle(ScrollbarPart partType, PseudoId pseudoId) const
{
if (!owningRenderer())
return nullptr;
StyleScrollbarState scrollbarState;
scrollbarState.scrollbarPart = partType;
scrollbarState.hoveredPart = hoveredPart();
scrollbarState.pressedPart = pressedPart();
scrollbarState.orientation = orientation();
scrollbarState.buttonsPlacement = theme().buttonsPlacement();
scrollbarState.enabled = enabled();
scrollbarState.scrollCornerIsVisible = scrollableArea().isScrollCornerVisible();
std::unique_ptr<RenderStyle> result = owningRenderer()->getUncachedPseudoStyle(PseudoStyleRequest(pseudoId, scrollbarState), &owningRenderer()->style());
// Scrollbars for root frames should always have background color
// unless explicitly specified as transparent. So we force it.
// This is because WebKit assumes scrollbar to be always painted and missing background
// causes visual artifact like non-repainted dirty region.
if (result && m_owningFrame && m_owningFrame->view() && !m_owningFrame->view()->isTransparent() && !result->hasBackground())
result->setBackgroundColor(Color::white);
return result;
}
void RenderScrollbar::updateScrollbarParts()
{
updateScrollbarPart(ScrollbarBGPart);
updateScrollbarPart(BackButtonStartPart);
updateScrollbarPart(ForwardButtonStartPart);
updateScrollbarPart(BackTrackPart);
updateScrollbarPart(ThumbPart);
updateScrollbarPart(ForwardTrackPart);
updateScrollbarPart(BackButtonEndPart);
updateScrollbarPart(ForwardButtonEndPart);
updateScrollbarPart(TrackBGPart);
// See if the scrollbar's thickness changed. If so, we need to mark our owning object as needing a layout.
bool isHorizontal = orientation() == HorizontalScrollbar;
int oldThickness = isHorizontal ? height() : width();
int newThickness = 0;
RenderScrollbarPart* part = m_parts.get(ScrollbarBGPart);
if (part) {
part->layout();
newThickness = isHorizontal ? part->height() : part->width();
}
if (newThickness != oldThickness) {
setFrameRect(IntRect(location(), IntSize(isHorizontal ? width() : newThickness, isHorizontal ? newThickness : height())));
if (RenderBox* box = owningRenderer())
box->setChildNeedsLayout();
}
}
static PseudoId pseudoForScrollbarPart(ScrollbarPart part)
{
switch (part) {
case BackButtonStartPart:
case ForwardButtonStartPart:
case BackButtonEndPart:
case ForwardButtonEndPart:
return PseudoId::ScrollbarButton;
case BackTrackPart:
case ForwardTrackPart:
return PseudoId::ScrollbarTrackPiece;
case ThumbPart:
return PseudoId::ScrollbarThumb;
case TrackBGPart:
return PseudoId::ScrollbarTrack;
case ScrollbarBGPart:
return PseudoId::Scrollbar;
case NoPart:
case AllParts:
break;
}
ASSERT_NOT_REACHED();
return PseudoId::Scrollbar;
}
void RenderScrollbar::updateScrollbarPart(ScrollbarPart partType)
{
if (partType == NoPart)
return;
std::unique_ptr<RenderStyle> partStyle = getScrollbarPseudoStyle(partType, pseudoForScrollbarPart(partType));
bool needRenderer = partStyle && partStyle->display() != DisplayType::None;
if (needRenderer && partStyle->display() != DisplayType::Block) {
// See if we are a button that should not be visible according to OS settings.
ScrollbarButtonsPlacement buttonsPlacement = theme().buttonsPlacement();
switch (partType) {
case BackButtonStartPart:
needRenderer = (buttonsPlacement == ScrollbarButtonsSingle || buttonsPlacement == ScrollbarButtonsDoubleStart ||
buttonsPlacement == ScrollbarButtonsDoubleBoth);
break;
case ForwardButtonStartPart:
needRenderer = (buttonsPlacement == ScrollbarButtonsDoubleStart || buttonsPlacement == ScrollbarButtonsDoubleBoth);
break;
case BackButtonEndPart:
needRenderer = (buttonsPlacement == ScrollbarButtonsDoubleEnd || buttonsPlacement == ScrollbarButtonsDoubleBoth);
break;
case ForwardButtonEndPart:
needRenderer = (buttonsPlacement == ScrollbarButtonsSingle || buttonsPlacement == ScrollbarButtonsDoubleEnd ||
buttonsPlacement == ScrollbarButtonsDoubleBoth);
break;
default:
break;
}
}
if (!needRenderer) {
m_parts.remove(partType);
return;
}
if (auto& partRendererSlot = m_parts.add(partType, nullptr).iterator->value)
partRendererSlot->setStyle(WTFMove(*partStyle));
else {
partRendererSlot = createRenderer<RenderScrollbarPart>(owningRenderer()->document(), WTFMove(*partStyle), this, partType);
partRendererSlot->initializeStyle();
}
}
void RenderScrollbar::paintPart(GraphicsContext& graphicsContext, ScrollbarPart partType, const IntRect& rect)
{
RenderScrollbarPart* partRenderer = m_parts.get(partType);
if (!partRenderer)
return;
partRenderer->paintIntoRect(graphicsContext, location(), rect);
}
IntRect RenderScrollbar::buttonRect(ScrollbarPart partType)
{
RenderScrollbarPart* partRenderer = m_parts.get(partType);
if (!partRenderer)
return IntRect();
partRenderer->layout();
bool isHorizontal = orientation() == HorizontalScrollbar;
IntSize pixelSnappedIntSize = snappedIntRect(partRenderer->frameRect()).size();
if (partType == BackButtonStartPart)
return IntRect(location(), IntSize(isHorizontal ? pixelSnappedIntSize.width() : width(), isHorizontal ? height() : pixelSnappedIntSize.height()));
if (partType == ForwardButtonEndPart)
return IntRect(isHorizontal ? x() + width() - pixelSnappedIntSize.width() : x(), isHorizontal ? y() : y() + height() - pixelSnappedIntSize.height(),
isHorizontal ? pixelSnappedIntSize.width() : width(),
isHorizontal ? height() : pixelSnappedIntSize.height());
if (partType == ForwardButtonStartPart) {
IntRect previousButton = buttonRect(BackButtonStartPart);
return IntRect(isHorizontal ? x() + previousButton.width() : x(),
isHorizontal ? y() : y() + previousButton.height(),
isHorizontal ? pixelSnappedIntSize.width() : width(),
isHorizontal ? height() : pixelSnappedIntSize.height());
}
IntRect followingButton = buttonRect(ForwardButtonEndPart);
return IntRect(isHorizontal ? x() + width() - followingButton.width() - pixelSnappedIntSize.width() : x(),
isHorizontal ? y() : y() + height() - followingButton.height() - pixelSnappedIntSize.height(),
isHorizontal ? pixelSnappedIntSize.width() : width(),
isHorizontal ? height() : pixelSnappedIntSize.height());
}
IntRect RenderScrollbar::trackRect(int startLength, int endLength)
{
RenderScrollbarPart* part = m_parts.get(TrackBGPart);
if (part)
part->layout();
if (orientation() == HorizontalScrollbar) {
int marginLeft = part ? static_cast<int>(part->marginLeft()) : 0;
int marginRight = part ? static_cast<int>(part->marginRight()) : 0;
startLength += marginLeft;
endLength += marginRight;
int totalLength = startLength + endLength;
return IntRect(x() + startLength, y(), width() - totalLength, height());
}
int marginTop = part ? static_cast<int>(part->marginTop()) : 0;
int marginBottom = part ? static_cast<int>(part->marginBottom()) : 0;
startLength += marginTop;
endLength += marginBottom;
int totalLength = startLength + endLength;
return IntRect(x(), y() + startLength, width(), height() - totalLength);
}
IntRect RenderScrollbar::trackPieceRectWithMargins(ScrollbarPart partType, const IntRect& oldRect)
{
RenderScrollbarPart* partRenderer = m_parts.get(partType);
if (!partRenderer)
return oldRect;
partRenderer->layout();
IntRect rect = oldRect;
if (orientation() == HorizontalScrollbar) {
rect.setX(rect.x() + partRenderer->marginLeft());
rect.setWidth(rect.width() - partRenderer->horizontalMarginExtent());
} else {
rect.setY(rect.y() + partRenderer->marginTop());
rect.setHeight(rect.height() - partRenderer->verticalMarginExtent());
}
return rect;
}
int RenderScrollbar::minimumThumbLength()
{
RenderScrollbarPart* partRenderer = m_parts.get(ThumbPart);
if (!partRenderer)
return 0;
partRenderer->layout();
return orientation() == HorizontalScrollbar ? partRenderer->width() : partRenderer->height();
}
float RenderScrollbar::opacity()
{
RenderScrollbarPart* partRenderer = m_parts.get(ScrollbarBGPart);
if (!partRenderer)
return 1;
return partRenderer->style().opacity();
}
bool RenderScrollbar::isHiddenByStyle() const
{
std::unique_ptr<RenderStyle> partStyle = getScrollbarPseudoStyle(ScrollbarBGPart, pseudoForScrollbarPart(ScrollbarBGPart));
return partStyle && partStyle->display() != DisplayType::None;
}
}