c57813f1c663d670ac157e7b5b3767aa9dbee2eb
[WebKit-https.git] / Source / WebCore / platform / blackberry / RenderThemeBlackBerry.cpp
1 /*
2  * Copyright (C) 2006, 2007 Apple Inc.
3  * Copyright (C) 2009 Google Inc.
4  * Copyright (C) 2009, 2010, 2011, 2012 Research In Motion Limited. All rights reserved.
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
19  */
20
21 #include "config.h"
22 #include "RenderThemeBlackBerry.h"
23
24 #include "CSSValueKeywords.h"
25 #include "Frame.h"
26 #include "MediaControlElements.h"
27 #include "MediaPlayerPrivateBlackBerry.h"
28 #include "PaintInfo.h"
29 #include "RenderProgress.h"
30 #include "RenderSlider.h"
31 #include "RenderView.h"
32 #include "UserAgentStyleSheets.h"
33
34 namespace WebCore {
35
36 // Sizes (unit px)
37 const unsigned smallRadius = 1;
38 const unsigned largeRadius = 3;
39 const unsigned lineWidth = 1;
40 const int marginSize = 4;
41 const int mediaSliderThumbWidth = 40;
42 const int mediaSliderThumbHeight = 13;
43 const int mediaSliderThumbRadius = 5;
44 const int sliderThumbWidth = 15;
45 const int sliderThumbHeight = 25;
46
47 // Checkbox check scalers
48 const float checkboxLeftX = 7 / 40.0;
49 const float checkboxLeftY = 1 / 2.0;
50 const float checkboxMiddleX = 19 / 50.0;
51 const float checkboxMiddleY = 7 / 25.0;
52 const float checkboxRightX = 33 / 40.0;
53 const float checkboxRightY = 1 / 5.0;
54 const float checkboxStrokeThickness = 6.5;
55
56 // Radio button scaler
57 const float radioButtonCheckStateScaler = 7 / 30.0;
58
59 // Multipliers
60 const unsigned paddingDivisor = 5;
61
62 // Colors
63 const RGBA32 caretBottom = 0xff2163bf;
64 const RGBA32 caretTop = 0xff69a5fa;
65
66 const RGBA32 regularBottom = 0xffdcdee4;
67 const RGBA32 regularTop = 0xfff7f2ee;
68 const RGBA32 hoverBottom = 0xffb5d3fc;
69 const RGBA32 hoverTop = 0xffcceaff;
70 const RGBA32 depressedBottom = 0xff3388ff;
71 const RGBA32 depressedTop = 0xff66a0f2;
72 const RGBA32 disabledBottom = 0xffe7e7e7;
73 const RGBA32 disabledTop = 0xffefefef;
74
75 const RGBA32 regularBottomOutline = 0xff6e7073;
76 const RGBA32 regularTopOutline = 0xffb9b8b8;
77 const RGBA32 hoverBottomOutline = 0xff2163bf;
78 const RGBA32 hoverTopOutline = 0xff69befa;
79 const RGBA32 depressedBottomOutline = 0xff0c3d81;
80 const RGBA32 depressedTopOutline = 0xff1d4d70;
81 const RGBA32 disabledOutline = 0xffd5d9de;
82
83 const RGBA32 progressRegularBottom = caretTop;
84 const RGBA32 progressRegularTop = caretBottom;
85
86 const RGBA32 rangeSliderRegularBottom = 0xfff6f2ee;
87 const RGBA32 rangeSliderRegularTop = 0xffdee0e5;
88 const RGBA32 rangeSliderRollBottom = 0xffc9e8fe;
89 const RGBA32 rangeSliderRollTop = 0xffb5d3fc;
90
91 const RGBA32 rangeSliderRegularBottomOutline = 0xffb9babd;
92 const RGBA32 rangeSliderRegularTopOutline = 0xffb7b7b7;
93 const RGBA32 rangeSliderRollBottomOutline = 0xff67abe0;
94 const RGBA32 rangeSliderRollTopOutline = 0xff69adf9;
95
96 const RGBA32 dragRegularLight = 0xfffdfdfd;
97 const RGBA32 dragRegularDark = 0xffbababa;
98 const RGBA32 dragRollLight = 0xfff2f2f2;
99 const RGBA32 dragRollDark = 0xff69a8ff;
100
101 const RGBA32 selection = 0xff2b8fff;
102
103 const RGBA32 blackPen = Color::black;
104 const RGBA32 focusRingPen = 0xffa3c8fe;
105
106 float RenderThemeBlackBerry::defaultFontSize = 16;
107
108 // We aim to match IE here.
109 // -IE uses a font based on the encoding as the default font for form controls.
110 // -Gecko uses MS Shell Dlg (actually calls GetStockObject(DEFAULT_GUI_FONT),
111 // which returns MS Shell Dlg)
112 // -Safari uses Lucida Grande.
113 //
114 // FIXME: The only case where we know we don't match IE is for ANSI encodings.
115 // IE uses MS Shell Dlg there, which we render incorrectly at certain pixel
116 // sizes (e.g. 15px). So we just use Arial for now.
117 const String& RenderThemeBlackBerry::defaultGUIFont()
118 {
119     DEFINE_STATIC_LOCAL(String, fontFace, ("Arial"));
120     return fontFace;
121 }
122
123 static PassRefPtr<Gradient> createLinearGradient(RGBA32 top, RGBA32 bottom, const IntPoint& a, const IntPoint& b)
124 {
125     RefPtr<Gradient> gradient = Gradient::create(a, b);
126     gradient->addColorStop(0.0, Color(top));
127     gradient->addColorStop(1.0, Color(bottom));
128     return gradient.release();
129 }
130
131 static Path roundedRectForBorder(RenderObject* object, const IntRect& rect)
132 {
133     RenderStyle* style = object->style();
134     LengthSize topLeftRadius = style->borderTopLeftRadius();
135     LengthSize topRightRadius = style->borderTopRightRadius();
136     LengthSize bottomLeftRadius = style->borderBottomLeftRadius();
137     LengthSize bottomRightRadius = style->borderBottomRightRadius();
138
139     Path roundedRect;
140     roundedRect.addRoundedRect(rect, IntSize(topLeftRadius.width().value(), topLeftRadius.height().value()),
141                                      IntSize(topRightRadius.width().value(), topRightRadius.height().value()),
142                                      IntSize(bottomLeftRadius.width().value(), bottomLeftRadius.height().value()),
143                                      IntSize(bottomRightRadius.width().value(), bottomRightRadius.height().value()));
144     return roundedRect;
145 }
146
147 PassRefPtr<RenderTheme> RenderTheme::themeForPage(Page* page)
148 {
149     static RenderTheme* theme = RenderThemeBlackBerry::create().leakRef();
150     return theme;
151 }
152
153 PassRefPtr<RenderTheme> RenderThemeBlackBerry::create()
154 {
155     return adoptRef(new RenderThemeBlackBerry());
156 }
157
158 RenderThemeBlackBerry::RenderThemeBlackBerry()
159 {
160 }
161
162 RenderThemeBlackBerry::~RenderThemeBlackBerry()
163 {
164 }
165
166 String RenderThemeBlackBerry::extraDefaultStyleSheet()
167 {
168     return String(themeBlackBerryUserAgentStyleSheet, sizeof(themeBlackBerryUserAgentStyleSheet));
169 }
170
171 #if ENABLE(VIDEO)
172 String RenderThemeBlackBerry::extraMediaControlsStyleSheet()
173 {
174     return String(mediaControlsBlackBerryUserAgentStyleSheet, sizeof(mediaControlsBlackBerryUserAgentStyleSheet));
175 }
176
177 String RenderThemeBlackBerry::formatMediaControlsRemainingTime(float, float duration) const
178 {
179     // This is a workaround to make the appearance of media time controller in
180     // in-page mode the same as in fullscreen mode.
181     return formatMediaControlsTime(duration);
182 }
183 #endif
184
185 double RenderThemeBlackBerry::caretBlinkInterval() const
186 {
187     return 0; // Turn off caret blinking.
188 }
189
190 void RenderThemeBlackBerry::systemFont(int propId, FontDescription& fontDescription) const
191 {
192     float fontSize = defaultFontSize;
193
194     if (propId == CSSValueWebkitMiniControl || propId ==  CSSValueWebkitSmallControl || propId == CSSValueWebkitControl) {
195         // Why 2 points smaller? Because that's what Gecko does. Note that we
196         // are assuming a 96dpi screen, which is the default value we use on Windows.
197         static const float pointsPerInch = 72.0f;
198         static const float pixelsPerInch = 96.0f;
199         fontSize -= (2.0f / pointsPerInch) * pixelsPerInch;
200     }
201
202     fontDescription.firstFamily().setFamily(defaultGUIFont());
203     fontDescription.setSpecifiedSize(fontSize);
204     fontDescription.setIsAbsoluteSize(true);
205     fontDescription.setGenericFamily(FontDescription::NoFamily);
206     fontDescription.setWeight(FontWeightNormal);
207     fontDescription.setItalic(false);
208 }
209
210 void RenderThemeBlackBerry::setButtonStyle(RenderStyle* style) const
211 {
212     Length vertPadding(int(style->fontSize() / paddingDivisor), Fixed);
213     style->setPaddingTop(vertPadding);
214     style->setPaddingBottom(vertPadding);
215 }
216
217 void RenderThemeBlackBerry::adjustButtonStyle(CSSStyleSelector*, RenderStyle* style, Element*) const
218 {
219     setButtonStyle(style);
220     style->setCursor(CURSOR_WEBKIT_GRAB);
221 }
222
223 void RenderThemeBlackBerry::adjustTextAreaStyle(CSSStyleSelector*, RenderStyle* style, Element*) const
224 {
225     setButtonStyle(style);
226 }
227
228 bool RenderThemeBlackBerry::paintTextArea(RenderObject* object, const PaintInfo& info, const IntRect& rect)
229 {
230     return paintTextFieldOrTextAreaOrSearchField(object, info, rect);
231 }
232
233 void RenderThemeBlackBerry::adjustTextFieldStyle(CSSStyleSelector*, RenderStyle* style, Element*) const
234 {
235     setButtonStyle(style);
236 }
237
238 bool RenderThemeBlackBerry::paintTextFieldOrTextAreaOrSearchField(RenderObject* object, const PaintInfo& info, const IntRect& rect)
239 {
240     ASSERT(info.context);
241     GraphicsContext* context = info.context;
242
243     context->save();
244     context->setStrokeStyle(SolidStroke);
245     context->setStrokeThickness(lineWidth);
246     if (!isEnabled(object))
247         context->setStrokeColor(disabledOutline, ColorSpaceDeviceRGB);
248     else if (isPressed(object))
249         info.context->setStrokeGradient(createLinearGradient(depressedTopOutline, depressedBottomOutline, rect.maxXMinYCorner(), rect.maxXMaxYCorner()));
250     else if (isHovered(object) || isFocused(object))
251         context->setStrokeGradient(createLinearGradient(hoverTopOutline, hoverBottomOutline, rect.maxXMinYCorner(), rect.maxXMaxYCorner()));
252     else
253         context->setStrokeGradient(createLinearGradient(regularTopOutline, regularBottomOutline, rect.maxXMinYCorner(), rect.maxXMaxYCorner()));
254
255     Path textFieldRoundedRectangle = roundedRectForBorder(object, rect);
256     if (object->style()->appearance() == SearchFieldPart) {
257         // We force the fill color to White so as to match the background color of the search cancel button graphic.
258         context->setFillColor(Color::white, ColorSpaceDeviceRGB);
259         context->drawPath(textFieldRoundedRectangle);
260     } else
261         context->strokePath(textFieldRoundedRectangle);
262     context->restore();
263     return false;
264 }
265
266 bool RenderThemeBlackBerry::paintTextField(RenderObject* object, const PaintInfo& info, const IntRect& rect)
267 {
268     return paintTextFieldOrTextAreaOrSearchField(object, info, rect);
269 }
270
271 void RenderThemeBlackBerry::adjustSearchFieldStyle(CSSStyleSelector*, RenderStyle* style, Element*) const
272 {
273     setButtonStyle(style);
274 }
275
276 void RenderThemeBlackBerry::adjustSearchFieldCancelButtonStyle(CSSStyleSelector*, RenderStyle* style, Element*) const
277 {
278     static const float defaultControlFontPixelSize = 13;
279     static const float defaultCancelButtonSize = 9;
280     static const float minCancelButtonSize = 5;
281     static const float maxCancelButtonSize = 21;
282
283     // Scale the button size based on the font size
284     float fontScale = style->fontSize() / defaultControlFontPixelSize;
285     int cancelButtonSize = lroundf(std::min(std::max(minCancelButtonSize, defaultCancelButtonSize * fontScale), maxCancelButtonSize));
286     Length length(cancelButtonSize, Fixed);
287     style->setWidth(length);
288     style->setHeight(length);
289 }
290
291 bool RenderThemeBlackBerry::paintSearchField(RenderObject* object, const PaintInfo& info, const IntRect& rect)
292 {
293     return paintTextFieldOrTextAreaOrSearchField(object, info, rect);
294 }
295
296 bool RenderThemeBlackBerry::paintSearchFieldCancelButton(RenderObject* object, const PaintInfo& paintInfo, const IntRect& rect)
297 {
298     ASSERT(object->parent());
299     if (!object->parent() || !object->parent()->isBox())
300         return false;
301
302     RenderBox* parentRenderBox = toRenderBox(object->parent());
303
304     IntRect parentBox = parentRenderBox->absoluteContentBox();
305     IntRect bounds = rect;
306     // Make sure the scaled button stays square and fits in its parent's box.
307     bounds.setHeight(std::min(parentBox.width(), std::min(parentBox.height(), bounds.height())));
308     bounds.setWidth(bounds.height());
309
310     // Put the button in the middle vertically, and round up the value.
311     // So if it has to be one pixel off-center, it would be one pixel closer
312     // to the bottom of the field. This would look better with the text.
313     bounds.setY(parentBox.y() + (parentBox.height() - bounds.height() + 1) / 2);
314
315     static Image* cancelImage = Image::loadPlatformResource("searchCancel").leakRef();
316     static Image* cancelPressedImage = Image::loadPlatformResource("searchCancelPressed").leakRef();
317     paintInfo.context->drawImage(isPressed(object) ? cancelPressedImage : cancelImage, object->style()->colorSpace(), bounds);
318     return false;
319 }
320
321 void RenderThemeBlackBerry::adjustMenuListButtonStyle(CSSStyleSelector*, RenderStyle* style, Element*) const
322 {
323     // These seem to be reasonable padding values from observation.
324     const int paddingLeft = 8;
325     const int paddingRight = 4;
326
327     const int minHeight = style->fontSize() * 2;
328
329     style->resetPadding();
330     style->setHeight(Length(Auto));
331
332     style->setPaddingRight(Length(minHeight + paddingRight, Fixed));
333     style->setPaddingLeft(Length(paddingLeft, Fixed));
334     style->setCursor(CURSOR_WEBKIT_GRAB);
335 }
336
337 void RenderThemeBlackBerry::calculateButtonSize(RenderStyle* style) const
338 {
339     int size = style->fontSize();
340     Length length(size, Fixed);
341     if (style->appearance() == CheckboxPart || style->appearance() == RadioPart) {
342         style->setWidth(length);
343         style->setHeight(length);
344         return;
345     }
346
347     // If the width and height are both specified, then we have nothing to do.
348     if (!style->width().isIntrinsicOrAuto() && !style->height().isAuto())
349         return;
350
351     if (style->width().isIntrinsicOrAuto())
352         style->setWidth(length);
353
354     if (style->height().isAuto())
355         style->setHeight(length);
356 }
357
358 bool RenderThemeBlackBerry::paintCheckbox(RenderObject* object, const PaintInfo& info, const IntRect& rect)
359 {
360     return paintButton(object, info, rect);
361 }
362
363 void RenderThemeBlackBerry::setCheckboxSize(RenderStyle* style) const
364 {
365     calculateButtonSize(style);
366 }
367
368 bool RenderThemeBlackBerry::paintRadio(RenderObject* object, const PaintInfo& info, const IntRect& rect)
369 {
370     return paintButton(object, info, rect);
371 }
372
373 void RenderThemeBlackBerry::setRadioSize(RenderStyle* style) const
374 {
375     calculateButtonSize(style);
376 }
377
378 // If this function returns false, WebCore assumes the button is fully decorated
379 bool RenderThemeBlackBerry::paintButton(RenderObject* object, const PaintInfo& info, const IntRect& rect)
380 {
381     ASSERT(info.context);
382     info.context->save();
383
384     info.context->setStrokeStyle(SolidStroke);
385     info.context->setStrokeThickness(lineWidth);
386
387     Color check(blackPen);
388     if (!isEnabled(object)) {
389         info.context->setFillGradient(createLinearGradient(disabledTop, disabledBottom, rect.maxXMinYCorner(), rect.maxXMaxYCorner()));
390         info.context->setStrokeColor(disabledOutline, ColorSpaceDeviceRGB);
391     } else if (isPressed(object)) {
392         info.context->setFillGradient(createLinearGradient(depressedTop, depressedBottom, rect.maxXMinYCorner(), rect.maxXMaxYCorner()));
393         info.context->setStrokeGradient(createLinearGradient(depressedTopOutline, depressedBottomOutline, rect.maxXMinYCorner(), rect.maxXMaxYCorner()));
394     } else if (isHovered(object)) {
395         info.context->setFillGradient(createLinearGradient(hoverTop, hoverBottom, rect.maxXMinYCorner(), rect.maxXMaxYCorner()));
396         info.context->setStrokeGradient(createLinearGradient(hoverTopOutline, hoverBottomOutline, rect.maxXMinYCorner(), rect.maxXMaxYCorner()));
397     } else {
398         info.context->setFillGradient(createLinearGradient(regularTop, regularBottom, rect.maxXMinYCorner(), rect.maxXMaxYCorner()));
399         info.context->setStrokeGradient(createLinearGradient(regularTopOutline, regularBottomOutline, rect.maxXMinYCorner(), rect.maxXMaxYCorner()));
400     }
401
402     ControlPart part = object->style()->appearance();
403     switch (part) {
404     case CheckboxPart: {
405         FloatSize smallCorner(smallRadius, smallRadius);
406         Path path;
407         path.addRoundedRect(rect, smallCorner);
408         info.context->drawPath(path);
409
410         if (isChecked(object)) {
411             Path checkPath;
412             IntRect rect2 = rect;
413             rect2.inflate(-1);
414             checkPath.moveTo(FloatPoint(rect2.x() + rect2.width() * checkboxLeftX, rect2.y() + rect2.height() * checkboxLeftY));
415             checkPath.addLineTo(FloatPoint(rect2.x() + rect2.width() * checkboxMiddleX, rect2.maxY() - rect2.height() * checkboxMiddleY));
416             checkPath.addLineTo(FloatPoint(rect2.x() + rect2.width() * checkboxRightX, rect2.y() + rect2.height() * checkboxRightY));
417             info.context->setLineCap(RoundCap);
418             info.context->setStrokeColor(blackPen, ColorSpaceDeviceRGB);
419             info.context->setStrokeThickness(rect2.width() / checkboxStrokeThickness);
420             info.context->drawPath(checkPath);
421         }
422         break;
423     }
424     case RadioPart:
425         info.context->drawEllipse(rect);
426         if (isChecked(object)) {
427             IntRect rect2 = rect;
428             rect2.inflate(-rect.width() * radioButtonCheckStateScaler);
429             info.context->setFillColor(check, ColorSpaceDeviceRGB);
430             info.context->setStrokeColor(check, ColorSpaceDeviceRGB);
431             info.context->drawEllipse(rect2);
432         }
433         break;
434     case ButtonPart:
435     case PushButtonPart: {
436         FloatSize largeCorner(largeRadius, largeRadius);
437         Path path;
438         path.addRoundedRect(rect, largeCorner);
439         info.context->drawPath(path);
440         break;
441     }
442     case SquareButtonPart: {
443         Path path;
444         path.addRect(rect);
445         info.context->drawPath(path);
446         break;
447     }
448     default:
449         info.context->restore();
450         return true;
451     }
452
453     info.context->restore();
454     return false;
455 }
456
457 void RenderThemeBlackBerry::adjustMenuListStyle(CSSStyleSelector* css, RenderStyle* style, Element* element) const
458 {
459     adjustMenuListButtonStyle(css, style, element);
460 }
461
462 void RenderThemeBlackBerry::adjustCheckboxStyle(CSSStyleSelector*, RenderStyle* style, Element*) const
463 {
464     setCheckboxSize(style);
465     style->setBoxShadow(nullptr);
466     Length margin(marginSize, Fixed);
467     style->setMarginBottom(margin);
468     style->setMarginRight(margin);
469     style->setCursor(CURSOR_WEBKIT_GRAB);
470 }
471
472 void RenderThemeBlackBerry::adjustRadioStyle(CSSStyleSelector*, RenderStyle* style, Element*) const
473 {
474     setRadioSize(style);
475     style->setBoxShadow(nullptr);
476     Length margin(marginSize, Fixed);
477     style->setMarginBottom(margin);
478     style->setMarginRight(margin);
479     style->setCursor(CURSOR_WEBKIT_GRAB);
480 }
481
482 void RenderThemeBlackBerry::paintMenuListButtonGradientAndArrow(GraphicsContext* context, RenderObject* object, IntRect buttonRect, const Path& clipPath)
483 {
484     ASSERT(context);
485     context->save();
486     if (!isEnabled(object))
487         context->setFillGradient(createLinearGradient(disabledTop, disabledBottom, buttonRect.maxXMinYCorner(), buttonRect.maxXMaxYCorner()));
488     else if (isPressed(object))
489         context->setFillGradient(createLinearGradient(depressedTop, depressedBottom, buttonRect.maxXMinYCorner(), buttonRect.maxXMaxYCorner()));
490     else if (isHovered(object))
491         context->setFillGradient(createLinearGradient(hoverTop, hoverBottom, buttonRect.maxXMinYCorner(), buttonRect.maxXMaxYCorner()));
492     else
493         context->setFillGradient(createLinearGradient(regularTop, regularBottom, buttonRect.maxXMinYCorner(), buttonRect.maxXMaxYCorner()));
494
495     // 1. Paint the background of the button.
496     context->clip(clipPath);
497     context->drawRect(buttonRect);
498     context->restore();
499
500     // 2. Paint the button arrow.
501     buttonRect.inflate(-buttonRect.width() / 3);
502     buttonRect.move(0, buttonRect.height() * 7 / 20);
503     Path path;
504     path.moveTo(FloatPoint(buttonRect.x(), buttonRect.y()));
505     path.addLineTo(FloatPoint(buttonRect.x() + buttonRect.width(), buttonRect.y()));
506     path.addLineTo(FloatPoint(buttonRect.x() + buttonRect.width() / 2.0, buttonRect.y() + buttonRect.height() / 2.0));
507     path.closeSubpath();
508
509     context->save();
510     context->setStrokeStyle(SolidStroke);
511     context->setStrokeThickness(lineWidth);
512     context->setStrokeColor(Color::black, ColorSpaceDeviceRGB);
513     context->setFillColor(Color::black, ColorSpaceDeviceRGB);
514     context->setLineJoin(BevelJoin);
515     context->fillPath(path);
516     context->restore();
517 }
518
519 static IntRect computeMenuListArrowButtonRect(const IntRect& rect)
520 {
521     // FIXME: The menu list arrow button should have a minimum and maximum width (to ensure usability) or
522     // scale with respect to the font size used in the menu list control or some combination of both.
523     return IntRect(IntPoint(rect.maxX() - rect.height(), rect.y()), IntSize(rect.height(), rect.height()));
524 }
525
526 static void paintMenuListBackground(GraphicsContext* context, const Path& menuListPath, const Color& backgroundColor)
527 {
528     ASSERT(context);
529     context->save();
530     context->setFillColor(backgroundColor, ColorSpaceDeviceRGB);
531     context->fillPath(menuListPath);
532     context->restore();
533 }
534
535 bool RenderThemeBlackBerry::paintMenuList(RenderObject* object, const PaintInfo& info, const IntRect& rect)
536 {
537     // Note, this method is not called if the menu list explicitly specifies either a border or background color.
538     // Instead, RenderThemeBlackBerry::paintMenuListButton is called. Therefore, when this method is called, we don't
539     // have to adjust rect with respect to the border dimensions.
540
541     ASSERT(info.context);
542     GraphicsContext* context = info.context;
543
544     Path menuListRoundedRectangle = roundedRectForBorder(object, rect);
545
546     // 1. Paint the background of the entire control.
547     paintMenuListBackground(context, menuListRoundedRectangle, Color::white);
548
549     // 2. Paint the background of the button and its arrow.
550     IntRect arrowButtonRectangle = computeMenuListArrowButtonRect(rect);
551     paintMenuListButtonGradientAndArrow(context, object, arrowButtonRectangle, menuListRoundedRectangle);
552
553     // 4. Stroke an outline around the entire control.
554     context->save();
555     context->setStrokeStyle(SolidStroke);
556     context->setStrokeThickness(lineWidth);
557     if (!isEnabled(object))
558         context->setStrokeColor(disabledOutline, ColorSpaceDeviceRGB);
559     else if (isPressed(object))
560         context->setStrokeGradient(createLinearGradient(depressedTopOutline, depressedBottomOutline, rect.maxXMinYCorner(), rect.maxXMaxYCorner()));
561     else if (isHovered(object))
562         context->setStrokeGradient(createLinearGradient(hoverTopOutline, hoverBottomOutline, rect.maxXMinYCorner(), rect.maxXMaxYCorner()));
563     else
564         context->setStrokeGradient(createLinearGradient(regularTopOutline, regularBottomOutline, rect.maxXMinYCorner(), rect.maxXMaxYCorner()));
565
566     context->strokePath(menuListRoundedRectangle);
567     context->restore();
568     return false;
569 }
570
571 bool RenderThemeBlackBerry::paintMenuListButton(RenderObject* object, const PaintInfo& info, const IntRect& rect)
572 {
573     // Note, this method is only called if the menu list explicitly specifies either a border or background color.
574     // Otherwise, RenderThemeBlackBerry::paintMenuList is called. We need to fit the arrow button with the border box
575     // of the menu-list so as to not occlude the custom border.
576
577     // We compute menuListRoundedRectangle with respect to the dimensions of the entire menu-list control (i.e. rect) and
578     // its border radius so that we clip the contour of the arrow button (when we paint it below) to match the contour of
579     // the control.
580     Path menuListRoundedRectangle = roundedRectForBorder(object, rect);
581
582     // 1. Paint the background of the entire control.
583     Color fillColor = object->style()->visitedDependentColor(CSSPropertyBackgroundColor);
584     if (!fillColor.isValid())
585         fillColor = Color::white;
586     paintMenuListBackground(info.context, menuListRoundedRectangle, fillColor);
587
588     // 2. Paint the background of the button and its arrow.
589     IntRect bounds = IntRect(rect.x() + object->style()->borderLeftWidth(),
590                          rect.y() + object->style()->borderTopWidth(),
591                          rect.width() - object->style()->borderLeftWidth() - object->style()->borderRightWidth(),
592                          rect.height() - object->style()->borderTopWidth() - object->style()->borderBottomWidth());
593
594     IntRect arrowButtonRectangle = computeMenuListArrowButtonRect(bounds); // Fit the arrow button within the border box of the menu-list.
595     paintMenuListButtonGradientAndArrow(info.context, object, arrowButtonRectangle, menuListRoundedRectangle);
596     return false;
597 }
598
599 void RenderThemeBlackBerry::adjustSliderThumbSize(RenderStyle* style) const
600 {
601     ControlPart part = style->appearance();
602     if (part == MediaVolumeSliderThumbPart || part == SliderThumbHorizontalPart || part == SliderThumbVerticalPart) {
603         style->setWidth(Length(part == SliderThumbVerticalPart ? sliderThumbHeight : sliderThumbWidth, Fixed));
604         style->setHeight(Length(part == SliderThumbVerticalPart ? sliderThumbWidth : sliderThumbHeight, Fixed));
605     } else if (part == MediaSliderThumbPart) {
606         style->setWidth(Length(mediaSliderThumbWidth, Fixed));
607         style->setHeight(Length(mediaSliderThumbHeight, Fixed));
608     }
609 }
610
611 bool RenderThemeBlackBerry::paintSliderTrack(RenderObject* object, const PaintInfo& info, const IntRect& rect)
612 {
613     const static int SliderTrackHeight = 5;
614     IntRect rect2;
615     if (object->style()->appearance() == SliderHorizontalPart) {
616         rect2.setHeight(SliderTrackHeight);
617         rect2.setWidth(rect.width());
618         rect2.setX(rect.x());
619         rect2.setY(rect.y() + (rect.height() - SliderTrackHeight) / 2);
620     } else {
621         rect2.setHeight(rect.height());
622         rect2.setWidth(SliderTrackHeight);
623         rect2.setX(rect.x() + (rect.width() - SliderTrackHeight) / 2);
624         rect2.setY(rect.y());
625     }
626     return paintSliderTrackRect(object, info, rect2);
627 }
628
629 bool RenderThemeBlackBerry::paintSliderTrackRect(RenderObject* object, const PaintInfo& info, const IntRect& rect)
630 {
631     return paintSliderTrackRect(object, info, rect, rangeSliderRegularTopOutline, rangeSliderRegularBottomOutline,
632                 rangeSliderRegularTop, rangeSliderRegularBottom);
633 }
634
635 bool RenderThemeBlackBerry::paintSliderTrackRect(RenderObject* object, const PaintInfo& info, const IntRect& rect,
636         RGBA32 strokeColorStart, RGBA32 strokeColorEnd, RGBA32 fillColorStart, RGBA32 fillColorEnd)
637 {
638     FloatSize smallCorner(smallRadius, smallRadius);
639
640     info.context->save();
641     info.context->setStrokeStyle(SolidStroke);
642     info.context->setStrokeThickness(lineWidth);
643
644     info.context->setStrokeGradient(createLinearGradient(strokeColorStart, strokeColorEnd, rect.maxXMinYCorner(), rect. maxXMaxYCorner()));
645     info.context->setFillGradient(createLinearGradient(fillColorStart, fillColorEnd, rect.maxXMinYCorner(), rect.maxXMaxYCorner()));
646
647     Path path;
648     path.addRoundedRect(rect, smallCorner);
649     info.context->fillPath(path);
650
651     info.context->restore();
652     return false;
653 }
654
655 bool RenderThemeBlackBerry::paintSliderThumb(RenderObject* object, const PaintInfo& info, const IntRect& rect)
656 {
657     FloatSize largeCorner(largeRadius, largeRadius);
658
659     info.context->save();
660     info.context->setStrokeStyle(SolidStroke);
661     info.context->setStrokeThickness(lineWidth);
662
663     if (isPressed(object) || isHovered(object)) {
664         info.context->setStrokeGradient(createLinearGradient(hoverTopOutline, hoverBottomOutline, rect.maxXMinYCorner(), rect. maxXMaxYCorner()));
665         info.context->setFillGradient(createLinearGradient(hoverTop, hoverBottom, rect.maxXMinYCorner(), rect.maxXMaxYCorner()));
666     } else {
667         info.context->setStrokeGradient(createLinearGradient(regularTopOutline, regularBottomOutline, rect.maxXMinYCorner(), rect. maxXMaxYCorner()));
668         info.context->setFillGradient(createLinearGradient(regularTop, regularBottom, rect.maxXMinYCorner(), rect.maxXMaxYCorner()));
669     }
670
671     Path path;
672     path.addRoundedRect(rect, largeCorner);
673     info.context->fillPath(path);
674
675     bool isVertical = rect.width() > rect.height();
676     IntPoint startPoint(rect.x() + (isVertical ? 5 : 2), rect.y() + (isVertical ? 2 : 5));
677     IntPoint endPoint(rect.x() + (isVertical ? 20 : 2), rect.y() + (isVertical ? 2 : 20));
678     const int lineOffset = 2;
679     const int shadowOffset = 1;
680
681     for (int i = 0; i < 3; i++) {
682         if (isVertical) {
683             startPoint.setY(startPoint.y() + lineOffset);
684             endPoint.setY(endPoint.y() + lineOffset);
685         } else {
686             startPoint.setX(startPoint.x() + lineOffset);
687             endPoint.setX(endPoint.x() + lineOffset);
688         }
689         if (isPressed(object) || isHovered(object))
690             info.context->setStrokeColor(dragRollLight, ColorSpaceDeviceRGB);
691         else
692             info.context->setStrokeColor(dragRegularLight, ColorSpaceDeviceRGB);
693         info.context->drawLine(startPoint, endPoint);
694
695         if (isVertical) {
696             startPoint.setY(startPoint.y() + shadowOffset);
697             endPoint.setY(endPoint.y() + shadowOffset);
698         } else {
699             startPoint.setX(startPoint.x() + shadowOffset);
700             endPoint.setX(endPoint.x() + shadowOffset);
701         }
702         if (isPressed(object) || isHovered(object))
703             info.context->setStrokeColor(dragRollDark, ColorSpaceDeviceRGB);
704         else
705             info.context->setStrokeColor(dragRegularDark, ColorSpaceDeviceRGB);
706         info.context->drawLine(startPoint, endPoint);
707     }
708     info.context->restore();
709     return false;
710 }
711
712 static bool paintMediaButton(GraphicsContext* context, const IntRect& rect, Image* image)
713 {
714     context->drawImage(image, ColorSpaceDeviceRGB, rect);
715     return false;
716 }
717
718 bool RenderThemeBlackBerry::paintMediaPlayButton(RenderObject* object, const PaintInfo& paintInfo, const IntRect& rect)
719 {
720 #if ENABLE(VIDEO)
721     HTMLMediaElement* mediaElement = toParentMediaElement(object);
722
723     if (!mediaElement)
724         return false;
725
726     static Image* mediaPlay = Image::loadPlatformResource("play").leakRef();
727     static Image* mediaPause = Image::loadPlatformResource("pause").leakRef();
728
729     return paintMediaButton(paintInfo.context, rect, mediaElement->canPlay() ? mediaPlay : mediaPause);
730 #else
731     UNUSED_PARAM(object);
732     UNUSED_PARAM(paintInfo);
733     UNUSED_PARAM(rect);
734     return false;
735 #endif
736 }
737
738 bool RenderThemeBlackBerry::paintMediaMuteButton(RenderObject* object, const PaintInfo& paintInfo, const IntRect& rect)
739 {
740 #if ENABLE(VIDEO)
741     HTMLMediaElement* mediaElement = toParentMediaElement(object);
742
743     if (!mediaElement)
744         return false;
745
746     static Image* mediaMute = Image::loadPlatformResource("speaker").leakRef();
747     static Image* mediaUnmute = Image::loadPlatformResource("speaker_mute").leakRef();
748
749     return paintMediaButton(paintInfo.context, rect, mediaElement->muted() || !mediaElement->volume() ? mediaUnmute : mediaMute);
750 #else
751     UNUSED_PARAM(object);
752     UNUSED_PARAM(paintInfo);
753     UNUSED_PARAM(rect);
754     return false;
755 #endif
756 }
757
758 bool RenderThemeBlackBerry::paintMediaFullscreenButton(RenderObject* object, const PaintInfo& paintInfo, const IntRect& rect)
759 {
760 #if ENABLE(VIDEO)
761     if (!toParentMediaElement(object))
762         return false;
763
764     static Image* mediaFullscreen = Image::loadPlatformResource("fullscreen").leakRef();
765
766     return paintMediaButton(paintInfo.context, rect, mediaFullscreen);
767 #else
768     UNUSED_PARAM(object);
769     UNUSED_PARAM(paintInfo);
770     UNUSED_PARAM(rect);
771     return false;
772 #endif
773 }
774
775 bool RenderThemeBlackBerry::paintMediaSliderTrack(RenderObject* object, const PaintInfo& paintInfo, const IntRect& rect)
776 {
777 #if ENABLE(VIDEO)
778     IntRect rect2(rect.x() + 3, rect.y() + 14, rect.width() - 6, 2);
779
780     HTMLMediaElement* mediaElement = toParentMediaElement(object);
781
782     if (!mediaElement)
783         return false;
784
785     float loaded = 0;
786     // FIXME: replace loaded with commented out one when buffer bug is fixed (see comment in
787     // MediaPlayerPrivateMMrenderer::percentLoaded).
788     // loaded = mediaElement->percentLoaded();
789     if (mediaElement->player())
790         loaded = static_cast<MediaPlayerPrivate *>(mediaElement->player()->implementation())->percentLoaded();
791     float position = mediaElement->duration() > 0 ? (mediaElement->currentTime() / mediaElement->duration()) : 0;
792
793     int x = rect.x() + 3;
794     int y = rect.y() + 14;
795     int w = rect.width() - 6;
796     int h = 2;
797
798     int wPlayed = (w * position);
799     int wLoaded = (w - mediaSliderThumbWidth) * loaded + mediaSliderThumbWidth;
800
801     IntRect played(x, y, wPlayed, h);
802     IntRect buffered(x, y, wLoaded, h);
803
804     // This is to paint main slider bar.
805     bool result = paintSliderTrackRect(object, paintInfo, rect2);
806
807     if (loaded > 0 || position > 0) {
808         // This is to paint buffered bar.
809         paintSliderTrackRect(object, paintInfo, buffered, Color::darkGray, Color::darkGray, Color::darkGray, Color::darkGray);
810
811         // This is to paint played part of bar (left of slider thumb) using selection color.
812         paintSliderTrackRect(object, paintInfo, played, selection, selection, selection, selection);
813     }
814     return result;
815
816 #else
817     UNUSED_PARAM(object);
818     UNUSED_PARAM(paintInfo);
819     UNUSED_PARAM(rect);
820     return false;
821 #endif
822 }
823
824 bool RenderThemeBlackBerry::paintMediaSliderThumb(RenderObject* object, const PaintInfo& paintInfo, const IntRect& rect)
825 {
826 #if ENABLE(VIDEO)
827     if (!object->parent())
828         return false;
829
830     RenderSlider* slider = toRenderSlider(object->parent()->parent()->parent());
831     if (!slider)
832         return false;
833
834     paintInfo.context->save();
835     Path mediaThumbRoundedRectangle;
836     mediaThumbRoundedRectangle.addRoundedRect(rect, FloatSize(mediaSliderThumbRadius, mediaSliderThumbRadius));
837     paintInfo.context->setStrokeStyle(SolidStroke);
838     paintInfo.context->setStrokeThickness(0.5);
839     paintInfo.context->setStrokeColor(Color::black, ColorSpaceDeviceRGB);
840
841     if (isPressed(object) || isHovered(object) || slider->inDragMode()) {
842         paintInfo.context->setFillGradient(createLinearGradient(selection, Color(selection).dark().rgb(),
843                 rect.maxXMinYCorner(), rect.maxXMaxYCorner()));
844     } else {
845         paintInfo.context->setFillGradient(createLinearGradient(Color::white, Color(Color::white).dark().rgb(),
846                 rect.maxXMinYCorner(), rect.maxXMaxYCorner()));
847     }
848     paintInfo.context->fillPath(mediaThumbRoundedRectangle);
849     paintInfo.context->restore();
850
851     return true;
852 #else
853     UNUSED_PARAM(object);
854     UNUSED_PARAM(paintInfo);
855     UNUSED_PARAM(rect);
856     return false;
857 #endif
858 }
859
860 bool RenderThemeBlackBerry::paintMediaVolumeSliderTrack(RenderObject* object, const PaintInfo& paintInfo, const IntRect& rect)
861 {
862 #if ENABLE(VIDEO)
863     float pad = rect.width() * 0.45;
864     float x = rect.x() + pad;
865     float y = rect.y() + pad;
866     float width = rect.width() * 0.1;
867     float height = rect.height() - (2.0 * pad);
868
869     IntRect rect2(x, y, width, height);
870
871     return paintSliderTrackRect(object, paintInfo, rect2);
872 #else
873     UNUSED_PARAM(object);
874     UNUSED_PARAM(paintInfo);
875     UNUSED_PARAM(rect);
876     return false;
877 #endif
878 }
879
880 bool RenderThemeBlackBerry::paintMediaVolumeSliderThumb(RenderObject* object, const PaintInfo& paintInfo, const IntRect& rect)
881 {
882 #if ENABLE(VIDEO)
883     static Image* mediaVolumeThumb = Image::loadPlatformResource("volume_thumb").leakRef();
884
885     return paintMediaButton(paintInfo.context, rect, mediaVolumeThumb);
886 #else
887     UNUSED_PARAM(object);
888     UNUSED_PARAM(paintInfo);
889     UNUSED_PARAM(rect);
890     return false;
891 #endif
892 }
893
894 Color RenderThemeBlackBerry::platformFocusRingColor() const
895 {
896     return focusRingPen;
897 }
898
899 #if ENABLE(TOUCH_EVENTS)
900 Color RenderThemeBlackBerry::platformTapHighlightColor() const
901 {
902     // Same color as 'focusRingPen' + 80 of alpha channel.
903     return Color(163, 200, 254, 80);
904 }
905 #endif
906
907 Color RenderThemeBlackBerry::platformActiveSelectionBackgroundColor() const
908 {
909     return Color(selection);
910 }
911
912 double RenderThemeBlackBerry::animationRepeatIntervalForProgressBar(RenderProgress* renderProgress) const
913 {
914     return renderProgress->isDeterminate() ? 0.0 : 0.1;
915 }
916
917 double RenderThemeBlackBerry::animationDurationForProgressBar(RenderProgress* renderProgress) const
918 {
919     return renderProgress->isDeterminate() ? 0.0 : 2.0;
920 }
921
922 bool RenderThemeBlackBerry::paintProgressBar(RenderObject* object, const PaintInfo& info, const IntRect& rect)
923 {
924     if (!object->isProgress())
925         return true;
926
927     RenderProgress* renderProgress = toRenderProgress(object);
928
929     FloatSize smallCorner(smallRadius, smallRadius);
930
931     info.context->save();
932     info.context->setStrokeStyle(SolidStroke);
933     info.context->setStrokeThickness(lineWidth);
934
935     info.context->setStrokeGradient(createLinearGradient(rangeSliderRegularTopOutline, rangeSliderRegularBottomOutline, rect.maxXMinYCorner(), rect.maxXMaxYCorner()));
936     info.context->setFillGradient(createLinearGradient(rangeSliderRegularTop, rangeSliderRegularBottom, rect.maxXMinYCorner(), rect.maxXMaxYCorner()));
937
938     Path path;
939     path.addRoundedRect(rect, smallCorner);
940     info.context->fillPath(path);
941
942     IntRect rect2 = rect;
943     rect2.setX(rect2.x() + 1);
944     rect2.setHeight(rect2.height() - 2);
945     rect2.setY(rect2.y() + 1);
946     info.context->setStrokeStyle(NoStroke);
947     info.context->setStrokeThickness(0);
948     if (renderProgress->isDeterminate()) {
949         rect2.setWidth(rect2.width() * renderProgress->position() - 2);
950         info.context->setFillGradient(createLinearGradient(progressRegularTop, progressRegularBottom, rect2.maxXMinYCorner(), rect2.maxXMaxYCorner()));
951     } else {
952         // Animating
953         rect2.setWidth(rect2.width() - 2);
954         RefPtr<Gradient> gradient = Gradient::create(rect2.minXMaxYCorner(), rect2.maxXMaxYCorner());
955         gradient->addColorStop(0.0, Color(progressRegularBottom));
956         gradient->addColorStop(renderProgress->animationProgress(), Color(progressRegularTop));
957         gradient->addColorStop(1.0, Color(progressRegularBottom));
958         info.context->setFillGradient(gradient);
959     }
960     Path path2;
961     path2.addRoundedRect(rect2, smallCorner);
962     info.context->fillPath(path2);
963
964     info.context->restore();
965     return false;
966 }
967
968 Color RenderThemeBlackBerry::platformActiveTextSearchHighlightColor() const
969 {
970     return Color(255, 150, 50); // Orange.
971 }
972
973 Color RenderThemeBlackBerry::platformInactiveTextSearchHighlightColor() const
974 {
975     return Color(255, 255, 0); // Yellow.
976 }
977
978 } // namespace WebCore