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 "BlockExceptions.h"
30#include "ColorMac.h"
31#include "ImageBuffer.h"
32#include "GraphicsLayer.h"
33#include "LocalCurrentGraphicsContext.h"
34#include "NSScrollerImpDetails.h"
35#include "PlatformMouseEvent.h"
36#include "ScrollAnimatorMac.h"
37#include "ScrollView.h"
38#include "WebCoreSystemInterface.h"
39#include <Carbon/Carbon.h>
40#include <wtf/HashMap.h>
41#include <wtf/StdLibExtras.h>
42#include <wtf/TemporaryChange.h>
43
44// FIXME: There are repainting problems due to Aqua scroll bar buttons' visual overflow.
45
46using namespace WebCore;
47
48@interface NSColor (WebNSColorDetails)
49+ (NSImage *)_linenPatternImage;
50@end
51
52namespace WebCore {
53
54typedef HashMap<ScrollbarThemeClient*, RetainPtr<ScrollbarPainter>> ScrollbarPainterMap;
55
56static ScrollbarPainterMap* scrollbarMap()
57{
58    static ScrollbarPainterMap* map = new ScrollbarPainterMap;
59    return map;
60}
61
62}
63
64@interface WebScrollbarPrefsObserver : NSObject
65{
66}
67
68+ (void)registerAsObserver;
69+ (void)appearancePrefsChanged:(NSNotification*)theNotification;
70+ (void)behaviorPrefsChanged:(NSNotification*)theNotification;
71
72@end
73
74@implementation WebScrollbarPrefsObserver
75
76+ (void)appearancePrefsChanged:(NSNotification*)unusedNotification
77{
78    UNUSED_PARAM(unusedNotification);
79
80    ScrollbarTheme* theme = ScrollbarTheme::theme();
81    if (theme->isMockTheme())
82        return;
83
84    static_cast<ScrollbarThemeMac*>(ScrollbarTheme::theme())->preferencesChanged();
85    if (scrollbarMap()->isEmpty())
86        return;
87    ScrollbarPainterMap::iterator end = scrollbarMap()->end();
88    for (ScrollbarPainterMap::iterator it = scrollbarMap()->begin(); it != end; ++it) {
89        it->key->styleChanged();
90        it->key->invalidate();
91    }
92}
93
94+ (void)behaviorPrefsChanged:(NSNotification*)unusedNotification
95{
96    UNUSED_PARAM(unusedNotification);
97
98    ScrollbarTheme* theme = ScrollbarTheme::theme();
99    if (theme->isMockTheme())
100        return;
101
102    static_cast<ScrollbarThemeMac*>(ScrollbarTheme::theme())->preferencesChanged();
103}
104
105+ (void)registerAsObserver
106{
107    [[NSDistributedNotificationCenter defaultCenter] addObserver:self selector:@selector(appearancePrefsChanged:) name:@"AppleAquaScrollBarVariantChanged" object:nil suspensionBehavior:NSNotificationSuspensionBehaviorDeliverImmediately];
108    [[NSDistributedNotificationCenter defaultCenter] addObserver:self selector:@selector(behaviorPrefsChanged:) name:@"AppleNoRedisplayAppearancePreferenceChanged" object:nil suspensionBehavior:NSNotificationSuspensionBehaviorCoalesce];
109}
110
111@end
112
113namespace WebCore {
114
115ScrollbarTheme* ScrollbarTheme::nativeTheme()
116{
117    DEPRECATED_DEFINE_STATIC_LOCAL(ScrollbarThemeMac, theme, ());
118    return &theme;
119}
120
121// FIXME: Get these numbers from CoreUI.
122static int cRealButtonLength[] = { 28, 21 };
123static int cButtonHitInset[] = { 3, 2 };
124// cRealButtonLength - cButtonInset
125static int cButtonLength[] = { 14, 10 };
126
127static int cOuterButtonLength[] = { 16, 14 }; // The outer button in a double button pair is a bit bigger.
128static int cOuterButtonOverlap = 2;
129
130static float gInitialButtonDelay = 0.5f;
131static float gAutoscrollButtonDelay = 0.05f;
132static bool gJumpOnTrackClick = false;
133static bool gUsesOverlayScrollbars = false;
134
135static ScrollbarButtonsPlacement gButtonPlacement = ScrollbarButtonsDoubleEnd;
136
137static bool supportsExpandedScrollbars()
138{
139    // FIXME: This is temporary until all platforms that support ScrollbarPainter support this part of the API.
140    static bool globalSupportsExpandedScrollbars = [NSClassFromString(@"NSScrollerImp") instancesRespondToSelector:@selector(setExpanded:)];
141    return globalSupportsExpandedScrollbars;
142}
143
144static NSControlSize scrollbarControlSizeToNSControlSize(ScrollbarControlSize controlSize)
145{
146    switch (controlSize) {
147    case RegularScrollbar:
148        return NSRegularControlSize;
149    case SmallScrollbar:
150        return NSSmallControlSize;
151    }
152
153    ASSERT_NOT_REACHED();
154    return NSRegularControlSize;
155}
156
157void ScrollbarThemeMac::registerScrollbar(ScrollbarThemeClient* scrollbar)
158{
159    if (scrollbar->isCustomScrollbar())
160        return;
161
162    bool isHorizontal = scrollbar->orientation() == HorizontalScrollbar;
163    ScrollbarPainter scrollbarPainter = [NSClassFromString(@"NSScrollerImp") scrollerImpWithStyle:recommendedScrollerStyle() controlSize:scrollbarControlSizeToNSControlSize(scrollbar->controlSize()) horizontal:isHorizontal replacingScrollerImp:nil];
164    scrollbarMap()->add(scrollbar, scrollbarPainter);
165    updateEnabledState(scrollbar);
166    updateScrollbarOverlayStyle(scrollbar);
167}
168
169void ScrollbarThemeMac::unregisterScrollbar(ScrollbarThemeClient* scrollbar)
170{
171    scrollbarMap()->remove(scrollbar);
172}
173
174void ScrollbarThemeMac::setNewPainterForScrollbar(ScrollbarThemeClient* scrollbar, ScrollbarPainter newPainter)
175{
176    scrollbarMap()->set(scrollbar, newPainter);
177    updateEnabledState(scrollbar);
178    updateScrollbarOverlayStyle(scrollbar);
179}
180
181ScrollbarPainter ScrollbarThemeMac::painterForScrollbar(ScrollbarThemeClient* scrollbar)
182{
183    return scrollbarMap()->get(scrollbar).get();
184}
185
186static bool g_isCurrentlyDrawingIntoLayer;
187
188bool ScrollbarThemeMac::isCurrentlyDrawingIntoLayer()
189{
190    return g_isCurrentlyDrawingIntoLayer;
191}
192
193void ScrollbarThemeMac::setIsCurrentlyDrawingIntoLayer(bool b)
194{
195    g_isCurrentlyDrawingIntoLayer = b;
196}
197
198ScrollbarThemeMac::ScrollbarThemeMac()
199{
200    static bool initialized;
201    if (!initialized) {
202        initialized = true;
203        gButtonPlacement = ScrollbarButtonsNone;
204        [WebScrollbarPrefsObserver registerAsObserver];
205        preferencesChanged();
206    }
207}
208
209ScrollbarThemeMac::~ScrollbarThemeMac()
210{
211}
212
213void ScrollbarThemeMac::preferencesChanged()
214{
215    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
216    [defaults synchronize];
217    gInitialButtonDelay = [defaults floatForKey:@"NSScrollerButtonDelay"];
218    gAutoscrollButtonDelay = [defaults floatForKey:@"NSScrollerButtonPeriod"];
219    gJumpOnTrackClick = [defaults boolForKey:@"AppleScrollerPagingBehavior"];
220    usesOverlayScrollbarsChanged();
221}
222
223int ScrollbarThemeMac::scrollbarThickness(ScrollbarControlSize controlSize)
224{
225    BEGIN_BLOCK_OBJC_EXCEPTIONS;
226    ScrollbarPainter scrollbarPainter = [NSClassFromString(@"NSScrollerImp") scrollerImpWithStyle:recommendedScrollerStyle() controlSize:scrollbarControlSizeToNSControlSize(controlSize) horizontal:NO replacingScrollerImp:nil];
227    if (supportsExpandedScrollbars())
228        [scrollbarPainter setExpanded:YES];
229    return [scrollbarPainter trackBoxWidth];
230    END_BLOCK_OBJC_EXCEPTIONS;
231}
232
233bool ScrollbarThemeMac::usesOverlayScrollbars() const
234{
235    return gUsesOverlayScrollbars;
236}
237
238void ScrollbarThemeMac::usesOverlayScrollbarsChanged()
239{
240    gUsesOverlayScrollbars = recommendedScrollerStyle() == NSScrollerStyleOverlay;
241}
242
243void ScrollbarThemeMac::updateScrollbarOverlayStyle(ScrollbarThemeClient* scrollbar)
244{
245    BEGIN_BLOCK_OBJC_EXCEPTIONS;
246    ScrollbarPainter painter = painterForScrollbar(scrollbar);
247    switch (scrollbar->scrollbarOverlayStyle()) {
248    case ScrollbarOverlayStyleDefault:
249        [painter setKnobStyle:NSScrollerKnobStyleDefault];
250        break;
251    case ScrollbarOverlayStyleDark:
252        [painter setKnobStyle:NSScrollerKnobStyleDark];
253        break;
254    case ScrollbarOverlayStyleLight:
255        [painter setKnobStyle:NSScrollerKnobStyleLight];
256        break;
257    }
258    END_BLOCK_OBJC_EXCEPTIONS;
259}
260
261double ScrollbarThemeMac::initialAutoscrollTimerDelay()
262{
263    return gInitialButtonDelay;
264}
265
266double ScrollbarThemeMac::autoscrollTimerDelay()
267{
268    return gAutoscrollButtonDelay;
269}
270
271ScrollbarButtonsPlacement ScrollbarThemeMac::buttonsPlacement() const
272{
273    return gButtonPlacement;
274}
275
276bool ScrollbarThemeMac::hasButtons(ScrollbarThemeClient* scrollbar)
277{
278    return scrollbar->enabled() && buttonsPlacement() != ScrollbarButtonsNone
279             && (scrollbar->orientation() == HorizontalScrollbar
280             ? scrollbar->width()
281             : scrollbar->height()) >= 2 * (cRealButtonLength[scrollbar->controlSize()] - cButtonHitInset[scrollbar->controlSize()]);
282}
283
284bool ScrollbarThemeMac::hasThumb(ScrollbarThemeClient* scrollbar)
285{
286    int minLengthForThumb;
287
288    ScrollbarPainter painter = scrollbarMap()->get(scrollbar).get();
289    minLengthForThumb = [painter knobMinLength] + [painter trackOverlapEndInset] + [painter knobOverlapEndInset]
290        + 2 * ([painter trackEndInset] + [painter knobEndInset]);
291
292    return scrollbar->enabled() && (scrollbar->orientation() == HorizontalScrollbar ?
293             scrollbar->width() :
294             scrollbar->height()) >= minLengthForThumb;
295}
296
297static IntRect buttonRepaintRect(const IntRect& buttonRect, ScrollbarOrientation orientation, ScrollbarControlSize controlSize, bool start)
298{
299    ASSERT(gButtonPlacement != ScrollbarButtonsNone);
300
301    IntRect paintRect(buttonRect);
302    if (orientation == HorizontalScrollbar) {
303        paintRect.setWidth(cRealButtonLength[controlSize]);
304        if (!start)
305            paintRect.setX(buttonRect.x() - (cRealButtonLength[controlSize] - buttonRect.width()));
306    } else {
307        paintRect.setHeight(cRealButtonLength[controlSize]);
308        if (!start)
309            paintRect.setY(buttonRect.y() - (cRealButtonLength[controlSize] - buttonRect.height()));
310    }
311
312    return paintRect;
313}
314
315IntRect ScrollbarThemeMac::backButtonRect(ScrollbarThemeClient* scrollbar, ScrollbarPart part, bool painting)
316{
317    IntRect result;
318
319    if (part == BackButtonStartPart && (buttonsPlacement() == ScrollbarButtonsNone || buttonsPlacement() == ScrollbarButtonsDoubleEnd))
320        return result;
321
322    if (part == BackButtonEndPart && (buttonsPlacement() == ScrollbarButtonsNone || buttonsPlacement() == ScrollbarButtonsDoubleStart || buttonsPlacement() == ScrollbarButtonsSingle))
323        return result;
324
325    int thickness = scrollbarThickness(scrollbar->controlSize());
326    bool outerButton = part == BackButtonStartPart && (buttonsPlacement() == ScrollbarButtonsDoubleStart || buttonsPlacement() == ScrollbarButtonsDoubleBoth);
327    if (outerButton) {
328        if (scrollbar->orientation() == HorizontalScrollbar)
329            result = IntRect(scrollbar->x(), scrollbar->y(), cOuterButtonLength[scrollbar->controlSize()] + (painting ? cOuterButtonOverlap : 0), thickness);
330        else
331            result = IntRect(scrollbar->x(), scrollbar->y(), thickness, cOuterButtonLength[scrollbar->controlSize()] + (painting ? cOuterButtonOverlap : 0));
332        return result;
333    }
334
335    // Our repaint rect is slightly larger, since we are a button that is adjacent to the track.
336    if (scrollbar->orientation() == HorizontalScrollbar) {
337        int start = part == BackButtonStartPart ? scrollbar->x() : scrollbar->x() + scrollbar->width() - cOuterButtonLength[scrollbar->controlSize()] - cButtonLength[scrollbar->controlSize()];
338        result = IntRect(start, scrollbar->y(), cButtonLength[scrollbar->controlSize()], thickness);
339    } else {
340        int start = part == BackButtonStartPart ? scrollbar->y() : scrollbar->y() + scrollbar->height() - cOuterButtonLength[scrollbar->controlSize()] - cButtonLength[scrollbar->controlSize()];
341        result = IntRect(scrollbar->x(), start, thickness, cButtonLength[scrollbar->controlSize()]);
342    }
343
344    if (painting)
345        return buttonRepaintRect(result, scrollbar->orientation(), scrollbar->controlSize(), part == BackButtonStartPart);
346    return result;
347}
348
349IntRect ScrollbarThemeMac::forwardButtonRect(ScrollbarThemeClient* scrollbar, ScrollbarPart part, bool painting)
350{
351    IntRect result;
352
353    if (part == ForwardButtonEndPart && (buttonsPlacement() == ScrollbarButtonsNone || buttonsPlacement() == ScrollbarButtonsDoubleStart))
354        return result;
355
356    if (part == ForwardButtonStartPart && (buttonsPlacement() == ScrollbarButtonsNone || buttonsPlacement() == ScrollbarButtonsDoubleEnd || buttonsPlacement() == ScrollbarButtonsSingle))
357        return result;
358
359    int thickness = scrollbarThickness(scrollbar->controlSize());
360    int outerButtonLength = cOuterButtonLength[scrollbar->controlSize()];
361    int buttonLength = cButtonLength[scrollbar->controlSize()];
362
363    bool outerButton = part == ForwardButtonEndPart && (buttonsPlacement() == ScrollbarButtonsDoubleEnd || buttonsPlacement() == ScrollbarButtonsDoubleBoth);
364    if (outerButton) {
365        if (scrollbar->orientation() == HorizontalScrollbar) {
366            result = IntRect(scrollbar->x() + scrollbar->width() - outerButtonLength, scrollbar->y(), outerButtonLength, thickness);
367            if (painting)
368                result.inflateX(cOuterButtonOverlap);
369        } else {
370            result = IntRect(scrollbar->x(), scrollbar->y() + scrollbar->height() - outerButtonLength, thickness, outerButtonLength);
371            if (painting)
372                result.inflateY(cOuterButtonOverlap);
373        }
374        return result;
375    }
376
377    if (scrollbar->orientation() == HorizontalScrollbar) {
378        int start = part == ForwardButtonEndPart ? scrollbar->x() + scrollbar->width() - buttonLength : scrollbar->x() + outerButtonLength;
379        result = IntRect(start, scrollbar->y(), buttonLength, thickness);
380    } else {
381        int start = part == ForwardButtonEndPart ? scrollbar->y() + scrollbar->height() - buttonLength : scrollbar->y() + outerButtonLength;
382        result = IntRect(scrollbar->x(), start, thickness, buttonLength);
383    }
384    if (painting)
385        return buttonRepaintRect(result, scrollbar->orientation(), scrollbar->controlSize(), part == ForwardButtonStartPart);
386    return result;
387}
388
389IntRect ScrollbarThemeMac::trackRect(ScrollbarThemeClient* scrollbar, bool painting)
390{
391    if (painting || !hasButtons(scrollbar))
392        return scrollbar->frameRect();
393
394    IntRect result;
395    int thickness = scrollbarThickness(scrollbar->controlSize());
396    int startWidth = 0;
397    int endWidth = 0;
398    int outerButtonLength = cOuterButtonLength[scrollbar->controlSize()];
399    int buttonLength = cButtonLength[scrollbar->controlSize()];
400    int doubleButtonLength = outerButtonLength + buttonLength;
401    switch (buttonsPlacement()) {
402        case ScrollbarButtonsSingle:
403            startWidth = buttonLength;
404            endWidth = buttonLength;
405            break;
406        case ScrollbarButtonsDoubleStart:
407            startWidth = doubleButtonLength;
408            break;
409        case ScrollbarButtonsDoubleEnd:
410            endWidth = doubleButtonLength;
411            break;
412        case ScrollbarButtonsDoubleBoth:
413            startWidth = doubleButtonLength;
414            endWidth = doubleButtonLength;
415            break;
416        default:
417            break;
418    }
419
420    int totalWidth = startWidth + endWidth;
421    if (scrollbar->orientation() == HorizontalScrollbar)
422        return IntRect(scrollbar->x() + startWidth, scrollbar->y(), scrollbar->width() - totalWidth, thickness);
423    return IntRect(scrollbar->x(), scrollbar->y() + startWidth, thickness, scrollbar->height() - totalWidth);
424}
425
426int ScrollbarThemeMac::minimumThumbLength(ScrollbarThemeClient* scrollbar)
427{
428    BEGIN_BLOCK_OBJC_EXCEPTIONS;
429    return [scrollbarMap()->get(scrollbar) knobMinLength];
430    END_BLOCK_OBJC_EXCEPTIONS;
431}
432
433bool ScrollbarThemeMac::shouldCenterOnThumb(ScrollbarThemeClient*, const PlatformMouseEvent& evt)
434{
435    if (evt.button() != LeftButton)
436        return false;
437    if (gJumpOnTrackClick)
438        return !evt.altKey();
439    return evt.altKey();
440}
441
442bool ScrollbarThemeMac::shouldDragDocumentInsteadOfThumb(ScrollbarThemeClient*, const PlatformMouseEvent& event)
443{
444    return event.altKey();
445}
446
447int ScrollbarThemeMac::scrollbarPartToHIPressedState(ScrollbarPart part)
448{
449    switch (part) {
450        case BackButtonStartPart:
451            return kThemeTopOutsideArrowPressed;
452        case BackButtonEndPart:
453            return kThemeTopOutsideArrowPressed; // This does not make much sense.  For some reason the outside constant is required.
454        case ForwardButtonStartPart:
455            return kThemeTopInsideArrowPressed;
456        case ForwardButtonEndPart:
457            return kThemeBottomOutsideArrowPressed;
458        case ThumbPart:
459            return kThemeThumbPressed;
460        default:
461            return 0;
462    }
463}
464
465void ScrollbarThemeMac::updateEnabledState(ScrollbarThemeClient* scrollbar)
466{
467    BEGIN_BLOCK_OBJC_EXCEPTIONS;
468    [scrollbarMap()->get(scrollbar) setEnabled:scrollbar->enabled()];
469    END_BLOCK_OBJC_EXCEPTIONS;
470}
471
472void ScrollbarThemeMac::setPaintCharacteristicsForScrollbar(ScrollbarThemeClient* scrollbar)
473{
474    BEGIN_BLOCK_OBJC_EXCEPTIONS;
475    ScrollbarPainter painter = painterForScrollbar(scrollbar);
476
477    float value;
478    float overhang;
479    ScrollableArea::computeScrollbarValueAndOverhang(scrollbar->currentPos(), scrollbar->totalSize(), scrollbar->visibleSize(), value, overhang);
480    float proportion = scrollbar->totalSize() > 0 ? (static_cast<CGFloat>(scrollbar->visibleSize()) - overhang) / scrollbar->totalSize() : 1;
481
482    [painter setEnabled:scrollbar->enabled()];
483    [painter setBoundsSize:scrollbar->frameRect().size()];
484    [painter setDoubleValue:value];
485#if ENABLE(ASYNC_SCROLLING) && PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101000
486    [painter setPresentationValue:value];
487#endif
488    [painter setKnobProportion:proportion];
489    END_BLOCK_OBJC_EXCEPTIONS;
490}
491
492static void scrollbarPainterPaint(ScrollbarPainter scrollbarPainter, bool enabled)
493{
494    BEGIN_BLOCK_OBJC_EXCEPTIONS;
495    // Use rectForPart: here; it will take the expansion transition progress into account.
496    NSRect trackRect = [scrollbarPainter rectForPart:NSScrollerKnobSlot];
497    [scrollbarPainter drawKnobSlotInRect:trackRect highlight:NO];
498
499    // If the scrollbar is not enabled, then there is nothing to scroll to, and we shouldn't
500    // call drawKnob.
501    if (enabled)
502        [scrollbarPainter drawKnob];
503    END_BLOCK_OBJC_EXCEPTIONS;
504}
505
506bool ScrollbarThemeMac::paint(ScrollbarThemeClient* scrollbar, GraphicsContext* context, const IntRect& damageRect)
507{
508    setPaintCharacteristicsForScrollbar(scrollbar);
509
510    if (!scrollbar->supportsUpdateOnSecondaryThread()) {
511        TemporaryChange<bool> isCurrentlyDrawingIntoLayer(g_isCurrentlyDrawingIntoLayer, context->isCALayerContext());
512
513        GraphicsContextStateSaver stateSaver(*context);
514        context->clip(damageRect);
515        context->translate(scrollbar->frameRect().x(), scrollbar->frameRect().y());
516        LocalCurrentGraphicsContext localContext(context);
517        scrollbarPainterPaint(scrollbarMap()->get(scrollbar).get(), scrollbar->enabled());
518    }
519
520    return true;
521}
522
523#if ENABLE(RUBBER_BANDING)
524static RetainPtr<CGColorRef> linenBackgroundColor()
525{
526    NSImage *image = nil;
527    CGImageRef cgImage = nullptr;
528    BEGIN_BLOCK_OBJC_EXCEPTIONS;
529    image = [NSColor _linenPatternImage];
530    cgImage = [image CGImageForProposedRect:NULL context:NULL hints:nil];
531    END_BLOCK_OBJC_EXCEPTIONS;
532
533    if (!cgImage)
534        return nullptr;
535
536    RetainPtr<CGPatternRef> pattern = adoptCF(wkCGPatternCreateWithImageAndTransform(cgImage, CGAffineTransformIdentity, wkPatternTilingNoDistortion));
537    RetainPtr<CGColorSpaceRef> colorSpace = adoptCF(CGColorSpaceCreatePattern(0));
538
539    const CGFloat alpha = 1.0;
540    return adoptCF(CGColorCreateWithPattern(colorSpace.get(), pattern.get(), &alpha));
541}
542
543void ScrollbarThemeMac::setUpOverhangAreaBackground(CALayer *layer, const Color& customBackgroundColor)
544{
545    static CGColorRef cachedLinenBackgroundColor = linenBackgroundColor().leakRef();
546    // We operate on the CALayer directly here, since GraphicsLayer doesn't have the concept
547    // of pattern images, and we know that WebCore won't touch this layer.
548    layer.backgroundColor = customBackgroundColor.isValid() ? cachedCGColor(customBackgroundColor, ColorSpaceDeviceRGB) : cachedLinenBackgroundColor;
549}
550
551void ScrollbarThemeMac::removeOverhangAreaBackground(CALayer *layer)
552{
553    layer.backgroundColor = nil;
554}
555
556void ScrollbarThemeMac::setUpOverhangAreaShadow(CALayer *layer)
557{
558    static const CGFloat shadowOpacity = 0.66;
559    static const CGFloat shadowRadius = 3;
560
561    // We only need to set these shadow properties once.
562    if (!layer.shadowOpacity) {
563        layer.shadowColor = CGColorGetConstantColor(kCGColorBlack);
564        layer.shadowOffset = CGSizeZero;
565        layer.shadowOpacity = shadowOpacity;
566        layer.shadowRadius = shadowRadius;
567    }
568
569    RetainPtr<CGPathRef> shadowPath = adoptCF(CGPathCreateWithRect(layer.bounds, NULL));
570    layer.shadowPath = shadowPath.get();
571}
572
573void ScrollbarThemeMac::removeOverhangAreaShadow(CALayer *layer)
574{
575    layer.shadowPath = nil;
576    layer.shadowOpacity = 0;
577}
578
579void ScrollbarThemeMac::setUpOverhangAreasLayerContents(GraphicsLayer* graphicsLayer, const Color& customBackgroundColor)
580{
581    ScrollbarThemeMac::setUpOverhangAreaBackground(graphicsLayer->platformLayer(), customBackgroundColor);
582}
583
584void ScrollbarThemeMac::setUpContentShadowLayer(GraphicsLayer* graphicsLayer)
585{
586    // We operate on the CALayer directly here, since GraphicsLayer doesn't have the concept
587    // of shadows, and we know that WebCore won't touch this layer.
588    setUpOverhangAreaShadow(graphicsLayer->platformLayer());
589}
590
591#endif
592
593} // namespace WebCore
594