initial import
[vuplus_webkit] / Source / WebCore / platform / chromium / ScrollbarThemeChromiumMac.mm
1 /*
2  * Copyright (C) 2008 Apple Inc. All Rights Reserved.
3  * Copyright (C) 2009 Google Inc. All Rights Reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
15  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
18  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  */
26
27 #include "config.h"
28 #include "ScrollbarThemeChromiumMac.h"
29
30 #include "BitmapImage.h"
31 #include "FrameView.h"
32 #include "Gradient.h"
33 #include "ImageBuffer.h"
34 #include "LocalCurrentGraphicsContext.h"
35 #include "PlatformMouseEvent.h"
36 #include "PlatformSupport.h"
37 #include "ScrollAnimatorChromiumMac.h"
38 #include "ScrollView.h"
39 #include <Carbon/Carbon.h>
40 #include <wtf/HashMap.h>
41 #include <wtf/StdLibExtras.h>
42 #include <wtf/UnusedParam.h>
43
44 #if USE(SKIA)
45 #include "PlatformContextSkia.h"
46 #include "skia/ext/skia_utils_mac.h"
47 #endif
48
49
50
51 // FIXME: There are repainting problems due to Aqua scroll bar buttons' visual overflow.
52
53 using namespace std;
54 using namespace WebCore;
55
56 // This file (and its associated .h file) is a clone of ScrollbarThemeMac.mm.
57 // Because we want to draw tickmarks in the scrollbar, we must maintain a fork.
58 // Please maintain this file by performing parallel changes to it.
59 //
60 // The only changes from ScrollbarThemeMac should be:
61 // - The classname change from ScrollbarThemeMac to ScrollbarThemeChromiumMac.
62 // - In paint() the code to paint the track, tickmarks, and thumb separately.
63 // - In paint() the thumb is drawn via ChromeBridge/WebThemeEngine.
64 // - Various functions that were split using #if USE(WK_SCROLLBAR_PAINTER)
65 //   have been combined using runtime checks instead.
66 //
67 // For all other differences, if it was introduced in this file, then the
68 // maintainer forgot to include it in the list; otherwise it is an update that
69 // should have been applied to this file but was not.
70
71 namespace WebCore {
72
73 typedef HashMap<Scrollbar*, RetainPtr<WKScrollbarPainterRef> > ScrollbarPainterMap;
74
75 static ScrollbarPainterMap* scrollbarMap()
76 {
77     static ScrollbarPainterMap* map = new ScrollbarPainterMap;
78     return map;
79 }
80
81 }
82
83 @interface ScrollbarPrefsObserver : NSObject
84 {
85 }
86
87 + (void)registerAsObserver;
88 + (void)appearancePrefsChanged:(NSNotification*)theNotification;
89 + (void)behaviorPrefsChanged:(NSNotification*)theNotification;
90
91 @end
92
93 @implementation ScrollbarPrefsObserver
94
95 + (void)appearancePrefsChanged:(NSNotification*)unusedNotification
96 {
97     UNUSED_PARAM(unusedNotification);
98
99     static_cast<ScrollbarThemeChromiumMac*>(ScrollbarTheme::nativeTheme())->preferencesChanged();
100     if (scrollbarMap()->isEmpty())
101         return;
102     ScrollbarPainterMap::iterator end = scrollbarMap()->end();
103     for (ScrollbarPainterMap::iterator it = scrollbarMap()->begin(); it != end; ++it) {
104         it->first->styleChanged();
105         it->first->invalidate();
106     }
107 }
108
109 + (void)behaviorPrefsChanged:(NSNotification*)unusedNotification
110 {
111     UNUSED_PARAM(unusedNotification);
112
113     static_cast<ScrollbarThemeChromiumMac*>(ScrollbarTheme::nativeTheme())->preferencesChanged();
114 }
115
116 + (void)registerAsObserver
117 {
118     [[NSDistributedNotificationCenter defaultCenter] addObserver:self selector:@selector(appearancePrefsChanged:) name:@"AppleAquaScrollBarVariantChanged" object:nil suspensionBehavior:NSNotificationSuspensionBehaviorDeliverImmediately];
119     [[NSDistributedNotificationCenter defaultCenter] addObserver:self selector:@selector(behaviorPrefsChanged:) name:@"AppleNoRedisplayAppearancePreferenceChanged" object:nil suspensionBehavior:NSNotificationSuspensionBehaviorCoalesce];
120 }
121
122 @end
123
124 namespace WebCore {
125
126 ScrollbarTheme* ScrollbarTheme::nativeTheme()
127 {
128     DEFINE_STATIC_LOCAL(ScrollbarThemeChromiumMac, theme, ());
129     return &theme;
130 }
131
132 // FIXME: Get these numbers from CoreUI.
133 static int cScrollbarThickness[] = { 15, 11 };
134 static int cRealButtonLength[] = { 28, 21 };
135 static int cButtonInset[] = { 14, 11 };
136 static int cButtonHitInset[] = { 3, 2 };
137 // cRealButtonLength - cButtonInset
138 static int cButtonLength[] = { 14, 10 };
139 static int cThumbMinLength[] = { 26, 20 };
140
141 static int cOuterButtonLength[] = { 16, 14 }; // The outer button in a double button pair is a bit bigger.
142 static int cOuterButtonOverlap = 2;
143
144 static float gInitialButtonDelay = 0.5f;
145 static float gAutoscrollButtonDelay = 0.05f;
146 static bool gJumpOnTrackClick = false;
147 static ScrollbarButtonsPlacement gButtonPlacement = isScrollbarOverlayAPIAvailable() ? ScrollbarButtonsNone : ScrollbarButtonsDoubleEnd;
148
149 static void updateArrowPlacement()
150 {
151     if (isScrollbarOverlayAPIAvailable())
152         return;
153
154     NSString *buttonPlacement = [[NSUserDefaults standardUserDefaults] objectForKey:@"AppleScrollBarVariant"];
155     if ([buttonPlacement isEqualToString:@"Single"])
156         gButtonPlacement = ScrollbarButtonsSingle;
157     else if ([buttonPlacement isEqualToString:@"DoubleMin"])
158         gButtonPlacement = ScrollbarButtonsDoubleStart;
159     else if ([buttonPlacement isEqualToString:@"DoubleBoth"])
160         gButtonPlacement = ScrollbarButtonsDoubleBoth;
161     else
162         gButtonPlacement = ScrollbarButtonsDoubleEnd;
163 }
164
165 void ScrollbarThemeChromiumMac::registerScrollbar(Scrollbar* scrollbar)
166 {
167     bool isHorizontal = scrollbar->orientation() == HorizontalScrollbar;
168     WKScrollbarPainterRef scrollbarPainter = wkMakeScrollbarPainter(scrollbar->controlSize(), isHorizontal);
169     scrollbarMap()->add(scrollbar, scrollbarPainter);
170     updateEnabledState(scrollbar);
171     updateScrollbarOverlayStyle(scrollbar);
172 }
173
174 void ScrollbarThemeChromiumMac::unregisterScrollbar(Scrollbar* scrollbar)
175 {
176     scrollbarMap()->remove(scrollbar);
177 }
178
179 void ScrollbarThemeChromiumMac::setNewPainterForScrollbar(Scrollbar* scrollbar, WKScrollbarPainterRef newPainter)
180 {
181     scrollbarMap()->set(scrollbar, newPainter);
182     updateEnabledState(scrollbar);
183     updateScrollbarOverlayStyle(scrollbar);
184 }
185
186 WKScrollbarPainterRef ScrollbarThemeChromiumMac::painterForScrollbar(Scrollbar* scrollbar)
187 {
188     return scrollbarMap()->get(scrollbar).get();
189 }
190
191 ScrollbarThemeChromiumMac::ScrollbarThemeChromiumMac()
192 {
193     static bool initialized;
194     if (!initialized) {
195         initialized = true;
196
197         // Load the linen pattern image used for overhang drawing.
198         RefPtr<Image> patternImage = Image::loadPlatformResource("overhangPattern");
199         m_overhangPattern = Pattern::create(patternImage, true, true);
200
201         [ScrollbarPrefsObserver registerAsObserver];
202         preferencesChanged();
203     }
204 }
205
206 ScrollbarThemeChromiumMac::~ScrollbarThemeChromiumMac()
207 {
208 }
209
210 void ScrollbarThemeChromiumMac::preferencesChanged()
211 {
212     NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
213     [defaults synchronize];
214     updateArrowPlacement();
215     gInitialButtonDelay = [defaults floatForKey:@"NSScrollerButtonDelay"];
216     gAutoscrollButtonDelay = [defaults floatForKey:@"NSScrollerButtonPeriod"];
217     gJumpOnTrackClick = [defaults boolForKey:@"AppleScrollerPagingBehavior"];
218 }
219
220 int ScrollbarThemeChromiumMac::scrollbarThickness(ScrollbarControlSize controlSize)
221 {
222     if (isScrollbarOverlayAPIAvailable())
223         return wkScrollbarThickness(controlSize);
224     else
225         return cScrollbarThickness[controlSize];
226 }
227
228 bool ScrollbarThemeChromiumMac::usesOverlayScrollbars() const
229 {
230     if (isScrollbarOverlayAPIAvailable())
231         return wkScrollbarPainterUsesOverlayScrollers();
232     else
233         return false;
234 }
235
236 static inline wkScrollerKnobStyle toScrollbarPainterKnobStyle(ScrollbarOverlayStyle style)
237 {
238     switch (style) {
239     case ScrollbarOverlayStyleDark:
240         return wkScrollerKnobStyleDark;
241     case ScrollbarOverlayStyleLight:
242         return wkScrollerKnobStyleLight;
243     default:
244         return wkScrollerKnobStyleDefault;
245     }
246 }
247
248 void ScrollbarThemeChromiumMac::updateScrollbarOverlayStyle(Scrollbar* scrollbar)
249 {
250     if (isScrollbarOverlayAPIAvailable()) {
251         wkSetScrollbarPainterKnobStyle(painterForScrollbar(scrollbar), toScrollbarPainterKnobStyle(scrollbar->scrollableArea()->scrollbarOverlayStyle()));
252     }
253 }
254
255 double ScrollbarThemeChromiumMac::initialAutoscrollTimerDelay()
256 {
257     return gInitialButtonDelay;
258 }
259
260 double ScrollbarThemeChromiumMac::autoscrollTimerDelay()
261 {
262     return gAutoscrollButtonDelay;
263 }
264
265 ScrollbarButtonsPlacement ScrollbarThemeChromiumMac::buttonsPlacement() const
266 {
267     return gButtonPlacement;
268 }
269
270 bool ScrollbarThemeChromiumMac::hasButtons(Scrollbar* scrollbar)
271 {
272     return scrollbar->enabled() && gButtonPlacement != ScrollbarButtonsNone
273              && (scrollbar->orientation() == HorizontalScrollbar
274              ? scrollbar->width()
275              : scrollbar->height()) >= 2 * (cRealButtonLength[scrollbar->controlSize()] - cButtonHitInset[scrollbar->controlSize()]);
276 }
277
278 bool ScrollbarThemeChromiumMac::hasThumb(Scrollbar* scrollbar)
279 {
280     int minLengthForThumb;
281     if (isScrollbarOverlayAPIAvailable())
282         minLengthForThumb = wkScrollbarMinimumTotalLengthNeededForThumb(scrollbarMap()->get(scrollbar).get());
283     else
284         minLengthForThumb = 2 * cButtonInset[scrollbar->controlSize()] + cThumbMinLength[scrollbar->controlSize()] + 1;
285     return scrollbar->enabled() && (scrollbar->orientation() == HorizontalScrollbar ?
286              scrollbar->width() :
287              scrollbar->height()) >= minLengthForThumb;
288 }
289
290 static IntRect buttonRepaintRect(const IntRect& buttonRect, ScrollbarOrientation orientation, ScrollbarControlSize controlSize, bool start)
291 {
292     ASSERT(gButtonPlacement != ScrollbarButtonsNone);
293
294     IntRect paintRect(buttonRect);
295     if (orientation == HorizontalScrollbar) {
296         paintRect.setWidth(cRealButtonLength[controlSize]);
297         if (!start)
298             paintRect.setX(buttonRect.x() - (cRealButtonLength[controlSize] - buttonRect.width()));
299     } else {
300         paintRect.setHeight(cRealButtonLength[controlSize]);
301         if (!start)
302             paintRect.setY(buttonRect.y() - (cRealButtonLength[controlSize] - buttonRect.height()));
303     }
304
305     return paintRect;
306 }
307
308 IntRect ScrollbarThemeChromiumMac::backButtonRect(Scrollbar* scrollbar, ScrollbarPart part, bool painting)
309 {
310     IntRect result;
311
312     if (part == BackButtonStartPart && (buttonsPlacement() == ScrollbarButtonsNone || buttonsPlacement() == ScrollbarButtonsDoubleEnd))
313         return result;
314
315     if (part == BackButtonEndPart && (buttonsPlacement() == ScrollbarButtonsNone || buttonsPlacement() == ScrollbarButtonsDoubleStart || buttonsPlacement() == ScrollbarButtonsSingle))
316         return result;
317
318     int thickness = scrollbarThickness(scrollbar->controlSize());
319     bool outerButton = part == BackButtonStartPart && (buttonsPlacement() == ScrollbarButtonsDoubleStart || buttonsPlacement() == ScrollbarButtonsDoubleBoth);
320     if (outerButton) {
321         if (scrollbar->orientation() == HorizontalScrollbar)
322             result = IntRect(scrollbar->x(), scrollbar->y(), cOuterButtonLength[scrollbar->controlSize()] + (painting ? cOuterButtonOverlap : 0), thickness);
323         else
324             result = IntRect(scrollbar->x(), scrollbar->y(), thickness, cOuterButtonLength[scrollbar->controlSize()] + (painting ? cOuterButtonOverlap : 0));
325         return result;
326     }
327
328     // Our repaint rect is slightly larger, since we are a button that is adjacent to the track.
329     if (scrollbar->orientation() == HorizontalScrollbar) {
330         int start = part == BackButtonStartPart ? scrollbar->x() : scrollbar->x() + scrollbar->width() - cOuterButtonLength[scrollbar->controlSize()] - cButtonLength[scrollbar->controlSize()];
331         result = IntRect(start, scrollbar->y(), cButtonLength[scrollbar->controlSize()], thickness);
332     } else {
333         int start = part == BackButtonStartPart ? scrollbar->y() : scrollbar->y() + scrollbar->height() - cOuterButtonLength[scrollbar->controlSize()] - cButtonLength[scrollbar->controlSize()];
334         result = IntRect(scrollbar->x(), start, thickness, cButtonLength[scrollbar->controlSize()]);
335     }
336
337     if (painting)
338         return buttonRepaintRect(result, scrollbar->orientation(), scrollbar->controlSize(), part == BackButtonStartPart);
339     return result;
340 }
341
342 IntRect ScrollbarThemeChromiumMac::forwardButtonRect(Scrollbar* scrollbar, ScrollbarPart part, bool painting)
343 {
344     IntRect result;
345
346     if (part == ForwardButtonEndPart && (buttonsPlacement() == ScrollbarButtonsNone || buttonsPlacement() == ScrollbarButtonsDoubleStart))
347         return result;
348
349     if (part == ForwardButtonStartPart && (buttonsPlacement() == ScrollbarButtonsNone || buttonsPlacement() == ScrollbarButtonsDoubleEnd || buttonsPlacement() == ScrollbarButtonsSingle))
350         return result;
351
352     int thickness = scrollbarThickness(scrollbar->controlSize());
353     int outerButtonLength = cOuterButtonLength[scrollbar->controlSize()];
354     int buttonLength = cButtonLength[scrollbar->controlSize()];
355
356     bool outerButton = part == ForwardButtonEndPart && (buttonsPlacement() == ScrollbarButtonsDoubleEnd || buttonsPlacement() == ScrollbarButtonsDoubleBoth);
357     if (outerButton) {
358         if (scrollbar->orientation() == HorizontalScrollbar) {
359             result = IntRect(scrollbar->x() + scrollbar->width() - outerButtonLength, scrollbar->y(), outerButtonLength, thickness);
360             if (painting)
361                 result.inflateX(cOuterButtonOverlap);
362         } else {
363             result = IntRect(scrollbar->x(), scrollbar->y() + scrollbar->height() - outerButtonLength, thickness, outerButtonLength);
364             if (painting)
365                 result.inflateY(cOuterButtonOverlap);
366         }
367         return result;
368     }
369
370     if (scrollbar->orientation() == HorizontalScrollbar) {
371         int start = part == ForwardButtonEndPart ? scrollbar->x() + scrollbar->width() - buttonLength : scrollbar->x() + outerButtonLength;
372         result = IntRect(start, scrollbar->y(), buttonLength, thickness);
373     } else {
374         int start = part == ForwardButtonEndPart ? scrollbar->y() + scrollbar->height() - buttonLength : scrollbar->y() + outerButtonLength;
375         result = IntRect(scrollbar->x(), start, thickness, buttonLength);
376     }
377     if (painting)
378         return buttonRepaintRect(result, scrollbar->orientation(), scrollbar->controlSize(), part == ForwardButtonStartPart);
379     return result;
380 }
381
382 IntRect ScrollbarThemeChromiumMac::trackRect(Scrollbar* scrollbar, bool painting)
383 {
384     if (painting || !hasButtons(scrollbar))
385         return scrollbar->frameRect();
386
387     IntRect result;
388     int thickness = scrollbarThickness(scrollbar->controlSize());
389     int startWidth = 0;
390     int endWidth = 0;
391     int outerButtonLength = cOuterButtonLength[scrollbar->controlSize()];
392     int buttonLength = cButtonLength[scrollbar->controlSize()];
393     int doubleButtonLength = outerButtonLength + buttonLength;
394     switch (buttonsPlacement()) {
395         case ScrollbarButtonsSingle:
396             startWidth = buttonLength;
397             endWidth = buttonLength;
398             break;
399         case ScrollbarButtonsDoubleStart:
400             startWidth = doubleButtonLength;
401             break;
402         case ScrollbarButtonsDoubleEnd:
403             endWidth = doubleButtonLength;
404             break;
405         case ScrollbarButtonsDoubleBoth:
406             startWidth = doubleButtonLength;
407             endWidth = doubleButtonLength;
408             break;
409         default:
410             break;
411     }
412
413     int totalWidth = startWidth + endWidth;
414     if (scrollbar->orientation() == HorizontalScrollbar)
415         return IntRect(scrollbar->x() + startWidth, scrollbar->y(), scrollbar->width() - totalWidth, thickness);
416     return IntRect(scrollbar->x(), scrollbar->y() + startWidth, thickness, scrollbar->height() - totalWidth);
417 }
418
419 int ScrollbarThemeChromiumMac::minimumThumbLength(Scrollbar* scrollbar)
420 {
421     if (isScrollbarOverlayAPIAvailable())
422         return wkScrollbarMinimumThumbLength(scrollbarMap()->get(scrollbar).get());
423     else
424         return cThumbMinLength[scrollbar->controlSize()];
425 }
426
427 bool ScrollbarThemeChromiumMac::shouldCenterOnThumb(Scrollbar*, const PlatformMouseEvent& evt)
428 {
429     if (evt.button() != LeftButton)
430         return false;
431     if (gJumpOnTrackClick)
432         return !evt.altKey();
433     return evt.altKey();
434 }
435
436 bool ScrollbarThemeChromiumMac::shouldDragDocumentInsteadOfThumb(Scrollbar*, const PlatformMouseEvent& event)
437 {
438     return event.altKey();
439 }
440
441 static int scrollbarPartToHIPressedState(ScrollbarPart part)
442 {
443     switch (part) {
444         case BackButtonStartPart:
445             return kThemeTopOutsideArrowPressed;
446         case BackButtonEndPart:
447             return kThemeTopOutsideArrowPressed; // This does not make much sense.  For some reason the outside constant is required.
448         case ForwardButtonStartPart:
449             return kThemeTopInsideArrowPressed;
450         case ForwardButtonEndPart:
451             return kThemeBottomOutsideArrowPressed;
452         case ThumbPart:
453             return kThemeThumbPressed;
454         default:
455             return 0;
456     }
457 }
458
459 static PlatformSupport::ThemePaintState scrollbarStateToThemeState(Scrollbar* scrollbar)
460 {
461     if (!scrollbar->enabled())
462         return PlatformSupport::StateDisabled;
463     if (!scrollbar->scrollableArea()->isActive())
464         return PlatformSupport::StateInactive;
465     if (scrollbar->pressedPart() == ThumbPart)
466         return PlatformSupport::StatePressed;
467
468     return PlatformSupport::StateActive;
469 }
470
471 void ScrollbarThemeChromiumMac::updateEnabledState(Scrollbar* scrollbar)
472 {
473     if (isScrollbarOverlayAPIAvailable()) {
474         wkScrollbarPainterSetEnabled(scrollbarMap()->get(scrollbar).get(), scrollbar->enabled());
475     }
476 }
477
478 bool ScrollbarThemeChromiumMac::paint(Scrollbar* scrollbar, GraphicsContext* context, const IntRect& damageRect)
479 {
480     // Get the tickmarks for the frameview.
481     Vector<IntRect> tickmarks;
482     scrollbar->scrollableArea()->getTickmarks(tickmarks);
483
484     if (isScrollbarOverlayAPIAvailable()) {
485         float value = 0;
486         float overhang = 0;
487
488         if (scrollbar->currentPos() < 0) {
489             // Scrolled past the top.
490             value = 0;
491             overhang = -scrollbar->currentPos();
492         } else if (scrollbar->visibleSize() + scrollbar->currentPos() > scrollbar->totalSize()) {
493             // Scrolled past the bottom.
494             value = 1;
495             overhang = scrollbar->currentPos() + scrollbar->visibleSize() - scrollbar->totalSize();
496         } else {
497             // Within the bounds of the scrollable area.
498             int maximum = scrollbar->maximum();
499             if (maximum > 0)
500                 value = scrollbar->currentPos() / maximum;
501             else
502                 value = 0;
503         }
504
505         ScrollAnimatorChromiumMac* scrollAnimator = static_cast<ScrollAnimatorChromiumMac*>(scrollbar->scrollableArea()->scrollAnimator());
506 #if !USE(SKIA)
507         scrollAnimator->setIsDrawingIntoLayer(context->isCALayerContext());
508 #else
509         scrollAnimator->setIsDrawingIntoLayer(false);
510 #endif
511
512         CGFloat oldKnobAlpha = 0;
513         CGFloat oldTrackAlpha = 0;
514         bool hasTickmarks = tickmarks.size() > 0 && scrollbar->orientation() == VerticalScrollbar;
515         if (hasTickmarks) {
516           oldKnobAlpha = wkScrollbarPainterKnobAlpha(painterForScrollbar(scrollbar));
517           wkSetScrollbarPainterKnobAlpha(painterForScrollbar(scrollbar), 1.0);
518           oldTrackAlpha = wkScrollbarPainterTrackAlpha(painterForScrollbar(scrollbar));
519           wkSetScrollbarPainterTrackAlpha(painterForScrollbar(scrollbar), 1.0);
520         }
521
522         GraphicsContextStateSaver stateSaver(*context);
523         context->clip(damageRect);
524         context->translate(scrollbar->frameRect().x(), scrollbar->frameRect().y());
525         LocalCurrentGraphicsContext localContext(context);
526         WKScrollbarPainterRef scrollbarPainter = scrollbarMap()->get(scrollbar).get();
527         wkScrollbarPainterPaintTrack(scrollbarPainter,
528                                      scrollbar->enabled(),
529                                      value,
530                                      (static_cast<CGFloat>(scrollbar->visibleSize()) - overhang) / scrollbar->totalSize(),
531                                      scrollbar->frameRect());
532
533         IntRect tickmarkTrackRect(IntPoint(), trackRect(scrollbar, false).size());
534         if (tickmarkTrackRect.width() <= 10) {
535             // For narrow scrollbars inset by 1 on the left and 3 on the right.
536             tickmarkTrackRect.setX(tickmarkTrackRect.x() + 1);
537             tickmarkTrackRect.setWidth(tickmarkTrackRect.width() - 4);
538         } else {
539             // For wide scrollbars inset by 2 on the left and 3 on the right.
540             tickmarkTrackRect.setX(tickmarkTrackRect.x() + 2);
541             tickmarkTrackRect.setWidth(tickmarkTrackRect.width() - 5);
542         }
543         paintGivenTickmarks(context, scrollbar, tickmarkTrackRect, tickmarks);
544
545         wkScrollbarPainterPaintKnob(scrollbarPainter);
546
547         scrollAnimator->setIsDrawingIntoLayer(false);
548
549         if (hasTickmarks) {
550           wkSetScrollbarPainterKnobAlpha(scrollbarPainter, oldKnobAlpha);
551           wkSetScrollbarPainterTrackAlpha(scrollbarPainter, oldTrackAlpha);
552         }
553
554         return true;
555     }
556
557     HIThemeTrackDrawInfo trackInfo;
558     trackInfo.version = 0;
559     trackInfo.kind = scrollbar->controlSize() == RegularScrollbar ? kThemeMediumScrollBar : kThemeSmallScrollBar;
560     trackInfo.bounds = scrollbar->frameRect();
561     trackInfo.min = 0;
562     trackInfo.max = scrollbar->maximum();
563     trackInfo.value = scrollbar->currentPos();
564     trackInfo.trackInfo.scrollbar.viewsize = scrollbar->visibleSize();
565     trackInfo.attributes = 0;
566     if (scrollbar->orientation() == HorizontalScrollbar)
567         trackInfo.attributes |= kThemeTrackHorizontal;
568
569     if (!scrollbar->enabled())
570         trackInfo.enableState = kThemeTrackDisabled;
571     else
572         trackInfo.enableState = scrollbar->scrollableArea()->isActive() ? kThemeTrackActive : kThemeTrackInactive;
573
574     if (!hasButtons(scrollbar))
575         trackInfo.enableState = kThemeTrackNothingToScroll;
576     trackInfo.trackInfo.scrollbar.pressState = scrollbarPartToHIPressedState(scrollbar->pressedPart());
577
578 #if USE(SKIA)
579     SkCanvas* canvas = context->platformContext()->canvas();
580     CGAffineTransform currentCTM = gfx::SkMatrixToCGAffineTransform(canvas->getTotalMatrix());
581 #else
582     CGAffineTransform currentCTM = CGContextGetCTM(context->platformContext());
583 #endif
584
585     // The Aqua scrollbar is buggy when rotated and scaled.  We will just draw into a bitmap if we detect a scale or rotation.
586     bool canDrawDirectly = currentCTM.a == 1.0f && currentCTM.b == 0.0f && currentCTM.c == 0.0f && (currentCTM.d == 1.0f || currentCTM.d == -1.0f);
587     GraphicsContext* drawingContext = context;
588     OwnPtr<ImageBuffer> imageBuffer;
589     if (!canDrawDirectly) {
590         trackInfo.bounds = IntRect(IntPoint(), scrollbar->frameRect().size());
591
592         IntRect bufferRect(scrollbar->frameRect());
593         bufferRect.intersect(damageRect);
594         bufferRect.move(-scrollbar->frameRect().x(), -scrollbar->frameRect().y());
595
596         imageBuffer = ImageBuffer::create(bufferRect.size());
597         if (!imageBuffer)
598             return true;
599
600         drawingContext = imageBuffer->context();
601     }
602
603     // Draw thumbless.
604 #if USE(SKIA)
605     gfx::SkiaBitLocker bitLocker(drawingContext->platformContext()->canvas());
606     CGContextRef cgContext = bitLocker.cgContext();
607 #else
608     CGContextRef cgContext = drawingContext->platformContext();
609 #endif
610     HIThemeDrawTrack(&trackInfo, 0, cgContext, kHIThemeOrientationNormal);
611
612     IntRect tickmarkTrackRect = trackRect(scrollbar, false);
613     if (!canDrawDirectly) {
614         tickmarkTrackRect.setX(0);
615         tickmarkTrackRect.setY(0);
616     }
617     // The ends are rounded and the thumb doesn't go there.
618     tickmarkTrackRect.inflateY(-tickmarkTrackRect.width());
619     // Inset by 2 on the left and 3 on the right.
620     tickmarkTrackRect.setX(tickmarkTrackRect.x() + 2);
621     tickmarkTrackRect.setWidth(tickmarkTrackRect.width() - 5);
622     paintGivenTickmarks(drawingContext, scrollbar, tickmarkTrackRect, tickmarks);
623
624     if (hasThumb(scrollbar)) {
625         PlatformSupport::ThemePaintScrollbarInfo scrollbarInfo;
626         scrollbarInfo.orientation = scrollbar->orientation() == HorizontalScrollbar ? PlatformSupport::ScrollbarOrientationHorizontal : PlatformSupport::ScrollbarOrientationVertical;
627         scrollbarInfo.parent = scrollbar->parent() && scrollbar->parent()->isFrameView() && static_cast<FrameView*>(scrollbar->parent())->isScrollViewScrollbar(scrollbar) ? PlatformSupport::ScrollbarParentScrollView : PlatformSupport::ScrollbarParentRenderLayer;
628         scrollbarInfo.maxValue = scrollbar->maximum();
629         scrollbarInfo.currentValue = scrollbar->currentPos();
630         scrollbarInfo.visibleSize = scrollbar->visibleSize();
631         scrollbarInfo.totalSize = scrollbar->totalSize();
632
633         PlatformSupport::paintScrollbarThumb(
634             drawingContext,
635             scrollbarStateToThemeState(scrollbar),
636             scrollbar->controlSize() == RegularScrollbar ? PlatformSupport::SizeRegular : PlatformSupport::SizeSmall,
637             scrollbar->frameRect(),
638             scrollbarInfo);
639     }
640
641     if (!canDrawDirectly)
642         context->drawImageBuffer(imageBuffer.get(), ColorSpaceDeviceRGB, scrollbar->frameRect().location());
643
644     return true;
645 }
646
647 void ScrollbarThemeChromiumMac::paintGivenTickmarks(GraphicsContext* context, Scrollbar* scrollbar, const IntRect& rect, const Vector<IntRect>& tickmarks)
648 {
649     if (scrollbar->orientation() != VerticalScrollbar)
650         return;
651
652     if (rect.height() <= 0 || rect.width() <= 0)
653         return;  // nothing to draw on.
654
655     if (!tickmarks.size())
656         return;
657
658     GraphicsContextStateSaver stateSaver(*context);
659     context->setShouldAntialias(false);
660     context->setStrokeColor(Color(0xCC, 0xAA, 0x00, 0xFF), ColorSpaceDeviceRGB);
661     context->setFillColor(Color(0xFF, 0xDD, 0x00, 0xFF), ColorSpaceDeviceRGB);
662
663     for (Vector<IntRect>::const_iterator i = tickmarks.begin(); i != tickmarks.end(); ++i) {
664         // Calculate how far down (in %) the tick-mark should appear.
665         const float percent = static_cast<float>(i->y()) / scrollbar->totalSize();
666         if (percent < 0.0 || percent > 1.0)
667             continue;
668
669         // Calculate how far down (in pixels) the tick-mark should appear.
670         const int yPos = rect.y() + (rect.height() * percent);
671
672         // Paint.
673         FloatRect tickRect(rect.x(), yPos, rect.width(), 2);
674         context->fillRect(tickRect);
675         context->strokeRect(tickRect, 1);
676     }
677 }
678
679 void ScrollbarThemeChromiumMac::paintOverhangAreas(ScrollView* view, GraphicsContext* context, const IntRect& horizontalOverhangRect, const IntRect& verticalOverhangRect, const IntRect& dirtyRect)
680 {
681     // The extent of each shadow in pixels.
682     const int kShadowSize = 4;
683     // Offset of negative one pixel to make the gradient blend with the toolbar's bottom border.
684     const int kToolbarShadowOffset = -1;
685     const struct {
686         float stop;
687         Color color;
688     } kShadowColors[] = {
689         { 0.000, Color(0, 0, 0, 255) },
690         { 0.125, Color(0, 0, 0, 57) },
691         { 0.375, Color(0, 0, 0, 41) },
692         { 0.625, Color(0, 0, 0, 18) },
693         { 0.875, Color(0, 0, 0, 6) },
694         { 1.000, Color(0, 0, 0, 0) }
695     };
696     const unsigned kNumShadowColors = sizeof(kShadowColors)/sizeof(kShadowColors[0]);
697
698     const bool hasHorizontalOverhang = !horizontalOverhangRect.isEmpty();
699     const bool hasVerticalOverhang = !verticalOverhangRect.isEmpty();
700     // Prefer non-additive shadows, but degrade to additive shadows if there is vertical overhang.
701     const bool useAdditiveShadows = hasVerticalOverhang;
702
703     GraphicsContextStateSaver stateSaver(*context);
704
705     context->setFillPattern(m_overhangPattern);
706     if (hasHorizontalOverhang)
707         context->fillRect(intersection(horizontalOverhangRect, dirtyRect));
708     if (hasVerticalOverhang)
709         context->fillRect(intersection(verticalOverhangRect, dirtyRect));
710
711     IntSize scrollOffset = view->scrollOffset();
712     FloatPoint shadowCornerOrigin;
713     FloatPoint shadowCornerOffset;
714
715     // Draw the shadow for the horizontal overhang.
716     if (hasHorizontalOverhang) {
717         int toolbarShadowHeight = kShadowSize;
718         RefPtr<Gradient> gradient;
719         IntRect shadowRect = horizontalOverhangRect;
720         shadowRect.setHeight(kShadowSize);
721         if (scrollOffset.height() < 0) {
722             if (useAdditiveShadows) {
723                 toolbarShadowHeight = std::min(horizontalOverhangRect.height(), kShadowSize);
724             } else if (horizontalOverhangRect.height() < 2 * kShadowSize + kToolbarShadowOffset) {
725                 // Split the overhang area between the web content shadow and toolbar shadow if it's too small.
726                 shadowRect.setHeight((horizontalOverhangRect.height() + 1) / 2);
727                 toolbarShadowHeight = horizontalOverhangRect.height() - shadowRect.height() - kToolbarShadowOffset;
728             }
729             shadowRect.setY(horizontalOverhangRect.maxY() - shadowRect.height());
730             gradient = Gradient::create(FloatPoint(0, shadowRect.maxY()), FloatPoint(0, shadowRect.maxY() - kShadowSize));
731             shadowCornerOrigin.setY(shadowRect.maxY());
732             shadowCornerOffset.setY(-kShadowSize);
733         } else {
734             gradient = Gradient::create(FloatPoint(0, shadowRect.y()), FloatPoint(0, shadowRect.maxY()));
735             shadowCornerOrigin.setY(shadowRect.y());
736         }
737         if (hasVerticalOverhang) {
738             shadowRect.setWidth(shadowRect.width() - verticalOverhangRect.width());
739             if (scrollOffset.width() < 0) {
740                 shadowRect.setX(shadowRect.x() + verticalOverhangRect.width());
741                 shadowCornerOrigin.setX(shadowRect.x());
742                 shadowCornerOffset.setX(-kShadowSize);
743             } else {
744                 shadowCornerOrigin.setX(shadowRect.maxX());
745             }
746         }
747         for (unsigned i = 0; i < kNumShadowColors; i++)
748             gradient->addColorStop(kShadowColors[i].stop, kShadowColors[i].color);
749         context->setFillGradient(gradient);
750         context->fillRect(intersection(shadowRect, dirtyRect));
751
752         // Draw a drop-shadow from the toolbar.
753         if (scrollOffset.height() < 0) {
754             shadowRect.setY(kToolbarShadowOffset);
755             shadowRect.setHeight(toolbarShadowHeight);
756             gradient = Gradient::create(FloatPoint(0, shadowRect.y()), FloatPoint(0, shadowRect.y() + kShadowSize));
757             for (unsigned i = 0; i < kNumShadowColors; i++)
758                 gradient->addColorStop(kShadowColors[i].stop, kShadowColors[i].color);
759             context->setFillGradient(gradient);
760             context->fillRect(intersection(shadowRect, dirtyRect));
761         }
762     }
763
764     // Draw the shadow for the vertical overhang.
765     if (hasVerticalOverhang) {
766         RefPtr<Gradient> gradient;
767         IntRect shadowRect = verticalOverhangRect;
768         shadowRect.setWidth(kShadowSize);
769         if (scrollOffset.width() < 0) {
770             shadowRect.setX(verticalOverhangRect.maxX() - shadowRect.width());
771             gradient = Gradient::create(FloatPoint(shadowRect.maxX(), 0), FloatPoint(shadowRect.x(), 0));
772         } else {
773             gradient = Gradient::create(FloatPoint(shadowRect.x(), 0), FloatPoint(shadowRect.maxX(), 0));
774         }
775         for (unsigned i = 0; i < kNumShadowColors; i++)
776             gradient->addColorStop(kShadowColors[i].stop, kShadowColors[i].color);
777         context->setFillGradient(gradient);
778         context->fillRect(intersection(shadowRect, dirtyRect));
779
780         // Draw a drop-shadow from the toolbar.
781         shadowRect = verticalOverhangRect;
782         shadowRect.setY(kToolbarShadowOffset);
783         shadowRect.setHeight(kShadowSize);
784         gradient = Gradient::create(FloatPoint(0, shadowRect.y()), FloatPoint(0, shadowRect.maxY()));
785         for (unsigned i = 0; i < kNumShadowColors; i++)
786             gradient->addColorStop(kShadowColors[i].stop, kShadowColors[i].color);
787         context->setFillGradient(gradient);
788         context->fillRect(intersection(shadowRect, dirtyRect));
789     }
790
791     // If both rectangles present, draw a radial gradient for the corner.
792     if (hasHorizontalOverhang && hasVerticalOverhang) {
793         RefPtr<Gradient> gradient = Gradient::create(shadowCornerOrigin, 0, shadowCornerOrigin, kShadowSize);
794         for (unsigned i = 0; i < kNumShadowColors; i++)
795             gradient->addColorStop(kShadowColors[i].stop, kShadowColors[i].color);
796         context->setFillGradient(gradient);
797         context->fillRect(FloatRect(shadowCornerOrigin.x() + shadowCornerOffset.x(), shadowCornerOrigin.y() + shadowCornerOffset.y(), kShadowSize, kShadowSize));
798     }
799 }
800
801
802 }