1/*
2 * Copyright (C) 2008, 2011 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 "ScrollbarThemeMac.h"
28
29#include "ImageBuffer.h"
30#include "LocalCurrentGraphicsContext.h"
31#include "PlatformMouseEvent.h"
32#include "ScrollAnimatorMac.h"
33#include "ScrollView.h"
34#include "WebCoreSystemInterface.h"
35#include <Carbon/Carbon.h>
36#include <wtf/HashMap.h>
37#include <wtf/StdLibExtras.h>
38#include <wtf/UnusedParam.h>
39
40// FIXME: There are repainting problems due to Aqua scroll bar buttons' visual overflow.
41
42using namespace std;
43using namespace WebCore;
44
45namespace WebCore {
46
47#if USE(WK_SCROLLBAR_PAINTER)
48typedef HashMap<Scrollbar*, RetainPtr<WKScrollbarPainterRef> > ScrollbarPainterMap;
49#else
50typedef HashSet<Scrollbar*> ScrollbarPainterMap;
51#endif
52
53static ScrollbarPainterMap* scrollbarMap()
54{
55    static ScrollbarPainterMap* map = new ScrollbarPainterMap;
56    return map;
57}
58
59}
60
61@interface ScrollbarPrefsObserver : NSObject
62{
63}
64
65+ (void)registerAsObserver;
66+ (void)appearancePrefsChanged:(NSNotification*)theNotification;
67+ (void)behaviorPrefsChanged:(NSNotification*)theNotification;
68
69@end
70
71@implementation ScrollbarPrefsObserver
72
73+ (void)appearancePrefsChanged:(NSNotification*)unusedNotification
74{
75    UNUSED_PARAM(unusedNotification);
76
77    static_cast<ScrollbarThemeMac*>(ScrollbarTheme::nativeTheme())->preferencesChanged();
78    if (scrollbarMap()->isEmpty())
79        return;
80    ScrollbarPainterMap::iterator end = scrollbarMap()->end();
81    for (ScrollbarPainterMap::iterator it = scrollbarMap()->begin(); it != end; ++it) {
82#if USE(WK_SCROLLBAR_PAINTER)
83        it->first->styleChanged();
84        it->first->invalidate();
85#else
86        (*it)->styleChanged();
87        (*it)->invalidate();
88#endif
89    }
90}
91
92+ (void)behaviorPrefsChanged:(NSNotification*)unusedNotification
93{
94    UNUSED_PARAM(unusedNotification);
95
96    static_cast<ScrollbarThemeMac*>(ScrollbarTheme::nativeTheme())->preferencesChanged();
97}
98
99+ (void)registerAsObserver
100{
101    [[NSDistributedNotificationCenter defaultCenter] addObserver:self selector:@selector(appearancePrefsChanged:) name:@"AppleAquaScrollBarVariantChanged" object:nil suspensionBehavior:NSNotificationSuspensionBehaviorDeliverImmediately];
102    [[NSDistributedNotificationCenter defaultCenter] addObserver:self selector:@selector(behaviorPrefsChanged:) name:@"AppleNoRedisplayAppearancePreferenceChanged" object:nil suspensionBehavior:NSNotificationSuspensionBehaviorCoalesce];
103}
104
105@end
106
107namespace WebCore {
108
109ScrollbarTheme* ScrollbarTheme::nativeTheme()
110{
111    DEFINE_STATIC_LOCAL(ScrollbarThemeMac, theme, ());
112    return &theme;
113}
114
115// FIXME: Get these numbers from CoreUI.
116static int cRealButtonLength[] = { 28, 21 };
117static int cButtonHitInset[] = { 3, 2 };
118// cRealButtonLength - cButtonInset
119static int cButtonLength[] = { 14, 10 };
120#if !USE(WK_SCROLLBAR_PAINTER)
121static int cScrollbarThickness[] = { 15, 11 };
122static int cButtonInset[] = { 14, 11 };
123static int cThumbMinLength[] = { 26, 20 };
124#endif
125
126static int cOuterButtonLength[] = { 16, 14 }; // The outer button in a double button pair is a bit bigger.
127static int cOuterButtonOverlap = 2;
128
129static float gInitialButtonDelay = 0.5f;
130static float gAutoscrollButtonDelay = 0.05f;
131static bool gJumpOnTrackClick = false;
132
133#if USE(WK_SCROLLBAR_PAINTER)
134static ScrollbarButtonsPlacement gButtonPlacement = ScrollbarButtonsNone;
135#else
136static ScrollbarButtonsPlacement gButtonPlacement = ScrollbarButtonsDoubleEnd;
137#endif
138
139static void updateArrowPlacement()
140{
141#if USE(WK_SCROLLBAR_PAINTER)
142    return;
143#endif
144    NSString *buttonPlacement = [[NSUserDefaults standardUserDefaults] objectForKey:@"AppleScrollBarVariant"];
145    if ([buttonPlacement isEqualToString:@"Single"])
146        gButtonPlacement = ScrollbarButtonsSingle;
147    else if ([buttonPlacement isEqualToString:@"DoubleMin"])
148        gButtonPlacement = ScrollbarButtonsDoubleStart;
149    else if ([buttonPlacement isEqualToString:@"DoubleBoth"])
150        gButtonPlacement = ScrollbarButtonsDoubleBoth;
151    else {
152
153        gButtonPlacement = ScrollbarButtonsDoubleEnd;
154    }
155}
156
157void ScrollbarThemeMac::registerScrollbar(Scrollbar* scrollbar)
158{
159#if USE(WK_SCROLLBAR_PAINTER)
160    bool isHorizontal = scrollbar->orientation() == HorizontalScrollbar;
161    WKScrollbarPainterRef scrollbarPainter = wkMakeScrollbarPainter(scrollbar->controlSize(), isHorizontal);
162    scrollbarMap()->add(scrollbar, scrollbarPainter);
163#else
164    scrollbarMap()->add(scrollbar);
165#endif
166}
167
168void ScrollbarThemeMac::unregisterScrollbar(Scrollbar* scrollbar)
169{
170    scrollbarMap()->remove(scrollbar);
171}
172
173#if USE(WK_SCROLLBAR_PAINTER)
174void ScrollbarThemeMac::setNewPainterForScrollbar(Scrollbar* scrollbar, WKScrollbarPainterRef newPainter)
175{
176    scrollbarMap()->set(scrollbar, newPainter);
177}
178
179WKScrollbarPainterRef ScrollbarThemeMac::painterForScrollbar(Scrollbar* scrollbar)
180{
181    return scrollbarMap()->get(scrollbar).get();
182}
183#endif
184
185ScrollbarThemeMac::ScrollbarThemeMac()
186{
187    static bool initialized;
188    if (!initialized) {
189        initialized = true;
190        [ScrollbarPrefsObserver registerAsObserver];
191        preferencesChanged();
192    }
193}
194
195ScrollbarThemeMac::~ScrollbarThemeMac()
196{
197}
198
199void ScrollbarThemeMac::preferencesChanged()
200{
201    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
202    [defaults synchronize];
203    updateArrowPlacement();
204    gInitialButtonDelay = [defaults floatForKey:@"NSScrollerButtonDelay"];
205    gAutoscrollButtonDelay = [defaults floatForKey:@"NSScrollerButtonPeriod"];
206    gJumpOnTrackClick = [defaults boolForKey:@"AppleScrollerPagingBehavior"];
207}
208
209int ScrollbarThemeMac::scrollbarThickness(ScrollbarControlSize controlSize)
210{
211#if USE(WK_SCROLLBAR_PAINTER)
212    return wkScrollbarThickness(controlSize);
213#else
214    return cScrollbarThickness[controlSize];
215#endif
216}
217
218bool ScrollbarThemeMac::usesOverlayScrollbars() const
219{
220#if USE(WK_SCROLLBAR_PAINTER)
221    return wkScrollbarPainterUsesOverlayScrollers();
222#else
223    return false;
224#endif
225}
226
227double ScrollbarThemeMac::initialAutoscrollTimerDelay()
228{
229    return gInitialButtonDelay;
230}
231
232double ScrollbarThemeMac::autoscrollTimerDelay()
233{
234    return gAutoscrollButtonDelay;
235}
236
237ScrollbarButtonsPlacement ScrollbarThemeMac::buttonsPlacement() const
238{
239    return gButtonPlacement;
240}
241
242bool ScrollbarThemeMac::hasButtons(Scrollbar* scrollbar)
243{
244    return scrollbar->enabled() && gButtonPlacement != ScrollbarButtonsNone
245             && (scrollbar->orientation() == HorizontalScrollbar
246             ? scrollbar->width()
247             : scrollbar->height()) >= 2 * (cRealButtonLength[scrollbar->controlSize()] - cButtonHitInset[scrollbar->controlSize()]);
248}
249
250bool ScrollbarThemeMac::hasThumb(Scrollbar* scrollbar)
251{
252    int minLengthForThumb;
253#if USE(WK_SCROLLBAR_PAINTER)
254    minLengthForThumb = wkScrollbarMinimumTotalLengthNeededForThumb(scrollbarMap()->get(scrollbar).get());
255#else
256    minLengthForThumb = 2 * cButtonInset[scrollbar->controlSize()] + cThumbMinLength[scrollbar->controlSize()] + 1;
257#endif
258    return scrollbar->enabled() && (scrollbar->orientation() == HorizontalScrollbar ?
259             scrollbar->width() :
260             scrollbar->height()) >= minLengthForThumb;
261}
262
263static IntRect buttonRepaintRect(const IntRect& buttonRect, ScrollbarOrientation orientation, ScrollbarControlSize controlSize, bool start)
264{
265    ASSERT(gButtonPlacement != ScrollbarButtonsNone);
266
267    IntRect paintRect(buttonRect);
268    if (orientation == HorizontalScrollbar) {
269        paintRect.setWidth(cRealButtonLength[controlSize]);
270        if (!start)
271            paintRect.setX(buttonRect.x() - (cRealButtonLength[controlSize] - buttonRect.width()));
272    } else {
273        paintRect.setHeight(cRealButtonLength[controlSize]);
274        if (!start)
275            paintRect.setY(buttonRect.y() - (cRealButtonLength[controlSize] - buttonRect.height()));
276    }
277
278    return paintRect;
279}
280
281IntRect ScrollbarThemeMac::backButtonRect(Scrollbar* scrollbar, ScrollbarPart part, bool painting)
282{
283    IntRect result;
284
285    if (part == BackButtonStartPart && (buttonsPlacement() == ScrollbarButtonsNone || buttonsPlacement() == ScrollbarButtonsDoubleEnd))
286        return result;
287
288    if (part == BackButtonEndPart && (buttonsPlacement() == ScrollbarButtonsNone || buttonsPlacement() == ScrollbarButtonsDoubleStart || buttonsPlacement() == ScrollbarButtonsSingle))
289        return result;
290
291    int thickness = scrollbarThickness(scrollbar->controlSize());
292    bool outerButton = part == BackButtonStartPart && (buttonsPlacement() == ScrollbarButtonsDoubleStart || buttonsPlacement() == ScrollbarButtonsDoubleBoth);
293    if (outerButton) {
294        if (scrollbar->orientation() == HorizontalScrollbar)
295            result = IntRect(scrollbar->x(), scrollbar->y(), cOuterButtonLength[scrollbar->controlSize()] + painting ? cOuterButtonOverlap : 0, thickness);
296        else
297            result = IntRect(scrollbar->x(), scrollbar->y(), thickness, cOuterButtonLength[scrollbar->controlSize()] + painting ? cOuterButtonOverlap : 0);
298        return result;
299    }
300
301    // Our repaint rect is slightly larger, since we are a button that is adjacent to the track.
302    if (scrollbar->orientation() == HorizontalScrollbar) {
303        int start = part == BackButtonStartPart ? scrollbar->x() : scrollbar->x() + scrollbar->width() - cOuterButtonLength[scrollbar->controlSize()] - cButtonLength[scrollbar->controlSize()];
304        result = IntRect(start, scrollbar->y(), cButtonLength[scrollbar->controlSize()], thickness);
305    } else {
306        int start = part == BackButtonStartPart ? scrollbar->y() : scrollbar->y() + scrollbar->height() - cOuterButtonLength[scrollbar->controlSize()] - cButtonLength[scrollbar->controlSize()];
307        result = IntRect(scrollbar->x(), start, thickness, cButtonLength[scrollbar->controlSize()]);
308    }
309
310    if (painting)
311        return buttonRepaintRect(result, scrollbar->orientation(), scrollbar->controlSize(), part == BackButtonStartPart);
312    return result;
313}
314
315IntRect ScrollbarThemeMac::forwardButtonRect(Scrollbar* scrollbar, ScrollbarPart part, bool painting)
316{
317    IntRect result;
318
319    if (part == ForwardButtonEndPart && (buttonsPlacement() == ScrollbarButtonsNone || buttonsPlacement() == ScrollbarButtonsDoubleStart))
320        return result;
321
322    if (part == ForwardButtonStartPart && (buttonsPlacement() == ScrollbarButtonsNone || buttonsPlacement() == ScrollbarButtonsDoubleEnd || buttonsPlacement() == ScrollbarButtonsSingle))
323        return result;
324
325    int thickness = scrollbarThickness(scrollbar->controlSize());
326    int outerButtonLength = cOuterButtonLength[scrollbar->controlSize()];
327    int buttonLength = cButtonLength[scrollbar->controlSize()];
328
329    bool outerButton = part == ForwardButtonEndPart && (buttonsPlacement() == ScrollbarButtonsDoubleEnd || buttonsPlacement() == ScrollbarButtonsDoubleBoth);
330    if (outerButton) {
331        if (scrollbar->orientation() == HorizontalScrollbar) {
332            result = IntRect(scrollbar->x() + scrollbar->width() - outerButtonLength, scrollbar->y(), outerButtonLength, thickness);
333            if (painting)
334                result.inflateX(cOuterButtonOverlap);
335        } else {
336            result = IntRect(scrollbar->x(), scrollbar->y() + scrollbar->height() - outerButtonLength, thickness, outerButtonLength);
337            if (painting)
338                result.inflateY(cOuterButtonOverlap);
339        }
340        return result;
341    }
342
343    if (scrollbar->orientation() == HorizontalScrollbar) {
344        int start = part == ForwardButtonEndPart ? scrollbar->x() + scrollbar->width() - buttonLength : scrollbar->x() + outerButtonLength;
345        result = IntRect(start, scrollbar->y(), buttonLength, thickness);
346    } else {
347        int start = part == ForwardButtonEndPart ? scrollbar->y() + scrollbar->height() - buttonLength : scrollbar->y() + outerButtonLength;
348        result = IntRect(scrollbar->x(), start, thickness, buttonLength);
349    }
350    if (painting)
351        return buttonRepaintRect(result, scrollbar->orientation(), scrollbar->controlSize(), part == ForwardButtonStartPart);
352    return result;
353}
354
355IntRect ScrollbarThemeMac::trackRect(Scrollbar* scrollbar, bool painting)
356{
357    if (painting || !hasButtons(scrollbar))
358        return scrollbar->frameRect();
359
360    IntRect result;
361    int thickness = scrollbarThickness(scrollbar->controlSize());
362    int startWidth = 0;
363    int endWidth = 0;
364    int outerButtonLength = cOuterButtonLength[scrollbar->controlSize()];
365    int buttonLength = cButtonLength[scrollbar->controlSize()];
366    int doubleButtonLength = outerButtonLength + buttonLength;
367    switch (buttonsPlacement()) {
368        case ScrollbarButtonsSingle:
369            startWidth = buttonLength;
370            endWidth = buttonLength;
371            break;
372        case ScrollbarButtonsDoubleStart:
373            startWidth = doubleButtonLength;
374            break;
375        case ScrollbarButtonsDoubleEnd:
376            endWidth = doubleButtonLength;
377            break;
378        case ScrollbarButtonsDoubleBoth:
379            startWidth = doubleButtonLength;
380            endWidth = doubleButtonLength;
381            break;
382        default:
383            break;
384    }
385
386    int totalWidth = startWidth + endWidth;
387    if (scrollbar->orientation() == HorizontalScrollbar)
388        return IntRect(scrollbar->x() + startWidth, scrollbar->y(), scrollbar->width() - totalWidth, thickness);
389    return IntRect(scrollbar->x(), scrollbar->y() + startWidth, thickness, scrollbar->height() - totalWidth);
390}
391
392int ScrollbarThemeMac::minimumThumbLength(Scrollbar* scrollbar)
393{
394#if USE(WK_SCROLLBAR_PAINTER)
395    return wkScrollbarMinimumThumbLength(scrollbarMap()->get(scrollbar).get());
396#else
397    return cThumbMinLength[scrollbar->controlSize()];
398#endif
399}
400
401bool ScrollbarThemeMac::shouldCenterOnThumb(Scrollbar*, const PlatformMouseEvent& evt)
402{
403    if (evt.button() != LeftButton)
404        return false;
405    if (gJumpOnTrackClick)
406        return !evt.altKey();
407    return evt.altKey();
408}
409
410bool ScrollbarThemeMac::shouldDragDocumentInsteadOfThumb(Scrollbar*, const PlatformMouseEvent& event)
411{
412    return event.altKey();
413}
414
415static int scrollbarPartToHIPressedState(ScrollbarPart part)
416{
417    switch (part) {
418        case BackButtonStartPart:
419            return kThemeTopOutsideArrowPressed;
420        case BackButtonEndPart:
421            return kThemeTopOutsideArrowPressed; // This does not make much sense.  For some reason the outside constant is required.
422        case ForwardButtonStartPart:
423            return kThemeTopInsideArrowPressed;
424        case ForwardButtonEndPart:
425            return kThemeBottomOutsideArrowPressed;
426        case ThumbPart:
427            return kThemeThumbPressed;
428        default:
429            return 0;
430    }
431}
432
433#if USE(WK_SCROLLBAR_PAINTER)
434static inline wkScrollerKnobStyle toScrollbarPainterKnobStyle(ScrollbarOverlayStyle style)
435{
436    switch (style) {
437    case ScrollbarOverlayStyleDark:
438        return wkScrollerKnobStyleDark;
439    case ScrollbarOverlayStyleLight:
440        return wkScrollerKnobStyleLight;
441    default:
442        return wkScrollerKnobStyleDefault;
443    }
444}
445#endif
446
447bool ScrollbarThemeMac::paint(Scrollbar* scrollbar, GraphicsContext* context, const IntRect& damageRect)
448{
449#if USE(WK_SCROLLBAR_PAINTER)
450    float value = 0;
451    float overhang = 0;
452
453    if (scrollbar->currentPos() < 0) {
454        // Scrolled past the top.
455        value = 0;
456        overhang = -scrollbar->currentPos();
457    } else if (scrollbar->visibleSize() + scrollbar->currentPos() > scrollbar->totalSize()) {
458        // Scrolled past the bottom.
459        value = 1;
460        overhang = scrollbar->currentPos() + scrollbar->visibleSize() - scrollbar->totalSize();
461    } else {
462        // Within the bounds of the scrollable area.
463        int maximum = scrollbar->maximum();
464        if (maximum > 0)
465            value = scrollbar->currentPos() / maximum;
466        else
467            value = 0;
468    }
469
470    ScrollAnimatorMac* scrollAnimator = static_cast<ScrollAnimatorMac*>(scrollbar->scrollableArea()->scrollAnimator());
471    scrollAnimator->setIsDrawingIntoLayer(context->isCALayerContext());
472
473#if USE(WK_SCROLLBAR_PAINTER)
474    wkSetScrollbarPainterKnobStyle(painterForScrollbar(scrollbar), toScrollbarPainterKnobStyle(scrollbar->scrollableArea()->recommendedScrollbarOverlayStyle()));
475#endif
476
477    GraphicsContextStateSaver stateSaver(*context);
478    context->clip(damageRect);
479    context->translate(scrollbar->frameRect().x(), scrollbar->frameRect().y());
480    LocalCurrentGraphicsContext localContext(context);
481    wkScrollbarPainterPaint(scrollbarMap()->get(scrollbar).get(),
482                            scrollbar->enabled(),
483                            value,
484                            (static_cast<CGFloat>(scrollbar->visibleSize()) - overhang) / scrollbar->totalSize(),
485                            scrollbar->frameRect());
486
487    scrollAnimator->setIsDrawingIntoLayer(false);
488    return true;
489#endif
490
491    HIThemeTrackDrawInfo trackInfo;
492    trackInfo.version = 0;
493    trackInfo.kind = scrollbar->controlSize() == RegularScrollbar ? kThemeMediumScrollBar : kThemeSmallScrollBar;
494    trackInfo.bounds = scrollbar->frameRect();
495
496    float maximum = 0.0f;
497    float position = 0.0f;
498    if (scrollbar->currentPos() < 0) {
499        // Scrolled past the top.
500        maximum = (scrollbar->totalSize() - scrollbar->currentPos()) - scrollbar->visibleSize();
501        position = 0;
502    } else if (scrollbar->visibleSize() + scrollbar->currentPos() > scrollbar->totalSize()) {
503        // Scrolled past the bottom.
504        maximum = scrollbar->currentPos();
505        position = maximum;
506    } else {
507        // Within the bounds of the scrollable area.
508        maximum = scrollbar->maximum();
509        position = scrollbar->currentPos();
510    }
511
512    trackInfo.min = 0;
513    trackInfo.max = static_cast<int>(maximum);
514    trackInfo.value = static_cast<int>(position);
515
516    trackInfo.trackInfo.scrollbar.viewsize = scrollbar->visibleSize();
517    trackInfo.attributes = 0;
518    if (scrollbar->orientation() == HorizontalScrollbar)
519        trackInfo.attributes |= kThemeTrackHorizontal;
520
521    if (!scrollbar->enabled())
522        trackInfo.enableState = kThemeTrackDisabled;
523    else
524        trackInfo.enableState = scrollbar->scrollableArea()->isActive() ? kThemeTrackActive : kThemeTrackInactive;
525
526    if (hasThumb(scrollbar))
527        trackInfo.attributes |= kThemeTrackShowThumb;
528    else if (!hasButtons(scrollbar))
529        trackInfo.enableState = kThemeTrackNothingToScroll;
530    trackInfo.trackInfo.scrollbar.pressState = scrollbarPartToHIPressedState(scrollbar->pressedPart());
531
532    // The Aqua scrollbar is buggy when rotated and scaled.  We will just draw into a bitmap if we detect a scale or rotation.
533    const AffineTransform& currentCTM = context->getCTM();
534    bool canDrawDirectly = currentCTM.isIdentityOrTranslationOrFlipped();
535    if (canDrawDirectly)
536        HIThemeDrawTrack(&trackInfo, 0, context->platformContext(), kHIThemeOrientationNormal);
537    else {
538        trackInfo.bounds = IntRect(IntPoint(), scrollbar->frameRect().size());
539
540        IntRect bufferRect(scrollbar->frameRect());
541        bufferRect.intersect(damageRect);
542
543        OwnPtr<ImageBuffer> imageBuffer = ImageBuffer::create(bufferRect.size());
544        if (!imageBuffer)
545            return true;
546
547        imageBuffer->context()->translate(scrollbar->frameRect().x() - bufferRect.x(), scrollbar->frameRect().y() - bufferRect.y());
548        HIThemeDrawTrack(&trackInfo, 0, imageBuffer->context()->platformContext(), kHIThemeOrientationNormal);
549        context->drawImageBuffer(imageBuffer.get(), ColorSpaceDeviceRGB, bufferRect.location());
550    }
551
552    return true;
553}
554
555}
556
557