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 "RenderScrollbar.h"
30 #include "FrameView.h"
31 #include "RenderPart.h"
32 #include "RenderScrollbarPart.h"
33 #include "RenderScrollbarTheme.h"
37 PassRefPtr<Scrollbar> RenderScrollbar::createCustomScrollbar(ScrollableArea* scrollableArea, ScrollbarOrientation orientation, Node* ownerNode, Frame* owningFrame)
39 return adoptRef(new RenderScrollbar(scrollableArea, orientation, ownerNode, owningFrame));
42 RenderScrollbar::RenderScrollbar(ScrollableArea* scrollableArea, ScrollbarOrientation orientation, Node* ownerNode, Frame* owningFrame)
43 : Scrollbar(scrollableArea, orientation, RegularScrollbar, RenderScrollbarTheme::renderScrollbarTheme())
45 , m_owningFrame(owningFrame)
47 ASSERT(ownerNode || owningFrame);
49 // FIXME: We need to do this because RenderScrollbar::styleChanged is called as soon as the scrollbar is created.
51 // Update the scrollbar size.
54 updateScrollbarPart(ScrollbarBGPart);
55 if (RenderScrollbarPart* part = m_parts.get(ScrollbarBGPart)) {
57 width = part->width();
58 height = part->height();
59 } else if (this->orientation() == HorizontalScrollbar)
60 width = this->width();
62 height = this->height();
64 setFrameRect(IntRect(0, 0, width, height));
67 RenderScrollbar::~RenderScrollbar()
69 if (!m_parts.isEmpty()) {
70 // When a scrollbar is detached from its parent (causing all parts removal) and
71 // ready to be destroyed, its destruction can be delayed because of RefPtr
72 // maintained in other classes such as EventHandler (m_lastScrollbarUnderMouse).
73 // Meanwhile, we can have a call to updateScrollbarPart which recreates the
74 // scrollbar part. So, we need to destroy these parts since we don't want them
75 // to call on a destroyed scrollbar. See webkit bug 68009.
76 updateScrollbarParts(true);
80 RenderBox* RenderScrollbar::owningRenderer() const
83 RenderBox* currentRenderer = m_owningFrame->ownerRenderer();
84 return currentRenderer;
86 return m_owner && m_owner->renderer() ? m_owner->renderer()->enclosingBox() : 0;
89 void RenderScrollbar::setParent(ScrollView* parent)
91 Scrollbar::setParent(parent);
93 // Destroy all of the scrollbar's RenderBoxes.
94 updateScrollbarParts(true);
98 void RenderScrollbar::setEnabled(bool e)
100 bool wasEnabled = enabled();
101 Scrollbar::setEnabled(e);
103 updateScrollbarParts();
106 void RenderScrollbar::styleChanged()
108 updateScrollbarParts();
111 void RenderScrollbar::paint(GraphicsContext* context, const IntRect& damageRect)
113 if (context->updatingControlTints()) {
114 updateScrollbarParts();
117 Scrollbar::paint(context, damageRect);
120 void RenderScrollbar::setHoveredPart(ScrollbarPart part)
122 if (part == m_hoveredPart)
125 ScrollbarPart oldPart = m_hoveredPart;
126 m_hoveredPart = part;
128 updateScrollbarPart(oldPart);
129 updateScrollbarPart(m_hoveredPart);
131 updateScrollbarPart(ScrollbarBGPart);
132 updateScrollbarPart(TrackBGPart);
135 void RenderScrollbar::setPressedPart(ScrollbarPart part)
137 ScrollbarPart oldPart = m_pressedPart;
138 Scrollbar::setPressedPart(part);
140 updateScrollbarPart(oldPart);
141 updateScrollbarPart(part);
143 updateScrollbarPart(ScrollbarBGPart);
144 updateScrollbarPart(TrackBGPart);
147 static ScrollbarPart s_styleResolvePart;
148 static RenderScrollbar* s_styleResolveScrollbar;
150 RenderScrollbar* RenderScrollbar::scrollbarForStyleResolve()
152 return s_styleResolveScrollbar;
155 ScrollbarPart RenderScrollbar::partForStyleResolve()
157 return s_styleResolvePart;
160 PassRefPtr<RenderStyle> RenderScrollbar::getScrollbarPseudoStyle(ScrollbarPart partType, PseudoId pseudoId)
162 if (!owningRenderer())
165 s_styleResolvePart = partType;
166 s_styleResolveScrollbar = this;
167 RefPtr<RenderStyle> result = owningRenderer()->getUncachedPseudoStyle(pseudoId, owningRenderer()->style());
168 s_styleResolvePart = NoPart;
169 s_styleResolveScrollbar = 0;
171 // Scrollbars for root frames should always have background color
172 // unless explicitly specified as transparent. So we force it.
173 // This is because WebKit assumes scrollbar to be always painted and missing background
174 // causes visual artifact like non-repainted dirty region.
175 if (result && m_owningFrame && m_owningFrame->view() && !m_owningFrame->view()->isTransparent() && !result->hasBackground())
176 result->setBackgroundColor(Color::white);
181 void RenderScrollbar::updateScrollbarParts(bool destroy)
183 updateScrollbarPart(ScrollbarBGPart, destroy);
184 updateScrollbarPart(BackButtonStartPart, destroy);
185 updateScrollbarPart(ForwardButtonStartPart, destroy);
186 updateScrollbarPart(BackTrackPart, destroy);
187 updateScrollbarPart(ThumbPart, destroy);
188 updateScrollbarPart(ForwardTrackPart, destroy);
189 updateScrollbarPart(BackButtonEndPart, destroy);
190 updateScrollbarPart(ForwardButtonEndPart, destroy);
191 updateScrollbarPart(TrackBGPart, destroy);
196 // See if the scrollbar's thickness changed. If so, we need to mark our owning object as needing a layout.
197 bool isHorizontal = orientation() == HorizontalScrollbar;
198 int oldThickness = isHorizontal ? height() : width();
199 int newThickness = 0;
200 RenderScrollbarPart* part = m_parts.get(ScrollbarBGPart);
203 newThickness = isHorizontal ? part->height() : part->width();
206 if (newThickness != oldThickness) {
207 setFrameRect(IntRect(location(), IntSize(isHorizontal ? width() : newThickness, isHorizontal ? newThickness : height())));
208 if (RenderBox* box = owningRenderer())
209 box->setChildNeedsLayout(true);
213 static PseudoId pseudoForScrollbarPart(ScrollbarPart part)
216 case BackButtonStartPart:
217 case ForwardButtonStartPart:
218 case BackButtonEndPart:
219 case ForwardButtonEndPart:
220 return SCROLLBAR_BUTTON;
222 case ForwardTrackPart:
223 return SCROLLBAR_TRACK_PIECE;
225 return SCROLLBAR_THUMB;
227 return SCROLLBAR_TRACK;
228 case ScrollbarBGPart:
234 ASSERT_NOT_REACHED();
238 void RenderScrollbar::updateScrollbarPart(ScrollbarPart partType, bool destroy)
240 if (partType == NoPart)
243 RefPtr<RenderStyle> partStyle = !destroy ? getScrollbarPseudoStyle(partType, pseudoForScrollbarPart(partType)) : PassRefPtr<RenderStyle>(0);
245 bool needRenderer = !destroy && partStyle && partStyle->display() != NONE && partStyle->visibility() == VISIBLE;
247 if (needRenderer && partStyle->display() != BLOCK) {
248 // See if we are a button that should not be visible according to OS settings.
249 ScrollbarButtonsPlacement buttonsPlacement = theme()->buttonsPlacement();
251 case BackButtonStartPart:
252 needRenderer = (buttonsPlacement == ScrollbarButtonsSingle || buttonsPlacement == ScrollbarButtonsDoubleStart ||
253 buttonsPlacement == ScrollbarButtonsDoubleBoth);
255 case ForwardButtonStartPart:
256 needRenderer = (buttonsPlacement == ScrollbarButtonsDoubleStart || buttonsPlacement == ScrollbarButtonsDoubleBoth);
258 case BackButtonEndPart:
259 needRenderer = (buttonsPlacement == ScrollbarButtonsDoubleEnd || buttonsPlacement == ScrollbarButtonsDoubleBoth);
261 case ForwardButtonEndPart:
262 needRenderer = (buttonsPlacement == ScrollbarButtonsSingle || buttonsPlacement == ScrollbarButtonsDoubleEnd ||
263 buttonsPlacement == ScrollbarButtonsDoubleBoth);
270 RenderScrollbarPart* partRenderer = m_parts.get(partType);
271 if (!partRenderer && needRenderer) {
272 partRenderer = new (owningRenderer()->renderArena()) RenderScrollbarPart(owningRenderer()->document(), this, partType);
273 m_parts.set(partType, partRenderer);
274 } else if (partRenderer && !needRenderer) {
275 m_parts.remove(partType);
276 partRenderer->destroy();
281 partRenderer->setStyle(partStyle.release());
284 void RenderScrollbar::paintPart(GraphicsContext* graphicsContext, ScrollbarPart partType, const IntRect& rect)
286 RenderScrollbarPart* partRenderer = m_parts.get(partType);
289 partRenderer->paintIntoRect(graphicsContext, location(), rect);
292 IntRect RenderScrollbar::buttonRect(ScrollbarPart partType)
294 RenderScrollbarPart* partRenderer = m_parts.get(partType);
298 partRenderer->layout();
300 bool isHorizontal = orientation() == HorizontalScrollbar;
301 if (partType == BackButtonStartPart)
302 return IntRect(location(), IntSize(isHorizontal ? partRenderer->pixelSnappedWidth() : width(), isHorizontal ? height() : partRenderer->pixelSnappedHeight()));
303 if (partType == ForwardButtonEndPart)
304 return IntRect(isHorizontal ? x() + width() - partRenderer->pixelSnappedWidth() : x(),
305 isHorizontal ? y() : y() + height() - partRenderer->pixelSnappedHeight(),
306 isHorizontal ? partRenderer->pixelSnappedWidth() : width(),
307 isHorizontal ? height() : partRenderer->pixelSnappedHeight());
309 if (partType == ForwardButtonStartPart) {
310 IntRect previousButton = buttonRect(BackButtonStartPart);
311 return IntRect(isHorizontal ? x() + previousButton.width() : x(),
312 isHorizontal ? y() : y() + previousButton.height(),
313 isHorizontal ? partRenderer->pixelSnappedWidth() : width(),
314 isHorizontal ? height() : partRenderer->pixelSnappedHeight());
317 IntRect followingButton = buttonRect(ForwardButtonEndPart);
318 return IntRect(isHorizontal ? x() + width() - followingButton.width() - partRenderer->pixelSnappedWidth() : x(),
319 isHorizontal ? y() : y() + height() - followingButton.height() - partRenderer->pixelSnappedHeight(),
320 isHorizontal ? partRenderer->pixelSnappedWidth() : width(),
321 isHorizontal ? height() : partRenderer->pixelSnappedHeight());
324 IntRect RenderScrollbar::trackRect(int startLength, int endLength)
326 RenderScrollbarPart* part = m_parts.get(TrackBGPart);
330 if (orientation() == HorizontalScrollbar) {
331 int marginLeft = part ? static_cast<int>(part->marginLeft()) : 0;
332 int marginRight = part ? static_cast<int>(part->marginRight()) : 0;
333 startLength += marginLeft;
334 endLength += marginRight;
335 int totalLength = startLength + endLength;
336 return IntRect(x() + startLength, y(), width() - totalLength, height());
339 int marginTop = part ? static_cast<int>(part->marginTop()) : 0;
340 int marginBottom = part ? static_cast<int>(part->marginBottom()) : 0;
341 startLength += marginTop;
342 endLength += marginBottom;
343 int totalLength = startLength + endLength;
345 return IntRect(x(), y() + startLength, width(), height() - totalLength);
348 IntRect RenderScrollbar::trackPieceRectWithMargins(ScrollbarPart partType, const IntRect& oldRect)
350 RenderScrollbarPart* partRenderer = m_parts.get(partType);
354 partRenderer->layout();
356 IntRect rect = oldRect;
357 if (orientation() == HorizontalScrollbar) {
358 rect.setX(rect.x() + partRenderer->marginLeft());
359 rect.setWidth(rect.width() - partRenderer->marginWidth());
361 rect.setY(rect.y() + partRenderer->marginTop());
362 rect.setHeight(rect.height() - partRenderer->marginHeight());
367 int RenderScrollbar::minimumThumbLength()
369 RenderScrollbarPart* partRenderer = m_parts.get(ThumbPart);
372 partRenderer->layout();
373 return orientation() == HorizontalScrollbar ? partRenderer->width() : partRenderer->height();