1 /*
2 * Copyright (C) 2006, 2007 Apple Inc.
3 * Copyright (C) 2009 Kenneth Rohde Christiansen
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
14 *
15 * You should have received a copy of the GNU Library General Public License
16 * along with this library; see the file COPYING.LIB. If not, write to
17 * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 * Boston, MA 02111-1307, USA.
19 *
20 */
21
22 #include "config.h"
23 #include "RenderThemeWin.h"
24
25 #include "CSSValueKeywords.h"
26 #include "Element.h"
27 #include "Frame.h"
28 #include "GraphicsContext.h"
29 #include "RenderSlider.h"
30 #include "Settings.h"
31 #include "SoftLinking.h"
32 #include "SystemInfo.h"
33 #include "UserAgentStyleSheets.h"
34
35 #if ENABLE(VIDEO)
36 #include "RenderMediaControls.h"
37 #endif
38
39 #include <tchar.h>
40
41 /*
42 * The following constants are used to determine how a widget is drawn using
43 * Windows' Theme API. For more information on theme parts and states see
44 * http://msdn.microsoft.com/library/default.asp?url=/library/en-us/shellcc/platform/commctls/userex/topics/partsandstates.asp
45 */
46
47 // Generic state constants
48 #define TS_NORMAL 1
49 #define TS_HOVER 2
50 #define TS_ACTIVE 3
51 #define TS_DISABLED 4
52 #define TS_FOCUSED 5
53
54 // Button constants
55 #define BP_BUTTON 1
56 #define BP_RADIO 2
57 #define BP_CHECKBOX 3
58
59 // Textfield constants
60 #define TFP_TEXTFIELD 1
61 #define EP_EDITBORDER_NOSCROLL 6
62 #define TFS_READONLY 6
63
64 // ComboBox constants (from vsstyle.h)
65 #define CP_DROPDOWNBUTTON 1
66 #define CP_BORDER 4
67 #define CP_READONLY 5
68 #define CP_DROPDOWNBUTTONRIGHT 6
69
70 // TrackBar (slider) parts
71 #define TKP_TRACK 1
72 #define TKP_TRACKVERT 2
73
74 // TrackBar (slider) thumb parts
75 #define TKP_THUMBBOTTOM 4
76 #define TKP_THUMBTOP 5
77 #define TKP_THUMBLEFT 7
78 #define TKP_THUMBRIGHT 8
79
80 // Trackbar (slider) thumb states
81 #define TUS_NORMAL 1
82 #define TUS_HOT 2
83 #define TUS_PRESSED 3
84 #define TUS_FOCUSED 4
85 #define TUS_DISABLED 5
86
87 // button states
88 #define PBS_NORMAL 1
89 #define PBS_HOT 2
90 #define PBS_PRESSED 3
91 #define PBS_DISABLED 4
92 #define PBS_DEFAULTED 5
93
94 SOFT_LINK_LIBRARY(uxtheme)
95 SOFT_LINK(uxtheme, OpenThemeData, HANDLE, WINAPI, (HWND hwnd, LPCWSTR pszClassList), (hwnd, pszClassList))
96 SOFT_LINK(uxtheme, CloseThemeData, HRESULT, WINAPI, (HANDLE hTheme), (hTheme))
97 SOFT_LINK(uxtheme, DrawThemeBackground, HRESULT, WINAPI, (HANDLE hTheme, HDC hdc, int iPartId, int iStateId, const RECT* pRect, const RECT* pClipRect), (hTheme, hdc, iPartId, iStateId, pRect, pClipRect))
98 SOFT_LINK(uxtheme, IsThemeActive, BOOL, WINAPI, (), ())
99 SOFT_LINK(uxtheme, IsThemeBackgroundPartiallyTransparent, BOOL, WINAPI, (HANDLE hTheme, int iPartId, int iStateId), (hTheme, iPartId, iStateId))
100
101 static bool haveTheme;
102
103 static const unsigned vistaMenuListButtonOutset = 1;
104
105 using namespace std;
106
107 namespace WebCore {
108
109 // This is the fixed width IE and Firefox use for buttons on dropdown menus
110 static const int dropDownButtonWidth = 17;
111
112 static const int shell32MagnifierIconIndex = 22;
113
114 // Default font size to match Firefox.
115 static const float defaultControlFontPixelSize = 13;
116
117 static const float defaultCancelButtonSize = 9;
118 static const float minCancelButtonSize = 5;
119 static const float maxCancelButtonSize = 21;
120 static const float defaultSearchFieldResultsDecorationSize = 13;
121 static const float minSearchFieldResultsDecorationSize = 9;
122 static const float maxSearchFieldResultsDecorationSize = 30;
123 static const float defaultSearchFieldResultsButtonWidth = 18;
124
125 static bool gWebKitIsBeingUnloaded;
126
documentIsInApplicationChromeMode(const Document * document)127 static bool documentIsInApplicationChromeMode(const Document* document)
128 {
129 Settings* settings = document->settings();
130 return settings && settings->inApplicationChromeMode();
131 }
132
setWebKitIsBeingUnloaded()133 void RenderThemeWin::setWebKitIsBeingUnloaded()
134 {
135 gWebKitIsBeingUnloaded = true;
136 }
137
create()138 PassRefPtr<RenderTheme> RenderThemeWin::create()
139 {
140 return adoptRef(new RenderThemeWin);
141 }
142
143 #if !USE(SAFARI_THEME)
themeForPage(Page * page)144 PassRefPtr<RenderTheme> RenderTheme::themeForPage(Page* page)
145 {
146 static RenderTheme* winTheme = RenderThemeWin::create().releaseRef();
147 return winTheme;
148 }
149 #endif
150
RenderThemeWin()151 RenderThemeWin::RenderThemeWin()
152 : m_buttonTheme(0)
153 , m_textFieldTheme(0)
154 , m_menuListTheme(0)
155 , m_sliderTheme(0)
156 {
157 haveTheme = uxthemeLibrary() && IsThemeActive();
158 }
159
~RenderThemeWin()160 RenderThemeWin::~RenderThemeWin()
161 {
162 // If WebKit is being unloaded, then uxtheme.dll is no longer available.
163 if (gWebKitIsBeingUnloaded || !uxthemeLibrary())
164 return;
165 close();
166 }
167
buttonTheme() const168 HANDLE RenderThemeWin::buttonTheme() const
169 {
170 if (haveTheme && !m_buttonTheme)
171 m_buttonTheme = OpenThemeData(0, L"Button");
172 return m_buttonTheme;
173 }
174
textFieldTheme() const175 HANDLE RenderThemeWin::textFieldTheme() const
176 {
177 if (haveTheme && !m_textFieldTheme)
178 m_textFieldTheme = OpenThemeData(0, L"Edit");
179 return m_textFieldTheme;
180 }
181
menuListTheme() const182 HANDLE RenderThemeWin::menuListTheme() const
183 {
184 if (haveTheme && !m_menuListTheme)
185 m_menuListTheme = OpenThemeData(0, L"ComboBox");
186 return m_menuListTheme;
187 }
188
sliderTheme() const189 HANDLE RenderThemeWin::sliderTheme() const
190 {
191 if (haveTheme && !m_sliderTheme)
192 m_sliderTheme = OpenThemeData(0, L"TrackBar");
193 return m_sliderTheme;
194 }
195
close()196 void RenderThemeWin::close()
197 {
198 // This method will need to be called when the OS theme changes to flush our cached themes.
199 if (m_buttonTheme)
200 CloseThemeData(m_buttonTheme);
201 if (m_textFieldTheme)
202 CloseThemeData(m_textFieldTheme);
203 if (m_menuListTheme)
204 CloseThemeData(m_menuListTheme);
205 if (m_sliderTheme)
206 CloseThemeData(m_sliderTheme);
207 m_buttonTheme = m_textFieldTheme = m_menuListTheme = m_sliderTheme = 0;
208
209 haveTheme = uxthemeLibrary() && IsThemeActive();
210 }
211
themeChanged()212 void RenderThemeWin::themeChanged()
213 {
214 close();
215 }
216
extraDefaultStyleSheet()217 String RenderThemeWin::extraDefaultStyleSheet()
218 {
219 return String(themeWinUserAgentStyleSheet, sizeof(themeWinUserAgentStyleSheet));
220 }
221
extraQuirksStyleSheet()222 String RenderThemeWin::extraQuirksStyleSheet()
223 {
224 return String(themeWinQuirksUserAgentStyleSheet, sizeof(themeWinQuirksUserAgentStyleSheet));
225 }
226
supportsHover(const RenderStyle *) const227 bool RenderThemeWin::supportsHover(const RenderStyle*) const
228 {
229 // The Classic/2k look has no hover effects.
230 return haveTheme;
231 }
232
platformActiveSelectionBackgroundColor() const233 Color RenderThemeWin::platformActiveSelectionBackgroundColor() const
234 {
235 COLORREF color = GetSysColor(COLOR_HIGHLIGHT);
236 return Color(GetRValue(color), GetGValue(color), GetBValue(color));
237 }
238
platformInactiveSelectionBackgroundColor() const239 Color RenderThemeWin::platformInactiveSelectionBackgroundColor() const
240 {
241 // This color matches Firefox.
242 return Color(176, 176, 176);
243 }
244
platformActiveSelectionForegroundColor() const245 Color RenderThemeWin::platformActiveSelectionForegroundColor() const
246 {
247 COLORREF color = GetSysColor(COLOR_HIGHLIGHTTEXT);
248 return Color(GetRValue(color), GetGValue(color), GetBValue(color));
249 }
250
platformInactiveSelectionForegroundColor() const251 Color RenderThemeWin::platformInactiveSelectionForegroundColor() const
252 {
253 return platformActiveSelectionForegroundColor();
254 }
255
fillFontDescription(FontDescription & fontDescription,LOGFONT & logFont,float fontSize)256 static void fillFontDescription(FontDescription& fontDescription, LOGFONT& logFont, float fontSize)
257 {
258 fontDescription.setIsAbsoluteSize(true);
259 fontDescription.setGenericFamily(FontDescription::NoFamily);
260 fontDescription.firstFamily().setFamily(String(logFont.lfFaceName));
261 fontDescription.setSpecifiedSize(fontSize);
262 fontDescription.setWeight(logFont.lfWeight >= 700 ? FontWeightBold : FontWeightNormal); // FIXME: Use real weight.
263 fontDescription.setItalic(logFont.lfItalic);
264 }
265
fillFontDescription(FontDescription & fontDescription,LOGFONT & logFont)266 static void fillFontDescription(FontDescription& fontDescription, LOGFONT& logFont)
267 {
268 fillFontDescription(fontDescription, logFont, abs(logFont.lfHeight));
269 }
270
systemFont(int propId,FontDescription & fontDescription) const271 void RenderThemeWin::systemFont(int propId, FontDescription& fontDescription) const
272 {
273 static FontDescription captionFont;
274 static FontDescription controlFont;
275 static FontDescription smallCaptionFont;
276 static FontDescription menuFont;
277 static FontDescription iconFont;
278 static FontDescription messageBoxFont;
279 static FontDescription statusBarFont;
280 static FontDescription systemFont;
281
282 static bool initialized;
283 static NONCLIENTMETRICS ncm;
284
285 if (!initialized) {
286 initialized = true;
287 ncm.cbSize = sizeof(NONCLIENTMETRICS);
288 ::SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(ncm), &ncm, 0);
289 }
290
291 switch (propId) {
292 case CSSValueIcon: {
293 if (!iconFont.isAbsoluteSize()) {
294 LOGFONT logFont;
295 ::SystemParametersInfo(SPI_GETICONTITLELOGFONT, sizeof(logFont), &logFont, 0);
296 fillFontDescription(iconFont, logFont);
297 }
298 fontDescription = iconFont;
299 break;
300 }
301 case CSSValueMenu:
302 if (!menuFont.isAbsoluteSize())
303 fillFontDescription(menuFont, ncm.lfMenuFont);
304 fontDescription = menuFont;
305 break;
306 case CSSValueMessageBox:
307 if (!messageBoxFont.isAbsoluteSize())
308 fillFontDescription(messageBoxFont, ncm.lfMessageFont);
309 fontDescription = messageBoxFont;
310 break;
311 case CSSValueStatusBar:
312 if (!statusBarFont.isAbsoluteSize())
313 fillFontDescription(statusBarFont, ncm.lfStatusFont);
314 fontDescription = statusBarFont;
315 break;
316 case CSSValueCaption:
317 if (!captionFont.isAbsoluteSize())
318 fillFontDescription(captionFont, ncm.lfCaptionFont);
319 fontDescription = captionFont;
320 break;
321 case CSSValueSmallCaption:
322 if (!smallCaptionFont.isAbsoluteSize())
323 fillFontDescription(smallCaptionFont, ncm.lfSmCaptionFont);
324 fontDescription = smallCaptionFont;
325 break;
326 case CSSValueWebkitSmallControl:
327 case CSSValueWebkitMiniControl: // Just map to small.
328 case CSSValueWebkitControl: // Just map to small.
329 if (!controlFont.isAbsoluteSize()) {
330 HGDIOBJ hGDI = ::GetStockObject(DEFAULT_GUI_FONT);
331 if (hGDI) {
332 LOGFONT logFont;
333 if (::GetObject(hGDI, sizeof(logFont), &logFont) > 0)
334 fillFontDescription(controlFont, logFont, defaultControlFontPixelSize);
335 }
336 }
337 fontDescription = controlFont;
338 break;
339 default: { // Everything else uses the stock GUI font.
340 if (!systemFont.isAbsoluteSize()) {
341 HGDIOBJ hGDI = ::GetStockObject(DEFAULT_GUI_FONT);
342 if (hGDI) {
343 LOGFONT logFont;
344 if (::GetObject(hGDI, sizeof(logFont), &logFont) > 0)
345 fillFontDescription(systemFont, logFont);
346 }
347 }
348 fontDescription = systemFont;
349 }
350 }
351 }
352
supportsFocus(ControlPart appearance) const353 bool RenderThemeWin::supportsFocus(ControlPart appearance) const
354 {
355 switch (appearance) {
356 case PushButtonPart:
357 case ButtonPart:
358 case DefaultButtonPart:
359 return true;
360 default:
361 return false;
362 }
363 }
364
supportsFocusRing(const RenderStyle * style) const365 bool RenderThemeWin::supportsFocusRing(const RenderStyle* style) const
366 {
367 return supportsFocus(style->appearance());
368 }
369
determineClassicState(RenderObject * o)370 unsigned RenderThemeWin::determineClassicState(RenderObject* o)
371 {
372 unsigned state = 0;
373 switch (o->style()->appearance()) {
374 case PushButtonPart:
375 case ButtonPart:
376 case DefaultButtonPart:
377 state = DFCS_BUTTONPUSH;
378 if (!isEnabled(o))
379 state |= DFCS_INACTIVE;
380 else if (isPressed(o))
381 state |= DFCS_PUSHED;
382 break;
383 case RadioPart:
384 case CheckboxPart:
385 state = (o->style()->appearance() == RadioPart) ? DFCS_BUTTONRADIO : DFCS_BUTTONCHECK;
386 if (isChecked(o))
387 state |= DFCS_CHECKED;
388 if (!isEnabled(o))
389 state |= DFCS_INACTIVE;
390 else if (isPressed(o))
391 state |= DFCS_PUSHED;
392 break;
393 case MenulistPart:
394 state = DFCS_SCROLLCOMBOBOX;
395 if (!isEnabled(o))
396 state |= DFCS_INACTIVE;
397 else if (isPressed(o))
398 state |= DFCS_PUSHED;
399 default:
400 break;
401 }
402 return state;
403 }
404
determineState(RenderObject * o)405 unsigned RenderThemeWin::determineState(RenderObject* o)
406 {
407 unsigned result = TS_NORMAL;
408 ControlPart appearance = o->style()->appearance();
409 if (!isEnabled(o))
410 result = TS_DISABLED;
411 else if (isReadOnlyControl(o) && (TextFieldPart == appearance || TextAreaPart == appearance || SearchFieldPart == appearance))
412 result = TFS_READONLY; // Readonly is supported on textfields.
413 else if (isPressed(o)) // Active overrides hover and focused.
414 result = TS_ACTIVE;
415 else if (supportsFocus(appearance) && isFocused(o))
416 result = TS_FOCUSED;
417 else if (isHovered(o))
418 result = TS_HOVER;
419 if (isChecked(o))
420 result += 4; // 4 unchecked states, 4 checked states.
421 return result;
422 }
423
determineSliderThumbState(RenderObject * o)424 unsigned RenderThemeWin::determineSliderThumbState(RenderObject* o)
425 {
426 unsigned result = TUS_NORMAL;
427 if (!isEnabled(o->parent()))
428 result = TUS_DISABLED;
429 else if (supportsFocus(o->style()->appearance()) && isFocused(o->parent()))
430 result = TUS_FOCUSED;
431 else if (toRenderSlider(o->parent())->inDragMode())
432 result = TUS_PRESSED;
433 else if (isHovered(o))
434 result = TUS_HOT;
435 return result;
436 }
437
determineButtonState(RenderObject * o)438 unsigned RenderThemeWin::determineButtonState(RenderObject* o)
439 {
440 unsigned result = PBS_NORMAL;
441 if (!isEnabled(o))
442 result = PBS_DISABLED;
443 else if (isPressed(o))
444 result = PBS_PRESSED;
445 else if (supportsFocus(o->style()->appearance()) && isFocused(o))
446 result = PBS_DEFAULTED;
447 else if (isHovered(o))
448 result = PBS_HOT;
449 else if (isDefault(o))
450 result = PBS_DEFAULTED;
451 return result;
452 }
453
getClassicThemeData(RenderObject * o)454 ThemeData RenderThemeWin::getClassicThemeData(RenderObject* o)
455 {
456 ThemeData result;
457 switch (o->style()->appearance()) {
458 case PushButtonPart:
459 case ButtonPart:
460 case DefaultButtonPart:
461 case CheckboxPart:
462 case RadioPart:
463 result.m_part = DFC_BUTTON;
464 result.m_state = determineClassicState(o);
465 break;
466 case MenulistPart:
467 result.m_part = DFC_SCROLL;
468 result.m_state = determineClassicState(o);
469 break;
470 case SearchFieldPart:
471 case TextFieldPart:
472 case TextAreaPart:
473 result.m_part = TFP_TEXTFIELD;
474 result.m_state = determineState(o);
475 break;
476 case SliderHorizontalPart:
477 result.m_part = TKP_TRACK;
478 result.m_state = TS_NORMAL;
479 break;
480 case SliderVerticalPart:
481 result.m_part = TKP_TRACKVERT;
482 result.m_state = TS_NORMAL;
483 break;
484 case SliderThumbHorizontalPart:
485 result.m_part = TKP_THUMBBOTTOM;
486 result.m_state = determineSliderThumbState(o);
487 break;
488 case SliderThumbVerticalPart:
489 result.m_part = TKP_THUMBRIGHT;
490 result.m_state = determineSliderThumbState(o);
491 break;
492 default:
493 break;
494 }
495 return result;
496 }
497
getThemeData(RenderObject * o)498 ThemeData RenderThemeWin::getThemeData(RenderObject* o)
499 {
500 if (!haveTheme)
501 return getClassicThemeData(o);
502
503 ThemeData result;
504 switch (o->style()->appearance()) {
505 case PushButtonPart:
506 case ButtonPart:
507 case DefaultButtonPart:
508 result.m_part = BP_BUTTON;
509 result.m_state = determineButtonState(o);
510 break;
511 case CheckboxPart:
512 result.m_part = BP_CHECKBOX;
513 result.m_state = determineState(o);
514 break;
515 case MenulistPart:
516 case MenulistButtonPart:
517 result.m_part = isRunningOnVistaOrLater() ? CP_DROPDOWNBUTTONRIGHT : CP_DROPDOWNBUTTON;
518 if (isRunningOnVistaOrLater() && documentIsInApplicationChromeMode(o->document())) {
519 // The "readonly" look we use in application chrome mode
520 // only uses a "normal" look for the drop down button.
521 result.m_state = TS_NORMAL;
522 } else
523 result.m_state = determineState(o);
524 break;
525 case RadioPart:
526 result.m_part = BP_RADIO;
527 result.m_state = determineState(o);
528 break;
529 case SearchFieldPart:
530 case TextFieldPart:
531 case TextAreaPart:
532 result.m_part = isRunningOnVistaOrLater() ? EP_EDITBORDER_NOSCROLL : TFP_TEXTFIELD;
533 result.m_state = determineState(o);
534 break;
535 case SliderHorizontalPart:
536 result.m_part = TKP_TRACK;
537 result.m_state = TS_NORMAL;
538 break;
539 case SliderVerticalPart:
540 result.m_part = TKP_TRACKVERT;
541 result.m_state = TS_NORMAL;
542 break;
543 case SliderThumbHorizontalPart:
544 result.m_part = TKP_THUMBBOTTOM;
545 result.m_state = determineSliderThumbState(o);
546 break;
547 case SliderThumbVerticalPart:
548 result.m_part = TKP_THUMBRIGHT;
549 result.m_state = determineSliderThumbState(o);
550 break;
551 }
552
553 return result;
554 }
555
drawControl(GraphicsContext * context,RenderObject * o,HANDLE theme,const ThemeData & themeData,const IntRect & r)556 static void drawControl(GraphicsContext* context, RenderObject* o, HANDLE theme, const ThemeData& themeData, const IntRect& r)
557 {
558 bool alphaBlend = false;
559 if (theme)
560 alphaBlend = IsThemeBackgroundPartiallyTransparent(theme, themeData.m_part, themeData.m_state);
561 HDC hdc = context->getWindowsContext(r, alphaBlend);
562 RECT widgetRect = r;
563 if (theme)
564 DrawThemeBackground(theme, hdc, themeData.m_part, themeData.m_state, &widgetRect, NULL);
565 else {
566 if (themeData.m_part == TFP_TEXTFIELD) {
567 ::DrawEdge(hdc, &widgetRect, EDGE_SUNKEN, BF_RECT | BF_ADJUST);
568 if (themeData.m_state == TS_DISABLED || themeData.m_state == TFS_READONLY)
569 ::FillRect(hdc, &widgetRect, (HBRUSH)(COLOR_BTNFACE+1));
570 else
571 ::FillRect(hdc, &widgetRect, (HBRUSH)(COLOR_WINDOW+1));
572 } else if (themeData.m_part == TKP_TRACK || themeData.m_part == TKP_TRACKVERT) {
573 ::DrawEdge(hdc, &widgetRect, EDGE_SUNKEN, BF_RECT | BF_ADJUST);
574 ::FillRect(hdc, &widgetRect, (HBRUSH)GetStockObject(GRAY_BRUSH));
575 } else if ((o->style()->appearance() == SliderThumbHorizontalPart ||
576 o->style()->appearance() == SliderThumbVerticalPart) &&
577 (themeData.m_part == TKP_THUMBBOTTOM || themeData.m_part == TKP_THUMBTOP ||
578 themeData.m_part == TKP_THUMBLEFT || themeData.m_part == TKP_THUMBRIGHT)) {
579 ::DrawEdge(hdc, &widgetRect, EDGE_RAISED, BF_RECT | BF_SOFT | BF_MIDDLE | BF_ADJUST);
580 if (themeData.m_state == TUS_DISABLED) {
581 static WORD patternBits[8] = {0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55};
582 HBITMAP patternBmp = ::CreateBitmap(8, 8, 1, 1, patternBits);
583 if (patternBmp) {
584 HBRUSH brush = (HBRUSH) ::CreatePatternBrush(patternBmp);
585 COLORREF oldForeColor = ::SetTextColor(hdc, ::GetSysColor(COLOR_3DFACE));
586 COLORREF oldBackColor = ::SetBkColor(hdc, ::GetSysColor(COLOR_3DHILIGHT));
587 POINT p;
588 ::GetViewportOrgEx(hdc, &p);
589 ::SetBrushOrgEx(hdc, p.x + widgetRect.left, p.y + widgetRect.top, NULL);
590 HBRUSH oldBrush = (HBRUSH) ::SelectObject(hdc, brush);
591 ::FillRect(hdc, &widgetRect, brush);
592 ::SetTextColor(hdc, oldForeColor);
593 ::SetBkColor(hdc, oldBackColor);
594 ::SelectObject(hdc, oldBrush);
595 ::DeleteObject(brush);
596 } else
597 ::FillRect(hdc, &widgetRect, (HBRUSH)COLOR_3DHILIGHT);
598 ::DeleteObject(patternBmp);
599 }
600 } else {
601 // Push buttons, buttons, checkboxes and radios, and the dropdown arrow in menulists.
602 if (o->style()->appearance() == DefaultButtonPart) {
603 HBRUSH brush = ::GetSysColorBrush(COLOR_3DDKSHADOW);
604 ::FrameRect(hdc, &widgetRect, brush);
605 ::InflateRect(&widgetRect, -1, -1);
606 ::DrawEdge(hdc, &widgetRect, BDR_RAISEDOUTER, BF_RECT | BF_MIDDLE);
607 }
608 ::DrawFrameControl(hdc, &widgetRect, themeData.m_part, themeData.m_state);
609 }
610 }
611 context->releaseWindowsContext(hdc, r, alphaBlend);
612 }
613
paintButton(RenderObject * o,const RenderObject::PaintInfo & i,const IntRect & r)614 bool RenderThemeWin::paintButton(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& r)
615 {
616 drawControl(i.context, o, buttonTheme(), getThemeData(o), r);
617 return false;
618 }
619
setCheckboxSize(RenderStyle * style) const620 void RenderThemeWin::setCheckboxSize(RenderStyle* style) const
621 {
622 // If the width and height are both specified, then we have nothing to do.
623 if (!style->width().isIntrinsicOrAuto() && !style->height().isAuto())
624 return;
625
626 // FIXME: A hard-coded size of 13 is used. This is wrong but necessary for now. It matches Firefox.
627 // At different DPI settings on Windows, querying the theme gives you a larger size that accounts for
628 // the higher DPI. Until our entire engine honors a DPI setting other than 96, we can't rely on the theme's
629 // metrics.
630 if (style->width().isIntrinsicOrAuto())
631 style->setWidth(Length(13, Fixed));
632 if (style->height().isAuto())
633 style->setHeight(Length(13, Fixed));
634 }
635
paintTextField(RenderObject * o,const RenderObject::PaintInfo & i,const IntRect & r)636 bool RenderThemeWin::paintTextField(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& r)
637 {
638 drawControl(i.context, o, textFieldTheme(), getThemeData(o), r);
639 return false;
640 }
641
paintMenuList(RenderObject * o,const RenderObject::PaintInfo & i,const IntRect & r)642 bool RenderThemeWin::paintMenuList(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& r)
643 {
644 HANDLE theme;
645 int part;
646 if (haveTheme && isRunningOnVistaOrLater()) {
647 theme = menuListTheme();
648 if (documentIsInApplicationChromeMode(o->document()))
649 part = CP_READONLY;
650 else
651 part = CP_BORDER;
652 } else {
653 theme = textFieldTheme();
654 part = TFP_TEXTFIELD;
655 }
656
657 drawControl(i.context, o, theme, ThemeData(part, determineState(o)), r);
658
659 return paintMenuListButton(o, i, r);
660 }
661
adjustMenuListStyle(CSSStyleSelector * selector,RenderStyle * style,Element * e) const662 void RenderThemeWin::adjustMenuListStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const
663 {
664 style->resetBorder();
665 adjustMenuListButtonStyle(selector, style, e);
666 }
667
adjustMenuListButtonStyle(CSSStyleSelector * selector,RenderStyle * style,Element * e) const668 void RenderThemeWin::adjustMenuListButtonStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const
669 {
670 // These are the paddings needed to place the text correctly in the <select> box
671 const int dropDownBoxPaddingTop = 2;
672 const int dropDownBoxPaddingRight = style->direction() == LTR ? 4 + dropDownButtonWidth : 4;
673 const int dropDownBoxPaddingBottom = 2;
674 const int dropDownBoxPaddingLeft = style->direction() == LTR ? 4 : 4 + dropDownButtonWidth;
675 // The <select> box must be at least 12px high for the button to render nicely on Windows
676 const int dropDownBoxMinHeight = 12;
677
678 // Position the text correctly within the select box and make the box wide enough to fit the dropdown button
679 style->setPaddingTop(Length(dropDownBoxPaddingTop, Fixed));
680 style->setPaddingRight(Length(dropDownBoxPaddingRight, Fixed));
681 style->setPaddingBottom(Length(dropDownBoxPaddingBottom, Fixed));
682 style->setPaddingLeft(Length(dropDownBoxPaddingLeft, Fixed));
683
684 // Height is locked to auto
685 style->setHeight(Length(Auto));
686
687 // Calculate our min-height
688 int minHeight = style->font().height();
689 minHeight = max(minHeight, dropDownBoxMinHeight);
690
691 style->setMinHeight(Length(minHeight, Fixed));
692
693 // White-space is locked to pre
694 style->setWhiteSpace(PRE);
695 }
696
paintMenuListButton(RenderObject * o,const RenderObject::PaintInfo & i,const IntRect & r)697 bool RenderThemeWin::paintMenuListButton(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& r)
698 {
699 // FIXME: Don't make hardcoded assumptions about the thickness of the textfield border.
700 int borderThickness = haveTheme ? 1 : 2;
701
702 // Paint the dropdown button on the inner edge of the text field,
703 // leaving space for the text field's 1px border
704 IntRect buttonRect(r);
705 buttonRect.inflate(-borderThickness);
706 if (o->style()->direction() == LTR)
707 buttonRect.setX(buttonRect.right() - dropDownButtonWidth);
708 buttonRect.setWidth(dropDownButtonWidth);
709
710 if (isRunningOnVistaOrLater()) {
711 // Outset the top, right, and bottom borders of the button so that they coincide with the <select>'s border.
712 buttonRect.setY(buttonRect.y() - vistaMenuListButtonOutset);
713 buttonRect.setHeight(buttonRect.height() + 2 * vistaMenuListButtonOutset);
714 buttonRect.setWidth(buttonRect.width() + vistaMenuListButtonOutset);
715 }
716
717 drawControl(i.context, o, menuListTheme(), getThemeData(o), buttonRect);
718
719 return false;
720 }
721
722 const int trackWidth = 4;
723
paintSliderTrack(RenderObject * o,const RenderObject::PaintInfo & i,const IntRect & r)724 bool RenderThemeWin::paintSliderTrack(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& r)
725 {
726 IntRect bounds = r;
727
728 if (o->style()->appearance() == SliderHorizontalPart) {
729 bounds.setHeight(trackWidth);
730 bounds.setY(r.y() + r.height() / 2 - trackWidth / 2);
731 } else if (o->style()->appearance() == SliderVerticalPart) {
732 bounds.setWidth(trackWidth);
733 bounds.setX(r.x() + r.width() / 2 - trackWidth / 2);
734 }
735
736 drawControl(i.context, o, sliderTheme(), getThemeData(o), bounds);
737 return false;
738 }
739
paintSliderThumb(RenderObject * o,const RenderObject::PaintInfo & i,const IntRect & r)740 bool RenderThemeWin::paintSliderThumb(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& r)
741 {
742 drawControl(i.context, o, sliderTheme(), getThemeData(o), r);
743 return false;
744 }
745
746 const int sliderThumbWidth = 7;
747 const int sliderThumbHeight = 15;
748
adjustSliderThumbSize(RenderObject * o) const749 void RenderThemeWin::adjustSliderThumbSize(RenderObject* o) const
750 {
751 if (o->style()->appearance() == SliderThumbVerticalPart) {
752 o->style()->setWidth(Length(sliderThumbHeight, Fixed));
753 o->style()->setHeight(Length(sliderThumbWidth, Fixed));
754 } else if (o->style()->appearance() == SliderThumbHorizontalPart) {
755 o->style()->setWidth(Length(sliderThumbWidth, Fixed));
756 o->style()->setHeight(Length(sliderThumbHeight, Fixed));
757 }
758 #if ENABLE(VIDEO)
759 else if (o->style()->appearance() == MediaSliderThumbPart)
760 RenderMediaControls::adjustMediaSliderThumbSize(o);
761 #endif
762 }
763
buttonInternalPaddingLeft() const764 int RenderThemeWin::buttonInternalPaddingLeft() const
765 {
766 return 3;
767 }
768
buttonInternalPaddingRight() const769 int RenderThemeWin::buttonInternalPaddingRight() const
770 {
771 return 3;
772 }
773
buttonInternalPaddingTop() const774 int RenderThemeWin::buttonInternalPaddingTop() const
775 {
776 return 1;
777 }
778
buttonInternalPaddingBottom() const779 int RenderThemeWin::buttonInternalPaddingBottom() const
780 {
781 return 1;
782 }
783
paintSearchField(RenderObject * o,const RenderObject::PaintInfo & i,const IntRect & r)784 bool RenderThemeWin::paintSearchField(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& r)
785 {
786 return paintTextField(o, i, r);
787 }
788
adjustSearchFieldStyle(CSSStyleSelector * selector,RenderStyle * style,Element * e) const789 void RenderThemeWin::adjustSearchFieldStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const
790 {
791 // Override paddingSize to match AppKit text positioning.
792 const int padding = 1;
793 style->setPaddingLeft(Length(padding, Fixed));
794 style->setPaddingRight(Length(padding, Fixed));
795 style->setPaddingTop(Length(padding, Fixed));
796 style->setPaddingBottom(Length(padding, Fixed));
797 if (e && e->focused() && e->document()->frame()->selection()->isFocusedAndActive())
798 style->setOutlineOffset(-2);
799 }
800
paintSearchFieldCancelButton(RenderObject * o,const RenderObject::PaintInfo & paintInfo,const IntRect & r)801 bool RenderThemeWin::paintSearchFieldCancelButton(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r)
802 {
803 IntRect bounds = r;
804 ASSERT(o->parent());
805 if (!o->parent() || !o->parent()->isBox())
806 return false;
807
808 RenderBox* parentRenderBox = toRenderBox(o->parent());
809
810 IntRect parentBox = parentRenderBox->absoluteContentBox();
811
812 // Make sure the scaled button stays square and will fit in its parent's box
813 bounds.setHeight(min(parentBox.width(), min(parentBox.height(), bounds.height())));
814 bounds.setWidth(bounds.height());
815
816 // Center the button vertically. Round up though, so if it has to be one pixel off-center, it will
817 // be one pixel closer to the bottom of the field. This tends to look better with the text.
818 bounds.setY(parentBox.y() + (parentBox.height() - bounds.height() + 1) / 2);
819
820 static Image* cancelImage = Image::loadPlatformResource("searchCancel").releaseRef();
821 static Image* cancelPressedImage = Image::loadPlatformResource("searchCancelPressed").releaseRef();
822 paintInfo.context->drawImage(isPressed(o) ? cancelPressedImage : cancelImage, bounds);
823 return false;
824 }
825
adjustSearchFieldCancelButtonStyle(CSSStyleSelector * selector,RenderStyle * style,Element * e) const826 void RenderThemeWin::adjustSearchFieldCancelButtonStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const
827 {
828 // Scale the button size based on the font size
829 float fontScale = style->fontSize() / defaultControlFontPixelSize;
830 int cancelButtonSize = lroundf(min(max(minCancelButtonSize, defaultCancelButtonSize * fontScale), maxCancelButtonSize));
831 style->setWidth(Length(cancelButtonSize, Fixed));
832 style->setHeight(Length(cancelButtonSize, Fixed));
833 }
834
adjustSearchFieldDecorationStyle(CSSStyleSelector * selector,RenderStyle * style,Element * e) const835 void RenderThemeWin::adjustSearchFieldDecorationStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const
836 {
837 IntSize emptySize(1, 11);
838 style->setWidth(Length(emptySize.width(), Fixed));
839 style->setHeight(Length(emptySize.height(), Fixed));
840 }
841
adjustSearchFieldResultsDecorationStyle(CSSStyleSelector * selector,RenderStyle * style,Element * e) const842 void RenderThemeWin::adjustSearchFieldResultsDecorationStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const
843 {
844 // Scale the decoration size based on the font size
845 float fontScale = style->fontSize() / defaultControlFontPixelSize;
846 int magnifierSize = lroundf(min(max(minSearchFieldResultsDecorationSize, defaultSearchFieldResultsDecorationSize * fontScale),
847 maxSearchFieldResultsDecorationSize));
848 style->setWidth(Length(magnifierSize, Fixed));
849 style->setHeight(Length(magnifierSize, Fixed));
850 }
851
paintSearchFieldResultsDecoration(RenderObject * o,const RenderObject::PaintInfo & paintInfo,const IntRect & r)852 bool RenderThemeWin::paintSearchFieldResultsDecoration(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r)
853 {
854 IntRect bounds = r;
855 ASSERT(o->parent());
856 if (!o->parent() || !o->parent()->isBox())
857 return false;
858
859 RenderBox* parentRenderBox = toRenderBox(o->parent());
860 IntRect parentBox = parentRenderBox->absoluteContentBox();
861
862 // Make sure the scaled decoration stays square and will fit in its parent's box
863 bounds.setHeight(min(parentBox.width(), min(parentBox.height(), bounds.height())));
864 bounds.setWidth(bounds.height());
865
866 // Center the decoration vertically. Round up though, so if it has to be one pixel off-center, it will
867 // be one pixel closer to the bottom of the field. This tends to look better with the text.
868 bounds.setY(parentBox.y() + (parentBox.height() - bounds.height() + 1) / 2);
869
870 static Image* magnifierImage = Image::loadPlatformResource("searchMagnifier").releaseRef();
871 paintInfo.context->drawImage(magnifierImage, bounds);
872 return false;
873 }
874
adjustSearchFieldResultsButtonStyle(CSSStyleSelector * selector,RenderStyle * style,Element * e) const875 void RenderThemeWin::adjustSearchFieldResultsButtonStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const
876 {
877 // Scale the button size based on the font size
878 float fontScale = style->fontSize() / defaultControlFontPixelSize;
879 int magnifierHeight = lroundf(min(max(minSearchFieldResultsDecorationSize, defaultSearchFieldResultsDecorationSize * fontScale),
880 maxSearchFieldResultsDecorationSize));
881 int magnifierWidth = lroundf(magnifierHeight * defaultSearchFieldResultsButtonWidth / defaultSearchFieldResultsDecorationSize);
882 style->setWidth(Length(magnifierWidth, Fixed));
883 style->setHeight(Length(magnifierHeight, Fixed));
884 }
885
paintSearchFieldResultsButton(RenderObject * o,const RenderObject::PaintInfo & paintInfo,const IntRect & r)886 bool RenderThemeWin::paintSearchFieldResultsButton(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r)
887 {
888 IntRect bounds = r;
889 ASSERT(o->parent());
890 if (!o->parent())
891 return false;
892 if (!o->parent() || !o->parent()->isBox())
893 return false;
894
895 RenderBox* parentRenderBox = toRenderBox(o->parent());
896 IntRect parentBox = parentRenderBox->absoluteContentBox();
897
898 // Make sure the scaled decoration will fit in its parent's box
899 bounds.setHeight(min(parentBox.height(), bounds.height()));
900 bounds.setWidth(min(parentBox.width(), static_cast<int>(bounds.height() * defaultSearchFieldResultsButtonWidth / defaultSearchFieldResultsDecorationSize)));
901
902 // Center the button vertically. Round up though, so if it has to be one pixel off-center, it will
903 // be one pixel closer to the bottom of the field. This tends to look better with the text.
904 bounds.setY(parentBox.y() + (parentBox.height() - bounds.height() + 1) / 2);
905
906 static Image* magnifierImage = Image::loadPlatformResource("searchMagnifierResults").releaseRef();
907 paintInfo.context->drawImage(magnifierImage, bounds);
908 return false;
909 }
910
911 // Map a CSSValue* system color to an index understood by GetSysColor
cssValueIdToSysColorIndex(int cssValueId)912 static int cssValueIdToSysColorIndex(int cssValueId)
913 {
914 switch (cssValueId) {
915 case CSSValueActiveborder: return COLOR_ACTIVEBORDER;
916 case CSSValueActivecaption: return COLOR_ACTIVECAPTION;
917 case CSSValueAppworkspace: return COLOR_APPWORKSPACE;
918 case CSSValueBackground: return COLOR_BACKGROUND;
919 case CSSValueButtonface: return COLOR_BTNFACE;
920 case CSSValueButtonhighlight: return COLOR_BTNHIGHLIGHT;
921 case CSSValueButtonshadow: return COLOR_BTNSHADOW;
922 case CSSValueButtontext: return COLOR_BTNTEXT;
923 case CSSValueCaptiontext: return COLOR_CAPTIONTEXT;
924 case CSSValueGraytext: return COLOR_GRAYTEXT;
925 case CSSValueHighlight: return COLOR_HIGHLIGHT;
926 case CSSValueHighlighttext: return COLOR_HIGHLIGHTTEXT;
927 case CSSValueInactiveborder: return COLOR_INACTIVEBORDER;
928 case CSSValueInactivecaption: return COLOR_INACTIVECAPTION;
929 case CSSValueInactivecaptiontext: return COLOR_INACTIVECAPTIONTEXT;
930 case CSSValueInfobackground: return COLOR_INFOBK;
931 case CSSValueInfotext: return COLOR_INFOTEXT;
932 case CSSValueMenu: return COLOR_MENU;
933 case CSSValueMenutext: return COLOR_MENUTEXT;
934 case CSSValueScrollbar: return COLOR_SCROLLBAR;
935 case CSSValueThreeddarkshadow: return COLOR_3DDKSHADOW;
936 case CSSValueThreedface: return COLOR_3DFACE;
937 case CSSValueThreedhighlight: return COLOR_3DHIGHLIGHT;
938 case CSSValueThreedlightshadow: return COLOR_3DLIGHT;
939 case CSSValueThreedshadow: return COLOR_3DSHADOW;
940 case CSSValueWindow: return COLOR_WINDOW;
941 case CSSValueWindowframe: return COLOR_WINDOWFRAME;
942 case CSSValueWindowtext: return COLOR_WINDOWTEXT;
943 default: return -1; // Unsupported CSSValue
944 }
945 }
946
systemColor(int cssValueId) const947 Color RenderThemeWin::systemColor(int cssValueId) const
948 {
949 int sysColorIndex = cssValueIdToSysColorIndex(cssValueId);
950 if (sysColorIndex == -1)
951 return RenderTheme::systemColor(cssValueId);
952
953 COLORREF color = GetSysColor(sysColorIndex);
954 return Color(GetRValue(color), GetGValue(color), GetBValue(color));
955 }
956
957 #if ENABLE(VIDEO)
paintMediaFullscreenButton(RenderObject * o,const RenderObject::PaintInfo & paintInfo,const IntRect & r)958 bool RenderThemeWin::paintMediaFullscreenButton(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r)
959 {
960 return RenderMediaControls::paintMediaControlsPart(MediaFullscreenButton, o, paintInfo, r);
961 }
962
paintMediaMuteButton(RenderObject * o,const RenderObject::PaintInfo & paintInfo,const IntRect & r)963 bool RenderThemeWin::paintMediaMuteButton(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r)
964 {
965 return RenderMediaControls::paintMediaControlsPart(MediaMuteButton, o, paintInfo, r);
966 }
967
paintMediaPlayButton(RenderObject * o,const RenderObject::PaintInfo & paintInfo,const IntRect & r)968 bool RenderThemeWin::paintMediaPlayButton(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r)
969 {
970 return RenderMediaControls::paintMediaControlsPart(MediaPlayButton, o, paintInfo, r);
971 }
972
paintMediaSeekBackButton(RenderObject * o,const RenderObject::PaintInfo & paintInfo,const IntRect & r)973 bool RenderThemeWin::paintMediaSeekBackButton(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r)
974 {
975 return RenderMediaControls::paintMediaControlsPart(MediaSeekBackButton, o, paintInfo, r);
976 }
977
paintMediaSeekForwardButton(RenderObject * o,const RenderObject::PaintInfo & paintInfo,const IntRect & r)978 bool RenderThemeWin::paintMediaSeekForwardButton(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r)
979 {
980 return RenderMediaControls::paintMediaControlsPart(MediaSeekForwardButton, o, paintInfo, r);
981 }
982
paintMediaSliderTrack(RenderObject * o,const RenderObject::PaintInfo & paintInfo,const IntRect & r)983 bool RenderThemeWin::paintMediaSliderTrack(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r)
984 {
985 return RenderMediaControls::paintMediaControlsPart(MediaSlider, o, paintInfo, r);
986 }
987
paintMediaSliderThumb(RenderObject * o,const RenderObject::PaintInfo & paintInfo,const IntRect & r)988 bool RenderThemeWin::paintMediaSliderThumb(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r)
989 {
990 return RenderMediaControls::paintMediaControlsPart(MediaSliderThumb, o, paintInfo, r);
991 }
992 #endif
993
994 }
995