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