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