RenderTheme does not need to be per-page
[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 "FileSystem.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 "RenderThemeGadget.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/glib/GRefPtr.h>
58 #include <wtf/glib/GUniquePtr.h>
59 #include <wtf/text/CString.h>
60 #include <wtf/text/StringBuilder.h>
61
62 namespace WebCore {
63
64 Ref<RenderTheme> RenderThemeGtk::create()
65 {
66     return adoptRef(*new RenderThemeGtk());
67 }
68
69 RenderTheme& RenderTheme::singleton()
70 {
71     static NeverDestroyed<Ref<RenderTheme>> theme(RenderThemeGtk::create());
72     return theme.get();
73 }
74
75 static double getScreenDPI()
76 {
77     // FIXME: Really this should be the widget's screen.
78     GdkScreen* screen = gdk_screen_get_default();
79     if (!screen)
80         return 96; // Default to 96 DPI.
81
82     float dpi = gdk_screen_get_resolution(screen);
83     if (dpi <= 0)
84         return 96;
85     return dpi;
86 }
87
88 void RenderThemeGtk::updateCachedSystemFontDescription(CSSValueID, FontCascadeDescription& fontDescription) const
89 {
90     GtkSettings* settings = gtk_settings_get_default();
91     if (!settings)
92         return;
93
94     // This will be a font selection string like "Sans 10" so we cannot use it as the family name.
95     GUniqueOutPtr<gchar> fontName;
96     g_object_get(settings, "gtk-font-name", &fontName.outPtr(), nullptr);
97     if (!fontName || !fontName.get()[0])
98         return;
99
100     PangoFontDescription* pangoDescription = pango_font_description_from_string(fontName.get());
101     if (!pangoDescription)
102         return;
103
104     fontDescription.setOneFamily(pango_font_description_get_family(pangoDescription));
105
106     int size = pango_font_description_get_size(pangoDescription) / PANGO_SCALE;
107     // If the size of the font is in points, we need to convert it to pixels.
108     if (!pango_font_description_get_size_is_absolute(pangoDescription))
109         size = size * (getScreenDPI() / 72.0);
110
111     fontDescription.setSpecifiedSize(size);
112     fontDescription.setIsAbsoluteSize(true);
113     fontDescription.setWeight(normalWeightValue());
114     fontDescription.setItalic(FontSelectionValue());
115     pango_font_description_free(pangoDescription);
116 }
117
118 #if ENABLE(DATALIST_ELEMENT)
119 IntSize RenderThemeGtk::sliderTickSize() const
120 {
121     // FIXME: We need to set this to the size of one tick mark.
122     return IntSize(0, 0);
123 }
124
125 int RenderThemeGtk::sliderTickOffsetFromTrackCenter() const
126 {
127     // FIXME: We need to set this to the position of the tick marks.
128     return 0;
129 }
130 #endif
131
132 #ifndef GTK_API_VERSION_2
133
134 static void themeChangedCallback()
135 {
136     Page::updateStyleForAllPagesAfterGlobalChangeInEnvironment();
137 }
138
139 RenderThemeGtk::RenderThemeGtk()
140 {
141     static bool themeMonitorInitialized = false;
142     if (!themeMonitorInitialized) {
143         GtkSettings* settings = gtk_settings_get_default();
144         g_signal_connect(settings, "notify::gtk-theme-name", G_CALLBACK(themeChangedCallback), nullptr);
145         g_signal_connect(settings, "notify::gtk-color-scheme", G_CALLBACK(themeChangedCallback), nullptr);
146         themeMonitorInitialized = true;
147     }
148 }
149
150 enum RenderThemePart {
151     Entry,
152     EntrySelection,
153     EntryIconLeft,
154     EntryIconRight,
155     Button,
156     CheckButton,
157     RadioButton,
158     ComboBox,
159     ComboBoxButton,
160     ComboBoxArrow,
161     Scale,
162     ScaleTrough,
163     ScaleSlider,
164     ProgressBar,
165     ProgressBarTrough,
166     ProgressBarProgress,
167     ListBox,
168     SpinButton,
169     SpinButtonUpButton,
170     SpinButtonDownButton,
171 #if ENABLE(VIDEO)
172     MediaButton,
173 #endif
174 };
175
176 #if !GTK_CHECK_VERSION(3, 20, 0)
177 // This is the default value defined by GTK+, where it was defined as MIN_ARROW_SIZE in gtkarrow.c.
178 static const int minArrowSize = 15;
179 // This is the default value defined by GTK+, where it was defined as MIN_ARROW_WIDTH in gtkspinbutton.c.
180 static const int minSpinButtonArrowSize = 6;
181
182 static GRefPtr<GtkStyleContext> createStyleContext(RenderThemePart themePart, GtkStyleContext* parent = nullptr)
183 {
184     GRefPtr<GtkWidgetPath> path = adoptGRef(parent ? gtk_widget_path_copy(gtk_style_context_get_path(parent)) : gtk_widget_path_new());
185
186     switch (themePart) {
187     case Entry:
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 static bool nodeHasPseudo(Node& node, const char* pseudo)
302 {
303     return is<Element>(node) && downcast<Element>(node).pseudo() == pseudo;
304 }
305
306 static bool nodeHasClass(Node* node, const char* className)
307 {
308     if (!is<Element>(*node))
309         return false;
310
311     Element& element = downcast<Element>(*node);
312
313     if (!element.hasClass())
314         return false;
315
316     return element.classNames().contains(className);
317 }
318
319 RenderThemeGtk::~RenderThemeGtk()
320 {
321 }
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 RTL:
369         stateFlags |= GTK_STATE_FLAG_DIR_RTL;
370         break;
371     case 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 RTL:
429         return GTK_TEXT_DIR_RTL;
430     case 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     RenderThemeGadget::Info info = {
531         .type = RenderThemeGadget::Type::Generic,
532         .name = themePart == CheckButton ? "checkbutton" : "radiobutton",
533         .state = GTK_STATE_FLAG_NORMAL,
534         .classList = { }
535     };
536     auto parentGadget = RenderThemeGadget::create(info);
537     if (themePart == CheckButton) {
538         info.type = RenderThemeGadget::Type::Check;
539         info.name = "check";
540     } else {
541         info.type = RenderThemeGadget::Type::Radio;
542         info.name = "radio";
543     }
544     auto gadget = RenderThemeToggleGadget::create(info);
545     IntSize preferredSize = parentGadget->preferredSize();
546     preferredSize = preferredSize.expandedTo(gadget->preferredSize());
547
548     if (style.width().isIntrinsicOrAuto())
549         style.setWidth(Length(preferredSize.width(), Fixed));
550
551     if (style.height().isAuto())
552         style.setHeight(Length(preferredSize.height(), Fixed));
553 }
554
555 static void paintToggle(const RenderThemeGtk* theme, RenderThemePart themePart, const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& fullRect)
556 {
557     ASSERT(themePart == CheckButton || themePart == RadioButton);
558
559     RenderThemeGadget::Info parentInfo = {
560         .type = RenderThemeGadget::Type::Generic,
561         .name = themePart == CheckButton ? "checkbutton" : "radiobutton",
562         .state = themePartStateFlags(*theme, themePart, renderObject),
563         .classList = { "text-button" }
564     };
565     auto parentGadget = RenderThemeGadget::create(parentInfo);
566     RenderThemeGadget::Info info;
567     info.state = parentInfo.state;
568     if (themePart == CheckButton) {
569         info.type = RenderThemeGadget::Type::Check;
570         info.name = "check";
571     } else {
572         info.type = RenderThemeGadget::Type::Radio;
573         info.name = "radio";
574     }
575     auto gadget = RenderThemeGadget::create(info, parentGadget.get());
576
577     FloatRect rect = fullRect;
578     // Some themes do not render large toggle buttons properly, so we simply
579     // shrink the rectangle back down to the default size and then center it
580     // in the full toggle button region. The reason for not simply forcing toggle
581     // buttons to be a smaller size is that we don't want to break site layouts.
582     IntSize preferredSize = parentGadget->preferredSize();
583     preferredSize = preferredSize.expandedTo(gadget->preferredSize());
584     shrinkToMinimumSizeAndCenterRectangle(rect, preferredSize);
585     parentGadget->render(paintInfo.context().platformContext()->cr(), rect);
586     gadget->render(paintInfo.context().platformContext()->cr(), rect);
587
588     if (theme->isFocused(renderObject))
589         parentGadget->renderFocus(paintInfo.context().platformContext()->cr(), rect);
590 }
591 #else
592 static void setToggleSize(RenderThemePart themePart, RenderStyle& style)
593 {
594     // The width and height are both specified, so we shouldn't change them.
595     if (!style.width().isIntrinsicOrAuto() && !style.height().isAuto())
596         return;
597
598     GRefPtr<GtkStyleContext> context = createStyleContext(themePart);
599     // Other ports hard-code this to 13. GTK+ users tend to demand the native look.
600     gint indicatorSize;
601     gtk_style_context_get_style(context.get(), "indicator-size", &indicatorSize, nullptr);
602
603     if (style.width().isIntrinsicOrAuto())
604         style.setWidth(Length(indicatorSize, Fixed));
605
606     if (style.height().isAuto())
607         style.setHeight(Length(indicatorSize, Fixed));
608 }
609
610 static void paintToggle(const RenderThemeGtk* theme, RenderThemePart themePart, const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& fullRect)
611 {
612     GRefPtr<GtkStyleContext> context = createStyleContext(themePart);
613     gtk_style_context_set_direction(context.get(), static_cast<GtkTextDirection>(gtkTextDirection(renderObject.style().direction())));
614
615     unsigned flags = 0;
616     if (!theme->isEnabled(renderObject))
617         flags |= GTK_STATE_FLAG_INSENSITIVE;
618     else if (theme->isHovered(renderObject))
619         flags |= GTK_STATE_FLAG_PRELIGHT;
620     if (theme->isIndeterminate(renderObject))
621         flags |= GTK_STATE_FLAG_INCONSISTENT;
622     else if (theme->isChecked(renderObject))
623 #if GTK_CHECK_VERSION(3, 13, 7)
624         flags |= GTK_STATE_FLAG_CHECKED;
625 #else
626         flags |= GTK_STATE_FLAG_ACTIVE;
627 #endif
628     if (theme->isPressed(renderObject))
629         flags |= GTK_STATE_FLAG_SELECTED;
630     gtk_style_context_set_state(context.get(), static_cast<GtkStateFlags>(flags));
631
632     // Some themes do not render large toggle buttons properly, so we simply
633     // shrink the rectangle back down to the default size and then center it
634     // in the full toggle button region. The reason for not simply forcing toggle
635     // buttons to be a smaller size is that we don't want to break site layouts.
636     FloatRect rect(fullRect);
637     gint indicatorSize;
638     gtk_style_context_get_style(context.get(), "indicator-size", &indicatorSize, nullptr);
639     IntSize minSize(indicatorSize, indicatorSize);
640     shrinkToMinimumSizeAndCenterRectangle(rect, minSize);
641
642     gtk_render_background(context.get(), paintInfo.context().platformContext()->cr(), rect.x(), rect.y(), rect.width(), rect.height());
643     gtk_render_frame(context.get(), paintInfo.context().platformContext()->cr(), rect.x(), rect.y(), rect.width(), rect.height());
644
645     if (themePart == CheckButton)
646         gtk_render_check(context.get(), paintInfo.context().platformContext()->cr(), rect.x(), rect.y(), rect.width(), rect.height());
647     else
648         gtk_render_option(context.get(), paintInfo.context().platformContext()->cr(), rect.x(), rect.y(), rect.width(), rect.height());
649
650     if (theme->isFocused(renderObject)) {
651         IntRect indicatorRect(rect);
652         gint indicatorSpacing;
653         gtk_style_context_get_style(context.get(), "indicator-spacing", &indicatorSpacing, nullptr);
654         indicatorRect.inflate(indicatorSpacing);
655         gtk_render_focus(context.get(), paintInfo.context().platformContext()->cr(), indicatorRect.x(), indicatorRect.y(),
656             indicatorRect.width(), indicatorRect.height());
657     }
658 }
659 #endif // GTK_CHECK_VERSION(3, 20, 0)
660
661 void RenderThemeGtk::setCheckboxSize(RenderStyle& style) const
662 {
663     setToggleSize(CheckButton, style);
664 }
665
666 bool RenderThemeGtk::paintCheckbox(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
667 {
668     paintToggle(this, CheckButton, renderObject, paintInfo, rect);
669     return false;
670 }
671
672 void RenderThemeGtk::setRadioSize(RenderStyle& style) const
673 {
674     setToggleSize(RadioButton, style);
675 }
676
677 bool RenderThemeGtk::paintRadio(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
678 {
679     paintToggle(this, RadioButton, renderObject, paintInfo, rect);
680     return false;
681 }
682
683 #if GTK_CHECK_VERSION(3, 20, 0)
684 bool RenderThemeGtk::paintButton(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
685 {
686     RenderThemeGadget::Info info = { RenderThemeGadget::Type::Generic, "button", themePartStateFlags(*this, Button, renderObject), { "text-button" } };
687     if (isDefault(renderObject))
688         info.classList.append("default");
689     auto gadget = RenderThemeGadget::create(info);
690     gadget->render(paintInfo.context().platformContext()->cr(), rect);
691     if (isFocused(renderObject))
692         gadget->renderFocus(paintInfo.context().platformContext()->cr(), rect);
693     return false;
694 }
695 #else
696 static void renderButton(RenderTheme* theme, GtkStyleContext* context, const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
697 {
698     IntRect buttonRect(rect);
699
700     guint flags = 0;
701     if (!theme->isEnabled(renderObject))
702         flags |= GTK_STATE_FLAG_INSENSITIVE;
703     else if (theme->isHovered(renderObject))
704         flags |= GTK_STATE_FLAG_PRELIGHT;
705     if (theme->isPressed(renderObject))
706         flags |= GTK_STATE_FLAG_ACTIVE;
707     gtk_style_context_set_state(context, static_cast<GtkStateFlags>(flags));
708
709     if (theme->isDefault(renderObject)) {
710         GtkBorder* borderPtr = 0;
711         GtkBorder border = { 1, 1, 1, 1 };
712
713         gtk_style_context_get_style(context, "default-border", &borderPtr, nullptr);
714         if (borderPtr) {
715             border = *borderPtr;
716             gtk_border_free(borderPtr);
717         }
718
719         buttonRect.move(border.left, border.top);
720         buttonRect.setWidth(buttonRect.width() - (border.left + border.right));
721         buttonRect.setHeight(buttonRect.height() - (border.top + border.bottom));
722
723         gtk_style_context_add_class(context, GTK_STYLE_CLASS_DEFAULT);
724     }
725
726     gtk_render_background(context, paintInfo.context().platformContext()->cr(), buttonRect.x(), buttonRect.y(), buttonRect.width(), buttonRect.height());
727     gtk_render_frame(context, paintInfo.context().platformContext()->cr(), buttonRect.x(), buttonRect.y(), buttonRect.width(), buttonRect.height());
728
729     if (theme->isFocused(renderObject)) {
730         gint focusWidth, focusPad;
731         gboolean displaceFocus, interiorFocus;
732         gtk_style_context_get_style(
733             context,
734             "focus-line-width", &focusWidth,
735             "focus-padding", &focusPad,
736             "interior-focus", &interiorFocus,
737             "displace-focus", &displaceFocus,
738             nullptr);
739
740         if (interiorFocus) {
741             GtkBorder borderWidth;
742             gtk_style_context_get_border(context, gtk_style_context_get_state(context), &borderWidth);
743
744             buttonRect = IntRect(
745                 buttonRect.x() + borderWidth.left + focusPad,
746                 buttonRect.y() + borderWidth.top + focusPad,
747                 buttonRect.width() - (2 * focusPad + borderWidth.left + borderWidth.right),
748                 buttonRect.height() - (2 * focusPad + borderWidth.top + borderWidth.bottom));
749         } else
750             buttonRect.inflate(focusWidth + focusPad);
751
752         if (displaceFocus && theme->isPressed(renderObject)) {
753             gint childDisplacementX;
754             gint childDisplacementY;
755             gtk_style_context_get_style(context, "child-displacement-x", &childDisplacementX, "child-displacement-y", &childDisplacementY, nullptr);
756             buttonRect.move(childDisplacementX, childDisplacementY);
757         }
758
759         gtk_render_focus(context, paintInfo.context().platformContext()->cr(), buttonRect.x(), buttonRect.y(), buttonRect.width(), buttonRect.height());
760     }
761 }
762 bool RenderThemeGtk::paintButton(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
763 {
764     GRefPtr<GtkStyleContext> context = createStyleContext(Button);
765     gtk_style_context_set_direction(context.get(), static_cast<GtkTextDirection>(gtkTextDirection(renderObject.style().direction())));
766     renderButton(this, context.get(), renderObject, paintInfo, rect);
767     return false;
768 }
769 #endif // GTK_CHECK_VERSION(3, 20, 0)
770
771 static Color menuListColor(const Element* element)
772 {
773 #if GTK_CHECK_VERSION(3, 20, 0)
774     RenderThemeGadget::Info info { RenderThemeGadget::Type::Generic, "combobox", element->isDisabledFormControl() ? GTK_STATE_FLAG_INSENSITIVE : GTK_STATE_FLAG_NORMAL, { } };
775     auto comboGadget = RenderThemeGadget::create(info);
776     Vector<RenderThemeGadget::Info> children {
777         { RenderThemeGadget::Type::Generic, "button", info.state, { "combo" } }
778     };
779     info.name = "box";
780     info.classList = { "horizontal", "linked" };
781     return RenderThemeBoxGadget(info, GTK_ORIENTATION_HORIZONTAL, children, comboGadget.get()).child(0)->color();
782 #else
783     GRefPtr<GtkStyleContext> parentStyleContext = createStyleContext(ComboBox);
784     GRefPtr<GtkStyleContext> buttonStyleContext = createStyleContext(ComboBoxButton, parentStyleContext.get());
785     gtk_style_context_set_state(buttonStyleContext.get(), element->isDisabledFormControl() ? GTK_STATE_FLAG_INSENSITIVE : GTK_STATE_FLAG_NORMAL);
786
787     GdkRGBA gdkRGBAColor;
788     gtk_style_context_get_color(buttonStyleContext.get(), gtk_style_context_get_state(buttonStyleContext.get()), &gdkRGBAColor);
789     return gdkRGBAColor;
790 #endif // GTK_CHECK_VERSION(3, 20, 0)
791 }
792
793 void RenderThemeGtk::adjustMenuListStyle(StyleResolver&, RenderStyle& style, const Element* element) const
794 {
795     // The tests check explicitly that select menu buttons ignore line height.
796     style.setLineHeight(RenderStyle::initialLineHeight());
797
798     // We cannot give a proper rendering when border radius is active, unfortunately.
799     style.resetBorderRadius();
800
801     if (element)
802         style.setColor(menuListColor(element));
803 }
804
805 void RenderThemeGtk::adjustMenuListButtonStyle(StyleResolver& styleResolver, RenderStyle& style, const Element* e) const
806 {
807     adjustMenuListStyle(styleResolver, style, e);
808 }
809
810 #if GTK_CHECK_VERSION(3, 20, 0)
811 /*
812  * GtkComboBox gadgets tree
813  *
814  * combobox
815  * ├── box.linked
816  * │   ╰── button.combo
817  * │       ╰── box
818  * │           ├── cellview
819  * │           ╰── arrow
820  * ╰── window.popup
821  */
822 LengthBox RenderThemeGtk::popupInternalPaddingBox(const RenderStyle& style) const
823 {
824     if (style.appearance() == NoControlPart)
825         return LengthBox(0);
826
827     RenderThemeGadget::Info info = { RenderThemeGadget::Type::Generic, "combobox", GTK_STATE_FLAG_NORMAL, { } };
828     auto comboGadget = RenderThemeGadget::create(info);
829     Vector<RenderThemeGadget::Info> children = {
830         { RenderThemeGadget::Type::Generic, "button", GTK_STATE_FLAG_NORMAL, { "combo" } }
831     };
832     info.name = "box";
833     info.classList = { "horizontal", "linked" };
834     auto boxGadget = std::make_unique<RenderThemeBoxGadget>(info, GTK_ORIENTATION_HORIZONTAL, children, comboGadget.get());
835     RenderThemeGadget* buttonGadget = boxGadget->child(0);
836     info.classList.removeLast();
837     auto buttonBoxGadget = RenderThemeGadget::create(info, buttonGadget);
838     info.name = "arrow";
839     info.classList = { };
840     auto arrowGadget = RenderThemeGadget::create(info, buttonBoxGadget.get());
841     GtkBorder comboContentsBox = comboGadget->contentsBox();
842     GtkBorder boxContentsBox = boxGadget->contentsBox();
843     GtkBorder buttonContentsBox = buttonGadget->contentsBox();
844     GtkBorder buttonBoxContentsBox = buttonBoxGadget->contentsBox();
845     GtkBorder padding;
846     padding.left = comboContentsBox.left + boxContentsBox.left + buttonContentsBox.left + buttonBoxContentsBox.left;
847     padding.right = comboContentsBox.right + boxContentsBox.right + buttonContentsBox.right + buttonBoxContentsBox.right;
848     padding.top = comboContentsBox.top + boxContentsBox.top + buttonContentsBox.top + buttonBoxContentsBox.top;
849     padding.bottom = comboContentsBox.bottom + boxContentsBox.bottom + buttonContentsBox.bottom + buttonBoxContentsBox.bottom;
850
851     auto arrowSize = arrowGadget->preferredSize();
852     return LengthBox(padding.top, padding.right + (style.direction() == LTR ? arrowSize.width() : 0),
853         padding.bottom, padding.left + (style.direction() == RTL ? arrowSize.width() : 0));
854 }
855
856 bool RenderThemeGtk::paintMenuList(const RenderObject& renderObject, const PaintInfo& paintInfo, const FloatRect& rect)
857 {
858     RenderThemeGadget::Info info = { RenderThemeGadget::Type::Generic, "combobox", themePartStateFlags(*this, ComboBoxButton, renderObject), { } };
859     auto comboGadget = RenderThemeGadget::create(info);
860     Vector<RenderThemeGadget::Info> children = {
861         { RenderThemeGadget::Type::Generic, "button", info.state, { "combo" } }
862     };
863     info.name = "box";
864     info.classList = { "horizontal", "linked" };
865     auto boxGadget = std::make_unique<RenderThemeBoxGadget>(info, GTK_ORIENTATION_HORIZONTAL, children, comboGadget.get());
866     RenderThemeGadget* buttonGadget = boxGadget->child(0);
867     info.classList.removeLast();
868     auto buttonBoxGadget = RenderThemeGadget::create(info, buttonGadget);
869     info.type = RenderThemeGadget::Type::Arrow;
870     info.name = "arrow";
871     info.classList = { };
872     auto arrowGadget = RenderThemeGadget::create(info, buttonBoxGadget.get());
873
874     cairo_t* cr = paintInfo.context().platformContext()->cr();
875     comboGadget->render(cr, rect);
876     boxGadget->render(cr, rect);
877     FloatRect contentsRect;
878     buttonGadget->render(cr, rect, &contentsRect);
879     buttonBoxGadget->render(cr, contentsRect);
880     arrowGadget->render(cr, contentsRect);
881     if (isFocused(renderObject))
882         buttonGadget->renderFocus(cr, rect);
883
884     return false;
885 }
886 #else
887 LengthBox RenderThemeGtk::popupInternalPaddingBox(const RenderStyle& style) const
888 {
889     if (style.appearance() == NoControlPart)
890         return { 0, 0, 0, 0 };
891
892     GRefPtr<GtkStyleContext> parentContext = createStyleContext(ComboBox);
893     GRefPtr<GtkStyleContext> context = createStyleContext(ComboBoxButton, parentContext.get());
894     gtk_style_context_set_direction(context.get(), static_cast<GtkTextDirection>(gtkTextDirection(style.direction())));
895     gtk_style_context_set_state(context.get(), static_cast<GtkStateFlags>(0));
896     GtkBorder borderWidth = { 0, 0, 0, 0 };
897     gtk_style_context_get_border(context.get(), gtk_style_context_get_state(context.get()), &borderWidth);
898
899     gboolean interiorFocus;
900     gint focusWidth, focusPad;
901     gtk_style_context_get_style(context.get(), "interior-focus", &interiorFocus, "focus-line-width", &focusWidth, "focus-padding", &focusPad, nullptr);
902     focusWidth = interiorFocus ? focusWidth + focusPad : 0;
903
904     return { borderWidth.top + focusWidth, borderWidth.right + focusWidth + (style.direction() == LTR ? minArrowSize : 0),
905         borderWidth.bottom + focusWidth, borderWidth.left + focusWidth + (style.direction() == RTL ? minArrowSize : 0) };
906 }
907
908 bool RenderThemeGtk::paintMenuList(const RenderObject& renderObject, const PaintInfo& paintInfo, const FloatRect& r)
909 {
910     // FIXME: adopt subpixel themes.
911     IntRect rect = IntRect(r);
912
913     cairo_t* cairoContext = paintInfo.context().platformContext()->cr();
914     GtkTextDirection direction = static_cast<GtkTextDirection>(gtkTextDirection(renderObject.style().direction()));
915
916     GRefPtr<GtkStyleContext> parentStyleContext = createStyleContext(ComboBox);
917
918     // Paint the button.
919     GRefPtr<GtkStyleContext> buttonStyleContext = createStyleContext(ComboBoxButton, parentStyleContext.get());
920     gtk_style_context_set_direction(buttonStyleContext.get(), direction);
921     renderButton(this, buttonStyleContext.get(), renderObject, paintInfo, rect);
922
923     // Get the inner rectangle.
924     gint focusWidth, focusPad;
925     GtkBorder* innerBorderPtr = 0;
926     GtkBorder innerBorder = { 1, 1, 1, 1 };
927     gtk_style_context_get_style(buttonStyleContext.get(), "inner-border", &innerBorderPtr, "focus-line-width", &focusWidth, "focus-padding", &focusPad, nullptr);
928     if (innerBorderPtr) {
929         innerBorder = *innerBorderPtr;
930         gtk_border_free(innerBorderPtr);
931     }
932
933     GtkBorder borderWidth;
934     GtkStateFlags state = gtk_style_context_get_state(buttonStyleContext.get());
935     gtk_style_context_get_border(buttonStyleContext.get(), state, &borderWidth);
936
937     focusWidth += focusPad;
938     IntRect innerRect(
939         rect.x() + innerBorder.left + borderWidth.left + focusWidth,
940         rect.y() + innerBorder.top + borderWidth.top + focusWidth,
941         rect.width() - borderWidth.left - borderWidth.right - innerBorder.left - innerBorder.right - (2 * focusWidth),
942         rect.height() - borderWidth.top - borderWidth.bottom - innerBorder.top - innerBorder.bottom - (2 * focusWidth));
943
944     if (isPressed(renderObject)) {
945         gint childDisplacementX;
946         gint childDisplacementY;
947         gtk_style_context_get_style(buttonStyleContext.get(), "child-displacement-x", &childDisplacementX, "child-displacement-y", &childDisplacementY, nullptr);
948         innerRect.move(childDisplacementX, childDisplacementY);
949     }
950     innerRect.setWidth(std::max(1, innerRect.width()));
951     innerRect.setHeight(std::max(1, innerRect.height()));
952
953     // Paint the arrow.
954     GRefPtr<GtkStyleContext> arrowStyleContext = createStyleContext(ComboBoxArrow, buttonStyleContext.get());
955     gtk_style_context_set_direction(arrowStyleContext.get(), direction);
956
957     gfloat arrowScaling;
958     gtk_style_context_get_style(parentStyleContext.get(), "arrow-scaling", &arrowScaling, nullptr);
959
960     IntSize arrowSize(minArrowSize, innerRect.height());
961     FloatPoint arrowPosition(innerRect.location());
962     if (direction == GTK_TEXT_DIR_LTR)
963         arrowPosition.move(innerRect.width() - arrowSize.width(), 0);
964
965     // GTK+ actually fetches the xalign and valign values from the widget, but since we
966     // don't have a widget here, we are just using the default xalign and valign values of 0.5.
967     gint extent = std::min(arrowSize.width(), arrowSize.height()) * arrowScaling;
968     arrowPosition.move((arrowSize.width() - extent) / 2, (arrowSize.height() - extent) / 2);
969
970     gtk_style_context_set_state(arrowStyleContext.get(), state);
971     gtk_render_arrow(arrowStyleContext.get(), cairoContext, G_PI, arrowPosition.x(), arrowPosition.y(), extent);
972
973     return false;
974 }
975 #endif // GTK_CHECK_VERSION(3, 20, 0)
976
977 bool RenderThemeGtk::paintMenuListButtonDecorations(const RenderBox& object, const PaintInfo& info, const FloatRect& rect)
978 {
979     return paintMenuList(object, info, rect);
980 }
981
982 #if GTK_CHECK_VERSION(3, 20, 0)
983 void RenderThemeGtk::adjustTextFieldStyle(StyleResolver&, RenderStyle& style, const Element* element) const
984 {
985     if (!is<HTMLInputElement>(element) || !shouldHaveSpinButton(downcast<HTMLInputElement>(*element)))
986         return;
987
988     // Spinbuttons need a minimum height to be rendered correctly.
989     RenderThemeGadget::Info info = { RenderThemeGadget::Type::Generic, "spinbutton", GTK_STATE_FLAG_NORMAL, { "horizontal" } };
990     auto spinbuttonGadget = RenderThemeGadget::create(info);
991     info.type = RenderThemeGadget::Type::TextField;
992     info.name = "entry";
993     info.classList.clear();
994     auto entryGadget = RenderThemeGadget::create(info, spinbuttonGadget.get());
995     info.type = RenderThemeGadget::Type::Icon;
996     info.name = "button";
997     info.classList.append("up");
998     auto buttonUpGadget = RenderThemeGadget::create(info, spinbuttonGadget.get());
999     static_cast<RenderThemeIconGadget*>(buttonUpGadget.get())->setIconSize(RenderThemeIconGadget::IconSizeGtk::Menu);
1000     info.classList[0] = "down";
1001     auto buttonDownGadget = RenderThemeGadget::create(info, spinbuttonGadget.get());
1002     static_cast<RenderThemeIconGadget*>(buttonDownGadget.get())->setIconSize(RenderThemeIconGadget::IconSizeGtk::Menu);
1003     IntSize preferredSize = spinbuttonGadget->preferredSize();
1004     preferredSize = preferredSize.expandedTo(entryGadget->preferredSize());
1005     IntSize upPreferredSize = preferredSize.expandedTo(buttonUpGadget->preferredSize());
1006     IntSize downPreferredSize = preferredSize.expandedTo(buttonDownGadget->preferredSize());
1007     int height = std::max(upPreferredSize.height(), downPreferredSize.height());
1008     style.setMinHeight(Length(height, Fixed));
1009 }
1010
1011 bool RenderThemeGtk::paintTextField(const RenderObject& renderObject, const PaintInfo& paintInfo, const FloatRect& rect)
1012 {
1013     RenderThemeGadget::Info info = { RenderThemeGadget::Type::TextField, "entry", themePartStateFlags(*this, Entry, renderObject), { } };
1014     std::unique_ptr<RenderThemeGadget> parentGadget;
1015     if (is<HTMLInputElement>(renderObject.node()) && shouldHaveSpinButton(downcast<HTMLInputElement>(*renderObject.node()))) {
1016         info.name = "spinbutton";
1017         info.classList.append("horizontal");
1018         parentGadget = RenderThemeTextFieldGadget::create(info);
1019         info.name = "entry";
1020         info.classList.clear();
1021     }
1022
1023     auto entryGadget = RenderThemeTextFieldGadget::create(info, parentGadget.get());
1024     if (parentGadget)
1025         parentGadget->render(paintInfo.context().platformContext()->cr(), rect);
1026     entryGadget->render(paintInfo.context().platformContext()->cr(), rect);
1027     return false;
1028 }
1029 #else
1030 void RenderThemeGtk::adjustTextFieldStyle(StyleResolver&, RenderStyle&, const Element*) const
1031 {
1032 }
1033
1034 bool RenderThemeGtk::paintTextField(const RenderObject& renderObject, const PaintInfo& paintInfo, const FloatRect& rect)
1035 {
1036     GRefPtr<GtkStyleContext> context = createStyleContext(Entry);
1037     gtk_style_context_set_direction(context.get(), static_cast<GtkTextDirection>(gtkTextDirection(renderObject.style().direction())));
1038
1039     guint flags = 0;
1040     if (!isEnabled(renderObject) || isReadOnlyControl(renderObject))
1041         flags |= GTK_STATE_FLAG_INSENSITIVE;
1042     else if (isFocused(renderObject))
1043         flags |= GTK_STATE_FLAG_FOCUSED;
1044     gtk_style_context_set_state(context.get(), static_cast<GtkStateFlags>(flags));
1045
1046     gtk_render_background(context.get(), paintInfo.context().platformContext()->cr(), rect.x(), rect.y(), rect.width(), rect.height());
1047     gtk_render_frame(context.get(), paintInfo.context().platformContext()->cr(), rect.x(), rect.y(), rect.width(), rect.height());
1048
1049     if (isFocused(renderObject) && isEnabled(renderObject)) {
1050         gboolean interiorFocus;
1051         gint focusWidth, focusPad;
1052         gtk_style_context_get_style(context.get(), "interior-focus", &interiorFocus, "focus-line-width", &focusWidth, "focus-padding", &focusPad, nullptr);
1053         if (!interiorFocus) {
1054             IntRect focusRect(rect);
1055             focusRect.inflate(focusWidth + focusPad);
1056             gtk_render_focus(context.get(), paintInfo.context().platformContext()->cr(), focusRect.x(), focusRect.y(), focusRect.width(), focusRect.height());
1057         }
1058     }
1059
1060     return false;
1061 }
1062 #endif
1063
1064 #if GTK_CHECK_VERSION(3, 20, 0)
1065 static void adjustSearchFieldIconStyle(RenderThemePart themePart, RenderStyle& style)
1066 {
1067     ASSERT(themePart == EntryIconLeft || themePart == EntryIconRight);
1068     RenderThemeGadget::Info info = { RenderThemeGadget::Type::TextField, "entry", GTK_STATE_FLAG_NORMAL, { } };
1069     auto parentGadget = RenderThemeGadget::create(info);
1070     info.type = RenderThemeGadget::Type::Icon;
1071     info.name = "image";
1072     if (themePart == EntryIconLeft)
1073         info.classList.append("left");
1074     else
1075         info.classList.append("right");
1076     auto gadget = RenderThemeIconGadget::create(info, parentGadget.get());
1077
1078     // Get the icon size based on the font size.
1079     static_cast<RenderThemeIconGadget*>(gadget.get())->setIconSize(style.fontSize());
1080     IntSize preferredSize = gadget->preferredSize();
1081     GtkBorder contentsBox = parentGadget->contentsBox();
1082     if (themePart == EntryIconLeft)
1083         preferredSize.expand(contentsBox.left, contentsBox.top + contentsBox.bottom);
1084     else
1085         preferredSize.expand(contentsBox.right, contentsBox.top + contentsBox.bottom);
1086     style.setWidth(Length(preferredSize.width(), Fixed));
1087     style.setHeight(Length(preferredSize.height(), Fixed));
1088 }
1089 #else
1090 // Defined in GTK+ (gtk/gtkiconfactory.c)
1091 static const gint gtkIconSizeMenu = 16;
1092 static const gint gtkIconSizeSmallToolbar = 18;
1093 static const gint gtkIconSizeButton = 20;
1094 static const gint gtkIconSizeLargeToolbar = 24;
1095 static const gint gtkIconSizeDnd = 32;
1096 static const gint gtkIconSizeDialog = 48;
1097
1098 static GtkIconSize getIconSizeForPixelSize(gint pixelSize)
1099 {
1100     if (pixelSize < gtkIconSizeSmallToolbar)
1101         return GTK_ICON_SIZE_MENU;
1102     if (pixelSize >= gtkIconSizeSmallToolbar && pixelSize < gtkIconSizeButton)
1103         return GTK_ICON_SIZE_SMALL_TOOLBAR;
1104     if (pixelSize >= gtkIconSizeButton && pixelSize < gtkIconSizeLargeToolbar)
1105         return GTK_ICON_SIZE_BUTTON;
1106     if (pixelSize >= gtkIconSizeLargeToolbar && pixelSize < gtkIconSizeDnd)
1107         return GTK_ICON_SIZE_LARGE_TOOLBAR;
1108     if (pixelSize >= gtkIconSizeDnd && pixelSize < gtkIconSizeDialog)
1109         return GTK_ICON_SIZE_DND;
1110
1111     return GTK_ICON_SIZE_DIALOG;
1112 }
1113
1114 static void adjustSearchFieldIconStyle(RenderThemePart themePart, RenderStyle& style)
1115 {
1116     style.resetBorder();
1117     style.resetPadding();
1118
1119     GRefPtr<GtkStyleContext> parentContext = createStyleContext(Entry);
1120     GRefPtr<GtkStyleContext> context = createStyleContext(themePart, parentContext.get());
1121
1122     GtkBorder padding;
1123     gtk_style_context_get_padding(context.get(), gtk_style_context_get_state(context.get()), &padding);
1124
1125     // Get the icon size based on the font size.
1126     int fontSize = style.fontSize();
1127     if (fontSize < gtkIconSizeMenu) {
1128         style.setWidth(Length(fontSize + (padding.left + padding.right), Fixed));
1129         style.setHeight(Length(fontSize + (padding.top + padding.bottom), Fixed));
1130         return;
1131     }
1132     gint width = 0, height = 0;
1133     gtk_icon_size_lookup(getIconSizeForPixelSize(fontSize), &width, &height);
1134     style.setWidth(Length(width + (padding.left + padding.right), Fixed));
1135     style.setHeight(Length(height + (padding.top + padding.bottom), Fixed));
1136 }
1137 #endif
1138
1139 bool RenderThemeGtk::paintTextArea(const RenderObject& o, const PaintInfo& i, const FloatRect& r)
1140 {
1141     return paintTextField(o, i, r);
1142 }
1143
1144 void RenderThemeGtk::adjustSearchFieldResultsButtonStyle(StyleResolver& styleResolver, RenderStyle& style, const Element* e) const
1145 {
1146     adjustSearchFieldCancelButtonStyle(styleResolver, style, e);
1147 }
1148
1149 bool RenderThemeGtk::paintSearchFieldResultsButton(const RenderBox& o, const PaintInfo& i, const IntRect& rect)
1150 {
1151     return paintSearchFieldResultsDecorationPart(o, i, rect);
1152 }
1153
1154 void RenderThemeGtk::adjustSearchFieldResultsDecorationPartStyle(StyleResolver&, RenderStyle& style, const Element*) const
1155 {
1156     adjustSearchFieldIconStyle(EntryIconLeft, style);
1157 }
1158
1159 void RenderThemeGtk::adjustSearchFieldCancelButtonStyle(StyleResolver&, RenderStyle& style, const Element*) const
1160 {
1161     adjustSearchFieldIconStyle(EntryIconRight, style);
1162 }
1163
1164 #if GTK_CHECK_VERSION(3, 20, 0)
1165 static bool paintSearchFieldIcon(RenderThemeGtk* theme, RenderThemePart themePart, const RenderBox& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
1166 {
1167     ASSERT(themePart == EntryIconLeft || themePart == EntryIconRight);
1168     RenderThemeGadget::Info info = { RenderThemeGadget::Type::TextField, "entry", themePartStateFlags(*theme, Entry, renderObject), { } };
1169     auto parentGadget = RenderThemeGadget::create(info);
1170     info.type = RenderThemeGadget::Type::Icon;
1171     info.state = themePartStateFlags(*theme, themePart, renderObject);
1172     info.name = "image";
1173     if (themePart == EntryIconLeft)
1174         info.classList.append("left");
1175     else
1176         info.classList.append("right");
1177     auto gadget = RenderThemeGadget::create(info, parentGadget.get());
1178     auto* gadgetIcon = static_cast<RenderThemeIconGadget*>(gadget.get());
1179     gadgetIcon->setIconSize(renderObject.style().fontSize());
1180     if (themePart == EntryIconLeft)
1181         gadgetIcon->setIconName("edit-find-symbolic");
1182     else
1183         gadgetIcon->setIconName("edit-clear-symbolic");
1184     GtkBorder contentsBox = parentGadget->contentsBox();
1185     IntRect iconRect = rect;
1186     if (themePart == EntryIconLeft) {
1187         iconRect.move(contentsBox.left, contentsBox.top);
1188         iconRect.contract(contentsBox.left, contentsBox.top + contentsBox.bottom);
1189     } else
1190         iconRect.contract(contentsBox.right, contentsBox.top + contentsBox.bottom);
1191     return !gadget->render(paintInfo.context().platformContext()->cr(), iconRect);
1192 }
1193 bool RenderThemeGtk::paintSearchFieldResultsDecorationPart(const RenderBox& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
1194 {
1195     return paintSearchFieldIcon(this, EntryIconLeft, renderObject, paintInfo, rect);
1196 }
1197
1198 bool RenderThemeGtk::paintSearchFieldCancelButton(const RenderBox& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
1199 {
1200     return paintSearchFieldIcon(this, EntryIconRight, renderObject, paintInfo, rect);
1201 }
1202 #else
1203 static bool paintIcon(GtkStyleContext* context, GraphicsContext& graphicsContext, const IntRect& rect, const char* iconName)
1204 {
1205     GRefPtr<GdkPixbuf> icon = loadThemedIcon(context, iconName, getIconSizeForPixelSize(rect.height()));
1206     if (!icon)
1207         return false;
1208
1209     if (gdk_pixbuf_get_width(icon.get()) > rect.width() || gdk_pixbuf_get_height(icon.get()) > rect.height())
1210         icon = adoptGRef(gdk_pixbuf_scale_simple(icon.get(), rect.width(), rect.height(), GDK_INTERP_BILINEAR));
1211
1212     gtk_render_icon(context, graphicsContext.platformContext()->cr(), icon.get(), rect.x(), rect.y());
1213     return true;
1214 }
1215
1216 static bool paintEntryIcon(RenderThemePart themePart, const char* iconName, GraphicsContext& graphicsContext, const IntRect& rect, GtkTextDirection direction, GtkStateFlags state)
1217 {
1218     GRefPtr<GtkStyleContext> parentContext = createStyleContext(Entry);
1219     GRefPtr<GtkStyleContext> context = createStyleContext(themePart, parentContext.get());
1220     gtk_style_context_set_direction(context.get(), direction);
1221     gtk_style_context_set_state(context.get(), state);
1222     return paintIcon(context.get(), graphicsContext, rect, iconName);
1223 }
1224
1225 static IntRect centerRectVerticallyInParentInputElement(const RenderObject& renderObject, const IntRect& rect)
1226 {
1227     if (!renderObject.node())
1228         return IntRect();
1229
1230     // Get the renderer of <input> element.
1231     Node* input = renderObject.node()->shadowHost();
1232     if (!input)
1233         input = renderObject.node();
1234     if (!is<RenderBox>(*input->renderer()))
1235         return IntRect();
1236
1237     // If possible center the y-coordinate of the rect vertically in the parent input element.
1238     // We also add one pixel here to ensure that the y coordinate is rounded up for box heights
1239     // that are even, which looks in relation to the box text.
1240     IntRect inputContentBox = downcast<RenderBox>(*input->renderer()).absoluteContentBox();
1241
1242     // Make sure the scaled decoration stays square and will fit in its parent's box.
1243     int iconSize = std::min(inputContentBox.width(), std::min(inputContentBox.height(), rect.height()));
1244     IntRect scaledRect(rect.x(), inputContentBox.y() + (inputContentBox.height() - iconSize + 1) / 2, iconSize, iconSize);
1245     return scaledRect;
1246 }
1247
1248 bool RenderThemeGtk::paintSearchFieldResultsDecorationPart(const RenderBox& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
1249 {
1250     IntRect iconRect = centerRectVerticallyInParentInputElement(renderObject, rect);
1251     if (iconRect.isEmpty())
1252         return true;
1253
1254     return !paintEntryIcon(EntryIconLeft, "edit-find-symbolic", paintInfo.context(), iconRect, gtkTextDirection(renderObject.style().direction()),
1255         gtkIconStateFlags(this, renderObject));
1256 }
1257
1258 bool RenderThemeGtk::paintSearchFieldCancelButton(const RenderBox& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
1259 {
1260     IntRect iconRect = centerRectVerticallyInParentInputElement(renderObject, rect);
1261     if (iconRect.isEmpty())
1262         return true;
1263
1264     return !paintEntryIcon(EntryIconRight, "edit-clear-symbolic", paintInfo.context(), iconRect, gtkTextDirection(renderObject.style().direction()),
1265         gtkIconStateFlags(this, renderObject));
1266 }
1267 #endif // GTK_CHECK_VERSION(3, 20, 0)
1268
1269 void RenderThemeGtk::adjustSearchFieldStyle(StyleResolver&, RenderStyle& style, const Element*) const
1270 {
1271     // We cannot give a proper rendering when border radius is active, unfortunately.
1272     style.resetBorderRadius();
1273     style.setLineHeight(RenderStyle::initialLineHeight());
1274 }
1275
1276 bool RenderThemeGtk::paintSearchField(const RenderObject& o, const PaintInfo& i, const IntRect& rect)
1277 {
1278     return paintTextField(o, i, rect);
1279 }
1280
1281 bool RenderThemeGtk::shouldHaveCapsLockIndicator(const HTMLInputElement& element) const
1282 {
1283     return element.isPasswordField();
1284 }
1285
1286 void RenderThemeGtk::adjustSliderTrackStyle(StyleResolver&, RenderStyle& style, const Element*) const
1287 {
1288     style.setBoxShadow(nullptr);
1289 }
1290
1291 void RenderThemeGtk::adjustSliderThumbStyle(StyleResolver& styleResolver, RenderStyle& style, const Element* element) const
1292 {
1293     RenderTheme::adjustSliderThumbStyle(styleResolver, style, element);
1294     style.setBoxShadow(nullptr);
1295 }
1296
1297 #if GTK_CHECK_VERSION(3, 20, 0)
1298 /*
1299  * GtkScale
1300  *
1301  * scale
1302  * ╰── contents
1303  *     ╰── trough
1304  *         ├── slider
1305  *         ╰── [highlight]
1306  */
1307 bool RenderThemeGtk::paintSliderTrack(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
1308 {
1309     ControlPart part = renderObject.style().appearance();
1310     ASSERT(part == SliderHorizontalPart || part == SliderVerticalPart);
1311
1312     RenderThemeGadget::Info info = { RenderThemeGadget::Type::Generic, "scale", themePartStateFlags(*this, Scale, renderObject), { } };
1313     if (part == SliderHorizontalPart)
1314         info.classList.append("horizontal");
1315     else
1316         info.classList.append("vertical");
1317     auto scaleGadget = RenderThemeGadget::create(info);
1318     info.name = "contents";
1319     info.classList.clear();
1320     auto contentsGadget = RenderThemeGadget::create(info, scaleGadget.get());
1321     info.name = "trough";
1322     auto troughGadget = RenderThemeGadget::create(info, contentsGadget.get());
1323     info.name = "slider";
1324     auto sliderGadget = RenderThemeGadget::create(info, troughGadget.get());
1325     info.name = "highlight";
1326     auto highlightGadget = RenderThemeGadget::create(info, troughGadget.get());
1327
1328     // The given rectangle is not calculated based on the scale size, but all the margins and paddings are based on it.
1329     IntSize preferredSize = scaleGadget->preferredSize();
1330     preferredSize = preferredSize.expandedTo(contentsGadget->preferredSize());
1331     preferredSize = preferredSize.expandedTo(troughGadget->preferredSize());
1332     FloatRect trackRect = rect;
1333     if (part == SliderHorizontalPart) {
1334         trackRect.move(0, rect.height() / 2 - (preferredSize.height() / 2));
1335         trackRect.setHeight(preferredSize.height());
1336     } else {
1337         trackRect.move(rect.width() / 2 - (preferredSize.width() / 2), 0);
1338         trackRect.setWidth(preferredSize.width());
1339     }
1340
1341     FloatRect contentsRect;
1342     scaleGadget->render(paintInfo.context().platformContext()->cr(), trackRect, &contentsRect);
1343     contentsGadget->render(paintInfo.context().platformContext()->cr(), contentsRect, &contentsRect);
1344     // Scale trough defines its size querying slider and highlight.
1345     if (part == SliderHorizontalPart)
1346         contentsRect.setHeight(troughGadget->preferredSize().height() + std::max(sliderGadget->preferredSize().height(), highlightGadget->preferredSize().height()));
1347     else
1348         contentsRect.setWidth(troughGadget->preferredSize().width() + std::max(sliderGadget->preferredSize().width(), highlightGadget->preferredSize().width()));
1349     FloatRect troughRect = contentsRect;
1350     troughGadget->render(paintInfo.context().platformContext()->cr(), troughRect, &contentsRect);
1351     if (isFocused(renderObject))
1352         troughGadget->renderFocus(paintInfo.context().platformContext()->cr(), troughRect);
1353
1354     LayoutPoint thumbLocation;
1355     if (is<HTMLInputElement>(renderObject.node())) {
1356         auto& input = downcast<HTMLInputElement>(*renderObject.node());
1357         if (auto* element = input.sliderThumbElement())
1358             thumbLocation = element->renderBox()->location();
1359     }
1360
1361     if (part == SliderHorizontalPart) {
1362         if (renderObject.style().direction() == RTL) {
1363             contentsRect.move(thumbLocation.x(), 0);
1364             contentsRect.setWidth(contentsRect.width() - thumbLocation.x());
1365         } else
1366             contentsRect.setWidth(thumbLocation.x());
1367     } else
1368         contentsRect.setHeight(thumbLocation.y());
1369     highlightGadget->render(paintInfo.context().platformContext()->cr(), contentsRect);
1370
1371     return false;
1372 }
1373
1374 void RenderThemeGtk::adjustSliderThumbSize(RenderStyle& style, const Element*) const
1375 {
1376     ControlPart part = style.appearance();
1377     if (part != SliderThumbHorizontalPart && part != SliderThumbVerticalPart)
1378         return;
1379
1380     RenderThemeGadget::Info info = { RenderThemeGadget::Type::Generic, "scale", GTK_STATE_FLAG_NORMAL, { } };
1381     if (part == SliderHorizontalPart)
1382         info.classList.append("horizontal");
1383     else
1384         info.classList.append("vertical");
1385     auto scaleGadget = RenderThemeGadget::create(info);
1386     info.name = "contents";
1387     info.classList.clear();
1388     auto contentsGadget = RenderThemeGadget::create(info, scaleGadget.get());
1389     info.name = "trough";
1390     auto troughGadget = RenderThemeGadget::create(info, contentsGadget.get());
1391     info.name = "slider";
1392     auto sliderGadget = RenderThemeGadget::create(info, troughGadget.get());
1393     info.name = "highlight";
1394     auto highlightGadget = RenderThemeGadget::create(info, troughGadget.get());
1395
1396     IntSize preferredSize = scaleGadget->preferredSize();
1397     preferredSize = preferredSize.expandedTo(contentsGadget->preferredSize());
1398     preferredSize = preferredSize.expandedTo(troughGadget->preferredSize());
1399     preferredSize = preferredSize.expandedTo(sliderGadget->preferredSize());
1400     if (part == SliderThumbHorizontalPart) {
1401         style.setWidth(Length(preferredSize.width(), Fixed));
1402         style.setHeight(Length(preferredSize.height(), Fixed));
1403         return;
1404     }
1405     ASSERT(part == SliderThumbVerticalPart);
1406     style.setWidth(Length(preferredSize.height(), Fixed));
1407     style.setHeight(Length(preferredSize.width(), Fixed));
1408 }
1409
1410 bool RenderThemeGtk::paintSliderThumb(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
1411 {
1412     ControlPart part = renderObject.style().appearance();
1413     ASSERT(part == SliderThumbHorizontalPart || part == SliderThumbVerticalPart);
1414
1415     RenderThemeGadget::Info info = { RenderThemeGadget::Type::Generic, "scale", themePartStateFlags(*this, Scale, renderObject), { } };
1416     if (part == SliderHorizontalPart)
1417         info.classList.append("horizontal");
1418     else
1419         info.classList.append("vertical");
1420     auto scaleGadget = RenderThemeGadget::create(info);
1421     info.name = "contents";
1422     info.classList.clear();
1423     auto contentsGadget = RenderThemeGadget::create(info, scaleGadget.get());
1424     info.name = "trough";
1425     auto troughGadget = RenderThemeGadget::create(info, contentsGadget.get());
1426     info.name = "slider";
1427     info.state = themePartStateFlags(*this, ScaleSlider, renderObject);
1428     auto sliderGadget = RenderThemeGadget::create(info, troughGadget.get());
1429     info.name = "highlight";
1430     auto highlightGadget = RenderThemeGadget::create(info, troughGadget.get());
1431
1432     GtkBorder scaleContentsBox = scaleGadget->contentsBox();
1433     GtkBorder contentsContentsBox = contentsGadget->contentsBox();
1434     GtkBorder troughContentsBox = troughGadget->contentsBox();
1435     GtkBorder padding;
1436     padding.left = scaleContentsBox.left + contentsContentsBox.left + troughContentsBox.left;
1437     padding.right = scaleContentsBox.right + contentsContentsBox.right + troughContentsBox.right;
1438     padding.top = scaleContentsBox.top + contentsContentsBox.top + troughContentsBox.top;
1439     padding.bottom = scaleContentsBox.bottom + contentsContentsBox.bottom + troughContentsBox.bottom;
1440
1441     // Scale trough defines its size querying slider and highlight.
1442     int troughHeight = troughGadget->preferredSize().height() + std::max(sliderGadget->preferredSize().height(), highlightGadget->preferredSize().height());
1443     IntRect sliderRect(rect.location(), IntSize(troughHeight, troughHeight));
1444     sliderRect.move(padding.left, padding.top);
1445     sliderRect.contract(padding.left + padding.right, padding.top + padding.bottom);
1446     sliderGadget->render(paintInfo.context().platformContext()->cr(), sliderRect);
1447     return false;
1448 }
1449 #else
1450 bool RenderThemeGtk::paintSliderTrack(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
1451 {
1452     ControlPart part = renderObject.style().appearance();
1453     ASSERT(part == SliderHorizontalPart || part == SliderVerticalPart);
1454
1455     GRefPtr<GtkStyleContext> parentContext = createStyleContext(Scale);
1456     gtk_style_context_add_class(parentContext.get(), part == SliderHorizontalPart ? GTK_STYLE_CLASS_HORIZONTAL : GTK_STYLE_CLASS_VERTICAL);
1457     GRefPtr<GtkStyleContext> context = createStyleContext(ScaleTrough, parentContext.get());
1458     gtk_style_context_set_direction(context.get(), gtkTextDirection(renderObject.style().direction()));
1459
1460     if (!isEnabled(renderObject))
1461         gtk_style_context_set_state(context.get(), GTK_STATE_FLAG_INSENSITIVE);
1462
1463     IntRect sliderRect = rect;
1464     // GTK+ uses the slider thumb size and margins to calculate the trough size, but in WebKit we render the thumb and
1465     // the slider track separately and the track rectangle we receive here can't be used to apply the GTK+ CSS sizes
1466     // and margins. So we use a maximum fixed size for the trough to match at least Adwaita, but that should look
1467     // good in other themes as well.
1468     static const int sliderSize = 4;
1469
1470     if (part == SliderHorizontalPart) {
1471         sliderRect.setHeight(std::min(rect.height(), sliderSize));
1472         sliderRect.move(0, (rect.height() - sliderRect.height()) / 2);
1473     } else {
1474         sliderRect.setWidth(std::min(rect.width(), sliderSize));
1475         sliderRect.move((rect.width() - sliderRect.width()) / 2, 0);
1476     }
1477
1478     gtk_render_background(context.get(), paintInfo.context().platformContext()->cr(), sliderRect.x(), sliderRect.y(), sliderRect.width(), sliderRect.height());
1479     gtk_render_frame(context.get(), paintInfo.context().platformContext()->cr(), sliderRect.x(), sliderRect.y(), sliderRect.width(), sliderRect.height());
1480
1481     if (isFocused(renderObject)) {
1482         gint focusWidth, focusPad;
1483         gtk_style_context_get_style(context.get(), "focus-line-width", &focusWidth, "focus-padding", &focusPad, nullptr);
1484         IntRect focusRect(sliderRect);
1485         focusRect.inflate(focusWidth + focusPad);
1486         gtk_render_focus(context.get(), paintInfo.context().platformContext()->cr(), focusRect.x(), focusRect.y(), focusRect.width(), focusRect.height());
1487     }
1488
1489     return false;
1490 }
1491
1492 void RenderThemeGtk::adjustSliderThumbSize(RenderStyle& style, const Element*) const
1493 {
1494     ControlPart part = style.appearance();
1495     if (part != SliderThumbHorizontalPart && part != SliderThumbVerticalPart)
1496         return;
1497
1498     GRefPtr<GtkStyleContext> context = createStyleContext(Scale);
1499     gint sliderWidth, sliderLength;
1500     gtk_style_context_get_style(context.get(), "slider-width", &sliderWidth, "slider-length", &sliderLength, nullptr);
1501
1502     if (part == SliderThumbHorizontalPart) {
1503         style.setWidth(Length(sliderLength, Fixed));
1504         style.setHeight(Length(sliderWidth, Fixed));
1505         return;
1506     }
1507     ASSERT(part == SliderThumbVerticalPart);
1508     style.setWidth(Length(sliderWidth, Fixed));
1509     style.setHeight(Length(sliderLength, Fixed));
1510 }
1511
1512 bool RenderThemeGtk::paintSliderThumb(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
1513 {
1514     ControlPart part = renderObject.style().appearance();
1515     ASSERT(part == SliderThumbHorizontalPart || part == SliderThumbVerticalPart);
1516
1517     // FIXME: The entire slider is too wide, stretching the thumb into an oval rather than a circle.
1518     GRefPtr<GtkStyleContext> parentContext = createStyleContext(Scale);
1519     gtk_style_context_add_class(parentContext.get(), part == SliderThumbHorizontalPart ? GTK_STYLE_CLASS_HORIZONTAL : GTK_STYLE_CLASS_VERTICAL);
1520     GRefPtr<GtkStyleContext> troughContext = createStyleContext(ScaleTrough, parentContext.get());
1521     GRefPtr<GtkStyleContext> context = createStyleContext(ScaleSlider, troughContext.get());
1522     gtk_style_context_set_direction(context.get(), gtkTextDirection(renderObject.style().direction()));
1523
1524     guint flags = 0;
1525     if (!isEnabled(renderObject))
1526         flags |= GTK_STATE_FLAG_INSENSITIVE;
1527     else if (isHovered(renderObject))
1528         flags |= GTK_STATE_FLAG_PRELIGHT;
1529     if (isPressed(renderObject))
1530         flags |= GTK_STATE_FLAG_ACTIVE;
1531     gtk_style_context_set_state(context.get(), static_cast<GtkStateFlags>(flags));
1532
1533     gtk_render_slider(context.get(), paintInfo.context().platformContext()->cr(), rect.x(), rect.y(), rect.width(), rect.height(),
1534         part == SliderThumbHorizontalPart ? GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL);
1535
1536     return false;
1537 }
1538 #endif
1539
1540 #if GTK_CHECK_VERSION(3, 20, 0)
1541 IntRect RenderThemeGtk::progressBarRectForBounds(const RenderObject& renderObject, const IntRect& bounds) const
1542 {
1543     RenderThemeGadget::Info info = { RenderThemeGadget::Type::Generic, "progressbar", GTK_STATE_FLAG_NORMAL, { "horizontal" } };
1544     auto progressBarGadget = RenderThemeGadget::create(info);
1545     info.name = "trough";
1546     info.classList.clear();
1547     auto troughGadget = RenderThemeGadget::create(info, progressBarGadget.get());
1548     info.name = "progress";
1549     if (renderObject.style().direction() == RTL)
1550         info.classList.append("right");
1551     else
1552         info.classList.append("left");
1553     const auto& renderProgress = downcast<RenderProgress>(renderObject);
1554     if (renderProgress.isDeterminate())
1555         info.classList.append("pulse");
1556     auto progressGadget = RenderThemeGadget::create(info, troughGadget.get());
1557     IntSize preferredSize = progressBarGadget->preferredSize();
1558     preferredSize = preferredSize.expandedTo(troughGadget->preferredSize());
1559     preferredSize = preferredSize.expandedTo(progressGadget->preferredSize());
1560     return IntRect(bounds.x(), bounds.y(), bounds.width(), preferredSize.height());
1561 }
1562
1563 bool RenderThemeGtk::paintProgressBar(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
1564 {
1565     if (!renderObject.isProgress())
1566         return true;
1567
1568     RenderThemeGadget::Info info = { RenderThemeGadget::Type::Generic, "progressbar", GTK_STATE_FLAG_NORMAL, { "horizontal" } };
1569     auto progressBarGadget = RenderThemeGadget::create(info);
1570     info.name = "trough";
1571     info.classList.clear();
1572     auto troughGadget = RenderThemeGadget::create(info, progressBarGadget.get());
1573     info.name = "progress";
1574     if (renderObject.style().direction() == RTL)
1575         info.classList.append("right");
1576     else
1577         info.classList.append("left");
1578     const auto& renderProgress = downcast<RenderProgress>(renderObject);
1579     if (renderProgress.isDeterminate())
1580         info.classList.append("pulse");
1581     auto progressGadget = RenderThemeGadget::create(info, troughGadget.get());
1582
1583     progressBarGadget->render(paintInfo.context().platformContext()->cr(), rect);
1584     troughGadget->render(paintInfo.context().platformContext()->cr(), rect);
1585     progressGadget->render(paintInfo.context().platformContext()->cr(), calculateProgressRect(renderObject, rect));
1586     return false;
1587 }
1588 #else
1589 IntRect RenderThemeGtk::progressBarRectForBounds(const RenderObject&, const IntRect& bounds) const
1590 {
1591     return bounds;
1592 }
1593
1594 bool RenderThemeGtk::paintProgressBar(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
1595 {
1596     if (!renderObject.isProgress())
1597         return true;
1598
1599     GRefPtr<GtkStyleContext> parentContext = createStyleContext(ProgressBar);
1600     GRefPtr<GtkStyleContext> troughContext = createStyleContext(ProgressBarTrough, parentContext.get());
1601     GRefPtr<GtkStyleContext> context = createStyleContext(ProgressBarProgress, troughContext.get());
1602
1603     gtk_render_background(troughContext.get(), paintInfo.context().platformContext()->cr(), rect.x(), rect.y(), rect.width(), rect.height());
1604     gtk_render_frame(troughContext.get(), paintInfo.context().platformContext()->cr(), rect.x(), rect.y(), rect.width(), rect.height());
1605
1606     gtk_style_context_set_state(context.get(), static_cast<GtkStateFlags>(0));
1607
1608     GtkBorder padding;
1609     gtk_style_context_get_padding(context.get(), gtk_style_context_get_state(context.get()), &padding);
1610     IntRect progressRect(
1611         rect.x() + padding.left,
1612         rect.y() + padding.top,
1613         rect.width() - (padding.left + padding.right),
1614         rect.height() - (padding.top + padding.bottom));
1615     progressRect = RenderThemeGtk::calculateProgressRect(renderObject, progressRect);
1616
1617     if (!progressRect.isEmpty()) {
1618 #if GTK_CHECK_VERSION(3, 13, 7)
1619         gtk_render_background(context.get(), paintInfo.context().platformContext()->cr(), progressRect.x(), progressRect.y(), progressRect.width(), progressRect.height());
1620         gtk_render_frame(context.get(), paintInfo.context().platformContext()->cr(), progressRect.x(), progressRect.y(), progressRect.width(), progressRect.height());
1621 #else
1622         gtk_render_activity(context.get(), paintInfo.context().platformContext()->cr(), progressRect.x(), progressRect.y(), progressRect.width(), progressRect.height());
1623 #endif
1624     }
1625
1626     return false;
1627 }
1628 #endif // GTK_CHECK_VERSION(3, 20, 0)
1629
1630 #if GTK_CHECK_VERSION(3, 20, 0)
1631 RenderTheme::InnerSpinButtonLayout RenderThemeGtk::innerSpinButtonLayout(const RenderObject& renderObject) const
1632 {
1633     return renderObject.style().direction() == RTL ? InnerSpinButtonLayout::HorizontalUpLeft : InnerSpinButtonLayout::HorizontalUpRight;
1634 }
1635
1636 void RenderThemeGtk::adjustInnerSpinButtonStyle(StyleResolver&, RenderStyle& style, const Element*) const
1637 {
1638     RenderThemeGadget::Info info = { RenderThemeGadget::Type::Generic, "spinbutton", GTK_STATE_FLAG_NORMAL, { "horizontal" } };
1639     auto spinbuttonGadget = RenderThemeGadget::create(info);
1640     info.type = RenderThemeGadget::Type::TextField;
1641     info.name = "entry";
1642     info.classList.clear();
1643     auto entryGadget = RenderThemeGadget::create(info, spinbuttonGadget.get());
1644     info.type = RenderThemeGadget::Type::Icon;
1645     info.name = "button";
1646     info.classList.append("up");
1647     auto buttonUpGadget = RenderThemeGadget::create(info, spinbuttonGadget.get());
1648     static_cast<RenderThemeIconGadget*>(buttonUpGadget.get())->setIconSize(RenderThemeIconGadget::IconSizeGtk::Menu);
1649     info.classList[0] = "down";
1650     auto buttonDownGadget = RenderThemeGadget::create(info, spinbuttonGadget.get());
1651     static_cast<RenderThemeIconGadget*>(buttonDownGadget.get())->setIconSize(RenderThemeIconGadget::IconSizeGtk::Menu);
1652     IntSize upPreferredSize = buttonUpGadget->preferredSize();
1653     IntSize downPreferredSize = buttonDownGadget->preferredSize();
1654     int buttonSize = std::max(std::max(upPreferredSize.width(), downPreferredSize.width()), std::max(upPreferredSize.height(), downPreferredSize.height()));
1655     style.setWidth(Length(buttonSize * 2, Fixed));
1656     style.setHeight(Length(buttonSize, Fixed));
1657 }
1658
1659 bool RenderThemeGtk::paintInnerSpinButton(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
1660 {
1661     RenderThemeGadget::Info info = { RenderThemeGadget::Type::Generic, "spinbutton", themePartStateFlags(*this, SpinButton, renderObject), { } };
1662     auto spinbuttonGadget = RenderThemeGadget::create(info);
1663     info.type = RenderThemeGadget::Type::TextField;
1664     info.name = "entry";
1665     info.classList.clear();
1666     auto entryGadget = RenderThemeGadget::create(info, spinbuttonGadget.get());
1667     info.type = RenderThemeGadget::Type::Icon;
1668     info.name = "button";
1669     info.classList.append("up");
1670     info.state = themePartStateFlags(*this, SpinButtonUpButton, renderObject);
1671     auto buttonUpGadget = RenderThemeGadget::create(info, spinbuttonGadget.get());
1672     auto* gadgetIcon = static_cast<RenderThemeIconGadget*>(buttonUpGadget.get());
1673     gadgetIcon->setIconSize(RenderThemeIconGadget::IconSizeGtk::Menu);
1674     gadgetIcon->setIconName("list-add-symbolic");
1675     info.classList[0] = "down";
1676     info.state = themePartStateFlags(*this, SpinButtonDownButton, renderObject);
1677     auto buttonDownGadget = RenderThemeGadget::create(info, spinbuttonGadget.get());
1678     gadgetIcon = static_cast<RenderThemeIconGadget*>(buttonDownGadget.get());
1679     gadgetIcon->setIconSize(RenderThemeIconGadget::IconSizeGtk::Menu);
1680     gadgetIcon->setIconName("list-remove-symbolic");
1681
1682     IntRect iconRect = rect;
1683     iconRect.setWidth(iconRect.width() / 2);
1684     if (renderObject.style().direction() == RTL)
1685         buttonUpGadget->render(paintInfo.context().platformContext()->cr(), iconRect);
1686     else
1687         buttonDownGadget->render(paintInfo.context().platformContext()->cr(), iconRect);
1688     iconRect.move(iconRect.width(), 0);
1689     if (renderObject.style().direction() == RTL)
1690         buttonDownGadget->render(paintInfo.context().platformContext()->cr(), iconRect);
1691     else
1692         buttonUpGadget->render(paintInfo.context().platformContext()->cr(), iconRect);
1693
1694     return false;
1695 }
1696 #else
1697 RenderTheme::InnerSpinButtonLayout RenderThemeGtk::innerSpinButtonLayout(const RenderObject&) const
1698 {
1699     return InnerSpinButtonLayout::Vertical;
1700 }
1701 static gint spinButtonArrowSize(GtkStyleContext* context)
1702 {
1703     PangoFontDescription* fontDescription;
1704     gtk_style_context_get(context, gtk_style_context_get_state(context), "font", &fontDescription, nullptr);
1705     gint fontSize = pango_font_description_get_size(fontDescription);
1706     gint arrowSize = std::max(PANGO_PIXELS(fontSize), minSpinButtonArrowSize);
1707     pango_font_description_free(fontDescription);
1708
1709     return arrowSize - arrowSize % 2; // Force even.
1710 }
1711
1712 void RenderThemeGtk::adjustInnerSpinButtonStyle(StyleResolver&, RenderStyle& style, const Element*) const
1713 {
1714     GRefPtr<GtkStyleContext> context = createStyleContext(SpinButton);
1715
1716     GtkBorder padding;
1717     gtk_style_context_get_padding(context.get(), gtk_style_context_get_state(context.get()), &padding);
1718
1719     int width = spinButtonArrowSize(context.get()) + padding.left + padding.right;
1720     style.setWidth(Length(width, Fixed));
1721     style.setMinWidth(Length(width, Fixed));
1722 }
1723
1724 static void paintSpinArrowButton(RenderTheme* theme, GtkStyleContext* parentContext, const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect, GtkArrowType arrowType)
1725 {
1726     ASSERT(arrowType == GTK_ARROW_UP || arrowType == GTK_ARROW_DOWN);
1727
1728     GRefPtr<GtkStyleContext> context = createStyleContext(arrowType == GTK_ARROW_UP ? SpinButtonUpButton : SpinButtonDownButton, parentContext);
1729     GtkTextDirection direction = gtk_style_context_get_direction(context.get());
1730     guint state = static_cast<guint>(gtk_style_context_get_state(context.get()));
1731     if (!(state & GTK_STATE_FLAG_INSENSITIVE)) {
1732         if (theme->isPressed(renderObject)) {
1733             if ((arrowType == GTK_ARROW_UP && theme->isSpinUpButtonPartPressed(renderObject))
1734                 || (arrowType == GTK_ARROW_DOWN && !theme->isSpinUpButtonPartPressed(renderObject)))
1735                 state |= GTK_STATE_FLAG_ACTIVE;
1736         } else if (theme->isHovered(renderObject)) {
1737             if ((arrowType == GTK_ARROW_UP && theme->isSpinUpButtonPartHovered(renderObject))
1738                 || (arrowType == GTK_ARROW_DOWN && !theme->isSpinUpButtonPartHovered(renderObject)))
1739                 state |= GTK_STATE_FLAG_PRELIGHT;
1740         }
1741     }
1742     gtk_style_context_set_state(context.get(), static_cast<GtkStateFlags>(state));
1743
1744     // Paint button.
1745     IntRect buttonRect(rect);
1746     guint junction = gtk_style_context_get_junction_sides(context.get());
1747     if (arrowType == GTK_ARROW_UP)
1748         junction |= GTK_JUNCTION_BOTTOM;
1749     else {
1750         junction |= GTK_JUNCTION_TOP;
1751         buttonRect.move(0, rect.height() / 2);
1752     }
1753     buttonRect.setHeight(rect.height() / 2);
1754     gtk_style_context_set_junction_sides(context.get(), static_cast<GtkJunctionSides>(junction));
1755
1756     gtk_render_background(context.get(), paintInfo.context().platformContext()->cr(), buttonRect.x(), buttonRect.y(), buttonRect.width(), buttonRect.height());
1757     gtk_render_frame(context.get(), paintInfo.context().platformContext()->cr(), buttonRect.x(), buttonRect.y(), buttonRect.width(), buttonRect.height());
1758
1759     // Paint arrow centered inside button.
1760     // This code is based on gtkspinbutton.c code.
1761     IntRect arrowRect;
1762     gdouble angle;
1763     if (arrowType == GTK_ARROW_UP) {
1764         angle = 0;
1765         arrowRect.setY(rect.y());
1766         arrowRect.setHeight(rect.height() / 2 - 2);
1767     } else {
1768         angle = G_PI;
1769         arrowRect.setY(rect.y() + buttonRect.y());
1770         arrowRect.setHeight(rect.height() - arrowRect.y() - 2);
1771     }
1772     arrowRect.setWidth(rect.width() - 3);
1773     if (direction == GTK_TEXT_DIR_LTR)
1774         arrowRect.setX(rect.x() + 1);
1775     else
1776         arrowRect.setX(rect.x() + 2);
1777
1778     gint width = arrowRect.width() / 2;
1779     width -= width % 2 - 1; // Force odd.
1780     gint height = (width + 1) / 2;
1781
1782     arrowRect.move((arrowRect.width() - width) / 2, (arrowRect.height() - height) / 2);
1783     gtk_render_arrow(context.get(), paintInfo.context().platformContext()->cr(), angle, arrowRect.x(), arrowRect.y(), width);
1784 }
1785
1786 bool RenderThemeGtk::paintInnerSpinButton(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
1787 {
1788     GRefPtr<GtkStyleContext> context = createStyleContext(SpinButton);
1789     gtk_style_context_set_direction(context.get(), gtkTextDirection(renderObject.style().direction()));
1790
1791     guint flags = 0;
1792     if (!isEnabled(renderObject) || isReadOnlyControl(renderObject))
1793         flags |= GTK_STATE_FLAG_INSENSITIVE;
1794     else if (isFocused(renderObject))
1795         flags |= GTK_STATE_FLAG_FOCUSED;
1796     gtk_style_context_set_state(context.get(), static_cast<GtkStateFlags>(flags));
1797
1798     paintSpinArrowButton(this, context.get(), renderObject, paintInfo, rect, GTK_ARROW_UP);
1799     paintSpinArrowButton(this, context.get(), renderObject, paintInfo, rect, GTK_ARROW_DOWN);
1800
1801     return false;
1802 }
1803 #endif // GTK_CHECK_VERSION(3, 20, 0)
1804
1805 Seconds RenderThemeGtk::caretBlinkInterval() const
1806 {
1807     GtkSettings* settings = gtk_settings_get_default();
1808
1809     gboolean shouldBlink;
1810     gint time;
1811
1812     g_object_get(settings, "gtk-cursor-blink", &shouldBlink, "gtk-cursor-blink-time", &time, nullptr);
1813
1814     if (!shouldBlink)
1815         return 0_s;
1816
1817     return 500_us * time;
1818 }
1819
1820 enum StyleColorType { StyleColorBackground, StyleColorForeground };
1821
1822 #if GTK_CHECK_VERSION(3, 20, 0)
1823 static Color styleColor(RenderThemePart themePart, GtkStateFlags state, StyleColorType colorType)
1824 {
1825     RenderThemeGadget::Info info = { RenderThemeGadget::Type::Generic, nullptr, state, { } };
1826     std::unique_ptr<RenderThemeGadget> parentGadget;
1827     RenderThemePart part = themePart;
1828     if (themePart == Entry && (state & GTK_STATE_FLAG_SELECTED)) {
1829         info.name = "entry";
1830         parentGadget = RenderThemeGadget::create(info);
1831         part = EntrySelection;
1832     }
1833
1834     switch (part) {
1835     case Entry:
1836         info.name = "entry";
1837         break;
1838     case EntrySelection:
1839         info.name = "selection";
1840         break;
1841     case ListBox:
1842         info.name = "treeview";
1843         info.classList.append("view");
1844         break;
1845     case Button:
1846         info.name = "button";
1847         break;
1848     default:
1849         ASSERT_NOT_REACHED();
1850         info.name = "entry";
1851     }
1852
1853     auto gadget = RenderThemeGadget::create(info, parentGadget.get());
1854     return colorType == StyleColorBackground ? gadget->backgroundColor() : gadget->color();
1855 }
1856 #else
1857 static Color styleColor(RenderThemePart themePart, GtkStateFlags state, StyleColorType colorType)
1858 {
1859     GRefPtr<GtkStyleContext> context = createStyleContext(themePart);
1860     gtk_style_context_set_state(context.get(), state);
1861
1862     GdkRGBA gdkRGBAColor;
1863     if (colorType == StyleColorBackground)
1864         gtk_style_context_get_background_color(context.get(), state, &gdkRGBAColor);
1865     else
1866         gtk_style_context_get_color(context.get(), state, &gdkRGBAColor);
1867     return gdkRGBAColor;
1868 }
1869 #endif // GTK_CHECK_VERSION(3, 20, 0)
1870
1871 Color RenderThemeGtk::platformActiveSelectionBackgroundColor() const
1872 {
1873     return styleColor(Entry, static_cast<GtkStateFlags>(GTK_STATE_FLAG_SELECTED | GTK_STATE_FLAG_FOCUSED), StyleColorBackground);
1874 }
1875
1876 Color RenderThemeGtk::platformInactiveSelectionBackgroundColor() const
1877 {
1878     return styleColor(Entry, GTK_STATE_FLAG_SELECTED, StyleColorBackground);
1879 }
1880
1881 Color RenderThemeGtk::platformActiveSelectionForegroundColor() const
1882 {
1883     return styleColor(Entry, static_cast<GtkStateFlags>(GTK_STATE_FLAG_SELECTED | GTK_STATE_FLAG_FOCUSED), StyleColorForeground);
1884 }
1885
1886 Color RenderThemeGtk::platformInactiveSelectionForegroundColor() const
1887 {
1888     return styleColor(Entry, GTK_STATE_FLAG_SELECTED, StyleColorForeground);
1889 }
1890
1891 Color RenderThemeGtk::platformActiveListBoxSelectionBackgroundColor() const
1892 {
1893     return styleColor(ListBox, static_cast<GtkStateFlags>(GTK_STATE_FLAG_SELECTED | GTK_STATE_FLAG_FOCUSED), StyleColorBackground);
1894 }
1895
1896 Color RenderThemeGtk::platformInactiveListBoxSelectionBackgroundColor() const
1897 {
1898     return styleColor(ListBox, GTK_STATE_FLAG_SELECTED, StyleColorBackground);
1899 }
1900
1901 Color RenderThemeGtk::platformActiveListBoxSelectionForegroundColor() const
1902 {
1903     return styleColor(ListBox, static_cast<GtkStateFlags>(GTK_STATE_FLAG_SELECTED | GTK_STATE_FLAG_FOCUSED), StyleColorForeground);
1904 }
1905
1906 Color RenderThemeGtk::platformInactiveListBoxSelectionForegroundColor() const
1907 {
1908     return styleColor(ListBox, GTK_STATE_FLAG_SELECTED, StyleColorForeground);
1909 }
1910
1911 Color RenderThemeGtk::systemColor(CSSValueID cssValueId) const
1912 {
1913     switch (cssValueId) {
1914     case CSSValueButtontext:
1915         return styleColor(Button, GTK_STATE_FLAG_ACTIVE, StyleColorForeground);
1916     case CSSValueCaptiontext:
1917         return styleColor(Entry, GTK_STATE_FLAG_ACTIVE, StyleColorForeground);
1918     default:
1919         return RenderTheme::systemColor(cssValueId);
1920     }
1921 }
1922
1923 void RenderThemeGtk::platformColorsDidChange()
1924 {
1925     RenderTheme::platformColorsDidChange();
1926 }
1927
1928 #if ENABLE(VIDEO)
1929 String RenderThemeGtk::extraMediaControlsStyleSheet()
1930 {
1931     return String(mediaControlsGtkUserAgentStyleSheet, sizeof(mediaControlsGtkUserAgentStyleSheet));
1932 }
1933
1934 #if ENABLE(FULLSCREEN_API)
1935 String RenderThemeGtk::extraFullScreenStyleSheet()
1936 {
1937     return String();
1938 }
1939 #endif
1940
1941 #if GTK_CHECK_VERSION(3, 20, 0)
1942 bool RenderThemeGtk::paintMediaButton(const RenderObject& renderObject, GraphicsContext& graphicsContext, const IntRect& rect, const char* iconName)
1943 {
1944     RenderThemeGadget::Info info = { RenderThemeGadget::Type::Icon, "image", themePartStateFlags(*this, MediaButton, renderObject), { } };
1945     auto gadget = RenderThemeGadget::create(info);
1946     auto* gadgetIcon = static_cast<RenderThemeIconGadget*>(gadget.get());
1947     gadgetIcon->setIconSize(RenderThemeIconGadget::IconSizeGtk::Menu);
1948     gadgetIcon->setIconName(iconName);
1949     return !gadget->render(graphicsContext.platformContext()->cr(), rect);
1950 }
1951 #else
1952 bool RenderThemeGtk::paintMediaButton(const RenderObject& renderObject, GraphicsContext& graphicsContext, const IntRect& rect, const char* iconName)
1953 {
1954     GRefPtr<GtkStyleContext> context = createStyleContext(MediaButton);
1955     gtk_style_context_set_direction(context.get(), gtkTextDirection(renderObject.style().direction()));
1956     gtk_style_context_set_state(context.get(), gtkIconStateFlags(this, renderObject));
1957     static const unsigned mediaIconSize = 16;
1958     IntRect iconRect(rect.x() + (rect.width() - mediaIconSize) / 2, rect.y() + (rect.height() - mediaIconSize) / 2, mediaIconSize, mediaIconSize);
1959     return !paintIcon(context.get(), graphicsContext, iconRect, iconName);
1960 }
1961 #endif // GTK_CHECK_VERSION(3, 20, 0)
1962
1963 bool RenderThemeGtk::hasOwnDisabledStateHandlingFor(ControlPart part) const
1964 {
1965     return (part != MediaMuteButtonPart);
1966 }
1967
1968 bool RenderThemeGtk::paintMediaFullscreenButton(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
1969 {
1970     return paintMediaButton(renderObject, paintInfo.context(), rect, "view-fullscreen-symbolic");
1971 }
1972
1973 bool RenderThemeGtk::paintMediaMuteButton(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
1974 {
1975     Node* node = renderObject.node();
1976     if (!node)
1977         return true;
1978     Node* mediaNode = node->shadowHost();
1979     if (!is<HTMLMediaElement>(mediaNode))
1980         return true;
1981
1982     HTMLMediaElement* mediaElement = downcast<HTMLMediaElement>(mediaNode);
1983     return paintMediaButton(renderObject, paintInfo.context(), rect, mediaElement->muted() ? "audio-volume-muted-symbolic" : "audio-volume-high-symbolic");
1984 }
1985
1986 bool RenderThemeGtk::paintMediaPlayButton(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
1987 {
1988     Node* node = renderObject.node();
1989     if (!node)
1990         return true;
1991     if (!nodeHasPseudo(*node, "-webkit-media-controls-play-button"))
1992         return true;
1993
1994     return paintMediaButton(renderObject, paintInfo.context(), rect, nodeHasClass(node, "paused") ? "media-playback-start-symbolic" : "media-playback-pause-symbolic");
1995 }
1996
1997 bool RenderThemeGtk::paintMediaSeekBackButton(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
1998 {
1999     return paintMediaButton(renderObject, paintInfo.context(), rect, "media-seek-backward-symbolic");
2000 }
2001
2002 bool RenderThemeGtk::paintMediaSeekForwardButton(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
2003 {
2004     return paintMediaButton(renderObject, paintInfo.context(), rect, "media-seek-forward-symbolic");
2005 }
2006
2007 #if ENABLE(VIDEO_TRACK)
2008 bool RenderThemeGtk::paintMediaToggleClosedCaptionsButton(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
2009 {
2010     return paintMediaButton(renderObject, paintInfo.context(), rect, "media-view-subtitles-symbolic");
2011 }
2012 #endif
2013
2014 static FloatRoundedRect::Radii borderRadiiFromStyle(const RenderStyle& style)
2015 {
2016     return FloatRoundedRect::Radii(
2017         IntSize(style.borderTopLeftRadius().width.intValue(), style.borderTopLeftRadius().height.intValue()),
2018         IntSize(style.borderTopRightRadius().width.intValue(), style.borderTopRightRadius().height.intValue()),
2019         IntSize(style.borderBottomLeftRadius().width.intValue(), style.borderBottomLeftRadius().height.intValue()),
2020         IntSize(style.borderBottomRightRadius().width.intValue(), style.borderBottomRightRadius().height.intValue()));
2021 }
2022
2023 bool RenderThemeGtk::paintMediaSliderTrack(const RenderObject& o, const PaintInfo& paintInfo, const IntRect& r)
2024 {
2025     HTMLMediaElement* mediaElement = parentMediaElement(o);
2026     if (!mediaElement)
2027         return true;
2028
2029     GraphicsContext& context = paintInfo.context();
2030     context.save();
2031     context.setStrokeStyle(NoStroke);
2032
2033     float mediaDuration = mediaElement->duration();
2034     float totalTrackWidth = r.width();
2035     auto& style = o.style();
2036     RefPtr<TimeRanges> timeRanges = mediaElement->buffered();
2037     for (unsigned index = 0; index < timeRanges->length(); ++index) {
2038         float start = timeRanges->start(index).releaseReturnValue();
2039         float end = timeRanges->end(index).releaseReturnValue();
2040         float startRatio = start / mediaDuration;
2041         float lengthRatio = (end - start) / mediaDuration;
2042         if (!lengthRatio)
2043             continue;
2044
2045         IntRect rangeRect(r);
2046         rangeRect.setWidth(lengthRatio * totalTrackWidth);
2047         if (index)
2048             rangeRect.move(startRatio * totalTrackWidth, 0);
2049         context.fillRoundedRect(FloatRoundedRect(rangeRect, borderRadiiFromStyle(style)), style.visitedDependentColor(CSSPropertyColor));
2050     }
2051
2052     context.restore();
2053     return false;
2054 }
2055
2056 bool RenderThemeGtk::paintMediaSliderThumb(const RenderObject& o, const PaintInfo& paintInfo, const IntRect& r)
2057 {
2058     auto& style = o.style();
2059     paintInfo.context().fillRoundedRect(FloatRoundedRect(r, borderRadiiFromStyle(style)), style.visitedDependentColor(CSSPropertyColor));
2060     return false;
2061 }
2062
2063 bool RenderThemeGtk::paintMediaVolumeSliderTrack(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
2064 {
2065     HTMLMediaElement* mediaElement = parentMediaElement(renderObject);
2066     if (!mediaElement)
2067         return true;
2068
2069     float volume = mediaElement->muted() ? 0.0f : mediaElement->volume();
2070     if (!volume)
2071         return true;
2072
2073     GraphicsContext& context = paintInfo.context();
2074     context.save();
2075     context.setStrokeStyle(NoStroke);
2076
2077     int rectHeight = rect.height();
2078     float trackHeight = rectHeight * volume;
2079     auto& style = renderObject.style();
2080     IntRect volumeRect(rect);
2081     volumeRect.move(0, rectHeight - trackHeight);
2082     volumeRect.setHeight(ceil(trackHeight));
2083
2084     context.fillRoundedRect(FloatRoundedRect(volumeRect, borderRadiiFromStyle(style)), style.visitedDependentColor(CSSPropertyColor));
2085     context.restore();
2086
2087     return false;
2088 }
2089
2090 bool RenderThemeGtk::paintMediaVolumeSliderThumb(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
2091 {
2092     return paintMediaSliderThumb(renderObject, paintInfo, rect);
2093 }
2094
2095 String RenderThemeGtk::formatMediaControlsCurrentTime(float currentTime, float duration) const
2096 {
2097     return formatMediaControlsTime(currentTime) + " / " + formatMediaControlsTime(duration);
2098 }
2099
2100 bool RenderThemeGtk::paintMediaCurrentTime(const RenderObject&, const PaintInfo&, const IntRect&)
2101 {
2102     return false;
2103 }
2104 #endif
2105
2106 void RenderThemeGtk::adjustProgressBarStyle(StyleResolver&, RenderStyle& style, const Element*) const
2107 {
2108     style.setBoxShadow(nullptr);
2109 }
2110
2111 // These values have been copied from RenderThemeChromiumSkia.cpp
2112 static const int progressActivityBlocks = 5;
2113 static const int progressAnimationFrames = 10;
2114 static const Seconds progressAnimationInterval { 125_ms };
2115 Seconds RenderThemeGtk::animationRepeatIntervalForProgressBar(RenderProgress&) const
2116 {
2117     return progressAnimationInterval;
2118 }
2119
2120 double RenderThemeGtk::animationDurationForProgressBar(RenderProgress&) const
2121 {
2122     return progressAnimationInterval.value() * progressAnimationFrames * 2; // "2" for back and forth;
2123 }
2124
2125 IntRect RenderThemeGtk::calculateProgressRect(const RenderObject& renderObject, const IntRect& fullBarRect)
2126 {
2127     IntRect progressRect(fullBarRect);
2128     const auto& renderProgress = downcast<RenderProgress>(renderObject);
2129     if (renderProgress.isDeterminate()) {
2130         int progressWidth = progressRect.width() * renderProgress.position();
2131         if (renderObject.style().direction() == RTL)
2132             progressRect.setX(progressRect.x() + progressRect.width() - progressWidth);
2133         progressRect.setWidth(progressWidth);
2134         return progressRect;
2135     }
2136
2137     double animationProgress = renderProgress.animationProgress();
2138
2139     // Never let the progress rect shrink smaller than 2 pixels.
2140     int newWidth = std::max(2, progressRect.width() / progressActivityBlocks);
2141     int movableWidth = progressRect.width() - newWidth;
2142     progressRect.setWidth(newWidth);
2143
2144     // We want the first 0.5 units of the animation progress to represent the
2145     // forward motion and the second 0.5 units to represent the backward motion,
2146     // thus we multiply by two here to get the full sweep of the progress bar with
2147     // each direction.
2148     if (animationProgress < 0.5)
2149         progressRect.setX(progressRect.x() + (animationProgress * 2 * movableWidth));
2150     else
2151         progressRect.setX(progressRect.x() + ((1.0 - animationProgress) * 2 * movableWidth));
2152     return progressRect;
2153 }
2154
2155 String RenderThemeGtk::fileListNameForWidth(const FileList* fileList, const FontCascade& font, int width, bool multipleFilesAllowed) const
2156 {
2157     if (width <= 0)
2158         return String();
2159
2160     if (fileList->length() > 1)
2161         return StringTruncator::rightTruncate(multipleFileUploadText(fileList->length()), width, font);
2162
2163     String string;
2164     if (fileList->length())
2165         string = pathGetFileName(fileList->item(0)->path());
2166     else if (multipleFilesAllowed)
2167         string = fileButtonNoFilesSelectedLabel();
2168     else
2169         string = fileButtonNoFileSelectedLabel();
2170
2171     return StringTruncator::centerTruncate(string, width, font);
2172 }
2173
2174 #if ENABLE(VIDEO)
2175 String RenderThemeGtk::mediaControlsScript()
2176 {
2177     StringBuilder scriptBuilder;
2178     scriptBuilder.append(mediaControlsLocalizedStringsJavaScript, sizeof(mediaControlsLocalizedStringsJavaScript));
2179     scriptBuilder.append(mediaControlsBaseJavaScript, sizeof(mediaControlsBaseJavaScript));
2180     scriptBuilder.append(mediaControlsGtkJavaScript, sizeof(mediaControlsGtkJavaScript));
2181     return scriptBuilder.toString();
2182 }
2183 #endif // ENABLE(VIDEO)
2184
2185 #endif // GTK_API_VERSION_2
2186 }