• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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