2 * Copyright (C) 2007 Apple Inc.
3 * Copyright (C) 2007 Alp Toker <alp@atoker.com>
4 * Copyright (C) 2008 Collabora Ltd.
5 * Copyright (C) 2008, 2009 Google Inc.
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Library General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Library General Public License for more details.
17 * You should have received a copy of the GNU Library General Public License
18 * along with this library; see the file COPYING.LIB. If not, write to
19 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20 * Boston, MA 02110-1301, USA.
25 #include "RenderThemeChromiumSkia.h"
27 #include "ChromiumBridge.h"
28 #include "CSSValueKeywords.h"
29 #include "GraphicsContext.h"
30 #include "HTMLMediaElement.h"
31 #include "HTMLNames.h"
33 #include "MediaControlElements.h"
34 #include "PlatformContextSkia.h"
35 #include "RenderBox.h"
36 #include "RenderObject.h"
37 #include "ScrollbarTheme.h"
38 #include "TransformationMatrix.h"
39 #include "UserAgentStyleSheets.h"
42 #include "SkGradientShader.h"
53 static const int styledMenuListInternalPadding
[4] = { 1, 4, 1, 4 };
55 // The background for the media player controls should be a 60% opaque black rectangle. This
56 // matches the UI mockups for the default UI theme.
57 static const float defaultMediaControlOpacity
= 0.6f
;
59 // These values all match Safari/Win.
60 static const float defaultControlFontPixelSize
= 13;
61 static const float defaultCancelButtonSize
= 9;
62 static const float minCancelButtonSize
= 5;
63 static const float maxCancelButtonSize
= 21;
64 static const float defaultSearchFieldResultsDecorationSize
= 13;
65 static const float minSearchFieldResultsDecorationSize
= 9;
66 static const float maxSearchFieldResultsDecorationSize
= 30;
67 static const float defaultSearchFieldResultsButtonWidth
= 18;
69 static void setSizeIfAuto(RenderStyle
* style
, const IntSize
& size
)
71 if (style
->width().isIntrinsicOrAuto())
72 style
->setWidth(Length(size
.width(), Fixed
));
73 if (style
->height().isAuto())
74 style
->setHeight(Length(size
.height(), Fixed
));
78 // Attempt to retrieve a HTMLMediaElement from a Node. Returns NULL if one cannot be found.
79 static HTMLMediaElement
* mediaElementParent(Node
* node
)
83 Node
* mediaNode
= node
->shadowAncestorNode();
84 if (!mediaNode
|| (!mediaNode
->hasTagName(HTMLNames::videoTag
) && !mediaNode
->hasTagName(HTMLNames::audioTag
)))
87 return static_cast<HTMLMediaElement
*>(mediaNode
);
91 // We aim to match IE here.
92 // -IE uses a font based on the encoding as the default font for form controls.
93 // -Gecko uses MS Shell Dlg (actually calls GetStockObject(DEFAULT_GUI_FONT),
94 // which returns MS Shell Dlg)
95 // -Safari uses Lucida Grande.
97 // FIXME: The only case where we know we don't match IE is for ANSI encodings.
98 // IE uses MS Shell Dlg there, which we render incorrectly at certain pixel
99 // sizes (e.g. 15px). So, for now we just use Arial.
100 const String
& RenderThemeChromiumSkia::defaultGUIFont()
102 DEFINE_STATIC_LOCAL(String
, fontFace
, ("Arial"));
106 float RenderThemeChromiumSkia::defaultFontSize
= 16.0;
108 RenderThemeChromiumSkia::RenderThemeChromiumSkia()
112 RenderThemeChromiumSkia::~RenderThemeChromiumSkia()
116 // Use the Windows style sheets to match their metrics.
117 String
RenderThemeChromiumSkia::extraDefaultStyleSheet()
119 return String(themeWinUserAgentStyleSheet
, sizeof(themeWinUserAgentStyleSheet
));
122 String
RenderThemeChromiumSkia::extraQuirksStyleSheet()
124 return String(themeWinQuirksUserAgentStyleSheet
, sizeof(themeWinQuirksUserAgentStyleSheet
));
128 String
RenderThemeChromiumSkia::extraMediaControlsStyleSheet()
130 return String(mediaControlsChromiumUserAgentStyleSheet
, sizeof(mediaControlsChromiumUserAgentStyleSheet
));
134 bool RenderThemeChromiumSkia::supportsHover(const RenderStyle
* style
) const
139 bool RenderThemeChromiumSkia::supportsFocusRing(const RenderStyle
* style
) const
141 // This causes WebKit to draw the focus rings for us.
145 Color
RenderThemeChromiumSkia::platformActiveSelectionBackgroundColor() const
147 return Color(0x1e, 0x90, 0xff);
150 Color
RenderThemeChromiumSkia::platformInactiveSelectionBackgroundColor() const
152 return Color(0xc8, 0xc8, 0xc8);
155 Color
RenderThemeChromiumSkia::platformActiveSelectionForegroundColor() const
160 Color
RenderThemeChromiumSkia::platformInactiveSelectionForegroundColor() const
162 return Color(0x32, 0x32, 0x32);
165 Color
RenderThemeChromiumSkia::focusRingColor() const
167 static Color
focusRingColor(229, 151, 0, 255);
168 return focusRingColor
;
171 double RenderThemeChromiumSkia::caretBlinkInterval() const
173 // Disable the blinking caret in layout test mode, as it introduces
174 // a race condition for the pixel tests. http://b/1198440
175 if (ChromiumBridge::layoutTestMode())
178 return caretBlinkIntervalInternal();
181 void RenderThemeChromiumSkia::systemFont(int propId
, FontDescription
& fontDescription
) const
183 float fontSize
= defaultFontSize
;
186 case CSSValueWebkitMiniControl
:
187 case CSSValueWebkitSmallControl
:
188 case CSSValueWebkitControl
:
189 // Why 2 points smaller? Because that's what Gecko does. Note that we
190 // are assuming a 96dpi screen, which is the default that we use on
192 static const float pointsPerInch
= 72.0f
;
193 static const float pixelsPerInch
= 96.0f
;
194 fontSize
-= (2.0f
/ pointsPerInch
) * pixelsPerInch
;
198 fontDescription
.firstFamily().setFamily(defaultGUIFont());
199 fontDescription
.setSpecifiedSize(fontSize
);
200 fontDescription
.setIsAbsoluteSize(true);
201 fontDescription
.setGenericFamily(FontDescription::NoFamily
);
202 fontDescription
.setWeight(FontWeightNormal
);
203 fontDescription
.setItalic(false);
206 int RenderThemeChromiumSkia::minimumMenuListSize(RenderStyle
* style
) const
211 bool RenderThemeChromiumSkia::paintCheckbox(RenderObject
* o
, const RenderObject::PaintInfo
& i
, const IntRect
& rect
)
213 static Image
* const checkedImage
= Image::loadPlatformResource("linuxCheckboxOn").releaseRef();
214 static Image
* const uncheckedImage
= Image::loadPlatformResource("linuxCheckboxOff").releaseRef();
216 Image
* image
= this->isChecked(o
) ? checkedImage
: uncheckedImage
;
217 i
.context
->drawImage(image
, rect
);
221 void RenderThemeChromiumSkia::setCheckboxSize(RenderStyle
* style
) const
223 // If the width and height are both specified, then we have nothing to do.
224 if (!style
->width().isIntrinsicOrAuto() && !style
->height().isAuto())
227 // FIXME: A hard-coded size of 13 is used. This is wrong but necessary
228 // for now. It matches Firefox. At different DPI settings on Windows,
229 // querying the theme gives you a larger size that accounts for the higher
230 // DPI. Until our entire engine honors a DPI setting other than 96, we
231 // can't rely on the theme's metrics.
232 const IntSize
size(13, 13);
233 setSizeIfAuto(style
, size
);
236 bool RenderThemeChromiumSkia::paintRadio(RenderObject
* o
, const RenderObject::PaintInfo
& i
, const IntRect
& rect
)
238 static Image
* const checkedImage
= Image::loadPlatformResource("linuxRadioOn").releaseRef();
239 static Image
* const uncheckedImage
= Image::loadPlatformResource("linuxRadioOff").releaseRef();
241 Image
* image
= this->isChecked(o
) ? checkedImage
: uncheckedImage
;
242 i
.context
->drawImage(image
, rect
);
246 void RenderThemeChromiumSkia::setRadioSize(RenderStyle
* style
) const
248 // Use same sizing for radio box as checkbox.
249 setCheckboxSize(style
);
252 static SkColor
brightenColor(double h
, double s
, double l
, float brightenAmount
)
260 return makeRGBAFromHSLA(h
, s
, l
, 1.0);
263 static void paintButtonLike(RenderTheme
* theme
, RenderObject
* o
, const RenderObject::PaintInfo
& i
, const IntRect
& rect
)
265 SkCanvas
* const canvas
= i
.context
->platformContext()->canvas();
268 const int right
= rect
.x() + rect
.width();
269 const int bottom
= rect
.y() + rect
.height();
270 SkColor baseColor
= SkColorSetARGB(0xff, 0xdd, 0xdd, 0xdd);
271 if (o
->style()->hasBackground())
272 baseColor
= o
->style()->backgroundColor().rgb();
274 Color(baseColor
).getHSL(h
, s
, l
);
275 // Our standard gradient is from 0xdd to 0xf8. This is the amount of
276 // increased luminance between those values.
277 SkColor
lightColor(brightenColor(h
, s
, l
, 0.105));
279 // If the button is too small, fallback to drawing a single, solid color
280 if (rect
.width() < 5 || rect
.height() < 5) {
281 paint
.setColor(baseColor
);
282 skrect
.set(rect
.x(), rect
.y(), right
, bottom
);
283 canvas
->drawRect(skrect
, paint
);
287 const int borderAlpha
= theme
->isHovered(o
) ? 0x80 : 0x55;
288 paint
.setARGB(borderAlpha
, 0, 0, 0);
289 canvas
->drawLine(rect
.x() + 1, rect
.y(), right
- 1, rect
.y(), paint
);
290 canvas
->drawLine(right
- 1, rect
.y() + 1, right
- 1, bottom
- 1, paint
);
291 canvas
->drawLine(rect
.x() + 1, bottom
- 1, right
- 1, bottom
- 1, paint
);
292 canvas
->drawLine(rect
.x(), rect
.y() + 1, rect
.x(), bottom
- 1, paint
);
294 paint
.setARGB(0xff, 0, 0, 0);
296 const int lightEnd
= theme
->isPressed(o
) ? 1 : 0;
297 const int darkEnd
= !lightEnd
;
298 p
[lightEnd
].set(SkIntToScalar(rect
.x()), SkIntToScalar(rect
.y()));
299 p
[darkEnd
].set(SkIntToScalar(rect
.x()), SkIntToScalar(bottom
- 1));
301 colors
[0] = lightColor
;
302 colors
[1] = baseColor
;
304 SkShader
* shader
= SkGradientShader::CreateLinear(
305 p
, colors
, NULL
, 2, SkShader::kClamp_TileMode
, NULL
);
306 paint
.setStyle(SkPaint::kFill_Style
);
307 paint
.setShader(shader
);
310 skrect
.set(rect
.x() + 1, rect
.y() + 1, right
- 1, bottom
- 1);
311 canvas
->drawRect(skrect
, paint
);
313 paint
.setShader(NULL
);
314 paint
.setColor(brightenColor(h
, s
, l
, -0.0588));
315 canvas
->drawPoint(rect
.x() + 1, rect
.y() + 1, paint
);
316 canvas
->drawPoint(right
- 2, rect
.y() + 1, paint
);
317 canvas
->drawPoint(rect
.x() + 1, bottom
- 2, paint
);
318 canvas
->drawPoint(right
- 2, bottom
- 2, paint
);
321 bool RenderThemeChromiumSkia::paintButton(RenderObject
* o
, const RenderObject::PaintInfo
& i
, const IntRect
& rect
)
323 paintButtonLike(this, o
, i
, rect
);
327 bool RenderThemeChromiumSkia::paintTextField(RenderObject
* o
, const RenderObject::PaintInfo
& i
, const IntRect
& rect
)
332 bool RenderThemeChromiumSkia::paintTextArea(RenderObject
* o
, const RenderObject::PaintInfo
& i
, const IntRect
& r
)
334 return paintTextField(o
, i
, r
);
337 bool RenderThemeChromiumSkia::paintSearchField(RenderObject
* o
, const RenderObject::PaintInfo
& i
, const IntRect
& r
)
339 return paintTextField(o
, i
, r
);
342 void RenderThemeChromiumSkia::adjustSearchFieldCancelButtonStyle(CSSStyleSelector
*, RenderStyle
* style
, Element
*) const
344 // Scale the button size based on the font size
345 float fontScale
= style
->fontSize() / defaultControlFontPixelSize
;
346 int cancelButtonSize
= lroundf(std::min(std::max(minCancelButtonSize
, defaultCancelButtonSize
* fontScale
), maxCancelButtonSize
));
347 style
->setWidth(Length(cancelButtonSize
, Fixed
));
348 style
->setHeight(Length(cancelButtonSize
, Fixed
));
351 bool RenderThemeChromiumSkia::paintSearchFieldCancelButton(RenderObject
* o
, const RenderObject::PaintInfo
& i
, const IntRect
& r
)
355 if (!o
->parent() || !o
->parent()->isBox())
358 RenderBox
* parentRenderBox
= toRenderBox(o
->parent());
360 IntRect parentBox
= parentRenderBox
->absoluteContentBox();
362 // Make sure the scaled button stays square and will fit in its parent's box
363 bounds
.setHeight(std::min(parentBox
.width(), std::min(parentBox
.height(), bounds
.height())));
364 bounds
.setWidth(bounds
.height());
366 // Center the button vertically. Round up though, so if it has to be one pixel off-center, it will
367 // be one pixel closer to the bottom of the field. This tends to look better with the text.
368 bounds
.setY(parentBox
.y() + (parentBox
.height() - bounds
.height() + 1) / 2);
370 static Image
* cancelImage
= Image::loadPlatformResource("searchCancel").releaseRef();
371 static Image
* cancelPressedImage
= Image::loadPlatformResource("searchCancelPressed").releaseRef();
372 i
.context
->drawImage(isPressed(o
) ? cancelPressedImage
: cancelImage
, bounds
);
376 void RenderThemeChromiumSkia::adjustSearchFieldDecorationStyle(CSSStyleSelector
*, RenderStyle
* style
, Element
*) const
378 IntSize
emptySize(1, 11);
379 style
->setWidth(Length(emptySize
.width(), Fixed
));
380 style
->setHeight(Length(emptySize
.height(), Fixed
));
383 void RenderThemeChromiumSkia::adjustSearchFieldResultsDecorationStyle(CSSStyleSelector
*, RenderStyle
* style
, Element
*) const
385 // Scale the decoration size based on the font size
386 float fontScale
= style
->fontSize() / defaultControlFontPixelSize
;
387 int magnifierSize
= lroundf(std::min(std::max(minSearchFieldResultsDecorationSize
, defaultSearchFieldResultsDecorationSize
* fontScale
),
388 maxSearchFieldResultsDecorationSize
));
389 style
->setWidth(Length(magnifierSize
, Fixed
));
390 style
->setHeight(Length(magnifierSize
, Fixed
));
393 bool RenderThemeChromiumSkia::paintSearchFieldResultsDecoration(RenderObject
* o
, const RenderObject::PaintInfo
& i
, const IntRect
& r
)
397 if (!o
->parent() || !o
->parent()->isBox())
400 RenderBox
* parentRenderBox
= toRenderBox(o
->parent());
401 IntRect parentBox
= parentRenderBox
->absoluteContentBox();
403 // Make sure the scaled decoration stays square and will fit in its parent's box
404 bounds
.setHeight(std::min(parentBox
.width(), std::min(parentBox
.height(), bounds
.height())));
405 bounds
.setWidth(bounds
.height());
407 // Center the decoration vertically. Round up though, so if it has to be one pixel off-center, it will
408 // be one pixel closer to the bottom of the field. This tends to look better with the text.
409 bounds
.setY(parentBox
.y() + (parentBox
.height() - bounds
.height() + 1) / 2);
411 static Image
* magnifierImage
= Image::loadPlatformResource("searchMagnifier").releaseRef();
412 i
.context
->drawImage(magnifierImage
, bounds
);
416 void RenderThemeChromiumSkia::adjustSearchFieldResultsButtonStyle(CSSStyleSelector
*, RenderStyle
* style
, Element
*) const
418 // Scale the button size based on the font size
419 float fontScale
= style
->fontSize() / defaultControlFontPixelSize
;
420 int magnifierHeight
= lroundf(std::min(std::max(minSearchFieldResultsDecorationSize
, defaultSearchFieldResultsDecorationSize
* fontScale
),
421 maxSearchFieldResultsDecorationSize
));
422 int magnifierWidth
= lroundf(magnifierHeight
* defaultSearchFieldResultsButtonWidth
/ defaultSearchFieldResultsDecorationSize
);
423 style
->setWidth(Length(magnifierWidth
, Fixed
));
424 style
->setHeight(Length(magnifierHeight
, Fixed
));
427 bool RenderThemeChromiumSkia::paintSearchFieldResultsButton(RenderObject
* o
, const RenderObject::PaintInfo
& i
, const IntRect
& r
)
433 if (!o
->parent() || !o
->parent()->isBox())
436 RenderBox
* parentRenderBox
= toRenderBox(o
->parent());
437 IntRect parentBox
= parentRenderBox
->absoluteContentBox();
439 // Make sure the scaled decoration will fit in its parent's box
440 bounds
.setHeight(std::min(parentBox
.height(), bounds
.height()));
441 bounds
.setWidth(std::min(parentBox
.width(), static_cast<int>(bounds
.height() * defaultSearchFieldResultsButtonWidth
/ defaultSearchFieldResultsDecorationSize
)));
443 // Center the button vertically. Round up though, so if it has to be one pixel off-center, it will
444 // be one pixel closer to the bottom of the field. This tends to look better with the text.
445 bounds
.setY(parentBox
.y() + (parentBox
.height() - bounds
.height() + 1) / 2);
447 static Image
* magnifierImage
= Image::loadPlatformResource("searchMagnifierResults").releaseRef();
448 i
.context
->drawImage(magnifierImage
, bounds
);
452 bool RenderThemeChromiumSkia::paintMediaButtonInternal(GraphicsContext
* context
, const IntRect
& rect
, Image
* image
)
454 context
->beginTransparencyLayer(defaultMediaControlOpacity
);
457 Color oldFill
= context
->fillColor();
458 Color oldStroke
= context
->strokeColor();
460 context
->setFillColor(Color::black
);
461 context
->setStrokeColor(Color::black
);
462 context
->drawRect(rect
);
464 context
->setFillColor(oldFill
);
465 context
->setStrokeColor(oldStroke
);
467 // Create a destination rectangle for the image that is centered in the drawing rectangle, rounded left, and down.
468 IntRect imageRect
= image
->rect();
469 imageRect
.setY(rect
.y() + (rect
.height() - image
->height() + 1) / 2);
470 imageRect
.setX(rect
.x() + (rect
.width() - image
->width() + 1) / 2);
472 context
->drawImage(image
, imageRect
, CompositeSourceAtop
);
473 context
->endTransparencyLayer();
478 bool RenderThemeChromiumSkia::paintMediaPlayButton(RenderObject
* o
, const RenderObject::PaintInfo
& paintInfo
, const IntRect
& rect
)
481 HTMLMediaElement
* mediaElement
= mediaElementParent(o
->node());
485 static Image
* mediaPlay
= Image::loadPlatformResource("mediaPlay").releaseRef();
486 static Image
* mediaPause
= Image::loadPlatformResource("mediaPause").releaseRef();
488 return paintMediaButtonInternal(paintInfo
.context
, rect
, mediaElement
->paused() ? mediaPlay
: mediaPause
);
494 bool RenderThemeChromiumSkia::paintMediaMuteButton(RenderObject
* o
, const RenderObject::PaintInfo
& paintInfo
, const IntRect
& rect
)
497 HTMLMediaElement
* mediaElement
= mediaElementParent(o
->node());
501 static Image
* soundFull
= Image::loadPlatformResource("mediaSoundFull").releaseRef();
502 static Image
* soundNone
= Image::loadPlatformResource("mediaSoundNone").releaseRef();
504 return paintMediaButtonInternal(paintInfo
.context
, rect
, mediaElement
->muted() ? soundNone
: soundFull
);
510 void RenderThemeChromiumSkia::adjustMenuListStyle(CSSStyleSelector
* selector
, RenderStyle
* style
, WebCore::Element
* e
) const
512 // Height is locked to auto on all browsers.
513 style
->setLineHeight(RenderStyle::initialLineHeight());
516 bool RenderThemeChromiumSkia::paintMenuList(RenderObject
* o
, const RenderObject::PaintInfo
& i
, const IntRect
& rect
)
518 SkCanvas
* const canvas
= i
.context
->platformContext()->canvas();
519 const int right
= rect
.x() + rect
.width();
520 const int middle
= rect
.y() + rect
.height() / 2;
522 paintButtonLike(this, o
, i
, rect
);
525 paint
.setARGB(0xff, 0, 0, 0);
526 paint
.setAntiAlias(true);
527 paint
.setStyle(SkPaint::kFill_Style
);
530 path
.moveTo(right
- 13, middle
- 3);
534 canvas
->drawPath(path
, paint
);
539 void RenderThemeChromiumSkia::adjustMenuListButtonStyle(CSSStyleSelector
* selector
, RenderStyle
* style
, Element
* e
) const
541 adjustMenuListStyle(selector
, style
, e
);
544 // Used to paint styled menulists (i.e. with a non-default border)
545 bool RenderThemeChromiumSkia::paintMenuListButton(RenderObject
* o
, const RenderObject::PaintInfo
& i
, const IntRect
& rect
)
547 return paintMenuList(o
, i
, rect
);
550 int RenderThemeChromiumSkia::popupInternalPaddingLeft(RenderStyle
* style
) const
552 return menuListInternalPadding(style
, LeftPadding
);
555 int RenderThemeChromiumSkia::popupInternalPaddingRight(RenderStyle
* style
) const
557 return menuListInternalPadding(style
, RightPadding
);
560 int RenderThemeChromiumSkia::popupInternalPaddingTop(RenderStyle
* style
) const
562 return menuListInternalPadding(style
, TopPadding
);
565 int RenderThemeChromiumSkia::popupInternalPaddingBottom(RenderStyle
* style
) const
567 return menuListInternalPadding(style
, BottomPadding
);
570 int RenderThemeChromiumSkia::buttonInternalPaddingLeft() const
575 int RenderThemeChromiumSkia::buttonInternalPaddingRight() const
580 int RenderThemeChromiumSkia::buttonInternalPaddingTop() const
585 int RenderThemeChromiumSkia::buttonInternalPaddingBottom() const
591 void RenderThemeChromiumSkia::setDefaultFontSize(int fontSize
)
593 defaultFontSize
= static_cast<float>(fontSize
);
596 double RenderThemeChromiumSkia::caretBlinkIntervalInternal() const
598 return RenderTheme::caretBlinkInterval();
601 int RenderThemeChromiumSkia::menuListInternalPadding(RenderStyle
* style
, int paddingType
) const
603 // This internal padding is in addition to the user-supplied padding.
604 // Matches the FF behavior.
605 int padding
= styledMenuListInternalPadding
[paddingType
];
607 // Reserve the space for right arrow here. The rest of the padding is
608 // set by adjustMenuListStyle, since PopMenuWin.cpp uses the padding from
609 // RenderMenuList to lay out the individual items in the popup.
610 // If the MenuList actually has appearance "NoAppearance", then that means
611 // we don't draw a button, so don't reserve space for it.
612 const int barType
= style
->direction() == LTR
? RightPadding
: LeftPadding
;
613 if (paddingType
== barType
&& style
->appearance() != NoControlPart
)
614 padding
+= ScrollbarTheme::nativeTheme()->scrollbarThickness();
619 } // namespace WebCore