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