1/* 2 * Copyright (C) 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. 3 * Copyright (C) 2008, 2009 Google, Inc. 4 * 5 * This library is free software; you can redistribute it and/or 6 * modify it under the terms of the GNU Library General Public 7 * License as published by the Free Software Foundation; either 8 * version 2 of the License, or (at your option) any later version. 9 * 10 * This library is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 * Library General Public License for more details. 14 * 15 * You should have received a copy of the GNU Library General Public License 16 * along with this library; see the file COPYING.LIB. If not, write to 17 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 18 * Boston, MA 02110-1301, USA. 19 */ 20 21#import "config.h" 22#import "RenderThemeChromiumMac.h" 23 24#import "BitmapImage.h" 25#import "ChromiumBridge.h" 26#import "ColorMac.h" 27#import "CSSStyleSelector.h" 28#import "CSSValueKeywords.h" 29#import "Document.h" 30#import "Element.h" 31#import "FrameView.h" 32#import "GraphicsContext.h" 33#import "HTMLInputElement.h" 34#import "HTMLMediaElement.h" 35#import "HTMLNames.h" 36#import "Image.h" 37#import "LocalCurrentGraphicsContext.h" 38#import "MediaControlElements.h" 39#import "RenderMedia.h" 40#import "RenderMediaControlsChromium.h" 41#import "RenderSlider.h" 42#import "RenderView.h" 43#import "SharedBuffer.h" 44#import "TimeRanges.h" 45#import "WebCoreSystemInterface.h" 46#import "UserAgentStyleSheets.h" 47#import <Carbon/Carbon.h> 48#import <Cocoa/Cocoa.h> 49#import <wtf/RetainPtr.h> 50#import <wtf/StdLibExtras.h> 51#import <math.h> 52 53#ifdef BUILDING_ON_TIGER 54typedef int NSInteger; 55typedef unsigned NSUInteger; 56#endif 57 58using std::min; 59 60// This file (and its associated .h file) is a clone of RenderThemeMac.mm. 61// Because the original file is designed to run in-process inside a Cocoa view, 62// we must maintain a fork. Please maintain this file by performing parallel 63// changes to it. 64// 65// The only changes from RenderThemeMac should be: 66// - The classname change from RenderThemeMac to RenderThemeChromiumMac. 67// - The introduction of RTCMFlippedView and FlippedView() and its use as the 68// parent view for cell rendering. 69// - In platformFocusRingColor(), the use of ChromiumBridge to determine if 70// we're in layout test mode. 71// - updateActiveState() and its use to update the cells' visual appearance. 72// - All the paintMedia*() functions and extraMediaControlsStyleSheet() 73// are forked from RenderThemeChromiumSkia instead of RenderThemeMac. 74// 75// For all other differences, if it was introduced in this file, then the 76// maintainer forgot to include it in the list; otherwise it is an update that 77// should have been applied to this file but was not. 78 79// The methods in this file are specific to the Mac OS X platform. 80 81// FIXME: The platform-independent code in this class should be factored out and merged with RenderThemeSafari. 82 83@interface WebCoreRenderThemeNotificationObserver : NSObject 84{ 85 WebCore::RenderTheme *_theme; 86} 87 88- (id)initWithTheme:(WebCore::RenderTheme *)theme; 89- (void)systemColorsDidChange:(NSNotification *)notification; 90 91@end 92 93@implementation WebCoreRenderThemeNotificationObserver 94 95- (id)initWithTheme:(WebCore::RenderTheme *)theme 96{ 97 [super init]; 98 _theme = theme; 99 100 return self; 101} 102 103- (void)systemColorsDidChange:(NSNotification *)unusedNotification 104{ 105 ASSERT_UNUSED(unusedNotification, [[unusedNotification name] isEqualToString:NSSystemColorsDidChangeNotification]); 106 _theme->platformColorsDidChange(); 107} 108 109@end 110 111@interface RTCMFlippedView : NSView 112{} 113 114- (BOOL)isFlipped; 115- (NSText *)currentEditor; 116 117@end 118 119@implementation RTCMFlippedView 120 121- (BOOL)isFlipped { 122 return [[NSGraphicsContext currentContext] isFlipped]; 123} 124 125- (NSText *)currentEditor { 126 return nil; 127} 128 129@end 130 131namespace WebCore { 132 133using namespace HTMLNames; 134 135enum { 136 topMargin, 137 rightMargin, 138 bottomMargin, 139 leftMargin 140}; 141 142enum { 143 topPadding, 144 rightPadding, 145 bottomPadding, 146 leftPadding 147}; 148 149// In Snow Leopard, many cells only check to see if the view they're passed is 150// flipped, and if a nil view is passed, neglect to check if the current 151// graphics context is flipped. Thus we pass a sham view to them, one whose 152// flipped state just reflects the state of the context. 153NSView* FlippedView() 154{ 155 static NSView* view = [[RTCMFlippedView alloc] init]; 156 return view; 157} 158 159PassRefPtr<RenderTheme> RenderTheme::themeForPage(Page*) 160{ 161 static RenderTheme* rt = RenderThemeChromiumMac::create().releaseRef(); 162 return rt; 163} 164 165PassRefPtr<RenderTheme> RenderThemeChromiumMac::create() 166{ 167 return adoptRef(new RenderThemeChromiumMac); 168} 169 170RenderThemeChromiumMac::RenderThemeChromiumMac() 171 : m_isSliderThumbHorizontalPressed(false) 172 , m_isSliderThumbVerticalPressed(false) 173 , m_notificationObserver(AdoptNS, [[WebCoreRenderThemeNotificationObserver alloc] initWithTheme:this]) 174{ 175 [[NSNotificationCenter defaultCenter] addObserver:m_notificationObserver.get() 176 selector:@selector(systemColorsDidChange:) 177 name:NSSystemColorsDidChangeNotification 178 object:nil]; 179} 180 181RenderThemeChromiumMac::~RenderThemeChromiumMac() 182{ 183 [[NSNotificationCenter defaultCenter] removeObserver:m_notificationObserver.get()]; 184} 185 186Color RenderThemeChromiumMac::platformActiveSelectionBackgroundColor() const 187{ 188 NSColor* color = [[NSColor selectedTextBackgroundColor] colorUsingColorSpaceName:NSDeviceRGBColorSpace]; 189 return Color(static_cast<int>(255.0 * [color redComponent]), static_cast<int>(255.0 * [color greenComponent]), static_cast<int>(255.0 * [color blueComponent])); 190} 191 192Color RenderThemeChromiumMac::platformInactiveSelectionBackgroundColor() const 193{ 194 NSColor* color = [[NSColor secondarySelectedControlColor] colorUsingColorSpaceName:NSDeviceRGBColorSpace]; 195 return Color(static_cast<int>(255.0 * [color redComponent]), static_cast<int>(255.0 * [color greenComponent]), static_cast<int>(255.0 * [color blueComponent])); 196} 197 198Color RenderThemeChromiumMac::platformActiveListBoxSelectionBackgroundColor() const 199{ 200 NSColor* color = [[NSColor alternateSelectedControlColor] colorUsingColorSpaceName:NSDeviceRGBColorSpace]; 201 return Color(static_cast<int>(255.0 * [color redComponent]), static_cast<int>(255.0 * [color greenComponent]), static_cast<int>(255.0 * [color blueComponent])); 202} 203 204Color RenderThemeChromiumMac::platformActiveListBoxSelectionForegroundColor() const 205{ 206 return Color::white; 207} 208 209Color RenderThemeChromiumMac::platformInactiveListBoxSelectionForegroundColor() const 210{ 211 return Color::black; 212} 213 214Color RenderThemeChromiumMac::platformFocusRingColor() const 215{ 216 if (ChromiumBridge::layoutTestMode()) 217 return oldAquaFocusRingColor(); 218 219 return systemColor(CSSValueWebkitFocusRingColor); 220} 221 222Color RenderThemeChromiumMac::platformInactiveListBoxSelectionBackgroundColor() const 223{ 224 return platformInactiveSelectionBackgroundColor(); 225} 226 227static FontWeight toFontWeight(NSInteger appKitFontWeight) 228{ 229 ASSERT(appKitFontWeight > 0 && appKitFontWeight < 15); 230 if (appKitFontWeight > 14) 231 appKitFontWeight = 14; 232 else if (appKitFontWeight < 1) 233 appKitFontWeight = 1; 234 235 static FontWeight fontWeights[] = { 236 FontWeight100, 237 FontWeight100, 238 FontWeight200, 239 FontWeight300, 240 FontWeight400, 241 FontWeight500, 242 FontWeight600, 243 FontWeight600, 244 FontWeight700, 245 FontWeight800, 246 FontWeight800, 247 FontWeight900, 248 FontWeight900, 249 FontWeight900 250 }; 251 return fontWeights[appKitFontWeight - 1]; 252} 253 254void RenderThemeChromiumMac::systemFont(int cssValueId, FontDescription& fontDescription) const 255{ 256 DEFINE_STATIC_LOCAL(FontDescription, systemFont, ()); 257 DEFINE_STATIC_LOCAL(FontDescription, smallSystemFont, ()); 258 DEFINE_STATIC_LOCAL(FontDescription, menuFont, ()); 259 DEFINE_STATIC_LOCAL(FontDescription, labelFont, ()); 260 DEFINE_STATIC_LOCAL(FontDescription, miniControlFont, ()); 261 DEFINE_STATIC_LOCAL(FontDescription, smallControlFont, ()); 262 DEFINE_STATIC_LOCAL(FontDescription, controlFont, ()); 263 264 FontDescription* cachedDesc; 265 NSFont* font = nil; 266 switch (cssValueId) { 267 case CSSValueSmallCaption: 268 cachedDesc = &smallSystemFont; 269 if (!smallSystemFont.isAbsoluteSize()) 270 font = [NSFont systemFontOfSize:[NSFont smallSystemFontSize]]; 271 break; 272 case CSSValueMenu: 273 cachedDesc = &menuFont; 274 if (!menuFont.isAbsoluteSize()) 275 font = [NSFont menuFontOfSize:[NSFont systemFontSize]]; 276 break; 277 case CSSValueStatusBar: 278 cachedDesc = &labelFont; 279 if (!labelFont.isAbsoluteSize()) 280 font = [NSFont labelFontOfSize:[NSFont labelFontSize]]; 281 break; 282 case CSSValueWebkitMiniControl: 283 cachedDesc = &miniControlFont; 284 if (!miniControlFont.isAbsoluteSize()) 285 font = [NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:NSMiniControlSize]]; 286 break; 287 case CSSValueWebkitSmallControl: 288 cachedDesc = &smallControlFont; 289 if (!smallControlFont.isAbsoluteSize()) 290 font = [NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:NSSmallControlSize]]; 291 break; 292 case CSSValueWebkitControl: 293 cachedDesc = &controlFont; 294 if (!controlFont.isAbsoluteSize()) 295 font = [NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:NSRegularControlSize]]; 296 break; 297 default: 298 cachedDesc = &systemFont; 299 if (!systemFont.isAbsoluteSize()) 300 font = [NSFont systemFontOfSize:[NSFont systemFontSize]]; 301 } 302 303 if (font) { 304 NSFontManager *fontManager = [NSFontManager sharedFontManager]; 305 cachedDesc->setIsAbsoluteSize(true); 306 cachedDesc->setGenericFamily(FontDescription::NoFamily); 307 cachedDesc->firstFamily().setFamily([font familyName]); 308 cachedDesc->setSpecifiedSize([font pointSize]); 309 cachedDesc->setWeight(toFontWeight([fontManager weightOfFont:font])); 310 cachedDesc->setItalic([fontManager traitsOfFont:font] & NSItalicFontMask); 311 } 312 fontDescription = *cachedDesc; 313} 314 315static RGBA32 convertNSColorToColor(NSColor *color) 316{ 317 NSColor *colorInColorSpace = [color colorUsingColorSpaceName:NSDeviceRGBColorSpace]; 318 if (colorInColorSpace) { 319 static const double scaleFactor = nextafter(256.0, 0.0); 320 return makeRGB(static_cast<int>(scaleFactor * [colorInColorSpace redComponent]), 321 static_cast<int>(scaleFactor * [colorInColorSpace greenComponent]), 322 static_cast<int>(scaleFactor * [colorInColorSpace blueComponent])); 323 } 324 325 // This conversion above can fail if the NSColor in question is an NSPatternColor 326 // (as many system colors are). These colors are actually a repeating pattern 327 // not just a solid color. To work around this we simply draw a 1x1 image of 328 // the color and use that pixel's color. It might be better to use an average of 329 // the colors in the pattern instead. 330 NSBitmapImageRep *offscreenRep = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:nil 331 pixelsWide:1 332 pixelsHigh:1 333 bitsPerSample:8 334 samplesPerPixel:4 335 hasAlpha:YES 336 isPlanar:NO 337 colorSpaceName:NSDeviceRGBColorSpace 338 bytesPerRow:4 339 bitsPerPixel:32]; 340 341 [NSGraphicsContext saveGraphicsState]; 342 [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithBitmapImageRep:offscreenRep]]; 343 NSEraseRect(NSMakeRect(0, 0, 1, 1)); 344 [color drawSwatchInRect:NSMakeRect(0, 0, 1, 1)]; 345 [NSGraphicsContext restoreGraphicsState]; 346 347 NSUInteger pixel[4]; 348 [offscreenRep getPixel:pixel atX:0 y:0]; 349 350 [offscreenRep release]; 351 352 return makeRGB(pixel[0], pixel[1], pixel[2]); 353} 354 355static RGBA32 menuBackgroundColor() 356{ 357 NSBitmapImageRep *offscreenRep = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:nil 358 pixelsWide:1 359 pixelsHigh:1 360 bitsPerSample:8 361 samplesPerPixel:4 362 hasAlpha:YES 363 isPlanar:NO 364 colorSpaceName:NSDeviceRGBColorSpace 365 bytesPerRow:4 366 bitsPerPixel:32]; 367 368 CGContextRef context = static_cast<CGContextRef>([[NSGraphicsContext graphicsContextWithBitmapImageRep:offscreenRep] graphicsPort]); 369 CGRect rect = CGRectMake(0, 0, 1, 1); 370 HIThemeMenuDrawInfo drawInfo; 371 drawInfo.version = 0; 372 drawInfo.menuType = kThemeMenuTypePopUp; 373 HIThemeDrawMenuBackground(&rect, &drawInfo, context, kHIThemeOrientationInverted); 374 375 NSUInteger pixel[4]; 376 [offscreenRep getPixel:pixel atX:0 y:0]; 377 378 [offscreenRep release]; 379 380 return makeRGB(pixel[0], pixel[1], pixel[2]); 381} 382 383void RenderThemeChromiumMac::platformColorsDidChange() 384{ 385 m_systemColorCache.clear(); 386 RenderTheme::platformColorsDidChange(); 387} 388 389Color RenderThemeChromiumMac::systemColor(int cssValueId) const 390{ 391 if (m_systemColorCache.contains(cssValueId)) 392 return m_systemColorCache.get(cssValueId); 393 394 Color color; 395 switch (cssValueId) { 396 case CSSValueActiveborder: 397 color = convertNSColorToColor([NSColor keyboardFocusIndicatorColor]); 398 break; 399 case CSSValueActivecaption: 400 color = convertNSColorToColor([NSColor windowFrameTextColor]); 401 break; 402 case CSSValueAppworkspace: 403 color = convertNSColorToColor([NSColor headerColor]); 404 break; 405 case CSSValueBackground: 406 // Use theme independent default 407 break; 408 case CSSValueButtonface: 409 // We use this value instead of NSColor's controlColor to avoid website incompatibilities. 410 // We may want to change this to use the NSColor in future. 411 color = 0xFFC0C0C0; 412 break; 413 case CSSValueButtonhighlight: 414 color = convertNSColorToColor([NSColor controlHighlightColor]); 415 break; 416 case CSSValueButtonshadow: 417 color = convertNSColorToColor([NSColor controlShadowColor]); 418 break; 419 case CSSValueButtontext: 420 color = convertNSColorToColor([NSColor controlTextColor]); 421 break; 422 case CSSValueCaptiontext: 423 color = convertNSColorToColor([NSColor textColor]); 424 break; 425 case CSSValueGraytext: 426 color = convertNSColorToColor([NSColor disabledControlTextColor]); 427 break; 428 case CSSValueHighlight: 429 color = convertNSColorToColor([NSColor selectedTextBackgroundColor]); 430 break; 431 case CSSValueHighlighttext: 432 color = convertNSColorToColor([NSColor selectedTextColor]); 433 break; 434 case CSSValueInactiveborder: 435 color = convertNSColorToColor([NSColor controlBackgroundColor]); 436 break; 437 case CSSValueInactivecaption: 438 color = convertNSColorToColor([NSColor controlBackgroundColor]); 439 break; 440 case CSSValueInactivecaptiontext: 441 color = convertNSColorToColor([NSColor textColor]); 442 break; 443 case CSSValueInfobackground: 444 // There is no corresponding NSColor for this so we use a hard coded value. 445 color = 0xFFFBFCC5; 446 break; 447 case CSSValueInfotext: 448 color = convertNSColorToColor([NSColor textColor]); 449 break; 450 case CSSValueMenu: 451 color = menuBackgroundColor(); 452 break; 453 case CSSValueMenutext: 454 color = convertNSColorToColor([NSColor selectedMenuItemTextColor]); 455 break; 456 case CSSValueScrollbar: 457 color = convertNSColorToColor([NSColor scrollBarColor]); 458 break; 459 case CSSValueText: 460 color = convertNSColorToColor([NSColor textColor]); 461 break; 462 case CSSValueThreeddarkshadow: 463 color = convertNSColorToColor([NSColor controlDarkShadowColor]); 464 break; 465 case CSSValueThreedshadow: 466 color = convertNSColorToColor([NSColor shadowColor]); 467 break; 468 case CSSValueThreedface: 469 // We use this value instead of NSColor's controlColor to avoid website incompatibilities. 470 // We may want to change this to use the NSColor in future. 471 color = 0xFFC0C0C0; 472 break; 473 case CSSValueThreedhighlight: 474 color = convertNSColorToColor([NSColor highlightColor]); 475 break; 476 case CSSValueThreedlightshadow: 477 color = convertNSColorToColor([NSColor controlLightHighlightColor]); 478 break; 479 case CSSValueWebkitFocusRingColor: 480 color = convertNSColorToColor([NSColor keyboardFocusIndicatorColor]); 481 break; 482 case CSSValueWindow: 483 color = convertNSColorToColor([NSColor windowBackgroundColor]); 484 break; 485 case CSSValueWindowframe: 486 color = convertNSColorToColor([NSColor windowFrameColor]); 487 break; 488 case CSSValueWindowtext: 489 color = convertNSColorToColor([NSColor windowFrameTextColor]); 490 break; 491 } 492 493 if (!color.isValid()) 494 color = RenderTheme::systemColor(cssValueId); 495 496 if (color.isValid()) 497 m_systemColorCache.set(cssValueId, color.rgb()); 498 499 return color; 500} 501 502bool RenderThemeChromiumMac::isControlStyled(const RenderStyle* style, const BorderData& border, 503 const FillLayer& background, const Color& backgroundColor) const 504{ 505 if (style->appearance() == TextFieldPart || style->appearance() == TextAreaPart || style->appearance() == ListboxPart) 506 return style->border() != border; 507 508 // FIXME: This is horrible, but there is not much else that can be done. Menu lists cannot draw properly when 509 // scaled. They can't really draw properly when transformed either. We can't detect the transform case at style 510 // adjustment time so that will just have to stay broken. We can however detect that we're zooming. If zooming 511 // is in effect we treat it like the control is styled. 512 if (style->appearance() == MenulistPart && style->effectiveZoom() != 1.0f) 513 return true; 514 515 return RenderTheme::isControlStyled(style, border, background, backgroundColor); 516} 517 518void RenderThemeChromiumMac::adjustRepaintRect(const RenderObject* o, IntRect& r) 519{ 520 ControlPart part = o->style()->appearance(); 521 522#if USE(NEW_THEME) 523 switch (part) { 524 case CheckboxPart: 525 case RadioPart: 526 case PushButtonPart: 527 case SquareButtonPart: 528 case ListButtonPart: 529 case DefaultButtonPart: 530 case ButtonPart: 531 return RenderTheme::adjustRepaintRect(o, r); 532 default: 533 break; 534 } 535#endif 536 537 float zoomLevel = o->style()->effectiveZoom(); 538 539 if (part == MenulistPart) { 540 setPopupButtonCellState(o, r); 541 IntSize size = popupButtonSizes()[[popupButton() controlSize]]; 542 size.setHeight(size.height() * zoomLevel); 543 size.setWidth(r.width()); 544 r = inflateRect(r, size, popupButtonMargins(), zoomLevel); 545 } 546} 547 548IntRect RenderThemeChromiumMac::inflateRect(const IntRect& r, const IntSize& size, const int* margins, float zoomLevel) const 549{ 550 // Only do the inflation if the available width/height are too small. Otherwise try to 551 // fit the glow/check space into the available box's width/height. 552 int widthDelta = r.width() - (size.width() + margins[leftMargin] * zoomLevel + margins[rightMargin] * zoomLevel); 553 int heightDelta = r.height() - (size.height() + margins[topMargin] * zoomLevel + margins[bottomMargin] * zoomLevel); 554 IntRect result(r); 555 if (widthDelta < 0) { 556 result.setX(result.x() - margins[leftMargin] * zoomLevel); 557 result.setWidth(result.width() - widthDelta); 558 } 559 if (heightDelta < 0) { 560 result.setY(result.y() - margins[topMargin] * zoomLevel); 561 result.setHeight(result.height() - heightDelta); 562 } 563 return result; 564} 565 566FloatRect RenderThemeChromiumMac::convertToPaintingRect(const RenderObject* inputRenderer, const RenderObject* partRenderer, const FloatRect& inputRect, const IntRect& r) const 567{ 568 FloatRect partRect(inputRect); 569 570 // Compute an offset between the part renderer and the input renderer 571 FloatSize offsetFromInputRenderer; 572 const RenderObject* renderer = partRenderer; 573 while (renderer && renderer != inputRenderer) { 574 RenderObject* containingRenderer = renderer->container(); 575 offsetFromInputRenderer -= renderer->offsetFromContainer(containingRenderer); 576 renderer = containingRenderer; 577 } 578 // If the input renderer was not a container, something went wrong 579 ASSERT(renderer == inputRenderer); 580 // Move the rect into partRenderer's coords 581 partRect.move(offsetFromInputRenderer); 582 // Account for the local drawing offset (tx, ty) 583 partRect.move(r.x(), r.y()); 584 585 return partRect; 586} 587 588// Updates the control tint (a.k.a. active state) of |cell| (from |o|). 589// In the Chromium port, the renderer runs as a background process and controls' 590// NSCell(s) lack a parent NSView. Therefore controls don't have their tint 591// color updated correctly when the application is activated/deactivated. 592// FocusController's setActive() is called when the application is 593// activated/deactivated, which causes a repaint at which time this code is 594// called. 595// This function should be called before drawing any NSCell-derived controls, 596// unless you're sure it isn't needed. 597void RenderThemeChromiumMac::updateActiveState(NSCell* cell, const RenderObject* o) 598{ 599 NSControlTint oldTint = [cell controlTint]; 600 NSControlTint tint = isActive(o) ? [NSColor currentControlTint] : 601 NSClearControlTint; 602 603 if (tint != oldTint) 604 [cell setControlTint:tint]; 605} 606 607void RenderThemeChromiumMac::updateCheckedState(NSCell* cell, const RenderObject* o) 608{ 609 bool oldIndeterminate = [cell state] == NSMixedState; 610 bool indeterminate = isIndeterminate(o); 611 bool checked = isChecked(o); 612 613 if (oldIndeterminate != indeterminate) { 614 [cell setState:indeterminate ? NSMixedState : (checked ? NSOnState : NSOffState)]; 615 return; 616 } 617 618 bool oldChecked = [cell state] == NSOnState; 619 if (checked != oldChecked) 620 [cell setState:checked ? NSOnState : NSOffState]; 621} 622 623void RenderThemeChromiumMac::updateEnabledState(NSCell* cell, const RenderObject* o) 624{ 625 bool oldEnabled = [cell isEnabled]; 626 bool enabled = isEnabled(o); 627 if (enabled != oldEnabled) 628 [cell setEnabled:enabled]; 629} 630 631void RenderThemeChromiumMac::updateFocusedState(NSCell* cell, const RenderObject* o) 632{ 633 bool oldFocused = [cell showsFirstResponder]; 634 bool focused = isFocused(o) && o->style()->outlineStyleIsAuto(); 635 if (focused != oldFocused) 636 [cell setShowsFirstResponder:focused]; 637} 638 639void RenderThemeChromiumMac::updatePressedState(NSCell* cell, const RenderObject* o) 640{ 641 bool oldPressed = [cell isHighlighted]; 642 bool pressed = (o->node() && o->node()->active()); 643 if (pressed != oldPressed) 644 [cell setHighlighted:pressed]; 645} 646 647bool RenderThemeChromiumMac::controlSupportsTints(const RenderObject* o) const 648{ 649 // An alternate way to implement this would be to get the appropriate cell object 650 // and call the private _needRedrawOnWindowChangedKeyState method. An advantage of 651 // that would be that we would match AppKit behavior more closely, but a disadvantage 652 // would be that we would rely on an AppKit SPI method. 653 654 if (!isEnabled(o)) 655 return false; 656 657 // Checkboxes only have tint when checked. 658 if (o->style()->appearance() == CheckboxPart) 659 return isChecked(o); 660 661 // For now assume other controls have tint if enabled. 662 return true; 663} 664 665NSControlSize RenderThemeChromiumMac::controlSizeForFont(RenderStyle* style) const 666{ 667 int fontSize = style->fontSize(); 668 if (fontSize >= 16) 669 return NSRegularControlSize; 670 if (fontSize >= 11) 671 return NSSmallControlSize; 672 return NSMiniControlSize; 673} 674 675void RenderThemeChromiumMac::setControlSize(NSCell* cell, const IntSize* sizes, const IntSize& minSize, float zoomLevel) 676{ 677 NSControlSize size; 678 if (minSize.width() >= static_cast<int>(sizes[NSRegularControlSize].width() * zoomLevel) && 679 minSize.height() >= static_cast<int>(sizes[NSRegularControlSize].height() * zoomLevel)) 680 size = NSRegularControlSize; 681 else if (minSize.width() >= static_cast<int>(sizes[NSSmallControlSize].width() * zoomLevel) && 682 minSize.height() >= static_cast<int>(sizes[NSSmallControlSize].height() * zoomLevel)) 683 size = NSSmallControlSize; 684 else 685 size = NSMiniControlSize; 686 if (size != [cell controlSize]) // Only update if we have to, since AppKit does work even if the size is the same. 687 [cell setControlSize:size]; 688} 689 690IntSize RenderThemeChromiumMac::sizeForFont(RenderStyle* style, const IntSize* sizes) const 691{ 692 if (style->effectiveZoom() != 1.0f) { 693 IntSize result = sizes[controlSizeForFont(style)]; 694 return IntSize(result.width() * style->effectiveZoom(), result.height() * style->effectiveZoom()); 695 } 696 return sizes[controlSizeForFont(style)]; 697} 698 699IntSize RenderThemeChromiumMac::sizeForSystemFont(RenderStyle* style, const IntSize* sizes) const 700{ 701 if (style->effectiveZoom() != 1.0f) { 702 IntSize result = sizes[controlSizeForSystemFont(style)]; 703 return IntSize(result.width() * style->effectiveZoom(), result.height() * style->effectiveZoom()); 704 } 705 return sizes[controlSizeForSystemFont(style)]; 706} 707 708void RenderThemeChromiumMac::setSizeFromFont(RenderStyle* style, const IntSize* sizes) const 709{ 710 // FIXME: Check is flawed, since it doesn't take min-width/max-width into account. 711 IntSize size = sizeForFont(style, sizes); 712 if (style->width().isIntrinsicOrAuto() && size.width() > 0) 713 style->setWidth(Length(size.width(), Fixed)); 714 if (style->height().isAuto() && size.height() > 0) 715 style->setHeight(Length(size.height(), Fixed)); 716} 717 718void RenderThemeChromiumMac::setFontFromControlSize(CSSStyleSelector*, RenderStyle* style, NSControlSize controlSize) const 719{ 720 FontDescription fontDescription; 721 fontDescription.setIsAbsoluteSize(true); 722 fontDescription.setGenericFamily(FontDescription::SerifFamily); 723 724 NSFont* font = [NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:controlSize]]; 725 fontDescription.firstFamily().setFamily([font familyName]); 726 fontDescription.setComputedSize([font pointSize] * style->effectiveZoom()); 727 fontDescription.setSpecifiedSize([font pointSize] * style->effectiveZoom()); 728 729 // Reset line height 730 style->setLineHeight(RenderStyle::initialLineHeight()); 731 732 if (style->setFontDescription(fontDescription)) 733 style->font().update(0); 734} 735 736NSControlSize RenderThemeChromiumMac::controlSizeForSystemFont(RenderStyle* style) const 737{ 738 int fontSize = style->fontSize(); 739 if (fontSize >= [NSFont systemFontSizeForControlSize:NSRegularControlSize]) 740 return NSRegularControlSize; 741 if (fontSize >= [NSFont systemFontSizeForControlSize:NSSmallControlSize]) 742 return NSSmallControlSize; 743 return NSMiniControlSize; 744} 745 746bool RenderThemeChromiumMac::paintTextField(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r) 747{ 748 LocalCurrentGraphicsContext localContext(paintInfo.context); 749 wkDrawBezeledTextFieldCell(r, isEnabled(o) && !isReadOnlyControl(o)); 750 return false; 751} 752 753void RenderThemeChromiumMac::adjustTextFieldStyle(CSSStyleSelector*, RenderStyle*, Element*) const 754{ 755} 756 757bool RenderThemeChromiumMac::paintCapsLockIndicator(RenderObject*, const RenderObject::PaintInfo& paintInfo, const IntRect& r) 758{ 759 if (paintInfo.context->paintingDisabled()) 760 return true; 761 762 LocalCurrentGraphicsContext localContext(paintInfo.context); 763 wkDrawCapsLockIndicator(paintInfo.context->platformContext(), r); 764 765 return false; 766} 767 768bool RenderThemeChromiumMac::paintTextArea(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r) 769{ 770 LocalCurrentGraphicsContext localContext(paintInfo.context); 771 wkDrawBezeledTextArea(r, isEnabled(o) && !isReadOnlyControl(o)); 772 return false; 773} 774 775void RenderThemeChromiumMac::adjustTextAreaStyle(CSSStyleSelector*, RenderStyle*, Element*) const 776{ 777} 778 779const int* RenderThemeChromiumMac::popupButtonMargins() const 780{ 781 static const int margins[3][4] = 782 { 783 { 0, 3, 1, 3 }, 784 { 0, 3, 2, 3 }, 785 { 0, 1, 0, 1 } 786 }; 787 return margins[[popupButton() controlSize]]; 788} 789 790const IntSize* RenderThemeChromiumMac::popupButtonSizes() const 791{ 792 static const IntSize sizes[3] = { IntSize(0, 21), IntSize(0, 18), IntSize(0, 15) }; 793 return sizes; 794} 795 796const int* RenderThemeChromiumMac::popupButtonPadding(NSControlSize size) const 797{ 798 static const int padding[3][4] = 799 { 800 { 2, 26, 3, 8 }, 801 { 2, 23, 3, 8 }, 802 { 2, 22, 3, 10 } 803 }; 804 return padding[size]; 805} 806 807bool RenderThemeChromiumMac::paintMenuList(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r) 808{ 809 setPopupButtonCellState(o, r); 810 811 NSPopUpButtonCell* popupButton = this->popupButton(); 812 813 float zoomLevel = o->style()->effectiveZoom(); 814 IntSize size = popupButtonSizes()[[popupButton controlSize]]; 815 size.setHeight(size.height() * zoomLevel); 816 size.setWidth(r.width()); 817 818 // Now inflate it to account for the shadow. 819 IntRect inflatedRect = r; 820 if (r.width() >= minimumMenuListSize(o->style())) 821 inflatedRect = inflateRect(inflatedRect, size, popupButtonMargins(), zoomLevel); 822 823 paintInfo.context->save(); 824 825#ifndef BUILDING_ON_TIGER 826 // On Leopard, the cell will draw outside of the given rect, so we have to clip to the rect 827 paintInfo.context->clip(inflatedRect); 828#endif 829 830 if (zoomLevel != 1.0f) { 831 inflatedRect.setWidth(inflatedRect.width() / zoomLevel); 832 inflatedRect.setHeight(inflatedRect.height() / zoomLevel); 833 paintInfo.context->translate(inflatedRect.x(), inflatedRect.y()); 834 paintInfo.context->scale(FloatSize(zoomLevel, zoomLevel)); 835 paintInfo.context->translate(-inflatedRect.x(), -inflatedRect.y()); 836 } 837 838 [popupButton drawWithFrame:inflatedRect inView:FlippedView()]; 839 [popupButton setControlView:nil]; 840 841 paintInfo.context->restore(); 842 843 return false; 844} 845 846const float baseFontSize = 11.0f; 847const float baseArrowHeight = 4.0f; 848const float baseArrowWidth = 5.0f; 849const float baseSpaceBetweenArrows = 2.0f; 850const int arrowPaddingLeft = 6; 851const int arrowPaddingRight = 6; 852const int paddingBeforeSeparator = 4; 853const int baseBorderRadius = 5; 854const int styledPopupPaddingLeft = 8; 855const int styledPopupPaddingTop = 1; 856const int styledPopupPaddingBottom = 2; 857 858static void TopGradientInterpolate(void*, const CGFloat* inData, CGFloat* outData) 859{ 860 static float dark[4] = { 1.0f, 1.0f, 1.0f, 0.4f }; 861 static float light[4] = { 1.0f, 1.0f, 1.0f, 0.15f }; 862 float a = inData[0]; 863 int i = 0; 864 for (i = 0; i < 4; i++) 865 outData[i] = (1.0f - a) * dark[i] + a * light[i]; 866} 867 868static void BottomGradientInterpolate(void*, const CGFloat* inData, CGFloat* outData) 869{ 870 static float dark[4] = { 1.0f, 1.0f, 1.0f, 0.0f }; 871 static float light[4] = { 1.0f, 1.0f, 1.0f, 0.3f }; 872 float a = inData[0]; 873 int i = 0; 874 for (i = 0; i < 4; i++) 875 outData[i] = (1.0f - a) * dark[i] + a * light[i]; 876} 877 878static void MainGradientInterpolate(void*, const CGFloat* inData, CGFloat* outData) 879{ 880 static float dark[4] = { 0.0f, 0.0f, 0.0f, 0.15f }; 881 static float light[4] = { 0.0f, 0.0f, 0.0f, 0.0f }; 882 float a = inData[0]; 883 int i = 0; 884 for (i = 0; i < 4; i++) 885 outData[i] = (1.0f - a) * dark[i] + a * light[i]; 886} 887 888static void TrackGradientInterpolate(void*, const CGFloat* inData, CGFloat* outData) 889{ 890 static float dark[4] = { 0.0f, 0.0f, 0.0f, 0.678f }; 891 static float light[4] = { 0.0f, 0.0f, 0.0f, 0.13f }; 892 float a = inData[0]; 893 int i = 0; 894 for (i = 0; i < 4; i++) 895 outData[i] = (1.0f - a) * dark[i] + a * light[i]; 896} 897 898void RenderThemeChromiumMac::paintMenuListButtonGradients(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r) 899{ 900 if (r.isEmpty()) 901 return; 902 903 CGContextRef context = paintInfo.context->platformContext(); 904 905 paintInfo.context->save(); 906 907 IntSize topLeftRadius; 908 IntSize topRightRadius; 909 IntSize bottomLeftRadius; 910 IntSize bottomRightRadius; 911 912 o->style()->getBorderRadiiForRect(r, topLeftRadius, topRightRadius, bottomLeftRadius, bottomRightRadius); 913 914 int radius = topLeftRadius.width(); 915 916 RetainPtr<CGColorSpaceRef> cspace(AdoptCF, CGColorSpaceCreateDeviceRGB()); 917 918 FloatRect topGradient(r.x(), r.y(), r.width(), r.height() / 2.0f); 919 struct CGFunctionCallbacks topCallbacks = { 0, TopGradientInterpolate, NULL }; 920 RetainPtr<CGFunctionRef> topFunction(AdoptCF, CGFunctionCreate(NULL, 1, NULL, 4, NULL, &topCallbacks)); 921 RetainPtr<CGShadingRef> topShading(AdoptCF, CGShadingCreateAxial(cspace.get(), CGPointMake(topGradient.x(), topGradient.y()), CGPointMake(topGradient.x(), topGradient.bottom()), topFunction.get(), false, false)); 922 923 FloatRect bottomGradient(r.x() + radius, r.y() + r.height() / 2.0f, r.width() - 2.0f * radius, r.height() / 2.0f); 924 struct CGFunctionCallbacks bottomCallbacks = { 0, BottomGradientInterpolate, NULL }; 925 RetainPtr<CGFunctionRef> bottomFunction(AdoptCF, CGFunctionCreate(NULL, 1, NULL, 4, NULL, &bottomCallbacks)); 926 RetainPtr<CGShadingRef> bottomShading(AdoptCF, CGShadingCreateAxial(cspace.get(), CGPointMake(bottomGradient.x(), bottomGradient.y()), CGPointMake(bottomGradient.x(), bottomGradient.bottom()), bottomFunction.get(), false, false)); 927 928 struct CGFunctionCallbacks mainCallbacks = { 0, MainGradientInterpolate, NULL }; 929 RetainPtr<CGFunctionRef> mainFunction(AdoptCF, CGFunctionCreate(NULL, 1, NULL, 4, NULL, &mainCallbacks)); 930 RetainPtr<CGShadingRef> mainShading(AdoptCF, CGShadingCreateAxial(cspace.get(), CGPointMake(r.x(), r.y()), CGPointMake(r.x(), r.bottom()), mainFunction.get(), false, false)); 931 932 RetainPtr<CGShadingRef> leftShading(AdoptCF, CGShadingCreateAxial(cspace.get(), CGPointMake(r.x(), r.y()), CGPointMake(r.x() + radius, r.y()), mainFunction.get(), false, false)); 933 934 RetainPtr<CGShadingRef> rightShading(AdoptCF, CGShadingCreateAxial(cspace.get(), CGPointMake(r.right(), r.y()), CGPointMake(r.right() - radius, r.y()), mainFunction.get(), false, false)); 935 paintInfo.context->save(); 936 CGContextClipToRect(context, r); 937 paintInfo.context->addRoundedRectClip(r, topLeftRadius, topRightRadius, bottomLeftRadius, bottomRightRadius); 938 CGContextDrawShading(context, mainShading.get()); 939 paintInfo.context->restore(); 940 941 paintInfo.context->save(); 942 CGContextClipToRect(context, topGradient); 943 paintInfo.context->addRoundedRectClip(enclosingIntRect(topGradient), topLeftRadius, topRightRadius, IntSize(), IntSize()); 944 CGContextDrawShading(context, topShading.get()); 945 paintInfo.context->restore(); 946 947 if (!bottomGradient.isEmpty()) { 948 paintInfo.context->save(); 949 CGContextClipToRect(context, bottomGradient); 950 paintInfo.context->addRoundedRectClip(enclosingIntRect(bottomGradient), IntSize(), IntSize(), bottomLeftRadius, bottomRightRadius); 951 CGContextDrawShading(context, bottomShading.get()); 952 paintInfo.context->restore(); 953 } 954 955 paintInfo.context->save(); 956 CGContextClipToRect(context, r); 957 paintInfo.context->addRoundedRectClip(r, topLeftRadius, topRightRadius, bottomLeftRadius, bottomRightRadius); 958 CGContextDrawShading(context, leftShading.get()); 959 CGContextDrawShading(context, rightShading.get()); 960 paintInfo.context->restore(); 961 962 paintInfo.context->restore(); 963} 964 965bool RenderThemeChromiumMac::paintMenuListButton(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r) 966{ 967 IntRect bounds = IntRect(r.x() + o->style()->borderLeftWidth(), 968 r.y() + o->style()->borderTopWidth(), 969 r.width() - o->style()->borderLeftWidth() - o->style()->borderRightWidth(), 970 r.height() - o->style()->borderTopWidth() - o->style()->borderBottomWidth()); 971 // Draw the gradients to give the styled popup menu a button appearance 972 paintMenuListButtonGradients(o, paintInfo, bounds); 973 974 // Since we actually know the size of the control here, we restrict the font scale to make sure the arrows will fit vertically in the bounds 975 float fontScale = min(o->style()->fontSize() / baseFontSize, bounds.height() / (baseArrowHeight * 2 + baseSpaceBetweenArrows)); 976 float centerY = bounds.y() + bounds.height() / 2.0f; 977 float arrowHeight = baseArrowHeight * fontScale; 978 float arrowWidth = baseArrowWidth * fontScale; 979 float leftEdge = bounds.right() - arrowPaddingRight * o->style()->effectiveZoom() - arrowWidth; 980 float spaceBetweenArrows = baseSpaceBetweenArrows * fontScale; 981 982 if (bounds.width() < arrowWidth + arrowPaddingLeft * o->style()->effectiveZoom()) 983 return false; 984 985 paintInfo.context->save(); 986 987 paintInfo.context->setFillColor(o->style()->color(), o->style()->colorSpace()); 988 paintInfo.context->setStrokeStyle(NoStroke); 989 990 FloatPoint arrow1[3]; 991 arrow1[0] = FloatPoint(leftEdge, centerY - spaceBetweenArrows / 2.0f); 992 arrow1[1] = FloatPoint(leftEdge + arrowWidth, centerY - spaceBetweenArrows / 2.0f); 993 arrow1[2] = FloatPoint(leftEdge + arrowWidth / 2.0f, centerY - spaceBetweenArrows / 2.0f - arrowHeight); 994 995 // Draw the top arrow 996 paintInfo.context->drawConvexPolygon(3, arrow1, true); 997 998 FloatPoint arrow2[3]; 999 arrow2[0] = FloatPoint(leftEdge, centerY + spaceBetweenArrows / 2.0f); 1000 arrow2[1] = FloatPoint(leftEdge + arrowWidth, centerY + spaceBetweenArrows / 2.0f); 1001 arrow2[2] = FloatPoint(leftEdge + arrowWidth / 2.0f, centerY + spaceBetweenArrows / 2.0f + arrowHeight); 1002 1003 // Draw the bottom arrow 1004 paintInfo.context->drawConvexPolygon(3, arrow2, true); 1005 1006 Color leftSeparatorColor(0, 0, 0, 40); 1007 Color rightSeparatorColor(255, 255, 255, 40); 1008 1009 // FIXME: Should the separator thickness and space be scaled up by fontScale? 1010 int separatorSpace = 2; // Deliberately ignores zoom since it looks nicer if it stays thin. 1011 int leftEdgeOfSeparator = static_cast<int>(leftEdge - arrowPaddingLeft * o->style()->effectiveZoom()); // FIXME: Round? 1012 1013 // Draw the separator to the left of the arrows 1014 paintInfo.context->setStrokeThickness(1.0f); // Deliberately ignores zoom since it looks nicer if it stays thin. 1015 paintInfo.context->setStrokeStyle(SolidStroke); 1016 paintInfo.context->setStrokeColor(leftSeparatorColor, DeviceColorSpace); 1017 paintInfo.context->drawLine(IntPoint(leftEdgeOfSeparator, bounds.y()), 1018 IntPoint(leftEdgeOfSeparator, bounds.bottom())); 1019 1020 paintInfo.context->setStrokeColor(rightSeparatorColor, DeviceColorSpace); 1021 paintInfo.context->drawLine(IntPoint(leftEdgeOfSeparator + separatorSpace, bounds.y()), 1022 IntPoint(leftEdgeOfSeparator + separatorSpace, bounds.bottom())); 1023 1024 paintInfo.context->restore(); 1025 return false; 1026} 1027 1028static const IntSize* menuListButtonSizes() 1029{ 1030 static const IntSize sizes[3] = { IntSize(0, 21), IntSize(0, 18), IntSize(0, 15) }; 1031 return sizes; 1032} 1033 1034void RenderThemeChromiumMac::adjustMenuListStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const 1035{ 1036 NSControlSize controlSize = controlSizeForFont(style); 1037 1038 style->resetBorder(); 1039 style->resetPadding(); 1040 1041 // Height is locked to auto. 1042 style->setHeight(Length(Auto)); 1043 1044 // White-space is locked to pre 1045 style->setWhiteSpace(PRE); 1046 1047 // Set the foreground color to black or gray when we have the aqua look. 1048 // Cast to RGB32 is to work around a compiler bug. 1049 style->setColor(e && e->isEnabledFormControl() ? static_cast<RGBA32>(Color::black) : Color::darkGray); 1050 1051 // Set the button's vertical size. 1052 setSizeFromFont(style, menuListButtonSizes()); 1053 1054 // Our font is locked to the appropriate system font size for the control. To clarify, we first use the CSS-specified font to figure out 1055 // a reasonable control size, but once that control size is determined, we throw that font away and use the appropriate 1056 // system font for the control size instead. 1057 setFontFromControlSize(selector, style, controlSize); 1058 1059 style->setBoxShadow(0); 1060} 1061 1062int RenderThemeChromiumMac::popupInternalPaddingLeft(RenderStyle* style) const 1063{ 1064 if (style->appearance() == MenulistPart) 1065 return popupButtonPadding(controlSizeForFont(style))[leftPadding] * style->effectiveZoom(); 1066 if (style->appearance() == MenulistButtonPart) 1067 return styledPopupPaddingLeft * style->effectiveZoom(); 1068 return 0; 1069} 1070 1071int RenderThemeChromiumMac::popupInternalPaddingRight(RenderStyle* style) const 1072{ 1073 if (style->appearance() == MenulistPart) 1074 return popupButtonPadding(controlSizeForFont(style))[rightPadding] * style->effectiveZoom(); 1075 if (style->appearance() == MenulistButtonPart) { 1076 float fontScale = style->fontSize() / baseFontSize; 1077 float arrowWidth = baseArrowWidth * fontScale; 1078 return static_cast<int>(ceilf(arrowWidth + (arrowPaddingLeft + arrowPaddingRight + paddingBeforeSeparator) * style->effectiveZoom())); 1079 } 1080 return 0; 1081} 1082 1083int RenderThemeChromiumMac::popupInternalPaddingTop(RenderStyle* style) const 1084{ 1085 if (style->appearance() == MenulistPart) 1086 return popupButtonPadding(controlSizeForFont(style))[topPadding] * style->effectiveZoom(); 1087 if (style->appearance() == MenulistButtonPart) 1088 return styledPopupPaddingTop * style->effectiveZoom(); 1089 return 0; 1090} 1091 1092int RenderThemeChromiumMac::popupInternalPaddingBottom(RenderStyle* style) const 1093{ 1094 if (style->appearance() == MenulistPart) 1095 return popupButtonPadding(controlSizeForFont(style))[bottomPadding] * style->effectiveZoom(); 1096 if (style->appearance() == MenulistButtonPart) 1097 return styledPopupPaddingBottom * style->effectiveZoom(); 1098 return 0; 1099} 1100 1101void RenderThemeChromiumMac::adjustMenuListButtonStyle(CSSStyleSelector*, RenderStyle* style, Element*) const 1102{ 1103 float fontScale = style->fontSize() / baseFontSize; 1104 1105 style->resetPadding(); 1106 style->setBorderRadius(IntSize(int(baseBorderRadius + fontScale - 1), int(baseBorderRadius + fontScale - 1))); // FIXME: Round up? 1107 1108 const int minHeight = 15; 1109 style->setMinHeight(Length(minHeight, Fixed)); 1110 1111 style->setLineHeight(RenderStyle::initialLineHeight()); 1112} 1113 1114void RenderThemeChromiumMac::setPopupButtonCellState(const RenderObject* o, const IntRect& r) 1115{ 1116 NSPopUpButtonCell* popupButton = this->popupButton(); 1117 1118 // Set the control size based off the rectangle we're painting into. 1119 setControlSize(popupButton, popupButtonSizes(), r.size(), o->style()->effectiveZoom()); 1120 1121 // Update the various states we respond to. 1122 updateActiveState(popupButton, o); 1123 updateCheckedState(popupButton, o); 1124 updateEnabledState(popupButton, o); 1125 updatePressedState(popupButton, o); 1126 updateFocusedState(popupButton, o); 1127} 1128 1129const IntSize* RenderThemeChromiumMac::menuListSizes() const 1130{ 1131 static const IntSize sizes[3] = { IntSize(9, 0), IntSize(5, 0), IntSize(0, 0) }; 1132 return sizes; 1133} 1134 1135int RenderThemeChromiumMac::minimumMenuListSize(RenderStyle* style) const 1136{ 1137 return sizeForSystemFont(style, menuListSizes()).width(); 1138} 1139 1140const int trackWidth = 5; 1141const int trackRadius = 2; 1142 1143void RenderThemeChromiumMac::adjustSliderTrackStyle(CSSStyleSelector*, RenderStyle* style, Element*) const 1144{ 1145 style->setBoxShadow(0); 1146} 1147 1148bool RenderThemeChromiumMac::paintSliderTrack(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r) 1149{ 1150 IntRect bounds = r; 1151 float zoomLevel = o->style()->effectiveZoom(); 1152 float zoomedTrackWidth = trackWidth * zoomLevel; 1153 1154 if (o->style()->appearance() == SliderHorizontalPart || o->style()->appearance() == MediaSliderPart) { 1155 bounds.setHeight(zoomedTrackWidth); 1156 bounds.setY(r.y() + r.height() / 2 - zoomedTrackWidth / 2); 1157 } else if (o->style()->appearance() == SliderVerticalPart) { 1158 bounds.setWidth(zoomedTrackWidth); 1159 bounds.setX(r.x() + r.width() / 2 - zoomedTrackWidth / 2); 1160 } 1161 1162 LocalCurrentGraphicsContext localContext(paintInfo.context); 1163 CGContextRef context = paintInfo.context->platformContext(); 1164 RetainPtr<CGColorSpaceRef> cspace(AdoptCF, CGColorSpaceCreateDeviceRGB()); 1165 1166 paintInfo.context->save(); 1167 CGContextClipToRect(context, bounds); 1168 1169 struct CGFunctionCallbacks mainCallbacks = { 0, TrackGradientInterpolate, NULL }; 1170 RetainPtr<CGFunctionRef> mainFunction(AdoptCF, CGFunctionCreate(NULL, 1, NULL, 4, NULL, &mainCallbacks)); 1171 RetainPtr<CGShadingRef> mainShading; 1172 if (o->style()->appearance() == SliderVerticalPart) 1173 mainShading.adoptCF(CGShadingCreateAxial(cspace.get(), CGPointMake(bounds.x(), bounds.bottom()), CGPointMake(bounds.right(), bounds.bottom()), mainFunction.get(), false, false)); 1174 else 1175 mainShading.adoptCF(CGShadingCreateAxial(cspace.get(), CGPointMake(bounds.x(), bounds.y()), CGPointMake(bounds.x(), bounds.bottom()), mainFunction.get(), false, false)); 1176 1177 IntSize radius(trackRadius, trackRadius); 1178 paintInfo.context->addRoundedRectClip(bounds, 1179 radius, radius, 1180 radius, radius); 1181 CGContextDrawShading(context, mainShading.get()); 1182 paintInfo.context->restore(); 1183 1184 return false; 1185} 1186 1187void RenderThemeChromiumMac::adjustSliderThumbStyle(CSSStyleSelector*, RenderStyle* style, Element*) const 1188{ 1189 style->setBoxShadow(0); 1190} 1191 1192const float verticalSliderHeightPadding = 0.1f; 1193 1194bool RenderThemeChromiumMac::paintSliderThumb(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r) 1195{ 1196 ASSERT(o->parent()->isSlider()); 1197 1198 NSSliderCell* sliderThumbCell = o->style()->appearance() == SliderThumbVerticalPart 1199 ? sliderThumbVertical() 1200 : sliderThumbHorizontal(); 1201 1202 LocalCurrentGraphicsContext localContext(paintInfo.context); 1203 1204 // Update the various states we respond to. 1205 updateActiveState(sliderThumbCell, o->parent()); 1206 updateEnabledState(sliderThumbCell, o->parent()); 1207 updateFocusedState(sliderThumbCell, o->parent()); 1208 1209 // Update the pressed state using the NSCell tracking methods, since that's how NSSliderCell keeps track of it. 1210 bool oldPressed; 1211 if (o->style()->appearance() == SliderThumbVerticalPart) 1212 oldPressed = m_isSliderThumbVerticalPressed; 1213 else 1214 oldPressed = m_isSliderThumbHorizontalPressed; 1215 1216 bool pressed = toRenderSlider(o->parent())->inDragMode(); 1217 1218 if (o->style()->appearance() == SliderThumbVerticalPart) 1219 m_isSliderThumbVerticalPressed = pressed; 1220 else 1221 m_isSliderThumbHorizontalPressed = pressed; 1222 1223 if (pressed != oldPressed) { 1224 if (pressed) 1225 [sliderThumbCell startTrackingAt:NSPoint() inView:nil]; 1226 else 1227 [sliderThumbCell stopTracking:NSPoint() at:NSPoint() inView:nil mouseIsUp:YES]; 1228 } 1229 1230 FloatRect bounds = r; 1231 // Make the height of the vertical slider slightly larger so NSSliderCell will draw a vertical slider. 1232 if (o->style()->appearance() == SliderThumbVerticalPart) 1233 bounds.setHeight(bounds.height() + verticalSliderHeightPadding * o->style()->effectiveZoom()); 1234 1235 paintInfo.context->save(); 1236 float zoomLevel = o->style()->effectiveZoom(); 1237 1238 FloatRect unzoomedRect = bounds; 1239 if (zoomLevel != 1.0f) { 1240 unzoomedRect.setWidth(unzoomedRect.width() / zoomLevel); 1241 unzoomedRect.setHeight(unzoomedRect.height() / zoomLevel); 1242 paintInfo.context->translate(unzoomedRect.x(), unzoomedRect.y()); 1243 paintInfo.context->scale(FloatSize(zoomLevel, zoomLevel)); 1244 paintInfo.context->translate(-unzoomedRect.x(), -unzoomedRect.y()); 1245 } 1246 1247 [sliderThumbCell drawWithFrame:unzoomedRect inView:FlippedView()]; 1248 [sliderThumbCell setControlView:nil]; 1249 1250 paintInfo.context->restore(); 1251 1252 return false; 1253} 1254 1255bool RenderThemeChromiumMac::paintSearchField(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r) 1256{ 1257 NSSearchFieldCell* search = this->search(); 1258 LocalCurrentGraphicsContext localContext(paintInfo.context); 1259 1260 setSearchCellState(o, r); 1261 1262 paintInfo.context->save(); 1263 1264 float zoomLevel = o->style()->effectiveZoom(); 1265 1266 IntRect unzoomedRect = r; 1267 1268 if (zoomLevel != 1.0f) { 1269 unzoomedRect.setWidth(unzoomedRect.width() / zoomLevel); 1270 unzoomedRect.setHeight(unzoomedRect.height() / zoomLevel); 1271 paintInfo.context->translate(unzoomedRect.x(), unzoomedRect.y()); 1272 paintInfo.context->scale(FloatSize(zoomLevel, zoomLevel)); 1273 paintInfo.context->translate(-unzoomedRect.x(), -unzoomedRect.y()); 1274 } 1275 1276 // Set the search button to nil before drawing. Then reset it so we can draw it later. 1277 [search setSearchButtonCell:nil]; 1278 1279 [search drawWithFrame:NSRect(unzoomedRect) inView:FlippedView()]; 1280#ifdef BUILDING_ON_TIGER 1281 if ([search showsFirstResponder]) 1282 wkDrawTextFieldCellFocusRing(search, NSRect(unzoomedRect)); 1283#endif 1284 1285 [search setControlView:nil]; 1286 [search resetSearchButtonCell]; 1287 1288 paintInfo.context->restore(); 1289 1290 return false; 1291} 1292 1293void RenderThemeChromiumMac::setSearchCellState(RenderObject* o, const IntRect&) 1294{ 1295 NSSearchFieldCell* search = this->search(); 1296 1297 [search setControlSize:controlSizeForFont(o->style())]; 1298 1299 // Update the various states we respond to. 1300 updateActiveState(search, o); 1301 updateEnabledState(search, o); 1302 updateFocusedState(search, o); 1303} 1304 1305const IntSize* RenderThemeChromiumMac::searchFieldSizes() const 1306{ 1307 static const IntSize sizes[3] = { IntSize(0, 22), IntSize(0, 19), IntSize(0, 17) }; 1308 return sizes; 1309} 1310 1311void RenderThemeChromiumMac::setSearchFieldSize(RenderStyle* style) const 1312{ 1313 // If the width and height are both specified, then we have nothing to do. 1314 if (!style->width().isIntrinsicOrAuto() && !style->height().isAuto()) 1315 return; 1316 1317 // Use the font size to determine the intrinsic width of the control. 1318 setSizeFromFont(style, searchFieldSizes()); 1319} 1320 1321void RenderThemeChromiumMac::adjustSearchFieldStyle(CSSStyleSelector* selector, RenderStyle* style, Element*) const 1322{ 1323 // Override border. 1324 style->resetBorder(); 1325 const short borderWidth = 2 * style->effectiveZoom(); 1326 style->setBorderLeftWidth(borderWidth); 1327 style->setBorderLeftStyle(INSET); 1328 style->setBorderRightWidth(borderWidth); 1329 style->setBorderRightStyle(INSET); 1330 style->setBorderBottomWidth(borderWidth); 1331 style->setBorderBottomStyle(INSET); 1332 style->setBorderTopWidth(borderWidth); 1333 style->setBorderTopStyle(INSET); 1334 1335 // Override height. 1336 style->setHeight(Length(Auto)); 1337 setSearchFieldSize(style); 1338 1339 // Override padding size to match AppKit text positioning. 1340 const int padding = 1 * style->effectiveZoom(); 1341 style->setPaddingLeft(Length(padding, Fixed)); 1342 style->setPaddingRight(Length(padding, Fixed)); 1343 style->setPaddingTop(Length(padding, Fixed)); 1344 style->setPaddingBottom(Length(padding, Fixed)); 1345 1346 NSControlSize controlSize = controlSizeForFont(style); 1347 setFontFromControlSize(selector, style, controlSize); 1348 1349 style->setBoxShadow(0); 1350} 1351 1352bool RenderThemeChromiumMac::paintSearchFieldCancelButton(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r) 1353{ 1354 Node* input = o->node()->shadowAncestorNode(); 1355 if (!input->renderer()->isBox()) 1356 return false; 1357 1358 setSearchCellState(input->renderer(), r); 1359 1360 NSSearchFieldCell* search = this->search(); 1361 1362 updateActiveState([search cancelButtonCell], o); 1363 updatePressedState([search cancelButtonCell], o); 1364 1365 paintInfo.context->save(); 1366 1367 float zoomLevel = o->style()->effectiveZoom(); 1368 1369 FloatRect localBounds = [search cancelButtonRectForBounds:NSRect(input->renderBox()->borderBoxRect())]; 1370 localBounds = convertToPaintingRect(input->renderer(), o, localBounds, r); 1371 1372 FloatRect unzoomedRect(localBounds); 1373 if (zoomLevel != 1.0f) { 1374 unzoomedRect.setWidth(unzoomedRect.width() / zoomLevel); 1375 unzoomedRect.setHeight(unzoomedRect.height() / zoomLevel); 1376 paintInfo.context->translate(unzoomedRect.x(), unzoomedRect.y()); 1377 paintInfo.context->scale(FloatSize(zoomLevel, zoomLevel)); 1378 paintInfo.context->translate(-unzoomedRect.x(), -unzoomedRect.y()); 1379 } 1380 1381 [[search cancelButtonCell] drawWithFrame:unzoomedRect inView:FlippedView()]; 1382 [[search cancelButtonCell] setControlView:nil]; 1383 1384 paintInfo.context->restore(); 1385 return false; 1386} 1387 1388const IntSize* RenderThemeChromiumMac::cancelButtonSizes() const 1389{ 1390 static const IntSize sizes[3] = { IntSize(16, 13), IntSize(13, 11), IntSize(13, 9) }; 1391 return sizes; 1392} 1393 1394void RenderThemeChromiumMac::adjustSearchFieldCancelButtonStyle(CSSStyleSelector*, RenderStyle* style, Element*) const 1395{ 1396 IntSize size = sizeForSystemFont(style, cancelButtonSizes()); 1397 style->setWidth(Length(size.width(), Fixed)); 1398 style->setHeight(Length(size.height(), Fixed)); 1399 style->setBoxShadow(0); 1400} 1401 1402const IntSize* RenderThemeChromiumMac::resultsButtonSizes() const 1403{ 1404 static const IntSize sizes[3] = { IntSize(19, 13), IntSize(17, 11), IntSize(17, 9) }; 1405 return sizes; 1406} 1407 1408const int emptyResultsOffset = 9; 1409void RenderThemeChromiumMac::adjustSearchFieldDecorationStyle(CSSStyleSelector*, RenderStyle* style, Element*) const 1410{ 1411 IntSize size = sizeForSystemFont(style, resultsButtonSizes()); 1412 style->setWidth(Length(size.width() - emptyResultsOffset, Fixed)); 1413 style->setHeight(Length(size.height(), Fixed)); 1414 style->setBoxShadow(0); 1415} 1416 1417bool RenderThemeChromiumMac::paintSearchFieldDecoration(RenderObject*, const RenderObject::PaintInfo&, const IntRect&) 1418{ 1419 return false; 1420} 1421 1422void RenderThemeChromiumMac::adjustSearchFieldResultsDecorationStyle(CSSStyleSelector*, RenderStyle* style, Element*) const 1423{ 1424 IntSize size = sizeForSystemFont(style, resultsButtonSizes()); 1425 style->setWidth(Length(size.width(), Fixed)); 1426 style->setHeight(Length(size.height(), Fixed)); 1427 style->setBoxShadow(0); 1428} 1429 1430bool RenderThemeChromiumMac::paintSearchFieldResultsDecoration(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r) 1431{ 1432 Node* input = o->node()->shadowAncestorNode(); 1433 if (!input->renderer()->isBox()) 1434 return false; 1435 1436 setSearchCellState(input->renderer(), r); 1437 1438 NSSearchFieldCell* search = this->search(); 1439 1440 updateActiveState([search searchButtonCell], o); 1441 1442 if ([search searchMenuTemplate] != nil) 1443 [search setSearchMenuTemplate:nil]; 1444 1445 FloatRect localBounds = [search searchButtonRectForBounds:NSRect(input->renderBox()->borderBoxRect())]; 1446 localBounds = convertToPaintingRect(input->renderer(), o, localBounds, r); 1447 1448 [[search searchButtonCell] drawWithFrame:localBounds inView:FlippedView()]; 1449 [[search searchButtonCell] setControlView:nil]; 1450 return false; 1451} 1452 1453const int resultsArrowWidth = 5; 1454void RenderThemeChromiumMac::adjustSearchFieldResultsButtonStyle(CSSStyleSelector*, RenderStyle* style, Element*) const 1455{ 1456 IntSize size = sizeForSystemFont(style, resultsButtonSizes()); 1457 style->setWidth(Length(size.width() + resultsArrowWidth, Fixed)); 1458 style->setHeight(Length(size.height(), Fixed)); 1459 style->setBoxShadow(0); 1460} 1461 1462bool RenderThemeChromiumMac::paintSearchFieldResultsButton(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r) 1463{ 1464 Node* input = o->node()->shadowAncestorNode(); 1465 if (!input->renderer()->isBox()) 1466 return false; 1467 1468 setSearchCellState(input->renderer(), r); 1469 1470 NSSearchFieldCell* search = this->search(); 1471 1472 updateActiveState([search searchButtonCell], o); 1473 1474 if (![search searchMenuTemplate]) 1475 [search setSearchMenuTemplate:searchMenuTemplate()]; 1476 1477 paintInfo.context->save(); 1478 1479 float zoomLevel = o->style()->effectiveZoom(); 1480 1481 FloatRect localBounds = [search searchButtonRectForBounds:NSRect(input->renderBox()->borderBoxRect())]; 1482 localBounds = convertToPaintingRect(input->renderer(), o, localBounds, r); 1483 1484 IntRect unzoomedRect(localBounds); 1485 if (zoomLevel != 1.0f) { 1486 unzoomedRect.setWidth(unzoomedRect.width() / zoomLevel); 1487 unzoomedRect.setHeight(unzoomedRect.height() / zoomLevel); 1488 paintInfo.context->translate(unzoomedRect.x(), unzoomedRect.y()); 1489 paintInfo.context->scale(FloatSize(zoomLevel, zoomLevel)); 1490 paintInfo.context->translate(-unzoomedRect.x(), -unzoomedRect.y()); 1491 } 1492 1493 [[search searchButtonCell] drawWithFrame:unzoomedRect inView:FlippedView()]; 1494 [[search searchButtonCell] setControlView:nil]; 1495 1496 paintInfo.context->restore(); 1497 1498 return false; 1499} 1500 1501const int sliderThumbWidth = 15; 1502const int sliderThumbHeight = 15; 1503 1504void RenderThemeChromiumMac::adjustSliderThumbSize(RenderObject* o) const 1505{ 1506 float zoomLevel = o->style()->effectiveZoom(); 1507 if (o->style()->appearance() == SliderThumbHorizontalPart || o->style()->appearance() == SliderThumbVerticalPart) { 1508 o->style()->setWidth(Length(static_cast<int>(sliderThumbWidth * zoomLevel), Fixed)); 1509 o->style()->setHeight(Length(static_cast<int>(sliderThumbHeight * zoomLevel), Fixed)); 1510 } 1511 1512#if ENABLE(VIDEO) 1513 RenderMediaControlsChromium::adjustMediaSliderThumbSize(o); 1514#endif 1515} 1516 1517#if ENABLE(VIDEO) 1518bool RenderThemeChromiumMac::shouldRenderMediaControlPart(ControlPart part, Element* e) 1519{ 1520 return RenderMediaControlsChromium::shouldRenderMediaControlPart(part, e); 1521} 1522 1523bool RenderThemeChromiumMac::paintMediaPlayButton(RenderObject* object, const RenderObject::PaintInfo& paintInfo, const IntRect& rect) 1524{ 1525 return RenderMediaControlsChromium::paintMediaControlsPart(MediaPlayButton, object, paintInfo, rect); 1526} 1527 1528bool RenderThemeChromiumMac::paintMediaMuteButton(RenderObject* object, const RenderObject::PaintInfo& paintInfo, const IntRect& rect) 1529{ 1530 return RenderMediaControlsChromium::paintMediaControlsPart(MediaMuteButton, object, paintInfo, rect); 1531} 1532 1533bool RenderThemeChromiumMac::paintMediaSliderTrack(RenderObject* object, const RenderObject::PaintInfo& paintInfo, const IntRect& rect) 1534{ 1535 return RenderMediaControlsChromium::paintMediaControlsPart(MediaSlider, object, paintInfo, rect); 1536} 1537 1538bool RenderThemeChromiumMac::paintMediaVolumeSliderTrack(RenderObject* object, const RenderObject::PaintInfo& paintInfo, const IntRect& rect) 1539{ 1540 return RenderMediaControlsChromium::paintMediaControlsPart(MediaVolumeSlider, object, paintInfo, rect); 1541} 1542 1543bool RenderThemeChromiumMac::paintMediaSliderThumb(RenderObject* object, const RenderObject::PaintInfo& paintInfo, const IntRect& rect) 1544{ 1545 return RenderMediaControlsChromium::paintMediaControlsPart(MediaSliderThumb, object, paintInfo, rect); 1546} 1547 1548bool RenderThemeChromiumMac::paintMediaVolumeSliderThumb(RenderObject* object, const RenderObject::PaintInfo& paintInfo, const IntRect& rect) 1549{ 1550 return RenderMediaControlsChromium::paintMediaControlsPart(MediaVolumeSliderThumb, object, paintInfo, rect); 1551} 1552 1553bool RenderThemeChromiumMac::paintMediaControlsBackground(RenderObject* object, const RenderObject::PaintInfo& paintInfo, const IntRect& rect) 1554{ 1555 return RenderMediaControlsChromium::paintMediaControlsPart(MediaTimelineContainer, object, paintInfo, rect); 1556} 1557 1558String RenderThemeChromiumMac::extraMediaControlsStyleSheet() 1559{ 1560 return String(mediaControlsChromiumUserAgentStyleSheet, sizeof(mediaControlsChromiumUserAgentStyleSheet)); 1561} 1562 1563#endif 1564 1565NSPopUpButtonCell* RenderThemeChromiumMac::popupButton() const 1566{ 1567 if (!m_popupButton) { 1568 m_popupButton.adoptNS([[NSPopUpButtonCell alloc] initTextCell:@"" pullsDown:NO]); 1569 [m_popupButton.get() setUsesItemFromMenu:NO]; 1570 [m_popupButton.get() setFocusRingType:NSFocusRingTypeExterior]; 1571 } 1572 1573 return m_popupButton.get(); 1574} 1575 1576NSSearchFieldCell* RenderThemeChromiumMac::search() const 1577{ 1578 if (!m_search) { 1579 m_search.adoptNS([[NSSearchFieldCell alloc] initTextCell:@""]); 1580 [m_search.get() setBezelStyle:NSTextFieldRoundedBezel]; 1581 [m_search.get() setBezeled:YES]; 1582 [m_search.get() setEditable:YES]; 1583 [m_search.get() setFocusRingType:NSFocusRingTypeExterior]; 1584 } 1585 1586 return m_search.get(); 1587} 1588 1589NSMenu* RenderThemeChromiumMac::searchMenuTemplate() const 1590{ 1591 if (!m_searchMenuTemplate) 1592 m_searchMenuTemplate.adoptNS([[NSMenu alloc] initWithTitle:@""]); 1593 1594 return m_searchMenuTemplate.get(); 1595} 1596 1597NSSliderCell* RenderThemeChromiumMac::sliderThumbHorizontal() const 1598{ 1599 if (!m_sliderThumbHorizontal) { 1600 m_sliderThumbHorizontal.adoptNS([[NSSliderCell alloc] init]); 1601 [m_sliderThumbHorizontal.get() setTitle:nil]; 1602 [m_sliderThumbHorizontal.get() setSliderType:NSLinearSlider]; 1603 [m_sliderThumbHorizontal.get() setControlSize:NSSmallControlSize]; 1604 [m_sliderThumbHorizontal.get() setFocusRingType:NSFocusRingTypeExterior]; 1605 } 1606 1607 return m_sliderThumbHorizontal.get(); 1608} 1609 1610NSSliderCell* RenderThemeChromiumMac::sliderThumbVertical() const 1611{ 1612 if (!m_sliderThumbVertical) { 1613 m_sliderThumbVertical.adoptNS([[NSSliderCell alloc] init]); 1614 [m_sliderThumbVertical.get() setTitle:nil]; 1615 [m_sliderThumbVertical.get() setSliderType:NSLinearSlider]; 1616 [m_sliderThumbVertical.get() setControlSize:NSSmallControlSize]; 1617 [m_sliderThumbVertical.get() setFocusRingType:NSFocusRingTypeExterior]; 1618 } 1619 1620 return m_sliderThumbVertical.get(); 1621} 1622 1623} // namespace WebCore 1624