RenderThemeChromiumWin.cpp revision 5f1ab04193ad0130ca8204aadaceae083aca9881
1/* 2 * This file is part of the WebKit project. 3 * 4 * Copyright (C) 2006 Apple Computer, Inc. 5 * Copyright (C) 2008, 2009 Google, Inc. 6 * 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. 11 * 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. 16 * 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., 59 Temple Place - Suite 330, 20 * Boston, MA 02111-1307, USA. 21 * 22 */ 23 24#include "config.h" 25#include "RenderThemeChromiumWin.h" 26 27#include <windows.h> 28#include <uxtheme.h> 29#include <vssym32.h> 30 31#include "ChromiumBridge.h" 32#include "CSSStyleSheet.h" 33#include "CSSValueKeywords.h" 34#include "FontSelector.h" 35#include "FontUtilsChromiumWin.h" 36#include "GraphicsContext.h" 37#include "HTMLMediaElement.h" 38#include "HTMLNames.h" 39#include "MediaControlElements.h" 40#include "RenderBox.h" 41#include "RenderSlider.h" 42#include "ScrollbarTheme.h" 43#include "SkiaUtils.h" 44#include "TransparencyWin.h" 45#include "UserAgentStyleSheets.h" 46#include "WindowsVersion.h" 47 48// FIXME: This dependency should eventually be removed. 49#include <skia/ext/skia_utils_win.h> 50 51#define SIZEOF_STRUCT_WITH_SPECIFIED_LAST_MEMBER(structName, member) \ 52 offsetof(structName, member) + \ 53 (sizeof static_cast<structName*>(0)->member) 54#define NONCLIENTMETRICS_SIZE_PRE_VISTA \ 55 SIZEOF_STRUCT_WITH_SPECIFIED_LAST_MEMBER(NONCLIENTMETRICS, lfMessageFont) 56 57namespace WebCore { 58 59namespace { 60 61// The background for the media player controls should be a 60% opaque black rectangle. This 62// matches the UI mockups for the default UI theme. 63static const float defaultMediaControlOpacity = 0.6f; 64 65// These values all match Safari/Win. 66static const float defaultControlFontPixelSize = 13; 67static const float defaultCancelButtonSize = 9; 68static const float minCancelButtonSize = 5; 69static const float maxCancelButtonSize = 21; 70static const float defaultSearchFieldResultsDecorationSize = 13; 71static const float minSearchFieldResultsDecorationSize = 9; 72static const float maxSearchFieldResultsDecorationSize = 30; 73static const float defaultSearchFieldResultsButtonWidth = 18; 74 75bool canvasHasMultipleLayers(const SkCanvas* canvas) 76{ 77 SkCanvas::LayerIter iter(const_cast<SkCanvas*>(canvas), false); 78 iter.next(); // There is always at least one layer. 79 return !iter.done(); // There is > 1 layer if the the iterator can stil advance. 80} 81 82class ThemePainter : public TransparencyWin { 83public: 84 ThemePainter(GraphicsContext* context, const IntRect& r) 85 { 86 TransformMode transformMode = getTransformMode(context->getCTM()); 87 init(context, getLayerMode(context, transformMode), transformMode, r); 88 } 89 90 ~ThemePainter() 91 { 92 composite(); 93 } 94 95private: 96 static LayerMode getLayerMode(GraphicsContext* context, TransformMode transformMode) 97 { 98 if (context->platformContext()->isDrawingToImageBuffer()) // Might have transparent background. 99 return WhiteLayer; 100 else if (canvasHasMultipleLayers(context->platformContext()->canvas())) // Needs antialiasing help. 101 return OpaqueCompositeLayer; 102 else // Nothing interesting. 103 return transformMode == KeepTransform ? NoLayer : OpaqueCompositeLayer; 104 } 105 106 static TransformMode getTransformMode(const TransformationMatrix& matrix) 107 { 108 if (matrix.b() != 0 || matrix.c() != 0) // Skew. 109 return Untransform; 110 else if (matrix.a() != 1.0 || matrix.d() != 1.0) // Scale. 111 return ScaleTransform; 112 else // Nothing interesting. 113 return KeepTransform; 114 } 115}; 116 117} // namespace 118 119static void getNonClientMetrics(NONCLIENTMETRICS* metrics) { 120 static UINT size = WebCore::isVistaOrNewer() ? 121 sizeof(NONCLIENTMETRICS) : NONCLIENTMETRICS_SIZE_PRE_VISTA; 122 metrics->cbSize = size; 123 bool success = !!SystemParametersInfo(SPI_GETNONCLIENTMETRICS, size, metrics, 0); 124 ASSERT(success); 125} 126 127enum PaddingType { 128 TopPadding, 129 RightPadding, 130 BottomPadding, 131 LeftPadding 132}; 133 134static const int styledMenuListInternalPadding[4] = { 1, 4, 1, 4 }; 135 136// The default variable-width font size. We use this as the default font 137// size for the "system font", and as a base size (which we then shrink) for 138// form control fonts. 139static float defaultFontSize = 16.0; 140 141static FontDescription smallSystemFont; 142static FontDescription menuFont; 143static FontDescription labelFont; 144 145// Internal static helper functions. We don't put them in an anonymous 146// namespace so they have easier access to the WebCore namespace. 147 148static bool supportsFocus(ControlPart appearance) 149{ 150 switch (appearance) { 151 case PushButtonPart: 152 case ButtonPart: 153 case DefaultButtonPart: 154 case SearchFieldPart: 155 case TextFieldPart: 156 case TextAreaPart: 157 return true; 158 } 159 return false; 160} 161 162static void setFixedPadding(RenderStyle* style, const int padding[4]) 163{ 164 style->setPaddingLeft(Length(padding[LeftPadding], Fixed)); 165 style->setPaddingRight(Length(padding[RightPadding], Fixed)); 166 style->setPaddingTop(Length(padding[TopPadding], Fixed)); 167 style->setPaddingBottom(Length(padding[BottomPadding], Fixed)); 168} 169 170// Return the height of system font |font| in pixels. We use this size by 171// default for some non-form-control elements. 172static float systemFontSize(const LOGFONT& font) 173{ 174 float size = -font.lfHeight; 175 if (size < 0) { 176 HFONT hFont = CreateFontIndirect(&font); 177 if (hFont) { 178 HDC hdc = GetDC(0); // What about printing? Is this the right DC? 179 if (hdc) { 180 HGDIOBJ hObject = SelectObject(hdc, hFont); 181 TEXTMETRIC tm; 182 GetTextMetrics(hdc, &tm); 183 SelectObject(hdc, hObject); 184 ReleaseDC(0, hdc); 185 size = tm.tmAscent; 186 } 187 DeleteObject(hFont); 188 } 189 } 190 191 // The "codepage 936" bit here is from Gecko; apparently this helps make 192 // fonts more legible in Simplified Chinese where the default font size is 193 // too small. 194 // 195 // FIXME: http://b/1119883 Since this is only used for "small caption", 196 // "menu", and "status bar" objects, I'm not sure how much this even 197 // matters. Plus the Gecko patch went in back in 2002, and maybe this 198 // isn't even relevant anymore. We should investigate whether this should 199 // be removed, or perhaps broadened to be "any CJK locale". 200 // 201 return ((size < 12.0f) && (GetACP() == 936)) ? 12.0f : size; 202} 203 204// We aim to match IE here. 205// -IE uses a font based on the encoding as the default font for form controls. 206// -Gecko uses MS Shell Dlg (actually calls GetStockObject(DEFAULT_GUI_FONT), 207// which returns MS Shell Dlg) 208// -Safari uses Lucida Grande. 209// 210// FIXME: The only case where we know we don't match IE is for ANSI encodings. 211// IE uses MS Shell Dlg there, which we render incorrectly at certain pixel 212// sizes (e.g. 15px). So, for now we just use Arial. 213static wchar_t* defaultGUIFont() 214{ 215 return L"Arial"; 216} 217 218// Converts |points| to pixels. One point is 1/72 of an inch. 219static float pointsToPixels(float points) 220{ 221 static float pixelsPerInch = 0.0f; 222 if (!pixelsPerInch) { 223 HDC hdc = GetDC(0); // What about printing? Is this the right DC? 224 if (hdc) { // Can this ever actually be NULL? 225 pixelsPerInch = GetDeviceCaps(hdc, LOGPIXELSY); 226 ReleaseDC(0, hdc); 227 } else { 228 pixelsPerInch = 96.0f; 229 } 230 } 231 232 static const float pointsPerInch = 72.0f; 233 return points / pointsPerInch * pixelsPerInch; 234} 235 236static void setSizeIfAuto(RenderStyle* style, const IntSize& size) 237{ 238 if (style->width().isIntrinsicOrAuto()) 239 style->setWidth(Length(size.width(), Fixed)); 240 if (style->height().isAuto()) 241 style->setHeight(Length(size.height(), Fixed)); 242} 243 244static double querySystemBlinkInterval(double defaultInterval) 245{ 246 UINT blinkTime = GetCaretBlinkTime(); 247 if (blinkTime == 0) 248 return defaultInterval; 249 if (blinkTime == INFINITE) 250 return 0; 251 return blinkTime / 1000.0; 252} 253 254#if ENABLE(VIDEO) 255// Attempt to retrieve a HTMLMediaElement from a Node. Returns NULL if one cannot be found. 256static HTMLMediaElement* mediaElementParent(Node* node) 257{ 258 if (!node) 259 return 0; 260 Node* mediaNode = node->shadowAncestorNode(); 261 if (!mediaNode || (!mediaNode->hasTagName(HTMLNames::videoTag) && !mediaNode->hasTagName(HTMLNames::audioTag))) 262 return 0; 263 264 return static_cast<HTMLMediaElement*>(mediaNode); 265} 266#endif 267 268// Implement WebCore::theme() for getting the global RenderTheme. 269RenderTheme* theme() 270{ 271 static RenderThemeChromiumWin winTheme; 272 return &winTheme; 273} 274 275String RenderThemeChromiumWin::extraDefaultStyleSheet() 276{ 277 return String(themeWinUserAgentStyleSheet, sizeof(themeWinUserAgentStyleSheet)); 278} 279 280String RenderThemeChromiumWin::extraQuirksStyleSheet() 281{ 282 return String(themeWinQuirksUserAgentStyleSheet, sizeof(themeWinQuirksUserAgentStyleSheet)); 283} 284 285#if ENABLE(VIDEO) 286String RenderThemeChromiumWin::extraMediaControlsStyleSheet() 287{ 288 return String(mediaControlsChromiumUserAgentStyleSheet, sizeof(mediaControlsChromiumUserAgentStyleSheet)); 289} 290#endif 291 292bool RenderThemeChromiumWin::supportsFocusRing(const RenderStyle* style) const 293{ 294 // Let webkit draw one of its halo rings around any focused element, 295 // except push buttons. For buttons we use the windows PBS_DEFAULTED 296 // styling to give it a blue border. 297 return style->appearance() == ButtonPart 298 || style->appearance() == PushButtonPart; 299} 300 301Color RenderThemeChromiumWin::platformActiveSelectionBackgroundColor() const 302{ 303 if (ChromiumBridge::layoutTestMode()) 304 return Color(0x00, 0x00, 0xff); // Royal blue. 305 COLORREF color = GetSysColor(COLOR_HIGHLIGHT); 306 return Color(GetRValue(color), GetGValue(color), GetBValue(color), 0xff); 307} 308 309Color RenderThemeChromiumWin::platformInactiveSelectionBackgroundColor() const 310{ 311 if (ChromiumBridge::layoutTestMode()) 312 return Color(0x99, 0x99, 0x99); // Medium gray. 313 COLORREF color = GetSysColor(COLOR_GRAYTEXT); 314 return Color(GetRValue(color), GetGValue(color), GetBValue(color), 0xff); 315} 316 317Color RenderThemeChromiumWin::platformActiveSelectionForegroundColor() const 318{ 319 if (ChromiumBridge::layoutTestMode()) 320 return Color(0xff, 0xff, 0xcc); // Pale yellow. 321 COLORREF color = GetSysColor(COLOR_HIGHLIGHTTEXT); 322 return Color(GetRValue(color), GetGValue(color), GetBValue(color), 0xff); 323} 324 325Color RenderThemeChromiumWin::platformInactiveSelectionForegroundColor() const 326{ 327 return Color::white; 328} 329 330Color RenderThemeChromiumWin::platformActiveTextSearchHighlightColor() const 331{ 332 return Color(0xff, 0x96, 0x32); // Orange. 333} 334 335Color RenderThemeChromiumWin::platformInactiveTextSearchHighlightColor() const 336{ 337 return Color(0xff, 0xff, 0x96); // Yellow. 338} 339 340double RenderThemeChromiumWin::caretBlinkInterval() const 341{ 342 // Disable the blinking caret in layout test mode, as it introduces 343 // a race condition for the pixel tests. http://b/1198440 344 if (ChromiumBridge::layoutTestMode()) 345 return 0; 346 347 // This involves a system call, so we cache the result. 348 static double blinkInterval = querySystemBlinkInterval(RenderTheme::caretBlinkInterval()); 349 return blinkInterval; 350} 351 352void RenderThemeChromiumWin::systemFont(int propId, FontDescription& fontDescription) const 353{ 354 // This logic owes much to RenderThemeSafari.cpp. 355 FontDescription* cachedDesc = NULL; 356 wchar_t* faceName = 0; 357 float fontSize = 0; 358 switch (propId) { 359 case CSSValueSmallCaption: 360 cachedDesc = &smallSystemFont; 361 if (!smallSystemFont.isAbsoluteSize()) { 362 NONCLIENTMETRICS metrics; 363 getNonClientMetrics(&metrics); 364 faceName = metrics.lfSmCaptionFont.lfFaceName; 365 fontSize = systemFontSize(metrics.lfSmCaptionFont); 366 } 367 break; 368 case CSSValueMenu: 369 cachedDesc = &menuFont; 370 if (!menuFont.isAbsoluteSize()) { 371 NONCLIENTMETRICS metrics; 372 getNonClientMetrics(&metrics); 373 faceName = metrics.lfMenuFont.lfFaceName; 374 fontSize = systemFontSize(metrics.lfMenuFont); 375 } 376 break; 377 case CSSValueStatusBar: 378 cachedDesc = &labelFont; 379 if (!labelFont.isAbsoluteSize()) { 380 NONCLIENTMETRICS metrics; 381 getNonClientMetrics(&metrics); 382 faceName = metrics.lfStatusFont.lfFaceName; 383 fontSize = systemFontSize(metrics.lfStatusFont); 384 } 385 break; 386 case CSSValueWebkitMiniControl: 387 case CSSValueWebkitSmallControl: 388 case CSSValueWebkitControl: 389 faceName = defaultGUIFont(); 390 // Why 2 points smaller? Because that's what Gecko does. 391 fontSize = defaultFontSize - pointsToPixels(2); 392 break; 393 default: 394 faceName = defaultGUIFont(); 395 fontSize = defaultFontSize; 396 break; 397 } 398 399 if (!cachedDesc) 400 cachedDesc = &fontDescription; 401 402 if (fontSize) { 403 ASSERT(faceName); 404 cachedDesc->firstFamily().setFamily(AtomicString(faceName, 405 wcslen(faceName))); 406 cachedDesc->setIsAbsoluteSize(true); 407 cachedDesc->setGenericFamily(FontDescription::NoFamily); 408 cachedDesc->setSpecifiedSize(fontSize); 409 cachedDesc->setWeight(FontWeightNormal); 410 cachedDesc->setItalic(false); 411 } 412 fontDescription = *cachedDesc; 413} 414 415int RenderThemeChromiumWin::minimumMenuListSize(RenderStyle* style) const 416{ 417 return 0; 418} 419 420void RenderThemeChromiumWin::adjustSliderThumbSize(RenderObject* o) const 421{ 422 // These sizes match what WinXP draws for various menus. 423 const int sliderThumbAlongAxis = 11; 424 const int sliderThumbAcrossAxis = 21; 425 if (o->style()->appearance() == SliderThumbHorizontalPart || o->style()->appearance() == MediaSliderThumbPart) { 426 o->style()->setWidth(Length(sliderThumbAlongAxis, Fixed)); 427 o->style()->setHeight(Length(sliderThumbAcrossAxis, Fixed)); 428 } else if (o->style()->appearance() == SliderThumbVerticalPart) { 429 o->style()->setWidth(Length(sliderThumbAcrossAxis, Fixed)); 430 o->style()->setHeight(Length(sliderThumbAlongAxis, Fixed)); 431 } 432} 433 434void RenderThemeChromiumWin::setCheckboxSize(RenderStyle* style) const 435{ 436 // If the width and height are both specified, then we have nothing to do. 437 if (!style->width().isIntrinsicOrAuto() && !style->height().isAuto()) 438 return; 439 440 // FIXME: A hard-coded size of 13 is used. This is wrong but necessary 441 // for now. It matches Firefox. At different DPI settings on Windows, 442 // querying the theme gives you a larger size that accounts for the higher 443 // DPI. Until our entire engine honors a DPI setting other than 96, we 444 // can't rely on the theme's metrics. 445 const IntSize size(13, 13); 446 setSizeIfAuto(style, size); 447} 448 449void RenderThemeChromiumWin::setRadioSize(RenderStyle* style) const 450{ 451 // Use same sizing for radio box as checkbox. 452 setCheckboxSize(style); 453} 454 455bool RenderThemeChromiumWin::paintButton(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& r) 456{ 457 const ThemeData& themeData = getThemeData(o); 458 459 WebCore::ThemePainter painter(i.context, r); 460 ChromiumBridge::paintButton(painter.context(), 461 themeData.m_part, 462 themeData.m_state, 463 themeData.m_classicState, 464 painter.drawRect()); 465 return false; 466} 467 468bool RenderThemeChromiumWin::paintTextField(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& r) 469{ 470 return paintTextFieldInternal(o, i, r, true); 471} 472 473bool RenderThemeChromiumWin::paintSliderTrack(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& r) 474{ 475 const ThemeData& themeData = getThemeData(o); 476 477 WebCore::ThemePainter painter(i.context, r); 478 ChromiumBridge::paintTrackbar(painter.context(), 479 themeData.m_part, 480 themeData.m_state, 481 themeData.m_classicState, 482 painter.drawRect()); 483 return false; 484} 485 486void RenderThemeChromiumWin::adjustSearchFieldCancelButtonStyle(CSSStyleSelector*, RenderStyle* style, Element*) const 487{ 488 // Scale the button size based on the font size 489 float fontScale = style->fontSize() / defaultControlFontPixelSize; 490 int cancelButtonSize = lroundf(std::min(std::max(minCancelButtonSize, defaultCancelButtonSize * fontScale), maxCancelButtonSize)); 491 style->setWidth(Length(cancelButtonSize, Fixed)); 492 style->setHeight(Length(cancelButtonSize, Fixed)); 493} 494 495bool RenderThemeChromiumWin::paintSearchFieldCancelButton(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& r) 496{ 497 IntRect bounds = r; 498 ASSERT(o->parent()); 499 if (!o->parent() || !o->parent()->isBox()) 500 return false; 501 502 RenderBox* parentRenderBox = toRenderBox(o->parent()); 503 504 IntRect parentBox = parentRenderBox->absoluteContentBox(); 505 506 // Make sure the scaled button stays square and will fit in its parent's box 507 bounds.setHeight(std::min(parentBox.width(), std::min(parentBox.height(), bounds.height()))); 508 bounds.setWidth(bounds.height()); 509 510 // Center the button vertically. Round up though, so if it has to be one pixel off-center, it will 511 // be one pixel closer to the bottom of the field. This tends to look better with the text. 512 bounds.setY(parentBox.y() + (parentBox.height() - bounds.height() + 1) / 2); 513 514 static Image* cancelImage = Image::loadPlatformResource("searchCancel").releaseRef(); 515 static Image* cancelPressedImage = Image::loadPlatformResource("searchCancelPressed").releaseRef(); 516 i.context->drawImage(isPressed(o) ? cancelPressedImage : cancelImage, bounds); 517 return false; 518} 519 520void RenderThemeChromiumWin::adjustSearchFieldDecorationStyle(CSSStyleSelector*, RenderStyle* style, Element*) const 521{ 522 IntSize emptySize(1, 11); 523 style->setWidth(Length(emptySize.width(), Fixed)); 524 style->setHeight(Length(emptySize.height(), Fixed)); 525} 526 527void RenderThemeChromiumWin::adjustSearchFieldResultsDecorationStyle(CSSStyleSelector*, RenderStyle* style, Element*) const 528{ 529 // Scale the decoration size based on the font size 530 float fontScale = style->fontSize() / defaultControlFontPixelSize; 531 int magnifierSize = lroundf(std::min(std::max(minSearchFieldResultsDecorationSize, defaultSearchFieldResultsDecorationSize * fontScale), 532 maxSearchFieldResultsDecorationSize)); 533 style->setWidth(Length(magnifierSize, Fixed)); 534 style->setHeight(Length(magnifierSize, Fixed)); 535} 536 537bool RenderThemeChromiumWin::paintSearchFieldResultsDecoration(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& r) 538{ 539 IntRect bounds = r; 540 ASSERT(o->parent()); 541 if (!o->parent() || !o->parent()->isBox()) 542 return false; 543 544 RenderBox* parentRenderBox = toRenderBox(o->parent()); 545 IntRect parentBox = parentRenderBox->absoluteContentBox(); 546 547 // Make sure the scaled decoration stays square and will fit in its parent's box 548 bounds.setHeight(std::min(parentBox.width(), std::min(parentBox.height(), bounds.height()))); 549 bounds.setWidth(bounds.height()); 550 551 // Center the decoration vertically. Round up though, so if it has to be one pixel off-center, it will 552 // be one pixel closer to the bottom of the field. This tends to look better with the text. 553 bounds.setY(parentBox.y() + (parentBox.height() - bounds.height() + 1) / 2); 554 555 static Image* magnifierImage = Image::loadPlatformResource("searchMagnifier").releaseRef(); 556 i.context->drawImage(magnifierImage, bounds); 557 return false; 558} 559 560void RenderThemeChromiumWin::adjustSearchFieldResultsButtonStyle(CSSStyleSelector*, RenderStyle* style, Element*) const 561{ 562 // Scale the button size based on the font size 563 float fontScale = style->fontSize() / defaultControlFontPixelSize; 564 int magnifierHeight = lroundf(std::min(std::max(minSearchFieldResultsDecorationSize, defaultSearchFieldResultsDecorationSize * fontScale), 565 maxSearchFieldResultsDecorationSize)); 566 int magnifierWidth = lroundf(magnifierHeight * defaultSearchFieldResultsButtonWidth / defaultSearchFieldResultsDecorationSize); 567 style->setWidth(Length(magnifierWidth, Fixed)); 568 style->setHeight(Length(magnifierHeight, Fixed)); 569} 570 571bool RenderThemeChromiumWin::paintSearchFieldResultsButton(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& r) 572{ 573 IntRect bounds = r; 574 ASSERT(o->parent()); 575 if (!o->parent()) 576 return false; 577 if (!o->parent() || !o->parent()->isBox()) 578 return false; 579 580 RenderBox* parentRenderBox = toRenderBox(o->parent()); 581 IntRect parentBox = parentRenderBox->absoluteContentBox(); 582 583 // Make sure the scaled decoration will fit in its parent's box 584 bounds.setHeight(std::min(parentBox.height(), bounds.height())); 585 bounds.setWidth(std::min(parentBox.width(), static_cast<int>(bounds.height() * defaultSearchFieldResultsButtonWidth / defaultSearchFieldResultsDecorationSize))); 586 587 // Center the button vertically. Round up though, so if it has to be one pixel off-center, it will 588 // be one pixel closer to the bottom of the field. This tends to look better with the text. 589 bounds.setY(parentBox.y() + (parentBox.height() - bounds.height() + 1) / 2); 590 591 static Image* magnifierImage = Image::loadPlatformResource("searchMagnifierResults").releaseRef(); 592 i.context->drawImage(magnifierImage, bounds); 593 return false; 594} 595 596bool RenderThemeChromiumWin::paintMediaButtonInternal(GraphicsContext* context, const IntRect& rect, Image* image) 597{ 598 context->beginTransparencyLayer(defaultMediaControlOpacity); 599 600 // Draw background. 601 Color oldFill = context->fillColor(); 602 Color oldStroke = context->strokeColor(); 603 604 context->setFillColor(Color::black); 605 context->setStrokeColor(Color::black); 606 context->drawRect(rect); 607 608 context->setFillColor(oldFill); 609 context->setStrokeColor(oldStroke); 610 611 // Create a destination rectangle for the image that is centered in the drawing rectangle, rounded left, and down. 612 IntRect imageRect = image->rect(); 613 imageRect.setY(rect.y() + (rect.height() - image->height() + 1) / 2); 614 imageRect.setX(rect.x() + (rect.width() - image->width() + 1) / 2); 615 616 context->drawImage(image, imageRect, CompositeSourceAtop); 617 context->endTransparencyLayer(); 618 619 return false; 620} 621 622bool RenderThemeChromiumWin::paintMediaPlayButton(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& rect) 623{ 624#if ENABLE(VIDEO) 625 HTMLMediaElement* mediaElement = mediaElementParent(o->node()); 626 if (!mediaElement) 627 return false; 628 629 static Image* mediaPlay = Image::loadPlatformResource("mediaPlay").releaseRef(); 630 static Image* mediaPause = Image::loadPlatformResource("mediaPause").releaseRef(); 631 632 return paintMediaButtonInternal(paintInfo.context, rect, mediaElement->paused() ? mediaPlay : mediaPause); 633#else 634 return false; 635#endif 636} 637 638bool RenderThemeChromiumWin::paintMediaMuteButton(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& rect) 639{ 640#if ENABLE(VIDEO) 641 HTMLMediaElement* mediaElement = mediaElementParent(o->node()); 642 if (!mediaElement) 643 return false; 644 645 static Image* soundFull = Image::loadPlatformResource("mediaSoundFull").releaseRef(); 646 static Image* soundNone = Image::loadPlatformResource("mediaSoundNone").releaseRef(); 647 648 return paintMediaButtonInternal(paintInfo.context, rect, mediaElement->muted() ? soundNone: soundFull); 649#else 650 return false; 651#endif 652} 653 654void RenderThemeChromiumWin::adjustMenuListStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const 655{ 656 // Height is locked to auto on all browsers. 657 style->setLineHeight(RenderStyle::initialLineHeight()); 658} 659 660// Used to paint unstyled menulists (i.e. with the default border) 661bool RenderThemeChromiumWin::paintMenuList(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& r) 662{ 663 if (!o->isBox()) 664 return false; 665 666 const RenderBox* box = toRenderBox(o); 667 int borderRight = box->borderRight(); 668 int borderLeft = box->borderLeft(); 669 int borderTop = box->borderTop(); 670 int borderBottom = box->borderBottom(); 671 672 // If all the borders are 0, then tell skia not to paint the border on the 673 // textfield. FIXME: http://b/1210017 Figure out how to get Windows to not 674 // draw individual borders and then pass that to skia so we can avoid 675 // drawing any borders that are set to 0. For non-zero borders, we draw the 676 // border, but webkit just draws over it. 677 bool drawEdges = !(borderRight == 0 && borderLeft == 0 && borderTop == 0 && borderBottom == 0); 678 679 paintTextFieldInternal(o, i, r, drawEdges); 680 681 // Take padding and border into account. If the MenuList is smaller than 682 // the size of a button, make sure to shrink it appropriately and not put 683 // its x position to the left of the menulist. 684 const int buttonWidth = GetSystemMetrics(SM_CXVSCROLL); 685 int spacingLeft = borderLeft + box->paddingLeft(); 686 int spacingRight = borderRight + box->paddingRight(); 687 int spacingTop = borderTop + box->paddingTop(); 688 int spacingBottom = borderBottom + box->paddingBottom(); 689 690 int buttonX; 691 if (r.right() - r.x() < buttonWidth) 692 buttonX = r.x(); 693 else 694 buttonX = o->style()->direction() == LTR ? r.right() - spacingRight - buttonWidth : r.x() + spacingLeft; 695 696 // Compute the rectangle of the button in the destination image. 697 IntRect rect(buttonX, 698 r.y() + spacingTop, 699 std::min(buttonWidth, r.right() - r.x()), 700 r.height() - (spacingTop + spacingBottom)); 701 702 // Get the correct theme data for a textfield and paint the menu. 703 WebCore::ThemePainter painter(i.context, rect); 704 ChromiumBridge::paintMenuList(painter.context(), 705 CP_DROPDOWNBUTTON, 706 determineState(o), 707 determineClassicState(o), 708 painter.drawRect()); 709 return false; 710} 711 712void RenderThemeChromiumWin::adjustMenuListButtonStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const 713{ 714 adjustMenuListStyle(selector, style, e); 715} 716 717// Used to paint styled menulists (i.e. with a non-default border) 718bool RenderThemeChromiumWin::paintMenuListButton(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& r) 719{ 720 return paintMenuList(o, i, r); 721} 722 723int RenderThemeChromiumWin::popupInternalPaddingLeft(RenderStyle* style) const 724{ 725 return menuListInternalPadding(style, LeftPadding); 726} 727 728int RenderThemeChromiumWin::popupInternalPaddingRight(RenderStyle* style) const 729{ 730 return menuListInternalPadding(style, RightPadding); 731} 732 733int RenderThemeChromiumWin::popupInternalPaddingTop(RenderStyle* style) const 734{ 735 return menuListInternalPadding(style, TopPadding); 736} 737 738int RenderThemeChromiumWin::popupInternalPaddingBottom(RenderStyle* style) const 739{ 740 return menuListInternalPadding(style, BottomPadding); 741} 742 743int RenderThemeChromiumWin::buttonInternalPaddingLeft() const 744{ 745 return 3; 746} 747 748int RenderThemeChromiumWin::buttonInternalPaddingRight() const 749{ 750 return 3; 751} 752 753int RenderThemeChromiumWin::buttonInternalPaddingTop() const 754{ 755 return 1; 756} 757 758int RenderThemeChromiumWin::buttonInternalPaddingBottom() const 759{ 760 return 1; 761} 762 763// static 764void RenderThemeChromiumWin::setDefaultFontSize(int fontSize) 765{ 766 defaultFontSize = static_cast<float>(fontSize); 767 768 // Reset cached fonts. 769 smallSystemFont = menuFont = labelFont = FontDescription(); 770} 771 772unsigned RenderThemeChromiumWin::determineState(RenderObject* o) 773{ 774 unsigned result = TS_NORMAL; 775 ControlPart appearance = o->style()->appearance(); 776 if (!isEnabled(o)) 777 result = TS_DISABLED; 778 else if (isReadOnlyControl(o) && (TextFieldPart == appearance || TextAreaPart == appearance || SearchFieldPart == appearance)) 779 result = ETS_READONLY; // Readonly is supported on textfields. 780 else if (isPressed(o)) // Active overrides hover and focused. 781 result = TS_PRESSED; 782 else if (supportsFocus(appearance) && isFocused(o)) 783 result = ETS_FOCUSED; 784 else if (isHovered(o)) 785 result = TS_HOT; 786 if (isChecked(o)) 787 result += 4; // 4 unchecked states, 4 checked states. 788 return result; 789} 790 791unsigned RenderThemeChromiumWin::determineSliderThumbState(RenderObject* o) 792{ 793 unsigned result = TUS_NORMAL; 794 if (!isEnabled(o->parent())) 795 result = TUS_DISABLED; 796 else if (supportsFocus(o->style()->appearance()) && isFocused(o->parent())) 797 result = TUS_FOCUSED; 798 else if (static_cast<RenderSlider*>(o->parent())->inDragMode()) 799 result = TUS_PRESSED; 800 else if (isHovered(o)) 801 result = TUS_HOT; 802 return result; 803} 804 805unsigned RenderThemeChromiumWin::determineClassicState(RenderObject* o) 806{ 807 unsigned result = 0; 808 if (!isEnabled(o)) 809 result = DFCS_INACTIVE; 810 else if (isPressed(o)) // Active supersedes hover 811 result = DFCS_PUSHED; 812 else if (isHovered(o)) 813 result = DFCS_HOT; 814 if (isChecked(o)) 815 result |= DFCS_CHECKED; 816 return result; 817} 818 819ThemeData RenderThemeChromiumWin::getThemeData(RenderObject* o) 820{ 821 ThemeData result; 822 switch (o->style()->appearance()) { 823 case CheckboxPart: 824 result.m_part = BP_CHECKBOX; 825 result.m_state = determineState(o); 826 result.m_classicState = DFCS_BUTTONCHECK; 827 break; 828 case RadioPart: 829 result.m_part = BP_RADIOBUTTON; 830 result.m_state = determineState(o); 831 result.m_classicState = DFCS_BUTTONRADIO; 832 break; 833 case PushButtonPart: 834 case ButtonPart: 835 result.m_part = BP_PUSHBUTTON; 836 result.m_state = determineState(o); 837 result.m_classicState = DFCS_BUTTONPUSH; 838 break; 839 case SliderHorizontalPart: 840 result.m_part = TKP_TRACK; 841 result.m_state = TRS_NORMAL; 842 break; 843 case SliderVerticalPart: 844 result.m_part = TKP_TRACKVERT; 845 result.m_state = TRVS_NORMAL; 846 break; 847 case SliderThumbHorizontalPart: 848 result.m_part = TKP_THUMBBOTTOM; 849 result.m_state = determineSliderThumbState(o); 850 break; 851 case SliderThumbVerticalPart: 852 result.m_part = TKP_THUMBVERT; 853 result.m_state = determineSliderThumbState(o); 854 break; 855 case ListboxPart: 856 case MenulistPart: 857 case SearchFieldPart: 858 case TextFieldPart: 859 case TextAreaPart: 860 result.m_part = EP_EDITTEXT; 861 result.m_state = determineState(o); 862 break; 863 } 864 865 result.m_classicState |= determineClassicState(o); 866 867 return result; 868} 869 870bool RenderThemeChromiumWin::paintTextFieldInternal(RenderObject* o, 871 const RenderObject::PaintInfo& i, 872 const IntRect& r, 873 bool drawEdges) 874{ 875 // Nasty hack to make us not paint the border on text fields with a 876 // border-radius. Webkit paints elements with border-radius for us. 877 // FIXME: Get rid of this if-check once we can properly clip rounded 878 // borders: http://b/1112604 and http://b/1108635 879 // FIXME: make sure we do the right thing if css background-clip is set. 880 if (o->style()->hasBorderRadius()) 881 return false; 882 883 const ThemeData& themeData = getThemeData(o); 884 885 // Fallback to white if the specified color object is invalid. 886 Color backgroundColor(Color::white); 887 if (o->style()->backgroundColor().isValid()) { 888 backgroundColor = o->style()->backgroundColor(); 889 } 890 891 // If we have background-image, don't fill the content area to expose the 892 // parent's background. Also, we shouldn't fill the content area if the 893 // alpha of the color is 0. The API of Windows GDI ignores the alpha. 894 // 895 // Note that we should paint the content area white if we have neither the 896 // background color nor background image explicitly specified to keep the 897 // appearance of select element consistent with other browsers. 898 bool fillContentArea = !o->style()->hasBackgroundImage() && backgroundColor.alpha() != 0; 899 900 WebCore::ThemePainter painter(i.context, r); 901 ChromiumBridge::paintTextField(painter.context(), 902 themeData.m_part, 903 themeData.m_state, 904 themeData.m_classicState, 905 painter.drawRect(), 906 backgroundColor, 907 fillContentArea, 908 drawEdges); 909 return false; 910} 911 912int RenderThemeChromiumWin::menuListInternalPadding(RenderStyle* style, int paddingType) const 913{ 914 // This internal padding is in addition to the user-supplied padding. 915 // Matches the FF behavior. 916 int padding = styledMenuListInternalPadding[paddingType]; 917 918 // Reserve the space for right arrow here. The rest of the padding is set 919 // by adjustMenuListStyle, since PopupMenuChromium.cpp uses the padding 920 // from RenderMenuList to lay out the individual items in the popup. If 921 // the MenuList actually has appearance "NoAppearance", then that means we 922 // don't draw a button, so don't reserve space for it. 923 const int barType = style->direction() == LTR ? RightPadding : LeftPadding; 924 if (paddingType == barType && style->appearance() != NoControlPart) 925 padding += ScrollbarTheme::nativeTheme()->scrollbarThickness(); 926 927 return padding; 928} 929 930} // namespace WebCore 931