1 /*
2  * This file is part of the WebKit project.
3  *
4  * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies)
5  *
6  * Copyright (C) 2006 Zack Rusin <zack@kde.org>
7  *               2006 Dirk Mueller <mueller@kde.org>
8  *               2006 Nikolas Zimmermann <zimmermann@kde.org>
9  * Copyright (C) 2008 Holger Hans Peter Freyther
10  *
11  * All rights reserved.
12  *
13  * This library is free software; you can redistribute it and/or
14  * modify it under the terms of the GNU Library General Public
15  * License as published by the Free Software Foundation; either
16  * version 2 of the License, or (at your option) any later version.
17  *
18  * This library is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
21  * Library General Public License for more details.
22  *
23  * You should have received a copy of the GNU Library General Public License
24  * along with this library; see the file COPYING.LIB.  If not, write to
25  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
26  * Boston, MA 02110-1301, USA.
27  *
28  */
29 
30 #include "config.h"
31 #include "RenderThemeQt.h"
32 
33 #include "CSSStyleSelector.h"
34 #include "CSSStyleSheet.h"
35 #include "CSSValueKeywords.h"
36 #include "Chrome.h"
37 #include "ChromeClientQt.h"
38 #include "Color.h"
39 #include "Document.h"
40 #include "Font.h"
41 #include "FontSelector.h"
42 #include "GraphicsContext.h"
43 #include "HTMLInputElement.h"
44 #include "HTMLMediaElement.h"
45 #include "HTMLNames.h"
46 #if USE(QT_MOBILE_THEME)
47 #include "QtMobileWebStyle.h"
48 #endif
49 #if ENABLE(VIDEO)
50 #include "MediaControlElements.h"
51 #endif
52 #include "NotImplemented.h"
53 #include "PaintInfo.h"
54 #include "Page.h"
55 #include "QWebPageClient.h"
56 #include "QtStyleOptionWebComboBox.h"
57 #include "qwebsettings.h"
58 #include "RenderBox.h"
59 #if ENABLE(PROGRESS_TAG)
60 #include "RenderProgress.h"
61 #endif
62 #include "RenderSlider.h"
63 #include "RenderTheme.h"
64 #include "ScrollbarThemeQt.h"
65 #include "TimeRanges.h"
66 #include "UserAgentStyleSheets.h"
67 
68 #include <QApplication>
69 #include <QColor>
70 #include <QFile>
71 #include <QLineEdit>
72 #include <QMacStyle>
73 #include <QPainter>
74 #include <QPushButton>
75 #include <QStyleFactory>
76 #include <QStyleOptionButton>
77 #include <QStyleOptionFrameV2>
78 #if ENABLE(PROGRESS_TAG)
79 #include <QStyleOptionProgressBarV2>
80 #endif
81 #include <QStyleOptionSlider>
82 #include <QWidget>
83 
84 namespace WebCore {
85 
86 using namespace HTMLNames;
87 
initStyleOption(QWidget * widget,QStyleOption & option)88 inline static void initStyleOption(QWidget *widget, QStyleOption& option)
89 {
90     if (widget)
91         option.initFrom(widget);
92     else {
93         /*
94           If a widget is not directly available for rendering, we fallback to default
95           value for an active widget.
96          */
97         option.state = QStyle::State_Active | QStyle::State_Enabled;
98     }
99 }
100 // These values all match Safari/Win/Chromium
101 static const float defaultControlFontPixelSize = 13;
102 static const float defaultCancelButtonSize = 9;
103 static const float minCancelButtonSize = 5;
104 static const float maxCancelButtonSize = 21;
105 static const float defaultSearchFieldResultsDecorationSize = 13;
106 static const float minSearchFieldResultsDecorationSize = 9;
107 static const float maxSearchFieldResultsDecorationSize = 30;
108 static const float defaultSearchFieldResultsButtonWidth = 18;
109 
110 #if USE(QT_MOBILE_THEME)
111 namespace {
112     float buttonPaddingLeft = 18;
113     float buttonPaddingRight = 18;
114     float buttonPaddingTop = 2;
115     float buttonPaddingBottom = 3;
116     float menuListPadding = 9;
117     float textFieldPadding = 5;
118 }
119 #endif
120 
StylePainter(RenderThemeQt * theme,const PaintInfo & paintInfo)121 StylePainter::StylePainter(RenderThemeQt* theme, const PaintInfo& paintInfo)
122 {
123     init(paintInfo.context ? paintInfo.context : 0, theme->qStyle());
124 }
125 
StylePainter(ScrollbarThemeQt * theme,GraphicsContext * context)126 StylePainter::StylePainter(ScrollbarThemeQt* theme, GraphicsContext* context)
127 {
128     init(context, theme->style());
129 }
130 
init(GraphicsContext * context,QStyle * themeStyle)131 void StylePainter::init(GraphicsContext* context, QStyle* themeStyle)
132 {
133     painter = static_cast<QPainter*>(context->platformContext());
134     widget = 0;
135     QPaintDevice* dev = 0;
136     if (painter)
137         dev = painter->device();
138     if (dev && dev->devType() == QInternal::Widget)
139         widget = static_cast<QWidget*>(dev);
140     style = themeStyle;
141 
142     if (painter) {
143         // the styles often assume being called with a pristine painter where no brush is set,
144         // so reset it manually
145         oldBrush = painter->brush();
146         painter->setBrush(Qt::NoBrush);
147 
148         // painting the widget with anti-aliasing will make it blurry
149         // disable it here and restore it later
150         oldAntialiasing = painter->testRenderHint(QPainter::Antialiasing);
151         painter->setRenderHint(QPainter::Antialiasing, false);
152     }
153 }
154 
~StylePainter()155 StylePainter::~StylePainter()
156 {
157     if (painter) {
158         painter->setBrush(oldBrush);
159         painter->setRenderHints(QPainter::Antialiasing, oldAntialiasing);
160     }
161 }
162 
create(Page * page)163 PassRefPtr<RenderTheme> RenderThemeQt::create(Page* page)
164 {
165     return adoptRef(new RenderThemeQt(page));
166 }
167 
themeForPage(Page * page)168 PassRefPtr<RenderTheme> RenderTheme::themeForPage(Page* page)
169 {
170     if (page)
171         return RenderThemeQt::create(page);
172 
173     static RenderTheme* fallback = RenderThemeQt::create(0).releaseRef();
174     return fallback;
175 }
176 
RenderThemeQt(Page * page)177 RenderThemeQt::RenderThemeQt(Page* page)
178     : RenderTheme()
179     , m_page(page)
180     , m_lineEdit(0)
181     , m_fallbackStyle(0)
182 {
183     if (QApplication::type() == QApplication::Tty) {
184         m_buttonFontFamily = "sans-serif";
185         return;
186     }
187 
188     QPushButton button;
189     button.setAttribute(Qt::WA_MacSmallSize);
190     QFont defaultButtonFont = QApplication::font(&button);
191     QFontInfo fontInfo(defaultButtonFont);
192     m_buttonFontFamily = defaultButtonFont.family();
193 #ifdef Q_WS_MAC
194     m_buttonFontPixelSize = fontInfo.pixelSize();
195 #endif
196 
197 #if USE(QT_MOBILE_THEME)
198     m_fallbackStyle = new QtMobileWebStyle;
199 #else
200     m_fallbackStyle = QStyleFactory::create(QLatin1String("windows"));
201 #endif
202 }
203 
~RenderThemeQt()204 RenderThemeQt::~RenderThemeQt()
205 {
206     delete m_fallbackStyle;
207 #ifndef QT_NO_LINEEDIT
208     delete m_lineEdit;
209 #endif
210 }
211 
212 #if USE(QT_MOBILE_THEME)
isControlStyled(const RenderStyle * style,const BorderData & border,const FillLayer & fill,const Color & backgroundColor) const213 bool RenderThemeQt::isControlStyled(const RenderStyle* style, const BorderData& border, const FillLayer& fill, const Color& backgroundColor) const
214 {
215     switch (style->appearance()) {
216     case PushButtonPart:
217     case ButtonPart:
218     case MenulistPart:
219     case SearchFieldPart:
220     case TextFieldPart:
221     case TextAreaPart:
222         // Test the style to see if the UA border and background match.
223         return (style->border() != border
224                 || *style->backgroundLayers() != fill
225                 || style->visitedDependentColor(CSSPropertyBackgroundColor) != backgroundColor);
226     case CheckboxPart:
227     case RadioPart:
228         return false;
229     default:
230         return RenderTheme::isControlStyled(style, border, fill, backgroundColor);
231     }
232 }
233 
popupInternalPaddingBottom(RenderStyle * style) const234 int RenderThemeQt::popupInternalPaddingBottom(RenderStyle* style) const
235 {
236     return 1;
237 }
238 #else
239 // Remove this when SearchFieldPart is style-able in RenderTheme::isControlStyled()
isControlStyled(const RenderStyle * style,const BorderData & border,const FillLayer & fill,const Color & backgroundColor) const240 bool RenderThemeQt::isControlStyled(const RenderStyle* style, const BorderData& border, const FillLayer& fill, const Color& backgroundColor) const
241 {
242     switch (style->appearance()) {
243     case SearchFieldPart:
244         // Test the style to see if the UA border and background match.
245         return (style->border() != border
246                 || *style->backgroundLayers() != fill
247                 || style->visitedDependentColor(CSSPropertyBackgroundColor) != backgroundColor);
248     default:
249         return RenderTheme::isControlStyled(style, border, fill, backgroundColor);
250     }
251 }
252 #endif
253 
254 // for some widget painting, we need to fallback to Windows style
fallbackStyle() const255 QStyle* RenderThemeQt::fallbackStyle() const
256 {
257     return (m_fallbackStyle) ? m_fallbackStyle : QApplication::style();
258 }
259 
qStyle() const260 QStyle* RenderThemeQt::qStyle() const
261 {
262 #if USE(QT_MOBILE_THEME)
263     return fallbackStyle();
264 #endif
265 
266     if (m_page) {
267         QWebPageClient* pageClient = m_page->chrome()->client()->platformPageClient();
268 
269         if (pageClient)
270             return pageClient->style();
271     }
272 
273     return QApplication::style();
274 }
275 
extraDefaultStyleSheet()276 String RenderThemeQt::extraDefaultStyleSheet()
277 {
278     String result = RenderTheme::extraDefaultStyleSheet();
279 #if ENABLE(NO_LISTBOX_RENDERING)
280     result += String(themeQtNoListboxesUserAgentStyleSheet, sizeof(themeQtNoListboxesUserAgentStyleSheet));
281 #endif
282     return result;
283 }
284 
supportsHover(const RenderStyle *) const285 bool RenderThemeQt::supportsHover(const RenderStyle*) const
286 {
287     return true;
288 }
289 
supportsFocusRing(const RenderStyle * style) const290 bool RenderThemeQt::supportsFocusRing(const RenderStyle* style) const
291 {
292     switch (style->appearance()) {
293     case CheckboxPart:
294     case RadioPart:
295     case PushButtonPart:
296     case SquareButtonPart:
297     case ButtonPart:
298     case ButtonBevelPart:
299     case ListboxPart:
300     case ListItemPart:
301     case MenulistPart:
302     case MenulistButtonPart:
303     case SliderHorizontalPart:
304     case SliderVerticalPart:
305     case SliderThumbHorizontalPart:
306     case SliderThumbVerticalPart:
307     case SearchFieldPart:
308     case SearchFieldResultsButtonPart:
309     case SearchFieldCancelButtonPart:
310     case TextFieldPart:
311     case TextAreaPart:
312         return true;
313     default:
314         return false;
315     }
316 }
317 
baselinePosition(const RenderObject * o) const318 int RenderThemeQt::baselinePosition(const RenderObject* o) const
319 {
320     if (!o->isBox())
321         return 0;
322 
323     if (o->style()->appearance() == CheckboxPart || o->style()->appearance() == RadioPart)
324         return toRenderBox(o)->marginTop() + toRenderBox(o)->height() - 2; // Same as in old khtml
325     return RenderTheme::baselinePosition(o);
326 }
327 
controlSupportsTints(const RenderObject * o) const328 bool RenderThemeQt::controlSupportsTints(const RenderObject* o) const
329 {
330     if (!isEnabled(o))
331         return false;
332 
333     // Checkboxes only have tint when checked.
334     if (o->style()->appearance() == CheckboxPart)
335         return isChecked(o);
336 
337     // For now assume other controls have tint if enabled.
338     return true;
339 }
340 
supportsControlTints() const341 bool RenderThemeQt::supportsControlTints() const
342 {
343     return true;
344 }
345 
findFrameLineWidth(QStyle * style) const346 int RenderThemeQt::findFrameLineWidth(QStyle* style) const
347 {
348     if (QApplication::type()==QApplication::Tty)
349         return 1;
350 
351 #ifndef QT_NO_LINEEDIT
352     if (!m_lineEdit)
353         m_lineEdit = new QLineEdit();
354 #endif
355 
356     QStyleOptionFrameV2 opt;
357     QWidget* widget = 0;
358 #ifndef QT_NO_LINEEDIT
359     widget = m_lineEdit;
360 #endif
361     return style->pixelMetric(QStyle::PM_DefaultFrameWidth, &opt, widget);
362 }
363 
inflateButtonRect(const QRect & originalRect,QStyle * style)364 static QRect inflateButtonRect(const QRect& originalRect, QStyle* style)
365 {
366     QStyleOptionButton option;
367     option.state |= QStyle::State_Small;
368     option.rect = originalRect;
369 
370     QRect layoutRect = style->subElementRect(QStyle::SE_PushButtonLayoutItem, &option, 0);
371     if (!layoutRect.isNull()) {
372         int paddingLeft = layoutRect.left() - originalRect.left();
373         int paddingRight = originalRect.right() - layoutRect.right();
374         int paddingTop = layoutRect.top() - originalRect.top();
375         int paddingBottom = originalRect.bottom() - layoutRect.bottom();
376 
377         return originalRect.adjusted(-paddingLeft, -paddingTop, paddingRight, paddingBottom);
378     }
379     return originalRect;
380 }
381 
adjustRepaintRect(const RenderObject * o,IntRect & rect)382 void RenderThemeQt::adjustRepaintRect(const RenderObject* o, IntRect& rect)
383 {
384     switch (o->style()->appearance()) {
385     case CheckboxPart:
386         break;
387     case RadioPart:
388         break;
389     case PushButtonPart:
390     case ButtonPart: {
391         QRect inflatedRect = inflateButtonRect(rect, qStyle());
392         rect = IntRect(inflatedRect.x(), inflatedRect.y(), inflatedRect.width(), inflatedRect.height());
393         break;
394     }
395     case MenulistPart:
396         break;
397     default:
398         break;
399     }
400 }
401 
platformActiveSelectionBackgroundColor() const402 Color RenderThemeQt::platformActiveSelectionBackgroundColor() const
403 {
404     QPalette pal = QApplication::palette();
405     setPaletteFromPageClientIfExists(pal);
406     return pal.brush(QPalette::Active, QPalette::Highlight).color();
407 }
408 
platformInactiveSelectionBackgroundColor() const409 Color RenderThemeQt::platformInactiveSelectionBackgroundColor() const
410 {
411     QPalette pal = QApplication::palette();
412     setPaletteFromPageClientIfExists(pal);
413     return pal.brush(QPalette::Inactive, QPalette::Highlight).color();
414 }
415 
platformActiveSelectionForegroundColor() const416 Color RenderThemeQt::platformActiveSelectionForegroundColor() const
417 {
418     QPalette pal = QApplication::palette();
419     setPaletteFromPageClientIfExists(pal);
420     return pal.brush(QPalette::Active, QPalette::HighlightedText).color();
421 }
422 
platformInactiveSelectionForegroundColor() const423 Color RenderThemeQt::platformInactiveSelectionForegroundColor() const
424 {
425     QPalette pal = QApplication::palette();
426     setPaletteFromPageClientIfExists(pal);
427     return pal.brush(QPalette::Inactive, QPalette::HighlightedText).color();
428 }
429 
platformFocusRingColor() const430 Color RenderThemeQt::platformFocusRingColor() const
431 {
432     QPalette pal = QApplication::palette();
433     setPaletteFromPageClientIfExists(pal);
434     return pal.brush(QPalette::Active, QPalette::Highlight).color();
435 }
436 
systemFont(int,FontDescription &) const437 void RenderThemeQt::systemFont(int, FontDescription&) const
438 {
439     // no-op
440 }
441 
systemColor(int cssValueId) const442 Color RenderThemeQt::systemColor(int cssValueId) const
443 {
444     QPalette pal = QApplication::palette();
445     switch (cssValueId) {
446     case CSSValueButtontext:
447         return pal.brush(QPalette::Active, QPalette::ButtonText).color();
448     case CSSValueCaptiontext:
449         return pal.brush(QPalette::Active, QPalette::Text).color();
450     default:
451         return RenderTheme::systemColor(cssValueId);
452     }
453 }
454 
minimumMenuListSize(RenderStyle *) const455 int RenderThemeQt::minimumMenuListSize(RenderStyle*) const
456 {
457     if (QApplication::type() == QApplication::Tty)
458         return 1;
459 
460     const QFontMetrics &fm = QApplication::fontMetrics();
461     return fm.width(QLatin1Char('x'));
462 }
463 
computeSizeBasedOnStyle(RenderStyle * renderStyle) const464 void RenderThemeQt::computeSizeBasedOnStyle(RenderStyle* renderStyle) const
465 {
466     QSize size(0, 0);
467     const QFontMetrics fm(renderStyle->font().font());
468     QStyle* style = qStyle();
469 
470     switch (renderStyle->appearance()) {
471     case TextAreaPart:
472     case SearchFieldPart:
473     case TextFieldPart: {
474         int padding = findFrameLineWidth(style);
475         renderStyle->setPaddingLeft(Length(padding, Fixed));
476         renderStyle->setPaddingRight(Length(padding, Fixed));
477         renderStyle->setPaddingTop(Length(padding, Fixed));
478         renderStyle->setPaddingBottom(Length(padding, Fixed));
479         break;
480     }
481     default:
482         break;
483     }
484     // If the width and height are both specified, then we have nothing to do.
485     if (!renderStyle->width().isIntrinsicOrAuto() && !renderStyle->height().isAuto())
486         return;
487 
488     switch (renderStyle->appearance()) {
489     case CheckboxPart: {
490         QStyleOption styleOption;
491         styleOption.state |= QStyle::State_Small;
492         int checkBoxWidth = style->pixelMetric(QStyle::PM_IndicatorWidth, &styleOption);
493         checkBoxWidth *= renderStyle->effectiveZoom();
494         size = QSize(checkBoxWidth, checkBoxWidth);
495         break;
496     }
497     case RadioPart: {
498         QStyleOption styleOption;
499         styleOption.state |= QStyle::State_Small;
500         int radioWidth = style->pixelMetric(QStyle::PM_ExclusiveIndicatorWidth, &styleOption);
501         radioWidth *= renderStyle->effectiveZoom();
502         size = QSize(radioWidth, radioWidth);
503         break;
504     }
505 #if !USE(QT_MOBILE_THEME)
506     case PushButtonPart:
507     case ButtonPart: {
508         QStyleOptionButton styleOption;
509         styleOption.state |= QStyle::State_Small;
510         QSize contentSize = fm.size(Qt::TextShowMnemonic, QString::fromLatin1("X"));
511         QSize pushButtonSize = style->sizeFromContents(QStyle::CT_PushButton,
512                                                        &styleOption, contentSize, 0);
513         styleOption.rect = QRect(0, 0, pushButtonSize.width(), pushButtonSize.height());
514         QRect layoutRect = style->subElementRect(QStyle::SE_PushButtonLayoutItem,
515                                                  &styleOption, 0);
516 
517         // If the style supports layout rects we use that, and  compensate accordingly
518         // in paintButton() below.
519         if (!layoutRect.isNull())
520             size.setHeight(layoutRect.height());
521         else
522             size.setHeight(pushButtonSize.height());
523 
524         break;
525     }
526     case MenulistPart: {
527         QStyleOptionComboBox styleOption;
528         styleOption.state |= QStyle::State_Small;
529         int contentHeight = qMax(fm.lineSpacing(), 14) + 2;
530         QSize menuListSize = style->sizeFromContents(QStyle::CT_ComboBox,
531                                                      &styleOption, QSize(0, contentHeight), 0);
532         size.setHeight(menuListSize.height());
533         break;
534     }
535 #endif
536     default:
537         break;
538     }
539 
540     // FIXME: Check is flawed, since it doesn't take min-width/max-width into account.
541     if (renderStyle->width().isIntrinsicOrAuto() && size.width() > 0)
542         renderStyle->setMinWidth(Length(size.width(), Fixed));
543     if (renderStyle->height().isAuto() && size.height() > 0)
544         renderStyle->setMinHeight(Length(size.height(), Fixed));
545 }
546 
setCheckboxSize(RenderStyle * style) const547 void RenderThemeQt::setCheckboxSize(RenderStyle* style) const
548 {
549     computeSizeBasedOnStyle(style);
550 }
551 
paintCheckbox(RenderObject * o,const PaintInfo & i,const IntRect & r)552 bool RenderThemeQt::paintCheckbox(RenderObject* o, const PaintInfo& i, const IntRect& r)
553 {
554     return paintButton(o, i, r);
555 }
556 
setRadioSize(RenderStyle * style) const557 void RenderThemeQt::setRadioSize(RenderStyle* style) const
558 {
559     computeSizeBasedOnStyle(style);
560 }
561 
paintRadio(RenderObject * o,const PaintInfo & i,const IntRect & r)562 bool RenderThemeQt::paintRadio(RenderObject* o, const PaintInfo& i, const IntRect& r)
563 {
564     return paintButton(o, i, r);
565 }
566 
adjustButtonStyle(CSSStyleSelector * selector,RenderStyle * style,Element *) const567 void RenderThemeQt::adjustButtonStyle(CSSStyleSelector* selector, RenderStyle* style, Element*) const
568 {
569     // Ditch the border.
570     style->resetBorder();
571 
572 #ifdef Q_WS_MAC
573     if (style->appearance() == PushButtonPart) {
574         // The Mac ports ignore the specified height for <input type="button"> elements
575         // unless a border and/or background CSS property is also specified.
576         style->setHeight(Length(Auto));
577     }
578 #endif
579 
580     FontDescription fontDescription = style->fontDescription();
581     fontDescription.setIsAbsoluteSize(true);
582 
583 #ifdef Q_WS_MAC // Use fixed font size and family on Mac (like Safari does)
584     fontDescription.setSpecifiedSize(m_buttonFontPixelSize);
585     fontDescription.setComputedSize(m_buttonFontPixelSize);
586 #else
587     fontDescription.setSpecifiedSize(style->fontSize());
588     fontDescription.setComputedSize(style->fontSize());
589 #endif
590 
591 #if !USE(QT_MOBILE_THEME)
592     FontFamily fontFamily;
593     fontFamily.setFamily(m_buttonFontFamily);
594     fontDescription.setFamily(fontFamily);
595     style->setFontDescription(fontDescription);
596     style->font().update(selector->fontSelector());
597 #endif
598     style->setLineHeight(RenderStyle::initialLineHeight());
599     setButtonSize(style);
600     setButtonPadding(style);
601 }
602 
setButtonSize(RenderStyle * style) const603 void RenderThemeQt::setButtonSize(RenderStyle* style) const
604 {
605     computeSizeBasedOnStyle(style);
606 }
607 
608 #if !USE(QT_MOBILE_THEME)
setButtonPadding(RenderStyle * style) const609 void RenderThemeQt::setButtonPadding(RenderStyle* style) const
610 {
611     QStyleOptionButton styleOption;
612     styleOption.state |= QStyle::State_Small;
613 
614     // Fake a button rect here, since we're just computing deltas
615     QRect originalRect = QRect(0, 0, 100, 30);
616     styleOption.rect = originalRect;
617 
618     // Default padding is based on the button margin pixel metric
619     int buttonMargin = qStyle()->pixelMetric(QStyle::PM_ButtonMargin, &styleOption, 0);
620     int paddingLeft = buttonMargin;
621     int paddingRight = buttonMargin;
622     int paddingTop = buttonMargin;
623     int paddingBottom = buttonMargin;
624 
625     // Then check if the style uses layout margins
626     QRect layoutRect = qStyle()->subElementRect(QStyle::SE_PushButtonLayoutItem,
627                                                 &styleOption, 0);
628     if (!layoutRect.isNull()) {
629         QRect contentsRect = qStyle()->subElementRect(QStyle::SE_PushButtonContents,
630                                                       &styleOption, 0);
631         paddingLeft = contentsRect.left() - layoutRect.left();
632         paddingRight = layoutRect.right() - contentsRect.right();
633         paddingTop = contentsRect.top() - layoutRect.top();
634 
635         // Can't use this right now because we don't have the baseline to compensate
636         // paddingBottom = layoutRect.bottom() - contentsRect.bottom();
637     }
638     style->setPaddingLeft(Length(paddingLeft, Fixed));
639     style->setPaddingRight(Length(paddingRight, Fixed));
640     style->setPaddingTop(Length(paddingTop, Fixed));
641     style->setPaddingBottom(Length(paddingBottom, Fixed));
642 }
643 #else
setButtonPadding(RenderStyle * style) const644 void RenderThemeQt::setButtonPadding(RenderStyle* style) const
645 {
646     if (!style)
647         return;
648     style->setPaddingLeft(Length(buttonPaddingLeft, Fixed));
649     style->setPaddingRight(Length(buttonPaddingRight, Fixed));
650     style->setPaddingTop(Length(buttonPaddingTop, Fixed));
651     style->setPaddingBottom(Length(buttonPaddingBottom, Fixed));
652 }
653 #endif
654 
paintButton(RenderObject * o,const PaintInfo & i,const IntRect & r)655 bool RenderThemeQt::paintButton(RenderObject* o, const PaintInfo& i, const IntRect& r)
656 {
657     StylePainter p(this, i);
658     if (!p.isValid())
659        return true;
660 
661     QStyleOptionButton option;
662     initStyleOption(p.widget, option);
663     option.rect = r;
664     option.state |= QStyle::State_Small;
665 
666     ControlPart appearance = initializeCommonQStyleOptions(option, o);
667     if (appearance == PushButtonPart || appearance == ButtonPart) {
668         option.rect = inflateButtonRect(option.rect, qStyle());
669         p.drawControl(QStyle::CE_PushButton, option);
670     } else if (appearance == RadioPart)
671        p.drawControl(QStyle::CE_RadioButton, option);
672     else if (appearance == CheckboxPart)
673        p.drawControl(QStyle::CE_CheckBox, option);
674 
675     return false;
676 }
677 
adjustTextFieldStyle(CSSStyleSelector *,RenderStyle * style,Element *) const678 void RenderThemeQt::adjustTextFieldStyle(CSSStyleSelector*, RenderStyle* style, Element*) const
679 {
680     // Resetting the style like this leads to differences like:
681     // - RenderTextControl {INPUT} at (2,2) size 168x25 [bgcolor=#FFFFFF] border: (2px inset #000000)]
682     // + RenderTextControl {INPUT} at (2,2) size 166x26
683     // in layout tests when a CSS style is applied that doesn't affect background color, border or
684     // padding. Just worth keeping in mind!
685     style->setBackgroundColor(Color::transparent);
686     style->resetBorder();
687     style->resetPadding();
688     computeSizeBasedOnStyle(style);
689 #if USE(QT_MOBILE_THEME)
690     style->setPaddingLeft(Length(textFieldPadding, Fixed));
691     style->setPaddingRight(Length(textFieldPadding, Fixed));
692 #endif
693 }
694 
paintTextField(RenderObject * o,const PaintInfo & i,const IntRect & r)695 bool RenderThemeQt::paintTextField(RenderObject* o, const PaintInfo& i, const IntRect& r)
696 {
697     StylePainter p(this, i);
698     if (!p.isValid())
699         return true;
700 
701     QStyleOptionFrameV2 panel;
702     initStyleOption(p.widget, panel);
703     panel.rect = r;
704     panel.lineWidth = findFrameLineWidth(qStyle());
705 #if USE(QT_MOBILE_THEME)
706     if (isPressed(o))
707         panel.state |= QStyle::State_Sunken;
708 #else
709     panel.state |= QStyle::State_Sunken;
710 #endif
711     panel.features = QStyleOptionFrameV2::None;
712 
713     // Get the correct theme data for a text field
714     ControlPart appearance = initializeCommonQStyleOptions(panel, o);
715     if (appearance != TextFieldPart
716         && appearance != SearchFieldPart
717         && appearance != TextAreaPart
718         && appearance != ListboxPart)
719         return true;
720 
721     // Now paint the text field.
722     p.drawPrimitive(QStyle::PE_PanelLineEdit, panel);
723     return false;
724 }
725 
adjustTextAreaStyle(CSSStyleSelector * selector,RenderStyle * style,Element * element) const726 void RenderThemeQt::adjustTextAreaStyle(CSSStyleSelector* selector, RenderStyle* style, Element* element) const
727 {
728     adjustTextFieldStyle(selector, style, element);
729 }
730 
paintTextArea(RenderObject * o,const PaintInfo & i,const IntRect & r)731 bool RenderThemeQt::paintTextArea(RenderObject* o, const PaintInfo& i, const IntRect& r)
732 {
733     return paintTextField(o, i, r);
734 }
735 
adjustMenuListStyle(CSSStyleSelector *,RenderStyle * style,Element *) const736 void RenderThemeQt::adjustMenuListStyle(CSSStyleSelector*, RenderStyle* style, Element*) const
737 {
738     style->resetBorder();
739 
740     // Height is locked to auto.
741     style->setHeight(Length(Auto));
742 
743     // White-space is locked to pre
744     style->setWhiteSpace(PRE);
745 
746     computeSizeBasedOnStyle(style);
747 
748     // Add in the padding that we'd like to use.
749     setPopupPadding(style);
750 #if USE(QT_MOBILE_THEME)
751     style->setPaddingLeft(Length(menuListPadding, Fixed));
752 #endif
753 }
754 
setPopupPadding(RenderStyle * style) const755 void RenderThemeQt::setPopupPadding(RenderStyle* style) const
756 {
757     const int paddingLeft = 4;
758     const int paddingRight = style->width().isFixed() || style->width().isPercent() ? 5 : 8;
759 
760     style->setPaddingLeft(Length(paddingLeft, Fixed));
761 
762     QStyleOptionComboBox opt;
763     int w = qStyle()->pixelMetric(QStyle::PM_ButtonIconSize, &opt, 0);
764     style->setPaddingRight(Length(paddingRight + w, Fixed));
765 
766     style->setPaddingTop(Length(2, Fixed));
767     style->setPaddingBottom(Length(2, Fixed));
768 }
769 
770 
paintMenuList(RenderObject * o,const PaintInfo & i,const IntRect & r)771 bool RenderThemeQt::paintMenuList(RenderObject* o, const PaintInfo& i, const IntRect& r)
772 {
773     StylePainter p(this, i);
774     if (!p.isValid())
775         return true;
776 
777     QtStyleOptionWebComboBox opt(o);
778     initStyleOption(p.widget, opt);
779     initializeCommonQStyleOptions(opt, o);
780 
781     IntRect rect = r;
782 
783 #if defined(Q_WS_MAC) && !defined(QT_NO_STYLE_MAC)
784     // QMacStyle makes the combo boxes a little bit smaller to leave space for the focus rect.
785     // Because of it, the combo button is drawn at a point to the left of where it was expect to be and may end up
786     // overlapped with the text. This will force QMacStyle to draw the combo box with the expected width.
787     if (qobject_cast<QMacStyle*>(p.style))
788         rect.inflateX(3);
789 #endif
790 
791     const QPoint topLeft = rect.location();
792     p.painter->translate(topLeft);
793     opt.rect.moveTo(QPoint(0, 0));
794     opt.rect.setSize(rect.size());
795 
796     p.drawComplexControl(QStyle::CC_ComboBox, opt);
797     p.painter->translate(-topLeft);
798     return false;
799 }
800 
adjustMenuListButtonStyle(CSSStyleSelector *,RenderStyle * style,Element *) const801 void RenderThemeQt::adjustMenuListButtonStyle(CSSStyleSelector*, RenderStyle* style, Element*) const
802 {
803 #if USE(QT_MOBILE_THEME)
804     // Mobile theme uses border radius.
805 #else
806     // WORKAROUND because html.css specifies -webkit-border-radius for <select> so we override it here
807     // see also http://bugs.webkit.org/show_bug.cgi?id=18399
808     style->resetBorderRadius();
809 #endif
810 
811     // Height is locked to auto.
812     style->setHeight(Length(Auto));
813 
814     // White-space is locked to pre
815     style->setWhiteSpace(PRE);
816 
817     computeSizeBasedOnStyle(style);
818 
819     // Add in the padding that we'd like to use.
820     setPopupPadding(style);
821 }
822 
paintMenuListButton(RenderObject * o,const PaintInfo & i,const IntRect & r)823 bool RenderThemeQt::paintMenuListButton(RenderObject* o, const PaintInfo& i,
824                                         const IntRect& r)
825 {
826     StylePainter p(this, i);
827     if (!p.isValid())
828         return true;
829 
830     QtStyleOptionWebComboBox option(o);
831     initStyleOption(p.widget, option);
832     initializeCommonQStyleOptions(option, o);
833     option.rect = r;
834 
835     // for drawing the combo box arrow, rely only on the fallback style
836     p.style = fallbackStyle();
837     option.subControls = QStyle::SC_ComboBoxArrow;
838     p.drawComplexControl(QStyle::CC_ComboBox, option);
839 
840     return false;
841 }
842 
843 #if ENABLE(PROGRESS_TAG)
animationRepeatIntervalForProgressBar(RenderProgress * renderProgress) const844 double RenderThemeQt::animationRepeatIntervalForProgressBar(RenderProgress* renderProgress) const
845 {
846     if (renderProgress->position() >= 0)
847         return 0;
848 
849     // FIXME: Use hard-coded value until http://bugreports.qt.nokia.com/browse/QTBUG-9171 is fixed.
850     // Use the value from windows style which is 10 fps.
851     return 0.1;
852 }
853 
animationDurationForProgressBar(RenderProgress * renderProgress) const854 double RenderThemeQt::animationDurationForProgressBar(RenderProgress* renderProgress) const
855 {
856     if (renderProgress->position() >= 0)
857         return 0;
858 
859     QStyleOptionProgressBarV2 option;
860     option.rect.setSize(renderProgress->size());
861     // FIXME: Until http://bugreports.qt.nokia.com/browse/QTBUG-9171 is fixed,
862     // we simulate one square animating across the progress bar.
863     return (option.rect.width() / qStyle()->pixelMetric(QStyle::PM_ProgressBarChunkWidth, &option)) * animationRepeatIntervalForProgressBar(renderProgress);
864 }
865 
adjustProgressBarStyle(CSSStyleSelector *,RenderStyle * style,Element *) const866 void RenderThemeQt::adjustProgressBarStyle(CSSStyleSelector*, RenderStyle* style, Element*) const
867 {
868     style->setBoxShadow(nullptr);
869 }
870 
paintProgressBar(RenderObject * o,const PaintInfo & pi,const IntRect & r)871 bool RenderThemeQt::paintProgressBar(RenderObject* o, const PaintInfo& pi, const IntRect& r)
872 {
873     if (!o->isProgress())
874         return true;
875 
876     StylePainter p(this, pi);
877     if (!p.isValid())
878        return true;
879 
880     QStyleOptionProgressBarV2 option;
881     initStyleOption(p.widget, option);
882     initializeCommonQStyleOptions(option, o);
883 
884     RenderProgress* renderProgress = toRenderProgress(o);
885     option.rect = r;
886     option.maximum = std::numeric_limits<int>::max();
887     option.minimum = 0;
888     option.progress = (renderProgress->position() * std::numeric_limits<int>::max());
889 
890     const QPoint topLeft = r.location();
891     p.painter->translate(topLeft);
892     option.rect.moveTo(QPoint(0, 0));
893     option.rect.setSize(r.size());
894 
895     if (option.progress < 0) {
896         // FIXME: Until http://bugreports.qt.nokia.com/browse/QTBUG-9171 is fixed,
897         // we simulate one square animating across the progress bar.
898         p.drawControl(QStyle::CE_ProgressBarGroove, option);
899         int chunkWidth = qStyle()->pixelMetric(QStyle::PM_ProgressBarChunkWidth, &option);
900         QColor color = (option.palette.highlight() == option.palette.background()) ? option.palette.color(QPalette::Active, QPalette::Highlight) : option.palette.color(QPalette::Highlight);
901         if (renderProgress->style()->direction() == RTL)
902             p.painter->fillRect(option.rect.right() - chunkWidth  - renderProgress->animationProgress() * option.rect.width(), 0, chunkWidth, option.rect.height(), color);
903         else
904             p.painter->fillRect(renderProgress->animationProgress() * option.rect.width(), 0, chunkWidth, option.rect.height(), color);
905     } else
906         p.drawControl(QStyle::CE_ProgressBar, option);
907 
908     p.painter->translate(-topLeft);
909 
910     return false;
911 }
912 #endif
913 
paintSliderTrack(RenderObject * o,const PaintInfo & pi,const IntRect & r)914 bool RenderThemeQt::paintSliderTrack(RenderObject* o, const PaintInfo& pi,
915                                      const IntRect& r)
916 {
917     StylePainter p(this, pi);
918     if (!p.isValid())
919        return true;
920 
921     QStyleOptionSlider option;
922     initStyleOption(p.widget, option);
923     option.subControls = QStyle::SC_SliderGroove | QStyle::SC_SliderHandle;
924     ControlPart appearance = initializeCommonQStyleOptions(option, o);
925 
926     RenderSlider* renderSlider = toRenderSlider(o);
927     IntRect thumbRect = renderSlider->thumbRect();
928 
929     option.rect = r;
930 
931     int value;
932     if (appearance == SliderVerticalPart) {
933         option.maximum = r.height() - thumbRect.height();
934         value = thumbRect.y();
935     } else {
936         option.maximum = r.width() - thumbRect.width();
937         value = thumbRect.x();
938     }
939 
940     value = QStyle::sliderValueFromPosition(0, option.maximum, value, option.maximum);
941 
942     option.sliderValue = value;
943     option.sliderPosition = value;
944     if (appearance == SliderVerticalPart)
945         option.orientation = Qt::Vertical;
946 
947     if (renderSlider->inDragMode()) {
948         option.activeSubControls = QStyle::SC_SliderHandle;
949         option.state |= QStyle::State_Sunken;
950     }
951 
952     const QPoint topLeft = r.location();
953     p.painter->translate(topLeft);
954     option.rect.moveTo(QPoint(0, 0));
955     option.rect.setSize(r.size());
956 
957     p.drawComplexControl(QStyle::CC_Slider, option);
958     p.painter->translate(-topLeft);
959 
960     return false;
961 }
962 
adjustSliderTrackStyle(CSSStyleSelector *,RenderStyle * style,Element *) const963 void RenderThemeQt::adjustSliderTrackStyle(CSSStyleSelector*, RenderStyle* style, Element*) const
964 {
965     style->setBoxShadow(nullptr);
966 }
967 
paintSliderThumb(RenderObject * o,const PaintInfo & pi,const IntRect & r)968 bool RenderThemeQt::paintSliderThumb(RenderObject* o, const PaintInfo& pi,
969                                      const IntRect& r)
970 {
971     // We've already painted it in paintSliderTrack(), no need to do anything here.
972     return false;
973 }
974 
adjustSliderThumbStyle(CSSStyleSelector *,RenderStyle * style,Element *) const975 void RenderThemeQt::adjustSliderThumbStyle(CSSStyleSelector*, RenderStyle* style, Element*) const
976 {
977     style->setBoxShadow(nullptr);
978 }
979 
paintSearchField(RenderObject * o,const PaintInfo & pi,const IntRect & r)980 bool RenderThemeQt::paintSearchField(RenderObject* o, const PaintInfo& pi,
981                                      const IntRect& r)
982 {
983     return paintTextField(o, pi, r);
984 }
985 
adjustSearchFieldStyle(CSSStyleSelector * selector,RenderStyle * style,Element * e) const986 void RenderThemeQt::adjustSearchFieldStyle(CSSStyleSelector* selector, RenderStyle* style,
987                                            Element* e) const
988 {
989     // Resetting the style like this leads to differences like:
990     // - RenderTextControl {INPUT} at (2,2) size 168x25 [bgcolor=#FFFFFF] border: (2px inset #000000)]
991     // + RenderTextControl {INPUT} at (2,2) size 166x26
992     // in layout tests when a CSS style is applied that doesn't affect background color, border or
993     // padding. Just worth keeping in mind!
994     style->setBackgroundColor(Color::transparent);
995     style->resetBorder();
996     style->resetPadding();
997     computeSizeBasedOnStyle(style);
998 }
999 
adjustSearchFieldCancelButtonStyle(CSSStyleSelector * selector,RenderStyle * style,Element * e) const1000 void RenderThemeQt::adjustSearchFieldCancelButtonStyle(CSSStyleSelector* selector, RenderStyle* style,
1001                                                        Element* e) const
1002 {
1003     // Logic taken from RenderThemeChromium.cpp.
1004     // Scale the button size based on the font size.
1005     float fontScale = style->fontSize() / defaultControlFontPixelSize;
1006     int cancelButtonSize = lroundf(qMin(qMax(minCancelButtonSize, defaultCancelButtonSize * fontScale), maxCancelButtonSize));
1007     style->setWidth(Length(cancelButtonSize, Fixed));
1008     style->setHeight(Length(cancelButtonSize, Fixed));
1009 }
1010 
1011 // Function taken from RenderThemeChromium.cpp
convertToPaintingRect(RenderObject * inputRenderer,const RenderObject * partRenderer,IntRect partRect,const IntRect & localOffset) const1012 IntRect RenderThemeQt::convertToPaintingRect(RenderObject* inputRenderer, const RenderObject* partRenderer, IntRect partRect, const IntRect& localOffset) const
1013 {
1014     // Compute an offset between the part renderer and the input renderer.
1015     IntSize offsetFromInputRenderer = -(partRenderer->offsetFromAncestorContainer(inputRenderer));
1016     // Move the rect into partRenderer's coords.
1017     partRect.move(offsetFromInputRenderer);
1018     // Account for the local drawing offset.
1019     partRect.move(localOffset.x(), localOffset.y());
1020 
1021     return partRect;
1022 }
1023 
paintSearchFieldCancelButton(RenderObject * o,const PaintInfo & pi,const IntRect & r)1024 bool RenderThemeQt::paintSearchFieldCancelButton(RenderObject* o, const PaintInfo& pi,
1025                                                  const IntRect& r)
1026 {
1027     // Logic copied from RenderThemeChromium.cpp.
1028 
1029     // Get the renderer of <input> element.
1030     Node* input = o->node()->shadowAncestorNode();
1031     if (!input->renderer()->isBox())
1032         return false;
1033     RenderBox* inputRenderBox = toRenderBox(input->renderer());
1034     IntRect inputContentBox = inputRenderBox->contentBoxRect();
1035 
1036     // Make sure the scaled button stays square and will fit in its parent's box.
1037     int cancelButtonSize = qMin(inputContentBox.width(), qMin(inputContentBox.height(), r.height()));
1038     // Calculate cancel button's coordinates relative to the input element.
1039     // Center the button vertically.  Round up though, so if it has to be one pixel off-center, it will
1040     // be one pixel closer to the bottom of the field.  This tends to look better with the text.
1041     IntRect cancelButtonRect(o->offsetFromAncestorContainer(inputRenderBox).width(),
1042                              inputContentBox.y() + (inputContentBox.height() - cancelButtonSize + 1) / 2,
1043                              cancelButtonSize, cancelButtonSize);
1044     IntRect paintingRect = convertToPaintingRect(inputRenderBox, o, cancelButtonRect, r);
1045     static Image* cancelImage = Image::loadPlatformResource("searchCancelButton").releaseRef();
1046     static Image* cancelPressedImage = Image::loadPlatformResource("searchCancelButtonPressed").releaseRef();
1047     pi.context->drawImage(isPressed(o) ? cancelPressedImage : cancelImage,
1048                                  o->style()->colorSpace(), paintingRect);
1049     return false;
1050 }
1051 
adjustSearchFieldDecorationStyle(CSSStyleSelector * selector,RenderStyle * style,Element * e) const1052 void RenderThemeQt::adjustSearchFieldDecorationStyle(CSSStyleSelector* selector, RenderStyle* style,
1053                                                      Element* e) const
1054 {
1055     notImplemented();
1056     RenderTheme::adjustSearchFieldDecorationStyle(selector, style, e);
1057 }
1058 
paintSearchFieldDecoration(RenderObject * o,const PaintInfo & pi,const IntRect & r)1059 bool RenderThemeQt::paintSearchFieldDecoration(RenderObject* o, const PaintInfo& pi,
1060                                                const IntRect& r)
1061 {
1062     notImplemented();
1063     return RenderTheme::paintSearchFieldDecoration(o, pi, r);
1064 }
1065 
adjustSearchFieldResultsDecorationStyle(CSSStyleSelector * selector,RenderStyle * style,Element * e) const1066 void RenderThemeQt::adjustSearchFieldResultsDecorationStyle(CSSStyleSelector* selector, RenderStyle* style,
1067                                                             Element* e) const
1068 {
1069     notImplemented();
1070     RenderTheme::adjustSearchFieldResultsDecorationStyle(selector, style, e);
1071 }
1072 
paintSearchFieldResultsDecoration(RenderObject * o,const PaintInfo & pi,const IntRect & r)1073 bool RenderThemeQt::paintSearchFieldResultsDecoration(RenderObject* o, const PaintInfo& pi,
1074                                                       const IntRect& r)
1075 {
1076     notImplemented();
1077     return RenderTheme::paintSearchFieldResultsDecoration(o, pi, r);
1078 }
1079 
supportsFocus(ControlPart appearance) const1080 bool RenderThemeQt::supportsFocus(ControlPart appearance) const
1081 {
1082     switch (appearance) {
1083     case PushButtonPart:
1084     case ButtonPart:
1085     case TextFieldPart:
1086     case TextAreaPart:
1087     case ListboxPart:
1088     case MenulistPart:
1089     case RadioPart:
1090     case CheckboxPart:
1091     case SliderHorizontalPart:
1092     case SliderVerticalPart:
1093         return true;
1094     default: // No for all others...
1095         return false;
1096     }
1097 }
1098 
setPaletteFromPageClientIfExists(QPalette & palette) const1099 void RenderThemeQt::setPaletteFromPageClientIfExists(QPalette& palette) const
1100 {
1101 #if USE(QT_MOBILE_THEME)
1102     static QPalette lightGrayPalette(Qt::lightGray);
1103     palette = lightGrayPalette;
1104     return;
1105 #endif
1106     // If the webview has a custom palette, use it
1107     if (!m_page)
1108         return;
1109     Chrome* chrome = m_page->chrome();
1110     if (!chrome)
1111         return;
1112     ChromeClient* chromeClient = chrome->client();
1113     if (!chromeClient)
1114         return;
1115     QWebPageClient* pageClient = chromeClient->platformPageClient();
1116     if (!pageClient)
1117         return;
1118     palette = pageClient->palette();
1119 }
1120 
initializeCommonQStyleOptions(QStyleOption & option,RenderObject * o) const1121 ControlPart RenderThemeQt::initializeCommonQStyleOptions(QStyleOption& option, RenderObject* o) const
1122 {
1123     // Default bits: no focus, no mouse over
1124     option.state &= ~(QStyle::State_HasFocus | QStyle::State_MouseOver);
1125 
1126     if (isReadOnlyControl(o))
1127         // Readonly is supported on textfields.
1128         option.state |= QStyle::State_ReadOnly;
1129 
1130     option.direction = Qt::LeftToRight;
1131 
1132     if (isHovered(o))
1133         option.state |= QStyle::State_MouseOver;
1134 
1135     setPaletteFromPageClientIfExists(option.palette);
1136 
1137     if (!isEnabled(o)) {
1138         option.palette.setCurrentColorGroup(QPalette::Disabled);
1139         option.state &= ~QStyle::State_Enabled;
1140     }
1141 
1142     RenderStyle* style = o->style();
1143     if (!style)
1144         return NoControlPart;
1145 
1146     ControlPart result = style->appearance();
1147     if (supportsFocus(result) && isFocused(o)) {
1148         option.state |= QStyle::State_HasFocus;
1149         option.state |= QStyle::State_KeyboardFocusChange;
1150     }
1151 
1152     if (style->direction() == WebCore::RTL)
1153         option.direction = Qt::RightToLeft;
1154 
1155     switch (result) {
1156     case PushButtonPart:
1157     case SquareButtonPart:
1158     case ButtonPart:
1159     case ButtonBevelPart:
1160     case ListItemPart:
1161     case MenulistButtonPart:
1162     case SearchFieldResultsButtonPart:
1163     case SearchFieldCancelButtonPart: {
1164         if (isPressed(o))
1165             option.state |= QStyle::State_Sunken;
1166         else if (result == PushButtonPart || result == ButtonPart)
1167             option.state |= QStyle::State_Raised;
1168         break;
1169     }
1170     case RadioPart:
1171     case CheckboxPart:
1172         option.state |= (isChecked(o) ? QStyle::State_On : QStyle::State_Off);
1173     }
1174 
1175     return result;
1176 }
1177 
1178 #if ENABLE(VIDEO)
1179 
extraMediaControlsStyleSheet()1180 String RenderThemeQt::extraMediaControlsStyleSheet()
1181 {
1182     String result = String(mediaControlsQtUserAgentStyleSheet, sizeof(mediaControlsQtUserAgentStyleSheet));
1183 
1184     if (m_page && m_page->chrome()->requiresFullscreenForVideoPlayback())
1185         result += String(mediaControlsQtFullscreenUserAgentStyleSheet, sizeof(mediaControlsQtFullscreenUserAgentStyleSheet));
1186 
1187     return result;
1188 }
1189 
1190 // Helper class to transform the painter's world matrix to the object's content area, scaled to 0,0,100,100
1191 class WorldMatrixTransformer {
1192 public:
WorldMatrixTransformer(QPainter * painter,RenderObject * renderObject,const IntRect & r)1193     WorldMatrixTransformer(QPainter* painter, RenderObject* renderObject, const IntRect& r) : m_painter(painter)
1194     {
1195         RenderStyle* style = renderObject->style();
1196         m_originalTransform = m_painter->transform();
1197         m_painter->translate(r.x() + style->paddingLeft().value(), r.y() + style->paddingTop().value());
1198         m_painter->scale((r.width() - style->paddingLeft().value() - style->paddingRight().value()) / 100.0,
1199              (r.height() - style->paddingTop().value() - style->paddingBottom().value()) / 100.0);
1200     }
1201 
~WorldMatrixTransformer()1202     ~WorldMatrixTransformer() { m_painter->setTransform(m_originalTransform); }
1203 
1204 private:
1205     QPainter* m_painter;
1206     QTransform m_originalTransform;
1207 };
1208 
mediaControlsBaselineOpacity() const1209 double RenderThemeQt::mediaControlsBaselineOpacity() const
1210 {
1211     return 0.4;
1212 }
1213 
paintMediaBackground(QPainter * painter,const IntRect & r) const1214 void RenderThemeQt::paintMediaBackground(QPainter* painter, const IntRect& r) const
1215 {
1216     painter->setPen(Qt::NoPen);
1217     static QColor transparentBlack(0, 0, 0, mediaControlsBaselineOpacity() * 255);
1218     painter->setBrush(transparentBlack);
1219     painter->drawRoundedRect(r.x(), r.y(), r.width(), r.height(), 5.0, 5.0);
1220 }
1221 
mediaElementCanPlay(RenderObject * o)1222 static bool mediaElementCanPlay(RenderObject* o)
1223 {
1224     HTMLMediaElement* mediaElement = toParentMediaElement(o);
1225     if (!mediaElement)
1226         return false;
1227 
1228     return mediaElement->readyState() > HTMLMediaElement::HAVE_METADATA
1229            || (mediaElement->readyState() == HTMLMediaElement::HAVE_NOTHING
1230                && o->style()->appearance() == MediaPlayButtonPart && mediaElement->preload() == "none");
1231 }
1232 
getMediaControlForegroundColor(RenderObject * o) const1233 QColor RenderThemeQt::getMediaControlForegroundColor(RenderObject* o) const
1234 {
1235     QColor fgColor = platformActiveSelectionBackgroundColor();
1236     if (!o)
1237         return fgColor;
1238 
1239     if (o->node()->active())
1240         fgColor = fgColor.lighter();
1241 
1242     if (!mediaElementCanPlay(o)) {
1243         QPalette pal = QApplication::palette();
1244         setPaletteFromPageClientIfExists(pal);
1245         fgColor = pal.brush(QPalette::Disabled, QPalette::Text).color();
1246     }
1247 
1248     return fgColor;
1249 }
1250 
paintMediaFullscreenButton(RenderObject * o,const PaintInfo & paintInfo,const IntRect & r)1251 bool RenderThemeQt::paintMediaFullscreenButton(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r)
1252 {
1253     HTMLMediaElement* mediaElement = toParentMediaElement(o);
1254     if (!mediaElement)
1255         return false;
1256 
1257     StylePainter p(this, paintInfo);
1258     if (!p.isValid())
1259         return true;
1260 
1261     p.painter->setRenderHint(QPainter::Antialiasing, true);
1262 
1263     paintMediaBackground(p.painter, r);
1264 
1265     WorldMatrixTransformer transformer(p.painter, o, r);
1266     const QPointF arrowPolygon[9] = { QPointF(20, 0), QPointF(100, 0), QPointF(100, 80),
1267             QPointF(80, 80), QPointF(80, 30), QPointF(10, 100), QPointF(0, 90), QPointF(70, 20), QPointF(20, 20)};
1268 
1269     p.painter->setBrush(getMediaControlForegroundColor(o));
1270     p.painter->drawPolygon(arrowPolygon, 9);
1271 
1272     return false;
1273 }
1274 
paintMediaMuteButton(RenderObject * o,const PaintInfo & paintInfo,const IntRect & r)1275 bool RenderThemeQt::paintMediaMuteButton(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r)
1276 {
1277     HTMLMediaElement* mediaElement = toParentMediaElement(o);
1278     if (!mediaElement)
1279         return false;
1280 
1281     StylePainter p(this, paintInfo);
1282     if (!p.isValid())
1283         return true;
1284 
1285     p.painter->setRenderHint(QPainter::Antialiasing, true);
1286 
1287     paintMediaBackground(p.painter, r);
1288 
1289     WorldMatrixTransformer transformer(p.painter, o, r);
1290     const QPointF speakerPolygon[6] = { QPointF(20, 30), QPointF(50, 30), QPointF(80, 0),
1291             QPointF(80, 100), QPointF(50, 70), QPointF(20, 70)};
1292 
1293     p.painter->setBrush(mediaElement->muted() ? Qt::darkRed : getMediaControlForegroundColor(o));
1294     p.painter->drawPolygon(speakerPolygon, 6);
1295 
1296     return false;
1297 }
1298 
paintMediaPlayButton(RenderObject * o,const PaintInfo & paintInfo,const IntRect & r)1299 bool RenderThemeQt::paintMediaPlayButton(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r)
1300 {
1301     HTMLMediaElement* mediaElement = toParentMediaElement(o);
1302     if (!mediaElement)
1303         return false;
1304 
1305     StylePainter p(this, paintInfo);
1306     if (!p.isValid())
1307         return true;
1308 
1309     p.painter->setRenderHint(QPainter::Antialiasing, true);
1310 
1311     paintMediaBackground(p.painter, r);
1312 
1313     WorldMatrixTransformer transformer(p.painter, o, r);
1314     p.painter->setBrush(getMediaControlForegroundColor(o));
1315     if (mediaElement->canPlay()) {
1316         const QPointF playPolygon[3] = { QPointF(0, 0), QPointF(100, 50), QPointF(0, 100)};
1317         p.painter->drawPolygon(playPolygon, 3);
1318     } else {
1319         p.painter->drawRect(0, 0, 30, 100);
1320         p.painter->drawRect(70, 0, 30, 100);
1321     }
1322 
1323     return false;
1324 }
1325 
paintMediaSeekBackButton(RenderObject *,const PaintInfo &,const IntRect &)1326 bool RenderThemeQt::paintMediaSeekBackButton(RenderObject*, const PaintInfo&, const IntRect&)
1327 {
1328     // We don't want to paint this at the moment.
1329     return false;
1330 }
1331 
paintMediaSeekForwardButton(RenderObject *,const PaintInfo &,const IntRect &)1332 bool RenderThemeQt::paintMediaSeekForwardButton(RenderObject*, const PaintInfo&, const IntRect&)
1333 {
1334     // We don't want to paint this at the moment.
1335     return false;
1336 }
1337 
paintMediaCurrentTime(RenderObject * o,const PaintInfo & paintInfo,const IntRect & r)1338 bool RenderThemeQt::paintMediaCurrentTime(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r)
1339 {
1340     StylePainter p(this, paintInfo);
1341     if (!p.isValid())
1342         return true;
1343 
1344     p.painter->setRenderHint(QPainter::Antialiasing, true);
1345     paintMediaBackground(p.painter, r);
1346 
1347     return false;
1348 }
1349 
formatMediaControlsCurrentTime(float currentTime,float duration) const1350 String RenderThemeQt::formatMediaControlsCurrentTime(float currentTime, float duration) const
1351 {
1352     return formatMediaControlsTime(currentTime) + " / " + formatMediaControlsTime(duration);
1353 }
1354 
formatMediaControlsRemainingTime(float currentTime,float duration) const1355 String RenderThemeQt::formatMediaControlsRemainingTime(float currentTime, float duration) const
1356 {
1357     return String();
1358 }
1359 
paintMediaVolumeSliderTrack(RenderObject * o,const PaintInfo & paintInfo,const IntRect & r)1360 bool RenderThemeQt::paintMediaVolumeSliderTrack(RenderObject *o, const PaintInfo &paintInfo, const IntRect &r)
1361 {
1362     StylePainter p(this, paintInfo);
1363     if (!p.isValid())
1364         return true;
1365 
1366     p.painter->setRenderHint(QPainter::Antialiasing, true);
1367 
1368     paintMediaBackground(p.painter, r);
1369 
1370     if (!o->isSlider())
1371         return false;
1372 
1373     IntRect b = toRenderBox(o)->contentBoxRect();
1374 
1375     // Position the outer rectangle
1376     int top = r.y() + b.y();
1377     int left = r.x() + b.x();
1378     int width = b.width();
1379     int height = b.height();
1380 
1381     // Get the scale color from the page client
1382     QPalette pal = QApplication::palette();
1383     setPaletteFromPageClientIfExists(pal);
1384     const QColor highlightText = pal.brush(QPalette::Active, QPalette::HighlightedText).color();
1385     const QColor scaleColor(highlightText.red(), highlightText.green(), highlightText.blue(), mediaControlsBaselineOpacity() * 255);
1386 
1387     // Draw the outer rectangle
1388     p.painter->setBrush(scaleColor);
1389     p.painter->drawRect(left, top, width, height);
1390 
1391     if (!o->node() || !o->node()->hasTagName(inputTag))
1392         return false;
1393 
1394     HTMLInputElement* slider = static_cast<HTMLInputElement*>(o->node());
1395 
1396     // Position the inner rectangle
1397     height = height * slider->valueAsNumber();
1398     top += b.height() - height;
1399 
1400     // Draw the inner rectangle
1401     p.painter->setPen(Qt::NoPen);
1402     p.painter->setBrush(getMediaControlForegroundColor(o));
1403     p.painter->drawRect(left, top, width, height);
1404 
1405     return false;
1406 }
1407 
paintMediaVolumeSliderThumb(RenderObject * o,const PaintInfo & paintInfo,const IntRect & r)1408 bool RenderThemeQt::paintMediaVolumeSliderThumb(RenderObject *o, const PaintInfo &paintInfo, const IntRect &r)
1409 {
1410     StylePainter p(this, paintInfo);
1411     if (!p.isValid())
1412         return true;
1413 
1414     // Nothing to draw here, this is all done in the track
1415     return false;
1416 }
1417 
paintMediaSliderTrack(RenderObject * o,const PaintInfo & paintInfo,const IntRect & r)1418 bool RenderThemeQt::paintMediaSliderTrack(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r)
1419 {
1420     HTMLMediaElement* mediaElement = toParentMediaElement(o);
1421     if (!mediaElement)
1422         return false;
1423 
1424     StylePainter p(this, paintInfo);
1425     if (!p.isValid())
1426         return true;
1427 
1428     p.painter->setRenderHint(QPainter::Antialiasing, true);
1429 
1430     paintMediaBackground(p.painter, r);
1431 
1432     if (MediaPlayer* player = mediaElement->player()) {
1433         // Get the buffered parts of the media
1434         PassRefPtr<TimeRanges> buffered = player->buffered();
1435         if (buffered->length() > 0 && player->duration() < std::numeric_limits<float>::infinity()) {
1436             // Set the transform and brush
1437             WorldMatrixTransformer transformer(p.painter, o, r);
1438             p.painter->setBrush(getMediaControlForegroundColor());
1439 
1440             // Paint each buffered section
1441             ExceptionCode ex;
1442             for (int i = 0; i < buffered->length(); i++) {
1443                 float startX = (buffered->start(i, ex) / player->duration()) * 100;
1444                 float width = ((buffered->end(i, ex) / player->duration()) * 100) - startX;
1445                 p.painter->drawRect(startX, 37, width, 26);
1446             }
1447         }
1448     }
1449 
1450     return false;
1451 }
1452 
paintMediaSliderThumb(RenderObject * o,const PaintInfo & paintInfo,const IntRect & r)1453 bool RenderThemeQt::paintMediaSliderThumb(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r)
1454 {
1455     if (!o->parent()->isSlider())
1456         return false;
1457 
1458     // We can get the HTMLMediaElement from the parent of the thumb : MediaControlTimelineElement.
1459     HTMLMediaElement* mediaElement = toParentMediaElement(o->parent());
1460     if (!mediaElement)
1461         return false;
1462 
1463     StylePainter p(this, paintInfo);
1464     if (!p.isValid())
1465         return true;
1466 
1467     p.painter->setRenderHint(QPainter::Antialiasing, true);
1468 
1469     p.painter->setPen(Qt::NoPen);
1470     p.painter->setBrush(getMediaControlForegroundColor(o->parent()));
1471     p.painter->drawRect(r.x(), r.y(), r.width(), r.height());
1472 
1473     return false;
1474 }
1475 #endif
1476 
adjustSliderThumbSize(RenderObject * o) const1477 void RenderThemeQt::adjustSliderThumbSize(RenderObject* o) const
1478 {
1479     ControlPart part = o->style()->appearance();
1480 
1481     if (part == MediaSliderThumbPart) {
1482         RenderStyle* parentStyle = o->parent()->style();
1483         Q_ASSERT(parentStyle);
1484 
1485         int parentHeight = parentStyle->height().value();
1486         o->style()->setWidth(Length(parentHeight / 3, Fixed));
1487         o->style()->setHeight(Length(parentHeight, Fixed));
1488     } else if (part == MediaVolumeSliderThumbPart) {
1489         RenderStyle* parentStyle = o->parent()->style();
1490         Q_ASSERT(parentStyle);
1491 
1492         int parentWidth = parentStyle->width().value();
1493         o->style()->setHeight(Length(parentWidth / 3, Fixed));
1494         o->style()->setWidth(Length(parentWidth, Fixed));
1495     } else if (part == SliderThumbHorizontalPart || part == SliderThumbVerticalPart) {
1496         QStyleOptionSlider option;
1497         if (part == SliderThumbVerticalPart)
1498             option.orientation = Qt::Vertical;
1499 
1500         QStyle* style = qStyle();
1501 
1502         int width = style->pixelMetric(QStyle::PM_SliderLength, &option);
1503         int height = style->pixelMetric(QStyle::PM_SliderThickness, &option);
1504         o->style()->setWidth(Length(width, Fixed));
1505         o->style()->setHeight(Length(height, Fixed));
1506     }
1507 }
1508 
caretBlinkInterval() const1509 double RenderThemeQt::caretBlinkInterval() const
1510 {
1511     return  QApplication::cursorFlashTime() / 1000.0 / 2.0;
1512 }
1513 
1514 }
1515 
1516 // vim: ts=4 sw=4 et
1517