/*
* This file is part of the WebKit project.
*
* Copyright (C) 2006 Apple Computer, Inc.
* Copyright (C) 2008, 2009 Google, Inc.
* Copyright (C) 2009 Kenneth Rohde Christiansen
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*
*/
#include "config.h"
#include "RenderThemeChromiumWin.h"
#include <windows.h>
#include <uxtheme.h>
#include <vssym32.h>
#include "CSSValueKeywords.h"
#include "CurrentTime.h"
#include "FontSelector.h"
#include "FontUtilsChromiumWin.h"
#include "GraphicsContext.h"
#include "HTMLMediaElement.h"
#include "HTMLNames.h"
#include "MediaControlElements.h"
#include "PaintInfo.h"
#include "PlatformBridge.h"
#include "RenderBox.h"
#include "RenderProgress.h"
#include "RenderSlider.h"
#include "ScrollbarTheme.h"
#include "SystemInfo.h"
#include "TransparencyWin.h"
// FIXME: This dependency should eventually be removed.
#include <skia/ext/skia_utils_win.h>
#define SIZEOF_STRUCT_WITH_SPECIFIED_LAST_MEMBER(structName, member) \
offsetof(structName, member) + \
(sizeof static_cast<structName*>(0)->member)
#define NONCLIENTMETRICS_SIZE_PRE_VISTA \
SIZEOF_STRUCT_WITH_SPECIFIED_LAST_MEMBER(NONCLIENTMETRICS, lfMessageFont)
namespace WebCore {
// The standard width for the menu list drop-down button when run under
// layout test mode. Use the value that's currently captured in most baselines.
static const int kStandardMenuListButtonWidth = 17;
namespace {
// We must not create multiple ThemePainter instances.
class ThemePainter {
public:
ThemePainter(GraphicsContext* context, const IntRect& r)
{
#ifndef NDEBUG
ASSERT(!s_hasInstance);
s_hasInstance = true;
#endif
TransparencyWin::TransformMode transformMode = getTransformMode(context->getCTM());
m_helper.init(context, getLayerMode(context, transformMode), transformMode, r);
if (!m_helper.context()) {
// TransparencyWin doesn't have well-defined copy-ctor nor op=()
// so we re-initialize it instead of assigning a fresh istance.
// On the reinitialization, we fallback to use NoLayer mode.
// Note that the original initialization failure can be caused by
// a failure of an internal buffer allocation and NoLayer mode
// does not have such buffer allocations.
m_helper.~TransparencyWin();
new (&m_helper) TransparencyWin();
m_helper.init(context, TransparencyWin::NoLayer, transformMode, r);
}
}
~ThemePainter()
{
m_helper.composite();
#ifndef NDEBUG
s_hasInstance = false;
#endif
}
GraphicsContext* context() { return m_helper.context(); }
const IntRect& drawRect() { return m_helper.drawRect(); }
private:
static bool canvasHasMultipleLayers(const SkCanvas* canvas)
{
SkCanvas::LayerIter iter(const_cast<SkCanvas*>(canvas), false);
iter.next(); // There is always at least one layer.
return !iter.done(); // There is > 1 layer if the the iterator can stil advance.
}
static TransparencyWin::LayerMode getLayerMode(GraphicsContext* context, TransparencyWin::TransformMode transformMode)
{
if (context->platformContext()->isDrawingToImageBuffer()) // Might have transparent background.
return TransparencyWin::WhiteLayer;
if (canvasHasMultipleLayers(context->platformContext()->canvas())) // Needs antialiasing help.
return TransparencyWin::OpaqueCompositeLayer;
// Nothing interesting.
return transformMode == TransparencyWin::KeepTransform ? TransparencyWin::NoLayer : TransparencyWin::OpaqueCompositeLayer;
}
static TransparencyWin::TransformMode getTransformMode(const AffineTransform& matrix)
{
if (matrix.b() || matrix.c()) // Skew.
return TransparencyWin::Untransform;
if (matrix.a() != 1.0 || matrix.d() != 1.0) // Scale.
return TransparencyWin::ScaleTransform;
// Nothing interesting.
return TransparencyWin::KeepTransform;
}
TransparencyWin m_helper;
#ifndef NDEBUG
static bool s_hasInstance;
#endif
};
#ifndef NDEBUG
bool ThemePainter::s_hasInstance = false;
#endif
} // namespace
static void getNonClientMetrics(NONCLIENTMETRICS* metrics)
{
static UINT size = (windowsVersion() >= WindowsVista) ?
(sizeof NONCLIENTMETRICS) : NONCLIENTMETRICS_SIZE_PRE_VISTA;
metrics->cbSize = size;
bool success = !!SystemParametersInfo(SPI_GETNONCLIENTMETRICS, size, metrics, 0);
ASSERT(success);
}
static FontDescription smallSystemFont;
static FontDescription menuFont;
static FontDescription labelFont;
// Internal static helper functions. We don't put them in an anonymous
// namespace so they have easier access to the WebCore namespace.
static bool supportsFocus(ControlPart appearance)
{
switch (appearance) {
case PushButtonPart:
case ButtonPart:
case DefaultButtonPart:
case SearchFieldPart:
case TextFieldPart:
case TextAreaPart:
return true;
}
return false;
}
// Return the height of system font |font| in pixels. We use this size by
// default for some non-form-control elements.
static float systemFontSize(const LOGFONT& font)
{
float size = -font.lfHeight;
if (size < 0) {
HFONT hFont = CreateFontIndirect(&font);
if (hFont) {
HDC hdc = GetDC(0); // What about printing? Is this the right DC?
if (hdc) {
HGDIOBJ hObject = SelectObject(hdc, hFont);
TEXTMETRIC tm;
GetTextMetrics(hdc, &tm);
SelectObject(hdc, hObject);
ReleaseDC(0, hdc);
size = tm.tmAscent;
}
DeleteObject(hFont);
}
}
// The "codepage 936" bit here is from Gecko; apparently this helps make
// fonts more legible in Simplified Chinese where the default font size is
// too small.
//
// FIXME: http://b/1119883 Since this is only used for "small caption",
// "menu", and "status bar" objects, I'm not sure how much this even
// matters. Plus the Gecko patch went in back in 2002, and maybe this
// isn't even relevant anymore. We should investigate whether this should
// be removed, or perhaps broadened to be "any CJK locale".
//
return ((size < 12.0f) && (GetACP() == 936)) ? 12.0f : size;
}
// Converts |points| to pixels. One point is 1/72 of an inch.
static float pointsToPixels(float points)
{
static float pixelsPerInch = 0.0f;
if (!pixelsPerInch) {
HDC hdc = GetDC(0); // What about printing? Is this the right DC?
if (hdc) { // Can this ever actually be NULL?
pixelsPerInch = GetDeviceCaps(hdc, LOGPIXELSY);
ReleaseDC(0, hdc);
} else {
pixelsPerInch = 96.0f;
}
}
static const float pointsPerInch = 72.0f;
return points / pointsPerInch * pixelsPerInch;
}
static double querySystemBlinkInterval(double defaultInterval)
{
UINT blinkTime = GetCaretBlinkTime();
if (!blinkTime)
return defaultInterval;
if (blinkTime == INFINITE)
return 0;
return blinkTime / 1000.0;
}
PassRefPtr<RenderTheme> RenderThemeChromiumWin::create()
{
return adoptRef(new RenderThemeChromiumWin);
}
PassRefPtr<RenderTheme> RenderTheme::themeForPage(Page* page)
{
static RenderTheme* rt = RenderThemeChromiumWin::create().releaseRef();
return rt;
}
bool RenderThemeChromiumWin::supportsFocusRing(const RenderStyle* style) const
{
// Let webkit draw one of its halo rings around any focused element,
// except push buttons. For buttons we use the windows PBS_DEFAULTED
// styling to give it a blue border.
return style->appearance() == ButtonPart
|| style->appearance() == PushButtonPart;
}
Color RenderThemeChromiumWin::platformActiveSelectionBackgroundColor() const
{
if (PlatformBridge::layoutTestMode())
return Color(0x00, 0x00, 0xff); // Royal blue.
COLORREF color = GetSysColor(COLOR_HIGHLIGHT);
return Color(GetRValue(color), GetGValue(color), GetBValue(color), 0xff);
}
Color RenderThemeChromiumWin::platformInactiveSelectionBackgroundColor() const
{
if (PlatformBridge::layoutTestMode())
return Color(0x99, 0x99, 0x99); // Medium gray.
COLORREF color = GetSysColor(COLOR_GRAYTEXT);
return Color(GetRValue(color), GetGValue(color), GetBValue(color), 0xff);
}
Color RenderThemeChromiumWin::platformActiveSelectionForegroundColor() const
{
if (PlatformBridge::layoutTestMode())
return Color(0xff, 0xff, 0xcc); // Pale yellow.
COLORREF color = GetSysColor(COLOR_HIGHLIGHTTEXT);
return Color(GetRValue(color), GetGValue(color), GetBValue(color), 0xff);
}
Color RenderThemeChromiumWin::platformInactiveSelectionForegroundColor() const
{
return Color::white;
}
Color RenderThemeChromiumWin::platformActiveTextSearchHighlightColor() const
{
return Color(0xff, 0x96, 0x32); // Orange.
}
Color RenderThemeChromiumWin::platformInactiveTextSearchHighlightColor() const
{
return Color(0xff, 0xff, 0x96); // Yellow.
}
void RenderThemeChromiumWin::systemFont(int propId, FontDescription& fontDescription) const
{
// This logic owes much to RenderThemeSafari.cpp.
FontDescription* cachedDesc = 0;
AtomicString faceName;
float fontSize = 0;
switch (propId) {
case CSSValueSmallCaption:
cachedDesc = &smallSystemFont;
if (!smallSystemFont.isAbsoluteSize()) {
NONCLIENTMETRICS metrics;
getNonClientMetrics(&metrics);
faceName = AtomicString(metrics.lfSmCaptionFont.lfFaceName, wcslen(metrics.lfSmCaptionFont.lfFaceName));
fontSize = systemFontSize(metrics.lfSmCaptionFont);
}
break;
case CSSValueMenu:
cachedDesc = &menuFont;
if (!menuFont.isAbsoluteSize()) {
NONCLIENTMETRICS metrics;
getNonClientMetrics(&metrics);
faceName = AtomicString(metrics.lfMenuFont.lfFaceName, wcslen(metrics.lfMenuFont.lfFaceName));
fontSize = systemFontSize(metrics.lfMenuFont);
}
break;
case CSSValueStatusBar:
cachedDesc = &labelFont;
if (!labelFont.isAbsoluteSize()) {
NONCLIENTMETRICS metrics;
getNonClientMetrics(&metrics);
faceName = metrics.lfStatusFont.lfFaceName;
fontSize = systemFontSize(metrics.lfStatusFont);
}
break;
case CSSValueWebkitMiniControl:
case CSSValueWebkitSmallControl:
case CSSValueWebkitControl:
faceName = defaultGUIFont();
// Why 2 points smaller? Because that's what Gecko does.
fontSize = defaultFontSize - pointsToPixels(2);
break;
default:
faceName = defaultGUIFont();
fontSize = defaultFontSize;
break;
}
if (!cachedDesc)
cachedDesc = &fontDescription;
if (fontSize) {
cachedDesc->firstFamily().setFamily(faceName);
cachedDesc->setIsAbsoluteSize(true);
cachedDesc->setGenericFamily(FontDescription::NoFamily);
cachedDesc->setSpecifiedSize(fontSize);
cachedDesc->setWeight(FontWeightNormal);
cachedDesc->setItalic(false);
}
fontDescription = *cachedDesc;
}
// Map a CSSValue* system color to an index understood by GetSysColor().
static int cssValueIdToSysColorIndex(int cssValueId)
{
switch (cssValueId) {
case CSSValueActiveborder: return COLOR_ACTIVEBORDER;
case CSSValueActivecaption: return COLOR_ACTIVECAPTION;
case CSSValueAppworkspace: return COLOR_APPWORKSPACE;
case CSSValueBackground: return COLOR_BACKGROUND;
case CSSValueButtonface: return COLOR_BTNFACE;
case CSSValueButtonhighlight: return COLOR_BTNHIGHLIGHT;
case CSSValueButtonshadow: return COLOR_BTNSHADOW;
case CSSValueButtontext: return COLOR_BTNTEXT;
case CSSValueCaptiontext: return COLOR_CAPTIONTEXT;
case CSSValueGraytext: return COLOR_GRAYTEXT;
case CSSValueHighlight: return COLOR_HIGHLIGHT;
case CSSValueHighlighttext: return COLOR_HIGHLIGHTTEXT;
case CSSValueInactiveborder: return COLOR_INACTIVEBORDER;
case CSSValueInactivecaption: return COLOR_INACTIVECAPTION;
case CSSValueInactivecaptiontext: return COLOR_INACTIVECAPTIONTEXT;
case CSSValueInfobackground: return COLOR_INFOBK;
case CSSValueInfotext: return COLOR_INFOTEXT;
case CSSValueMenu: return COLOR_MENU;
case CSSValueMenutext: return COLOR_MENUTEXT;
case CSSValueScrollbar: return COLOR_SCROLLBAR;
case CSSValueThreeddarkshadow: return COLOR_3DDKSHADOW;
case CSSValueThreedface: return COLOR_3DFACE;
case CSSValueThreedhighlight: return COLOR_3DHIGHLIGHT;
case CSSValueThreedlightshadow: return COLOR_3DLIGHT;
case CSSValueThreedshadow: return COLOR_3DSHADOW;
case CSSValueWindow: return COLOR_WINDOW;
case CSSValueWindowframe: return COLOR_WINDOWFRAME;
case CSSValueWindowtext: return COLOR_WINDOWTEXT;
default: return -1; // Unsupported CSSValue
}
}
Color RenderThemeChromiumWin::systemColor(int cssValueId) const
{
int sysColorIndex = cssValueIdToSysColorIndex(cssValueId);
if (PlatformBridge::layoutTestMode() || (sysColorIndex == -1))
return RenderTheme::systemColor(cssValueId);
COLORREF color = GetSysColor(sysColorIndex);
return Color(GetRValue(color), GetGValue(color), GetBValue(color));
}
void RenderThemeChromiumWin::adjustSliderThumbSize(RenderObject* o) const
{
// These sizes match what WinXP draws for various menus.
const int sliderThumbAlongAxis = 11;
const int sliderThumbAcrossAxis = 21;
if (o->style()->appearance() == SliderThumbHorizontalPart) {
o->style()->setWidth(Length(sliderThumbAlongAxis, Fixed));
o->style()->setHeight(Length(sliderThumbAcrossAxis, Fixed));
} else if (o->style()->appearance() == SliderThumbVerticalPart) {
o->style()->setWidth(Length(sliderThumbAcrossAxis, Fixed));
o->style()->setHeight(Length(sliderThumbAlongAxis, Fixed));
} else
RenderThemeChromiumSkia::adjustSliderThumbSize(o);
}
bool RenderThemeChromiumWin::paintCheckbox(RenderObject* o, const PaintInfo& i, const IntRect& r)
{
return paintButton(o, i, r);
}
bool RenderThemeChromiumWin::paintRadio(RenderObject* o, const PaintInfo& i, const IntRect& r)
{
return paintButton(o, i, r);
}
bool RenderThemeChromiumWin::paintButton(RenderObject* o, const PaintInfo& i, const IntRect& r)
{
const ThemeData& themeData = getThemeData(o);
ThemePainter painter(i.context, r);
PlatformBridge::paintButton(painter.context(),
themeData.m_part,
themeData.m_state,
themeData.m_classicState,
painter.drawRect());
return false;
}
bool RenderThemeChromiumWin::paintTextField(RenderObject* o, const PaintInfo& i, const IntRect& r)
{
return paintTextFieldInternal(o, i, r, true);
}
bool RenderThemeChromiumWin::paintSliderTrack(RenderObject* o, const PaintInfo& i, const IntRect& r)
{
const ThemeData& themeData = getThemeData(o);
ThemePainter painter(i.context, r);
PlatformBridge::paintTrackbar(painter.context(),
themeData.m_part,
themeData.m_state,
themeData.m_classicState,
painter.drawRect());
return false;
}
bool RenderThemeChromiumWin::paintSliderThumb(RenderObject* o, const PaintInfo& i, const IntRect& r)
{
return paintSliderTrack(o, i, r);
}
static int menuListButtonWidth()
{
static int width = PlatformBridge::layoutTestMode() ? kStandardMenuListButtonWidth : GetSystemMetrics(SM_CXVSCROLL);
return width;
}
// Used to paint unstyled menulists (i.e. with the default border)
bool RenderThemeChromiumWin::paintMenuList(RenderObject* o, const PaintInfo& i, const IntRect& r)
{
if (!o->isBox())
return false;
const RenderBox* box = toRenderBox(o);
int borderRight = box->borderRight();
int borderLeft = box->borderLeft();
int borderTop = box->borderTop();
int borderBottom = box->borderBottom();
// If all the borders are 0, then tell skia not to paint the border on the
// textfield. FIXME: http://b/1210017 Figure out how to get Windows to not
// draw individual borders and then pass that to skia so we can avoid
// drawing any borders that are set to 0. For non-zero borders, we draw the
// border, but webkit just draws over it.
bool drawEdges = !(!borderRight && !borderLeft && !borderTop && !borderBottom);
paintTextFieldInternal(o, i, r, drawEdges);
// Take padding and border into account. If the MenuList is smaller than
// the size of a button, make sure to shrink it appropriately and not put
// its x position to the left of the menulist.
const int buttonWidth = menuListButtonWidth();
int spacingLeft = borderLeft + box->paddingLeft();
int spacingRight = borderRight + box->paddingRight();
int spacingTop = borderTop + box->paddingTop();
int spacingBottom = borderBottom + box->paddingBottom();
int buttonX;
if (r.maxX() - r.x() < buttonWidth)
buttonX = r.x();
else
buttonX = o->style()->direction() == LTR ? r.maxX() - spacingRight - buttonWidth : r.x() + spacingLeft;
// Compute the rectangle of the button in the destination image.
IntRect rect(buttonX,
r.y() + spacingTop,
std::min(buttonWidth, r.maxX() - r.x()),
r.height() - (spacingTop + spacingBottom));
// Get the correct theme data for a textfield and paint the menu.
ThemePainter painter(i.context, rect);
PlatformBridge::paintMenuList(painter.context(),
CP_DROPDOWNBUTTON,
determineState(o),
determineClassicState(o),
painter.drawRect());
return false;
}
// static
void RenderThemeChromiumWin::setDefaultFontSize(int fontSize)
{
RenderThemeChromiumSkia::setDefaultFontSize(fontSize);
// Reset cached fonts.
smallSystemFont = menuFont = labelFont = FontDescription();
}
double RenderThemeChromiumWin::caretBlinkIntervalInternal() const
{
// This involves a system call, so we cache the result.
static double blinkInterval = querySystemBlinkInterval(RenderTheme::caretBlinkInterval());
return blinkInterval;
}
unsigned RenderThemeChromiumWin::determineState(RenderObject* o, ControlSubPart subPart)
{
unsigned result = TS_NORMAL;
ControlPart appearance = o->style()->appearance();
if (!isEnabled(o))
result = TS_DISABLED;
else if (isReadOnlyControl(o))
result = (appearance == TextFieldPart || appearance == TextAreaPart || appearance == SearchFieldPart) ? ETS_READONLY : TS_DISABLED;
// Active overrides hover and focused.
else if (isPressed(o) && (subPart == SpinButtonUp) == isSpinUpButtonPartPressed(o))
result = TS_PRESSED;
else if (supportsFocus(appearance) && isFocused(o))
result = ETS_FOCUSED;
else if (isHovered(o) && (subPart == SpinButtonUp) == isSpinUpButtonPartHovered(o))
result = TS_HOT;
// CBS_UNCHECKED*: 1-4
// CBS_CHECKED*: 5-8
// CBS_MIXED*: 9-12
if (isIndeterminate(o))
result += 8;
else if (isChecked(o))
result += 4;
return result;
}
unsigned RenderThemeChromiumWin::determineSliderThumbState(RenderObject* o)
{
unsigned result = TUS_NORMAL;
if (!isEnabled(o->parent()))
result = TUS_DISABLED;
else if (supportsFocus(o->style()->appearance()) && isFocused(o->parent()))
result = TUS_FOCUSED;
else if (toRenderSlider(o->parent())->inDragMode())
result = TUS_PRESSED;
else if (isHovered(o))
result = TUS_HOT;
return result;
}
unsigned RenderThemeChromiumWin::determineClassicState(RenderObject* o, ControlSubPart subPart)
{
unsigned result = 0;
ControlPart part = o->style()->appearance();
// Sliders are always in the normal state.
if (part == SliderHorizontalPart || part == SliderVerticalPart)
return result;
// So are readonly text fields.
if (isReadOnlyControl(o) && (part == TextFieldPart || part == TextAreaPart || part == SearchFieldPart))
return result;
if (part == SliderThumbHorizontalPart || part == SliderThumbVerticalPart) {
if (!isEnabled(o->parent()))
result = DFCS_INACTIVE;
else if (toRenderSlider(o->parent())->inDragMode()) // Active supersedes hover
result = DFCS_PUSHED;
else if (isHovered(o))
result = DFCS_HOT;
} else {
if (!isEnabled(o) || isReadOnlyControl(o))
result = DFCS_INACTIVE;
// Active supersedes hover
else if (isPressed(o) && (subPart == SpinButtonUp) == isSpinUpButtonPartPressed(o))
result = DFCS_PUSHED;
else if (supportsFocus(part) && isFocused(o)) // So does focused
result = 0;
else if (isHovered(o) && (subPart == SpinButtonUp) == isSpinUpButtonPartHovered(o))
result = DFCS_HOT;
// Classic theme can't represent indeterminate states. Use unchecked appearance.
if (isChecked(o) && !isIndeterminate(o))
result |= DFCS_CHECKED;
}
return result;
}
ThemeData RenderThemeChromiumWin::getThemeData(RenderObject* o, ControlSubPart subPart)
{
ThemeData result;
switch (o->style()->appearance()) {
case CheckboxPart:
result.m_part = BP_CHECKBOX;
result.m_state = determineState(o);
result.m_classicState = DFCS_BUTTONCHECK;
break;
case RadioPart:
result.m_part = BP_RADIOBUTTON;
result.m_state = determineState(o);
result.m_classicState = DFCS_BUTTONRADIO;
break;
case PushButtonPart:
case ButtonPart:
result.m_part = BP_PUSHBUTTON;
result.m_state = determineState(o);
result.m_classicState = DFCS_BUTTONPUSH;
break;
case SliderHorizontalPart:
result.m_part = TKP_TRACK;
result.m_state = TRS_NORMAL;
break;
case SliderVerticalPart:
result.m_part = TKP_TRACKVERT;
result.m_state = TRVS_NORMAL;
break;
case SliderThumbHorizontalPart:
result.m_part = TKP_THUMBBOTTOM;
result.m_state = determineSliderThumbState(o);
break;
case SliderThumbVerticalPart:
result.m_part = TKP_THUMBVERT;
result.m_state = determineSliderThumbState(o);
break;
case ListboxPart:
case MenulistPart:
case MenulistButtonPart:
case SearchFieldPart:
case TextFieldPart:
case TextAreaPart:
result.m_part = EP_EDITTEXT;
result.m_state = determineState(o);
break;
case InnerSpinButtonPart:
result.m_part = subPart == SpinButtonUp ? SPNP_UP : SPNP_DOWN;
result.m_state = determineState(o, subPart);
result.m_classicState = subPart == SpinButtonUp ? DFCS_SCROLLUP : DFCS_SCROLLDOWN;
break;
}
result.m_classicState |= determineClassicState(o, subPart);
return result;
}
bool RenderThemeChromiumWin::paintTextFieldInternal(RenderObject* o,
const PaintInfo& i,
const IntRect& r,
bool drawEdges)
{
// Fallback to white if the specified color object is invalid.
// (Note PlatformBridge::paintTextField duplicates this check).
Color backgroundColor(Color::white);
if (o->style()->visitedDependentColor(CSSPropertyBackgroundColor).isValid())
backgroundColor = o->style()->visitedDependentColor(CSSPropertyBackgroundColor);
// If we have background-image, don't fill the content area to expose the
// parent's background. Also, we shouldn't fill the content area if the
// alpha of the color is 0. The API of Windows GDI ignores the alpha.
//
// Note that we should paint the content area white if we have neither the
// background color nor background image explicitly specified to keep the
// appearance of select element consistent with other browsers.
bool fillContentArea = !o->style()->hasBackgroundImage() && backgroundColor.alpha();
if (o->style()->hasBorderRadius()) {
// If the style has rounded borders, setup the context to clip the
// background (themed or filled) appropriately.
// FIXME: make sure we do the right thing if css background-clip is set.
i.context->save();
i.context->addRoundedRectClip(o->style()->getRoundedBorderFor(r));
}
{
const ThemeData& themeData = getThemeData(o);
ThemePainter painter(i.context, r);
PlatformBridge::paintTextField(painter.context(),
themeData.m_part,
themeData.m_state,
themeData.m_classicState,
painter.drawRect(),
backgroundColor,
fillContentArea,
drawEdges);
// End of block commits the painter before restoring context.
}
if (o->style()->hasBorderRadius())
i.context->restore();
return false;
}
void RenderThemeChromiumWin::adjustInnerSpinButtonStyle(CSSStyleSelector*, RenderStyle* style, Element*) const
{
int width = ScrollbarTheme::nativeTheme()->scrollbarThickness();
style->setWidth(Length(width, Fixed));
style->setMinWidth(Length(width, Fixed));
}
bool RenderThemeChromiumWin::paintInnerSpinButton(RenderObject* object, const PaintInfo& info, const IntRect& rect)
{
IntRect half = rect;
// Need explicit blocks to avoid to create multiple ThemePainter instances.
{
half.setHeight(rect.height() / 2);
const ThemeData& upThemeData = getThemeData(object, SpinButtonUp);
ThemePainter upPainter(info.context, half);
PlatformBridge::paintSpinButton(upPainter.context(),
upThemeData.m_part,
upThemeData.m_state,
upThemeData.m_classicState,
upPainter.drawRect());
}
{
half.setY(rect.y() + rect.height() / 2);
const ThemeData& downThemeData = getThemeData(object, SpinButtonDown);
ThemePainter downPainter(info.context, half);
PlatformBridge::paintSpinButton(downPainter.context(),
downThemeData.m_part,
downThemeData.m_state,
downThemeData.m_classicState,
downPainter.drawRect());
}
return false;
}
#if ENABLE(PROGRESS_TAG)
// MSDN says that update intervals for the bar is 30ms.
// http://msdn.microsoft.com/en-us/library/bb760842(v=VS.85).aspx
static const double progressAnimationFrameRate = 0.033;
double RenderThemeChromiumWin::animationRepeatIntervalForProgressBar(RenderProgress*) const
{
return progressAnimationFrameRate;
}
double RenderThemeChromiumWin::animationDurationForProgressBar(RenderProgress* renderProgress) const
{
// On Chromium Windows port, animationProgress() and associated values aren't used.
// So here we can return arbitrary positive value.
return progressAnimationFrameRate;
}
void RenderThemeChromiumWin::adjustProgressBarStyle(CSSStyleSelector*, RenderStyle*, Element*) const
{
}
bool RenderThemeChromiumWin::paintProgressBar(RenderObject* o, const PaintInfo& i, const IntRect& r)
{
if (!o->isProgress())
return true;
RenderProgress* renderProgress = toRenderProgress(o);
// For indeterminate bar, valueRect is ignored and it is computed by the theme engine
// because the animation is a platform detail and WebKit doesn't need to know how.
IntRect valueRect = renderProgress->isDeterminate() ? determinateProgressValueRectFor(renderProgress, r) : IntRect(0, 0, 0, 0);
double animatedSeconds = renderProgress->animationStartTime() ? WTF::currentTime() - renderProgress->animationStartTime() : 0;
ThemePainter painter(i.context, r);
PlatformBridge::paintProgressBar(painter.context(), r, valueRect, renderProgress->isDeterminate(), animatedSeconds);
return false;
}
#endif
} // namespace WebCore