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