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