64e4945716a9589aab820635883465d7c481fd80
[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, 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 void RenderThemeGtk::adjustMenuListStyle(StyleResolver&, RenderStyle& style, Element*) const
777 {
778     // The tests check explicitly that select menu buttons ignore line height.
779     style.setLineHeight(RenderStyle::initialLineHeight());
780
781     // We cannot give a proper rendering when border radius is active, unfortunately.
782     style.resetBorderRadius();
783 }
784
785 void RenderThemeGtk::adjustMenuListButtonStyle(StyleResolver& styleResolver, RenderStyle& style, Element* e) const
786 {
787     adjustMenuListStyle(styleResolver, style, e);
788 }
789
790 #if GTK_CHECK_VERSION(3, 20, 0)
791 /*
792  * GtkComboBox gadgets tree
793  *
794  * combobox
795  * ├── box.linked
796  * │   ╰── button.combo
797  * │       ╰── box
798  * │           ├── cellview
799  * │           ╰── arrow
800  * ╰── window.popup
801  */
802 LengthBox RenderThemeGtk::popupInternalPaddingBox(const RenderStyle& style) const
803 {
804     if (style.appearance() == NoControlPart)
805         return LengthBox(0);
806
807     RenderThemeGadget::Info info = { RenderThemeGadget::Type::Generic, "combobox", GTK_STATE_FLAG_NORMAL, { } };
808     auto comboGadget = RenderThemeGadget::create(info);
809     Vector<RenderThemeGadget::Info> children = {
810         { RenderThemeGadget::Type::Generic, "button", GTK_STATE_FLAG_NORMAL, { "combo" } }
811     };
812     info.name = "box";
813     info.classList = { "horizontal", "linked" };
814     auto boxGadget = std::make_unique<RenderThemeBoxGadget>(info, children, comboGadget.get());
815     RenderThemeGadget* buttonGadget = boxGadget->child(0);
816     info.classList.removeLast();
817     auto buttonBoxGadget = RenderThemeGadget::create(info, buttonGadget);
818     info.name = "arrow";
819     info.classList = { };
820     auto arrowGadget = RenderThemeGadget::create(info, buttonBoxGadget.get());
821     GtkBorder comboContentsBox = comboGadget->contentsBox();
822     GtkBorder boxContentsBox = boxGadget->contentsBox();
823     GtkBorder buttonContentsBox = buttonGadget->contentsBox();
824     GtkBorder buttonBoxContentsBox = buttonBoxGadget->contentsBox();
825     GtkBorder padding;
826     padding.left = comboContentsBox.left + boxContentsBox.left + buttonContentsBox.left + buttonBoxContentsBox.left;
827     padding.right = comboContentsBox.right + boxContentsBox.right + buttonContentsBox.right + buttonBoxContentsBox.right;
828     padding.top = comboContentsBox.top + boxContentsBox.top + buttonContentsBox.top + buttonBoxContentsBox.top;
829     padding.bottom = comboContentsBox.bottom + boxContentsBox.bottom + buttonContentsBox.bottom + buttonBoxContentsBox.bottom;
830
831     auto arrowSize = arrowGadget->preferredSize();
832     return LengthBox(padding.top, padding.right + (style.direction() == LTR ? arrowSize.width() : 0),
833         padding.bottom, padding.left + (style.direction() == RTL ? arrowSize.width() : 0));
834 }
835
836 bool RenderThemeGtk::paintMenuList(const RenderObject& renderObject, const PaintInfo& paintInfo, const FloatRect& rect)
837 {
838     RenderThemeGadget::Info info = { RenderThemeGadget::Type::Generic, "combobox", themePartStateFlags(*this, ComboBoxButton, renderObject), { } };
839     auto comboGadget = RenderThemeGadget::create(info);
840     Vector<RenderThemeGadget::Info> children = {
841         { RenderThemeGadget::Type::Generic, "button", info.state, { "combo" } }
842     };
843     info.name = "box";
844     info.classList = { "horizontal", "linked" };
845     auto boxGadget = std::make_unique<RenderThemeBoxGadget>(info, children, comboGadget.get());
846     RenderThemeGadget* buttonGadget = boxGadget->child(0);
847     info.classList.removeLast();
848     auto buttonBoxGadget = RenderThemeGadget::create(info, buttonGadget);
849     info.type = RenderThemeGadget::Type::Arrow;
850     info.name = "arrow";
851     info.classList = { };
852     auto arrowGadget = RenderThemeGadget::create(info, buttonBoxGadget.get());
853
854     cairo_t* cr = paintInfo.context().platformContext()->cr();
855     comboGadget->render(cr, rect);
856     boxGadget->render(cr, rect);
857     FloatRect contentsRect;
858     buttonGadget->render(cr, rect, &contentsRect);
859     buttonBoxGadget->render(cr, contentsRect);
860     arrowGadget->render(cr, contentsRect);
861     if (isFocused(renderObject))
862         buttonGadget->renderFocus(cr, rect);
863
864     return false;
865 }
866 #else
867 LengthBox RenderThemeGtk::popupInternalPaddingBox(const RenderStyle& style) const
868 {
869     if (style.appearance() == NoControlPart)
870         return { 0, 0, 0, 0 };
871
872     GRefPtr<GtkStyleContext> parentContext = createStyleContext(ComboBox);
873     GRefPtr<GtkStyleContext> context = createStyleContext(ComboBoxButton, parentContext.get());
874     gtk_style_context_set_direction(context.get(), static_cast<GtkTextDirection>(gtkTextDirection(style.direction())));
875     gtk_style_context_set_state(context.get(), static_cast<GtkStateFlags>(0));
876     GtkBorder borderWidth = { 0, 0, 0, 0 };
877     gtk_style_context_get_border(context.get(), gtk_style_context_get_state(context.get()), &borderWidth);
878
879     gboolean interiorFocus;
880     gint focusWidth, focusPad;
881     gtk_style_context_get_style(context.get(), "interior-focus", &interiorFocus, "focus-line-width", &focusWidth, "focus-padding", &focusPad, nullptr);
882     focusWidth = interiorFocus ? focusWidth + focusPad : 0;
883
884     return { borderWidth.top + focusWidth, borderWidth.right + focusWidth + (style.direction() == LTR ? minArrowSize : 0),
885         borderWidth.bottom + focusWidth, borderWidth.left + focusWidth + (style.direction() == RTL ? minArrowSize : 0) };
886 }
887
888 bool RenderThemeGtk::paintMenuList(const RenderObject& renderObject, const PaintInfo& paintInfo, const FloatRect& r)
889 {
890     // FIXME: adopt subpixel themes.
891     IntRect rect = IntRect(r);
892
893     cairo_t* cairoContext = paintInfo.context().platformContext()->cr();
894     GtkTextDirection direction = static_cast<GtkTextDirection>(gtkTextDirection(renderObject.style().direction()));
895
896     GRefPtr<GtkStyleContext> parentStyleContext = createStyleContext(ComboBox);
897
898     // Paint the button.
899     GRefPtr<GtkStyleContext> buttonStyleContext = createStyleContext(ComboBoxButton, parentStyleContext.get());
900     gtk_style_context_set_direction(buttonStyleContext.get(), direction);
901     renderButton(this, buttonStyleContext.get(), renderObject, paintInfo, rect);
902
903     // Get the inner rectangle.
904     gint focusWidth, focusPad;
905     GtkBorder* innerBorderPtr = 0;
906     GtkBorder innerBorder = { 1, 1, 1, 1 };
907     gtk_style_context_get_style(buttonStyleContext.get(), "inner-border", &innerBorderPtr, "focus-line-width", &focusWidth, "focus-padding", &focusPad, nullptr);
908     if (innerBorderPtr) {
909         innerBorder = *innerBorderPtr;
910         gtk_border_free(innerBorderPtr);
911     }
912
913     GtkBorder borderWidth;
914     GtkStateFlags state = gtk_style_context_get_state(buttonStyleContext.get());
915     gtk_style_context_get_border(buttonStyleContext.get(), state, &borderWidth);
916
917     focusWidth += focusPad;
918     IntRect innerRect(
919         rect.x() + innerBorder.left + borderWidth.left + focusWidth,
920         rect.y() + innerBorder.top + borderWidth.top + focusWidth,
921         rect.width() - borderWidth.left - borderWidth.right - innerBorder.left - innerBorder.right - (2 * focusWidth),
922         rect.height() - borderWidth.top - borderWidth.bottom - innerBorder.top - innerBorder.bottom - (2 * focusWidth));
923
924     if (isPressed(renderObject)) {
925         gint childDisplacementX;
926         gint childDisplacementY;
927         gtk_style_context_get_style(buttonStyleContext.get(), "child-displacement-x", &childDisplacementX, "child-displacement-y", &childDisplacementY, nullptr);
928         innerRect.move(childDisplacementX, childDisplacementY);
929     }
930     innerRect.setWidth(std::max(1, innerRect.width()));
931     innerRect.setHeight(std::max(1, innerRect.height()));
932
933     // Paint the arrow.
934     GRefPtr<GtkStyleContext> arrowStyleContext = createStyleContext(ComboBoxArrow, buttonStyleContext.get());
935     gtk_style_context_set_direction(arrowStyleContext.get(), direction);
936
937     gfloat arrowScaling;
938     gtk_style_context_get_style(parentStyleContext.get(), "arrow-scaling", &arrowScaling, nullptr);
939
940     IntSize arrowSize(minArrowSize, innerRect.height());
941     FloatPoint arrowPosition(innerRect.location());
942     if (direction == GTK_TEXT_DIR_LTR)
943         arrowPosition.move(innerRect.width() - arrowSize.width(), 0);
944
945     // GTK+ actually fetches the xalign and valign values from the widget, but since we
946     // don't have a widget here, we are just using the default xalign and valign values of 0.5.
947     gint extent = std::min(arrowSize.width(), arrowSize.height()) * arrowScaling;
948     arrowPosition.move((arrowSize.width() - extent) / 2, (arrowSize.height() - extent) / 2);
949
950     gtk_style_context_set_state(arrowStyleContext.get(), state);
951     gtk_render_arrow(arrowStyleContext.get(), cairoContext, G_PI, arrowPosition.x(), arrowPosition.y(), extent);
952
953     return false;
954 }
955 #endif // GTK_CHECK_VERSION(3, 20, 0)
956
957 bool RenderThemeGtk::paintMenuListButtonDecorations(const RenderBox& object, const PaintInfo& info, const FloatRect& rect)
958 {
959     return paintMenuList(object, info, rect);
960 }
961
962 #if GTK_CHECK_VERSION(3, 20, 0)
963 void RenderThemeGtk::adjustTextFieldStyle(StyleResolver&, RenderStyle& style, Element* element) const
964 {
965     if (!is<HTMLInputElement>(element) || !shouldHaveSpinButton(downcast<HTMLInputElement>(*element)))
966         return;
967
968     // Spinbuttons need a minimum height to be rendered correctly.
969     RenderThemeGadget::Info info = { RenderThemeGadget::Type::Generic, "spinbutton", GTK_STATE_FLAG_NORMAL, { "horizontal" } };
970     auto spinbuttonGadget = RenderThemeGadget::create(info);
971     info.type = RenderThemeGadget::Type::TextField;
972     info.name = "entry";
973     info.classList.clear();
974     auto entryGadget = RenderThemeGadget::create(info, spinbuttonGadget.get());
975     info.type = RenderThemeGadget::Type::Icon;
976     info.name = "button";
977     info.classList.append("up");
978     auto buttonUpGadget = RenderThemeGadget::create(info, spinbuttonGadget.get());
979     static_cast<RenderThemeIconGadget*>(buttonUpGadget.get())->setIconSize(RenderThemeIconGadget::IconSizeGtk::Menu);
980     info.classList[0] = "down";
981     auto buttonDownGadget = RenderThemeGadget::create(info, spinbuttonGadget.get());
982     static_cast<RenderThemeIconGadget*>(buttonDownGadget.get())->setIconSize(RenderThemeIconGadget::IconSizeGtk::Menu);
983     IntSize preferredSize = spinbuttonGadget->preferredSize();
984     preferredSize = preferredSize.expandedTo(entryGadget->preferredSize());
985     IntSize upPreferredSize = preferredSize.expandedTo(buttonUpGadget->preferredSize());
986     IntSize downPreferredSize = preferredSize.expandedTo(buttonDownGadget->preferredSize());
987     int height = std::max(upPreferredSize.height(), downPreferredSize.height());
988     style.setMinHeight(Length(height, Fixed));
989 }
990
991 bool RenderThemeGtk::paintTextField(const RenderObject& renderObject, const PaintInfo& paintInfo, const FloatRect& rect)
992 {
993     RenderThemeGadget::Info info = { RenderThemeGadget::Type::TextField, "entry", themePartStateFlags(*this, Entry, renderObject), { } };
994     std::unique_ptr<RenderThemeGadget> parentGadget;
995     if (is<HTMLInputElement>(renderObject.node()) && shouldHaveSpinButton(downcast<HTMLInputElement>(*renderObject.node()))) {
996         info.name = "spinbutton";
997         info.classList.append("horizontal");
998         parentGadget = RenderThemeTextFieldGadget::create(info);
999         info.name = "entry";
1000         info.classList.clear();
1001     }
1002
1003     auto entryGadget = RenderThemeTextFieldGadget::create(info, parentGadget.get());
1004     if (parentGadget)
1005         parentGadget->render(paintInfo.context().platformContext()->cr(), rect);
1006     entryGadget->render(paintInfo.context().platformContext()->cr(), rect);
1007     return false;
1008 }
1009 #else
1010 void RenderThemeGtk::adjustTextFieldStyle(StyleResolver&, RenderStyle&, Element*) const
1011 {
1012 }
1013
1014 bool RenderThemeGtk::paintTextField(const RenderObject& renderObject, const PaintInfo& paintInfo, const FloatRect& rect)
1015 {
1016     GRefPtr<GtkStyleContext> context = createStyleContext(Entry);
1017     gtk_style_context_set_direction(context.get(), static_cast<GtkTextDirection>(gtkTextDirection(renderObject.style().direction())));
1018
1019     guint flags = 0;
1020     if (!isEnabled(renderObject) || isReadOnlyControl(renderObject))
1021         flags |= GTK_STATE_FLAG_INSENSITIVE;
1022     else if (isFocused(renderObject))
1023         flags |= GTK_STATE_FLAG_FOCUSED;
1024     gtk_style_context_set_state(context.get(), static_cast<GtkStateFlags>(flags));
1025
1026     gtk_render_background(context.get(), paintInfo.context().platformContext()->cr(), rect.x(), rect.y(), rect.width(), rect.height());
1027     gtk_render_frame(context.get(), paintInfo.context().platformContext()->cr(), rect.x(), rect.y(), rect.width(), rect.height());
1028
1029     if (isFocused(renderObject) && isEnabled(renderObject)) {
1030         gboolean interiorFocus;
1031         gint focusWidth, focusPad;
1032         gtk_style_context_get_style(context.get(), "interior-focus", &interiorFocus, "focus-line-width", &focusWidth, "focus-padding", &focusPad, nullptr);
1033         if (!interiorFocus) {
1034             IntRect focusRect(rect);
1035             focusRect.inflate(focusWidth + focusPad);
1036             gtk_render_focus(context.get(), paintInfo.context().platformContext()->cr(), focusRect.x(), focusRect.y(), focusRect.width(), focusRect.height());
1037         }
1038     }
1039
1040     return false;
1041 }
1042 #endif
1043
1044 #if GTK_CHECK_VERSION(3, 20, 0)
1045 static void adjustSearchFieldIconStyle(RenderThemePart themePart, RenderStyle& style)
1046 {
1047     ASSERT(themePart == EntryIconLeft || themePart == EntryIconRight);
1048     RenderThemeGadget::Info info = { RenderThemeGadget::Type::TextField, "entry", GTK_STATE_FLAG_NORMAL, { } };
1049     auto parentGadget = RenderThemeGadget::create(info);
1050     info.type = RenderThemeGadget::Type::Icon;
1051     info.name = "image";
1052     if (themePart == EntryIconLeft)
1053         info.classList.append("left");
1054     else
1055         info.classList.append("right");
1056     auto gadget = RenderThemeIconGadget::create(info, parentGadget.get());
1057
1058     // Get the icon size based on the font size.
1059     static_cast<RenderThemeIconGadget*>(gadget.get())->setIconSize(style.fontSize());
1060     IntSize preferredSize = gadget->preferredSize();
1061     GtkBorder contentsBox = parentGadget->contentsBox();
1062     if (themePart == EntryIconLeft)
1063         preferredSize.expand(contentsBox.left, contentsBox.top + contentsBox.bottom);
1064     else
1065         preferredSize.expand(contentsBox.right, contentsBox.top + contentsBox.bottom);
1066     style.setWidth(Length(preferredSize.width(), Fixed));
1067     style.setHeight(Length(preferredSize.height(), Fixed));
1068 }
1069 #else
1070 // Defined in GTK+ (gtk/gtkiconfactory.c)
1071 static const gint gtkIconSizeMenu = 16;
1072 static const gint gtkIconSizeSmallToolbar = 18;
1073 static const gint gtkIconSizeButton = 20;
1074 static const gint gtkIconSizeLargeToolbar = 24;
1075 static const gint gtkIconSizeDnd = 32;
1076 static const gint gtkIconSizeDialog = 48;
1077
1078 static GtkIconSize getIconSizeForPixelSize(gint pixelSize)
1079 {
1080     if (pixelSize < gtkIconSizeSmallToolbar)
1081         return GTK_ICON_SIZE_MENU;
1082     if (pixelSize >= gtkIconSizeSmallToolbar && pixelSize < gtkIconSizeButton)
1083         return GTK_ICON_SIZE_SMALL_TOOLBAR;
1084     if (pixelSize >= gtkIconSizeButton && pixelSize < gtkIconSizeLargeToolbar)
1085         return GTK_ICON_SIZE_BUTTON;
1086     if (pixelSize >= gtkIconSizeLargeToolbar && pixelSize < gtkIconSizeDnd)
1087         return GTK_ICON_SIZE_LARGE_TOOLBAR;
1088     if (pixelSize >= gtkIconSizeDnd && pixelSize < gtkIconSizeDialog)
1089         return GTK_ICON_SIZE_DND;
1090
1091     return GTK_ICON_SIZE_DIALOG;
1092 }
1093
1094 static void adjustSearchFieldIconStyle(RenderThemePart themePart, RenderStyle& style)
1095 {
1096     style.resetBorder();
1097     style.resetPadding();
1098
1099     GRefPtr<GtkStyleContext> parentContext = createStyleContext(Entry);
1100     GRefPtr<GtkStyleContext> context = createStyleContext(themePart, parentContext.get());
1101
1102     GtkBorder padding;
1103     gtk_style_context_get_padding(context.get(), gtk_style_context_get_state(context.get()), &padding);
1104
1105     // Get the icon size based on the font size.
1106     int fontSize = style.fontSize();
1107     if (fontSize < gtkIconSizeMenu) {
1108         style.setWidth(Length(fontSize + (padding.left + padding.right), Fixed));
1109         style.setHeight(Length(fontSize + (padding.top + padding.bottom), Fixed));
1110         return;
1111     }
1112     gint width = 0, height = 0;
1113     gtk_icon_size_lookup(getIconSizeForPixelSize(fontSize), &width, &height);
1114     style.setWidth(Length(width + (padding.left + padding.right), Fixed));
1115     style.setHeight(Length(height + (padding.top + padding.bottom), Fixed));
1116 }
1117 #endif
1118
1119 bool RenderThemeGtk::paintTextArea(const RenderObject& o, const PaintInfo& i, const FloatRect& r)
1120 {
1121     return paintTextField(o, i, r);
1122 }
1123
1124 void RenderThemeGtk::adjustSearchFieldResultsButtonStyle(StyleResolver& styleResolver, RenderStyle& style, Element* e) const
1125 {
1126     adjustSearchFieldCancelButtonStyle(styleResolver, style, e);
1127 }
1128
1129 bool RenderThemeGtk::paintSearchFieldResultsButton(const RenderBox& o, const PaintInfo& i, const IntRect& rect)
1130 {
1131     return paintSearchFieldResultsDecorationPart(o, i, rect);
1132 }
1133
1134 void RenderThemeGtk::adjustSearchFieldResultsDecorationPartStyle(StyleResolver&, RenderStyle& style, Element*) const
1135 {
1136     adjustSearchFieldIconStyle(EntryIconLeft, style);
1137 }
1138
1139 void RenderThemeGtk::adjustSearchFieldCancelButtonStyle(StyleResolver&, RenderStyle& style, Element*) const
1140 {
1141     adjustSearchFieldIconStyle(EntryIconRight, style);
1142 }
1143
1144 #if GTK_CHECK_VERSION(3, 20, 0)
1145 static bool paintSearchFieldIcon(RenderThemeGtk* theme, RenderThemePart themePart, const RenderBox& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
1146 {
1147     ASSERT(themePart == EntryIconLeft || themePart == EntryIconRight);
1148     RenderThemeGadget::Info info = { RenderThemeGadget::Type::TextField, "entry", themePartStateFlags(*theme, Entry, renderObject), { } };
1149     auto parentGadget = RenderThemeGadget::create(info);
1150     info.type = RenderThemeGadget::Type::Icon;
1151     info.state = themePartStateFlags(*theme, themePart, renderObject);
1152     info.name = "image";
1153     if (themePart == EntryIconLeft)
1154         info.classList.append("left");
1155     else
1156         info.classList.append("right");
1157     auto gadget = RenderThemeGadget::create(info, parentGadget.get());
1158     auto* gadgetIcon = static_cast<RenderThemeIconGadget*>(gadget.get());
1159     gadgetIcon->setIconSize(renderObject.style().fontSize());
1160     if (themePart == EntryIconLeft)
1161         gadgetIcon->setIconName("edit-find-symbolic");
1162     else
1163         gadgetIcon->setIconName("edit-clear-symbolic");
1164     GtkBorder contentsBox = parentGadget->contentsBox();
1165     IntRect iconRect = rect;
1166     if (themePart == EntryIconLeft) {
1167         iconRect.move(contentsBox.left, contentsBox.top);
1168         iconRect.contract(contentsBox.left, contentsBox.top + contentsBox.bottom);
1169     } else
1170         iconRect.contract(contentsBox.right, contentsBox.top + contentsBox.bottom);
1171     return !gadget->render(paintInfo.context().platformContext()->cr(), iconRect);
1172 }
1173 bool RenderThemeGtk::paintSearchFieldResultsDecorationPart(const RenderBox& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
1174 {
1175     return paintSearchFieldIcon(this, EntryIconLeft, renderObject, paintInfo, rect);
1176 }
1177
1178 bool RenderThemeGtk::paintSearchFieldCancelButton(const RenderBox& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
1179 {
1180     return paintSearchFieldIcon(this, EntryIconRight, renderObject, paintInfo, rect);
1181 }
1182 #else
1183 static bool paintIcon(GtkStyleContext* context, GraphicsContext& graphicsContext, const IntRect& rect, const char* iconName)
1184 {
1185     GRefPtr<GdkPixbuf> icon = loadThemedIcon(context, iconName, getIconSizeForPixelSize(rect.height()));
1186     if (!icon)
1187         return false;
1188
1189     if (gdk_pixbuf_get_width(icon.get()) > rect.width() || gdk_pixbuf_get_height(icon.get()) > rect.height())
1190         icon = adoptGRef(gdk_pixbuf_scale_simple(icon.get(), rect.width(), rect.height(), GDK_INTERP_BILINEAR));
1191
1192     gtk_render_icon(context, graphicsContext.platformContext()->cr(), icon.get(), rect.x(), rect.y());
1193     return true;
1194 }
1195
1196 static bool paintEntryIcon(RenderThemePart themePart, const char* iconName, GraphicsContext& graphicsContext, const IntRect& rect, GtkTextDirection direction, GtkStateFlags state)
1197 {
1198     GRefPtr<GtkStyleContext> parentContext = createStyleContext(Entry);
1199     GRefPtr<GtkStyleContext> context = createStyleContext(themePart, parentContext.get());
1200     gtk_style_context_set_direction(context.get(), direction);
1201     gtk_style_context_set_state(context.get(), state);
1202     return paintIcon(context.get(), graphicsContext, rect, iconName);
1203 }
1204
1205 static IntRect centerRectVerticallyInParentInputElement(const RenderObject& renderObject, const IntRect& rect)
1206 {
1207     if (!renderObject.node())
1208         return IntRect();
1209
1210     // Get the renderer of <input> element.
1211     Node* input = renderObject.node()->shadowHost();
1212     if (!input)
1213         input = renderObject.node();
1214     if (!is<RenderBox>(*input->renderer()))
1215         return IntRect();
1216
1217     // If possible center the y-coordinate of the rect vertically in the parent input element.
1218     // We also add one pixel here to ensure that the y coordinate is rounded up for box heights
1219     // that are even, which looks in relation to the box text.
1220     IntRect inputContentBox = downcast<RenderBox>(*input->renderer()).absoluteContentBox();
1221
1222     // Make sure the scaled decoration stays square and will fit in its parent's box.
1223     int iconSize = std::min(inputContentBox.width(), std::min(inputContentBox.height(), rect.height()));
1224     IntRect scaledRect(rect.x(), inputContentBox.y() + (inputContentBox.height() - iconSize + 1) / 2, iconSize, iconSize);
1225     return scaledRect;
1226 }
1227
1228 bool RenderThemeGtk::paintSearchFieldResultsDecorationPart(const RenderBox& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
1229 {
1230     IntRect iconRect = centerRectVerticallyInParentInputElement(renderObject, rect);
1231     if (iconRect.isEmpty())
1232         return true;
1233
1234     return !paintEntryIcon(EntryIconLeft, "edit-find-symbolic", paintInfo.context(), iconRect, gtkTextDirection(renderObject.style().direction()),
1235         gtkIconStateFlags(this, renderObject));
1236 }
1237
1238 bool RenderThemeGtk::paintSearchFieldCancelButton(const RenderBox& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
1239 {
1240     IntRect iconRect = centerRectVerticallyInParentInputElement(renderObject, rect);
1241     if (iconRect.isEmpty())
1242         return true;
1243
1244     return !paintEntryIcon(EntryIconRight, "edit-clear-symbolic", paintInfo.context(), iconRect, gtkTextDirection(renderObject.style().direction()),
1245         gtkIconStateFlags(this, renderObject));
1246 }
1247 #endif // GTK_CHECK_VERSION(3, 20, 0)
1248
1249 void RenderThemeGtk::adjustSearchFieldStyle(StyleResolver&, RenderStyle& style, Element*) const
1250 {
1251     // We cannot give a proper rendering when border radius is active, unfortunately.
1252     style.resetBorderRadius();
1253     style.setLineHeight(RenderStyle::initialLineHeight());
1254 }
1255
1256 bool RenderThemeGtk::paintSearchField(const RenderObject& o, const PaintInfo& i, const IntRect& rect)
1257 {
1258     return paintTextField(o, i, rect);
1259 }
1260
1261 bool RenderThemeGtk::shouldHaveCapsLockIndicator(HTMLInputElement& element) const
1262 {
1263     return element.isPasswordField();
1264 }
1265
1266 void RenderThemeGtk::adjustSliderTrackStyle(StyleResolver&, RenderStyle& style, Element*) const
1267 {
1268     style.setBoxShadow(nullptr);
1269 }
1270
1271 void RenderThemeGtk::adjustSliderThumbStyle(StyleResolver& styleResolver, RenderStyle& style, Element* element) const
1272 {
1273     RenderTheme::adjustSliderThumbStyle(styleResolver, style, element);
1274     style.setBoxShadow(nullptr);
1275 }
1276
1277 #if GTK_CHECK_VERSION(3, 20, 0)
1278 /*
1279  * GtkScale
1280  *
1281  * scale
1282  * ╰── contents
1283  *     ╰── trough
1284  *         ├── slider
1285  *         ╰── [highlight]
1286  */
1287 bool RenderThemeGtk::paintSliderTrack(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
1288 {
1289     ControlPart part = renderObject.style().appearance();
1290     ASSERT(part == SliderHorizontalPart || part == SliderVerticalPart);
1291
1292     RenderThemeGadget::Info info = { RenderThemeGadget::Type::Generic, "scale", themePartStateFlags(*this, Scale, renderObject), { } };
1293     if (part == SliderHorizontalPart)
1294         info.classList.append("horizontal");
1295     else
1296         info.classList.append("vertical");
1297     auto scaleGadget = RenderThemeGadget::create(info);
1298     info.name = "contents";
1299     info.classList.clear();
1300     auto contentsGadget = RenderThemeGadget::create(info, scaleGadget.get());
1301     info.name = "trough";
1302     auto troughGadget = RenderThemeGadget::create(info, contentsGadget.get());
1303     info.name = "slider";
1304     auto sliderGadget = RenderThemeGadget::create(info, troughGadget.get());
1305     info.name = "highlight";
1306     auto highlightGadget = RenderThemeGadget::create(info, troughGadget.get());
1307
1308     // The given rectangle is not calculated based on the scale size, but all the margins and paddings are based on it.
1309     IntSize preferredSize = scaleGadget->preferredSize();
1310     preferredSize = preferredSize.expandedTo(contentsGadget->preferredSize());
1311     preferredSize = preferredSize.expandedTo(troughGadget->preferredSize());
1312     FloatRect trackRect = rect;
1313     if (part == SliderHorizontalPart) {
1314         trackRect.move(0, rect.height() / 2 - (preferredSize.height() / 2));
1315         trackRect.setHeight(preferredSize.height());
1316     } else {
1317         trackRect.move(rect.width() / 2 - (preferredSize.width() / 2), 0);
1318         trackRect.setWidth(preferredSize.width());
1319     }
1320
1321     FloatRect contentsRect;
1322     scaleGadget->render(paintInfo.context().platformContext()->cr(), trackRect, &contentsRect);
1323     contentsGadget->render(paintInfo.context().platformContext()->cr(), contentsRect, &contentsRect);
1324     // Scale trough defines its size querying slider and highlight.
1325     if (part == SliderHorizontalPart)
1326         contentsRect.setHeight(troughGadget->preferredSize().height() + std::max(sliderGadget->preferredSize().height(), highlightGadget->preferredSize().height()));
1327     else
1328         contentsRect.setWidth(troughGadget->preferredSize().width() + std::max(sliderGadget->preferredSize().width(), highlightGadget->preferredSize().width()));
1329     FloatRect troughRect = contentsRect;
1330     troughGadget->render(paintInfo.context().platformContext()->cr(), troughRect, &contentsRect);
1331     if (isFocused(renderObject))
1332         troughGadget->renderFocus(paintInfo.context().platformContext()->cr(), troughRect);
1333
1334     LayoutPoint thumbLocation;
1335     if (is<HTMLInputElement>(renderObject.node())) {
1336         auto& input = downcast<HTMLInputElement>(*renderObject.node());
1337         if (auto* element = input.sliderThumbElement())
1338             thumbLocation = element->renderBox()->location();
1339     }
1340
1341     if (part == SliderHorizontalPart) {
1342         if (renderObject.style().direction() == RTL) {
1343             contentsRect.move(thumbLocation.x(), 0);
1344             contentsRect.setWidth(contentsRect.width() - thumbLocation.x());
1345         } else
1346             contentsRect.setWidth(thumbLocation.x());
1347     } else
1348         contentsRect.setHeight(thumbLocation.y());
1349     highlightGadget->render(paintInfo.context().platformContext()->cr(), contentsRect);
1350
1351     return false;
1352 }
1353
1354 void RenderThemeGtk::adjustSliderThumbSize(RenderStyle& style, Element*) const
1355 {
1356     ControlPart part = style.appearance();
1357     if (part != SliderThumbHorizontalPart && part != SliderThumbVerticalPart)
1358         return;
1359
1360     RenderThemeGadget::Info info = { RenderThemeGadget::Type::Generic, "scale", GTK_STATE_FLAG_NORMAL, { } };
1361     if (part == SliderHorizontalPart)
1362         info.classList.append("horizontal");
1363     else
1364         info.classList.append("vertical");
1365     auto scaleGadget = RenderThemeGadget::create(info);
1366     info.name = "contents";
1367     info.classList.clear();
1368     auto contentsGadget = RenderThemeGadget::create(info, scaleGadget.get());
1369     info.name = "trough";
1370     auto troughGadget = RenderThemeGadget::create(info, contentsGadget.get());
1371     info.name = "slider";
1372     auto sliderGadget = RenderThemeGadget::create(info, troughGadget.get());
1373     info.name = "highlight";
1374     auto highlightGadget = RenderThemeGadget::create(info, troughGadget.get());
1375
1376     IntSize preferredSize = scaleGadget->preferredSize();
1377     preferredSize = preferredSize.expandedTo(contentsGadget->preferredSize());
1378     preferredSize = preferredSize.expandedTo(troughGadget->preferredSize());
1379     preferredSize = preferredSize.expandedTo(sliderGadget->preferredSize());
1380     if (part == SliderThumbHorizontalPart) {
1381         style.setWidth(Length(preferredSize.width(), Fixed));
1382         style.setHeight(Length(preferredSize.height(), Fixed));
1383         return;
1384     }
1385     ASSERT(part == SliderThumbVerticalPart);
1386     style.setWidth(Length(preferredSize.height(), Fixed));
1387     style.setHeight(Length(preferredSize.width(), Fixed));
1388 }
1389
1390 bool RenderThemeGtk::paintSliderThumb(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
1391 {
1392     ControlPart part = renderObject.style().appearance();
1393     ASSERT(part == SliderThumbHorizontalPart || part == SliderThumbVerticalPart);
1394
1395     RenderThemeGadget::Info info = { RenderThemeGadget::Type::Generic, "scale", themePartStateFlags(*this, Scale, renderObject), { } };
1396     if (part == SliderHorizontalPart)
1397         info.classList.append("horizontal");
1398     else
1399         info.classList.append("vertical");
1400     auto scaleGadget = RenderThemeGadget::create(info);
1401     info.name = "contents";
1402     info.classList.clear();
1403     auto contentsGadget = RenderThemeGadget::create(info, scaleGadget.get());
1404     info.name = "trough";
1405     auto troughGadget = RenderThemeGadget::create(info, contentsGadget.get());
1406     info.name = "slider";
1407     info.state = themePartStateFlags(*this, ScaleSlider, renderObject);
1408     auto sliderGadget = RenderThemeGadget::create(info, troughGadget.get());
1409     info.name = "highlight";
1410     auto highlightGadget = RenderThemeGadget::create(info, troughGadget.get());
1411
1412     GtkBorder scaleContentsBox = scaleGadget->contentsBox();
1413     GtkBorder contentsContentsBox = contentsGadget->contentsBox();
1414     GtkBorder troughContentsBox = troughGadget->contentsBox();
1415     GtkBorder padding;
1416     padding.left = scaleContentsBox.left + contentsContentsBox.left + troughContentsBox.left;
1417     padding.right = scaleContentsBox.right + contentsContentsBox.right + troughContentsBox.right;
1418     padding.top = scaleContentsBox.top + contentsContentsBox.top + troughContentsBox.top;
1419     padding.bottom = scaleContentsBox.bottom + contentsContentsBox.bottom + troughContentsBox.bottom;
1420
1421     // Scale trough defines its size querying slider and highlight.
1422     int troughHeight = troughGadget->preferredSize().height() + std::max(sliderGadget->preferredSize().height(), highlightGadget->preferredSize().height());
1423     IntRect sliderRect(rect.location(), IntSize(troughHeight, troughHeight));
1424     sliderRect.move(padding.left, padding.top);
1425     sliderRect.contract(padding.left + padding.right, padding.top + padding.bottom);
1426     sliderGadget->render(paintInfo.context().platformContext()->cr(), sliderRect);
1427     return false;
1428 }
1429 #else
1430 bool RenderThemeGtk::paintSliderTrack(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
1431 {
1432     ControlPart part = renderObject.style().appearance();
1433     ASSERT(part == SliderHorizontalPart || part == SliderVerticalPart);
1434
1435     GRefPtr<GtkStyleContext> parentContext = createStyleContext(Scale);
1436     gtk_style_context_add_class(parentContext.get(), part == SliderHorizontalPart ? GTK_STYLE_CLASS_HORIZONTAL : GTK_STYLE_CLASS_VERTICAL);
1437     GRefPtr<GtkStyleContext> context = createStyleContext(ScaleTrough, parentContext.get());
1438     gtk_style_context_set_direction(context.get(), gtkTextDirection(renderObject.style().direction()));
1439
1440     if (!isEnabled(renderObject))
1441         gtk_style_context_set_state(context.get(), GTK_STATE_FLAG_INSENSITIVE);
1442
1443     IntRect sliderRect = rect;
1444     // GTK+ uses the slider thumb size and margins to calculate the trough size, but in WebKit we render the thumb and
1445     // the slider track separately and the track rectangle we receive here can't be used to apply the GTK+ CSS sizes
1446     // and margins. So we use a maximum fixed size for the trough to match at least Adwaita, but that should look
1447     // good in other themes as well.
1448     static const int sliderSize = 4;
1449
1450     if (part == SliderHorizontalPart) {
1451         sliderRect.setHeight(std::min(rect.height(), sliderSize));
1452         sliderRect.move(0, (rect.height() - sliderRect.height()) / 2);
1453     } else {
1454         sliderRect.setWidth(std::min(rect.width(), sliderSize));
1455         sliderRect.move((rect.width() - sliderRect.width()) / 2, 0);
1456     }
1457
1458     gtk_render_background(context.get(), paintInfo.context().platformContext()->cr(), sliderRect.x(), sliderRect.y(), sliderRect.width(), sliderRect.height());
1459     gtk_render_frame(context.get(), paintInfo.context().platformContext()->cr(), sliderRect.x(), sliderRect.y(), sliderRect.width(), sliderRect.height());
1460
1461     if (isFocused(renderObject)) {
1462         gint focusWidth, focusPad;
1463         gtk_style_context_get_style(context.get(), "focus-line-width", &focusWidth, "focus-padding", &focusPad, nullptr);
1464         IntRect focusRect(sliderRect);
1465         focusRect.inflate(focusWidth + focusPad);
1466         gtk_render_focus(context.get(), paintInfo.context().platformContext()->cr(), focusRect.x(), focusRect.y(), focusRect.width(), focusRect.height());
1467     }
1468
1469     return false;
1470 }
1471
1472 void RenderThemeGtk::adjustSliderThumbSize(RenderStyle& style, Element*) const
1473 {
1474     ControlPart part = style.appearance();
1475     if (part != SliderThumbHorizontalPart && part != SliderThumbVerticalPart)
1476         return;
1477
1478     GRefPtr<GtkStyleContext> context = createStyleContext(Scale);
1479     gint sliderWidth, sliderLength;
1480     gtk_style_context_get_style(context.get(), "slider-width", &sliderWidth, "slider-length", &sliderLength, nullptr);
1481
1482     if (part == SliderThumbHorizontalPart) {
1483         style.setWidth(Length(sliderLength, Fixed));
1484         style.setHeight(Length(sliderWidth, Fixed));
1485         return;
1486     }
1487     ASSERT(part == SliderThumbVerticalPart);
1488     style.setWidth(Length(sliderWidth, Fixed));
1489     style.setHeight(Length(sliderLength, Fixed));
1490 }
1491
1492 bool RenderThemeGtk::paintSliderThumb(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
1493 {
1494     ControlPart part = renderObject.style().appearance();
1495     ASSERT(part == SliderThumbHorizontalPart || part == SliderThumbVerticalPart);
1496
1497     // FIXME: The entire slider is too wide, stretching the thumb into an oval rather than a circle.
1498     GRefPtr<GtkStyleContext> parentContext = createStyleContext(Scale);
1499     gtk_style_context_add_class(parentContext.get(), part == SliderThumbHorizontalPart ? GTK_STYLE_CLASS_HORIZONTAL : GTK_STYLE_CLASS_VERTICAL);
1500     GRefPtr<GtkStyleContext> troughContext = createStyleContext(ScaleTrough, parentContext.get());
1501     GRefPtr<GtkStyleContext> context = createStyleContext(ScaleSlider, troughContext.get());
1502     gtk_style_context_set_direction(context.get(), gtkTextDirection(renderObject.style().direction()));
1503
1504     guint flags = 0;
1505     if (!isEnabled(renderObject))
1506         flags |= GTK_STATE_FLAG_INSENSITIVE;
1507     else if (isHovered(renderObject))
1508         flags |= GTK_STATE_FLAG_PRELIGHT;
1509     if (isPressed(renderObject))
1510         flags |= GTK_STATE_FLAG_ACTIVE;
1511     gtk_style_context_set_state(context.get(), static_cast<GtkStateFlags>(flags));
1512
1513     gtk_render_slider(context.get(), paintInfo.context().platformContext()->cr(), rect.x(), rect.y(), rect.width(), rect.height(),
1514         part == SliderThumbHorizontalPart ? GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL);
1515
1516     return false;
1517 }
1518 #endif
1519
1520 #if GTK_CHECK_VERSION(3, 20, 0)
1521 IntRect RenderThemeGtk::progressBarRectForBounds(const RenderObject& renderObject, const IntRect& bounds) const
1522 {
1523     RenderThemeGadget::Info info = { RenderThemeGadget::Type::Generic, "progressbar", GTK_STATE_FLAG_NORMAL, { "horizontal" } };
1524     auto progressBarGadget = RenderThemeGadget::create(info);
1525     info.name = "trough";
1526     info.classList.clear();
1527     auto troughGadget = RenderThemeGadget::create(info, progressBarGadget.get());
1528     info.name = "progress";
1529     if (renderObject.style().direction() == RTL)
1530         info.classList.append("right");
1531     else
1532         info.classList.append("left");
1533     const auto& renderProgress = downcast<RenderProgress>(renderObject);
1534     if (renderProgress.isDeterminate())
1535         info.classList.append("pulse");
1536     auto progressGadget = RenderThemeGadget::create(info, troughGadget.get());
1537     IntSize preferredSize = progressBarGadget->preferredSize();
1538     preferredSize = preferredSize.expandedTo(troughGadget->preferredSize());
1539     preferredSize = preferredSize.expandedTo(progressGadget->preferredSize());
1540     return IntRect(bounds.x(), bounds.y(), bounds.width(), preferredSize.height());
1541 }
1542
1543 bool RenderThemeGtk::paintProgressBar(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
1544 {
1545     if (!renderObject.isProgress())
1546         return true;
1547
1548     RenderThemeGadget::Info info = { RenderThemeGadget::Type::Generic, "progressbar", GTK_STATE_FLAG_NORMAL, { "horizontal" } };
1549     auto progressBarGadget = RenderThemeGadget::create(info);
1550     info.name = "trough";
1551     info.classList.clear();
1552     auto troughGadget = RenderThemeGadget::create(info, progressBarGadget.get());
1553     info.name = "progress";
1554     if (renderObject.style().direction() == RTL)
1555         info.classList.append("right");
1556     else
1557         info.classList.append("left");
1558     const auto& renderProgress = downcast<RenderProgress>(renderObject);
1559     if (renderProgress.isDeterminate())
1560         info.classList.append("pulse");
1561     auto progressGadget = RenderThemeGadget::create(info, troughGadget.get());
1562
1563     progressBarGadget->render(paintInfo.context().platformContext()->cr(), rect);
1564     troughGadget->render(paintInfo.context().platformContext()->cr(), rect);
1565     progressGadget->render(paintInfo.context().platformContext()->cr(), calculateProgressRect(renderObject, rect));
1566     return false;
1567 }
1568 #else
1569 IntRect RenderThemeGtk::progressBarRectForBounds(const RenderObject&, const IntRect& bounds) const
1570 {
1571     return bounds;
1572 }
1573
1574 bool RenderThemeGtk::paintProgressBar(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
1575 {
1576     if (!renderObject.isProgress())
1577         return true;
1578
1579     GRefPtr<GtkStyleContext> parentContext = createStyleContext(ProgressBar);
1580     GRefPtr<GtkStyleContext> troughContext = createStyleContext(ProgressBarTrough, parentContext.get());
1581     GRefPtr<GtkStyleContext> context = createStyleContext(ProgressBarProgress, troughContext.get());
1582
1583     gtk_render_background(troughContext.get(), paintInfo.context().platformContext()->cr(), rect.x(), rect.y(), rect.width(), rect.height());
1584     gtk_render_frame(troughContext.get(), paintInfo.context().platformContext()->cr(), rect.x(), rect.y(), rect.width(), rect.height());
1585
1586     gtk_style_context_set_state(context.get(), static_cast<GtkStateFlags>(0));
1587
1588     GtkBorder padding;
1589     gtk_style_context_get_padding(context.get(), gtk_style_context_get_state(context.get()), &padding);
1590     IntRect progressRect(
1591         rect.x() + padding.left,
1592         rect.y() + padding.top,
1593         rect.width() - (padding.left + padding.right),
1594         rect.height() - (padding.top + padding.bottom));
1595     progressRect = RenderThemeGtk::calculateProgressRect(renderObject, progressRect);
1596
1597     if (!progressRect.isEmpty()) {
1598 #if GTK_CHECK_VERSION(3, 13, 7)
1599         gtk_render_background(context.get(), paintInfo.context().platformContext()->cr(), progressRect.x(), progressRect.y(), progressRect.width(), progressRect.height());
1600         gtk_render_frame(context.get(), paintInfo.context().platformContext()->cr(), progressRect.x(), progressRect.y(), progressRect.width(), progressRect.height());
1601 #else
1602         gtk_render_activity(context.get(), paintInfo.context().platformContext()->cr(), progressRect.x(), progressRect.y(), progressRect.width(), progressRect.height());
1603 #endif
1604     }
1605
1606     return false;
1607 }
1608 #endif // GTK_CHECK_VERSION(3, 20, 0)
1609
1610 #if GTK_CHECK_VERSION(3, 20, 0)
1611 RenderTheme::InnerSpinButtonLayout RenderThemeGtk::innerSpinButtonLayout(const RenderObject& renderObject) const
1612 {
1613     return renderObject.style().direction() == RTL ? InnerSpinButtonLayout::HorizontalUpLeft : InnerSpinButtonLayout::HorizontalUpRight;
1614 }
1615
1616 void RenderThemeGtk::adjustInnerSpinButtonStyle(StyleResolver&, RenderStyle& style, Element*) const
1617 {
1618     RenderThemeGadget::Info info = { RenderThemeGadget::Type::Generic, "spinbutton", GTK_STATE_FLAG_NORMAL, { "horizontal" } };
1619     auto spinbuttonGadget = RenderThemeGadget::create(info);
1620     info.type = RenderThemeGadget::Type::TextField;
1621     info.name = "entry";
1622     info.classList.clear();
1623     auto entryGadget = RenderThemeGadget::create(info, spinbuttonGadget.get());
1624     info.type = RenderThemeGadget::Type::Icon;
1625     info.name = "button";
1626     info.classList.append("up");
1627     auto buttonUpGadget = RenderThemeGadget::create(info, spinbuttonGadget.get());
1628     static_cast<RenderThemeIconGadget*>(buttonUpGadget.get())->setIconSize(RenderThemeIconGadget::IconSizeGtk::Menu);
1629     info.classList[0] = "down";
1630     auto buttonDownGadget = RenderThemeGadget::create(info, spinbuttonGadget.get());
1631     static_cast<RenderThemeIconGadget*>(buttonDownGadget.get())->setIconSize(RenderThemeIconGadget::IconSizeGtk::Menu);
1632     IntSize upPreferredSize = buttonUpGadget->preferredSize();
1633     IntSize downPreferredSize = buttonDownGadget->preferredSize();
1634     int buttonSize = std::max(std::max(upPreferredSize.width(), downPreferredSize.width()), std::max(upPreferredSize.height(), downPreferredSize.height()));
1635     style.setWidth(Length(buttonSize * 2, Fixed));
1636     style.setHeight(Length(buttonSize, Fixed));
1637 }
1638
1639 bool RenderThemeGtk::paintInnerSpinButton(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
1640 {
1641     RenderThemeGadget::Info info = { RenderThemeGadget::Type::Generic, "spinbutton", themePartStateFlags(*this, SpinButton, renderObject), { } };
1642     auto spinbuttonGadget = RenderThemeGadget::create(info);
1643     info.type = RenderThemeGadget::Type::TextField;
1644     info.name = "entry";
1645     info.classList.clear();
1646     auto entryGadget = RenderThemeGadget::create(info, spinbuttonGadget.get());
1647     info.type = RenderThemeGadget::Type::Icon;
1648     info.name = "button";
1649     info.classList.append("up");
1650     info.state = themePartStateFlags(*this, SpinButtonUpButton, renderObject);
1651     auto buttonUpGadget = RenderThemeGadget::create(info, spinbuttonGadget.get());
1652     auto* gadgetIcon = static_cast<RenderThemeIconGadget*>(buttonUpGadget.get());
1653     gadgetIcon->setIconSize(RenderThemeIconGadget::IconSizeGtk::Menu);
1654     gadgetIcon->setIconName("list-add-symbolic");
1655     info.classList[0] = "down";
1656     info.state = themePartStateFlags(*this, SpinButtonDownButton, renderObject);
1657     auto buttonDownGadget = RenderThemeGadget::create(info, spinbuttonGadget.get());
1658     gadgetIcon = static_cast<RenderThemeIconGadget*>(buttonDownGadget.get());
1659     gadgetIcon->setIconSize(RenderThemeIconGadget::IconSizeGtk::Menu);
1660     gadgetIcon->setIconName("list-remove-symbolic");
1661
1662     IntRect iconRect = rect;
1663     iconRect.setWidth(iconRect.width() / 2);
1664     if (renderObject.style().direction() == RTL)
1665         buttonUpGadget->render(paintInfo.context().platformContext()->cr(), iconRect);
1666     else
1667         buttonDownGadget->render(paintInfo.context().platformContext()->cr(), iconRect);
1668     iconRect.move(iconRect.width(), 0);
1669     if (renderObject.style().direction() == RTL)
1670         buttonDownGadget->render(paintInfo.context().platformContext()->cr(), iconRect);
1671     else
1672         buttonUpGadget->render(paintInfo.context().platformContext()->cr(), iconRect);
1673
1674     return false;
1675 }
1676 #else
1677 RenderTheme::InnerSpinButtonLayout RenderThemeGtk::innerSpinButtonLayout(const RenderObject&) const
1678 {
1679     return InnerSpinButtonLayout::Vertical;
1680 }
1681 static gint spinButtonArrowSize(GtkStyleContext* context)
1682 {
1683     PangoFontDescription* fontDescription;
1684     gtk_style_context_get(context, gtk_style_context_get_state(context), "font", &fontDescription, nullptr);
1685     gint fontSize = pango_font_description_get_size(fontDescription);
1686     gint arrowSize = std::max(PANGO_PIXELS(fontSize), minSpinButtonArrowSize);
1687     pango_font_description_free(fontDescription);
1688
1689     return arrowSize - arrowSize % 2; // Force even.
1690 }
1691
1692 void RenderThemeGtk::adjustInnerSpinButtonStyle(StyleResolver&, RenderStyle& style, Element*) const
1693 {
1694     GRefPtr<GtkStyleContext> context = createStyleContext(SpinButton);
1695
1696     GtkBorder padding;
1697     gtk_style_context_get_padding(context.get(), gtk_style_context_get_state(context.get()), &padding);
1698
1699     int width = spinButtonArrowSize(context.get()) + padding.left + padding.right;
1700     style.setWidth(Length(width, Fixed));
1701     style.setMinWidth(Length(width, Fixed));
1702 }
1703
1704 static void paintSpinArrowButton(RenderTheme* theme, GtkStyleContext* parentContext, const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect, GtkArrowType arrowType)
1705 {
1706     ASSERT(arrowType == GTK_ARROW_UP || arrowType == GTK_ARROW_DOWN);
1707
1708     GRefPtr<GtkStyleContext> context = createStyleContext(arrowType == GTK_ARROW_UP ? SpinButtonUpButton : SpinButtonDownButton, parentContext);
1709     GtkTextDirection direction = gtk_style_context_get_direction(context.get());
1710     guint state = static_cast<guint>(gtk_style_context_get_state(context.get()));
1711     if (!(state & GTK_STATE_FLAG_INSENSITIVE)) {
1712         if (theme->isPressed(renderObject)) {
1713             if ((arrowType == GTK_ARROW_UP && theme->isSpinUpButtonPartPressed(renderObject))
1714                 || (arrowType == GTK_ARROW_DOWN && !theme->isSpinUpButtonPartPressed(renderObject)))
1715                 state |= GTK_STATE_FLAG_ACTIVE;
1716         } else if (theme->isHovered(renderObject)) {
1717             if ((arrowType == GTK_ARROW_UP && theme->isSpinUpButtonPartHovered(renderObject))
1718                 || (arrowType == GTK_ARROW_DOWN && !theme->isSpinUpButtonPartHovered(renderObject)))
1719                 state |= GTK_STATE_FLAG_PRELIGHT;
1720         }
1721     }
1722     gtk_style_context_set_state(context.get(), static_cast<GtkStateFlags>(state));
1723
1724     // Paint button.
1725     IntRect buttonRect(rect);
1726     guint junction = gtk_style_context_get_junction_sides(context.get());
1727     if (arrowType == GTK_ARROW_UP)
1728         junction |= GTK_JUNCTION_BOTTOM;
1729     else {
1730         junction |= GTK_JUNCTION_TOP;
1731         buttonRect.move(0, rect.height() / 2);
1732     }
1733     buttonRect.setHeight(rect.height() / 2);
1734     gtk_style_context_set_junction_sides(context.get(), static_cast<GtkJunctionSides>(junction));
1735
1736     gtk_render_background(context.get(), paintInfo.context().platformContext()->cr(), buttonRect.x(), buttonRect.y(), buttonRect.width(), buttonRect.height());
1737     gtk_render_frame(context.get(), paintInfo.context().platformContext()->cr(), buttonRect.x(), buttonRect.y(), buttonRect.width(), buttonRect.height());
1738
1739     // Paint arrow centered inside button.
1740     // This code is based on gtkspinbutton.c code.
1741     IntRect arrowRect;
1742     gdouble angle;
1743     if (arrowType == GTK_ARROW_UP) {
1744         angle = 0;
1745         arrowRect.setY(rect.y());
1746         arrowRect.setHeight(rect.height() / 2 - 2);
1747     } else {
1748         angle = G_PI;
1749         arrowRect.setY(rect.y() + buttonRect.y());
1750         arrowRect.setHeight(rect.height() - arrowRect.y() - 2);
1751     }
1752     arrowRect.setWidth(rect.width() - 3);
1753     if (direction == GTK_TEXT_DIR_LTR)
1754         arrowRect.setX(rect.x() + 1);
1755     else
1756         arrowRect.setX(rect.x() + 2);
1757
1758     gint width = arrowRect.width() / 2;
1759     width -= width % 2 - 1; // Force odd.
1760     gint height = (width + 1) / 2;
1761
1762     arrowRect.move((arrowRect.width() - width) / 2, (arrowRect.height() - height) / 2);
1763     gtk_render_arrow(context.get(), paintInfo.context().platformContext()->cr(), angle, arrowRect.x(), arrowRect.y(), width);
1764 }
1765
1766 bool RenderThemeGtk::paintInnerSpinButton(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
1767 {
1768     GRefPtr<GtkStyleContext> context = createStyleContext(SpinButton);
1769     gtk_style_context_set_direction(context.get(), gtkTextDirection(renderObject.style().direction()));
1770
1771     guint flags = 0;
1772     if (!isEnabled(renderObject) || isReadOnlyControl(renderObject))
1773         flags |= GTK_STATE_FLAG_INSENSITIVE;
1774     else if (isFocused(renderObject))
1775         flags |= GTK_STATE_FLAG_FOCUSED;
1776     gtk_style_context_set_state(context.get(), static_cast<GtkStateFlags>(flags));
1777
1778     paintSpinArrowButton(this, context.get(), renderObject, paintInfo, rect, GTK_ARROW_UP);
1779     paintSpinArrowButton(this, context.get(), renderObject, paintInfo, rect, GTK_ARROW_DOWN);
1780
1781     return false;
1782 }
1783 #endif // GTK_CHECK_VERSION(3, 20, 0)
1784
1785 double RenderThemeGtk::caretBlinkInterval() const
1786 {
1787     GtkSettings* settings = gtk_settings_get_default();
1788
1789     gboolean shouldBlink;
1790     gint time;
1791
1792     g_object_get(settings, "gtk-cursor-blink", &shouldBlink, "gtk-cursor-blink-time", &time, nullptr);
1793
1794     if (!shouldBlink)
1795         return 0;
1796
1797     return time / 2000.;
1798 }
1799
1800 enum StyleColorType { StyleColorBackground, StyleColorForeground };
1801
1802 #if GTK_CHECK_VERSION(3, 20, 0)
1803 static Color styleColor(RenderThemePart themePart, GtkStateFlags state, StyleColorType colorType)
1804 {
1805     RenderThemeGadget::Info info = { RenderThemeGadget::Type::Generic, nullptr, state, { } };
1806     std::unique_ptr<RenderThemeGadget> parentGadget;
1807     RenderThemePart part = themePart;
1808     if (themePart == Entry && (state & GTK_STATE_FLAG_SELECTED)) {
1809         info.name = "entry";
1810         parentGadget = RenderThemeGadget::create(info);
1811         part = EntrySelection;
1812     }
1813
1814     switch (part) {
1815     case Entry:
1816         info.name = "entry";
1817         break;
1818     case EntrySelection:
1819         info.name = "selection";
1820         break;
1821     case ListBox:
1822         info.name = "treeview";
1823         info.classList.append("view");
1824         break;
1825     case Button:
1826         info.name = "button";
1827         break;
1828     default:
1829         ASSERT_NOT_REACHED();
1830         info.name = "entry";
1831     }
1832
1833     auto gadget = RenderThemeGadget::create(info, parentGadget.get());
1834     return colorType == StyleColorBackground ? gadget->backgroundColor() : gadget->color();
1835 }
1836 #else
1837 static Color styleColor(RenderThemePart themePart, GtkStateFlags state, StyleColorType colorType)
1838 {
1839     GRefPtr<GtkStyleContext> context = createStyleContext(themePart);
1840     gtk_style_context_set_state(context.get(), state);
1841
1842     GdkRGBA gdkRGBAColor;
1843     if (colorType == StyleColorBackground)
1844         gtk_style_context_get_background_color(context.get(), state, &gdkRGBAColor);
1845     else
1846         gtk_style_context_get_color(context.get(), state, &gdkRGBAColor);
1847     return gdkRGBAColor;
1848 }
1849 #endif // GTK_CHECK_VERSION(3, 20, 0)
1850
1851 Color RenderThemeGtk::platformActiveSelectionBackgroundColor() const
1852 {
1853     return styleColor(Entry, static_cast<GtkStateFlags>(GTK_STATE_FLAG_SELECTED | GTK_STATE_FLAG_FOCUSED), StyleColorBackground);
1854 }
1855
1856 Color RenderThemeGtk::platformInactiveSelectionBackgroundColor() const
1857 {
1858     return styleColor(Entry, GTK_STATE_FLAG_SELECTED, StyleColorBackground);
1859 }
1860
1861 Color RenderThemeGtk::platformActiveSelectionForegroundColor() const
1862 {
1863     return styleColor(Entry, static_cast<GtkStateFlags>(GTK_STATE_FLAG_SELECTED | GTK_STATE_FLAG_FOCUSED), StyleColorForeground);
1864 }
1865
1866 Color RenderThemeGtk::platformInactiveSelectionForegroundColor() const
1867 {
1868     return styleColor(Entry, GTK_STATE_FLAG_SELECTED, StyleColorForeground);
1869 }
1870
1871 Color RenderThemeGtk::platformActiveListBoxSelectionBackgroundColor() const
1872 {
1873     return styleColor(ListBox, static_cast<GtkStateFlags>(GTK_STATE_FLAG_SELECTED | GTK_STATE_FLAG_FOCUSED), StyleColorBackground);
1874 }
1875
1876 Color RenderThemeGtk::platformInactiveListBoxSelectionBackgroundColor() const
1877 {
1878     return styleColor(ListBox, GTK_STATE_FLAG_SELECTED, StyleColorBackground);
1879 }
1880
1881 Color RenderThemeGtk::platformActiveListBoxSelectionForegroundColor() const
1882 {
1883     return styleColor(ListBox, static_cast<GtkStateFlags>(GTK_STATE_FLAG_SELECTED | GTK_STATE_FLAG_FOCUSED), StyleColorForeground);
1884 }
1885
1886 Color RenderThemeGtk::platformInactiveListBoxSelectionForegroundColor() const
1887 {
1888     return styleColor(ListBox, GTK_STATE_FLAG_SELECTED, StyleColorForeground);
1889 }
1890
1891 Color RenderThemeGtk::systemColor(CSSValueID cssValueId) const
1892 {
1893     switch (cssValueId) {
1894     case CSSValueButtontext:
1895         return styleColor(Button, GTK_STATE_FLAG_ACTIVE, StyleColorForeground);
1896     case CSSValueCaptiontext:
1897         return styleColor(Entry, GTK_STATE_FLAG_ACTIVE, StyleColorForeground);
1898     default:
1899         return RenderTheme::systemColor(cssValueId);
1900     }
1901 }
1902
1903 void RenderThemeGtk::platformColorsDidChange()
1904 {
1905     RenderTheme::platformColorsDidChange();
1906 }
1907
1908 #if ENABLE(VIDEO)
1909 String RenderThemeGtk::extraMediaControlsStyleSheet()
1910 {
1911     return String(mediaControlsGtkUserAgentStyleSheet, sizeof(mediaControlsGtkUserAgentStyleSheet));
1912 }
1913
1914 #if ENABLE(FULLSCREEN_API)
1915 String RenderThemeGtk::extraFullScreenStyleSheet()
1916 {
1917     return String();
1918 }
1919 #endif
1920
1921 #if GTK_CHECK_VERSION(3, 20, 0)
1922 bool RenderThemeGtk::paintMediaButton(const RenderObject& renderObject, GraphicsContext& graphicsContext, const IntRect& rect, const char* iconName)
1923 {
1924     RenderThemeGadget::Info info = { RenderThemeGadget::Type::Icon, "image", themePartStateFlags(*this, MediaButton, renderObject), { } };
1925     auto gadget = RenderThemeGadget::create(info);
1926     auto* gadgetIcon = static_cast<RenderThemeIconGadget*>(gadget.get());
1927     gadgetIcon->setIconSize(RenderThemeIconGadget::IconSizeGtk::Menu);
1928     gadgetIcon->setIconName(iconName);
1929     return !gadget->render(graphicsContext.platformContext()->cr(), rect);
1930 }
1931 #else
1932 bool RenderThemeGtk::paintMediaButton(const RenderObject& renderObject, GraphicsContext& graphicsContext, const IntRect& rect, const char* iconName)
1933 {
1934     GRefPtr<GtkStyleContext> context = createStyleContext(MediaButton);
1935     gtk_style_context_set_direction(context.get(), gtkTextDirection(renderObject.style().direction()));
1936     gtk_style_context_set_state(context.get(), gtkIconStateFlags(this, renderObject));
1937     static const unsigned mediaIconSize = 16;
1938     IntRect iconRect(rect.x() + (rect.width() - mediaIconSize) / 2, rect.y() + (rect.height() - mediaIconSize) / 2, mediaIconSize, mediaIconSize);
1939     return !paintIcon(context.get(), graphicsContext, iconRect, iconName);
1940 }
1941 #endif // GTK_CHECK_VERSION(3, 20, 0)
1942
1943 bool RenderThemeGtk::hasOwnDisabledStateHandlingFor(ControlPart part) const
1944 {
1945     return (part != MediaMuteButtonPart);
1946 }
1947
1948 bool RenderThemeGtk::paintMediaFullscreenButton(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
1949 {
1950     return paintMediaButton(renderObject, paintInfo.context(), rect, "view-fullscreen-symbolic");
1951 }
1952
1953 bool RenderThemeGtk::paintMediaMuteButton(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
1954 {
1955     Node* node = renderObject.node();
1956     if (!node)
1957         return true;
1958     Node* mediaNode = node->shadowHost();
1959     if (!is<HTMLMediaElement>(mediaNode))
1960         return true;
1961
1962     HTMLMediaElement* mediaElement = downcast<HTMLMediaElement>(mediaNode);
1963     return paintMediaButton(renderObject, paintInfo.context(), rect, mediaElement->muted() ? "audio-volume-muted-symbolic" : "audio-volume-high-symbolic");
1964 }
1965
1966 bool RenderThemeGtk::paintMediaPlayButton(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
1967 {
1968     Node* node = renderObject.node();
1969     if (!node)
1970         return true;
1971     if (!nodeHasPseudo(node, "-webkit-media-controls-play-button"))
1972         return true;
1973
1974     return paintMediaButton(renderObject, paintInfo.context(), rect, nodeHasClass(node, "paused") ? "media-playback-start-symbolic" : "media-playback-pause-symbolic");
1975 }
1976
1977 bool RenderThemeGtk::paintMediaSeekBackButton(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
1978 {
1979     return paintMediaButton(renderObject, paintInfo.context(), rect, "media-seek-backward-symbolic");
1980 }
1981
1982 bool RenderThemeGtk::paintMediaSeekForwardButton(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
1983 {
1984     return paintMediaButton(renderObject, paintInfo.context(), rect, "media-seek-forward-symbolic");
1985 }
1986
1987 #if ENABLE(VIDEO_TRACK)
1988 bool RenderThemeGtk::paintMediaToggleClosedCaptionsButton(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
1989 {
1990     return paintMediaButton(renderObject, paintInfo.context(), rect, "media-view-subtitles-symbolic");
1991 }
1992 #endif
1993
1994 static FloatRoundedRect::Radii borderRadiiFromStyle(RenderStyle& style)
1995 {
1996     return FloatRoundedRect::Radii(
1997         IntSize(style.borderTopLeftRadius().width().intValue(), style.borderTopLeftRadius().height().intValue()),
1998         IntSize(style.borderTopRightRadius().width().intValue(), style.borderTopRightRadius().height().intValue()),
1999         IntSize(style.borderBottomLeftRadius().width().intValue(), style.borderBottomLeftRadius().height().intValue()),
2000         IntSize(style.borderBottomRightRadius().width().intValue(), style.borderBottomRightRadius().height().intValue()));
2001 }
2002
2003 bool RenderThemeGtk::paintMediaSliderTrack(const RenderObject& o, const PaintInfo& paintInfo, const IntRect& r)
2004 {
2005     HTMLMediaElement* mediaElement = parentMediaElement(o);
2006     if (!mediaElement)
2007         return true;
2008
2009     GraphicsContext& context = paintInfo.context();
2010     context.save();
2011     context.setStrokeStyle(NoStroke);
2012
2013     float mediaDuration = mediaElement->duration();
2014     float totalTrackWidth = r.width();
2015     RenderStyle& style = o.style();
2016     RefPtr<TimeRanges> timeRanges = mediaElement->buffered();
2017     for (unsigned index = 0; index < timeRanges->length(); ++index) {
2018         float start = timeRanges->start(index, IGNORE_EXCEPTION);
2019         float end = timeRanges->end(index, IGNORE_EXCEPTION);
2020         float startRatio = start / mediaDuration;
2021         float lengthRatio = (end - start) / mediaDuration;
2022         if (!lengthRatio)
2023             continue;
2024
2025         IntRect rangeRect(r);
2026         rangeRect.setWidth(lengthRatio * totalTrackWidth);
2027         if (index)
2028             rangeRect.move(startRatio * totalTrackWidth, 0);
2029         context.fillRoundedRect(FloatRoundedRect(rangeRect, borderRadiiFromStyle(style)), style.visitedDependentColor(CSSPropertyColor));
2030     }
2031
2032     context.restore();
2033     return false;
2034 }
2035
2036 bool RenderThemeGtk::paintMediaSliderThumb(const RenderObject& o, const PaintInfo& paintInfo, const IntRect& r)
2037 {
2038     RenderStyle& style = o.style();
2039     paintInfo.context().fillRoundedRect(FloatRoundedRect(r, borderRadiiFromStyle(style)), style.visitedDependentColor(CSSPropertyColor));
2040     return false;
2041 }
2042
2043 bool RenderThemeGtk::paintMediaVolumeSliderTrack(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
2044 {
2045     HTMLMediaElement* mediaElement = parentMediaElement(renderObject);
2046     if (!mediaElement)
2047         return true;
2048
2049     float volume = mediaElement->muted() ? 0.0f : mediaElement->volume();
2050     if (!volume)
2051         return true;
2052
2053     GraphicsContext& context = paintInfo.context();
2054     context.save();
2055     context.setStrokeStyle(NoStroke);
2056
2057     int rectHeight = rect.height();
2058     float trackHeight = rectHeight * volume;
2059     RenderStyle& style = renderObject.style();
2060     IntRect volumeRect(rect);
2061     volumeRect.move(0, rectHeight - trackHeight);
2062     volumeRect.setHeight(ceil(trackHeight));
2063
2064     context.fillRoundedRect(FloatRoundedRect(volumeRect, borderRadiiFromStyle(style)), style.visitedDependentColor(CSSPropertyColor));
2065     context.restore();
2066
2067     return false;
2068 }
2069
2070 bool RenderThemeGtk::paintMediaVolumeSliderThumb(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
2071 {
2072     return paintMediaSliderThumb(renderObject, paintInfo, rect);
2073 }
2074
2075 String RenderThemeGtk::formatMediaControlsCurrentTime(float currentTime, float duration) const
2076 {
2077     return formatMediaControlsTime(currentTime) + " / " + formatMediaControlsTime(duration);
2078 }
2079
2080 bool RenderThemeGtk::paintMediaCurrentTime(const RenderObject&, const PaintInfo&, const IntRect&)
2081 {
2082     return false;
2083 }
2084 #endif
2085
2086 void RenderThemeGtk::adjustProgressBarStyle(StyleResolver&, RenderStyle& style, Element*) const
2087 {
2088     style.setBoxShadow(nullptr);
2089 }
2090
2091 // These values have been copied from RenderThemeChromiumSkia.cpp
2092 static const int progressActivityBlocks = 5;
2093 static const int progressAnimationFrames = 10;
2094 static const double progressAnimationInterval = 0.125;
2095 double RenderThemeGtk::animationRepeatIntervalForProgressBar(RenderProgress&) const
2096 {
2097     return progressAnimationInterval;
2098 }
2099
2100 double RenderThemeGtk::animationDurationForProgressBar(RenderProgress&) const
2101 {
2102     return progressAnimationInterval * progressAnimationFrames * 2; // "2" for back and forth;
2103 }
2104
2105 IntRect RenderThemeGtk::calculateProgressRect(const RenderObject& renderObject, const IntRect& fullBarRect)
2106 {
2107     IntRect progressRect(fullBarRect);
2108     const auto& renderProgress = downcast<RenderProgress>(renderObject);
2109     if (renderProgress.isDeterminate()) {
2110         int progressWidth = progressRect.width() * renderProgress.position();
2111         if (renderObject.style().direction() == RTL)
2112             progressRect.setX(progressRect.x() + progressRect.width() - progressWidth);
2113         progressRect.setWidth(progressWidth);
2114         return progressRect;
2115     }
2116
2117     double animationProgress = renderProgress.animationProgress();
2118
2119     // Never let the progress rect shrink smaller than 2 pixels.
2120     int newWidth = std::max(2, progressRect.width() / progressActivityBlocks);
2121     int movableWidth = progressRect.width() - newWidth;
2122     progressRect.setWidth(newWidth);
2123
2124     // We want the first 0.5 units of the animation progress to represent the
2125     // forward motion and the second 0.5 units to represent the backward motion,
2126     // thus we multiply by two here to get the full sweep of the progress bar with
2127     // each direction.
2128     if (animationProgress < 0.5)
2129         progressRect.setX(progressRect.x() + (animationProgress * 2 * movableWidth));
2130     else
2131         progressRect.setX(progressRect.x() + ((1.0 - animationProgress) * 2 * movableWidth));
2132     return progressRect;
2133 }
2134
2135 String RenderThemeGtk::fileListNameForWidth(const FileList* fileList, const FontCascade& font, int width, bool multipleFilesAllowed) const
2136 {
2137     if (width <= 0)
2138         return String();
2139
2140     if (fileList->length() > 1)
2141         return StringTruncator::rightTruncate(multipleFileUploadText(fileList->length()), width, font);
2142
2143     String string;
2144     if (fileList->length())
2145         string = pathGetFileName(fileList->item(0)->path());
2146     else if (multipleFilesAllowed)
2147         string = fileButtonNoFilesSelectedLabel();
2148     else
2149         string = fileButtonNoFileSelectedLabel();
2150
2151     return StringTruncator::centerTruncate(string, width, font);
2152 }
2153
2154 #if ENABLE(VIDEO)
2155 String RenderThemeGtk::mediaControlsScript()
2156 {
2157     StringBuilder scriptBuilder;
2158     scriptBuilder.append(mediaControlsLocalizedStringsJavaScript, sizeof(mediaControlsLocalizedStringsJavaScript));
2159     scriptBuilder.append(mediaControlsBaseJavaScript, sizeof(mediaControlsBaseJavaScript));
2160     scriptBuilder.append(mediaControlsGtkJavaScript, sizeof(mediaControlsGtkJavaScript));
2161     return scriptBuilder.toString();
2162 }
2163 #endif // ENABLE(VIDEO)
2164
2165 #endif // GTK_API_VERSION_2
2166 }