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