1 /*
2 * Copyright (C) 2008, 2009 Apple Inc. All Rights Reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
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.
12 *
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.
24 */
25
26 #include "config.h"
27 #include "RenderScrollbar.h"
28
29 #include "Frame.h"
30 #include "FrameView.h"
31 #include "RenderPart.h"
32 #include "RenderScrollbarPart.h"
33 #include "RenderScrollbarTheme.h"
34
35 namespace WebCore {
36
createCustomScrollbar(ScrollableArea * scrollableArea,ScrollbarOrientation orientation,RenderBox * renderer,Frame * owningFrame)37 PassRefPtr<Scrollbar> RenderScrollbar::createCustomScrollbar(ScrollableArea* scrollableArea, ScrollbarOrientation orientation, RenderBox* renderer, Frame* owningFrame)
38 {
39 return adoptRef(new RenderScrollbar(scrollableArea, orientation, renderer, owningFrame));
40 }
41
RenderScrollbar(ScrollableArea * scrollableArea,ScrollbarOrientation orientation,RenderBox * renderer,Frame * owningFrame)42 RenderScrollbar::RenderScrollbar(ScrollableArea* scrollableArea, ScrollbarOrientation orientation, RenderBox* renderer, Frame* owningFrame)
43 : Scrollbar(scrollableArea, orientation, RegularScrollbar, RenderScrollbarTheme::renderScrollbarTheme())
44 , m_owner(renderer)
45 , m_owningFrame(owningFrame)
46 {
47 // FIXME: We need to do this because RenderScrollbar::styleChanged is called as soon as the scrollbar is created.
48
49 // Update the scrollbar size.
50 int width = 0;
51 int height = 0;
52 updateScrollbarPart(ScrollbarBGPart);
53 if (RenderScrollbarPart* part = m_parts.get(ScrollbarBGPart)) {
54 part->layout();
55 width = part->width();
56 height = part->height();
57 } else if (this->orientation() == HorizontalScrollbar)
58 width = this->width();
59 else
60 height = this->height();
61
62 setFrameRect(IntRect(0, 0, width, height));
63 }
64
~RenderScrollbar()65 RenderScrollbar::~RenderScrollbar()
66 {
67 ASSERT(m_parts.isEmpty());
68 }
69
owningRenderer() const70 RenderBox* RenderScrollbar::owningRenderer() const
71 {
72 if (m_owningFrame) {
73 RenderBox* currentRenderer = m_owningFrame->ownerRenderer();
74 return currentRenderer;
75 }
76 return m_owner;
77 }
78
setParent(ScrollView * parent)79 void RenderScrollbar::setParent(ScrollView* parent)
80 {
81 Scrollbar::setParent(parent);
82 if (!parent) {
83 // Destroy all of the scrollbar's RenderBoxes.
84 updateScrollbarParts(true);
85 }
86 }
87
setEnabled(bool e)88 void RenderScrollbar::setEnabled(bool e)
89 {
90 bool wasEnabled = enabled();
91 Scrollbar::setEnabled(e);
92 if (wasEnabled != e)
93 updateScrollbarParts();
94 }
95
styleChanged()96 void RenderScrollbar::styleChanged()
97 {
98 updateScrollbarParts();
99 }
100
paint(GraphicsContext * context,const IntRect & damageRect)101 void RenderScrollbar::paint(GraphicsContext* context, const IntRect& damageRect)
102 {
103 if (context->updatingControlTints()) {
104 updateScrollbarParts();
105 return;
106 }
107 Scrollbar::paint(context, damageRect);
108 }
109
setHoveredPart(ScrollbarPart part)110 void RenderScrollbar::setHoveredPart(ScrollbarPart part)
111 {
112 if (part == m_hoveredPart)
113 return;
114
115 ScrollbarPart oldPart = m_hoveredPart;
116 m_hoveredPart = part;
117
118 updateScrollbarPart(oldPart);
119 updateScrollbarPart(m_hoveredPart);
120
121 updateScrollbarPart(ScrollbarBGPart);
122 updateScrollbarPart(TrackBGPart);
123 }
124
setPressedPart(ScrollbarPart part)125 void RenderScrollbar::setPressedPart(ScrollbarPart part)
126 {
127 ScrollbarPart oldPart = m_pressedPart;
128 Scrollbar::setPressedPart(part);
129
130 updateScrollbarPart(oldPart);
131 updateScrollbarPart(part);
132
133 updateScrollbarPart(ScrollbarBGPart);
134 updateScrollbarPart(TrackBGPart);
135 }
136
137 static ScrollbarPart s_styleResolvePart;
138 static RenderScrollbar* s_styleResolveScrollbar;
139
scrollbarForStyleResolve()140 RenderScrollbar* RenderScrollbar::scrollbarForStyleResolve()
141 {
142 return s_styleResolveScrollbar;
143 }
144
partForStyleResolve()145 ScrollbarPart RenderScrollbar::partForStyleResolve()
146 {
147 return s_styleResolvePart;
148 }
149
getScrollbarPseudoStyle(ScrollbarPart partType,PseudoId pseudoId)150 PassRefPtr<RenderStyle> RenderScrollbar::getScrollbarPseudoStyle(ScrollbarPart partType, PseudoId pseudoId)
151 {
152 if (!m_owner)
153 return 0;
154
155 s_styleResolvePart = partType;
156 s_styleResolveScrollbar = this;
157 RefPtr<RenderStyle> result = owningRenderer()->getUncachedPseudoStyle(pseudoId, owningRenderer()->style());
158 s_styleResolvePart = NoPart;
159 s_styleResolveScrollbar = 0;
160
161 // Scrollbars for root frames should always have background color
162 // unless explicitly specified as transparent. So we force it.
163 // This is because WebKit assumes scrollbar to be always painted and missing background
164 // causes visual artifact like non-repainted dirty region.
165 if (result && m_owningFrame && m_owningFrame->view() && !m_owningFrame->view()->isTransparent() && !result->hasBackground())
166 result->setBackgroundColor(Color::white);
167
168 return result;
169 }
170
updateScrollbarParts(bool destroy)171 void RenderScrollbar::updateScrollbarParts(bool destroy)
172 {
173 updateScrollbarPart(ScrollbarBGPart, destroy);
174 updateScrollbarPart(BackButtonStartPart, destroy);
175 updateScrollbarPart(ForwardButtonStartPart, destroy);
176 updateScrollbarPart(BackTrackPart, destroy);
177 updateScrollbarPart(ThumbPart, destroy);
178 updateScrollbarPart(ForwardTrackPart, destroy);
179 updateScrollbarPart(BackButtonEndPart, destroy);
180 updateScrollbarPart(ForwardButtonEndPart, destroy);
181 updateScrollbarPart(TrackBGPart, destroy);
182
183 if (destroy)
184 return;
185
186 // See if the scrollbar's thickness changed. If so, we need to mark our owning object as needing a layout.
187 bool isHorizontal = orientation() == HorizontalScrollbar;
188 int oldThickness = isHorizontal ? height() : width();
189 int newThickness = 0;
190 RenderScrollbarPart* part = m_parts.get(ScrollbarBGPart);
191 if (part) {
192 part->layout();
193 newThickness = isHorizontal ? part->height() : part->width();
194 }
195
196 if (newThickness != oldThickness) {
197 setFrameRect(IntRect(x(), y(), isHorizontal ? width() : newThickness, isHorizontal ? newThickness : height()));
198 owningRenderer()->setChildNeedsLayout(true);
199 }
200 }
201
pseudoForScrollbarPart(ScrollbarPart part)202 static PseudoId pseudoForScrollbarPart(ScrollbarPart part)
203 {
204 switch (part) {
205 case BackButtonStartPart:
206 case ForwardButtonStartPart:
207 case BackButtonEndPart:
208 case ForwardButtonEndPart:
209 return SCROLLBAR_BUTTON;
210 case BackTrackPart:
211 case ForwardTrackPart:
212 return SCROLLBAR_TRACK_PIECE;
213 case ThumbPart:
214 return SCROLLBAR_THUMB;
215 case TrackBGPart:
216 return SCROLLBAR_TRACK;
217 case ScrollbarBGPart:
218 return SCROLLBAR;
219 case NoPart:
220 case AllParts:
221 break;
222 }
223 ASSERT_NOT_REACHED();
224 return SCROLLBAR;
225 }
226
updateScrollbarPart(ScrollbarPart partType,bool destroy)227 void RenderScrollbar::updateScrollbarPart(ScrollbarPart partType, bool destroy)
228 {
229 if (partType == NoPart)
230 return;
231
232 RefPtr<RenderStyle> partStyle = !destroy ? getScrollbarPseudoStyle(partType, pseudoForScrollbarPart(partType)) : 0;
233
234 bool needRenderer = !destroy && partStyle && partStyle->display() != NONE && partStyle->visibility() == VISIBLE;
235
236 if (needRenderer && partStyle->display() != BLOCK) {
237 // See if we are a button that should not be visible according to OS settings.
238 ScrollbarButtonsPlacement buttonsPlacement = theme()->buttonsPlacement();
239 switch (partType) {
240 case BackButtonStartPart:
241 needRenderer = (buttonsPlacement == ScrollbarButtonsSingle || buttonsPlacement == ScrollbarButtonsDoubleStart ||
242 buttonsPlacement == ScrollbarButtonsDoubleBoth);
243 break;
244 case ForwardButtonStartPart:
245 needRenderer = (buttonsPlacement == ScrollbarButtonsDoubleStart || buttonsPlacement == ScrollbarButtonsDoubleBoth);
246 break;
247 case BackButtonEndPart:
248 needRenderer = (buttonsPlacement == ScrollbarButtonsDoubleEnd || buttonsPlacement == ScrollbarButtonsDoubleBoth);
249 break;
250 case ForwardButtonEndPart:
251 needRenderer = (buttonsPlacement == ScrollbarButtonsSingle || buttonsPlacement == ScrollbarButtonsDoubleEnd ||
252 buttonsPlacement == ScrollbarButtonsDoubleBoth);
253 break;
254 default:
255 break;
256 }
257 }
258
259 RenderScrollbarPart* partRenderer = m_parts.get(partType);
260 if (!partRenderer && needRenderer) {
261 partRenderer = new (owningRenderer()->renderArena()) RenderScrollbarPart(owningRenderer()->document(), this, partType);
262 m_parts.set(partType, partRenderer);
263 } else if (partRenderer && !needRenderer) {
264 m_parts.remove(partType);
265 partRenderer->destroy();
266 partRenderer = 0;
267 }
268
269 if (partRenderer)
270 partRenderer->setStyle(partStyle.release());
271 }
272
paintPart(GraphicsContext * graphicsContext,ScrollbarPart partType,const IntRect & rect)273 void RenderScrollbar::paintPart(GraphicsContext* graphicsContext, ScrollbarPart partType, const IntRect& rect)
274 {
275 RenderScrollbarPart* partRenderer = m_parts.get(partType);
276 if (!partRenderer)
277 return;
278 partRenderer->paintIntoRect(graphicsContext, x(), y(), rect);
279 }
280
buttonRect(ScrollbarPart partType)281 IntRect RenderScrollbar::buttonRect(ScrollbarPart partType)
282 {
283 RenderScrollbarPart* partRenderer = m_parts.get(partType);
284 if (!partRenderer)
285 return IntRect();
286
287 partRenderer->layout();
288
289 bool isHorizontal = orientation() == HorizontalScrollbar;
290 if (partType == BackButtonStartPart)
291 return IntRect(x(), y(), isHorizontal ? partRenderer->width() : width(), isHorizontal ? height() : partRenderer->height());
292 if (partType == ForwardButtonEndPart)
293 return IntRect(isHorizontal ? x() + width() - partRenderer->width() : x(),
294
295 isHorizontal ? y() : y() + height() - partRenderer->height(),
296 isHorizontal ? partRenderer->width() : width(),
297 isHorizontal ? height() : partRenderer->height());
298
299 if (partType == ForwardButtonStartPart) {
300 IntRect previousButton = buttonRect(BackButtonStartPart);
301 return IntRect(isHorizontal ? x() + previousButton.width() : x(),
302 isHorizontal ? y() : y() + previousButton.height(),
303 isHorizontal ? partRenderer->width() : width(),
304 isHorizontal ? height() : partRenderer->height());
305 }
306
307 IntRect followingButton = buttonRect(ForwardButtonEndPart);
308 return IntRect(isHorizontal ? x() + width() - followingButton.width() - partRenderer->width() : x(),
309 isHorizontal ? y() : y() + height() - followingButton.height() - partRenderer->height(),
310 isHorizontal ? partRenderer->width() : width(),
311 isHorizontal ? height() : partRenderer->height());
312 }
313
trackRect(int startLength,int endLength)314 IntRect RenderScrollbar::trackRect(int startLength, int endLength)
315 {
316 RenderScrollbarPart* part = m_parts.get(TrackBGPart);
317 if (part)
318 part->layout();
319
320 if (orientation() == HorizontalScrollbar) {
321 int marginLeft = part ? part->marginLeft() : 0;
322 int marginRight = part ? part->marginRight() : 0;
323 startLength += marginLeft;
324 endLength += marginRight;
325 int totalLength = startLength + endLength;
326 return IntRect(x() + startLength, y(), width() - totalLength, height());
327 }
328
329 int marginTop = part ? part->marginTop() : 0;
330 int marginBottom = part ? part->marginBottom() : 0;
331 startLength += marginTop;
332 endLength += marginBottom;
333 int totalLength = startLength + endLength;
334
335 return IntRect(x(), y() + startLength, width(), height() - totalLength);
336 }
337
trackPieceRectWithMargins(ScrollbarPart partType,const IntRect & oldRect)338 IntRect RenderScrollbar::trackPieceRectWithMargins(ScrollbarPart partType, const IntRect& oldRect)
339 {
340 RenderScrollbarPart* partRenderer = m_parts.get(partType);
341 if (!partRenderer)
342 return oldRect;
343
344 partRenderer->layout();
345
346 IntRect rect = oldRect;
347 if (orientation() == HorizontalScrollbar) {
348 rect.setX(rect.x() + partRenderer->marginLeft());
349 rect.setWidth(rect.width() - (partRenderer->marginLeft() + partRenderer->marginRight()));
350 } else {
351 rect.setY(rect.y() + partRenderer->marginTop());
352 rect.setHeight(rect.height() - (partRenderer->marginTop() + partRenderer->marginBottom()));
353 }
354 return rect;
355 }
356
minimumThumbLength()357 int RenderScrollbar::minimumThumbLength()
358 {
359 RenderScrollbarPart* partRenderer = m_parts.get(ThumbPart);
360 if (!partRenderer)
361 return 0;
362 partRenderer->layout();
363 return orientation() == HorizontalScrollbar ? partRenderer->width() : partRenderer->height();
364 }
365
366 }
367