[GTK] Hardcoded text color in input fields
[WebKit-https.git] / Source / WebCore / rendering / RenderThemeGtk.cpp
1 /*
2  * Copyright (C) 2007 Apple Inc.
3  * Copyright (C) 2007 Alp Toker <alp@atoker.com>
4  * Copyright (C) 2008 Collabora Ltd.
5  * Copyright (C) 2009 Kenneth Rohde Christiansen
6  * Copyright (C) 2010 Igalia S.L.
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Library General Public
10  * License as published by the Free Software Foundation; either
11  * version 2 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Library General Public License for more details.
17  *
18  * You should have received a copy of the GNU Library General Public License
19  * along with this library; see the file COPYING.LIB.  If not, write to
20  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21  * Boston, MA 02110-1301, USA.
22  *
23  */
24
25 #include "config.h"
26 #include "RenderThemeGtk.h"
27
28 #include "CSSValueKeywords.h"
29 #include "FileList.h"
30 #include "FloatRoundedRect.h"
31 #include "FontDescription.h"
32 #include "GRefPtrGtk.h"
33 #include "GUniquePtrGtk.h"
34 #include "Gradient.h"
35 #include "GraphicsContext.h"
36 #include "GtkVersioning.h"
37 #include "HTMLInputElement.h"
38 #include "HTMLMediaElement.h"
39 #include "LocalizedStrings.h"
40 #include "MediaControlElements.h"
41 #include "Page.h"
42 #include "PaintInfo.h"
43 #include "PlatformContextCairo.h"
44 #include "RenderBox.h"
45 #include "RenderObject.h"
46 #include "RenderProgress.h"
47 #include "RenderThemeWidget.h"
48 #include "ScrollbarThemeGtk.h"
49 #include "StringTruncator.h"
50 #include "TimeRanges.h"
51 #include "UserAgentScripts.h"
52 #include "UserAgentStyleSheets.h"
53 #include <cmath>
54 #include <gdk/gdk.h>
55 #include <glib.h>
56 #include <gtk/gtk.h>
57 #include <wtf/FileSystem.h>
58 #include <wtf/glib/GRefPtr.h>
59 #include <wtf/glib/GUniquePtr.h>
60 #include <wtf/text/CString.h>
61 #include <wtf/text/StringBuilder.h>
62
63 namespace WebCore {
64
65 RenderTheme& RenderTheme::singleton()
66 {
67     static NeverDestroyed<RenderThemeGtk> theme;
68     return theme;
69 }
70
71 static double getScreenDPI()
72 {
73     // FIXME: Really this should be the widget's screen.
74     GdkScreen* screen = gdk_screen_get_default();
75     if (!screen)
76         return 96; // Default to 96 DPI.
77
78     float dpi = gdk_screen_get_resolution(screen);
79     if (dpi <= 0)
80         return 96;
81     return dpi;
82 }
83
84 void RenderThemeGtk::updateCachedSystemFontDescription(CSSValueID, FontCascadeDescription& fontDescription) const
85 {
86     GtkSettings* settings = gtk_settings_get_default();
87     if (!settings)
88         return;
89
90     // This will be a font selection string like "Sans 10" so we cannot use it as the family name.
91     GUniqueOutPtr<gchar> fontName;
92     g_object_get(settings, "gtk-font-name", &fontName.outPtr(), nullptr);
93     if (!fontName || !fontName.get()[0])
94         return;
95
96     PangoFontDescription* pangoDescription = pango_font_description_from_string(fontName.get());
97     if (!pangoDescription)
98         return;
99
100     fontDescription.setOneFamily(pango_font_description_get_family(pangoDescription));
101
102     int size = pango_font_description_get_size(pangoDescription) / PANGO_SCALE;
103     // If the size of the font is in points, we need to convert it to pixels.
104     if (!pango_font_description_get_size_is_absolute(pangoDescription))
105         size = size * (getScreenDPI() / 72.0);
106
107     fontDescription.setSpecifiedSize(size);
108     fontDescription.setIsAbsoluteSize(true);
109     fontDescription.setWeight(normalWeightValue());
110     fontDescription.setItalic(FontSelectionValue());
111     pango_font_description_free(pangoDescription);
112 }
113
114 #if ENABLE(DATALIST_ELEMENT)
115 IntSize RenderThemeGtk::sliderTickSize() const
116 {
117     // FIXME: We need to set this to the size of one tick mark.
118     return IntSize(0, 0);
119 }
120
121 int RenderThemeGtk::sliderTickOffsetFromTrackCenter() const
122 {
123     // FIXME: We need to set this to the position of the tick marks.
124     return 0;
125 }
126 #endif
127
128 #ifndef GTK_API_VERSION_2
129
130 static void themeChangedCallback()
131 {
132     Page::updateStyleForAllPagesAfterGlobalChangeInEnvironment();
133 }
134
135 RenderThemeGtk::RenderThemeGtk()
136 {
137     static bool themeMonitorInitialized = false;
138     if (!themeMonitorInitialized) {
139         GtkSettings* settings = gtk_settings_get_default();
140         g_signal_connect(settings, "notify::gtk-theme-name", G_CALLBACK(themeChangedCallback), nullptr);
141         g_signal_connect(settings, "notify::gtk-color-scheme", G_CALLBACK(themeChangedCallback), nullptr);
142         themeMonitorInitialized = true;
143     }
144 }
145
146 enum RenderThemePart {
147     Entry,
148     EntrySelection,
149     EntryIconLeft,
150     EntryIconRight,
151     Button,
152     CheckButton,
153     RadioButton,
154     ComboBox,
155     ComboBoxButton,
156     ComboBoxArrow,
157     Scale,
158     ScaleTrough,
159     ScaleSlider,
160     ProgressBar,
161     ProgressBarTrough,
162     ProgressBarProgress,
163     ListBox,
164     SpinButton,
165     SpinButtonUpButton,
166     SpinButtonDownButton,
167 #if ENABLE(VIDEO)
168     MediaButton,
169 #endif
170 #if GTK_CHECK_VERSION(3, 20, 0)
171     Window,
172 #endif
173 };
174
175 #if !GTK_CHECK_VERSION(3, 20, 0)
176 // This is the default value defined by GTK+, where it was defined as MIN_ARROW_SIZE in gtkarrow.c.
177 static const int minArrowSize = 15;
178 // This is the default value defined by GTK+, where it was defined as MIN_ARROW_WIDTH in gtkspinbutton.c.
179 static const int minSpinButtonArrowSize = 6;
180
181 static GRefPtr<GtkStyleContext> createStyleContext(RenderThemePart themePart, GtkStyleContext* parent = nullptr)
182 {
183     GRefPtr<GtkWidgetPath> path = adoptGRef(parent ? gtk_widget_path_copy(gtk_style_context_get_path(parent)) : gtk_widget_path_new());
184
185     switch (themePart) {
186     case Entry:
187     case EntrySelection:
188         gtk_widget_path_append_type(path.get(), GTK_TYPE_ENTRY);
189         gtk_widget_path_iter_add_class(path.get(), -1, GTK_STYLE_CLASS_ENTRY);
190         break;
191     case EntryIconLeft:
192     case EntryIconRight:
193         gtk_widget_path_append_type(path.get(), GTK_TYPE_ENTRY);
194         break;
195     case Button:
196         gtk_widget_path_append_type(path.get(), GTK_TYPE_BUTTON);
197         gtk_widget_path_iter_add_class(path.get(), -1, GTK_STYLE_CLASS_BUTTON);
198         gtk_widget_path_iter_add_class(path.get(), -1, "text-button");
199         break;
200     case CheckButton:
201         gtk_widget_path_append_type(path.get(), GTK_TYPE_CHECK_BUTTON);
202         gtk_widget_path_iter_add_class(path.get(), -1, GTK_STYLE_CLASS_CHECK);
203         break;
204     case RadioButton:
205         gtk_widget_path_append_type(path.get(), GTK_TYPE_RADIO_BUTTON);
206         gtk_widget_path_iter_add_class(path.get(), -1, GTK_STYLE_CLASS_RADIO);
207         break;
208     case ComboBox:
209         gtk_widget_path_append_type(path.get(), GTK_TYPE_COMBO_BOX);
210         break;
211     case ComboBoxButton:
212         gtk_widget_path_append_type(path.get(), GTK_TYPE_BUTTON);
213         gtk_widget_path_iter_add_class(path.get(), -1, GTK_STYLE_CLASS_BUTTON);
214         gtk_widget_path_iter_add_class(path.get(), -1, "text-button");
215         gtk_widget_path_iter_add_class(path.get(), -1, "combo");
216         break;
217     case ComboBoxArrow:
218         gtk_widget_path_append_type(path.get(), GTK_TYPE_ARROW);
219         gtk_widget_path_iter_add_class(path.get(), -1, "arrow");
220         break;
221     case Scale:
222         gtk_widget_path_append_type(path.get(), GTK_TYPE_SCALE);
223         gtk_widget_path_iter_add_class(path.get(), -1, GTK_STYLE_CLASS_SCALE);
224         break;
225     case ScaleTrough:
226         gtk_widget_path_append_type(path.get(), GTK_TYPE_SCALE);
227         gtk_widget_path_iter_add_class(path.get(), -1, GTK_STYLE_CLASS_SCALE);
228         gtk_widget_path_iter_add_class(path.get(), -1, GTK_STYLE_CLASS_TROUGH);
229         break;
230     case ScaleSlider:
231         gtk_widget_path_append_type(path.get(), GTK_TYPE_SCALE);
232         gtk_widget_path_iter_add_class(path.get(), -1, GTK_STYLE_CLASS_SCALE);
233         gtk_widget_path_iter_add_class(path.get(), -1, GTK_STYLE_CLASS_SLIDER);
234         break;
235     case ProgressBar:
236         gtk_widget_path_append_type(path.get(), GTK_TYPE_PROGRESS_BAR);
237         gtk_widget_path_iter_add_class(path.get(), -1, GTK_STYLE_CLASS_PROGRESSBAR);
238         gtk_widget_path_iter_add_class(path.get(), -1, GTK_STYLE_CLASS_HORIZONTAL);
239         break;
240     case ProgressBarTrough:
241         gtk_widget_path_append_type(path.get(), GTK_TYPE_PROGRESS_BAR);
242         gtk_widget_path_iter_add_class(path.get(), -1, GTK_STYLE_CLASS_TROUGH);
243         break;
244     case ProgressBarProgress:
245         gtk_widget_path_append_type(path.get(), GTK_TYPE_PROGRESS_BAR);
246         gtk_widget_path_iter_add_class(path.get(), -1, GTK_STYLE_CLASS_PROGRESSBAR);
247         break;
248     case ListBox:
249         gtk_widget_path_append_type(path.get(), GTK_TYPE_TREE_VIEW);
250         gtk_widget_path_iter_add_class(path.get(), -1, GTK_STYLE_CLASS_VIEW);
251         break;
252     case SpinButton:
253         gtk_widget_path_append_type(path.get(), GTK_TYPE_SPIN_BUTTON);
254         gtk_widget_path_iter_add_class(path.get(), -1, GTK_STYLE_CLASS_SPINBUTTON);
255         gtk_widget_path_iter_add_class(path.get(), -1, GTK_STYLE_CLASS_HORIZONTAL);
256         break;
257     case SpinButtonUpButton:
258     case SpinButtonDownButton:
259         gtk_widget_path_append_type(path.get(), GTK_TYPE_SPIN_BUTTON);
260         gtk_widget_path_iter_add_class(path.get(), -1, GTK_STYLE_CLASS_SPINBUTTON);
261         gtk_widget_path_iter_add_class(path.get(), -1, GTK_STYLE_CLASS_BUTTON);
262         break;
263 #if ENABLE(VIDEO)
264     case MediaButton:
265         gtk_widget_path_append_type(path.get(), GTK_TYPE_IMAGE);
266         gtk_widget_path_iter_add_class(path.get(), -1, GTK_STYLE_CLASS_IMAGE);
267         break;
268 #endif // ENABLE(VIDEO)
269     default:
270         ASSERT_NOT_REACHED();
271         break;
272     }
273
274     GRefPtr<GtkStyleContext> context = adoptGRef(gtk_style_context_new());
275     gtk_style_context_set_path(context.get(), path.get());
276     gtk_style_context_set_parent(context.get(), parent);
277     return context;
278 }
279
280 static GRefPtr<GdkPixbuf> loadThemedIcon(GtkStyleContext* context, const char* iconName, GtkIconSize iconSize)
281 {
282     GRefPtr<GIcon> icon = adoptGRef(g_themed_icon_new(iconName));
283     unsigned lookupFlags = GTK_ICON_LOOKUP_USE_BUILTIN | GTK_ICON_LOOKUP_FORCE_SIZE | GTK_ICON_LOOKUP_FORCE_SVG;
284 #if GTK_CHECK_VERSION(3, 14, 0)
285     GtkTextDirection direction = gtk_style_context_get_direction(context);
286     if (direction & GTK_TEXT_DIR_LTR)
287         lookupFlags |= GTK_ICON_LOOKUP_DIR_LTR;
288     else if (direction & GTK_TEXT_DIR_RTL)
289         lookupFlags |= GTK_ICON_LOOKUP_DIR_RTL;
290 #endif
291     int width, height;
292     gtk_icon_size_lookup(iconSize, &width, &height);
293     GRefPtr<GtkIconInfo> iconInfo = adoptGRef(gtk_icon_theme_lookup_by_gicon(gtk_icon_theme_get_default(), icon.get(), std::min(width, height), static_cast<GtkIconLookupFlags>(lookupFlags)));
294     if (!iconInfo)
295         return nullptr;
296
297     return adoptGRef(gtk_icon_info_load_symbolic_for_context(iconInfo.get(), context, nullptr, nullptr));
298 }
299 #endif // !GTK_CHECK_VERSION(3, 20, 0)
300
301 #if ENABLE(VIDEO)
302 static bool nodeHasPseudo(Node& node, const char* pseudo)
303 {
304     return is<Element>(node) && downcast<Element>(node).pseudo() == pseudo;
305 }
306
307 static bool nodeHasClass(Node* node, const char* className)
308 {
309     if (!is<Element>(*node))
310         return false;
311
312     Element& element = downcast<Element>(*node);
313
314     if (!element.hasClass())
315         return false;
316
317     return element.classNames().contains(className);
318 }
319 #endif // ENABLE(VIDEO)
320
321 RenderThemeGtk::~RenderThemeGtk() = default;
322
323 static bool supportsFocus(ControlPart appearance)
324 {
325     switch (appearance) {
326     case PushButtonPart:
327     case ButtonPart:
328     case TextFieldPart:
329     case TextAreaPart:
330     case SearchFieldPart:
331     case MenulistPart:
332     case RadioPart:
333     case CheckboxPart:
334     case SliderHorizontalPart:
335     case SliderVerticalPart:
336         return true;
337     default:
338         return false;
339     }
340 }
341
342 bool RenderThemeGtk::supportsFocusRing(const RenderStyle& style) const
343 {
344     return supportsFocus(style.appearance());
345 }
346
347 bool RenderThemeGtk::controlSupportsTints(const RenderObject& o) const
348 {
349     return isEnabled(o);
350 }
351
352 int RenderThemeGtk::baselinePosition(const RenderBox& box) const
353 {
354     // FIXME: This strategy is possibly incorrect for the GTK+ port.
355     if (box.style().appearance() == CheckboxPart || box.style().appearance() == RadioPart)
356         return box.marginTop() + box.height() - 2;
357     return RenderTheme::baselinePosition(box);
358 }
359
360 #if GTK_CHECK_VERSION(3, 20, 0)
361 void RenderThemeGtk::adjustRepaintRect(const RenderObject&, FloatRect&)
362 {
363 }
364 static GtkStateFlags themePartStateFlags(const RenderThemeGtk& theme, RenderThemePart themePart, const RenderObject& renderObject)
365 {
366     unsigned stateFlags = 0;
367     switch (renderObject.style().direction()) {
368     case TextDirection::RTL:
369         stateFlags |= GTK_STATE_FLAG_DIR_RTL;
370         break;
371     case TextDirection::LTR:
372         stateFlags |= GTK_STATE_FLAG_DIR_LTR;
373         break;
374     }
375
376     if (!theme.isEnabled(renderObject) || (themePart == Entry && theme.isReadOnlyControl(renderObject)))
377         stateFlags |= GTK_STATE_FLAG_INSENSITIVE;
378     else {
379         if (theme.isHovered(renderObject))
380             stateFlags |= GTK_STATE_FLAG_PRELIGHT;
381         if (theme.isFocused(renderObject))
382             stateFlags |= GTK_STATE_FLAG_FOCUSED;
383     }
384
385     switch (themePart) {
386     case CheckButton:
387     case RadioButton:
388         if (theme.isChecked(renderObject))
389             stateFlags |= GTK_STATE_FLAG_CHECKED;
390         if (theme.isIndeterminate(renderObject))
391             stateFlags |= GTK_STATE_FLAG_INCONSISTENT;
392         if (theme.isPressed(renderObject))
393             stateFlags |= GTK_STATE_FLAG_SELECTED;
394         break;
395     case Button:
396     case ComboBoxButton:
397     case ScaleSlider:
398     case EntryIconLeft:
399     case EntryIconRight:
400 #if ENABLE(VIDEO)
401     case MediaButton:
402 #endif
403         if (theme.isPressed(renderObject))
404             stateFlags |= GTK_STATE_FLAG_ACTIVE;
405         break;
406     case SpinButtonUpButton:
407         if (theme.isPressed(renderObject) && theme.isSpinUpButtonPartPressed(renderObject))
408             stateFlags |= GTK_STATE_FLAG_ACTIVE;
409         if (theme.isHovered(renderObject) && !theme.isSpinUpButtonPartHovered(renderObject))
410             stateFlags &= ~GTK_STATE_FLAG_PRELIGHT;
411         break;
412     case SpinButtonDownButton:
413         if (theme.isPressed(renderObject) && !theme.isSpinUpButtonPartPressed(renderObject))
414             stateFlags |= GTK_STATE_FLAG_ACTIVE;
415         if (theme.isHovered(renderObject) && theme.isSpinUpButtonPartHovered(renderObject))
416             stateFlags &= ~GTK_STATE_FLAG_PRELIGHT;
417         break;
418     default:
419         break;
420     }
421
422     return static_cast<GtkStateFlags>(stateFlags);
423 }
424 #else
425 static GtkTextDirection gtkTextDirection(TextDirection direction)
426 {
427     switch (direction) {
428     case TextDirection::RTL:
429         return GTK_TEXT_DIR_RTL;
430     case TextDirection::LTR:
431         return GTK_TEXT_DIR_LTR;
432     default:
433         return GTK_TEXT_DIR_NONE;
434     }
435 }
436
437 static GtkStateFlags gtkIconStateFlags(RenderTheme* theme, const RenderObject& renderObject)
438 {
439     if (!theme->isEnabled(renderObject))
440         return GTK_STATE_FLAG_INSENSITIVE;
441     if (theme->isPressed(renderObject))
442         return GTK_STATE_FLAG_ACTIVE;
443     if (theme->isHovered(renderObject))
444         return GTK_STATE_FLAG_PRELIGHT;
445
446     return GTK_STATE_FLAG_NORMAL;
447 }
448
449 static void adjustRectForFocus(GtkStyleContext* context, FloatRect& rect)
450 {
451     gint focusWidth, focusPad;
452     gtk_style_context_get_style(context, "focus-line-width", &focusWidth, "focus-padding", &focusPad, nullptr);
453     rect.inflate(focusWidth + focusPad);
454 }
455
456 void RenderThemeGtk::adjustRepaintRect(const RenderObject& renderObject, FloatRect& rect)
457 {
458     GRefPtr<GtkStyleContext> context;
459     bool checkInteriorFocus = false;
460     ControlPart part = renderObject.style().appearance();
461     switch (part) {
462     case CheckboxPart:
463     case RadioPart:
464         context = createStyleContext(part == CheckboxPart ? CheckButton : RadioButton);
465
466         gint indicatorSpacing;
467         gtk_style_context_get_style(context.get(), "indicator-spacing", &indicatorSpacing, nullptr);
468         rect.inflate(indicatorSpacing);
469
470         return;
471     case SliderVerticalPart:
472     case SliderHorizontalPart:
473         context = createStyleContext(ScaleSlider);
474         break;
475     case ButtonPart:
476     case MenulistButtonPart:
477     case MenulistPart:
478         context = createStyleContext(Button);
479         checkInteriorFocus = true;
480         break;
481     case TextFieldPart:
482     case TextAreaPart:
483         context = createStyleContext(Entry);
484         checkInteriorFocus = true;
485         break;
486     default:
487         return;
488     }
489
490     ASSERT(context);
491     if (checkInteriorFocus) {
492         gboolean interiorFocus;
493         gtk_style_context_get_style(context.get(), "interior-focus", &interiorFocus, nullptr);
494         if (interiorFocus)
495             return;
496     }
497     adjustRectForFocus(context.get(), rect);
498 }
499 #endif // GTK_CHECK_VERSION(3, 20, 0)
500
501 void RenderThemeGtk::adjustButtonStyle(StyleResolver&, RenderStyle& style, const Element*) const
502 {
503     // Some layout tests check explicitly that buttons ignore line-height.
504     if (style.appearance() == PushButtonPart)
505         style.setLineHeight(RenderStyle::initialLineHeight());
506 }
507
508 static void shrinkToMinimumSizeAndCenterRectangle(FloatRect& rect, const IntSize& minSize)
509 {
510     if (rect.width() > minSize.width()) {
511         rect.inflateX(-(rect.width() - minSize.width()) / 2);
512         rect.setWidth(minSize.width()); // In case rect.width() was equal to minSize.width() + 1.
513     }
514
515     if (rect.height() > minSize.height()) {
516         rect.inflateY(-(rect.height() - minSize.height()) / 2);
517         rect.setHeight(minSize.height()); // In case rect.height() was equal to minSize.height() + 1.
518     }
519 }
520
521 #if GTK_CHECK_VERSION(3, 20, 0)
522 static void setToggleSize(RenderThemePart themePart, RenderStyle& style)
523 {
524     ASSERT(themePart == CheckButton || themePart == RadioButton);
525
526     // The width and height are both specified, so we shouldn't change them.
527     if (!style.width().isIntrinsicOrAuto() && !style.height().isAuto())
528         return;
529
530     auto& toggleWidget = static_cast<RenderThemeToggleButton&>(RenderThemeWidget::getOrCreate(themePart == CheckButton ? RenderThemeWidget::Type::CheckButton : RenderThemeWidget::Type::RadioButton));
531     toggleWidget.button().setState(GTK_STATE_FLAG_NORMAL);
532     toggleWidget.toggle().setState(GTK_STATE_FLAG_NORMAL);
533     IntSize preferredSize = toggleWidget.button().preferredSize();
534     preferredSize = preferredSize.expandedTo(toggleWidget.toggle().preferredSize());
535
536     if (style.width().isIntrinsicOrAuto())
537         style.setWidth(Length(preferredSize.width(), Fixed));
538
539     if (style.height().isAuto())
540         style.setHeight(Length(preferredSize.height(), Fixed));
541 }
542
543 static void paintToggle(const RenderThemeGtk* theme, RenderThemePart themePart, const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& fullRect)
544 {
545     ASSERT(themePart == CheckButton || themePart == RadioButton);
546
547     auto& toggleWidget = static_cast<RenderThemeToggleButton&>(RenderThemeWidget::getOrCreate(themePart == CheckButton ? RenderThemeWidget::Type::CheckButton : RenderThemeWidget::Type::RadioButton));
548     auto toggleState = themePartStateFlags(*theme, themePart, renderObject);
549     toggleWidget.button().setState(toggleState);
550     toggleWidget.toggle().setState(toggleState);
551
552     FloatRect rect = fullRect;
553     // Some themes do not render large toggle buttons properly, so we simply
554     // shrink the rectangle back down to the default size and then center it
555     // in the full toggle button region. The reason for not simply forcing toggle
556     // buttons to be a smaller size is that we don't want to break site layouts.
557     IntSize preferredSize = toggleWidget.button().preferredSize();
558     preferredSize = preferredSize.expandedTo(toggleWidget.toggle().preferredSize());
559     shrinkToMinimumSizeAndCenterRectangle(rect, preferredSize);
560     toggleWidget.button().render(paintInfo.context().platformContext()->cr(), rect);
561     toggleWidget.toggle().render(paintInfo.context().platformContext()->cr(), rect);
562
563     if (theme->isFocused(renderObject))
564         toggleWidget.button().renderFocus(paintInfo.context().platformContext()->cr(), rect);
565 }
566 #else
567 static void setToggleSize(RenderThemePart themePart, RenderStyle& style)
568 {
569     // The width and height are both specified, so we shouldn't change them.
570     if (!style.width().isIntrinsicOrAuto() && !style.height().isAuto())
571         return;
572
573     GRefPtr<GtkStyleContext> context = createStyleContext(themePart);
574     // Other ports hard-code this to 13. GTK+ users tend to demand the native look.
575     gint indicatorSize;
576     gtk_style_context_get_style(context.get(), "indicator-size", &indicatorSize, nullptr);
577
578     if (style.width().isIntrinsicOrAuto())
579         style.setWidth(Length(indicatorSize, Fixed));
580
581     if (style.height().isAuto())
582         style.setHeight(Length(indicatorSize, Fixed));
583 }
584
585 static void paintToggle(const RenderThemeGtk* theme, RenderThemePart themePart, const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& fullRect)
586 {
587     GRefPtr<GtkStyleContext> context = createStyleContext(themePart);
588     gtk_style_context_set_direction(context.get(), static_cast<GtkTextDirection>(gtkTextDirection(renderObject.style().direction())));
589
590     unsigned flags = 0;
591     if (!theme->isEnabled(renderObject))
592         flags |= GTK_STATE_FLAG_INSENSITIVE;
593     else if (theme->isHovered(renderObject))
594         flags |= GTK_STATE_FLAG_PRELIGHT;
595     if (theme->isIndeterminate(renderObject))
596         flags |= GTK_STATE_FLAG_INCONSISTENT;
597     else if (theme->isChecked(renderObject))
598 #if GTK_CHECK_VERSION(3, 13, 7)
599         flags |= GTK_STATE_FLAG_CHECKED;
600 #else
601         flags |= GTK_STATE_FLAG_ACTIVE;
602 #endif
603     if (theme->isPressed(renderObject))
604         flags |= GTK_STATE_FLAG_SELECTED;
605     gtk_style_context_set_state(context.get(), static_cast<GtkStateFlags>(flags));
606
607     // Some themes do not render large toggle buttons properly, so we simply
608     // shrink the rectangle back down to the default size and then center it
609     // in the full toggle button region. The reason for not simply forcing toggle
610     // buttons to be a smaller size is that we don't want to break site layouts.
611     FloatRect rect(fullRect);
612     gint indicatorSize;
613     gtk_style_context_get_style(context.get(), "indicator-size", &indicatorSize, nullptr);
614     IntSize minSize(indicatorSize, indicatorSize);
615     shrinkToMinimumSizeAndCenterRectangle(rect, minSize);
616
617     gtk_render_background(context.get(), paintInfo.context().platformContext()->cr(), rect.x(), rect.y(), rect.width(), rect.height());
618     gtk_render_frame(context.get(), paintInfo.context().platformContext()->cr(), rect.x(), rect.y(), rect.width(), rect.height());
619
620     if (themePart == CheckButton)
621         gtk_render_check(context.get(), paintInfo.context().platformContext()->cr(), rect.x(), rect.y(), rect.width(), rect.height());
622     else
623         gtk_render_option(context.get(), paintInfo.context().platformContext()->cr(), rect.x(), rect.y(), rect.width(), rect.height());
624
625     if (theme->isFocused(renderObject)) {
626         IntRect indicatorRect(rect);
627         gint indicatorSpacing;
628         gtk_style_context_get_style(context.get(), "indicator-spacing", &indicatorSpacing, nullptr);
629         indicatorRect.inflate(indicatorSpacing);
630         gtk_render_focus(context.get(), paintInfo.context().platformContext()->cr(), indicatorRect.x(), indicatorRect.y(),
631             indicatorRect.width(), indicatorRect.height());
632     }
633 }
634 #endif // GTK_CHECK_VERSION(3, 20, 0)
635
636 void RenderThemeGtk::setCheckboxSize(RenderStyle& style) const
637 {
638     setToggleSize(CheckButton, style);
639 }
640
641 bool RenderThemeGtk::paintCheckbox(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
642 {
643     paintToggle(this, CheckButton, renderObject, paintInfo, rect);
644     return false;
645 }
646
647 void RenderThemeGtk::setRadioSize(RenderStyle& style) const
648 {
649     setToggleSize(RadioButton, style);
650 }
651
652 bool RenderThemeGtk::paintRadio(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
653 {
654     paintToggle(this, RadioButton, renderObject, paintInfo, rect);
655     return false;
656 }
657
658 #if GTK_CHECK_VERSION(3, 20, 0)
659 bool RenderThemeGtk::paintButton(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
660 {
661     auto& buttonWidget = static_cast<RenderThemeButton&>(RenderThemeWidget::getOrCreate(isDefault(renderObject) ? RenderThemeWidget::Type::ButtonDefault : RenderThemeWidget::Type::Button));
662     buttonWidget.button().setState(themePartStateFlags(*this, Button, renderObject));
663     buttonWidget.button().render(paintInfo.context().platformContext()->cr(), rect);
664     if (isFocused(renderObject))
665         buttonWidget.button().renderFocus(paintInfo.context().platformContext()->cr(), rect);
666     return false;
667 }
668 #else
669 static void renderButton(RenderTheme* theme, GtkStyleContext* context, const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
670 {
671     IntRect buttonRect(rect);
672
673     guint flags = 0;
674     if (!theme->isEnabled(renderObject))
675         flags |= GTK_STATE_FLAG_INSENSITIVE;
676     else if (theme->isHovered(renderObject))
677         flags |= GTK_STATE_FLAG_PRELIGHT;
678     if (theme->isPressed(renderObject))
679         flags |= GTK_STATE_FLAG_ACTIVE;
680     gtk_style_context_set_state(context, static_cast<GtkStateFlags>(flags));
681
682     if (theme->isDefault(renderObject)) {
683         GtkBorder* borderPtr = 0;
684         GtkBorder border = { 1, 1, 1, 1 };
685
686         gtk_style_context_get_style(context, "default-border", &borderPtr, nullptr);
687         if (borderPtr) {
688             border = *borderPtr;
689             gtk_border_free(borderPtr);
690         }
691
692         buttonRect.move(border.left, border.top);
693         buttonRect.setWidth(buttonRect.width() - (border.left + border.right));
694         buttonRect.setHeight(buttonRect.height() - (border.top + border.bottom));
695
696         gtk_style_context_add_class(context, GTK_STYLE_CLASS_DEFAULT);
697     }
698
699     gtk_render_background(context, paintInfo.context().platformContext()->cr(), buttonRect.x(), buttonRect.y(), buttonRect.width(), buttonRect.height());
700     gtk_render_frame(context, paintInfo.context().platformContext()->cr(), buttonRect.x(), buttonRect.y(), buttonRect.width(), buttonRect.height());
701
702     if (theme->isFocused(renderObject)) {
703         gint focusWidth, focusPad;
704         gboolean displaceFocus, interiorFocus;
705         gtk_style_context_get_style(
706             context,
707             "focus-line-width", &focusWidth,
708             "focus-padding", &focusPad,
709             "interior-focus", &interiorFocus,
710             "displace-focus", &displaceFocus,
711             nullptr);
712
713         if (interiorFocus) {
714             GtkBorder borderWidth;
715             gtk_style_context_get_border(context, gtk_style_context_get_state(context), &borderWidth);
716
717             buttonRect = IntRect(
718                 buttonRect.x() + borderWidth.left + focusPad,
719                 buttonRect.y() + borderWidth.top + focusPad,
720                 buttonRect.width() - (2 * focusPad + borderWidth.left + borderWidth.right),
721                 buttonRect.height() - (2 * focusPad + borderWidth.top + borderWidth.bottom));
722         } else
723             buttonRect.inflate(focusWidth + focusPad);
724
725         if (displaceFocus && theme->isPressed(renderObject)) {
726             gint childDisplacementX;
727             gint childDisplacementY;
728             gtk_style_context_get_style(context, "child-displacement-x", &childDisplacementX, "child-displacement-y", &childDisplacementY, nullptr);
729             buttonRect.move(childDisplacementX, childDisplacementY);
730         }
731
732         gtk_render_focus(context, paintInfo.context().platformContext()->cr(), buttonRect.x(), buttonRect.y(), buttonRect.width(), buttonRect.height());
733     }
734 }
735 bool RenderThemeGtk::paintButton(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
736 {
737     GRefPtr<GtkStyleContext> context = createStyleContext(Button);
738     gtk_style_context_set_direction(context.get(), static_cast<GtkTextDirection>(gtkTextDirection(renderObject.style().direction())));
739     renderButton(this, context.get(), renderObject, paintInfo, rect);
740     return false;
741 }
742 #endif // GTK_CHECK_VERSION(3, 20, 0)
743
744 static Color menuListColor(const Element* element)
745 {
746 #if GTK_CHECK_VERSION(3, 20, 0)
747     auto& comboWidget = static_cast<RenderThemeComboBox&>(RenderThemeWidget::getOrCreate(RenderThemeWidget::Type::ComboBox));
748     GtkStateFlags state = element->isDisabledFormControl() ? GTK_STATE_FLAG_INSENSITIVE : GTK_STATE_FLAG_NORMAL;
749     comboWidget.comboBox().setState(state);
750     comboWidget.button().setState(state);
751     return comboWidget.button().color();
752 #else
753     GRefPtr<GtkStyleContext> parentStyleContext = createStyleContext(ComboBox);
754     GRefPtr<GtkStyleContext> buttonStyleContext = createStyleContext(ComboBoxButton, parentStyleContext.get());
755     gtk_style_context_set_state(buttonStyleContext.get(), element->isDisabledFormControl() ? GTK_STATE_FLAG_INSENSITIVE : GTK_STATE_FLAG_NORMAL);
756
757     GdkRGBA gdkRGBAColor;
758     gtk_style_context_get_color(buttonStyleContext.get(), gtk_style_context_get_state(buttonStyleContext.get()), &gdkRGBAColor);
759     return gdkRGBAColor;
760 #endif // GTK_CHECK_VERSION(3, 20, 0)
761 }
762
763 void RenderThemeGtk::adjustMenuListStyle(StyleResolver&, RenderStyle& style, const Element* element) const
764 {
765     // The tests check explicitly that select menu buttons ignore line height.
766     style.setLineHeight(RenderStyle::initialLineHeight());
767
768     // We cannot give a proper rendering when border radius is active, unfortunately.
769     style.resetBorderRadius();
770
771     if (element)
772         style.setColor(menuListColor(element));
773 }
774
775 void RenderThemeGtk::adjustMenuListButtonStyle(StyleResolver& styleResolver, RenderStyle& style, const Element* e) const
776 {
777     adjustMenuListStyle(styleResolver, style, e);
778 }
779
780 #if GTK_CHECK_VERSION(3, 20, 0)
781 /*
782  * GtkComboBox gadgets tree
783  *
784  * combobox
785  * ├── box.linked
786  * │   ╰── button.combo
787  * │       ╰── box
788  * │           ├── cellview
789  * │           ╰── arrow
790  * ╰── window.popup
791  */
792 LengthBox RenderThemeGtk::popupInternalPaddingBox(const RenderStyle& style) const
793 {
794     if (style.appearance() == NoControlPart)
795         return LengthBox(0);
796
797     auto& comboWidget = static_cast<RenderThemeComboBox&>(RenderThemeWidget::getOrCreate(RenderThemeWidget::Type::ComboBox));
798     comboWidget.comboBox().setState(GTK_STATE_FLAG_NORMAL);
799     comboWidget.button().setState(GTK_STATE_FLAG_NORMAL);
800     comboWidget.arrow().setState(GTK_STATE_FLAG_NORMAL);
801     GtkBorder comboContentsBox = comboWidget.comboBox().contentsBox();
802     GtkBorder boxContentsBox = comboWidget.box().contentsBox();
803     GtkBorder buttonContentsBox = comboWidget.button().contentsBox();
804     GtkBorder buttonBoxContentsBox = comboWidget.buttonBox().contentsBox();
805     GtkBorder padding;
806     padding.left = comboContentsBox.left + boxContentsBox.left + buttonContentsBox.left + buttonBoxContentsBox.left;
807     padding.right = comboContentsBox.right + boxContentsBox.right + buttonContentsBox.right + buttonBoxContentsBox.right;
808     padding.top = comboContentsBox.top + boxContentsBox.top + buttonContentsBox.top + buttonBoxContentsBox.top;
809     padding.bottom = comboContentsBox.bottom + boxContentsBox.bottom + buttonContentsBox.bottom + buttonBoxContentsBox.bottom;
810
811     auto arrowSize = comboWidget.arrow().preferredSize();
812     return LengthBox(padding.top, padding.right + (style.direction() == TextDirection::LTR ? arrowSize.width() : 0),
813         padding.bottom, padding.left + (style.direction() == TextDirection::RTL ? arrowSize.width() : 0));
814 }
815
816 bool RenderThemeGtk::paintMenuList(const RenderObject& renderObject, const PaintInfo& paintInfo, const FloatRect& rect)
817 {
818     auto& comboWidget = static_cast<RenderThemeComboBox&>(RenderThemeWidget::getOrCreate(RenderThemeWidget::Type::ComboBox));
819     auto comboState = themePartStateFlags(*this, ComboBoxButton, renderObject);
820     comboWidget.comboBox().setState(comboState);
821     comboWidget.button().setState(comboState);
822     comboWidget.arrow().setState(comboState);
823
824     cairo_t* cr = paintInfo.context().platformContext()->cr();
825     comboWidget.comboBox().render(cr, rect);
826     comboWidget.box().render(cr, rect);
827     FloatRect contentsRect;
828     comboWidget.button().render(cr, rect, &contentsRect);
829     comboWidget.buttonBox().render(cr, contentsRect);
830     comboWidget.arrow().render(cr, contentsRect);
831     if (isFocused(renderObject))
832         comboWidget.button().renderFocus(cr, rect);
833
834     return false;
835 }
836 #else
837 LengthBox RenderThemeGtk::popupInternalPaddingBox(const RenderStyle& style) const
838 {
839     if (style.appearance() == NoControlPart)
840         return { 0, 0, 0, 0 };
841
842     GRefPtr<GtkStyleContext> parentContext = createStyleContext(ComboBox);
843     GRefPtr<GtkStyleContext> context = createStyleContext(ComboBoxButton, parentContext.get());
844     gtk_style_context_set_direction(context.get(), static_cast<GtkTextDirection>(gtkTextDirection(style.direction())));
845     gtk_style_context_set_state(context.get(), static_cast<GtkStateFlags>(0));
846     GtkBorder borderWidth = { 0, 0, 0, 0 };
847     gtk_style_context_get_border(context.get(), gtk_style_context_get_state(context.get()), &borderWidth);
848
849     gboolean interiorFocus;
850     gint focusWidth, focusPad;
851     gtk_style_context_get_style(context.get(), "interior-focus", &interiorFocus, "focus-line-width", &focusWidth, "focus-padding", &focusPad, nullptr);
852     focusWidth = interiorFocus ? focusWidth + focusPad : 0;
853
854     return { borderWidth.top + focusWidth, borderWidth.right + focusWidth + (style.direction() == TextDirection::LTR ? minArrowSize : 0),
855         borderWidth.bottom + focusWidth, borderWidth.left + focusWidth + (style.direction() == TextDirection::RTL ? minArrowSize : 0) };
856 }
857
858 bool RenderThemeGtk::paintMenuList(const RenderObject& renderObject, const PaintInfo& paintInfo, const FloatRect& r)
859 {
860     // FIXME: adopt subpixel themes.
861     IntRect rect = IntRect(r);
862
863     cairo_t* cairoContext = paintInfo.context().platformContext()->cr();
864     GtkTextDirection direction = static_cast<GtkTextDirection>(gtkTextDirection(renderObject.style().direction()));
865
866     GRefPtr<GtkStyleContext> parentStyleContext = createStyleContext(ComboBox);
867
868     // Paint the button.
869     GRefPtr<GtkStyleContext> buttonStyleContext = createStyleContext(ComboBoxButton, parentStyleContext.get());
870     gtk_style_context_set_direction(buttonStyleContext.get(), direction);
871     renderButton(this, buttonStyleContext.get(), renderObject, paintInfo, rect);
872
873     // Get the inner rectangle.
874     gint focusWidth, focusPad;
875     GtkBorder* innerBorderPtr = 0;
876     GtkBorder innerBorder = { 1, 1, 1, 1 };
877     gtk_style_context_get_style(buttonStyleContext.get(), "inner-border", &innerBorderPtr, "focus-line-width", &focusWidth, "focus-padding", &focusPad, nullptr);
878     if (innerBorderPtr) {
879         innerBorder = *innerBorderPtr;
880         gtk_border_free(innerBorderPtr);
881     }
882
883     GtkBorder borderWidth;
884     GtkStateFlags state = gtk_style_context_get_state(buttonStyleContext.get());
885     gtk_style_context_get_border(buttonStyleContext.get(), state, &borderWidth);
886
887     focusWidth += focusPad;
888     IntRect innerRect(
889         rect.x() + innerBorder.left + borderWidth.left + focusWidth,
890         rect.y() + innerBorder.top + borderWidth.top + focusWidth,
891         rect.width() - borderWidth.left - borderWidth.right - innerBorder.left - innerBorder.right - (2 * focusWidth),
892         rect.height() - borderWidth.top - borderWidth.bottom - innerBorder.top - innerBorder.bottom - (2 * focusWidth));
893
894     if (isPressed(renderObject)) {
895         gint childDisplacementX;
896         gint childDisplacementY;
897         gtk_style_context_get_style(buttonStyleContext.get(), "child-displacement-x", &childDisplacementX, "child-displacement-y", &childDisplacementY, nullptr);
898         innerRect.move(childDisplacementX, childDisplacementY);
899     }
900     innerRect.setWidth(std::max(1, innerRect.width()));
901     innerRect.setHeight(std::max(1, innerRect.height()));
902
903     // Paint the arrow.
904     GRefPtr<GtkStyleContext> arrowStyleContext = createStyleContext(ComboBoxArrow, buttonStyleContext.get());
905     gtk_style_context_set_direction(arrowStyleContext.get(), direction);
906
907     gfloat arrowScaling;
908     gtk_style_context_get_style(parentStyleContext.get(), "arrow-scaling", &arrowScaling, nullptr);
909
910     IntSize arrowSize(minArrowSize, innerRect.height());
911     FloatPoint arrowPosition(innerRect.location());
912     if (direction == GTK_TEXT_DIR_LTR)
913         arrowPosition.move(innerRect.width() - arrowSize.width(), 0);
914
915     // GTK+ actually fetches the xalign and valign values from the widget, but since we
916     // don't have a widget here, we are just using the default xalign and valign values of 0.5.
917     gint extent = std::min(arrowSize.width(), arrowSize.height()) * arrowScaling;
918     arrowPosition.move((arrowSize.width() - extent) / 2, (arrowSize.height() - extent) / 2);
919
920     gtk_style_context_set_state(arrowStyleContext.get(), state);
921     gtk_render_arrow(arrowStyleContext.get(), cairoContext, G_PI, arrowPosition.x(), arrowPosition.y(), extent);
922
923     return false;
924 }
925 #endif // GTK_CHECK_VERSION(3, 20, 0)
926
927 bool RenderThemeGtk::paintMenuListButtonDecorations(const RenderBox& object, const PaintInfo& info, const FloatRect& rect)
928 {
929     return paintMenuList(object, info, rect);
930 }
931
932 #if GTK_CHECK_VERSION(3, 20, 0)
933
934 static IntSize spinButtonSize()
935 {
936     auto& spinButtonWidget = static_cast<RenderThemeSpinButton&>(RenderThemeWidget::getOrCreate(RenderThemeWidget::Type::SpinButton));
937     spinButtonWidget.spinButton().setState(GTK_STATE_FLAG_NORMAL);
938     spinButtonWidget.entry().setState(GTK_STATE_FLAG_NORMAL);
939     spinButtonWidget.up().setState(GTK_STATE_FLAG_NORMAL);
940     spinButtonWidget.down().setState(GTK_STATE_FLAG_NORMAL);
941
942     IntSize preferredSize = spinButtonWidget.spinButton().preferredSize();
943     preferredSize = preferredSize.expandedTo(spinButtonWidget.entry().preferredSize());
944     IntSize upPreferredSize = preferredSize.expandedTo(spinButtonWidget.up().preferredSize());
945     IntSize downPreferredSize = preferredSize.expandedTo(spinButtonWidget.down().preferredSize());
946
947     return IntSize(upPreferredSize.width() + downPreferredSize.width(), std::max(upPreferredSize.height(), downPreferredSize.height()));
948 }
949
950
951 void RenderThemeGtk::adjustTextFieldStyle(StyleResolver&, RenderStyle& style, const Element* element) const
952 {
953     if (!is<HTMLInputElement>(element) || !shouldHaveSpinButton(downcast<HTMLInputElement>(*element)))
954         return;
955
956     style.setMinHeight(Length(spinButtonSize().height(), Fixed));
957
958     // The default theme for the GTK+ port uses very wide spin buttons (66px) compared to what other
959     // browsers use (~13 px). And unfortunately, most of the web developers won't test how their site
960     // renders on WebKitGTK+. To ensure that spin buttons don't end up covering the values of the input
961     // field, we override the width of the input element and always increment it with the width needed
962     // for the spinbutton (when drawing the spinbutton).
963     int minimumWidth = style.width().intValue() + spinButtonSize().width();
964     style.setMinWidth(Length(minimumWidth, Fixed));
965 }
966
967 bool RenderThemeGtk::paintTextField(const RenderObject& renderObject, const PaintInfo& paintInfo, const FloatRect& rect)
968 {
969     if (is<HTMLInputElement>(renderObject.node()) && shouldHaveSpinButton(downcast<HTMLInputElement>(*renderObject.node()))) {
970         auto& spinButtonWidget = static_cast<RenderThemeSpinButton&>(RenderThemeWidget::getOrCreate(RenderThemeWidget::Type::SpinButton));
971         auto spinButtonState = themePartStateFlags(*this, Entry, renderObject);
972         spinButtonWidget.spinButton().setState(spinButtonState);
973         spinButtonWidget.entry().setState(spinButtonState);
974         spinButtonWidget.spinButton().render(paintInfo.context().platformContext()->cr(), rect);
975         spinButtonWidget.entry().render(paintInfo.context().platformContext()->cr(), rect);
976     } else {
977         auto& entryWidget = static_cast<RenderThemeEntry&>(RenderThemeWidget::getOrCreate(RenderThemeWidget::Type::Entry));
978         entryWidget.entry().setState(themePartStateFlags(*this, Entry, renderObject));
979         entryWidget.entry().render(paintInfo.context().platformContext()->cr(), rect);
980     }
981     return false;
982 }
983 #else
984 void RenderThemeGtk::adjustTextFieldStyle(StyleResolver&, RenderStyle&, const Element*) const
985 {
986 }
987
988 bool RenderThemeGtk::paintTextField(const RenderObject& renderObject, const PaintInfo& paintInfo, const FloatRect& rect)
989 {
990     GRefPtr<GtkStyleContext> context = createStyleContext(Entry);
991     gtk_style_context_set_direction(context.get(), static_cast<GtkTextDirection>(gtkTextDirection(renderObject.style().direction())));
992
993     guint flags = 0;
994     if (!isEnabled(renderObject) || isReadOnlyControl(renderObject))
995         flags |= GTK_STATE_FLAG_INSENSITIVE;
996     else if (isFocused(renderObject))
997         flags |= GTK_STATE_FLAG_FOCUSED;
998     gtk_style_context_set_state(context.get(), static_cast<GtkStateFlags>(flags));
999
1000     gtk_render_background(context.get(), paintInfo.context().platformContext()->cr(), rect.x(), rect.y(), rect.width(), rect.height());
1001     gtk_render_frame(context.get(), paintInfo.context().platformContext()->cr(), rect.x(), rect.y(), rect.width(), rect.height());
1002
1003     if (isFocused(renderObject) && isEnabled(renderObject)) {
1004         gboolean interiorFocus;
1005         gint focusWidth, focusPad;
1006         gtk_style_context_get_style(context.get(), "interior-focus", &interiorFocus, "focus-line-width", &focusWidth, "focus-padding", &focusPad, nullptr);
1007         if (!interiorFocus) {
1008             IntRect focusRect(rect);
1009             focusRect.inflate(focusWidth + focusPad);
1010             gtk_render_focus(context.get(), paintInfo.context().platformContext()->cr(), focusRect.x(), focusRect.y(), focusRect.width(), focusRect.height());
1011         }
1012     }
1013
1014     return false;
1015 }
1016 #endif
1017
1018 #if GTK_CHECK_VERSION(3, 20, 0)
1019 static void adjustSearchFieldIconStyle(RenderThemePart themePart, RenderStyle& style)
1020 {
1021     ASSERT(themePart == EntryIconLeft || themePart == EntryIconRight);
1022     auto& searchEntryWidget = static_cast<RenderThemeSearchEntry&>(RenderThemeWidget::getOrCreate(RenderThemeWidget::Type::SearchEntry));
1023     searchEntryWidget.entry().setState(GTK_STATE_FLAG_NORMAL);
1024     searchEntryWidget.leftIcon().setState(GTK_STATE_FLAG_NORMAL);
1025     searchEntryWidget.rightIcon().setState(GTK_STATE_FLAG_NORMAL);
1026
1027     // Get the icon size based on the font size.
1028     auto& icon = static_cast<RenderThemeIconGadget&>(themePart == EntryIconLeft ? searchEntryWidget.leftIcon() : searchEntryWidget.rightIcon());
1029     icon.setIconSize(style.computedFontPixelSize());
1030     IntSize preferredSize = icon.preferredSize();
1031     GtkBorder contentsBox = searchEntryWidget.entry().contentsBox();
1032     if (themePart == EntryIconLeft)
1033         preferredSize.expand(contentsBox.left, contentsBox.top + contentsBox.bottom);
1034     else
1035         preferredSize.expand(contentsBox.right, contentsBox.top + contentsBox.bottom);
1036     style.setWidth(Length(preferredSize.width(), Fixed));
1037     style.setHeight(Length(preferredSize.height(), Fixed));
1038 }
1039 #else
1040 // Defined in GTK+ (gtk/gtkiconfactory.c)
1041 static const gint gtkIconSizeMenu = 16;
1042 static const gint gtkIconSizeSmallToolbar = 18;
1043 static const gint gtkIconSizeButton = 20;
1044 static const gint gtkIconSizeLargeToolbar = 24;
1045 static const gint gtkIconSizeDnd = 32;
1046 static const gint gtkIconSizeDialog = 48;
1047
1048 static GtkIconSize getIconSizeForPixelSize(gint pixelSize)
1049 {
1050     if (pixelSize < gtkIconSizeSmallToolbar)
1051         return GTK_ICON_SIZE_MENU;
1052     if (pixelSize >= gtkIconSizeSmallToolbar && pixelSize < gtkIconSizeButton)
1053         return GTK_ICON_SIZE_SMALL_TOOLBAR;
1054     if (pixelSize >= gtkIconSizeButton && pixelSize < gtkIconSizeLargeToolbar)
1055         return GTK_ICON_SIZE_BUTTON;
1056     if (pixelSize >= gtkIconSizeLargeToolbar && pixelSize < gtkIconSizeDnd)
1057         return GTK_ICON_SIZE_LARGE_TOOLBAR;
1058     if (pixelSize >= gtkIconSizeDnd && pixelSize < gtkIconSizeDialog)
1059         return GTK_ICON_SIZE_DND;
1060
1061     return GTK_ICON_SIZE_DIALOG;
1062 }
1063
1064 static void adjustSearchFieldIconStyle(RenderThemePart themePart, RenderStyle& style)
1065 {
1066     style.resetBorder();
1067     style.resetPadding();
1068
1069     GRefPtr<GtkStyleContext> parentContext = createStyleContext(Entry);
1070     GRefPtr<GtkStyleContext> context = createStyleContext(themePart, parentContext.get());
1071
1072     GtkBorder padding;
1073     gtk_style_context_get_padding(context.get(), gtk_style_context_get_state(context.get()), &padding);
1074
1075     // Get the icon size based on the font size.
1076     int fontSize = style.computedFontPixelSize();
1077     if (fontSize < gtkIconSizeMenu) {
1078         style.setWidth(Length(fontSize + (padding.left + padding.right), Fixed));
1079         style.setHeight(Length(fontSize + (padding.top + padding.bottom), Fixed));
1080         return;
1081     }
1082     gint width = 0, height = 0;
1083     gtk_icon_size_lookup(getIconSizeForPixelSize(fontSize), &width, &height);
1084     style.setWidth(Length(width + (padding.left + padding.right), Fixed));
1085     style.setHeight(Length(height + (padding.top + padding.bottom), Fixed));
1086 }
1087 #endif
1088
1089 bool RenderThemeGtk::paintTextArea(const RenderObject& o, const PaintInfo& i, const FloatRect& r)
1090 {
1091     return paintTextField(o, i, r);
1092 }
1093
1094 void RenderThemeGtk::adjustSearchFieldResultsButtonStyle(StyleResolver& styleResolver, RenderStyle& style, const Element* e) const
1095 {
1096     adjustSearchFieldCancelButtonStyle(styleResolver, style, e);
1097 }
1098
1099 bool RenderThemeGtk::paintSearchFieldResultsButton(const RenderBox& o, const PaintInfo& i, const IntRect& rect)
1100 {
1101     return paintSearchFieldResultsDecorationPart(o, i, rect);
1102 }
1103
1104 void RenderThemeGtk::adjustSearchFieldResultsDecorationPartStyle(StyleResolver&, RenderStyle& style, const Element*) const
1105 {
1106     adjustSearchFieldIconStyle(EntryIconLeft, style);
1107 }
1108
1109 void RenderThemeGtk::adjustSearchFieldCancelButtonStyle(StyleResolver&, RenderStyle& style, const Element*) const
1110 {
1111     adjustSearchFieldIconStyle(EntryIconRight, style);
1112 }
1113
1114 #if GTK_CHECK_VERSION(3, 20, 0)
1115 static bool paintSearchFieldIcon(RenderThemeGtk* theme, RenderThemePart themePart, const RenderBox& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
1116 {
1117     ASSERT(themePart == EntryIconLeft || themePart == EntryIconRight);
1118     auto& searchEntryWidget = static_cast<RenderThemeSearchEntry&>(RenderThemeWidget::getOrCreate(RenderThemeWidget::Type::SearchEntry));
1119     searchEntryWidget.entry().setState(themePartStateFlags(*theme, Entry, renderObject));
1120     auto& icon = static_cast<RenderThemeIconGadget&>(themePart == EntryIconLeft ? searchEntryWidget.leftIcon() : searchEntryWidget.rightIcon());
1121     icon.setState(themePartStateFlags(*theme, themePart, renderObject));
1122     icon.setIconSize(renderObject.style().computedFontPixelSize());
1123     GtkBorder contentsBox = searchEntryWidget.entry().contentsBox();
1124     IntRect iconRect = rect;
1125     if (themePart == EntryIconLeft) {
1126         iconRect.move(contentsBox.left, contentsBox.top);
1127         iconRect.contract(contentsBox.left, contentsBox.top + contentsBox.bottom);
1128     } else
1129         iconRect.contract(contentsBox.right, contentsBox.top + contentsBox.bottom);
1130     return !icon.render(paintInfo.context().platformContext()->cr(), iconRect);
1131 }
1132 bool RenderThemeGtk::paintSearchFieldResultsDecorationPart(const RenderBox& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
1133 {
1134     return paintSearchFieldIcon(this, EntryIconLeft, renderObject, paintInfo, rect);
1135 }
1136
1137 bool RenderThemeGtk::paintSearchFieldCancelButton(const RenderBox& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
1138 {
1139     return paintSearchFieldIcon(this, EntryIconRight, renderObject, paintInfo, rect);
1140 }
1141 #else
1142 static bool paintIcon(GtkStyleContext* context, GraphicsContext& graphicsContext, const IntRect& rect, const char* iconName)
1143 {
1144     GRefPtr<GdkPixbuf> icon = loadThemedIcon(context, iconName, getIconSizeForPixelSize(rect.height()));
1145     if (!icon)
1146         return false;
1147
1148     if (gdk_pixbuf_get_width(icon.get()) > rect.width() || gdk_pixbuf_get_height(icon.get()) > rect.height())
1149         icon = adoptGRef(gdk_pixbuf_scale_simple(icon.get(), rect.width(), rect.height(), GDK_INTERP_BILINEAR));
1150
1151     gtk_render_icon(context, graphicsContext.platformContext()->cr(), icon.get(), rect.x(), rect.y());
1152     return true;
1153 }
1154
1155 static bool paintEntryIcon(RenderThemePart themePart, const char* iconName, GraphicsContext& graphicsContext, const IntRect& rect, GtkTextDirection direction, GtkStateFlags state)
1156 {
1157     GRefPtr<GtkStyleContext> parentContext = createStyleContext(Entry);
1158     GRefPtr<GtkStyleContext> context = createStyleContext(themePart, parentContext.get());
1159     gtk_style_context_set_direction(context.get(), direction);
1160     gtk_style_context_set_state(context.get(), state);
1161     return paintIcon(context.get(), graphicsContext, rect, iconName);
1162 }
1163
1164 static IntRect centerRectVerticallyInParentInputElement(const RenderObject& renderObject, const IntRect& rect)
1165 {
1166     if (!renderObject.node())
1167         return IntRect();
1168
1169     // Get the renderer of <input> element.
1170     Node* input = renderObject.node()->shadowHost();
1171     if (!input)
1172         input = renderObject.node();
1173     if (!is<RenderBox>(*input->renderer()))
1174         return IntRect();
1175
1176     // If possible center the y-coordinate of the rect vertically in the parent input element.
1177     // We also add one pixel here to ensure that the y coordinate is rounded up for box heights
1178     // that are even, which looks in relation to the box text.
1179     IntRect inputContentBox = downcast<RenderBox>(*input->renderer()).absoluteContentBox();
1180
1181     // Make sure the scaled decoration stays square and will fit in its parent's box.
1182     int iconSize = std::min(inputContentBox.width(), std::min(inputContentBox.height(), rect.height()));
1183     IntRect scaledRect(rect.x(), inputContentBox.y() + (inputContentBox.height() - iconSize + 1) / 2, iconSize, iconSize);
1184     return scaledRect;
1185 }
1186
1187 bool RenderThemeGtk::paintSearchFieldResultsDecorationPart(const RenderBox& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
1188 {
1189     IntRect iconRect = centerRectVerticallyInParentInputElement(renderObject, rect);
1190     if (iconRect.isEmpty())
1191         return true;
1192
1193     return !paintEntryIcon(EntryIconLeft, "edit-find-symbolic", paintInfo.context(), iconRect, gtkTextDirection(renderObject.style().direction()),
1194         gtkIconStateFlags(this, renderObject));
1195 }
1196
1197 bool RenderThemeGtk::paintSearchFieldCancelButton(const RenderBox& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
1198 {
1199     IntRect iconRect = centerRectVerticallyInParentInputElement(renderObject, rect);
1200     if (iconRect.isEmpty())
1201         return true;
1202
1203     return !paintEntryIcon(EntryIconRight, "edit-clear-symbolic", paintInfo.context(), iconRect, gtkTextDirection(renderObject.style().direction()),
1204         gtkIconStateFlags(this, renderObject));
1205 }
1206 #endif // GTK_CHECK_VERSION(3, 20, 0)
1207
1208 void RenderThemeGtk::adjustSearchFieldStyle(StyleResolver&, RenderStyle& style, const Element*) const
1209 {
1210     // We cannot give a proper rendering when border radius is active, unfortunately.
1211     style.resetBorderRadius();
1212     style.setLineHeight(RenderStyle::initialLineHeight());
1213 }
1214
1215 bool RenderThemeGtk::paintSearchField(const RenderObject& o, const PaintInfo& i, const IntRect& rect)
1216 {
1217     return paintTextField(o, i, rect);
1218 }
1219
1220 bool RenderThemeGtk::shouldHaveCapsLockIndicator(const HTMLInputElement& element) const
1221 {
1222     return element.isPasswordField();
1223 }
1224
1225 void RenderThemeGtk::adjustSliderTrackStyle(StyleResolver&, RenderStyle& style, const Element*) const
1226 {
1227     style.setBoxShadow(nullptr);
1228 }
1229
1230 void RenderThemeGtk::adjustSliderThumbStyle(StyleResolver& styleResolver, RenderStyle& style, const Element* element) const
1231 {
1232     RenderTheme::adjustSliderThumbStyle(styleResolver, style, element);
1233     style.setBoxShadow(nullptr);
1234 }
1235
1236 #if GTK_CHECK_VERSION(3, 20, 0)
1237 /*
1238  * GtkScale
1239  *
1240  * scale
1241  * ╰── contents
1242  *     ╰── trough
1243  *         ├── slider
1244  *         ╰── [highlight]
1245  */
1246 bool RenderThemeGtk::paintSliderTrack(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
1247 {
1248     ControlPart part = renderObject.style().appearance();
1249     ASSERT(part == SliderHorizontalPart || part == SliderVerticalPart);
1250
1251     auto& sliderWidget = static_cast<RenderThemeSlider&>(RenderThemeWidget::getOrCreate(part == SliderHorizontalPart ? RenderThemeWidget::Type::HorizontalSlider : RenderThemeWidget::Type::VerticalSlider));
1252     auto scaleState = themePartStateFlags(*this, Scale, renderObject);
1253     auto& scale = sliderWidget.scale();
1254     scale.setState(scaleState);
1255     auto& contents = sliderWidget.contents();
1256     auto& trough = sliderWidget.trough();
1257     trough.setState(scaleState);
1258     auto& slider = sliderWidget.slider();
1259     auto& highlight = sliderWidget.highlight();
1260
1261     // The given rectangle is not calculated based on the scale size, but all the margins and paddings are based on it.
1262     IntSize preferredSize = scale.preferredSize();
1263     preferredSize = preferredSize.expandedTo(contents.preferredSize());
1264     preferredSize = preferredSize.expandedTo(trough.preferredSize());
1265     FloatRect trackRect = rect;
1266     if (part == SliderHorizontalPart) {
1267         trackRect.move(0, rect.height() / 2 - (preferredSize.height() / 2));
1268         trackRect.setHeight(preferredSize.height());
1269     } else {
1270         trackRect.move(rect.width() / 2 - (preferredSize.width() / 2), 0);
1271         trackRect.setWidth(preferredSize.width());
1272     }
1273
1274     FloatRect contentsRect;
1275     scale.render(paintInfo.context().platformContext()->cr(), trackRect, &contentsRect);
1276     contents.render(paintInfo.context().platformContext()->cr(), contentsRect, &contentsRect);
1277     // Scale trough defines its size querying slider and highlight.
1278     if (part == SliderHorizontalPart)
1279         contentsRect.setHeight(trough.preferredSize().height() + std::max(slider.preferredSize().height(), highlight.preferredSize().height()));
1280     else
1281         contentsRect.setWidth(trough.preferredSize().width() + std::max(slider.preferredSize().width(), highlight.preferredSize().width()));
1282     FloatRect troughRect = contentsRect;
1283     trough.render(paintInfo.context().platformContext()->cr(), troughRect, &contentsRect);
1284     if (isFocused(renderObject))
1285         trough.renderFocus(paintInfo.context().platformContext()->cr(), troughRect);
1286
1287     LayoutPoint thumbLocation;
1288     if (is<HTMLInputElement>(renderObject.node())) {
1289         auto& input = downcast<HTMLInputElement>(*renderObject.node());
1290         if (auto* element = input.sliderThumbElement())
1291             thumbLocation = element->renderBox()->location();
1292     }
1293
1294     if (part == SliderHorizontalPart) {
1295         if (renderObject.style().direction() == TextDirection::RTL) {
1296             contentsRect.move(thumbLocation.x(), 0);
1297             contentsRect.setWidth(contentsRect.width() - thumbLocation.x());
1298         } else
1299             contentsRect.setWidth(thumbLocation.x());
1300     } else
1301         contentsRect.setHeight(thumbLocation.y());
1302     highlight.render(paintInfo.context().platformContext()->cr(), contentsRect);
1303
1304     return false;
1305 }
1306
1307 void RenderThemeGtk::adjustSliderThumbSize(RenderStyle& style, const Element*) const
1308 {
1309     ControlPart part = style.appearance();
1310     if (part != SliderThumbHorizontalPart && part != SliderThumbVerticalPart)
1311         return;
1312
1313     auto& sliderWidget = static_cast<RenderThemeSlider&>(RenderThemeWidget::getOrCreate(part == SliderThumbHorizontalPart ? RenderThemeWidget::Type::HorizontalSlider : RenderThemeWidget::Type::VerticalSlider));
1314     sliderWidget.scale().setState(GTK_STATE_FLAG_NORMAL);
1315     sliderWidget.trough().setState(GTK_STATE_FLAG_NORMAL);
1316
1317     IntSize preferredSize = sliderWidget.scale().preferredSize();
1318     preferredSize = preferredSize.expandedTo(sliderWidget.contents().preferredSize());
1319     preferredSize = preferredSize.expandedTo(sliderWidget.trough().preferredSize());
1320     preferredSize = preferredSize.expandedTo(sliderWidget.slider().preferredSize());
1321     if (part == SliderThumbHorizontalPart) {
1322         style.setWidth(Length(preferredSize.width(), Fixed));
1323         style.setHeight(Length(preferredSize.height(), Fixed));
1324         return;
1325     }
1326     ASSERT(part == SliderThumbVerticalPart);
1327     style.setWidth(Length(preferredSize.height(), Fixed));
1328     style.setHeight(Length(preferredSize.width(), Fixed));
1329 }
1330
1331 bool RenderThemeGtk::paintSliderThumb(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
1332 {
1333     ControlPart part = renderObject.style().appearance();
1334     ASSERT(part == SliderThumbHorizontalPart || part == SliderThumbVerticalPart);
1335
1336     auto& sliderWidget = static_cast<RenderThemeSlider&>(RenderThemeWidget::getOrCreate(part == SliderThumbHorizontalPart ? RenderThemeWidget::Type::HorizontalSlider : RenderThemeWidget::Type::VerticalSlider));
1337     auto scaleState = themePartStateFlags(*this, Scale, renderObject);
1338     auto& scale = sliderWidget.scale();
1339     scale.setState(scaleState);
1340     auto& contents = sliderWidget.contents();
1341     auto& trough = sliderWidget.trough();
1342     trough.setState(scaleState);
1343     auto& slider = sliderWidget.slider();
1344     slider.setState(themePartStateFlags(*this, ScaleSlider, renderObject));
1345     auto& highlight = sliderWidget.highlight();
1346
1347     GtkBorder scaleContentsBox = scale.contentsBox();
1348     GtkBorder contentsContentsBox = contents.contentsBox();
1349     GtkBorder troughContentsBox = trough.contentsBox();
1350     GtkBorder padding;
1351     padding.left = scaleContentsBox.left + contentsContentsBox.left + troughContentsBox.left;
1352     padding.right = scaleContentsBox.right + contentsContentsBox.right + troughContentsBox.right;
1353     padding.top = scaleContentsBox.top + contentsContentsBox.top + troughContentsBox.top;
1354     padding.bottom = scaleContentsBox.bottom + contentsContentsBox.bottom + troughContentsBox.bottom;
1355
1356     // Scale trough defines its size querying slider and highlight.
1357     int troughHeight = trough.preferredSize().height() + std::max(slider.preferredSize().height(), highlight.preferredSize().height());
1358     IntRect sliderRect(rect.location(), IntSize(troughHeight, troughHeight));
1359     sliderRect.move(padding.left, padding.top);
1360     sliderRect.contract(padding.left + padding.right, padding.top + padding.bottom);
1361     slider.render(paintInfo.context().platformContext()->cr(), sliderRect);
1362     return false;
1363 }
1364 #else
1365 bool RenderThemeGtk::paintSliderTrack(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
1366 {
1367     ControlPart part = renderObject.style().appearance();
1368     ASSERT(part == SliderHorizontalPart || part == SliderVerticalPart);
1369
1370     GRefPtr<GtkStyleContext> parentContext = createStyleContext(Scale);
1371     gtk_style_context_add_class(parentContext.get(), part == SliderHorizontalPart ? GTK_STYLE_CLASS_HORIZONTAL : GTK_STYLE_CLASS_VERTICAL);
1372     GRefPtr<GtkStyleContext> context = createStyleContext(ScaleTrough, parentContext.get());
1373     gtk_style_context_set_direction(context.get(), gtkTextDirection(renderObject.style().direction()));
1374
1375     if (!isEnabled(renderObject))
1376         gtk_style_context_set_state(context.get(), GTK_STATE_FLAG_INSENSITIVE);
1377
1378     IntRect sliderRect = rect;
1379     // GTK+ uses the slider thumb size and margins to calculate the trough size, but in WebKit we render the thumb and
1380     // the slider track separately and the track rectangle we receive here can't be used to apply the GTK+ CSS sizes
1381     // and margins. So we use a maximum fixed size for the trough to match at least Adwaita, but that should look
1382     // good in other themes as well.
1383     static const int sliderSize = 4;
1384
1385     if (part == SliderHorizontalPart) {
1386         sliderRect.setHeight(std::min(rect.height(), sliderSize));
1387         sliderRect.move(0, (rect.height() - sliderRect.height()) / 2);
1388     } else {
1389         sliderRect.setWidth(std::min(rect.width(), sliderSize));
1390         sliderRect.move((rect.width() - sliderRect.width()) / 2, 0);
1391     }
1392
1393     gtk_render_background(context.get(), paintInfo.context().platformContext()->cr(), sliderRect.x(), sliderRect.y(), sliderRect.width(), sliderRect.height());
1394     gtk_render_frame(context.get(), paintInfo.context().platformContext()->cr(), sliderRect.x(), sliderRect.y(), sliderRect.width(), sliderRect.height());
1395
1396     if (isFocused(renderObject)) {
1397         gint focusWidth, focusPad;
1398         gtk_style_context_get_style(context.get(), "focus-line-width", &focusWidth, "focus-padding", &focusPad, nullptr);
1399         IntRect focusRect(sliderRect);
1400         focusRect.inflate(focusWidth + focusPad);
1401         gtk_render_focus(context.get(), paintInfo.context().platformContext()->cr(), focusRect.x(), focusRect.y(), focusRect.width(), focusRect.height());
1402     }
1403
1404     return false;
1405 }
1406
1407 void RenderThemeGtk::adjustSliderThumbSize(RenderStyle& style, const Element*) const
1408 {
1409     ControlPart part = style.appearance();
1410     if (part != SliderThumbHorizontalPart && part != SliderThumbVerticalPart)
1411         return;
1412
1413     GRefPtr<GtkStyleContext> context = createStyleContext(Scale);
1414     gint sliderWidth, sliderLength;
1415     gtk_style_context_get_style(context.get(), "slider-width", &sliderWidth, "slider-length", &sliderLength, nullptr);
1416
1417     if (part == SliderThumbHorizontalPart) {
1418         style.setWidth(Length(sliderLength, Fixed));
1419         style.setHeight(Length(sliderWidth, Fixed));
1420         return;
1421     }
1422     ASSERT(part == SliderThumbVerticalPart);
1423     style.setWidth(Length(sliderWidth, Fixed));
1424     style.setHeight(Length(sliderLength, Fixed));
1425 }
1426
1427 bool RenderThemeGtk::paintSliderThumb(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
1428 {
1429     ControlPart part = renderObject.style().appearance();
1430     ASSERT(part == SliderThumbHorizontalPart || part == SliderThumbVerticalPart);
1431
1432     // FIXME: The entire slider is too wide, stretching the thumb into an oval rather than a circle.
1433     GRefPtr<GtkStyleContext> parentContext = createStyleContext(Scale);
1434     gtk_style_context_add_class(parentContext.get(), part == SliderThumbHorizontalPart ? GTK_STYLE_CLASS_HORIZONTAL : GTK_STYLE_CLASS_VERTICAL);
1435     GRefPtr<GtkStyleContext> troughContext = createStyleContext(ScaleTrough, parentContext.get());
1436     GRefPtr<GtkStyleContext> context = createStyleContext(ScaleSlider, troughContext.get());
1437     gtk_style_context_set_direction(context.get(), gtkTextDirection(renderObject.style().direction()));
1438
1439     guint flags = 0;
1440     if (!isEnabled(renderObject))
1441         flags |= GTK_STATE_FLAG_INSENSITIVE;
1442     else if (isHovered(renderObject))
1443         flags |= GTK_STATE_FLAG_PRELIGHT;
1444     if (isPressed(renderObject))
1445         flags |= GTK_STATE_FLAG_ACTIVE;
1446     gtk_style_context_set_state(context.get(), static_cast<GtkStateFlags>(flags));
1447
1448     gtk_render_slider(context.get(), paintInfo.context().platformContext()->cr(), rect.x(), rect.y(), rect.width(), rect.height(),
1449         part == SliderThumbHorizontalPart ? GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL);
1450
1451     return false;
1452 }
1453 #endif
1454
1455 #if GTK_CHECK_VERSION(3, 20, 0)
1456 IntRect RenderThemeGtk::progressBarRectForBounds(const RenderObject& renderObject, const IntRect& bounds) const
1457 {
1458     const auto& renderProgress = downcast<RenderProgress>(renderObject);
1459     auto& progressBarWidget = static_cast<RenderThemeProgressBar&>(RenderThemeWidget::getOrCreate(renderProgress.isDeterminate() ? RenderThemeProgressBar::Type::ProgressBar : RenderThemeProgressBar::Type::IndeterminateProgressBar));
1460     IntSize preferredSize = progressBarWidget.progressBar().preferredSize();
1461     preferredSize = preferredSize.expandedTo(progressBarWidget.trough().preferredSize());
1462     preferredSize = preferredSize.expandedTo(progressBarWidget.progress().preferredSize());
1463     return IntRect(bounds.x(), bounds.y(), bounds.width(), preferredSize.height());
1464 }
1465
1466 bool RenderThemeGtk::paintProgressBar(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
1467 {
1468     if (!renderObject.isProgress())
1469         return true;
1470
1471     const auto& renderProgress = downcast<RenderProgress>(renderObject);
1472     auto& progressBarWidget = static_cast<RenderThemeProgressBar&>(RenderThemeWidget::getOrCreate(renderProgress.isDeterminate() ? RenderThemeProgressBar::Type::ProgressBar : RenderThemeProgressBar::Type::IndeterminateProgressBar));
1473     progressBarWidget.progressBar().render(paintInfo.context().platformContext()->cr(), rect);
1474     progressBarWidget.trough().render(paintInfo.context().platformContext()->cr(), rect);
1475     progressBarWidget.progress().render(paintInfo.context().platformContext()->cr(), calculateProgressRect(renderObject, rect));
1476     return false;
1477 }
1478 #else
1479 IntRect RenderThemeGtk::progressBarRectForBounds(const RenderObject&, const IntRect& bounds) const
1480 {
1481     return bounds;
1482 }
1483
1484 bool RenderThemeGtk::paintProgressBar(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
1485 {
1486     if (!renderObject.isProgress())
1487         return true;
1488
1489     GRefPtr<GtkStyleContext> parentContext = createStyleContext(ProgressBar);
1490     GRefPtr<GtkStyleContext> troughContext = createStyleContext(ProgressBarTrough, parentContext.get());
1491     GRefPtr<GtkStyleContext> context = createStyleContext(ProgressBarProgress, troughContext.get());
1492
1493     gtk_render_background(troughContext.get(), paintInfo.context().platformContext()->cr(), rect.x(), rect.y(), rect.width(), rect.height());
1494     gtk_render_frame(troughContext.get(), paintInfo.context().platformContext()->cr(), rect.x(), rect.y(), rect.width(), rect.height());
1495
1496     gtk_style_context_set_state(context.get(), static_cast<GtkStateFlags>(0));
1497
1498     GtkBorder padding;
1499     gtk_style_context_get_padding(context.get(), gtk_style_context_get_state(context.get()), &padding);
1500     IntRect progressRect(
1501         rect.x() + padding.left,
1502         rect.y() + padding.top,
1503         rect.width() - (padding.left + padding.right),
1504         rect.height() - (padding.top + padding.bottom));
1505     progressRect = RenderThemeGtk::calculateProgressRect(renderObject, progressRect);
1506
1507     if (!progressRect.isEmpty()) {
1508 #if GTK_CHECK_VERSION(3, 13, 7)
1509         gtk_render_background(context.get(), paintInfo.context().platformContext()->cr(), progressRect.x(), progressRect.y(), progressRect.width(), progressRect.height());
1510         gtk_render_frame(context.get(), paintInfo.context().platformContext()->cr(), progressRect.x(), progressRect.y(), progressRect.width(), progressRect.height());
1511 #else
1512         gtk_render_activity(context.get(), paintInfo.context().platformContext()->cr(), progressRect.x(), progressRect.y(), progressRect.width(), progressRect.height());
1513 #endif
1514     }
1515
1516     return false;
1517 }
1518 #endif // GTK_CHECK_VERSION(3, 20, 0)
1519
1520 #if GTK_CHECK_VERSION(3, 20, 0)
1521 RenderTheme::InnerSpinButtonLayout RenderThemeGtk::innerSpinButtonLayout(const RenderObject& renderObject) const
1522 {
1523     return renderObject.style().direction() == TextDirection::RTL ? InnerSpinButtonLayout::HorizontalUpLeft : InnerSpinButtonLayout::HorizontalUpRight;
1524 }
1525
1526 void RenderThemeGtk::adjustInnerSpinButtonStyle(StyleResolver&, RenderStyle& style, const Element*) const
1527 {
1528     style.setWidth(Length(spinButtonSize().width(), Fixed));
1529     style.setHeight(Length(spinButtonSize().height(), Fixed));
1530 }
1531
1532 bool RenderThemeGtk::paintInnerSpinButton(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
1533 {
1534     auto& spinButtonWidget = static_cast<RenderThemeSpinButton&>(RenderThemeWidget::getOrCreate(RenderThemeWidget::Type::SpinButton));
1535     auto spinButtonState = themePartStateFlags(*this, SpinButton, renderObject);
1536     spinButtonWidget.spinButton().setState(spinButtonState);
1537     spinButtonWidget.entry().setState(spinButtonState);
1538     auto& up = spinButtonWidget.up();
1539     up.setState(themePartStateFlags(*this, SpinButtonUpButton, renderObject));
1540     auto& down = spinButtonWidget.down();
1541     down.setState(themePartStateFlags(*this, SpinButtonDownButton, renderObject));
1542
1543     IntRect iconRect = rect;
1544     iconRect.setWidth(iconRect.width() / 2);
1545     if (renderObject.style().direction() == TextDirection::RTL)
1546         up.render(paintInfo.context().platformContext()->cr(), iconRect);
1547     else
1548         down.render(paintInfo.context().platformContext()->cr(), iconRect);
1549     iconRect.move(iconRect.width(), 0);
1550     if (renderObject.style().direction() == TextDirection::RTL)
1551         down.render(paintInfo.context().platformContext()->cr(), iconRect);
1552     else
1553         up.render(paintInfo.context().platformContext()->cr(), iconRect);
1554
1555     return false;
1556 }
1557 #else
1558 RenderTheme::InnerSpinButtonLayout RenderThemeGtk::innerSpinButtonLayout(const RenderObject&) const
1559 {
1560     return InnerSpinButtonLayout::Vertical;
1561 }
1562 static gint spinButtonArrowSize(GtkStyleContext* context)
1563 {
1564     PangoFontDescription* fontDescription;
1565     gtk_style_context_get(context, gtk_style_context_get_state(context), "font", &fontDescription, nullptr);
1566     gint fontSize = pango_font_description_get_size(fontDescription);
1567     gint arrowSize = std::max(PANGO_PIXELS(fontSize), minSpinButtonArrowSize);
1568     pango_font_description_free(fontDescription);
1569
1570     return arrowSize - arrowSize % 2; // Force even.
1571 }
1572
1573 void RenderThemeGtk::adjustInnerSpinButtonStyle(StyleResolver&, RenderStyle& style, const Element*) const
1574 {
1575     GRefPtr<GtkStyleContext> context = createStyleContext(SpinButton);
1576
1577     GtkBorder padding;
1578     gtk_style_context_get_padding(context.get(), gtk_style_context_get_state(context.get()), &padding);
1579
1580     int width = spinButtonArrowSize(context.get()) + padding.left + padding.right;
1581     style.setWidth(Length(width, Fixed));
1582     style.setMinWidth(Length(width, Fixed));
1583 }
1584
1585 static void paintSpinArrowButton(RenderTheme* theme, GtkStyleContext* parentContext, const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect, GtkArrowType arrowType)
1586 {
1587     ASSERT(arrowType == GTK_ARROW_UP || arrowType == GTK_ARROW_DOWN);
1588
1589     GRefPtr<GtkStyleContext> context = createStyleContext(arrowType == GTK_ARROW_UP ? SpinButtonUpButton : SpinButtonDownButton, parentContext);
1590     GtkTextDirection direction = gtk_style_context_get_direction(context.get());
1591     guint state = static_cast<guint>(gtk_style_context_get_state(context.get()));
1592     if (!(state & GTK_STATE_FLAG_INSENSITIVE)) {
1593         if (theme->isPressed(renderObject)) {
1594             if ((arrowType == GTK_ARROW_UP && theme->isSpinUpButtonPartPressed(renderObject))
1595                 || (arrowType == GTK_ARROW_DOWN && !theme->isSpinUpButtonPartPressed(renderObject)))
1596                 state |= GTK_STATE_FLAG_ACTIVE;
1597         } else if (theme->isHovered(renderObject)) {
1598             if ((arrowType == GTK_ARROW_UP && theme->isSpinUpButtonPartHovered(renderObject))
1599                 || (arrowType == GTK_ARROW_DOWN && !theme->isSpinUpButtonPartHovered(renderObject)))
1600                 state |= GTK_STATE_FLAG_PRELIGHT;
1601         }
1602     }
1603     gtk_style_context_set_state(context.get(), static_cast<GtkStateFlags>(state));
1604
1605     // Paint button.
1606     IntRect buttonRect(rect);
1607     guint junction = gtk_style_context_get_junction_sides(context.get());
1608     if (arrowType == GTK_ARROW_UP)
1609         junction |= GTK_JUNCTION_BOTTOM;
1610     else {
1611         junction |= GTK_JUNCTION_TOP;
1612         buttonRect.move(0, rect.height() / 2);
1613     }
1614     buttonRect.setHeight(rect.height() / 2);
1615     gtk_style_context_set_junction_sides(context.get(), static_cast<GtkJunctionSides>(junction));
1616
1617     gtk_render_background(context.get(), paintInfo.context().platformContext()->cr(), buttonRect.x(), buttonRect.y(), buttonRect.width(), buttonRect.height());
1618     gtk_render_frame(context.get(), paintInfo.context().platformContext()->cr(), buttonRect.x(), buttonRect.y(), buttonRect.width(), buttonRect.height());
1619
1620     // Paint arrow centered inside button.
1621     // This code is based on gtkspinbutton.c code.
1622     IntRect arrowRect;
1623     gdouble angle;
1624     if (arrowType == GTK_ARROW_UP) {
1625         angle = 0;
1626         arrowRect.setY(rect.y());
1627         arrowRect.setHeight(rect.height() / 2 - 2);
1628     } else {
1629         angle = G_PI;
1630         arrowRect.setY(rect.y() + buttonRect.y());
1631         arrowRect.setHeight(rect.height() - arrowRect.y() - 2);
1632     }
1633     arrowRect.setWidth(rect.width() - 3);
1634     if (direction == GTK_TEXT_DIR_LTR)
1635         arrowRect.setX(rect.x() + 1);
1636     else
1637         arrowRect.setX(rect.x() + 2);
1638
1639     gint width = arrowRect.width() / 2;
1640     width -= width % 2 - 1; // Force odd.
1641     gint height = (width + 1) / 2;
1642
1643     arrowRect.move((arrowRect.width() - width) / 2, (arrowRect.height() - height) / 2);
1644     gtk_render_arrow(context.get(), paintInfo.context().platformContext()->cr(), angle, arrowRect.x(), arrowRect.y(), width);
1645 }
1646
1647 bool RenderThemeGtk::paintInnerSpinButton(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
1648 {
1649     GRefPtr<GtkStyleContext> context = createStyleContext(SpinButton);
1650     gtk_style_context_set_direction(context.get(), gtkTextDirection(renderObject.style().direction()));
1651
1652     guint flags = 0;
1653     if (!isEnabled(renderObject) || isReadOnlyControl(renderObject))
1654         flags |= GTK_STATE_FLAG_INSENSITIVE;
1655     else if (isFocused(renderObject))
1656         flags |= GTK_STATE_FLAG_FOCUSED;
1657     gtk_style_context_set_state(context.get(), static_cast<GtkStateFlags>(flags));
1658
1659     paintSpinArrowButton(this, context.get(), renderObject, paintInfo, rect, GTK_ARROW_UP);
1660     paintSpinArrowButton(this, context.get(), renderObject, paintInfo, rect, GTK_ARROW_DOWN);
1661
1662     return false;
1663 }
1664 #endif // GTK_CHECK_VERSION(3, 20, 0)
1665
1666 Seconds RenderThemeGtk::caretBlinkInterval() const
1667 {
1668     GtkSettings* settings = gtk_settings_get_default();
1669
1670     gboolean shouldBlink;
1671     gint time;
1672
1673     g_object_get(settings, "gtk-cursor-blink", &shouldBlink, "gtk-cursor-blink-time", &time, nullptr);
1674
1675     if (!shouldBlink)
1676         return 0_s;
1677
1678     return 500_us * time;
1679 }
1680
1681 enum StyleColorType { StyleColorBackground, StyleColorForeground };
1682
1683 #if GTK_CHECK_VERSION(3, 20, 0)
1684 static Color styleColor(RenderThemePart themePart, GtkStateFlags state, StyleColorType colorType)
1685 {
1686     RenderThemeGadget* gadget = nullptr;
1687     switch (themePart) {
1688     default:
1689         ASSERT_NOT_REACHED();
1690         FALLTHROUGH;
1691     case Entry:
1692         gadget = &static_cast<RenderThemeEntry&>(RenderThemeWidget::getOrCreate(RenderThemeWidget::Type::Entry)).entry();
1693         break;
1694     case EntrySelection:
1695         gadget = static_cast<RenderThemeEntry&>(RenderThemeWidget::getOrCreate(RenderThemeWidget::Type::SelectedEntry)).selection();
1696         break;
1697     case ListBox:
1698         gadget = &static_cast<RenderThemeListView&>(RenderThemeWidget::getOrCreate(RenderThemeWidget::Type::ListView)).treeview();
1699         break;
1700     case Button:
1701         gadget = &static_cast<RenderThemeButton&>(RenderThemeWidget::getOrCreate(RenderThemeWidget::Type::Button)).button();
1702         break;
1703     case Window:
1704         gadget = &static_cast<RenderThemeWindow&>(RenderThemeWidget::getOrCreate(RenderThemeWidget::Type::Window)).window();
1705         break;
1706     }
1707
1708     ASSERT(gadget);
1709     gadget->setState(state);
1710     return colorType == StyleColorBackground ? gadget->backgroundColor() : gadget->color();
1711 }
1712 #else
1713 static Color styleColor(RenderThemePart themePart, GtkStateFlags state, StyleColorType colorType)
1714 {
1715     GRefPtr<GtkStyleContext> context = createStyleContext(themePart);
1716     gtk_style_context_set_state(context.get(), state);
1717
1718     GdkRGBA gdkRGBAColor;
1719     if (colorType == StyleColorBackground)
1720         gtk_style_context_get_background_color(context.get(), state, &gdkRGBAColor);
1721     else
1722         gtk_style_context_get_color(context.get(), state, &gdkRGBAColor);
1723     return gdkRGBAColor;
1724 }
1725 #endif // GTK_CHECK_VERSION(3, 20, 0)
1726
1727 Color RenderThemeGtk::platformActiveSelectionBackgroundColor(OptionSet<StyleColor::Options>) const
1728 {
1729     return styleColor(EntrySelection, static_cast<GtkStateFlags>(GTK_STATE_FLAG_SELECTED | GTK_STATE_FLAG_FOCUSED), StyleColorBackground);
1730 }
1731
1732 Color RenderThemeGtk::platformInactiveSelectionBackgroundColor(OptionSet<StyleColor::Options>) const
1733 {
1734     return styleColor(EntrySelection, GTK_STATE_FLAG_SELECTED, StyleColorBackground);
1735 }
1736
1737 Color RenderThemeGtk::platformActiveSelectionForegroundColor(OptionSet<StyleColor::Options>) const
1738 {
1739     return styleColor(EntrySelection, static_cast<GtkStateFlags>(GTK_STATE_FLAG_SELECTED | GTK_STATE_FLAG_FOCUSED), StyleColorForeground);
1740 }
1741
1742 Color RenderThemeGtk::platformInactiveSelectionForegroundColor(OptionSet<StyleColor::Options>) const
1743 {
1744     return styleColor(EntrySelection, GTK_STATE_FLAG_SELECTED, StyleColorForeground);
1745 }
1746
1747 Color RenderThemeGtk::platformActiveListBoxSelectionBackgroundColor(OptionSet<StyleColor::Options>) const
1748 {
1749     return styleColor(ListBox, static_cast<GtkStateFlags>(GTK_STATE_FLAG_SELECTED | GTK_STATE_FLAG_FOCUSED), StyleColorBackground);
1750 }
1751
1752 Color RenderThemeGtk::platformInactiveListBoxSelectionBackgroundColor(OptionSet<StyleColor::Options>) const
1753 {
1754     return styleColor(ListBox, GTK_STATE_FLAG_SELECTED, StyleColorBackground);
1755 }
1756
1757 Color RenderThemeGtk::platformActiveListBoxSelectionForegroundColor(OptionSet<StyleColor::Options>) const
1758 {
1759     return styleColor(ListBox, static_cast<GtkStateFlags>(GTK_STATE_FLAG_SELECTED | GTK_STATE_FLAG_FOCUSED), StyleColorForeground);
1760 }
1761
1762 Color RenderThemeGtk::platformInactiveListBoxSelectionForegroundColor(OptionSet<StyleColor::Options>) const
1763 {
1764     return styleColor(ListBox, GTK_STATE_FLAG_SELECTED, StyleColorForeground);
1765 }
1766
1767 Color RenderThemeGtk::disabledTextColor(const Color&, const Color&) const
1768 {
1769     return styleColor(Entry, GTK_STATE_FLAG_INSENSITIVE, StyleColorForeground);
1770 }
1771
1772 Color RenderThemeGtk::systemColor(CSSValueID cssValueId, OptionSet<StyleColor::Options> options) const
1773 {
1774     switch (cssValueId) {
1775     case CSSValueButtontext:
1776         return styleColor(Button, GTK_STATE_FLAG_ACTIVE, StyleColorForeground);
1777     case CSSValueCaptiontext:
1778         return styleColor(Entry, GTK_STATE_FLAG_ACTIVE, StyleColorForeground);
1779     case CSSValueText:
1780         return styleColor(Entry, GTK_STATE_FLAG_ACTIVE, StyleColorForeground);
1781     case CSSValueGraytext:
1782         return styleColor(Entry, GTK_STATE_FLAG_INSENSITIVE, StyleColorForeground);
1783 #if GTK_CHECK_VERSION(3, 20, 0)
1784     case CSSValueWindowframe:
1785         return styleColor(Window, GTK_STATE_FLAG_ACTIVE, StyleColorBackground);
1786 #endif
1787     default:
1788         return RenderTheme::systemColor(cssValueId, options);
1789     }
1790 }
1791
1792 void RenderThemeGtk::platformColorsDidChange()
1793 {
1794     RenderTheme::platformColorsDidChange();
1795 }
1796
1797 #if ENABLE(VIDEO)
1798 String RenderThemeGtk::extraMediaControlsStyleSheet()
1799 {
1800     return String(mediaControlsGtkUserAgentStyleSheet, sizeof(mediaControlsGtkUserAgentStyleSheet));
1801 }
1802
1803 #if ENABLE(FULLSCREEN_API)
1804 String RenderThemeGtk::extraFullScreenStyleSheet()
1805 {
1806     return String();
1807 }
1808 #endif
1809
1810 #if GTK_CHECK_VERSION(3, 20, 0)
1811 bool RenderThemeGtk::paintMediaButton(const RenderObject& renderObject, GraphicsContext& graphicsContext, const IntRect& rect, const char* iconName)
1812 {
1813     auto& iconWidget = static_cast<RenderThemeIcon&>(RenderThemeWidget::getOrCreate(RenderThemeWidget::Type::Icon));
1814     auto& icon = static_cast<RenderThemeIconGadget&>(iconWidget.icon());
1815     icon.setState(themePartStateFlags(*this, MediaButton, renderObject));
1816     icon.setIconSize(RenderThemeIconGadget::IconSizeGtk::Menu);
1817     icon.setIconName(iconName);
1818     return !icon.render(graphicsContext.platformContext()->cr(), rect);
1819 }
1820 #else
1821 bool RenderThemeGtk::paintMediaButton(const RenderObject& renderObject, GraphicsContext& graphicsContext, const IntRect& rect, const char* iconName)
1822 {
1823     GRefPtr<GtkStyleContext> context = createStyleContext(MediaButton);
1824     gtk_style_context_set_direction(context.get(), gtkTextDirection(renderObject.style().direction()));
1825     gtk_style_context_set_state(context.get(), gtkIconStateFlags(this, renderObject));
1826     static const unsigned mediaIconSize = 16;
1827     IntRect iconRect(rect.x() + (rect.width() - mediaIconSize) / 2, rect.y() + (rect.height() - mediaIconSize) / 2, mediaIconSize, mediaIconSize);
1828     return !paintIcon(context.get(), graphicsContext, iconRect, iconName);
1829 }
1830 #endif // GTK_CHECK_VERSION(3, 20, 0)
1831
1832 bool RenderThemeGtk::hasOwnDisabledStateHandlingFor(ControlPart part) const
1833 {
1834     return (part != MediaMuteButtonPart);
1835 }
1836
1837 bool RenderThemeGtk::paintMediaFullscreenButton(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
1838 {
1839     return paintMediaButton(renderObject, paintInfo.context(), rect, "view-fullscreen-symbolic");
1840 }
1841
1842 bool RenderThemeGtk::paintMediaMuteButton(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
1843 {
1844     Node* node = renderObject.node();
1845     if (!node)
1846         return true;
1847     Node* mediaNode = node->shadowHost();
1848     if (!is<HTMLMediaElement>(mediaNode))
1849         return true;
1850
1851     HTMLMediaElement* mediaElement = downcast<HTMLMediaElement>(mediaNode);
1852     return paintMediaButton(renderObject, paintInfo.context(), rect, mediaElement->muted() ? "audio-volume-muted-symbolic" : "audio-volume-high-symbolic");
1853 }
1854
1855 bool RenderThemeGtk::paintMediaPlayButton(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
1856 {
1857     Node* node = renderObject.node();
1858     if (!node)
1859         return true;
1860     if (!nodeHasPseudo(*node, "-webkit-media-controls-play-button"))
1861         return true;
1862
1863     return paintMediaButton(renderObject, paintInfo.context(), rect, nodeHasClass(node, "paused") ? "media-playback-start-symbolic" : "media-playback-pause-symbolic");
1864 }
1865
1866 bool RenderThemeGtk::paintMediaSeekBackButton(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
1867 {
1868     return paintMediaButton(renderObject, paintInfo.context(), rect, "media-seek-backward-symbolic");
1869 }
1870
1871 bool RenderThemeGtk::paintMediaSeekForwardButton(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
1872 {
1873     return paintMediaButton(renderObject, paintInfo.context(), rect, "media-seek-forward-symbolic");
1874 }
1875
1876 #if ENABLE(VIDEO_TRACK)
1877 bool RenderThemeGtk::paintMediaToggleClosedCaptionsButton(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
1878 {
1879     return paintMediaButton(renderObject, paintInfo.context(), rect, "media-view-subtitles-symbolic");
1880 }
1881 #endif
1882
1883 static FloatRoundedRect::Radii borderRadiiFromStyle(const RenderStyle& style)
1884 {
1885     return FloatRoundedRect::Radii(
1886         IntSize(style.borderTopLeftRadius().width.intValue(), style.borderTopLeftRadius().height.intValue()),
1887         IntSize(style.borderTopRightRadius().width.intValue(), style.borderTopRightRadius().height.intValue()),
1888         IntSize(style.borderBottomLeftRadius().width.intValue(), style.borderBottomLeftRadius().height.intValue()),
1889         IntSize(style.borderBottomRightRadius().width.intValue(), style.borderBottomRightRadius().height.intValue()));
1890 }
1891
1892 bool RenderThemeGtk::paintMediaSliderTrack(const RenderObject& o, const PaintInfo& paintInfo, const IntRect& r)
1893 {
1894     auto mediaElement = parentMediaElement(o);
1895     if (!mediaElement)
1896         return true;
1897
1898     GraphicsContext& context = paintInfo.context();
1899     context.save();
1900     context.setStrokeStyle(NoStroke);
1901
1902     float mediaDuration = mediaElement->duration();
1903     float totalTrackWidth = r.width();
1904     auto& style = o.style();
1905     RefPtr<TimeRanges> timeRanges = mediaElement->buffered();
1906     for (unsigned index = 0; index < timeRanges->length(); ++index) {
1907         float start = timeRanges->start(index).releaseReturnValue();
1908         float end = timeRanges->end(index).releaseReturnValue();
1909         float startRatio = start / mediaDuration;
1910         float lengthRatio = (end - start) / mediaDuration;
1911         if (!lengthRatio)
1912             continue;
1913
1914         IntRect rangeRect(r);
1915         rangeRect.setWidth(lengthRatio * totalTrackWidth);
1916         if (index)
1917             rangeRect.move(startRatio * totalTrackWidth, 0);
1918         context.fillRoundedRect(FloatRoundedRect(rangeRect, borderRadiiFromStyle(style)), style.visitedDependentColor(CSSPropertyColor));
1919     }
1920
1921     context.restore();
1922     return false;
1923 }
1924
1925 bool RenderThemeGtk::paintMediaSliderThumb(const RenderObject& o, const PaintInfo& paintInfo, const IntRect& r)
1926 {
1927     auto& style = o.style();
1928     paintInfo.context().fillRoundedRect(FloatRoundedRect(r, borderRadiiFromStyle(style)), style.visitedDependentColor(CSSPropertyColor));
1929     return false;
1930 }
1931
1932 bool RenderThemeGtk::paintMediaVolumeSliderTrack(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
1933 {
1934     auto mediaElement = parentMediaElement(renderObject);
1935     if (!mediaElement)
1936         return true;
1937
1938     float volume = mediaElement->muted() ? 0.0f : mediaElement->volume();
1939     if (!volume)
1940         return true;
1941
1942     GraphicsContext& context = paintInfo.context();
1943     context.save();
1944     context.setStrokeStyle(NoStroke);
1945
1946     int rectHeight = rect.height();
1947     float trackHeight = rectHeight * volume;
1948     auto& style = renderObject.style();
1949     IntRect volumeRect(rect);
1950     volumeRect.move(0, rectHeight - trackHeight);
1951     volumeRect.setHeight(ceil(trackHeight));
1952
1953     context.fillRoundedRect(FloatRoundedRect(volumeRect, borderRadiiFromStyle(style)), style.visitedDependentColor(CSSPropertyColor));
1954     context.restore();
1955
1956     return false;
1957 }
1958
1959 bool RenderThemeGtk::paintMediaVolumeSliderThumb(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
1960 {
1961     return paintMediaSliderThumb(renderObject, paintInfo, rect);
1962 }
1963
1964 String RenderThemeGtk::formatMediaControlsCurrentTime(float currentTime, float duration) const
1965 {
1966     return formatMediaControlsTime(currentTime) + " / " + formatMediaControlsTime(duration);
1967 }
1968
1969 bool RenderThemeGtk::paintMediaCurrentTime(const RenderObject&, const PaintInfo&, const IntRect&)
1970 {
1971     return false;
1972 }
1973 #endif
1974
1975 void RenderThemeGtk::adjustProgressBarStyle(StyleResolver&, RenderStyle& style, const Element*) const
1976 {
1977     style.setBoxShadow(nullptr);
1978 }
1979
1980 // These values have been copied from RenderThemeChromiumSkia.cpp
1981 static const int progressActivityBlocks = 5;
1982 static const int progressAnimationFrames = 10;
1983 static const Seconds progressAnimationInterval { 125_ms };
1984 Seconds RenderThemeGtk::animationRepeatIntervalForProgressBar(RenderProgress&) const
1985 {
1986     return progressAnimationInterval;
1987 }
1988
1989 Seconds RenderThemeGtk::animationDurationForProgressBar(RenderProgress&) const
1990 {
1991     return progressAnimationInterval * progressAnimationFrames * 2; // "2" for back and forth;
1992 }
1993
1994 IntRect RenderThemeGtk::calculateProgressRect(const RenderObject& renderObject, const IntRect& fullBarRect)
1995 {
1996     IntRect progressRect(fullBarRect);
1997     const auto& renderProgress = downcast<RenderProgress>(renderObject);
1998     if (renderProgress.isDeterminate()) {
1999         int progressWidth = progressRect.width() * renderProgress.position();
2000         if (renderObject.style().direction() == TextDirection::RTL)
2001             progressRect.setX(progressRect.x() + progressRect.width() - progressWidth);
2002         progressRect.setWidth(progressWidth);
2003         return progressRect;
2004     }
2005
2006     double animationProgress = renderProgress.animationProgress();
2007
2008     // Never let the progress rect shrink smaller than 2 pixels.
2009     int newWidth = std::max(2, progressRect.width() / progressActivityBlocks);
2010     int movableWidth = progressRect.width() - newWidth;
2011     progressRect.setWidth(newWidth);
2012
2013     // We want the first 0.5 units of the animation progress to represent the
2014     // forward motion and the second 0.5 units to represent the backward motion,
2015     // thus we multiply by two here to get the full sweep of the progress bar with
2016     // each direction.
2017     if (animationProgress < 0.5)
2018         progressRect.setX(progressRect.x() + (animationProgress * 2 * movableWidth));
2019     else
2020         progressRect.setX(progressRect.x() + ((1.0 - animationProgress) * 2 * movableWidth));
2021     return progressRect;
2022 }
2023
2024 String RenderThemeGtk::fileListNameForWidth(const FileList* fileList, const FontCascade& font, int width, bool multipleFilesAllowed) const
2025 {
2026     if (width <= 0)
2027         return String();
2028
2029     if (fileList->length() > 1)
2030         return StringTruncator::rightTruncate(multipleFileUploadText(fileList->length()), width, font);
2031
2032     String string;
2033     if (fileList->length())
2034         string = FileSystem::pathGetFileName(fileList->item(0)->path());
2035     else if (multipleFilesAllowed)
2036         string = fileButtonNoFilesSelectedLabel();
2037     else
2038         string = fileButtonNoFileSelectedLabel();
2039
2040     return StringTruncator::centerTruncate(string, width, font);
2041 }
2042
2043 #if ENABLE(VIDEO)
2044 String RenderThemeGtk::mediaControlsScript()
2045 {
2046     StringBuilder scriptBuilder;
2047     scriptBuilder.append(mediaControlsLocalizedStringsJavaScript, sizeof(mediaControlsLocalizedStringsJavaScript));
2048     scriptBuilder.append(mediaControlsBaseJavaScript, sizeof(mediaControlsBaseJavaScript));
2049     scriptBuilder.append(mediaControlsGtkJavaScript, sizeof(mediaControlsGtkJavaScript));
2050     return scriptBuilder.toString();
2051 }
2052 #endif // ENABLE(VIDEO)
2053
2054 #endif // GTK_API_VERSION_2
2055 }