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