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