1 // Copyright 2013 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "content/shell/renderer/test_runner/WebTestThemeEngineMac.h" 6 7 #import <AppKit/NSAffineTransform.h> 8 #import <AppKit/NSGraphicsContext.h> 9 #import <AppKit/NSScroller.h> 10 #import <AppKit/NSWindow.h> 11 #include <Carbon/Carbon.h> 12 #include "skia/ext/skia_utils_mac.h" 13 #include "third_party/WebKit/public/platform/WebCanvas.h" 14 #include "third_party/WebKit/public/platform/WebRect.h" 15 16 using blink::WebCanvas; 17 using blink::WebRect; 18 using blink::WebThemeEngine; 19 20 // We can't directly tell the NSScroller to draw itself as active or inactive, 21 // instead we have to make it a child of an (in)active window. This class lets 22 // us fake that parent window. 23 @interface FakeActiveWindow : NSWindow { 24 @private 25 BOOL hasActiveControls; 26 } 27 + (NSWindow*)alwaysActiveWindow; 28 + (NSWindow*)alwaysInactiveWindow; 29 - (id)initWithActiveControls:(BOOL)_hasActiveControls; 30 - (BOOL)_hasActiveControls; 31 @end 32 33 @implementation FakeActiveWindow 34 35 static NSWindow* alwaysActiveWindow = nil; 36 static NSWindow* alwaysInactiveWindow = nil; 37 38 + (NSWindow*)alwaysActiveWindow 39 { 40 if (alwaysActiveWindow == nil) 41 alwaysActiveWindow = [[self alloc] initWithActiveControls:YES]; 42 return alwaysActiveWindow; 43 } 44 45 + (NSWindow*)alwaysInactiveWindow 46 { 47 if (alwaysInactiveWindow == nil) 48 alwaysInactiveWindow = [[self alloc] initWithActiveControls:NO]; 49 return alwaysInactiveWindow; 50 } 51 52 - (id)initWithActiveControls:(BOOL)_hasActiveControls 53 { 54 if ((self = [super initWithContentRect:NSMakeRect(0, 0, 100, 100) 55 styleMask:0 56 backing:NSBackingStoreBuffered 57 defer:YES])) { 58 hasActiveControls = _hasActiveControls; 59 } 60 return self; 61 } 62 63 - (BOOL)_hasActiveControls 64 { 65 return hasActiveControls; 66 } 67 68 @end 69 70 namespace content { 71 72 namespace { 73 74 ThemeTrackEnableState stateToHIEnableState(WebThemeEngine::State state) 75 { 76 switch (state) { 77 case WebThemeEngine::StateDisabled: 78 return kThemeTrackDisabled; 79 case WebThemeEngine::StateInactive: 80 return kThemeTrackInactive; 81 default: 82 return kThemeTrackActive; 83 } 84 } 85 86 } // namespace 87 88 void WebTestThemeEngineMac::paintScrollbarThumb( 89 WebCanvas* canvas, 90 WebThemeEngine::State state, 91 WebThemeEngine::Size size, 92 const WebRect& rect, 93 const WebThemeEngine::ScrollbarInfo& scrollbarInfo) 94 { 95 // To match the Mac port, we still use HITheme for inner scrollbars. 96 if (scrollbarInfo.parent == WebThemeEngine::ScrollbarParentRenderLayer) 97 paintHIThemeScrollbarThumb(canvas, state, size, rect, scrollbarInfo); 98 else 99 paintNSScrollerScrollbarThumb(canvas, state, size, rect, scrollbarInfo); 100 } 101 102 // Duplicated from webkit/glue/webthemeengine_impl_mac.cc in the downstream 103 // Chromium WebThemeEngine implementation. 104 void WebTestThemeEngineMac::paintHIThemeScrollbarThumb( 105 WebCanvas* canvas, 106 WebThemeEngine::State state, 107 WebThemeEngine::Size size, 108 const WebRect& rect, 109 const WebThemeEngine::ScrollbarInfo& scrollbarInfo) 110 { 111 HIThemeTrackDrawInfo trackInfo; 112 trackInfo.version = 0; 113 trackInfo.kind = size == WebThemeEngine::SizeRegular ? kThemeMediumScrollBar : kThemeSmallScrollBar; 114 trackInfo.bounds = CGRectMake(rect.x, rect.y, rect.width, rect.height); 115 trackInfo.min = 0; 116 trackInfo.max = scrollbarInfo.maxValue; 117 trackInfo.value = scrollbarInfo.currentValue; 118 trackInfo.trackInfo.scrollbar.viewsize = scrollbarInfo.visibleSize; 119 trackInfo.attributes = 0; 120 if (scrollbarInfo.orientation == WebThemeEngine::ScrollbarOrientationHorizontal) 121 trackInfo.attributes |= kThemeTrackHorizontal; 122 123 trackInfo.enableState = stateToHIEnableState(state); 124 125 trackInfo.trackInfo.scrollbar.pressState = 126 state == WebThemeEngine::StatePressed ? kThemeThumbPressed : 0; 127 trackInfo.attributes |= (kThemeTrackShowThumb | kThemeTrackHideTrack); 128 gfx::SkiaBitLocker bitLocker(canvas); 129 CGContextRef cgContext = bitLocker.cgContext(); 130 HIThemeDrawTrack(&trackInfo, 0, cgContext, kHIThemeOrientationNormal); 131 } 132 133 void WebTestThemeEngineMac::paintNSScrollerScrollbarThumb( 134 WebCanvas* canvas, 135 WebThemeEngine::State state, 136 WebThemeEngine::Size size, 137 const WebRect& rect, 138 const WebThemeEngine::ScrollbarInfo& scrollbarInfo) 139 { 140 [NSGraphicsContext saveGraphicsState]; 141 NSScroller* scroller = [[NSScroller alloc] initWithFrame:NSMakeRect(rect.x, rect.y, rect.width, rect.height)]; 142 [scroller setEnabled:state != WebThemeEngine::StateDisabled]; 143 if (state == WebThemeEngine::StateInactive) 144 [[[FakeActiveWindow alwaysInactiveWindow] contentView] addSubview:scroller]; 145 else 146 [[[FakeActiveWindow alwaysActiveWindow] contentView] addSubview:scroller]; 147 148 [scroller setControlSize:size == WebThemeEngine::SizeRegular ? NSRegularControlSize : NSSmallControlSize]; 149 150 double value = double(scrollbarInfo.currentValue) / double(scrollbarInfo.maxValue); 151 [scroller setDoubleValue: value]; 152 153 float knobProportion = float(scrollbarInfo.visibleSize) / float(scrollbarInfo.totalSize); 154 [scroller setKnobProportion: knobProportion]; 155 156 gfx::SkiaBitLocker bitLocker(canvas); 157 CGContextRef cgContext = bitLocker.cgContext(); 158 NSGraphicsContext* nsGraphicsContext = [NSGraphicsContext graphicsContextWithGraphicsPort:cgContext flipped:YES]; 159 [NSGraphicsContext setCurrentContext:nsGraphicsContext]; 160 161 // Despite passing in frameRect() to the scroller, it always draws at (0, 0). 162 // Force it to draw in the right location by translating the whole graphics 163 // context. 164 CGContextSaveGState(cgContext); 165 NSAffineTransform *transform = [NSAffineTransform transform]; 166 [transform translateXBy:rect.x yBy:rect.y]; 167 [transform concat]; 168 169 [scroller drawKnob]; 170 CGContextRestoreGState(cgContext); 171 172 [scroller release]; 173 174 [NSGraphicsContext restoreGraphicsState]; 175 } 176 177 } // namespace content 178