Revert "2009-09-16 Albert J. Wong <ajwong@chromium.org>"
[WebKit.git] / WebCore / rendering / RenderThemeChromiumSkia.cpp
1 /*
2  * Copyright (C) 2007 Apple Inc.
3  * Copyright (C) 2007 Alp Toker <alp@atoker.com>
4  * Copyright (C) 2008 Collabora Ltd.
5  * Copyright (C) 2008, 2009 Google Inc.
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., 51 Franklin Street, Fifth Floor,
20  * Boston, MA 02110-1301, USA.
21  *
22  */
23
24 #include "config.h"
25 #include "RenderThemeChromiumSkia.h"
26
27 #include "ChromiumBridge.h"
28 #include "CSSValueKeywords.h"
29 #include "GraphicsContext.h"
30 #include "HTMLMediaElement.h"
31 #include "HTMLNames.h"
32 #include "Image.h"
33 #include "MediaControlElements.h"
34 #include "PlatformContextSkia.h"
35 #include "RenderBox.h"
36 #include "RenderObject.h"
37 #include "RenderSlider.h"
38 #include "ScrollbarTheme.h"
39 #include "TimeRanges.h"
40 #include "TransformationMatrix.h"
41 #include "UserAgentStyleSheets.h"
42
43 #include "SkShader.h"
44 #include "SkGradientShader.h"
45
46 namespace WebCore {
47
48 enum PaddingType {
49     TopPadding,
50     RightPadding,
51     BottomPadding,
52     LeftPadding
53 };
54
55 static const int styledMenuListInternalPadding[4] = { 1, 4, 1, 4 };
56
57 // These values all match Safari/Win.
58 static const float defaultControlFontPixelSize = 13;
59 static const float defaultCancelButtonSize = 9;
60 static const float minCancelButtonSize = 5;
61 static const float maxCancelButtonSize = 21;
62 static const float defaultSearchFieldResultsDecorationSize = 13;
63 static const float minSearchFieldResultsDecorationSize = 9;
64 static const float maxSearchFieldResultsDecorationSize = 30;
65 static const float defaultSearchFieldResultsButtonWidth = 18;
66
67 static void setSizeIfAuto(RenderStyle* style, const IntSize& size)
68 {
69     if (style->width().isIntrinsicOrAuto())
70         style->setWidth(Length(size.width(), Fixed));
71     if (style->height().isAuto())
72         style->setHeight(Length(size.height(), Fixed));
73 }
74
75 static void drawVertLine(SkCanvas* canvas, int x, int y1, int y2, const SkPaint& paint)
76 {
77     SkIRect skrect;
78     skrect.set(x, y1, x + 1, y2 + 1);
79     canvas->drawIRect(skrect, paint);
80 }
81
82 static void drawHorizLine(SkCanvas* canvas, int x1, int x2, int y, const SkPaint& paint)
83 {
84     SkIRect skrect;
85     skrect.set(x1, y, x2 + 1, y + 1);
86     canvas->drawIRect(skrect, paint);
87 }
88
89 static void drawBox(SkCanvas* canvas, const IntRect& rect, const SkPaint& paint)
90 {
91     const int right = rect.x() + rect.width() - 1;
92     const int bottom = rect.y() + rect.height() - 1;
93     drawHorizLine(canvas, rect.x(), right, rect.y(), paint);
94     drawVertLine(canvas, right, rect.y(), bottom, paint);
95     drawHorizLine(canvas, rect.x(), right, bottom, paint);
96     drawVertLine(canvas, rect.x(), rect.y(), bottom, paint);
97 }
98
99 #if ENABLE(VIDEO)
100 // Attempt to retrieve a HTMLMediaElement from a Node. Returns NULL if one cannot be found.
101 static HTMLMediaElement* mediaElementParent(Node* node)
102 {
103     if (!node)
104         return 0;
105     Node* mediaNode = node->shadowAncestorNode();
106     if (!mediaNode || (!mediaNode->hasTagName(HTMLNames::videoTag) && !mediaNode->hasTagName(HTMLNames::audioTag)))
107         return 0;
108
109     return static_cast<HTMLMediaElement*>(mediaNode);
110 }
111 #endif
112
113 // We aim to match IE here.
114 // -IE uses a font based on the encoding as the default font for form controls.
115 // -Gecko uses MS Shell Dlg (actually calls GetStockObject(DEFAULT_GUI_FONT),
116 // which returns MS Shell Dlg)
117 // -Safari uses Lucida Grande.
118 //
119 // FIXME: The only case where we know we don't match IE is for ANSI encodings.
120 // IE uses MS Shell Dlg there, which we render incorrectly at certain pixel
121 // sizes (e.g. 15px). So, for now we just use Arial.
122 const String& RenderThemeChromiumSkia::defaultGUIFont()
123 {
124     DEFINE_STATIC_LOCAL(String, fontFace, ("Arial"));
125     return fontFace;
126 }
127
128 float RenderThemeChromiumSkia::defaultFontSize = 16.0;
129
130 RenderThemeChromiumSkia::RenderThemeChromiumSkia()
131 {
132 }
133
134 RenderThemeChromiumSkia::~RenderThemeChromiumSkia()
135 {
136 }
137
138 // Use the Windows style sheets to match their metrics.
139 String RenderThemeChromiumSkia::extraDefaultStyleSheet()
140 {
141     return String(themeWinUserAgentStyleSheet, sizeof(themeWinUserAgentStyleSheet));
142 }
143
144 String RenderThemeChromiumSkia::extraQuirksStyleSheet()
145 {
146     return String(themeWinQuirksUserAgentStyleSheet, sizeof(themeWinQuirksUserAgentStyleSheet));
147 }
148
149 #if ENABLE(VIDEO)
150 String RenderThemeChromiumSkia::extraMediaControlsStyleSheet()
151 {
152     return String(mediaControlsChromiumUserAgentStyleSheet, sizeof(mediaControlsChromiumUserAgentStyleSheet));
153 }
154 #endif
155
156 bool RenderThemeChromiumSkia::supportsHover(const RenderStyle* style) const
157 {
158     return true;
159 }
160
161 bool RenderThemeChromiumSkia::supportsFocusRing(const RenderStyle* style) const
162 {
163     // This causes WebKit to draw the focus rings for us.
164     return false;
165 }
166
167 Color RenderThemeChromiumSkia::platformActiveSelectionBackgroundColor() const
168 {
169     return Color(0x1e, 0x90, 0xff);
170 }
171
172 Color RenderThemeChromiumSkia::platformInactiveSelectionBackgroundColor() const
173 {
174     return Color(0xc8, 0xc8, 0xc8);
175 }
176
177 Color RenderThemeChromiumSkia::platformActiveSelectionForegroundColor() const
178 {
179     return Color::black;
180 }
181
182 Color RenderThemeChromiumSkia::platformInactiveSelectionForegroundColor() const
183 {
184     return Color(0x32, 0x32, 0x32);
185 }
186
187 Color RenderThemeChromiumSkia::platformFocusRingColor() const
188 {
189     static Color focusRingColor(229, 151, 0, 255);
190     return focusRingColor;
191 }
192
193 double RenderThemeChromiumSkia::caretBlinkInterval() const
194 {
195     // Disable the blinking caret in layout test mode, as it introduces
196     // a race condition for the pixel tests. http://b/1198440
197     if (ChromiumBridge::layoutTestMode())
198         return 0;
199
200     return caretBlinkIntervalInternal();
201 }
202
203 void RenderThemeChromiumSkia::systemFont(int propId, FontDescription& fontDescription) const
204 {
205     float fontSize = defaultFontSize;
206
207     switch (propId) {
208     case CSSValueWebkitMiniControl:
209     case CSSValueWebkitSmallControl:
210     case CSSValueWebkitControl:
211         // Why 2 points smaller? Because that's what Gecko does. Note that we
212         // are assuming a 96dpi screen, which is the default that we use on
213         // Windows.
214         static const float pointsPerInch = 72.0f;
215         static const float pixelsPerInch = 96.0f;
216         fontSize -= (2.0f / pointsPerInch) * pixelsPerInch;
217         break;
218     }
219
220     fontDescription.firstFamily().setFamily(defaultGUIFont());
221     fontDescription.setSpecifiedSize(fontSize);
222     fontDescription.setIsAbsoluteSize(true);
223     fontDescription.setGenericFamily(FontDescription::NoFamily);
224     fontDescription.setWeight(FontWeightNormal);
225     fontDescription.setItalic(false);
226 }
227
228 int RenderThemeChromiumSkia::minimumMenuListSize(RenderStyle* style) const
229 {
230     return 0;
231 }
232
233 bool RenderThemeChromiumSkia::paintCheckbox(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& rect)
234 {
235     static Image* const checkedImage = Image::loadPlatformResource("linuxCheckboxOn").releaseRef();
236     static Image* const uncheckedImage = Image::loadPlatformResource("linuxCheckboxOff").releaseRef();
237     static Image* const disabledCheckedImage = Image::loadPlatformResource("linuxCheckboxDisabledOn").releaseRef();
238     static Image* const disabledUncheckedImage = Image::loadPlatformResource("linuxCheckboxDisabledOff").releaseRef();
239
240     Image* image;
241
242     if (this->isEnabled(o))
243         image = this->isChecked(o) ? checkedImage : uncheckedImage;
244     else
245         image = this->isChecked(o) ? disabledCheckedImage : disabledUncheckedImage;
246
247     i.context->drawImage(image, rect);
248     return false;
249 }
250
251 void RenderThemeChromiumSkia::setCheckboxSize(RenderStyle* style) const
252 {
253     // If the width and height are both specified, then we have nothing to do.
254     if (!style->width().isIntrinsicOrAuto() && !style->height().isAuto())
255         return;
256
257     // FIXME:  A hard-coded size of 13 is used.  This is wrong but necessary
258     // for now.  It matches Firefox.  At different DPI settings on Windows,
259     // querying the theme gives you a larger size that accounts for the higher
260     // DPI.  Until our entire engine honors a DPI setting other than 96, we
261     // can't rely on the theme's metrics.
262     const IntSize size(13, 13);
263     setSizeIfAuto(style, size);
264 }
265
266 bool RenderThemeChromiumSkia::paintRadio(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& rect)
267 {
268     static Image* const checkedImage = Image::loadPlatformResource("linuxRadioOn").releaseRef();
269     static Image* const uncheckedImage = Image::loadPlatformResource("linuxRadioOff").releaseRef();
270     static Image* const disabledCheckedImage = Image::loadPlatformResource("linuxRadioDisabledOn").releaseRef();
271     static Image* const disabledUncheckedImage = Image::loadPlatformResource("linuxRadioDisabledOff").releaseRef();
272
273     Image* image;
274     if (this->isEnabled(o))
275         image = this->isChecked(o) ? checkedImage : uncheckedImage;
276     else
277         image = this->isChecked(o) ? disabledCheckedImage : disabledUncheckedImage;
278
279     i.context->drawImage(image, rect);
280     return false;
281 }
282
283 void RenderThemeChromiumSkia::setRadioSize(RenderStyle* style) const
284 {
285     // Use same sizing for radio box as checkbox.
286     setCheckboxSize(style);
287 }
288
289 static SkColor brightenColor(double h, double s, double l, float brightenAmount)
290 {
291     l += brightenAmount;
292     if (l > 1.0)
293         l = 1.0;
294     if (l < 0.0)
295         l = 0.0;
296
297     return makeRGBAFromHSLA(h, s, l, 1.0);
298 }
299
300 static void paintButtonLike(RenderTheme* theme, RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& rect)
301 {
302     SkCanvas* const canvas = i.context->platformContext()->canvas();
303     SkPaint paint;
304     SkRect skrect;
305     const int right = rect.x() + rect.width();
306     const int bottom = rect.y() + rect.height();
307     SkColor baseColor = SkColorSetARGB(0xff, 0xdd, 0xdd, 0xdd);
308     if (o->style()->hasBackground())
309         baseColor = o->style()->backgroundColor().rgb();
310     double h, s, l;
311     Color(baseColor).getHSL(h, s, l);
312     // Our standard gradient is from 0xdd to 0xf8. This is the amount of
313     // increased luminance between those values.
314     SkColor lightColor(brightenColor(h, s, l, 0.105));
315
316     // If the button is too small, fallback to drawing a single, solid color
317     if (rect.width() < 5 || rect.height() < 5) {
318         paint.setColor(baseColor);
319         skrect.set(rect.x(), rect.y(), right, bottom);
320         canvas->drawRect(skrect, paint);
321         return;
322     }
323
324     const int borderAlpha = theme->isHovered(o) ? 0x80 : 0x55;
325     paint.setARGB(borderAlpha, 0, 0, 0);
326     canvas->drawLine(rect.x() + 1, rect.y(), right - 1, rect.y(), paint);
327     canvas->drawLine(right - 1, rect.y() + 1, right - 1, bottom - 1, paint);
328     canvas->drawLine(rect.x() + 1, bottom - 1, right - 1, bottom - 1, paint);
329     canvas->drawLine(rect.x(), rect.y() + 1, rect.x(), bottom - 1, paint);
330
331     paint.setColor(SK_ColorBLACK);
332     SkPoint p[2];
333     const int lightEnd = theme->isPressed(o) ? 1 : 0;
334     const int darkEnd = !lightEnd;
335     p[lightEnd].set(SkIntToScalar(rect.x()), SkIntToScalar(rect.y()));
336     p[darkEnd].set(SkIntToScalar(rect.x()), SkIntToScalar(bottom - 1));
337     SkColor colors[2];
338     colors[0] = lightColor;
339     colors[1] = baseColor;
340
341     SkShader* shader = SkGradientShader::CreateLinear(
342         p, colors, NULL, 2, SkShader::kClamp_TileMode, NULL);
343     paint.setStyle(SkPaint::kFill_Style);
344     paint.setShader(shader);
345     shader->unref();
346
347     skrect.set(rect.x() + 1, rect.y() + 1, right - 1, bottom - 1);
348     canvas->drawRect(skrect, paint);
349
350     paint.setShader(NULL);
351     paint.setColor(brightenColor(h, s, l, -0.0588));
352     canvas->drawPoint(rect.x() + 1, rect.y() + 1, paint);
353     canvas->drawPoint(right - 2, rect.y() + 1, paint);
354     canvas->drawPoint(rect.x() + 1, bottom - 2, paint);
355     canvas->drawPoint(right - 2, bottom - 2, paint);
356 }
357
358 bool RenderThemeChromiumSkia::paintButton(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& rect)
359 {
360     paintButtonLike(this, o, i, rect);
361     return false;
362 }
363
364 bool RenderThemeChromiumSkia::paintTextField(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& rect)
365 {
366     return true;
367 }
368
369 bool RenderThemeChromiumSkia::paintTextArea(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& r)
370 {
371     return paintTextField(o, i, r);
372 }
373
374 bool RenderThemeChromiumSkia::paintSearchField(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& r)
375 {
376     return paintTextField(o, i, r);
377 }
378
379 void RenderThemeChromiumSkia::adjustSearchFieldCancelButtonStyle(CSSStyleSelector*, RenderStyle* style, Element*) const
380 {
381     // Scale the button size based on the font size
382     float fontScale = style->fontSize() / defaultControlFontPixelSize;
383     int cancelButtonSize = lroundf(std::min(std::max(minCancelButtonSize, defaultCancelButtonSize * fontScale), maxCancelButtonSize));
384     style->setWidth(Length(cancelButtonSize, Fixed));
385     style->setHeight(Length(cancelButtonSize, Fixed));
386 }
387
388 bool RenderThemeChromiumSkia::paintSearchFieldCancelButton(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& r)
389 {
390     IntRect bounds = r;
391     ASSERT(o->parent());
392     if (!o->parent() || !o->parent()->isBox())
393         return false;
394
395     RenderBox* parentRenderBox = toRenderBox(o->parent());
396
397     IntRect parentBox = parentRenderBox->absoluteContentBox();
398
399     // Make sure the scaled button stays square and will fit in its parent's box
400     bounds.setHeight(std::min(parentBox.width(), std::min(parentBox.height(), bounds.height())));
401     bounds.setWidth(bounds.height());
402
403     // Center the button vertically.  Round up though, so if it has to be one pixel off-center, it will
404     // be one pixel closer to the bottom of the field.  This tends to look better with the text.
405     bounds.setY(parentBox.y() + (parentBox.height() - bounds.height() + 1) / 2);
406
407     static Image* cancelImage = Image::loadPlatformResource("searchCancel").releaseRef();
408     static Image* cancelPressedImage = Image::loadPlatformResource("searchCancelPressed").releaseRef();
409     i.context->drawImage(isPressed(o) ? cancelPressedImage : cancelImage, bounds);
410     return false;
411 }
412
413 void RenderThemeChromiumSkia::adjustSearchFieldDecorationStyle(CSSStyleSelector*, RenderStyle* style, Element*) const
414 {
415     IntSize emptySize(1, 11);
416     style->setWidth(Length(emptySize.width(), Fixed));
417     style->setHeight(Length(emptySize.height(), Fixed));
418 }
419
420 void RenderThemeChromiumSkia::adjustSearchFieldResultsDecorationStyle(CSSStyleSelector*, RenderStyle* style, Element*) const
421 {
422     // Scale the decoration size based on the font size
423     float fontScale = style->fontSize() / defaultControlFontPixelSize;
424     int magnifierSize = lroundf(std::min(std::max(minSearchFieldResultsDecorationSize, defaultSearchFieldResultsDecorationSize * fontScale),
425                                          maxSearchFieldResultsDecorationSize));
426     style->setWidth(Length(magnifierSize, Fixed));
427     style->setHeight(Length(magnifierSize, Fixed));
428 }
429
430 bool RenderThemeChromiumSkia::paintSearchFieldResultsDecoration(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& r)
431 {
432     IntRect bounds = r;
433     ASSERT(o->parent());
434     if (!o->parent() || !o->parent()->isBox())
435         return false;
436
437     RenderBox* parentRenderBox = toRenderBox(o->parent());
438     IntRect parentBox = parentRenderBox->absoluteContentBox();
439
440     // Make sure the scaled decoration stays square and will fit in its parent's box
441     bounds.setHeight(std::min(parentBox.width(), std::min(parentBox.height(), bounds.height())));
442     bounds.setWidth(bounds.height());
443
444     // Center the decoration vertically.  Round up though, so if it has to be one pixel off-center, it will
445     // be one pixel closer to the bottom of the field.  This tends to look better with the text.
446     bounds.setY(parentBox.y() + (parentBox.height() - bounds.height() + 1) / 2);
447
448     static Image* magnifierImage = Image::loadPlatformResource("searchMagnifier").releaseRef();
449     i.context->drawImage(magnifierImage, bounds);
450     return false;
451 }
452
453 void RenderThemeChromiumSkia::adjustSearchFieldResultsButtonStyle(CSSStyleSelector*, RenderStyle* style, Element*) const
454 {
455     // Scale the button size based on the font size
456     float fontScale = style->fontSize() / defaultControlFontPixelSize;
457     int magnifierHeight = lroundf(std::min(std::max(minSearchFieldResultsDecorationSize, defaultSearchFieldResultsDecorationSize * fontScale),
458                                            maxSearchFieldResultsDecorationSize));
459     int magnifierWidth = lroundf(magnifierHeight * defaultSearchFieldResultsButtonWidth / defaultSearchFieldResultsDecorationSize);
460     style->setWidth(Length(magnifierWidth, Fixed));
461     style->setHeight(Length(magnifierHeight, Fixed));
462 }
463
464 bool RenderThemeChromiumSkia::paintSearchFieldResultsButton(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& r)
465 {
466     IntRect bounds = r;
467     ASSERT(o->parent());
468     if (!o->parent())
469         return false;
470     if (!o->parent() || !o->parent()->isBox())
471         return false;
472
473     RenderBox* parentRenderBox = toRenderBox(o->parent());
474     IntRect parentBox = parentRenderBox->absoluteContentBox();
475
476     // Make sure the scaled decoration will fit in its parent's box
477     bounds.setHeight(std::min(parentBox.height(), bounds.height()));
478     bounds.setWidth(std::min(parentBox.width(), static_cast<int>(bounds.height() * defaultSearchFieldResultsButtonWidth / defaultSearchFieldResultsDecorationSize)));
479
480     // Center the button vertically.  Round up though, so if it has to be one pixel off-center, it will
481     // be one pixel closer to the bottom of the field.  This tends to look better with the text.
482     bounds.setY(parentBox.y() + (parentBox.height() - bounds.height() + 1) / 2);
483
484     static Image* magnifierImage = Image::loadPlatformResource("searchMagnifierResults").releaseRef();
485     i.context->drawImage(magnifierImage, bounds);
486     return false;
487 }
488
489 bool RenderThemeChromiumSkia::paintMediaButtonInternal(GraphicsContext* context, const IntRect& rect, Image* image)
490 {
491     // Create a destination rectangle for the image that is centered in the drawing rectangle, rounded left, and down.
492     IntRect imageRect = image->rect();
493     imageRect.setY(rect.y() + (rect.height() - image->height() + 1) / 2);
494     imageRect.setX(rect.x() + (rect.width() - image->width() + 1) / 2);
495
496     context->drawImage(image, imageRect);
497     return true;
498 }
499
500 bool RenderThemeChromiumSkia::paintMediaControlsBackground(RenderObject* object, const RenderObject::PaintInfo& paintInfo, const IntRect& rect)
501 {
502 #if ENABLE(VIDEO)
503     HTMLMediaElement* mediaElement = mediaElementParent(object->node());
504     if (!mediaElement)
505         return false;
506
507     if (!rect.isEmpty())
508     {
509         SkCanvas* canvas = paintInfo.context->platformContext()->canvas();
510         SkPaint paint;
511
512         // Draws the left border, it is always 1px wide.
513         paint.setColor(object->style()->borderLeftColor().rgb());
514         canvas->drawLine(rect.x() + 1, rect.y(),
515                          rect.x() + 1, rect.y() + rect.height(),
516                          paint);
517
518         // Draws the right border, it is always 1px wide.
519         paint.setColor(object->style()->borderRightColor().rgb());
520         canvas->drawLine(rect.x() + rect.width() - 1, rect.y(),
521                          rect.x() + rect.width() - 1, rect.y() + rect.height(),
522                          paint);
523     }
524     return true;
525 #else
526     UNUSED_PARAM(object);
527     UNUSED_PARAM(paintInfo);
528     UNUSED_PARAM(rect);
529     return false;
530 #endif
531 }
532
533 bool RenderThemeChromiumSkia::paintMediaSliderTrack(RenderObject* object, const RenderObject::PaintInfo& paintInfo, const IntRect& rect)
534 {
535 #if ENABLE(VIDEO)
536     HTMLMediaElement* mediaElement = mediaElementParent(object->node());
537     if (!mediaElement)
538         return false;
539
540     SkCanvas* canvas = paintInfo.context->platformContext()->canvas();
541     SkRect backgroundRect;
542     backgroundRect.set(rect.x(), rect.y(), rect.x() + rect.width(), rect.y() + rect.height());
543
544     SkPaint paint;
545     paint.setAntiAlias(true);
546
547     // Draw the border of the time bar. The border only has one single color,
548     // width and radius. So use the property of the left border.
549     SkColor borderColor = object->style()->borderLeftColor().rgb();
550     int borderWidth = object->style()->borderLeftWidth();
551     IntSize borderRadius = object->style()->borderTopLeftRadius();
552     paint.setStyle(SkPaint::kStroke_Style);
553     paint.setStrokeWidth(borderWidth);
554     paint.setColor(borderColor);
555     canvas->drawRoundRect(backgroundRect, borderRadius.width(), borderRadius.height(), paint);
556
557     // Draw the background of the time bar.
558     SkColor backgroundColor = object->style()->backgroundColor().rgb();
559     paint.setStyle(SkPaint::kFill_Style);
560     paint.setColor(backgroundColor);
561     canvas->drawRoundRect(backgroundRect, borderRadius.width(), borderRadius.height(), paint);
562
563     if (backgroundRect.width() >= 3 && backgroundRect.height() >= 3)
564     {
565         // Draw the buffered ranges.
566         // FIXME: Draw multiple ranges if there are multiple buffered ranges.
567         SkRect bufferedRect;
568         bufferedRect.set(backgroundRect.fLeft + 2, backgroundRect.fTop + 2,
569                          backgroundRect.fRight - 1, backgroundRect.fBottom - 1);
570         int width = static_cast<int>(bufferedRect.width() * mediaElement->percentLoaded());
571         bufferedRect.fRight = bufferedRect.fLeft + width;
572
573         SkPoint points[2] = { { 0, bufferedRect.fTop }, { 0, bufferedRect.fBottom } };
574         SkColor startColor = object->style()->color().rgb();
575         SkColor endColor = SkColorSetRGB(SkColorGetR(startColor) / 2,
576                                          SkColorGetG(startColor) / 2,
577                                          SkColorGetB(startColor) / 2);
578         SkColor colors[2] = { startColor, endColor };
579         SkShader* gradient = SkGradientShader::CreateLinear(points, colors, 0,
580                                                             sizeof(points) / sizeof(points[0]),
581                                                             SkShader::kMirror_TileMode, 0);
582
583         paint.reset();
584         paint.setShader(gradient);
585         paint.setAntiAlias(true);
586         // Check for round rect with zero width or height, otherwise Skia will assert
587         if (bufferedRect.width() > 0 && bufferedRect.height() > 0)
588             canvas->drawRoundRect(bufferedRect, borderRadius.width(), borderRadius.height(), paint);
589         gradient->unref();
590     }
591     return true;
592 #else
593     UNUSED_PARAM(object);
594     UNUSED_PARAM(paintInfo);
595     UNUSED_PARAM(rect);
596     return false;
597 #endif
598 }
599
600 bool RenderThemeChromiumSkia::paintMediaVolumeSliderTrack(RenderObject* object, const RenderObject::PaintInfo& paintInfo, const IntRect& rect)
601 {
602 #if ENABLE(VIDEO)
603     HTMLMediaElement* mediaElement = mediaElementParent(object->node());
604     if (!mediaElement)
605         return false;
606
607     SkCanvas* canvas = paintInfo.context->platformContext()->canvas();
608     SkPaint paint;
609     paint.setColor(SK_ColorWHITE);
610
611     int x = rect.x() + rect.width() / 2;
612     canvas->drawLine(x, rect.y(), x, rect.y() + rect.height(), paint);
613     return true;
614 #else
615     UNUSED_PARAM(object);
616     UNUSED_PARAM(paintInfo);
617     UNUSED_PARAM(rect);
618     return false;
619 #endif
620 }
621
622 static Image* mediaSliderThumbImage()
623 {
624     static Image* mediaSliderThumb = Image::loadPlatformResource("mediaSliderThumb").releaseRef();
625     return mediaSliderThumb;
626 }
627
628 static Image* mediaVolumeSliderThumbImage()
629 {
630     static Image* mediaVolumeSliderThumb = Image::loadPlatformResource("mediaVolumeSliderThumb").releaseRef();
631     return mediaVolumeSliderThumb;
632 }
633
634 void RenderThemeChromiumSkia::adjustSliderThumbSize(RenderObject* object) const
635 {
636 #if ENABLE(VIDEO)
637     Image* thumbImage = 0;
638     if (object->style()->appearance() == MediaSliderThumbPart)
639         thumbImage = mediaSliderThumbImage();
640     else if (object->style()->appearance() == MediaVolumeSliderThumbPart)
641         thumbImage = mediaVolumeSliderThumbImage();
642
643     ASSERT(thumbImage);
644     object->style()->setWidth(Length(thumbImage->width(), Fixed));
645     object->style()->setHeight(Length(thumbImage->height(), Fixed));
646 #else
647     UNUSED_PARAM(object);
648 #endif
649 }
650
651 bool RenderThemeChromiumSkia::paintMediaSliderThumb(RenderObject* object, const RenderObject::PaintInfo& paintInfo, const IntRect& rect)
652 {
653 #if ENABLE(VIDEO)
654     if (!object->parent()->isSlider())
655         return false;
656
657     return paintMediaButtonInternal(paintInfo.context, rect, mediaSliderThumbImage());
658 #else
659     UNUSED_PARAM(object);
660     UNUSED_PARAM(paintInfo);
661     UNUSED_PARAM(rect);
662     return false;
663 #endif
664 }
665
666 bool RenderThemeChromiumSkia::paintMediaVolumeSliderThumb(RenderObject* object, const RenderObject::PaintInfo& paintInfo, const IntRect& rect)
667 {
668 #if ENABLE(VIDEO)
669     if (!object->parent()->isSlider())
670         return false;
671
672     return paintMediaButtonInternal(paintInfo.context, rect, mediaVolumeSliderThumbImage());
673 #else
674     UNUSED_PARAM(object);
675     UNUSED_PARAM(paintInfo);
676     UNUSED_PARAM(rect);
677     return false;
678 #endif
679 }
680
681 bool RenderThemeChromiumSkia::paintMediaPlayButton(RenderObject* object, const RenderObject::PaintInfo& paintInfo, const IntRect& rect)
682 {
683 #if ENABLE(VIDEO)
684     HTMLMediaElement* mediaElement = mediaElementParent(object->node());
685     if (!mediaElement)
686         return false;
687
688     static Image* mediaPlay = Image::loadPlatformResource("mediaPlay").releaseRef();
689     static Image* mediaPause = Image::loadPlatformResource("mediaPause").releaseRef();
690     static Image* mediaPlayDisabled = Image::loadPlatformResource("mediaPlayDisabled").releaseRef();
691
692     if (mediaElement->networkState() == HTMLMediaElement::NETWORK_NO_SOURCE)
693         return paintMediaButtonInternal(paintInfo.context, rect, mediaPlayDisabled);
694
695     return paintMediaButtonInternal(paintInfo.context, rect, mediaElement->paused() ? mediaPlay : mediaPause);
696 #else
697     UNUSED_PARAM(object);
698     UNUSED_PARAM(paintInfo);
699     UNUSED_PARAM(rect);
700     return false;
701 #endif
702 }
703
704 bool RenderThemeChromiumSkia::paintMediaMuteButton(RenderObject* object, const RenderObject::PaintInfo& paintInfo, const IntRect& rect)
705 {
706 #if ENABLE(VIDEO)
707     HTMLMediaElement* mediaElement = mediaElementParent(object->node());
708     if (!mediaElement)
709         return false;
710
711     static Image* soundFull = Image::loadPlatformResource("mediaSoundFull").releaseRef();
712     static Image* soundNone = Image::loadPlatformResource("mediaSoundNone").releaseRef();
713     static Image* soundDisabled = Image::loadPlatformResource("mediaSoundDisabled").releaseRef();
714
715     if (mediaElement->networkState() == HTMLMediaElement::NETWORK_NO_SOURCE || !mediaElement->hasAudio())
716         return paintMediaButtonInternal(paintInfo.context, rect, soundDisabled);
717
718     return paintMediaButtonInternal(paintInfo.context, rect, mediaElement->muted() ? soundNone: soundFull);
719 #else
720     UNUSED_PARAM(object);
721     UNUSED_PARAM(paintInfo);
722     UNUSED_PARAM(rect);
723     return false;
724 #endif
725 }
726
727 void RenderThemeChromiumSkia::adjustMenuListStyle(CSSStyleSelector* selector, RenderStyle* style, WebCore::Element* e) const
728 {
729     // Height is locked to auto on all browsers.
730     style->setLineHeight(RenderStyle::initialLineHeight());
731 }
732
733 bool RenderThemeChromiumSkia::paintMenuList(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& rect)
734 {
735     SkCanvas* const canvas = i.context->platformContext()->canvas();
736     const int right = rect.x() + rect.width();
737     const int middle = rect.y() + rect.height() / 2;
738
739     paintButtonLike(this, o, i, rect);
740
741     SkPaint paint;
742     paint.setColor(SK_ColorBLACK);
743     paint.setAntiAlias(true);
744     paint.setStyle(SkPaint::kFill_Style);
745
746     SkPath path;
747     path.moveTo(right - 13, middle - 3);
748     path.rLineTo(6, 0);
749     path.rLineTo(-3, 6);
750     path.close();
751     canvas->drawPath(path, paint);
752
753     return false;
754 }
755
756 void RenderThemeChromiumSkia::adjustMenuListButtonStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const
757 {
758     adjustMenuListStyle(selector, style, e);
759 }
760
761 // Used to paint styled menulists (i.e. with a non-default border)
762 bool RenderThemeChromiumSkia::paintMenuListButton(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& rect)
763 {
764     return paintMenuList(o, i, rect);
765 }
766
767 bool RenderThemeChromiumSkia::paintSliderTrack(RenderObject*, const RenderObject::PaintInfo& i, const IntRect& rect)
768 {
769     // Just paint a grey box for now (matches the color of a scrollbar background.
770     SkCanvas* const canvas = i.context->platformContext()->canvas();
771     int verticalCenter = rect.y() + rect.height() / 2;
772     int top = std::max(rect.y(), verticalCenter - 2);
773     int bottom = std::min(rect.y() + rect.height(), verticalCenter + 2);
774
775     SkPaint paint;
776     const SkColor grey = SkColorSetARGB(0xff, 0xe3, 0xdd, 0xd8);
777     paint.setColor(grey);
778
779     SkRect skrect;
780     skrect.set(rect.x(), top, rect.x() + rect.width(), bottom);
781     canvas->drawRect(skrect, paint);
782
783     return false;
784 }
785
786 bool RenderThemeChromiumSkia::paintSliderThumb(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& rect)
787 {
788     // Make a thumb similar to the scrollbar thumb.
789     const bool hovered = isHovered(o) || toRenderSlider(o->parent())->inDragMode();
790     const int midx = rect.x() + rect.width() / 2;
791     const int midy = rect.y() + rect.height() / 2;
792     const bool vertical = (o->style()->appearance() == SliderThumbVerticalPart);
793     SkCanvas* const canvas = i.context->platformContext()->canvas();
794
795     const SkColor thumbLightGrey = SkColorSetARGB(0xff, 0xf4, 0xf2, 0xef);
796     const SkColor thumbDarkGrey = SkColorSetARGB(0xff, 0xea, 0xe5, 0xe0);
797     SkPaint paint;
798     paint.setColor(hovered ? SK_ColorWHITE : thumbLightGrey);
799
800     SkIRect skrect;
801     if (vertical)
802         skrect.set(rect.x(), rect.y(), midx + 1, rect.bottom());
803     else
804         skrect.set(rect.x(), rect.y(), rect.right(), midy + 1);
805
806     canvas->drawIRect(skrect, paint);
807
808     paint.setColor(hovered ? thumbLightGrey : thumbDarkGrey);
809
810     if (vertical)
811         skrect.set(midx + 1, rect.y(), rect.right(), rect.bottom());
812     else
813         skrect.set(rect.x(), midy + 1, rect.right(), rect.bottom());
814
815     canvas->drawIRect(skrect, paint);
816
817     const SkColor borderDarkGrey = SkColorSetARGB(0xff, 0x9d, 0x96, 0x8e);
818     paint.setColor(borderDarkGrey);
819     drawBox(canvas, rect, paint);
820
821     if (rect.height() > 10 && rect.width() > 10) {
822         drawHorizLine(canvas, midx - 2, midx + 2, midy, paint);
823         drawHorizLine(canvas, midx - 2, midx + 2, midy - 3, paint);
824         drawHorizLine(canvas, midx - 2, midx + 2, midy + 3, paint);
825     }
826
827     return false;
828 }
829
830 int RenderThemeChromiumSkia::popupInternalPaddingLeft(RenderStyle* style) const
831 {
832     return menuListInternalPadding(style, LeftPadding);
833 }
834
835 int RenderThemeChromiumSkia::popupInternalPaddingRight(RenderStyle* style) const
836 {
837     return menuListInternalPadding(style, RightPadding);
838 }
839
840 int RenderThemeChromiumSkia::popupInternalPaddingTop(RenderStyle* style) const
841 {
842     return menuListInternalPadding(style, TopPadding);
843 }
844
845 int RenderThemeChromiumSkia::popupInternalPaddingBottom(RenderStyle* style) const
846 {
847     return menuListInternalPadding(style, BottomPadding);
848 }
849
850 int RenderThemeChromiumSkia::buttonInternalPaddingLeft() const
851 {
852     return 3;
853 }
854
855 int RenderThemeChromiumSkia::buttonInternalPaddingRight() const
856 {
857     return 3;
858 }
859
860 int RenderThemeChromiumSkia::buttonInternalPaddingTop() const
861 {
862     return 1;
863 }
864
865 int RenderThemeChromiumSkia::buttonInternalPaddingBottom() const
866 {
867     return 1;
868 }
869
870 // static
871 void RenderThemeChromiumSkia::setDefaultFontSize(int fontSize)
872 {
873     defaultFontSize = static_cast<float>(fontSize);
874 }
875
876 double RenderThemeChromiumSkia::caretBlinkIntervalInternal() const
877 {
878     return RenderTheme::caretBlinkInterval();
879 }
880
881 int RenderThemeChromiumSkia::menuListInternalPadding(RenderStyle* style, int paddingType) const
882 {
883     // This internal padding is in addition to the user-supplied padding.
884     // Matches the FF behavior.
885     int padding = styledMenuListInternalPadding[paddingType];
886
887     // Reserve the space for right arrow here. The rest of the padding is
888     // set by adjustMenuListStyle, since PopMenuWin.cpp uses the padding from
889     // RenderMenuList to lay out the individual items in the popup.
890     // If the MenuList actually has appearance "NoAppearance", then that means
891     // we don't draw a button, so don't reserve space for it.
892     const int barType = style->direction() == LTR ? RightPadding : LeftPadding;
893     if (paddingType == barType && style->appearance() != NoControlPart)
894         padding += ScrollbarTheme::nativeTheme()->scrollbarThickness();
895
896     return padding;
897 }
898
899 } // namespace WebCore