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