2 * Copyright (C) 2008, 2009 Apple Inc. All Rights Reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 #include "core/rendering/RenderScrollbar.h"
29 #include "core/css/PseudoStyleRequest.h"
30 #include "core/frame/FrameView.h"
31 #include "core/frame/LocalFrame.h"
32 #include "core/rendering/RenderPart.h"
33 #include "core/rendering/RenderScrollbarPart.h"
34 #include "core/rendering/RenderScrollbarTheme.h"
38 PassRefPtr<Scrollbar> RenderScrollbar::createCustomScrollbar(ScrollableArea* scrollableArea, ScrollbarOrientation orientation, Node* ownerNode, LocalFrame* owningFrame)
40 return adoptRef(new RenderScrollbar(scrollableArea, orientation, ownerNode, owningFrame));
43 RenderScrollbar::RenderScrollbar(ScrollableArea* scrollableArea, ScrollbarOrientation orientation, Node* ownerNode, LocalFrame* owningFrame)
44 : Scrollbar(scrollableArea, orientation, RegularScrollbar, RenderScrollbarTheme::renderScrollbarTheme())
46 , m_owningFrame(owningFrame)
48 ASSERT(ownerNode || owningFrame);
50 // FIXME: We need to do this because RenderScrollbar::styleChanged is called as soon as the scrollbar is created.
52 // Update the scrollbar size.
55 updateScrollbarPart(ScrollbarBGPart);
56 if (RenderScrollbarPart* part = m_parts.get(ScrollbarBGPart)) {
58 width = part->width();
59 height = part->height();
60 } else if (this->orientation() == HorizontalScrollbar)
61 width = this->width();
63 height = this->height();
65 setFrameRect(IntRect(0, 0, width, height));
68 RenderScrollbar::~RenderScrollbar()
70 if (!m_parts.isEmpty()) {
71 // When a scrollbar is detached from its parent (causing all parts removal) and
72 // ready to be destroyed, its destruction can be delayed because of RefPtr
73 // maintained in other classes such as EventHandler (m_lastScrollbarUnderMouse).
74 // Meanwhile, we can have a call to updateScrollbarPart which recreates the
75 // scrollbar part. So, we need to destroy these parts since we don't want them
76 // to call on a destroyed scrollbar. See webkit bug 68009.
77 updateScrollbarParts(true);
81 RenderBox* RenderScrollbar::owningRenderer() const
84 RenderBox* currentRenderer = m_owningFrame->ownerRenderer();
85 return currentRenderer;
87 return m_owner && m_owner->renderer() ? m_owner->renderer()->enclosingBox() : 0;
90 void RenderScrollbar::setParent(Widget* parent)
92 Scrollbar::setParent(parent);
94 // Destroy all of the scrollbar's RenderBoxes.
95 updateScrollbarParts(true);
99 void RenderScrollbar::setEnabled(bool e)
101 bool wasEnabled = enabled();
102 Scrollbar::setEnabled(e);
104 updateScrollbarParts();
107 void RenderScrollbar::styleChanged()
109 updateScrollbarParts();
112 void RenderScrollbar::paint(GraphicsContext* context, const IntRect& damageRect)
114 if (context->updatingControlTints()) {
115 updateScrollbarParts();
118 Scrollbar::paint(context, damageRect);
121 void RenderScrollbar::setHoveredPart(ScrollbarPart part)
123 if (part == m_hoveredPart)
126 ScrollbarPart oldPart = m_hoveredPart;
127 m_hoveredPart = part;
129 updateScrollbarPart(oldPart);
130 updateScrollbarPart(m_hoveredPart);
132 updateScrollbarPart(ScrollbarBGPart);
133 updateScrollbarPart(TrackBGPart);
136 void RenderScrollbar::setPressedPart(ScrollbarPart part)
138 ScrollbarPart oldPart = m_pressedPart;
139 Scrollbar::setPressedPart(part);
141 updateScrollbarPart(oldPart);
142 updateScrollbarPart(part);
144 updateScrollbarPart(ScrollbarBGPart);
145 updateScrollbarPart(TrackBGPart);
148 PassRefPtr<RenderStyle> RenderScrollbar::getScrollbarPseudoStyle(ScrollbarPart partType, PseudoId pseudoId)
150 if (!owningRenderer())
153 RefPtr<RenderStyle> result = owningRenderer()->getUncachedPseudoStyle(PseudoStyleRequest(pseudoId, this, partType), owningRenderer()->style());
154 // Scrollbars for root frames should always have background color
155 // unless explicitly specified as transparent. So we force it.
156 // This is because WebKit assumes scrollbar to be always painted and missing background
157 // causes visual artifact like non-repainted dirty region.
158 if (result && m_owningFrame && m_owningFrame->view() && !m_owningFrame->view()->isTransparent() && !result->hasBackground())
159 result->setBackgroundColor(StyleColor(Color::white));
164 void RenderScrollbar::updateScrollbarParts(bool destroy)
166 updateScrollbarPart(ScrollbarBGPart, destroy);
167 updateScrollbarPart(BackButtonStartPart, destroy);
168 updateScrollbarPart(ForwardButtonStartPart, destroy);
169 updateScrollbarPart(BackTrackPart, destroy);
170 updateScrollbarPart(ThumbPart, destroy);
171 updateScrollbarPart(ForwardTrackPart, destroy);
172 updateScrollbarPart(BackButtonEndPart, destroy);
173 updateScrollbarPart(ForwardButtonEndPart, destroy);
174 updateScrollbarPart(TrackBGPart, destroy);
179 // See if the scrollbar's thickness changed. If so, we need to mark our owning object as needing a layout.
180 bool isHorizontal = orientation() == HorizontalScrollbar;
181 int oldThickness = isHorizontal ? height() : width();
182 int newThickness = 0;
183 RenderScrollbarPart* part = m_parts.get(ScrollbarBGPart);
186 newThickness = isHorizontal ? part->height() : part->width();
189 if (newThickness != oldThickness) {
190 setFrameRect(IntRect(location(), IntSize(isHorizontal ? width() : newThickness, isHorizontal ? newThickness : height())));
191 if (RenderBox* box = owningRenderer())
192 box->setChildNeedsLayout();
196 static PseudoId pseudoForScrollbarPart(ScrollbarPart part)
199 case BackButtonStartPart:
200 case ForwardButtonStartPart:
201 case BackButtonEndPart:
202 case ForwardButtonEndPart:
203 return SCROLLBAR_BUTTON;
205 case ForwardTrackPart:
206 return SCROLLBAR_TRACK_PIECE;
208 return SCROLLBAR_THUMB;
210 return SCROLLBAR_TRACK;
211 case ScrollbarBGPart:
217 ASSERT_NOT_REACHED();
221 void RenderScrollbar::updateScrollbarPart(ScrollbarPart partType, bool destroy)
223 if (partType == NoPart)
226 RefPtr<RenderStyle> partStyle = !destroy ? getScrollbarPseudoStyle(partType, pseudoForScrollbarPart(partType)) : PassRefPtr<RenderStyle>(nullptr);
228 bool needRenderer = !destroy && partStyle && partStyle->display() != NONE && partStyle->visibility() == VISIBLE;
230 if (needRenderer && partStyle->display() != BLOCK) {
231 // See if we are a button that should not be visible according to OS settings.
232 ScrollbarButtonsPlacement buttonsPlacement = theme()->buttonsPlacement();
234 case BackButtonStartPart:
235 needRenderer = (buttonsPlacement == ScrollbarButtonsSingle || buttonsPlacement == ScrollbarButtonsDoubleStart ||
236 buttonsPlacement == ScrollbarButtonsDoubleBoth);
238 case ForwardButtonStartPart:
239 needRenderer = (buttonsPlacement == ScrollbarButtonsDoubleStart || buttonsPlacement == ScrollbarButtonsDoubleBoth);
241 case BackButtonEndPart:
242 needRenderer = (buttonsPlacement == ScrollbarButtonsDoubleEnd || buttonsPlacement == ScrollbarButtonsDoubleBoth);
244 case ForwardButtonEndPart:
245 needRenderer = (buttonsPlacement == ScrollbarButtonsSingle || buttonsPlacement == ScrollbarButtonsDoubleEnd ||
246 buttonsPlacement == ScrollbarButtonsDoubleBoth);
253 RenderScrollbarPart* partRenderer = m_parts.get(partType);
254 if (!partRenderer && needRenderer) {
255 partRenderer = RenderScrollbarPart::createAnonymous(&owningRenderer()->document(), this, partType);
256 m_parts.set(partType, partRenderer);
257 } else if (partRenderer && !needRenderer) {
258 m_parts.remove(partType);
259 partRenderer->destroy();
264 partRenderer->setStyle(partStyle.release());
267 void RenderScrollbar::paintPart(GraphicsContext* graphicsContext, ScrollbarPart partType, const IntRect& rect)
269 RenderScrollbarPart* partRenderer = m_parts.get(partType);
272 partRenderer->paintIntoRect(graphicsContext, location(), rect);
275 IntRect RenderScrollbar::buttonRect(ScrollbarPart partType)
277 RenderScrollbarPart* partRenderer = m_parts.get(partType);
281 partRenderer->layout();
283 bool isHorizontal = orientation() == HorizontalScrollbar;
284 if (partType == BackButtonStartPart)
285 return IntRect(location(), IntSize(isHorizontal ? partRenderer->pixelSnappedWidth() : width(), isHorizontal ? height() : partRenderer->pixelSnappedHeight()));
286 if (partType == ForwardButtonEndPart)
287 return IntRect(isHorizontal ? x() + width() - partRenderer->pixelSnappedWidth() : x(),
288 isHorizontal ? y() : y() + height() - partRenderer->pixelSnappedHeight(),
289 isHorizontal ? partRenderer->pixelSnappedWidth() : width(),
290 isHorizontal ? height() : partRenderer->pixelSnappedHeight());
292 if (partType == ForwardButtonStartPart) {
293 IntRect previousButton = buttonRect(BackButtonStartPart);
294 return IntRect(isHorizontal ? x() + previousButton.width() : x(),
295 isHorizontal ? y() : y() + previousButton.height(),
296 isHorizontal ? partRenderer->pixelSnappedWidth() : width(),
297 isHorizontal ? height() : partRenderer->pixelSnappedHeight());
300 IntRect followingButton = buttonRect(ForwardButtonEndPart);
301 return IntRect(isHorizontal ? x() + width() - followingButton.width() - partRenderer->pixelSnappedWidth() : x(),
302 isHorizontal ? y() : y() + height() - followingButton.height() - partRenderer->pixelSnappedHeight(),
303 isHorizontal ? partRenderer->pixelSnappedWidth() : width(),
304 isHorizontal ? height() : partRenderer->pixelSnappedHeight());
307 IntRect RenderScrollbar::trackRect(int startLength, int endLength)
309 RenderScrollbarPart* part = m_parts.get(TrackBGPart);
313 if (orientation() == HorizontalScrollbar) {
314 int marginLeft = part ? static_cast<int>(part->marginLeft()) : 0;
315 int marginRight = part ? static_cast<int>(part->marginRight()) : 0;
316 startLength += marginLeft;
317 endLength += marginRight;
318 int totalLength = startLength + endLength;
319 return IntRect(x() + startLength, y(), width() - totalLength, height());
322 int marginTop = part ? static_cast<int>(part->marginTop()) : 0;
323 int marginBottom = part ? static_cast<int>(part->marginBottom()) : 0;
324 startLength += marginTop;
325 endLength += marginBottom;
326 int totalLength = startLength + endLength;
328 return IntRect(x(), y() + startLength, width(), height() - totalLength);
331 IntRect RenderScrollbar::trackPieceRectWithMargins(ScrollbarPart partType, const IntRect& oldRect)
333 RenderScrollbarPart* partRenderer = m_parts.get(partType);
337 partRenderer->layout();
339 IntRect rect = oldRect;
340 if (orientation() == HorizontalScrollbar) {
341 rect.setX(rect.x() + partRenderer->marginLeft());
342 rect.setWidth(rect.width() - partRenderer->marginWidth());
344 rect.setY(rect.y() + partRenderer->marginTop());
345 rect.setHeight(rect.height() - partRenderer->marginHeight());
350 int RenderScrollbar::minimumThumbLength()
352 RenderScrollbarPart* partRenderer = m_parts.get(ThumbPart);
355 partRenderer->layout();
356 return orientation() == HorizontalScrollbar ? partRenderer->width() : partRenderer->height();