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