f444c5a1ad9d6beabdad472c45f11404cbad91f4
[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 "HTMLInputElement.h"
37 #include "HTMLMediaElement.h"
38 #include "LocalizedStrings.h"
39 #include "MediaControlElements.h"
40 #include "Page.h"
41 #include "PaintInfo.h"
42 #include "PlatformContextCairo.h"
43 #include "RenderBox.h"
44 #include "RenderObject.h"
45 #include "RenderProgress.h"
46 #include "RenderThemeWidget.h"
47 #include "ScrollbarThemeGtk.h"
48 #include "StringTruncator.h"
49 #include "TimeRanges.h"
50 #include "UserAgentScripts.h"
51 #include "UserAgentStyleSheets.h"
52 #include <cmath>
53 #include <gdk/gdk.h>
54 #include <glib.h>
55 #include <gtk/gtk.h>
56 #include <wtf/FileSystem.h>
57 #include <wtf/glib/GRefPtr.h>
58 #include <wtf/glib/GUniquePtr.h>
59 #include <wtf/text/CString.h>
60 #include <wtf/text/StringBuilder.h>
61
62 namespace WebCore {
63
64 RenderTheme& RenderTheme::singleton()
65 {
66     static NeverDestroyed<RenderThemeGtk> theme;
67     return theme;
68 }
69
70 static double getScreenDPI()
71 {
72     // FIXME: Really this should be the widget's screen.
73     GdkScreen* screen = gdk_screen_get_default();
74     if (!screen)
75         return 96; // Default to 96 DPI.
76
77     float dpi = gdk_screen_get_resolution(screen);
78     if (dpi <= 0)
79         return 96;
80     return dpi;
81 }
82
83 void RenderThemeGtk::updateCachedSystemFontDescription(CSSValueID, FontCascadeDescription& fontDescription) const
84 {
85     GtkSettings* settings = gtk_settings_get_default();
86     if (!settings)
87         return;
88
89     // This will be a font selection string like "Sans 10" so we cannot use it as the family name.
90     GUniqueOutPtr<gchar> fontName;
91     g_object_get(settings, "gtk-font-name", &fontName.outPtr(), nullptr);
92     if (!fontName || !fontName.get()[0])
93         return;
94
95     PangoFontDescription* pangoDescription = pango_font_description_from_string(fontName.get());
96     if (!pangoDescription)
97         return;
98
99     fontDescription.setOneFamily(pango_font_description_get_family(pangoDescription));
100
101     int size = pango_font_description_get_size(pangoDescription) / PANGO_SCALE;
102     // If the size of the font is in points, we need to convert it to pixels.
103     if (!pango_font_description_get_size_is_absolute(pangoDescription))
104         size = size * (getScreenDPI() / 72.0);
105
106     fontDescription.setSpecifiedSize(size);
107     fontDescription.setIsAbsoluteSize(true);
108     fontDescription.setWeight(normalWeightValue());
109     fontDescription.setItalic(FontSelectionValue());
110     pango_font_description_free(pangoDescription);
111 }
112
113 #if ENABLE(DATALIST_ELEMENT)
114 IntSize RenderThemeGtk::sliderTickSize() const
115 {
116     // FIXME: We need to set this to the size of one tick mark.
117     return IntSize(0, 0);
118 }
119
120 int RenderThemeGtk::sliderTickOffsetFromTrackCenter() const
121 {
122     // FIXME: We need to set this to the position of the tick marks.
123     return 0;
124 }
125 #endif
126
127 static void themeChangedCallback()
128 {
129     Page::updateStyleForAllPagesAfterGlobalChangeInEnvironment();
130 }
131
132 RenderThemeGtk::RenderThemeGtk()
133 {
134     static bool themeMonitorInitialized = false;
135     if (!themeMonitorInitialized) {
136         GtkSettings* settings = gtk_settings_get_default();
137         g_signal_connect(settings, "notify::gtk-theme-name", G_CALLBACK(themeChangedCallback), nullptr);
138         g_signal_connect(settings, "notify::gtk-color-scheme", G_CALLBACK(themeChangedCallback), nullptr);
139         themeMonitorInitialized = true;
140     }
141 }
142
143 enum RenderThemePart {
144     Entry,
145     EntrySelection,
146     EntryIconLeft,
147     EntryIconRight,
148     Button,
149     CheckButton,
150     RadioButton,
151     ComboBox,
152     ComboBoxButton,
153     ComboBoxArrow,
154     Scale,
155     ScaleTrough,
156     ScaleSlider,
157     ProgressBar,
158     ProgressBarTrough,
159     ProgressBarProgress,
160     ListBox,
161     SpinButton,
162     SpinButtonUpButton,
163     SpinButtonDownButton,
164 #if ENABLE(VIDEO)
165     MediaButton,
166 #endif
167 #if GTK_CHECK_VERSION(3, 20, 0)
168     Window,
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 #if GTK_CHECK_VERSION(3, 20, 0)
930
931 static IntSize spinButtonSize()
932 {
933     auto& spinButtonWidget = static_cast<RenderThemeSpinButton&>(RenderThemeWidget::getOrCreate(RenderThemeWidget::Type::SpinButton));
934     spinButtonWidget.spinButton().setState(GTK_STATE_FLAG_NORMAL);
935     spinButtonWidget.entry().setState(GTK_STATE_FLAG_NORMAL);
936     spinButtonWidget.up().setState(GTK_STATE_FLAG_NORMAL);
937     spinButtonWidget.down().setState(GTK_STATE_FLAG_NORMAL);
938
939     IntSize preferredSize = spinButtonWidget.spinButton().preferredSize();
940     preferredSize = preferredSize.expandedTo(spinButtonWidget.entry().preferredSize());
941     IntSize upPreferredSize = preferredSize.expandedTo(spinButtonWidget.up().preferredSize());
942     IntSize downPreferredSize = preferredSize.expandedTo(spinButtonWidget.down().preferredSize());
943
944     return IntSize(upPreferredSize.width() + downPreferredSize.width(), std::max(upPreferredSize.height(), downPreferredSize.height()));
945 }
946
947
948 void RenderThemeGtk::adjustTextFieldStyle(StyleResolver&, RenderStyle& style, const Element* element) const
949 {
950     if (!is<HTMLInputElement>(element) || !shouldHaveSpinButton(downcast<HTMLInputElement>(*element)))
951         return;
952
953     style.setMinHeight(Length(spinButtonSize().height(), Fixed));
954
955     // The default theme for the GTK+ port uses very wide spin buttons (66px) compared to what other
956     // browsers use (~13 px). And unfortunately, most of the web developers won't test how their site
957     // renders on WebKitGTK+. To ensure that spin buttons don't end up covering the values of the input
958     // field, we override the width of the input element and always increment it with the width needed
959     // for the spinbutton (when drawing the spinbutton).
960     int minimumWidth = style.width().intValue() + spinButtonSize().width();
961     style.setMinWidth(Length(minimumWidth, Fixed));
962 }
963
964 bool RenderThemeGtk::paintTextField(const RenderObject& renderObject, const PaintInfo& paintInfo, const FloatRect& rect)
965 {
966     if (is<HTMLInputElement>(renderObject.node()) && shouldHaveSpinButton(downcast<HTMLInputElement>(*renderObject.node()))) {
967         auto& spinButtonWidget = static_cast<RenderThemeSpinButton&>(RenderThemeWidget::getOrCreate(RenderThemeWidget::Type::SpinButton));
968         auto spinButtonState = themePartStateFlags(*this, Entry, renderObject);
969         spinButtonWidget.spinButton().setState(spinButtonState);
970         spinButtonWidget.entry().setState(spinButtonState);
971         spinButtonWidget.spinButton().render(paintInfo.context().platformContext()->cr(), rect);
972         spinButtonWidget.entry().render(paintInfo.context().platformContext()->cr(), rect);
973     } else {
974         auto& entryWidget = static_cast<RenderThemeEntry&>(RenderThemeWidget::getOrCreate(RenderThemeWidget::Type::Entry));
975         entryWidget.entry().setState(themePartStateFlags(*this, Entry, renderObject));
976         entryWidget.entry().render(paintInfo.context().platformContext()->cr(), rect);
977     }
978     return false;
979 }
980 #else
981 void RenderThemeGtk::adjustTextFieldStyle(StyleResolver&, RenderStyle&, const Element*) const
982 {
983 }
984
985 bool RenderThemeGtk::paintTextField(const RenderObject& renderObject, const PaintInfo& paintInfo, const FloatRect& rect)
986 {
987     GRefPtr<GtkStyleContext> context = createStyleContext(Entry);
988     gtk_style_context_set_direction(context.get(), static_cast<GtkTextDirection>(gtkTextDirection(renderObject.style().direction())));
989
990     guint flags = 0;
991     if (!isEnabled(renderObject) || isReadOnlyControl(renderObject))
992         flags |= GTK_STATE_FLAG_INSENSITIVE;
993     else if (isFocused(renderObject))
994         flags |= GTK_STATE_FLAG_FOCUSED;
995     gtk_style_context_set_state(context.get(), static_cast<GtkStateFlags>(flags));
996
997     gtk_render_background(context.get(), paintInfo.context().platformContext()->cr(), rect.x(), rect.y(), rect.width(), rect.height());
998     gtk_render_frame(context.get(), paintInfo.context().platformContext()->cr(), rect.x(), rect.y(), rect.width(), rect.height());
999
1000     if (isFocused(renderObject) && isEnabled(renderObject)) {
1001         gboolean interiorFocus;
1002         gint focusWidth, focusPad;
1003         gtk_style_context_get_style(context.get(), "interior-focus", &interiorFocus, "focus-line-width", &focusWidth, "focus-padding", &focusPad, nullptr);
1004         if (!interiorFocus) {
1005             IntRect focusRect(rect);
1006             focusRect.inflate(focusWidth + focusPad);
1007             gtk_render_focus(context.get(), paintInfo.context().platformContext()->cr(), focusRect.x(), focusRect.y(), focusRect.width(), focusRect.height());
1008         }
1009     }
1010
1011     return false;
1012 }
1013 #endif
1014
1015 #if GTK_CHECK_VERSION(3, 20, 0)
1016 static void adjustSearchFieldIconStyle(RenderThemePart themePart, RenderStyle& style)
1017 {
1018     ASSERT(themePart == EntryIconLeft || themePart == EntryIconRight);
1019     auto& searchEntryWidget = static_cast<RenderThemeSearchEntry&>(RenderThemeWidget::getOrCreate(RenderThemeWidget::Type::SearchEntry));
1020     searchEntryWidget.entry().setState(GTK_STATE_FLAG_NORMAL);
1021     searchEntryWidget.leftIcon().setState(GTK_STATE_FLAG_NORMAL);
1022     searchEntryWidget.rightIcon().setState(GTK_STATE_FLAG_NORMAL);
1023
1024     // Get the icon size based on the font size.
1025     auto& icon = static_cast<RenderThemeIconGadget&>(themePart == EntryIconLeft ? searchEntryWidget.leftIcon() : searchEntryWidget.rightIcon());
1026     icon.setIconSize(style.computedFontPixelSize());
1027     IntSize preferredSize = icon.preferredSize();
1028     GtkBorder contentsBox = searchEntryWidget.entry().contentsBox();
1029     if (themePart == EntryIconLeft)
1030         preferredSize.expand(contentsBox.left, contentsBox.top + contentsBox.bottom);
1031     else
1032         preferredSize.expand(contentsBox.right, contentsBox.top + contentsBox.bottom);
1033     style.setWidth(Length(preferredSize.width(), Fixed));
1034     style.setHeight(Length(preferredSize.height(), Fixed));
1035 }
1036 #else
1037 // Defined in GTK+ (gtk/gtkiconfactory.c)
1038 static const gint gtkIconSizeMenu = 16;
1039 static const gint gtkIconSizeSmallToolbar = 18;
1040 static const gint gtkIconSizeButton = 20;
1041 static const gint gtkIconSizeLargeToolbar = 24;
1042 static const gint gtkIconSizeDnd = 32;
1043 static const gint gtkIconSizeDialog = 48;
1044
1045 static GtkIconSize getIconSizeForPixelSize(gint pixelSize)
1046 {
1047     if (pixelSize < gtkIconSizeSmallToolbar)
1048         return GTK_ICON_SIZE_MENU;
1049     if (pixelSize >= gtkIconSizeSmallToolbar && pixelSize < gtkIconSizeButton)
1050         return GTK_ICON_SIZE_SMALL_TOOLBAR;
1051     if (pixelSize >= gtkIconSizeButton && pixelSize < gtkIconSizeLargeToolbar)
1052         return GTK_ICON_SIZE_BUTTON;
1053     if (pixelSize >= gtkIconSizeLargeToolbar && pixelSize < gtkIconSizeDnd)
1054         return GTK_ICON_SIZE_LARGE_TOOLBAR;
1055     if (pixelSize >= gtkIconSizeDnd && pixelSize < gtkIconSizeDialog)
1056         return GTK_ICON_SIZE_DND;
1057
1058     return GTK_ICON_SIZE_DIALOG;
1059 }
1060
1061 static void adjustSearchFieldIconStyle(RenderThemePart themePart, RenderStyle& style)
1062 {
1063     style.resetBorder();
1064     style.resetPadding();
1065
1066     GRefPtr<GtkStyleContext> parentContext = createStyleContext(Entry);
1067     GRefPtr<GtkStyleContext> context = createStyleContext(themePart, parentContext.get());
1068
1069     GtkBorder padding;
1070     gtk_style_context_get_padding(context.get(), gtk_style_context_get_state(context.get()), &padding);
1071
1072     // Get the icon size based on the font size.
1073     int fontSize = style.computedFontPixelSize();
1074     if (fontSize < gtkIconSizeMenu) {
1075         style.setWidth(Length(fontSize + (padding.left + padding.right), Fixed));
1076         style.setHeight(Length(fontSize + (padding.top + padding.bottom), Fixed));
1077         return;
1078     }
1079     gint width = 0, height = 0;
1080     gtk_icon_size_lookup(getIconSizeForPixelSize(fontSize), &width, &height);
1081     style.setWidth(Length(width + (padding.left + padding.right), Fixed));
1082     style.setHeight(Length(height + (padding.top + padding.bottom), Fixed));
1083 }
1084 #endif
1085
1086 bool RenderThemeGtk::paintTextArea(const RenderObject& o, const PaintInfo& i, const FloatRect& r)
1087 {
1088     return paintTextField(o, i, r);
1089 }
1090
1091 void RenderThemeGtk::adjustSearchFieldResultsButtonStyle(StyleResolver& styleResolver, RenderStyle& style, const Element* e) const
1092 {
1093     adjustSearchFieldCancelButtonStyle(styleResolver, style, e);
1094 }
1095
1096 bool RenderThemeGtk::paintSearchFieldResultsButton(const RenderBox& o, const PaintInfo& i, const IntRect& rect)
1097 {
1098     return paintSearchFieldResultsDecorationPart(o, i, rect);
1099 }
1100
1101 void RenderThemeGtk::adjustSearchFieldResultsDecorationPartStyle(StyleResolver&, RenderStyle& style, const Element*) const
1102 {
1103     adjustSearchFieldIconStyle(EntryIconLeft, style);
1104 }
1105
1106 void RenderThemeGtk::adjustSearchFieldCancelButtonStyle(StyleResolver&, RenderStyle& style, const Element*) const
1107 {
1108     adjustSearchFieldIconStyle(EntryIconRight, style);
1109 }
1110
1111 #if GTK_CHECK_VERSION(3, 20, 0)
1112 static bool paintSearchFieldIcon(RenderThemeGtk* theme, RenderThemePart themePart, const RenderBox& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
1113 {
1114     ASSERT(themePart == EntryIconLeft || themePart == EntryIconRight);
1115     auto& searchEntryWidget = static_cast<RenderThemeSearchEntry&>(RenderThemeWidget::getOrCreate(RenderThemeWidget::Type::SearchEntry));
1116     searchEntryWidget.entry().setState(themePartStateFlags(*theme, Entry, renderObject));
1117     auto& icon = static_cast<RenderThemeIconGadget&>(themePart == EntryIconLeft ? searchEntryWidget.leftIcon() : searchEntryWidget.rightIcon());
1118     icon.setState(themePartStateFlags(*theme, themePart, renderObject));
1119     icon.setIconSize(renderObject.style().computedFontPixelSize());
1120     GtkBorder contentsBox = searchEntryWidget.entry().contentsBox();
1121     IntRect iconRect = rect;
1122     if (themePart == EntryIconLeft) {
1123         iconRect.move(contentsBox.left, contentsBox.top);
1124         iconRect.contract(contentsBox.left, contentsBox.top + contentsBox.bottom);
1125     } else
1126         iconRect.contract(contentsBox.right, contentsBox.top + contentsBox.bottom);
1127     return !icon.render(paintInfo.context().platformContext()->cr(), iconRect);
1128 }
1129 bool RenderThemeGtk::paintSearchFieldResultsDecorationPart(const RenderBox& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
1130 {
1131     return paintSearchFieldIcon(this, EntryIconLeft, renderObject, paintInfo, rect);
1132 }
1133
1134 bool RenderThemeGtk::paintSearchFieldCancelButton(const RenderBox& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
1135 {
1136     return paintSearchFieldIcon(this, EntryIconRight, renderObject, paintInfo, rect);
1137 }
1138 #else
1139 static bool paintIcon(GtkStyleContext* context, GraphicsContext& graphicsContext, const IntRect& rect, const char* iconName)
1140 {
1141     GRefPtr<GdkPixbuf> icon = loadThemedIcon(context, iconName, getIconSizeForPixelSize(rect.height()));
1142     if (!icon)
1143         return false;
1144
1145     if (gdk_pixbuf_get_width(icon.get()) > rect.width() || gdk_pixbuf_get_height(icon.get()) > rect.height())
1146         icon = adoptGRef(gdk_pixbuf_scale_simple(icon.get(), rect.width(), rect.height(), GDK_INTERP_BILINEAR));
1147
1148     gtk_render_icon(context, graphicsContext.platformContext()->cr(), icon.get(), rect.x(), rect.y());
1149     return true;
1150 }
1151
1152 static bool paintEntryIcon(RenderThemePart themePart, const char* iconName, GraphicsContext& graphicsContext, const IntRect& rect, GtkTextDirection direction, GtkStateFlags state)
1153 {
1154     GRefPtr<GtkStyleContext> parentContext = createStyleContext(Entry);
1155     GRefPtr<GtkStyleContext> context = createStyleContext(themePart, parentContext.get());
1156     gtk_style_context_set_direction(context.get(), direction);
1157     gtk_style_context_set_state(context.get(), state);
1158     return paintIcon(context.get(), graphicsContext, rect, iconName);
1159 }
1160
1161 static IntRect centerRectVerticallyInParentInputElement(const RenderObject& renderObject, const IntRect& rect)
1162 {
1163     if (!renderObject.node())
1164         return IntRect();
1165
1166     // Get the renderer of <input> element.
1167     Node* input = renderObject.node()->shadowHost();
1168     if (!input)
1169         input = renderObject.node();
1170     if (!is<RenderBox>(*input->renderer()))
1171         return IntRect();
1172
1173     // If possible center the y-coordinate of the rect vertically in the parent input element.
1174     // We also add one pixel here to ensure that the y coordinate is rounded up for box heights
1175     // that are even, which looks in relation to the box text.
1176     IntRect inputContentBox = downcast<RenderBox>(*input->renderer()).absoluteContentBox();
1177
1178     // Make sure the scaled decoration stays square and will fit in its parent's box.
1179     int iconSize = std::min(inputContentBox.width(), std::min(inputContentBox.height(), rect.height()));
1180     IntRect scaledRect(rect.x(), inputContentBox.y() + (inputContentBox.height() - iconSize + 1) / 2, iconSize, iconSize);
1181     return scaledRect;
1182 }
1183
1184 bool RenderThemeGtk::paintSearchFieldResultsDecorationPart(const RenderBox& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
1185 {
1186     IntRect iconRect = centerRectVerticallyInParentInputElement(renderObject, rect);
1187     if (iconRect.isEmpty())
1188         return true;
1189
1190     return !paintEntryIcon(EntryIconLeft, "edit-find-symbolic", paintInfo.context(), iconRect, gtkTextDirection(renderObject.style().direction()),
1191         gtkIconStateFlags(this, renderObject));
1192 }
1193
1194 bool RenderThemeGtk::paintSearchFieldCancelButton(const RenderBox& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
1195 {
1196     IntRect iconRect = centerRectVerticallyInParentInputElement(renderObject, rect);
1197     if (iconRect.isEmpty())
1198         return true;
1199
1200     return !paintEntryIcon(EntryIconRight, "edit-clear-symbolic", paintInfo.context(), iconRect, gtkTextDirection(renderObject.style().direction()),
1201         gtkIconStateFlags(this, renderObject));
1202 }
1203 #endif // GTK_CHECK_VERSION(3, 20, 0)
1204
1205 void RenderThemeGtk::adjustSearchFieldStyle(StyleResolver&, RenderStyle& style, const Element*) const
1206 {
1207     // We cannot give a proper rendering when border radius is active, unfortunately.
1208     style.resetBorderRadius();
1209     style.setLineHeight(RenderStyle::initialLineHeight());
1210 }
1211
1212 bool RenderThemeGtk::paintSearchField(const RenderObject& o, const PaintInfo& i, const IntRect& rect)
1213 {
1214     return paintTextField(o, i, rect);
1215 }
1216
1217 bool RenderThemeGtk::shouldHaveCapsLockIndicator(const HTMLInputElement& element) const
1218 {
1219     return element.isPasswordField();
1220 }
1221
1222 void RenderThemeGtk::adjustSliderTrackStyle(StyleResolver&, RenderStyle& style, const Element*) const
1223 {
1224     style.setBoxShadow(nullptr);
1225 }
1226
1227 void RenderThemeGtk::adjustSliderThumbStyle(StyleResolver& styleResolver, RenderStyle& style, const Element* element) const
1228 {
1229     RenderTheme::adjustSliderThumbStyle(styleResolver, style, element);
1230     style.setBoxShadow(nullptr);
1231 }
1232
1233 #if GTK_CHECK_VERSION(3, 20, 0)
1234 /*
1235  * GtkScale
1236  *
1237  * scale
1238  * ╰── contents
1239  *     ╰── trough
1240  *         ├── slider
1241  *         ╰── [highlight]
1242  */
1243 bool RenderThemeGtk::paintSliderTrack(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
1244 {
1245     ControlPart part = renderObject.style().appearance();
1246     ASSERT(part == SliderHorizontalPart || part == SliderVerticalPart);
1247
1248     auto& sliderWidget = static_cast<RenderThemeSlider&>(RenderThemeWidget::getOrCreate(part == SliderHorizontalPart ? RenderThemeWidget::Type::HorizontalSlider : RenderThemeWidget::Type::VerticalSlider));
1249     auto scaleState = themePartStateFlags(*this, Scale, renderObject);
1250     auto& scale = sliderWidget.scale();
1251     scale.setState(scaleState);
1252     auto& contents = sliderWidget.contents();
1253     auto& trough = sliderWidget.trough();
1254     trough.setState(scaleState);
1255     auto& slider = sliderWidget.slider();
1256     auto& highlight = sliderWidget.highlight();
1257
1258     // The given rectangle is not calculated based on the scale size, but all the margins and paddings are based on it.
1259     IntSize preferredSize = scale.preferredSize();
1260     preferredSize = preferredSize.expandedTo(contents.preferredSize());
1261     preferredSize = preferredSize.expandedTo(trough.preferredSize());
1262     FloatRect trackRect = rect;
1263     if (part == SliderHorizontalPart) {
1264         trackRect.move(0, rect.height() / 2 - (preferredSize.height() / 2));
1265         trackRect.setHeight(preferredSize.height());
1266     } else {
1267         trackRect.move(rect.width() / 2 - (preferredSize.width() / 2), 0);
1268         trackRect.setWidth(preferredSize.width());
1269     }
1270
1271     FloatRect contentsRect;
1272     scale.render(paintInfo.context().platformContext()->cr(), trackRect, &contentsRect);
1273     contents.render(paintInfo.context().platformContext()->cr(), contentsRect, &contentsRect);
1274     // Scale trough defines its size querying slider and highlight.
1275     if (part == SliderHorizontalPart)
1276         contentsRect.setHeight(trough.preferredSize().height() + std::max(slider.preferredSize().height(), highlight.preferredSize().height()));
1277     else
1278         contentsRect.setWidth(trough.preferredSize().width() + std::max(slider.preferredSize().width(), highlight.preferredSize().width()));
1279     FloatRect troughRect = contentsRect;
1280     trough.render(paintInfo.context().platformContext()->cr(), troughRect, &contentsRect);
1281     if (isFocused(renderObject))
1282         trough.renderFocus(paintInfo.context().platformContext()->cr(), troughRect);
1283
1284     LayoutPoint thumbLocation;
1285     if (is<HTMLInputElement>(renderObject.node())) {
1286         auto& input = downcast<HTMLInputElement>(*renderObject.node());
1287         if (auto* element = input.sliderThumbElement())
1288             thumbLocation = element->renderBox()->location();
1289     }
1290
1291     if (part == SliderHorizontalPart) {
1292         if (renderObject.style().direction() == TextDirection::RTL) {
1293             contentsRect.move(thumbLocation.x(), 0);
1294             contentsRect.setWidth(contentsRect.width() - thumbLocation.x());
1295         } else
1296             contentsRect.setWidth(thumbLocation.x());
1297     } else
1298         contentsRect.setHeight(thumbLocation.y());
1299     highlight.render(paintInfo.context().platformContext()->cr(), contentsRect);
1300
1301     return false;
1302 }
1303
1304 void RenderThemeGtk::adjustSliderThumbSize(RenderStyle& style, const Element*) const
1305 {
1306     ControlPart part = style.appearance();
1307     if (part != SliderThumbHorizontalPart && part != SliderThumbVerticalPart)
1308         return;
1309
1310     auto& sliderWidget = static_cast<RenderThemeSlider&>(RenderThemeWidget::getOrCreate(part == SliderThumbHorizontalPart ? RenderThemeWidget::Type::HorizontalSlider : RenderThemeWidget::Type::VerticalSlider));
1311     sliderWidget.scale().setState(GTK_STATE_FLAG_NORMAL);
1312     sliderWidget.trough().setState(GTK_STATE_FLAG_NORMAL);
1313
1314     IntSize preferredSize = sliderWidget.scale().preferredSize();
1315     preferredSize = preferredSize.expandedTo(sliderWidget.contents().preferredSize());
1316     preferredSize = preferredSize.expandedTo(sliderWidget.trough().preferredSize());
1317     preferredSize = preferredSize.expandedTo(sliderWidget.slider().preferredSize());
1318     if (part == SliderThumbHorizontalPart) {
1319         style.setWidth(Length(preferredSize.width(), Fixed));
1320         style.setHeight(Length(preferredSize.height(), Fixed));
1321         return;
1322     }
1323     ASSERT(part == SliderThumbVerticalPart);
1324     style.setWidth(Length(preferredSize.height(), Fixed));
1325     style.setHeight(Length(preferredSize.width(), Fixed));
1326 }
1327
1328 bool RenderThemeGtk::paintSliderThumb(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
1329 {
1330     ControlPart part = renderObject.style().appearance();
1331     ASSERT(part == SliderThumbHorizontalPart || part == SliderThumbVerticalPart);
1332
1333     auto& sliderWidget = static_cast<RenderThemeSlider&>(RenderThemeWidget::getOrCreate(part == SliderThumbHorizontalPart ? RenderThemeWidget::Type::HorizontalSlider : RenderThemeWidget::Type::VerticalSlider));
1334     auto scaleState = themePartStateFlags(*this, Scale, renderObject);
1335     auto& scale = sliderWidget.scale();
1336     scale.setState(scaleState);
1337     auto& contents = sliderWidget.contents();
1338     auto& trough = sliderWidget.trough();
1339     trough.setState(scaleState);
1340     auto& slider = sliderWidget.slider();
1341     slider.setState(themePartStateFlags(*this, ScaleSlider, renderObject));
1342     auto& highlight = sliderWidget.highlight();
1343
1344     GtkBorder scaleContentsBox = scale.contentsBox();
1345     GtkBorder contentsContentsBox = contents.contentsBox();
1346     GtkBorder troughContentsBox = trough.contentsBox();
1347     GtkBorder padding;
1348     padding.left = scaleContentsBox.left + contentsContentsBox.left + troughContentsBox.left;
1349     padding.right = scaleContentsBox.right + contentsContentsBox.right + troughContentsBox.right;
1350     padding.top = scaleContentsBox.top + contentsContentsBox.top + troughContentsBox.top;
1351     padding.bottom = scaleContentsBox.bottom + contentsContentsBox.bottom + troughContentsBox.bottom;
1352
1353     // Scale trough defines its size querying slider and highlight.
1354     int troughHeight = trough.preferredSize().height() + std::max(slider.preferredSize().height(), highlight.preferredSize().height());
1355     IntRect sliderRect(rect.location(), IntSize(troughHeight, troughHeight));
1356     sliderRect.move(padding.left, padding.top);
1357     sliderRect.contract(padding.left + padding.right, padding.top + padding.bottom);
1358     slider.render(paintInfo.context().platformContext()->cr(), sliderRect);
1359     return false;
1360 }
1361 #else
1362 bool RenderThemeGtk::paintSliderTrack(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
1363 {
1364     ControlPart part = renderObject.style().appearance();
1365     ASSERT(part == SliderHorizontalPart || part == SliderVerticalPart);
1366
1367     GRefPtr<GtkStyleContext> parentContext = createStyleContext(Scale);
1368     gtk_style_context_add_class(parentContext.get(), part == SliderHorizontalPart ? GTK_STYLE_CLASS_HORIZONTAL : GTK_STYLE_CLASS_VERTICAL);
1369     GRefPtr<GtkStyleContext> context = createStyleContext(ScaleTrough, parentContext.get());
1370     gtk_style_context_set_direction(context.get(), gtkTextDirection(renderObject.style().direction()));
1371
1372     if (!isEnabled(renderObject))
1373         gtk_style_context_set_state(context.get(), GTK_STATE_FLAG_INSENSITIVE);
1374
1375     IntRect sliderRect = rect;
1376     // GTK+ uses the slider thumb size and margins to calculate the trough size, but in WebKit we render the thumb and
1377     // the slider track separately and the track rectangle we receive here can't be used to apply the GTK+ CSS sizes
1378     // and margins. So we use a maximum fixed size for the trough to match at least Adwaita, but that should look
1379     // good in other themes as well.
1380     static const int sliderSize = 4;
1381
1382     if (part == SliderHorizontalPart) {
1383         sliderRect.setHeight(std::min(rect.height(), sliderSize));
1384         sliderRect.move(0, (rect.height() - sliderRect.height()) / 2);
1385     } else {
1386         sliderRect.setWidth(std::min(rect.width(), sliderSize));
1387         sliderRect.move((rect.width() - sliderRect.width()) / 2, 0);
1388     }
1389
1390     gtk_render_background(context.get(), paintInfo.context().platformContext()->cr(), sliderRect.x(), sliderRect.y(), sliderRect.width(), sliderRect.height());
1391     gtk_render_frame(context.get(), paintInfo.context().platformContext()->cr(), sliderRect.x(), sliderRect.y(), sliderRect.width(), sliderRect.height());
1392
1393     if (isFocused(renderObject)) {
1394         gint focusWidth, focusPad;
1395         gtk_style_context_get_style(context.get(), "focus-line-width", &focusWidth, "focus-padding", &focusPad, nullptr);
1396         IntRect focusRect(sliderRect);
1397         focusRect.inflate(focusWidth + focusPad);
1398         gtk_render_focus(context.get(), paintInfo.context().platformContext()->cr(), focusRect.x(), focusRect.y(), focusRect.width(), focusRect.height());
1399     }
1400
1401     return false;
1402 }
1403
1404 void RenderThemeGtk::adjustSliderThumbSize(RenderStyle& style, const Element*) const
1405 {
1406     ControlPart part = style.appearance();
1407     if (part != SliderThumbHorizontalPart && part != SliderThumbVerticalPart)
1408         return;
1409
1410     GRefPtr<GtkStyleContext> context = createStyleContext(Scale);
1411     gint sliderWidth, sliderLength;
1412     gtk_style_context_get_style(context.get(), "slider-width", &sliderWidth, "slider-length", &sliderLength, nullptr);
1413
1414     if (part == SliderThumbHorizontalPart) {
1415         style.setWidth(Length(sliderLength, Fixed));
1416         style.setHeight(Length(sliderWidth, Fixed));
1417         return;
1418     }
1419     ASSERT(part == SliderThumbVerticalPart);
1420     style.setWidth(Length(sliderWidth, Fixed));
1421     style.setHeight(Length(sliderLength, Fixed));
1422 }
1423
1424 bool RenderThemeGtk::paintSliderThumb(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
1425 {
1426     ControlPart part = renderObject.style().appearance();
1427     ASSERT(part == SliderThumbHorizontalPart || part == SliderThumbVerticalPart);
1428
1429     // FIXME: The entire slider is too wide, stretching the thumb into an oval rather than a circle.
1430     GRefPtr<GtkStyleContext> parentContext = createStyleContext(Scale);
1431     gtk_style_context_add_class(parentContext.get(), part == SliderThumbHorizontalPart ? GTK_STYLE_CLASS_HORIZONTAL : GTK_STYLE_CLASS_VERTICAL);
1432     GRefPtr<GtkStyleContext> troughContext = createStyleContext(ScaleTrough, parentContext.get());
1433     GRefPtr<GtkStyleContext> context = createStyleContext(ScaleSlider, troughContext.get());
1434     gtk_style_context_set_direction(context.get(), gtkTextDirection(renderObject.style().direction()));
1435
1436     guint flags = 0;
1437     if (!isEnabled(renderObject))
1438         flags |= GTK_STATE_FLAG_INSENSITIVE;
1439     else if (isHovered(renderObject))
1440         flags |= GTK_STATE_FLAG_PRELIGHT;
1441     if (isPressed(renderObject))
1442         flags |= GTK_STATE_FLAG_ACTIVE;
1443     gtk_style_context_set_state(context.get(), static_cast<GtkStateFlags>(flags));
1444
1445     gtk_render_slider(context.get(), paintInfo.context().platformContext()->cr(), rect.x(), rect.y(), rect.width(), rect.height(),
1446         part == SliderThumbHorizontalPart ? GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL);
1447
1448     return false;
1449 }
1450 #endif
1451
1452 #if GTK_CHECK_VERSION(3, 20, 0)
1453 IntRect RenderThemeGtk::progressBarRectForBounds(const RenderObject& renderObject, const IntRect& bounds) const
1454 {
1455     const auto& renderProgress = downcast<RenderProgress>(renderObject);
1456     auto& progressBarWidget = static_cast<RenderThemeProgressBar&>(RenderThemeWidget::getOrCreate(renderProgress.isDeterminate() ? RenderThemeProgressBar::Type::ProgressBar : RenderThemeProgressBar::Type::IndeterminateProgressBar));
1457     IntSize preferredSize = progressBarWidget.progressBar().preferredSize();
1458     preferredSize = preferredSize.expandedTo(progressBarWidget.trough().preferredSize());
1459     preferredSize = preferredSize.expandedTo(progressBarWidget.progress().preferredSize());
1460     return IntRect(bounds.x(), bounds.y(), bounds.width(), preferredSize.height());
1461 }
1462
1463 bool RenderThemeGtk::paintProgressBar(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
1464 {
1465     if (!renderObject.isProgress())
1466         return true;
1467
1468     const auto& renderProgress = downcast<RenderProgress>(renderObject);
1469     auto& progressBarWidget = static_cast<RenderThemeProgressBar&>(RenderThemeWidget::getOrCreate(renderProgress.isDeterminate() ? RenderThemeProgressBar::Type::ProgressBar : RenderThemeProgressBar::Type::IndeterminateProgressBar));
1470     progressBarWidget.progressBar().render(paintInfo.context().platformContext()->cr(), rect);
1471     progressBarWidget.trough().render(paintInfo.context().platformContext()->cr(), rect);
1472     progressBarWidget.progress().render(paintInfo.context().platformContext()->cr(), calculateProgressRect(renderObject, rect));
1473     return false;
1474 }
1475 #else
1476 IntRect RenderThemeGtk::progressBarRectForBounds(const RenderObject&, const IntRect& bounds) const
1477 {
1478     return bounds;
1479 }
1480
1481 bool RenderThemeGtk::paintProgressBar(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
1482 {
1483     if (!renderObject.isProgress())
1484         return true;
1485
1486     GRefPtr<GtkStyleContext> parentContext = createStyleContext(ProgressBar);
1487     GRefPtr<GtkStyleContext> troughContext = createStyleContext(ProgressBarTrough, parentContext.get());
1488     GRefPtr<GtkStyleContext> context = createStyleContext(ProgressBarProgress, troughContext.get());
1489
1490     gtk_render_background(troughContext.get(), paintInfo.context().platformContext()->cr(), rect.x(), rect.y(), rect.width(), rect.height());
1491     gtk_render_frame(troughContext.get(), paintInfo.context().platformContext()->cr(), rect.x(), rect.y(), rect.width(), rect.height());
1492
1493     gtk_style_context_set_state(context.get(), static_cast<GtkStateFlags>(0));
1494
1495     GtkBorder padding;
1496     gtk_style_context_get_padding(context.get(), gtk_style_context_get_state(context.get()), &padding);
1497     IntRect progressRect(
1498         rect.x() + padding.left,
1499         rect.y() + padding.top,
1500         rect.width() - (padding.left + padding.right),
1501         rect.height() - (padding.top + padding.bottom));
1502     progressRect = RenderThemeGtk::calculateProgressRect(renderObject, progressRect);
1503
1504     if (!progressRect.isEmpty()) {
1505 #if GTK_CHECK_VERSION(3, 13, 7)
1506         gtk_render_background(context.get(), paintInfo.context().platformContext()->cr(), progressRect.x(), progressRect.y(), progressRect.width(), progressRect.height());
1507         gtk_render_frame(context.get(), paintInfo.context().platformContext()->cr(), progressRect.x(), progressRect.y(), progressRect.width(), progressRect.height());
1508 #else
1509         gtk_render_activity(context.get(), paintInfo.context().platformContext()->cr(), progressRect.x(), progressRect.y(), progressRect.width(), progressRect.height());
1510 #endif
1511     }
1512
1513     return false;
1514 }
1515 #endif // GTK_CHECK_VERSION(3, 20, 0)
1516
1517 #if GTK_CHECK_VERSION(3, 20, 0)
1518 RenderTheme::InnerSpinButtonLayout RenderThemeGtk::innerSpinButtonLayout(const RenderObject& renderObject) const
1519 {
1520     return renderObject.style().direction() == TextDirection::RTL ? InnerSpinButtonLayout::HorizontalUpLeft : InnerSpinButtonLayout::HorizontalUpRight;
1521 }
1522
1523 void RenderThemeGtk::adjustInnerSpinButtonStyle(StyleResolver&, RenderStyle& style, const Element*) const
1524 {
1525     style.setWidth(Length(spinButtonSize().width(), Fixed));
1526     style.setHeight(Length(spinButtonSize().height(), Fixed));
1527 }
1528
1529 bool RenderThemeGtk::paintInnerSpinButton(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
1530 {
1531     auto& spinButtonWidget = static_cast<RenderThemeSpinButton&>(RenderThemeWidget::getOrCreate(RenderThemeWidget::Type::SpinButton));
1532     auto spinButtonState = themePartStateFlags(*this, SpinButton, renderObject);
1533     spinButtonWidget.spinButton().setState(spinButtonState);
1534     spinButtonWidget.entry().setState(spinButtonState);
1535     auto& up = spinButtonWidget.up();
1536     up.setState(themePartStateFlags(*this, SpinButtonUpButton, renderObject));
1537     auto& down = spinButtonWidget.down();
1538     down.setState(themePartStateFlags(*this, SpinButtonDownButton, renderObject));
1539
1540     IntRect iconRect = rect;
1541     iconRect.setWidth(iconRect.width() / 2);
1542     if (renderObject.style().direction() == TextDirection::RTL)
1543         up.render(paintInfo.context().platformContext()->cr(), iconRect);
1544     else
1545         down.render(paintInfo.context().platformContext()->cr(), iconRect);
1546     iconRect.move(iconRect.width(), 0);
1547     if (renderObject.style().direction() == TextDirection::RTL)
1548         down.render(paintInfo.context().platformContext()->cr(), iconRect);
1549     else
1550         up.render(paintInfo.context().platformContext()->cr(), iconRect);
1551
1552     return false;
1553 }
1554 #else
1555 RenderTheme::InnerSpinButtonLayout RenderThemeGtk::innerSpinButtonLayout(const RenderObject&) const
1556 {
1557     return InnerSpinButtonLayout::Vertical;
1558 }
1559 static gint spinButtonArrowSize(GtkStyleContext* context)
1560 {
1561     PangoFontDescription* fontDescription;
1562     gtk_style_context_get(context, gtk_style_context_get_state(context), "font", &fontDescription, nullptr);
1563     gint fontSize = pango_font_description_get_size(fontDescription);
1564     gint arrowSize = std::max(PANGO_PIXELS(fontSize), minSpinButtonArrowSize);
1565     pango_font_description_free(fontDescription);
1566
1567     return arrowSize - arrowSize % 2; // Force even.
1568 }
1569
1570 void RenderThemeGtk::adjustInnerSpinButtonStyle(StyleResolver&, RenderStyle& style, const Element*) const
1571 {
1572     GRefPtr<GtkStyleContext> context = createStyleContext(SpinButton);
1573
1574     GtkBorder padding;
1575     gtk_style_context_get_padding(context.get(), gtk_style_context_get_state(context.get()), &padding);
1576
1577     int width = spinButtonArrowSize(context.get()) + padding.left + padding.right;
1578     style.setWidth(Length(width, Fixed));
1579     style.setMinWidth(Length(width, Fixed));
1580 }
1581
1582 static void paintSpinArrowButton(RenderTheme* theme, GtkStyleContext* parentContext, const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect, GtkArrowType arrowType)
1583 {
1584     ASSERT(arrowType == GTK_ARROW_UP || arrowType == GTK_ARROW_DOWN);
1585
1586     GRefPtr<GtkStyleContext> context = createStyleContext(arrowType == GTK_ARROW_UP ? SpinButtonUpButton : SpinButtonDownButton, parentContext);
1587     GtkTextDirection direction = gtk_style_context_get_direction(context.get());
1588     guint state = static_cast<guint>(gtk_style_context_get_state(context.get()));
1589     if (!(state & GTK_STATE_FLAG_INSENSITIVE)) {
1590         if (theme->isPressed(renderObject)) {
1591             if ((arrowType == GTK_ARROW_UP && theme->isSpinUpButtonPartPressed(renderObject))
1592                 || (arrowType == GTK_ARROW_DOWN && !theme->isSpinUpButtonPartPressed(renderObject)))
1593                 state |= GTK_STATE_FLAG_ACTIVE;
1594         } else if (theme->isHovered(renderObject)) {
1595             if ((arrowType == GTK_ARROW_UP && theme->isSpinUpButtonPartHovered(renderObject))
1596                 || (arrowType == GTK_ARROW_DOWN && !theme->isSpinUpButtonPartHovered(renderObject)))
1597                 state |= GTK_STATE_FLAG_PRELIGHT;
1598         }
1599     }
1600     gtk_style_context_set_state(context.get(), static_cast<GtkStateFlags>(state));
1601
1602     // Paint button.
1603     IntRect buttonRect(rect);
1604     guint junction = gtk_style_context_get_junction_sides(context.get());
1605     if (arrowType == GTK_ARROW_UP)
1606         junction |= GTK_JUNCTION_BOTTOM;
1607     else {
1608         junction |= GTK_JUNCTION_TOP;
1609         buttonRect.move(0, rect.height() / 2);
1610     }
1611     buttonRect.setHeight(rect.height() / 2);
1612     gtk_style_context_set_junction_sides(context.get(), static_cast<GtkJunctionSides>(junction));
1613
1614     gtk_render_background(context.get(), paintInfo.context().platformContext()->cr(), buttonRect.x(), buttonRect.y(), buttonRect.width(), buttonRect.height());
1615     gtk_render_frame(context.get(), paintInfo.context().platformContext()->cr(), buttonRect.x(), buttonRect.y(), buttonRect.width(), buttonRect.height());
1616
1617     // Paint arrow centered inside button.
1618     // This code is based on gtkspinbutton.c code.
1619     IntRect arrowRect;
1620     gdouble angle;
1621     if (arrowType == GTK_ARROW_UP) {
1622         angle = 0;
1623         arrowRect.setY(rect.y());
1624         arrowRect.setHeight(rect.height() / 2 - 2);
1625     } else {
1626         angle = G_PI;
1627         arrowRect.setY(rect.y() + buttonRect.y());
1628         arrowRect.setHeight(rect.height() - arrowRect.y() - 2);
1629     }
1630     arrowRect.setWidth(rect.width() - 3);
1631     if (direction == GTK_TEXT_DIR_LTR)
1632         arrowRect.setX(rect.x() + 1);
1633     else
1634         arrowRect.setX(rect.x() + 2);
1635
1636     gint width = arrowRect.width() / 2;
1637     width -= width % 2 - 1; // Force odd.
1638     gint height = (width + 1) / 2;
1639
1640     arrowRect.move((arrowRect.width() - width) / 2, (arrowRect.height() - height) / 2);
1641     gtk_render_arrow(context.get(), paintInfo.context().platformContext()->cr(), angle, arrowRect.x(), arrowRect.y(), width);
1642 }
1643
1644 bool RenderThemeGtk::paintInnerSpinButton(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
1645 {
1646     GRefPtr<GtkStyleContext> context = createStyleContext(SpinButton);
1647     gtk_style_context_set_direction(context.get(), gtkTextDirection(renderObject.style().direction()));
1648
1649     guint flags = 0;
1650     if (!isEnabled(renderObject) || isReadOnlyControl(renderObject))
1651         flags |= GTK_STATE_FLAG_INSENSITIVE;
1652     else if (isFocused(renderObject))
1653         flags |= GTK_STATE_FLAG_FOCUSED;
1654     gtk_style_context_set_state(context.get(), static_cast<GtkStateFlags>(flags));
1655
1656     paintSpinArrowButton(this, context.get(), renderObject, paintInfo, rect, GTK_ARROW_UP);
1657     paintSpinArrowButton(this, context.get(), renderObject, paintInfo, rect, GTK_ARROW_DOWN);
1658
1659     return false;
1660 }
1661 #endif // GTK_CHECK_VERSION(3, 20, 0)
1662
1663 Seconds RenderThemeGtk::caretBlinkInterval() const
1664 {
1665     GtkSettings* settings = gtk_settings_get_default();
1666
1667     gboolean shouldBlink;
1668     gint time;
1669
1670     g_object_get(settings, "gtk-cursor-blink", &shouldBlink, "gtk-cursor-blink-time", &time, nullptr);
1671
1672     if (!shouldBlink)
1673         return 0_s;
1674
1675     return 500_us * time;
1676 }
1677
1678 enum StyleColorType { StyleColorBackground, StyleColorForeground };
1679
1680 #if GTK_CHECK_VERSION(3, 20, 0)
1681 static Color styleColor(RenderThemePart themePart, GtkStateFlags state, StyleColorType colorType)
1682 {
1683     RenderThemeGadget* gadget = nullptr;
1684     switch (themePart) {
1685     default:
1686         ASSERT_NOT_REACHED();
1687         FALLTHROUGH;
1688     case Entry:
1689         gadget = &static_cast<RenderThemeEntry&>(RenderThemeWidget::getOrCreate(RenderThemeWidget::Type::Entry)).entry();
1690         break;
1691     case EntrySelection:
1692         gadget = static_cast<RenderThemeEntry&>(RenderThemeWidget::getOrCreate(RenderThemeWidget::Type::SelectedEntry)).selection();
1693         break;
1694     case ListBox:
1695         gadget = &static_cast<RenderThemeListView&>(RenderThemeWidget::getOrCreate(RenderThemeWidget::Type::ListView)).treeview();
1696         break;
1697     case Button:
1698         gadget = &static_cast<RenderThemeButton&>(RenderThemeWidget::getOrCreate(RenderThemeWidget::Type::Button)).button();
1699         break;
1700     case Window:
1701         gadget = &static_cast<RenderThemeWindow&>(RenderThemeWidget::getOrCreate(RenderThemeWidget::Type::Window)).window();
1702         break;
1703     }
1704
1705     ASSERT(gadget);
1706     gadget->setState(state);
1707     return colorType == StyleColorBackground ? gadget->backgroundColor() : gadget->color();
1708 }
1709 #else
1710 static Color styleColor(RenderThemePart themePart, GtkStateFlags state, StyleColorType colorType)
1711 {
1712     GRefPtr<GtkStyleContext> context = createStyleContext(themePart);
1713     gtk_style_context_set_state(context.get(), state);
1714
1715     GdkRGBA gdkRGBAColor;
1716     if (colorType == StyleColorBackground)
1717         gtk_style_context_get_background_color(context.get(), state, &gdkRGBAColor);
1718     else
1719         gtk_style_context_get_color(context.get(), state, &gdkRGBAColor);
1720     return gdkRGBAColor;
1721 }
1722 #endif // GTK_CHECK_VERSION(3, 20, 0)
1723
1724 Color RenderThemeGtk::platformActiveSelectionBackgroundColor(OptionSet<StyleColor::Options>) const
1725 {
1726     return styleColor(EntrySelection, static_cast<GtkStateFlags>(GTK_STATE_FLAG_SELECTED | GTK_STATE_FLAG_FOCUSED), StyleColorBackground);
1727 }
1728
1729 Color RenderThemeGtk::platformInactiveSelectionBackgroundColor(OptionSet<StyleColor::Options>) const
1730 {
1731     return styleColor(EntrySelection, GTK_STATE_FLAG_SELECTED, StyleColorBackground);
1732 }
1733
1734 Color RenderThemeGtk::platformActiveSelectionForegroundColor(OptionSet<StyleColor::Options>) const
1735 {
1736     return styleColor(EntrySelection, static_cast<GtkStateFlags>(GTK_STATE_FLAG_SELECTED | GTK_STATE_FLAG_FOCUSED), StyleColorForeground);
1737 }
1738
1739 Color RenderThemeGtk::platformInactiveSelectionForegroundColor(OptionSet<StyleColor::Options>) const
1740 {
1741     return styleColor(EntrySelection, GTK_STATE_FLAG_SELECTED, StyleColorForeground);
1742 }
1743
1744 Color RenderThemeGtk::platformActiveListBoxSelectionBackgroundColor(OptionSet<StyleColor::Options>) const
1745 {
1746     return styleColor(ListBox, static_cast<GtkStateFlags>(GTK_STATE_FLAG_SELECTED | GTK_STATE_FLAG_FOCUSED), StyleColorBackground);
1747 }
1748
1749 Color RenderThemeGtk::platformInactiveListBoxSelectionBackgroundColor(OptionSet<StyleColor::Options>) const
1750 {
1751     return styleColor(ListBox, GTK_STATE_FLAG_SELECTED, StyleColorBackground);
1752 }
1753
1754 Color RenderThemeGtk::platformActiveListBoxSelectionForegroundColor(OptionSet<StyleColor::Options>) const
1755 {
1756     return styleColor(ListBox, static_cast<GtkStateFlags>(GTK_STATE_FLAG_SELECTED | GTK_STATE_FLAG_FOCUSED), StyleColorForeground);
1757 }
1758
1759 Color RenderThemeGtk::platformInactiveListBoxSelectionForegroundColor(OptionSet<StyleColor::Options>) const
1760 {
1761     return styleColor(ListBox, GTK_STATE_FLAG_SELECTED, StyleColorForeground);
1762 }
1763
1764 Color RenderThemeGtk::disabledTextColor(const Color&, const Color&) const
1765 {
1766     return styleColor(Entry, GTK_STATE_FLAG_INSENSITIVE, StyleColorForeground);
1767 }
1768
1769 Color RenderThemeGtk::systemColor(CSSValueID cssValueId, OptionSet<StyleColor::Options> options) const
1770 {
1771     switch (cssValueId) {
1772     case CSSValueButtontext:
1773         return styleColor(Button, GTK_STATE_FLAG_ACTIVE, StyleColorForeground);
1774     case CSSValueCaptiontext:
1775         return styleColor(Entry, GTK_STATE_FLAG_ACTIVE, StyleColorForeground);
1776     case CSSValueText:
1777         return styleColor(Entry, GTK_STATE_FLAG_ACTIVE, StyleColorForeground);
1778     case CSSValueGraytext:
1779         return styleColor(Entry, GTK_STATE_FLAG_INSENSITIVE, StyleColorForeground);
1780     case CSSValueWebkitControlBackground:
1781         return styleColor(Entry, GTK_STATE_FLAG_ACTIVE, StyleColorBackground);
1782 #if GTK_CHECK_VERSION(3, 20, 0)
1783     case CSSValueWindow: {
1784         // Only get window color from the theme in dark mode.
1785         gboolean preferDarkTheme = FALSE;
1786         if (auto* settings = gtk_settings_get_default())
1787             g_object_get(settings, "gtk-application-prefer-dark-theme", &preferDarkTheme, nullptr);
1788         if (preferDarkTheme)
1789             return styleColor(Window, GTK_STATE_FLAG_ACTIVE, StyleColorBackground);
1790         break;
1791     }
1792 #endif
1793     default:
1794         break;
1795     }
1796
1797     return RenderTheme::systemColor(cssValueId, options);
1798 }
1799
1800 void RenderThemeGtk::platformColorsDidChange()
1801 {
1802     RenderTheme::platformColorsDidChange();
1803 }
1804
1805 #if ENABLE(VIDEO)
1806 String RenderThemeGtk::extraMediaControlsStyleSheet()
1807 {
1808     return String(mediaControlsGtkUserAgentStyleSheet, sizeof(mediaControlsGtkUserAgentStyleSheet));
1809 }
1810
1811 #if ENABLE(FULLSCREEN_API)
1812 String RenderThemeGtk::extraFullScreenStyleSheet()
1813 {
1814     return String();
1815 }
1816 #endif
1817
1818 #if GTK_CHECK_VERSION(3, 20, 0)
1819 bool RenderThemeGtk::paintMediaButton(const RenderObject& renderObject, GraphicsContext& graphicsContext, const IntRect& rect, const char* iconName)
1820 {
1821     auto& iconWidget = static_cast<RenderThemeIcon&>(RenderThemeWidget::getOrCreate(RenderThemeWidget::Type::Icon));
1822     auto& icon = static_cast<RenderThemeIconGadget&>(iconWidget.icon());
1823     icon.setState(themePartStateFlags(*this, MediaButton, renderObject));
1824     icon.setIconSize(RenderThemeIconGadget::IconSizeGtk::Menu);
1825     icon.setIconName(iconName);
1826     return !icon.render(graphicsContext.platformContext()->cr(), rect);
1827 }
1828 #else
1829 bool RenderThemeGtk::paintMediaButton(const RenderObject& renderObject, GraphicsContext& graphicsContext, const IntRect& rect, const char* iconName)
1830 {
1831     GRefPtr<GtkStyleContext> context = createStyleContext(MediaButton);
1832     gtk_style_context_set_direction(context.get(), gtkTextDirection(renderObject.style().direction()));
1833     gtk_style_context_set_state(context.get(), gtkIconStateFlags(this, renderObject));
1834     static const unsigned mediaIconSize = 16;
1835     IntRect iconRect(rect.x() + (rect.width() - mediaIconSize) / 2, rect.y() + (rect.height() - mediaIconSize) / 2, mediaIconSize, mediaIconSize);
1836     return !paintIcon(context.get(), graphicsContext, iconRect, iconName);
1837 }
1838 #endif // GTK_CHECK_VERSION(3, 20, 0)
1839
1840 bool RenderThemeGtk::hasOwnDisabledStateHandlingFor(ControlPart part) const
1841 {
1842     return (part != MediaMuteButtonPart);
1843 }
1844
1845 bool RenderThemeGtk::paintMediaFullscreenButton(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
1846 {
1847     return paintMediaButton(renderObject, paintInfo.context(), rect, "view-fullscreen-symbolic");
1848 }
1849
1850 bool RenderThemeGtk::paintMediaMuteButton(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
1851 {
1852     Node* node = renderObject.node();
1853     if (!node)
1854         return true;
1855     Node* mediaNode = node->shadowHost();
1856     if (!is<HTMLMediaElement>(mediaNode))
1857         return true;
1858
1859     HTMLMediaElement* mediaElement = downcast<HTMLMediaElement>(mediaNode);
1860     return paintMediaButton(renderObject, paintInfo.context(), rect, mediaElement->muted() ? "audio-volume-muted-symbolic" : "audio-volume-high-symbolic");
1861 }
1862
1863 bool RenderThemeGtk::paintMediaPlayButton(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
1864 {
1865     Node* node = renderObject.node();
1866     if (!node)
1867         return true;
1868     if (!nodeHasPseudo(*node, "-webkit-media-controls-play-button"))
1869         return true;
1870
1871     return paintMediaButton(renderObject, paintInfo.context(), rect, nodeHasClass(node, "paused") ? "media-playback-start-symbolic" : "media-playback-pause-symbolic");
1872 }
1873
1874 bool RenderThemeGtk::paintMediaSeekBackButton(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
1875 {
1876     return paintMediaButton(renderObject, paintInfo.context(), rect, "media-seek-backward-symbolic");
1877 }
1878
1879 bool RenderThemeGtk::paintMediaSeekForwardButton(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
1880 {
1881     return paintMediaButton(renderObject, paintInfo.context(), rect, "media-seek-forward-symbolic");
1882 }
1883
1884 #if ENABLE(VIDEO_TRACK)
1885 bool RenderThemeGtk::paintMediaToggleClosedCaptionsButton(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
1886 {
1887     return paintMediaButton(renderObject, paintInfo.context(), rect, "media-view-subtitles-symbolic");
1888 }
1889 #endif
1890
1891 static FloatRoundedRect::Radii borderRadiiFromStyle(const RenderStyle& style)
1892 {
1893     return FloatRoundedRect::Radii(
1894         IntSize(style.borderTopLeftRadius().width.intValue(), style.borderTopLeftRadius().height.intValue()),
1895         IntSize(style.borderTopRightRadius().width.intValue(), style.borderTopRightRadius().height.intValue()),
1896         IntSize(style.borderBottomLeftRadius().width.intValue(), style.borderBottomLeftRadius().height.intValue()),
1897         IntSize(style.borderBottomRightRadius().width.intValue(), style.borderBottomRightRadius().height.intValue()));
1898 }
1899
1900 bool RenderThemeGtk::paintMediaSliderTrack(const RenderObject& o, const PaintInfo& paintInfo, const IntRect& r)
1901 {
1902     auto mediaElement = parentMediaElement(o);
1903     if (!mediaElement)
1904         return true;
1905
1906     GraphicsContext& context = paintInfo.context();
1907     context.save();
1908     context.setStrokeStyle(NoStroke);
1909
1910     float mediaDuration = mediaElement->duration();
1911     float totalTrackWidth = r.width();
1912     auto& style = o.style();
1913     RefPtr<TimeRanges> timeRanges = mediaElement->buffered();
1914     for (unsigned index = 0; index < timeRanges->length(); ++index) {
1915         float start = timeRanges->start(index).releaseReturnValue();
1916         float end = timeRanges->end(index).releaseReturnValue();
1917         float startRatio = start / mediaDuration;
1918         float lengthRatio = (end - start) / mediaDuration;
1919         if (!lengthRatio)
1920             continue;
1921
1922         IntRect rangeRect(r);
1923         rangeRect.setWidth(lengthRatio * totalTrackWidth);
1924         if (index)
1925             rangeRect.move(startRatio * totalTrackWidth, 0);
1926         context.fillRoundedRect(FloatRoundedRect(rangeRect, borderRadiiFromStyle(style)), style.visitedDependentColor(CSSPropertyColor));
1927     }
1928
1929     context.restore();
1930     return false;
1931 }
1932
1933 bool RenderThemeGtk::paintMediaSliderThumb(const RenderObject& o, const PaintInfo& paintInfo, const IntRect& r)
1934 {
1935     auto& style = o.style();
1936     paintInfo.context().fillRoundedRect(FloatRoundedRect(r, borderRadiiFromStyle(style)), style.visitedDependentColor(CSSPropertyColor));
1937     return false;
1938 }
1939
1940 bool RenderThemeGtk::paintMediaVolumeSliderTrack(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
1941 {
1942     auto mediaElement = parentMediaElement(renderObject);
1943     if (!mediaElement)
1944         return true;
1945
1946     float volume = mediaElement->muted() ? 0.0f : mediaElement->volume();
1947     if (!volume)
1948         return true;
1949
1950     GraphicsContext& context = paintInfo.context();
1951     context.save();
1952     context.setStrokeStyle(NoStroke);
1953
1954     int rectHeight = rect.height();
1955     float trackHeight = rectHeight * volume;
1956     auto& style = renderObject.style();
1957     IntRect volumeRect(rect);
1958     volumeRect.move(0, rectHeight - trackHeight);
1959     volumeRect.setHeight(ceil(trackHeight));
1960
1961     context.fillRoundedRect(FloatRoundedRect(volumeRect, borderRadiiFromStyle(style)), style.visitedDependentColor(CSSPropertyColor));
1962     context.restore();
1963
1964     return false;
1965 }
1966
1967 bool RenderThemeGtk::paintMediaVolumeSliderThumb(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
1968 {
1969     return paintMediaSliderThumb(renderObject, paintInfo, rect);
1970 }
1971
1972 String RenderThemeGtk::formatMediaControlsCurrentTime(float currentTime, float duration) const
1973 {
1974     return formatMediaControlsTime(currentTime) + " / " + formatMediaControlsTime(duration);
1975 }
1976
1977 bool RenderThemeGtk::paintMediaCurrentTime(const RenderObject&, const PaintInfo&, const IntRect&)
1978 {
1979     return false;
1980 }
1981 #endif
1982
1983 void RenderThemeGtk::adjustProgressBarStyle(StyleResolver&, RenderStyle& style, const Element*) const
1984 {
1985     style.setBoxShadow(nullptr);
1986 }
1987
1988 // These values have been copied from RenderThemeChromiumSkia.cpp
1989 static const int progressActivityBlocks = 5;
1990 static const int progressAnimationFrames = 10;
1991 static const Seconds progressAnimationInterval { 125_ms };
1992 Seconds RenderThemeGtk::animationRepeatIntervalForProgressBar(RenderProgress&) const
1993 {
1994     return progressAnimationInterval;
1995 }
1996
1997 Seconds RenderThemeGtk::animationDurationForProgressBar(RenderProgress&) const
1998 {
1999     return progressAnimationInterval * progressAnimationFrames * 2; // "2" for back and forth;
2000 }
2001
2002 IntRect RenderThemeGtk::calculateProgressRect(const RenderObject& renderObject, const IntRect& fullBarRect)
2003 {
2004     IntRect progressRect(fullBarRect);
2005     const auto& renderProgress = downcast<RenderProgress>(renderObject);
2006     if (renderProgress.isDeterminate()) {
2007         int progressWidth = progressRect.width() * renderProgress.position();
2008         if (renderObject.style().direction() == TextDirection::RTL)
2009             progressRect.setX(progressRect.x() + progressRect.width() - progressWidth);
2010         progressRect.setWidth(progressWidth);
2011         return progressRect;
2012     }
2013
2014     double animationProgress = renderProgress.animationProgress();
2015
2016     // Never let the progress rect shrink smaller than 2 pixels.
2017     int newWidth = std::max(2, progressRect.width() / progressActivityBlocks);
2018     int movableWidth = progressRect.width() - newWidth;
2019     progressRect.setWidth(newWidth);
2020
2021     // We want the first 0.5 units of the animation progress to represent the
2022     // forward motion and the second 0.5 units to represent the backward motion,
2023     // thus we multiply by two here to get the full sweep of the progress bar with
2024     // each direction.
2025     if (animationProgress < 0.5)
2026         progressRect.setX(progressRect.x() + (animationProgress * 2 * movableWidth));
2027     else
2028         progressRect.setX(progressRect.x() + ((1.0 - animationProgress) * 2 * movableWidth));
2029     return progressRect;
2030 }
2031
2032 String RenderThemeGtk::fileListNameForWidth(const FileList* fileList, const FontCascade& font, int width, bool multipleFilesAllowed) const
2033 {
2034     if (width <= 0)
2035         return String();
2036
2037     if (fileList->length() > 1)
2038         return StringTruncator::rightTruncate(multipleFileUploadText(fileList->length()), width, font);
2039
2040     String string;
2041     if (fileList->length())
2042         string = FileSystem::pathGetFileName(fileList->item(0)->path());
2043     else if (multipleFilesAllowed)
2044         string = fileButtonNoFilesSelectedLabel();
2045     else
2046         string = fileButtonNoFileSelectedLabel();
2047
2048     return StringTruncator::centerTruncate(string, width, font);
2049 }
2050
2051 #if ENABLE(VIDEO)
2052 String RenderThemeGtk::mediaControlsScript()
2053 {
2054     StringBuilder scriptBuilder;
2055     scriptBuilder.append(mediaControlsLocalizedStringsJavaScript, sizeof(mediaControlsLocalizedStringsJavaScript));
2056     scriptBuilder.append(mediaControlsBaseJavaScript, sizeof(mediaControlsBaseJavaScript));
2057     scriptBuilder.append(mediaControlsGtkJavaScript, sizeof(mediaControlsGtkJavaScript));
2058     return scriptBuilder.toString();
2059 }
2060 #endif // ENABLE(VIDEO)
2061 }