2 * Copyright (C) 2007, 2008, 2009 Apple Inc.
3 * Copyright (C) 2009 Kenneth Rohde Christiansen
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.
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.
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.
23 #include "RenderThemeSafari.h"
24 #include "RenderThemeWin.h"
29 #include "CSSFontSelector.h"
30 #include "CSSValueKeywords.h"
34 #include "FrameView.h"
35 #include "GraphicsContextCG.h"
36 #include "HTMLInputElement.h"
37 #include "HTMLMediaElement.h"
38 #include "HTMLNames.h"
39 #include "PaintInfo.h"
40 #include "RenderMediaControls.h"
41 #include "RenderSlider.h"
42 #include "RenderView.h"
43 #include "SoftLinking.h"
44 #include "StyleResolver.h"
45 #include <CoreGraphics/CoreGraphics.h>
46 #include <wtf/RetainPtr.h>
50 // FIXME: The platform-independent code in this class should be factored out and merged with RenderThemeMac.
54 using namespace HTMLNames;
55 using namespace SafariTheme;
71 PassRefPtr<RenderTheme> RenderThemeSafari::create()
73 return adoptRef(new RenderThemeSafari);
76 PassRefPtr<RenderTheme> RenderTheme::themeForPage(Page* page)
78 static RenderTheme* safariTheme = RenderThemeSafari::create().leakRef();
79 static RenderTheme* windowsTheme = RenderThemeWin::create().leakRef();
81 // FIXME: This is called before Settings has been initialized by WebKit, so will return a
82 // potentially wrong answer the very first time it's called (see
83 // <https://bugs.webkit.org/show_bug.cgi?id=26493>).
84 if (Settings::shouldPaintNativeControls()) {
85 RenderTheme::setCustomFocusRingColor(safariTheme->platformFocusRingColor());
86 return windowsTheme; // keep the reference of one.
88 return safariTheme; // keep the reference of one.
92 SOFT_LINK_DEBUG_LIBRARY(SafariTheme)
94 SOFT_LINK_LIBRARY(SafariTheme)
97 SOFT_LINK(SafariTheme, paintThemePart, void, __stdcall, (ThemePart part, CGContextRef context, const CGRect& rect, NSControlSize size, ThemeControlState state), (part, context, rect, size, state))
98 #if defined(SAFARI_THEME_VERSION) && SAFARI_THEME_VERSION >= 2
99 SOFT_LINK(SafariTheme, STPaintProgressIndicator, void, APIENTRY, (ProgressIndicatorType type, CGContextRef context, const CGRect& rect, NSControlSize size, ThemeControlState state, float value), (type, context, rect, size, state, value))
101 SOFT_LINK_OPTIONAL(SafariTheme, STCopyThemeColor, CGColorRef, APIENTRY, (unsigned color, SafariTheme::ThemeControlState));
103 static const unsigned stFocusRingColorID = 4;
105 static const unsigned aquaFocusRingColor = 0xFF7DADD9;
107 static RGBA32 makeRGBAFromCGColor(CGColorRef color)
109 const CGFloat* components = CGColorGetComponents(color);
110 return makeRGBA(255 * components[0], 255 * components[1], 255 * components[2], 255 * components[3]);
113 ThemeControlState RenderThemeSafari::determineState(RenderObject* o) const
115 ThemeControlState result = 0;
117 result |= SafariTheme::ActiveState;
118 if (isEnabled(o) && !isReadOnlyControl(o))
119 result |= SafariTheme::EnabledState;
121 result |= SafariTheme::PressedState;
123 result |= SafariTheme::CheckedState;
124 if (isIndeterminate(o))
125 result |= SafariTheme::IndeterminateCheckedState;
127 result |= SafariTheme::FocusedState;
129 result |= SafariTheme::DefaultState;
133 static NSControlSize controlSizeFromRect(const IntRect& rect, const IntSize sizes[])
135 if (sizes[NSRegularControlSize].height() == rect.height())
136 return NSRegularControlSize;
137 else if (sizes[NSMiniControlSize].height() == rect.height())
138 return NSMiniControlSize;
140 return NSSmallControlSize;
143 RenderThemeSafari::RenderThemeSafari()
147 RenderThemeSafari::~RenderThemeSafari()
151 Color RenderThemeSafari::platformActiveSelectionBackgroundColor() const
153 return Color(181, 213, 255);
156 Color RenderThemeSafari::platformInactiveSelectionBackgroundColor() const
158 return Color(212, 212, 212);
161 Color RenderThemeSafari::activeListBoxSelectionBackgroundColor() const
163 // FIXME: This should probably just be a darker version of the platformActiveSelectionBackgroundColor
164 return Color(56, 117, 215);
167 Color RenderThemeSafari::platformFocusRingColor() const
169 static Color focusRingColor;
171 if (!focusRingColor.isValid()) {
172 if (STCopyThemeColorPtr()) {
173 RetainPtr<CGColorRef> color(AdoptCF, STCopyThemeColorPtr()(stFocusRingColorID, SafariTheme::ActiveState));
174 focusRingColor = makeRGBAFromCGColor(color.get());
176 if (!focusRingColor.isValid())
177 focusRingColor = aquaFocusRingColor;
180 return focusRingColor;
183 static float systemFontSizeForControlSize(NSControlSize controlSize)
185 static float sizes[] = { 13.0f, 11.0f, 9.0f };
187 return sizes[controlSize];
190 void RenderThemeSafari::systemFont(int propId, FontDescription& fontDescription) const
192 static FontDescription systemFont;
193 static FontDescription smallSystemFont;
194 static FontDescription menuFont;
195 static FontDescription labelFont;
196 static FontDescription miniControlFont;
197 static FontDescription smallControlFont;
198 static FontDescription controlFont;
200 FontDescription* cachedDesc;
203 case CSSValueSmallCaption:
204 cachedDesc = &smallSystemFont;
205 if (!smallSystemFont.isAbsoluteSize())
206 fontSize = systemFontSizeForControlSize(NSSmallControlSize);
209 cachedDesc = &menuFont;
210 if (!menuFont.isAbsoluteSize())
211 fontSize = systemFontSizeForControlSize(NSRegularControlSize);
213 case CSSValueStatusBar:
214 cachedDesc = &labelFont;
215 if (!labelFont.isAbsoluteSize())
218 case CSSValueWebkitMiniControl:
219 cachedDesc = &miniControlFont;
220 if (!miniControlFont.isAbsoluteSize())
221 fontSize = systemFontSizeForControlSize(NSMiniControlSize);
223 case CSSValueWebkitSmallControl:
224 cachedDesc = &smallControlFont;
225 if (!smallControlFont.isAbsoluteSize())
226 fontSize = systemFontSizeForControlSize(NSSmallControlSize);
228 case CSSValueWebkitControl:
229 cachedDesc = &controlFont;
230 if (!controlFont.isAbsoluteSize())
231 fontSize = systemFontSizeForControlSize(NSRegularControlSize);
234 cachedDesc = &systemFont;
235 if (!systemFont.isAbsoluteSize())
240 cachedDesc->setIsAbsoluteSize(true);
241 cachedDesc->setGenericFamily(FontDescription::NoFamily);
242 cachedDesc->firstFamily().setFamily("Lucida Grande");
243 cachedDesc->setSpecifiedSize(fontSize);
244 cachedDesc->setWeight(FontWeightNormal);
245 cachedDesc->setItalic(false);
247 fontDescription = *cachedDesc;
250 bool RenderThemeSafari::isControlStyled(const RenderStyle* style, const BorderData& border,
251 const FillLayer& background, const Color& backgroundColor) const
253 // If we didn't find SafariTheme.dll we won't be able to paint any themed controls.
254 if (!SafariThemeLibrary())
257 if (style->appearance() == TextFieldPart || style->appearance() == TextAreaPart || style->appearance() == ListboxPart)
258 return style->border() != border;
259 return RenderTheme::isControlStyled(style, border, background, backgroundColor);
262 void RenderThemeSafari::adjustRepaintRect(const RenderObject* o, IntRect& r)
264 NSControlSize controlSize = controlSizeForFont(o->style());
266 switch (o->style()->appearance()) {
268 // We inflate the rect as needed to account for padding included in the cell to accommodate the checkbox
269 // shadow" and the check. We don't consider this part of the bounds of the control in WebKit.
270 r = inflateRect(r, checkboxSizes()[controlSize], checkboxMargins(controlSize));
274 // We inflate the rect as needed to account for padding included in the cell to accommodate the checkbox
275 // shadow" and the check. We don't consider this part of the bounds of the control in WebKit.
276 r = inflateRect(r, radioSizes()[controlSize], radioMargins(controlSize));
280 case DefaultButtonPart:
282 // We inflate the rect as needed to account for padding included in the cell to accommodate the checkbox
283 // shadow" and the check. We don't consider this part of the bounds of the control in WebKit.
284 if (r.height() <= buttonSizes()[NSRegularControlSize].height())
285 r = inflateRect(r, buttonSizes()[controlSize], buttonMargins(controlSize));
289 r = inflateRect(r, popupButtonSizes()[controlSize], popupButtonMargins(controlSize));
297 IntRect RenderThemeSafari::inflateRect(const IntRect& r, const IntSize& size, const int* margins) const
299 // Only do the inflation if the available width/height are too small. Otherwise try to
300 // fit the glow/check space into the available box's width/height.
301 int widthDelta = r.width() - (size.width() + margins[leftMargin] + margins[rightMargin]);
302 int heightDelta = r.height() - (size.height() + margins[topMargin] + margins[bottomMargin]);
304 if (widthDelta < 0) {
305 result.setX(result.x() - margins[leftMargin]);
306 result.setWidth(result.width() - widthDelta);
308 if (heightDelta < 0) {
309 result.setY(result.y() - margins[topMargin]);
310 result.setHeight(result.height() - heightDelta);
315 LayoutUnit RenderThemeSafari::baselinePosition(const RenderObject* o) const
320 if (o->style()->appearance() == CheckboxPart || o->style()->appearance() == RadioPart) {
321 const RenderBox* box = toRenderBox(o);
322 return box->marginTop() + box->height() - 2; // The baseline is 2px up from the bottom of the checkbox/radio in AppKit.
325 return RenderTheme::baselinePosition(o);
328 bool RenderThemeSafari::controlSupportsTints(const RenderObject* o) const
333 // Checkboxes only have tint when checked.
334 if (o->style()->appearance() == CheckboxPart)
337 // For now assume other controls have tint if enabled.
341 NSControlSize RenderThemeSafari::controlSizeForFont(RenderStyle* style) const
343 int fontSize = style->fontSize();
345 return NSRegularControlSize;
347 return NSSmallControlSize;
348 return NSMiniControlSize;
351 void RenderThemeSafari::setControlSize(NSCell* cell, const IntSize* sizes, const IntSize& minSize)
354 if (minSize.width() >= sizes[NSRegularControlSize].width() &&
355 minSize.height() >= sizes[NSRegularControlSize].height())
356 size = NSRegularControlSize;
357 else if (minSize.width() >= sizes[NSSmallControlSize].width() &&
358 minSize.height() >= sizes[NSSmallControlSize].height())
359 size = NSSmallControlSize;
361 size = NSMiniControlSize;
362 if (size != [cell controlSize]) // Only update if we have to, since AppKit does work even if the size is the same.
363 [cell setControlSize:size];
366 IntSize RenderThemeSafari::sizeForFont(RenderStyle* style, const IntSize* sizes) const
368 return sizes[controlSizeForFont(style)];
371 IntSize RenderThemeSafari::sizeForSystemFont(RenderStyle* style, const IntSize* sizes) const
373 return sizes[controlSizeForSystemFont(style)];
376 void RenderThemeSafari::setSizeFromFont(RenderStyle* style, const IntSize* sizes) const
378 // FIXME: Check is flawed, since it doesn't take min-width/max-width into account.
379 IntSize size = sizeForFont(style, sizes);
380 if (style->width().isIntrinsicOrAuto() && size.width() > 0)
381 style->setWidth(Length(size.width(), Fixed));
382 if (style->height().isAuto() && size.height() > 0)
383 style->setHeight(Length(size.height(), Fixed));
386 void RenderThemeSafari::setFontFromControlSize(StyleResolver* styleResolver, RenderStyle* style, NSControlSize controlSize) const
388 FontDescription fontDescription;
389 fontDescription.setIsAbsoluteSize(true);
390 fontDescription.setGenericFamily(FontDescription::SerifFamily);
392 float fontSize = systemFontSizeForControlSize(controlSize);
393 fontDescription.firstFamily().setFamily("Lucida Grande");
394 fontDescription.setComputedSize(fontSize);
395 fontDescription.setSpecifiedSize(fontSize);
398 style->setLineHeight(RenderStyle::initialLineHeight());
400 if (style->setFontDescription(fontDescription))
401 style->font().update(styleResolver->fontSelector());
404 NSControlSize RenderThemeSafari::controlSizeForSystemFont(RenderStyle* style) const
406 int fontSize = style->fontSize();
408 return NSRegularControlSize;
410 return NSSmallControlSize;
411 return NSMiniControlSize;
414 bool RenderThemeSafari::paintCheckbox(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r)
416 ASSERT(SafariThemeLibrary());
418 NSControlSize controlSize = controlSizeForFont(o->style());
420 IntRect inflatedRect = inflateRect(r, checkboxSizes()[controlSize], checkboxMargins(controlSize));
421 paintThemePart(SafariTheme::CheckboxPart, paintInfo.context->platformContext(), inflatedRect, controlSize, determineState(o));
426 const IntSize* RenderThemeSafari::checkboxSizes() const
428 static const IntSize sizes[3] = { IntSize(14, 14), IntSize(12, 12), IntSize(10, 10) };
432 const int* RenderThemeSafari::checkboxMargins(NSControlSize controlSize) const
434 static const int margins[3][4] =
440 return margins[controlSize];
443 void RenderThemeSafari::setCheckboxSize(RenderStyle* style) const
445 // If the width and height are both specified, then we have nothing to do.
446 if (!style->width().isIntrinsicOrAuto() && !style->height().isAuto())
449 // Use the font size to determine the intrinsic width of the control.
450 setSizeFromFont(style, checkboxSizes());
453 bool RenderThemeSafari::paintRadio(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r)
455 ASSERT(SafariThemeLibrary());
457 NSControlSize controlSize = controlSizeForFont(o->style());
459 IntRect inflatedRect = inflateRect(r, radioSizes()[controlSize], radioMargins(controlSize));
460 paintThemePart(RadioButtonPart, paintInfo.context->platformContext(), inflatedRect, controlSize, determineState(o));
465 const IntSize* RenderThemeSafari::radioSizes() const
467 static const IntSize sizes[3] = { IntSize(14, 15), IntSize(12, 13), IntSize(10, 10) };
471 const int* RenderThemeSafari::radioMargins(NSControlSize controlSize) const
473 static const int margins[3][4] =
479 return margins[controlSize];
482 void RenderThemeSafari::setRadioSize(RenderStyle* style) const
484 // If the width and height are both specified, then we have nothing to do.
485 if (!style->width().isIntrinsicOrAuto() && !style->height().isAuto())
488 // Use the font size to determine the intrinsic width of the control.
489 setSizeFromFont(style, radioSizes());
492 void RenderThemeSafari::setButtonPaddingFromControlSize(RenderStyle* style, NSControlSize size) const
494 // Just use 8px. AppKit wants to use 11px for mini buttons, but that padding is just too large
495 // for real-world Web sites (creating a huge necessary minimum width for buttons whose space is
496 // by definition constrained, since we select mini only for small cramped environments.
497 // This also guarantees the HTML4 <button> will match our rendering by default, since we're using a consistent
499 const int padding = 8;
500 style->setPaddingLeft(Length(padding, Fixed));
501 style->setPaddingRight(Length(padding, Fixed));
502 style->setPaddingTop(Length(0, Fixed));
503 style->setPaddingBottom(Length(0, Fixed));
506 void RenderThemeSafari::adjustButtonStyle(StyleResolver* styleResolver, RenderStyle* style, Element* e) const
508 // There are three appearance constants for buttons.
509 // (1) Push-button is the constant for the default Aqua system button. Push buttons will not scale vertically and will not allow
510 // custom fonts or colors. <input>s use this constant. This button will allow custom colors and font weights/variants but won't
512 // (2) square-button is the constant for the square button. This button will allow custom fonts and colors and will scale vertically.
513 // (3) Button is the constant that means "pick the best button as appropriate." <button>s use this constant. This button will
514 // also scale vertically and allow custom fonts and colors. It will attempt to use Aqua if possible and will make this determination
515 // solely on the rectangle of the control.
517 // Determine our control size based off our font.
518 NSControlSize controlSize = controlSizeForFont(style);
520 if (style->appearance() == PushButtonPart) {
522 style->resetBorder();
524 // Height is locked to auto.
525 style->setHeight(Length(Auto));
527 // White-space is locked to pre
528 style->setWhiteSpace(PRE);
530 // Set the button's vertical size.
531 setButtonSize(style);
533 // Add in the padding that we'd like to use.
534 setButtonPaddingFromControlSize(style, controlSize);
536 // 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
537 // a reasonable control size, but once that control size is determined, we throw that font away and use the appropriate
538 // system font for the control size instead.
539 setFontFromControlSize(styleResolver, style, controlSize);
541 // Set a min-height so that we can't get smaller than the mini button.
542 style->setMinHeight(Length(15, Fixed));
544 // Reset the top and bottom borders.
545 style->resetBorderTop();
546 style->resetBorderBottom();
550 const IntSize* RenderThemeSafari::buttonSizes() const
552 static const IntSize sizes[3] = { IntSize(0, 21), IntSize(0, 18), IntSize(0, 15) };
556 const int* RenderThemeSafari::buttonMargins(NSControlSize controlSize) const
558 static const int margins[3][4] =
564 return margins[controlSize];
567 void RenderThemeSafari::setButtonSize(RenderStyle* style) const
569 // If the width and height are both specified, then we have nothing to do.
570 if (!style->width().isIntrinsicOrAuto() && !style->height().isAuto())
573 // Use the font size to determine the intrinsic width of the control.
574 setSizeFromFont(style, buttonSizes());
577 bool RenderThemeSafari::paintButton(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r)
579 ASSERT(SafariThemeLibrary());
581 // We inflate the rect as needed to account for padding included in the cell to accommodate the button
582 // shadow. We don't consider this part of the bounds of the control in WebKit.
584 NSControlSize controlSize = controlSizeFromRect(r, buttonSizes());
585 IntRect inflatedRect = r;
588 if (r.height() <= buttonSizes()[NSRegularControlSize].height()) {
590 part = SafariTheme::PushButtonPart;
592 IntSize size = buttonSizes()[controlSize];
593 size.setWidth(r.width());
595 // Center the button within the available space.
596 if (inflatedRect.height() > size.height()) {
597 inflatedRect.setY(inflatedRect.y() + (inflatedRect.height() - size.height()) / 2);
598 inflatedRect.setHeight(size.height());
601 // Now inflate it to account for the shadow.
602 inflatedRect = inflateRect(inflatedRect, size, buttonMargins(controlSize));
604 part = SafariTheme::SquareButtonPart;
606 paintThemePart(part, paintInfo.context->platformContext(), inflatedRect, controlSize, determineState(o));
610 bool RenderThemeSafari::paintTextField(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r)
612 ASSERT(SafariThemeLibrary());
614 paintThemePart(SafariTheme::TextFieldPart, paintInfo.context->platformContext(), r, (NSControlSize)0, determineState(o) & ~FocusedState);
618 void RenderThemeSafari::adjustTextFieldStyle(StyleResolver*, RenderStyle*, Element*) const
622 bool RenderThemeSafari::paintCapsLockIndicator(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r)
624 #if defined(SAFARI_THEME_VERSION) && SAFARI_THEME_VERSION >= 1
625 ASSERT(SafariThemeLibrary());
627 if (paintInfo.context->paintingDisabled())
630 paintThemePart(CapsLockPart, paintInfo.context->platformContext(), r, (NSControlSize)0, (ThemeControlState)0);
638 bool RenderThemeSafari::paintTextArea(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r)
640 ASSERT(SafariThemeLibrary());
642 paintThemePart(SafariTheme::TextAreaPart, paintInfo.context->platformContext(), r, (NSControlSize)0, determineState(o) & ~FocusedState);
646 void RenderThemeSafari::adjustTextAreaStyle(StyleResolver*, RenderStyle*, Element*) const
650 const int* RenderThemeSafari::popupButtonMargins(NSControlSize size) const
652 static const int margins[3][4] =
658 return margins[size];
661 const IntSize* RenderThemeSafari::popupButtonSizes() const
663 static const IntSize sizes[3] = { IntSize(0, 21), IntSize(0, 18), IntSize(0, 15) };
667 const int* RenderThemeSafari::popupButtonPadding(NSControlSize size) const
669 static const int padding[3][4] =
675 return padding[size];
678 bool RenderThemeSafari::paintMenuList(RenderObject* o, const PaintInfo& info, const IntRect& r)
680 ASSERT(SafariThemeLibrary());
682 NSControlSize controlSize = controlSizeFromRect(r, popupButtonSizes());
683 IntRect inflatedRect = r;
684 IntSize size = popupButtonSizes()[controlSize];
685 size.setWidth(r.width());
687 // Now inflate it to account for the shadow.
688 if (r.width() >= minimumMenuListSize(o->style()))
689 inflatedRect = inflateRect(inflatedRect, size, popupButtonMargins(controlSize));
691 paintThemePart(DropDownButtonPart, info.context->platformContext(), inflatedRect, controlSize, determineState(o));
696 const float baseFontSize = 11.0f;
697 const float baseArrowHeight = 5.0f;
698 const float baseArrowWidth = 7.0f;
699 const int arrowPaddingLeft = 5;
700 const int arrowPaddingRight = 5;
701 const int paddingBeforeSeparator = 4;
702 const int baseBorderRadius = 5;
703 const int styledPopupPaddingLeft = 8;
704 const int styledPopupPaddingTop = 1;
705 const int styledPopupPaddingBottom = 2;
707 static void TopGradientInterpolate(void* info, const CGFloat* inData, CGFloat* outData)
709 static float dark[4] = { 1.0f, 1.0f, 1.0f, 0.4f };
710 static float light[4] = { 1.0f, 1.0f, 1.0f, 0.15f };
713 for (i = 0; i < 4; i++)
714 outData[i] = (1.0f - a) * dark[i] + a * light[i];
717 static void BottomGradientInterpolate(void* info, const CGFloat* inData, CGFloat* outData)
719 static float dark[4] = { 1.0f, 1.0f, 1.0f, 0.0f };
720 static float light[4] = { 1.0f, 1.0f, 1.0f, 0.3f };
723 for (i = 0; i < 4; i++)
724 outData[i] = (1.0f - a) * dark[i] + a * light[i];
727 static void MainGradientInterpolate(void* info, const CGFloat* inData, CGFloat* outData)
729 static float dark[4] = { 0.0f, 0.0f, 0.0f, 0.15f };
730 static float light[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
733 for (i = 0; i < 4; i++)
734 outData[i] = (1.0f - a) * dark[i] + a * light[i];
737 static void TrackGradientInterpolate(void* info, const CGFloat* inData, CGFloat* outData)
739 static float dark[4] = { 0.0f, 0.0f, 0.0f, 0.678f };
740 static float light[4] = { 0.0f, 0.0f, 0.0f, 0.13f };
743 for (i = 0; i < 4; i++)
744 outData[i] = (1.0f - a) * dark[i] + a * light[i];
747 void RenderThemeSafari::paintMenuListButtonGradients(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r)
752 CGContextRef context = paintInfo.context->platformContext();
754 paintInfo.context->save();
756 RoundedRect bound = o->style()->getRoundedBorderFor(r);
757 int radius = bound.radii().topLeft().width();
759 CGColorSpaceRef cspace = deviceRGBColorSpaceRef();
761 FloatRect topGradient(r.x(), r.y(), r.width(), r.height() / 2.0f);
762 struct CGFunctionCallbacks topCallbacks = { 0, TopGradientInterpolate, NULL };
763 RetainPtr<CGFunctionRef> topFunction(AdoptCF, CGFunctionCreate(NULL, 1, NULL, 4, NULL, &topCallbacks));
764 RetainPtr<CGShadingRef> topShading(AdoptCF, CGShadingCreateAxial(cspace, CGPointMake(topGradient.x(), topGradient.y()), CGPointMake(topGradient.x(), topGradient.maxY()), topFunction.get(), false, false));
766 FloatRect bottomGradient(r.x() + radius, r.y() + r.height() / 2.0f, r.width() - 2.0f * radius, r.height() / 2.0f);
767 struct CGFunctionCallbacks bottomCallbacks = { 0, BottomGradientInterpolate, NULL };
768 RetainPtr<CGFunctionRef> bottomFunction(AdoptCF, CGFunctionCreate(NULL, 1, NULL, 4, NULL, &bottomCallbacks));
769 RetainPtr<CGShadingRef> bottomShading(AdoptCF, CGShadingCreateAxial(cspace, CGPointMake(bottomGradient.x(), bottomGradient.y()), CGPointMake(bottomGradient.x(), bottomGradient.maxY()), bottomFunction.get(), false, false));
771 struct CGFunctionCallbacks mainCallbacks = { 0, MainGradientInterpolate, NULL };
772 RetainPtr<CGFunctionRef> mainFunction(AdoptCF, CGFunctionCreate(NULL, 1, NULL, 4, NULL, &mainCallbacks));
773 RetainPtr<CGShadingRef> mainShading(AdoptCF, CGShadingCreateAxial(cspace, CGPointMake(r.x(), r.y()), CGPointMake(r.x(), r.maxY()), mainFunction.get(), false, false));
775 RetainPtr<CGShadingRef> leftShading(AdoptCF, CGShadingCreateAxial(cspace, CGPointMake(r.x(), r.y()), CGPointMake(r.x() + radius, r.y()), mainFunction.get(), false, false));
777 RetainPtr<CGShadingRef> rightShading(AdoptCF, CGShadingCreateAxial(cspace, CGPointMake(r.maxX(), r.y()), CGPointMake(r.maxX() - radius, r.y()), mainFunction.get(), false, false));
778 paintInfo.context->save();
779 CGContextClipToRect(context, bound.rect());
780 paintInfo.context->addRoundedRectClip(bound);
781 CGContextDrawShading(context, mainShading.get());
782 paintInfo.context->restore();
784 paintInfo.context->save();
785 CGContextClipToRect(context, topGradient);
786 paintInfo.context->addRoundedRectClip(RoundedRect(enclosingIntRect(topGradient), bound.radii().topLeft(), bound.radii().topRight(), IntSize(), IntSize()));
787 CGContextDrawShading(context, topShading.get());
788 paintInfo.context->restore();
790 if (!bottomGradient.isEmpty()) {
791 paintInfo.context->save();
792 CGContextClipToRect(context, bottomGradient);
793 paintInfo.context->addRoundedRectClip(RoundedRect(enclosingIntRect(bottomGradient), IntSize(), IntSize(), bound.radii().bottomLeft(), bound.radii().bottomRight()));
794 CGContextDrawShading(context, bottomShading.get());
795 paintInfo.context->restore();
798 paintInfo.context->save();
799 CGContextClipToRect(context, bound.rect());
800 paintInfo.context->addRoundedRectClip(bound);
801 CGContextDrawShading(context, leftShading.get());
802 CGContextDrawShading(context, rightShading.get());
803 paintInfo.context->restore();
805 paintInfo.context->restore();
808 bool RenderThemeSafari::paintMenuListButton(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r)
810 IntRect bounds = IntRect(r.x() + o->style()->borderLeftWidth(),
811 r.y() + o->style()->borderTopWidth(),
812 r.width() - o->style()->borderLeftWidth() - o->style()->borderRightWidth(),
813 r.height() - o->style()->borderTopWidth() - o->style()->borderBottomWidth());
814 // Draw the gradients to give the styled popup menu a button appearance
815 paintMenuListButtonGradients(o, paintInfo, bounds);
817 // Since we actually know the size of the control here, we restrict the font scale to make sure the arrow will fit vertically in the bounds
818 float fontScale = min(o->style()->fontSize() / baseFontSize, bounds.height() / baseArrowHeight);
819 float centerY = bounds.y() + bounds.height() / 2.0f;
820 float arrowHeight = baseArrowHeight * fontScale;
821 float arrowWidth = baseArrowWidth * fontScale;
822 float leftEdge = bounds.maxX() - arrowPaddingRight - arrowWidth;
824 if (bounds.width() < arrowWidth + arrowPaddingLeft)
827 paintInfo.context->save();
829 paintInfo.context->setFillColor(o->style()->visitedDependentColor(CSSPropertyColor), ColorSpaceDeviceRGB);
830 paintInfo.context->setStrokeColor(NoStroke, ColorSpaceDeviceRGB);
833 arrow[0] = FloatPoint(leftEdge, centerY - arrowHeight / 2.0f);
834 arrow[1] = FloatPoint(leftEdge + arrowWidth, centerY - arrowHeight / 2.0f);
835 arrow[2] = FloatPoint(leftEdge + arrowWidth / 2.0f, centerY + arrowHeight / 2.0f);
838 paintInfo.context->drawConvexPolygon(3, arrow, true);
840 Color leftSeparatorColor(0, 0, 0, 40);
841 Color rightSeparatorColor(255, 255, 255, 40);
843 // FIXME: Should the separator thickness and space be scaled up by fontScale?
844 int separatorSpace = 2;
845 int leftEdgeOfSeparator = static_cast<int>(leftEdge - arrowPaddingLeft); // FIXME: Round?
847 // Draw the separator to the left of the arrows
848 paintInfo.context->setStrokeThickness(1.0f);
849 paintInfo.context->setStrokeStyle(SolidStroke);
850 paintInfo.context->setStrokeColor(leftSeparatorColor, ColorSpaceDeviceRGB);
851 paintInfo.context->drawLine(IntPoint(leftEdgeOfSeparator, bounds.y()),
852 IntPoint(leftEdgeOfSeparator, bounds.maxY()));
854 paintInfo.context->setStrokeColor(rightSeparatorColor, ColorSpaceDeviceRGB);
855 paintInfo.context->drawLine(IntPoint(leftEdgeOfSeparator + separatorSpace, bounds.y()),
856 IntPoint(leftEdgeOfSeparator + separatorSpace, bounds.maxY()));
858 paintInfo.context->restore();
862 void RenderThemeSafari::adjustMenuListStyle(StyleResolver* styleResolver, RenderStyle* style, Element* e) const
864 NSControlSize controlSize = controlSizeForFont(style);
866 style->resetBorder();
867 style->resetPadding();
869 // Height is locked to auto.
870 style->setHeight(Length(Auto));
872 // White-space is locked to pre
873 style->setWhiteSpace(PRE);
875 // Set the foreground color to black or gray when we have the aqua look.
876 // Cast to RGB32 is to work around a compiler bug.
877 style->setColor(e && e->isEnabledFormControl() ? static_cast<RGBA32>(Color::black) : Color::darkGray);
879 // Set the button's vertical size.
880 setButtonSize(style);
882 // 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
883 // a reasonable control size, but once that control size is determined, we throw that font away and use the appropriate
884 // system font for the control size instead.
885 setFontFromControlSize(styleResolver, style, controlSize);
888 int RenderThemeSafari::popupInternalPaddingLeft(RenderStyle* style) const
890 if (style->appearance() == MenulistPart)
891 return popupButtonPadding(controlSizeForFont(style))[leftPadding];
892 if (style->appearance() == MenulistButtonPart)
893 return styledPopupPaddingLeft;
897 int RenderThemeSafari::popupInternalPaddingRight(RenderStyle* style) const
899 if (style->appearance() == MenulistPart)
900 return popupButtonPadding(controlSizeForFont(style))[rightPadding];
901 if (style->appearance() == MenulistButtonPart) {
902 float fontScale = style->fontSize() / baseFontSize;
903 float arrowWidth = baseArrowWidth * fontScale;
904 return static_cast<int>(ceilf(arrowWidth + arrowPaddingLeft + arrowPaddingRight + paddingBeforeSeparator));
909 int RenderThemeSafari::popupInternalPaddingTop(RenderStyle* style) const
911 if (style->appearance() == MenulistPart)
912 return popupButtonPadding(controlSizeForFont(style))[topPadding];
913 if (style->appearance() == MenulistButtonPart)
914 return styledPopupPaddingTop;
918 int RenderThemeSafari::popupInternalPaddingBottom(RenderStyle* style) const
920 if (style->appearance() == MenulistPart)
921 return popupButtonPadding(controlSizeForFont(style))[bottomPadding];
922 if (style->appearance() == MenulistButtonPart)
923 return styledPopupPaddingBottom;
927 void RenderThemeSafari::adjustMenuListButtonStyle(StyleResolver*, RenderStyle* style, Element*) const
929 float fontScale = style->fontSize() / baseFontSize;
931 style->resetPadding();
932 style->setBorderRadius(IntSize(int(baseBorderRadius + fontScale - 1), int(baseBorderRadius + fontScale - 1))); // FIXME: Round up?
934 const int minHeight = 15;
935 style->setMinHeight(Length(minHeight, Fixed));
937 style->setLineHeight(RenderStyle::initialLineHeight());
940 const IntSize* RenderThemeSafari::menuListSizes() const
942 static const IntSize sizes[3] = { IntSize(9, 0), IntSize(5, 0), IntSize(0, 0) };
946 int RenderThemeSafari::minimumMenuListSize(RenderStyle* style) const
948 return sizeForSystemFont(style, menuListSizes()).width();
951 const int trackWidth = 5;
952 const int trackRadius = 2;
954 bool RenderThemeSafari::paintSliderTrack(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r)
956 IntSize radius(trackRadius, trackRadius);
957 RoundedRect bounds(r, radius, radius, radius, radius);
959 if (o->style()->appearance() == SliderHorizontalPart)
960 bounds.setRect(IntRect(r.x(),
961 r.y() + r.height() / 2 - trackWidth / 2,
964 else if (o->style()->appearance() == SliderVerticalPart)
965 bounds.setRect(IntRect(r.x() + r.width() / 2 - trackWidth / 2,
970 CGContextRef context = paintInfo.context->platformContext();
971 CGColorSpaceRef cspace = deviceRGBColorSpaceRef();
973 paintInfo.context->save();
974 CGContextClipToRect(context, bounds.rect());
976 struct CGFunctionCallbacks mainCallbacks = { 0, TrackGradientInterpolate, NULL };
977 RetainPtr<CGFunctionRef> mainFunction(AdoptCF, CGFunctionCreate(NULL, 1, NULL, 4, NULL, &mainCallbacks));
978 RetainPtr<CGShadingRef> mainShading;
979 if (o->style()->appearance() == SliderVerticalPart)
980 mainShading.adoptCF(CGShadingCreateAxial(cspace, CGPointMake(bounds.rect().x(), bounds.rect().maxY()), CGPointMake(bounds.rect().maxX(), bounds.rect().maxY()), mainFunction.get(), false, false));
982 mainShading.adoptCF(CGShadingCreateAxial(cspace, CGPointMake(bounds.rect().x(), bounds.rect().y()), CGPointMake(bounds.rect().x(), bounds.rect().maxY()), mainFunction.get(), false, false));
984 paintInfo.context->addRoundedRectClip(bounds);
985 CGContextDrawShading(context, mainShading.get());
986 paintInfo.context->restore();
991 void RenderThemeSafari::adjustSliderThumbStyle(StyleResolver* styleResolver, RenderStyle* style, Element* e) const
993 RenderTheme::adjustSliderThumbStyle(styleResolver, style, e);
994 style->setBoxShadow(nullptr);
997 const float verticalSliderHeightPadding = 0.1f;
999 bool RenderThemeSafari::paintSliderThumb(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r)
1001 ASSERT(SafariThemeLibrary());
1002 paintThemePart(SliderThumbPart, paintInfo.context->platformContext(), r, NSSmallControlSize, determineState(o));
1006 const int sliderThumbWidth = 15;
1007 const int sliderThumbHeight = 15;
1009 void RenderThemeSafari::adjustSliderThumbSize(RenderStyle* style, Element*) const
1011 if (style->appearance() == SliderThumbHorizontalPart || style->appearance() == SliderThumbVerticalPart) {
1012 style->setWidth(Length(sliderThumbWidth, Fixed));
1013 style->setHeight(Length(sliderThumbHeight, Fixed));
1016 else if (style->appearance() == MediaSliderThumbPart)
1017 RenderMediaControls::adjustMediaSliderThumbSize(style);
1021 bool RenderThemeSafari::paintSearchField(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r)
1023 ASSERT(SafariThemeLibrary());
1025 paintThemePart(SafariTheme::SearchFieldPart, paintInfo.context->platformContext(), r, controlSizeFromRect(r, searchFieldSizes()), determineState(o));
1029 const IntSize* RenderThemeSafari::searchFieldSizes() const
1031 static const IntSize sizes[3] = { IntSize(0, 22), IntSize(0, 19), IntSize(0, 15) };
1035 void RenderThemeSafari::setSearchFieldSize(RenderStyle* style) const
1037 // If the width and height are both specified, then we have nothing to do.
1038 if (!style->width().isIntrinsicOrAuto() && !style->height().isAuto())
1041 // Use the font size to determine the intrinsic width of the control.
1042 setSizeFromFont(style, searchFieldSizes());
1045 void RenderThemeSafari::adjustSearchFieldStyle(StyleResolver* styleResolver, RenderStyle* style, Element* e) const
1048 style->resetBorder();
1049 const short borderWidth = 2;
1050 style->setBorderLeftWidth(borderWidth);
1051 style->setBorderLeftStyle(INSET);
1052 style->setBorderRightWidth(borderWidth);
1053 style->setBorderRightStyle(INSET);
1054 style->setBorderBottomWidth(borderWidth);
1055 style->setBorderBottomStyle(INSET);
1056 style->setBorderTopWidth(borderWidth);
1057 style->setBorderTopStyle(INSET);
1060 style->setHeight(Length(Auto));
1061 setSearchFieldSize(style);
1063 // Override padding size to match AppKit text positioning.
1064 const int padding = 1;
1065 style->setPaddingLeft(Length(padding, Fixed));
1066 style->setPaddingRight(Length(padding, Fixed));
1067 style->setPaddingTop(Length(padding, Fixed));
1068 style->setPaddingBottom(Length(padding, Fixed));
1070 NSControlSize controlSize = controlSizeForFont(style);
1071 setFontFromControlSize(styleResolver, style, controlSize);
1074 bool RenderThemeSafari::paintSearchFieldCancelButton(RenderObject* o, const PaintInfo& paintInfo, const IntRect&)
1076 ASSERT(SafariThemeLibrary());
1078 Node* input = o->node()->shadowHost();
1081 RenderObject* renderer = input->renderer();
1084 IntRect searchRect = renderer->absoluteBoundingBoxRectIgnoringTransforms();
1086 paintThemePart(SafariTheme::SearchFieldCancelButtonPart, paintInfo.context->platformContext(), searchRect, controlSizeFromRect(searchRect, searchFieldSizes()), determineState(o));
1090 const IntSize* RenderThemeSafari::cancelButtonSizes() const
1092 static const IntSize sizes[3] = { IntSize(16, 13), IntSize(13, 11), IntSize(13, 9) };
1096 void RenderThemeSafari::adjustSearchFieldCancelButtonStyle(StyleResolver*, RenderStyle* style, Element*) const
1098 IntSize size = sizeForSystemFont(style, cancelButtonSizes());
1099 style->setWidth(Length(size.width(), Fixed));
1100 style->setHeight(Length(size.height(), Fixed));
1103 const IntSize* RenderThemeSafari::resultsButtonSizes() const
1105 static const IntSize sizes[3] = { IntSize(19, 13), IntSize(17, 11), IntSize(17, 9) };
1109 const int emptyResultsOffset = 9;
1110 void RenderThemeSafari::adjustSearchFieldDecorationStyle(StyleResolver*, RenderStyle* style, Element*) const
1112 IntSize size = sizeForSystemFont(style, resultsButtonSizes());
1113 style->setWidth(Length(size.width() - emptyResultsOffset, Fixed));
1114 style->setHeight(Length(size.height(), Fixed));
1117 bool RenderThemeSafari::paintSearchFieldDecoration(RenderObject*, const PaintInfo&, const IntRect&)
1122 void RenderThemeSafari::adjustSearchFieldResultsDecorationStyle(StyleResolver*, RenderStyle* style, Element*) const
1124 IntSize size = sizeForSystemFont(style, resultsButtonSizes());
1125 style->setWidth(Length(size.width(), Fixed));
1126 style->setHeight(Length(size.height(), Fixed));
1129 bool RenderThemeSafari::paintSearchFieldResultsDecoration(RenderObject* o, const PaintInfo& paintInfo, const IntRect&)
1131 ASSERT(SafariThemeLibrary());
1133 Node* input = o->node()->shadowHost();
1136 RenderObject* renderer = input->renderer();
1139 IntRect searchRect = renderer->absoluteBoundingBoxRectIgnoringTransforms();
1141 paintThemePart(SafariTheme::SearchFieldResultsDecorationPart, paintInfo.context->platformContext(), searchRect, controlSizeFromRect(searchRect, searchFieldSizes()), determineState(o));
1145 const int resultsArrowWidth = 5;
1146 void RenderThemeSafari::adjustSearchFieldResultsButtonStyle(StyleResolver*, RenderStyle* style, Element*) const
1148 IntSize size = sizeForSystemFont(style, resultsButtonSizes());
1149 style->setWidth(Length(size.width() + resultsArrowWidth, Fixed));
1150 style->setHeight(Length(size.height(), Fixed));
1153 bool RenderThemeSafari::paintSearchFieldResultsButton(RenderObject* o, const PaintInfo& paintInfo, const IntRect&)
1155 ASSERT(SafariThemeLibrary());
1157 Node* input = o->node()->shadowHost();
1160 RenderObject* renderer = input->renderer();
1163 IntRect searchRect = renderer->absoluteBoundingBoxRectIgnoringTransforms();
1165 paintThemePart(SafariTheme::SearchFieldResultsButtonPart, paintInfo.context->platformContext(), searchRect, controlSizeFromRect(searchRect, searchFieldSizes()), determineState(o));
1169 bool RenderThemeSafari::paintMediaFullscreenButton(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r)
1171 return RenderMediaControls::paintMediaControlsPart(MediaEnterFullscreenButton, o, paintInfo, r);
1174 bool RenderThemeSafari::paintMediaMuteButton(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r)
1176 return RenderMediaControls::paintMediaControlsPart(MediaMuteButton, o, paintInfo, r);
1179 bool RenderThemeSafari::paintMediaPlayButton(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r)
1181 return RenderMediaControls::paintMediaControlsPart(MediaPlayButton, o, paintInfo, r);
1184 bool RenderThemeSafari::paintMediaSeekBackButton(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r)
1186 return RenderMediaControls::paintMediaControlsPart(MediaSeekBackButton, o, paintInfo, r);
1189 bool RenderThemeSafari::paintMediaSeekForwardButton(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r)
1191 return RenderMediaControls::paintMediaControlsPart(MediaSeekForwardButton, o, paintInfo, r);
1194 bool RenderThemeSafari::paintMediaSliderTrack(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r)
1196 return RenderMediaControls::paintMediaControlsPart(MediaSlider, o, paintInfo, r);
1199 bool RenderThemeSafari::paintMediaSliderThumb(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r)
1201 return RenderMediaControls::paintMediaControlsPart(MediaSliderThumb, o, paintInfo, r);
1205 } // namespace WebCore
1207 #endif // #if USE(SAFARI_THEME)