[GTK] Fix return value of some paint methods in RenderThemeGtk
[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 "HTMLMediaElement.h"
39 #include "LocalizedStrings.h"
40 #include "MediaControlElements.h"
41 #include "NamedNodeMap.h"
42 #include "Page.h"
43 #include "PaintInfo.h"
44 #include "PlatformContextCairo.h"
45 #include "RenderBox.h"
46 #include "RenderObject.h"
47 #include "RenderProgress.h"
48 #include "ScrollbarThemeGtk.h"
49 #include "StringTruncator.h"
50 #include "TimeRanges.h"
51 #include "UserAgentScripts.h"
52 #include "UserAgentStyleSheets.h"
53 #include <cmath>
54 #include <gdk/gdk.h>
55 #include <glib.h>
56 #include <gtk/gtk.h>
57 #include <wtf/NeverDestroyed.h>
58 #include <wtf/glib/GRefPtr.h>
59 #include <wtf/glib/GUniquePtr.h>
60 #include <wtf/text/CString.h>
61 #include <wtf/text/StringBuilder.h>
62
63 namespace WebCore {
64
65 Ref<RenderTheme> RenderThemeGtk::create()
66 {
67     return adoptRef(*new RenderThemeGtk());
68 }
69
70 PassRefPtr<RenderTheme> RenderTheme::themeForPage(Page*)
71 {
72     static RenderTheme& rt = RenderThemeGtk::create().leakRef();
73     return &rt;
74 }
75
76 static double getScreenDPI()
77 {
78     // FIXME: Really this should be the widget's screen.
79     GdkScreen* screen = gdk_screen_get_default();
80     if (!screen)
81         return 96; // Default to 96 DPI.
82
83     float dpi = gdk_screen_get_resolution(screen);
84     if (dpi <= 0)
85         return 96;
86     return dpi;
87 }
88
89 void RenderThemeGtk::updateCachedSystemFontDescription(CSSValueID, FontCascadeDescription& fontDescription) const
90 {
91     GtkSettings* settings = gtk_settings_get_default();
92     if (!settings)
93         return;
94
95     // This will be a font selection string like "Sans 10" so we cannot use it as the family name.
96     GUniqueOutPtr<gchar> fontName;
97     g_object_get(settings, "gtk-font-name", &fontName.outPtr(), nullptr);
98     if (!fontName || !fontName.get()[0])
99         return;
100
101     PangoFontDescription* pangoDescription = pango_font_description_from_string(fontName.get());
102     if (!pangoDescription)
103         return;
104
105     fontDescription.setOneFamily(pango_font_description_get_family(pangoDescription));
106
107     int size = pango_font_description_get_size(pangoDescription) / PANGO_SCALE;
108     // If the size of the font is in points, we need to convert it to pixels.
109     if (!pango_font_description_get_size_is_absolute(pangoDescription))
110         size = size * (getScreenDPI() / 72.0);
111
112     fontDescription.setSpecifiedSize(size);
113     fontDescription.setIsAbsoluteSize(true);
114     fontDescription.setWeight(FontWeightNormal);
115     fontDescription.setItalic(FontItalicOff);
116     pango_font_description_free(pangoDescription);
117 }
118
119 #if ENABLE(DATALIST_ELEMENT)
120 IntSize RenderThemeGtk::sliderTickSize() const
121 {
122     // FIXME: We need to set this to the size of one tick mark.
123     return IntSize(0, 0);
124 }
125
126 int RenderThemeGtk::sliderTickOffsetFromTrackCenter() const
127 {
128     // FIXME: We need to set this to the position of the tick marks.
129     return 0;
130 }
131 #endif
132
133 #ifndef GTK_API_VERSION_2
134
135 // This is the default value defined by GTK+, where it was defined as MIN_ARROW_SIZE in gtkarrow.c.
136 static const int minArrowSize = 15;
137 // This is the default value defined by GTK+, where it was defined as MIN_ARROW_WIDTH in gtkspinbutton.c.
138 static const int minSpinButtonArrowSize = 6;
139
140 enum RenderThemePart {
141     Entry,
142     EntrySelection,
143     EntryIconLeft,
144     EntryIconRight,
145     Button,
146     CheckButton,
147     CheckButtonCheck,
148     RadioButton,
149     RadioButtonRadio,
150     ComboBox,
151     ComboBoxButton,
152     ComboBoxArrow,
153     Scale,
154     ScaleTrough,
155     ScaleSlider,
156     ProgressBar,
157     ProgressBarTrough,
158     ProgressBarProgress,
159     ListBox,
160     ListBoxSelection,
161     SpinButton,
162     SpinButtonUpButton,
163     SpinButtonDownButton,
164 #if ENABLE(VIDEO)
165     MediaButton,
166 #endif
167 };
168
169 static void gtkStyleChangedCallback(GObject*, GParamSpec*)
170 {
171     static_cast<ScrollbarThemeGtk&>(ScrollbarTheme::theme()).themeChanged();
172     Page::updateStyleForAllPagesAfterGlobalChangeInEnvironment();
173 }
174
175 static GRefPtr<GtkStyleContext> createStyleContext(RenderThemePart themePart, GtkStyleContext* parent = nullptr)
176 {
177     static bool initialized = false;
178     if (!initialized) {
179         GtkSettings* settings = gtk_settings_get_default();
180         g_signal_connect(settings, "notify::gtk-theme-name", G_CALLBACK(gtkStyleChangedCallback), nullptr);
181         g_signal_connect(settings, "notify::gtk-color-scheme", G_CALLBACK(gtkStyleChangedCallback), nullptr);
182         initialized = true;
183     }
184
185     GRefPtr<GtkWidgetPath> path = adoptGRef(parent ? gtk_widget_path_copy(gtk_style_context_get_path(parent)) : gtk_widget_path_new());
186
187     switch (themePart) {
188     case Entry:
189         gtk_widget_path_append_type(path.get(), GTK_TYPE_ENTRY);
190 #if GTK_CHECK_VERSION(3, 19, 2)
191         gtk_widget_path_iter_set_object_name(path.get(), -1, "entry");
192 #else
193         gtk_widget_path_iter_add_class(path.get(), -1, GTK_STYLE_CLASS_ENTRY);
194 #endif
195         break;
196     case EntrySelection:
197         gtk_widget_path_append_type(path.get(), GTK_TYPE_ENTRY);
198 #if GTK_CHECK_VERSION(3, 19, 2)
199         gtk_widget_path_iter_set_object_name(path.get(), -1, "selection");
200 #else
201         gtk_widget_path_iter_add_class(path.get(), -1, GTK_STYLE_CLASS_ENTRY);
202 #endif
203         break;
204     case EntryIconLeft:
205     case EntryIconRight:
206         gtk_widget_path_append_type(path.get(), GTK_TYPE_ENTRY);
207 #if GTK_CHECK_VERSION(3, 19, 2)
208         gtk_widget_path_iter_set_object_name(path.get(), -1, "image");
209         gtk_widget_path_iter_add_class(path.get(), -1, themePart == EntryIconLeft ? "left" : "right");
210 #endif
211         break;
212     case Button:
213         gtk_widget_path_append_type(path.get(), GTK_TYPE_BUTTON);
214 #if GTK_CHECK_VERSION(3, 19, 2)
215         gtk_widget_path_iter_set_object_name(path.get(), -1, "button");
216 #else
217         gtk_widget_path_iter_add_class(path.get(), -1, GTK_STYLE_CLASS_BUTTON);
218 #endif
219         gtk_widget_path_iter_add_class(path.get(), -1, "text-button");
220         break;
221     case CheckButton:
222         gtk_widget_path_append_type(path.get(), GTK_TYPE_CHECK_BUTTON);
223 #if GTK_CHECK_VERSION(3, 19, 2)
224         gtk_widget_path_iter_set_object_name(path.get(), -1, "checkbutton");
225 #else
226         gtk_widget_path_iter_add_class(path.get(), -1, GTK_STYLE_CLASS_CHECK);
227 #endif
228         break;
229     case CheckButtonCheck:
230         gtk_widget_path_append_type(path.get(), GTK_TYPE_CHECK_BUTTON);
231 #if GTK_CHECK_VERSION(3, 19, 2)
232         gtk_widget_path_iter_set_object_name(path.get(), -1, "check");
233 #else
234         gtk_widget_path_iter_add_class(path.get(), -1, GTK_STYLE_CLASS_CHECK);
235 #endif
236         break;
237     case RadioButton:
238         gtk_widget_path_append_type(path.get(), GTK_TYPE_RADIO_BUTTON);
239 #if GTK_CHECK_VERSION(3, 19, 2)
240         gtk_widget_path_iter_set_object_name(path.get(), -1, "radiobutton");
241 #else
242         gtk_widget_path_iter_add_class(path.get(), -1, GTK_STYLE_CLASS_RADIO);
243 #endif
244         break;
245     case RadioButtonRadio:
246         gtk_widget_path_append_type(path.get(), GTK_TYPE_RADIO_BUTTON);
247 #if GTK_CHECK_VERSION(3, 19, 2)
248         gtk_widget_path_iter_set_object_name(path.get(), -1, "radio");
249 #else
250         gtk_widget_path_iter_add_class(path.get(), -1, GTK_STYLE_CLASS_RADIO);
251 #endif
252         break;
253     case ComboBox:
254         gtk_widget_path_append_type(path.get(), GTK_TYPE_COMBO_BOX);
255 #if GTK_CHECK_VERSION(3, 19, 2)
256         gtk_widget_path_iter_set_object_name(path.get(), -1, "combobox");
257 #endif
258         break;
259     case ComboBoxButton:
260         gtk_widget_path_append_type(path.get(), GTK_TYPE_BUTTON);
261 #if GTK_CHECK_VERSION(3, 19, 2)
262         gtk_widget_path_iter_set_object_name(path.get(), -1, "button");
263 #else
264         gtk_widget_path_iter_add_class(path.get(), -1, GTK_STYLE_CLASS_BUTTON);
265 #endif
266         gtk_widget_path_iter_add_class(path.get(), -1, "text-button");
267         gtk_widget_path_iter_add_class(path.get(), -1, "combo");
268         break;
269     case ComboBoxArrow:
270         gtk_widget_path_append_type(path.get(), GTK_TYPE_ARROW);
271 #if GTK_CHECK_VERSION(3, 19, 2)
272         gtk_widget_path_iter_set_object_name(path.get(), -1, "arrow");
273 #else
274         gtk_widget_path_iter_add_class(path.get(), -1, "arrow");
275 #endif
276         break;
277     case Scale:
278         gtk_widget_path_append_type(path.get(), GTK_TYPE_SCALE);
279 #if GTK_CHECK_VERSION(3, 19, 2)
280         gtk_widget_path_iter_set_object_name(path.get(), -1, "scale");
281 #else
282         gtk_widget_path_iter_add_class(path.get(), -1, GTK_STYLE_CLASS_SCALE);
283 #endif
284         break;
285     case ScaleTrough:
286         gtk_widget_path_append_type(path.get(), GTK_TYPE_SCALE);
287 #if GTK_CHECK_VERSION(3, 19, 2)
288         gtk_widget_path_iter_set_object_name(path.get(), -1, "trough");
289 #else
290         gtk_widget_path_iter_add_class(path.get(), -1, GTK_STYLE_CLASS_SCALE);
291         gtk_widget_path_iter_add_class(path.get(), -1, GTK_STYLE_CLASS_TROUGH);
292 #endif
293         break;
294     case ScaleSlider:
295         gtk_widget_path_append_type(path.get(), GTK_TYPE_SCALE);
296 #if GTK_CHECK_VERSION(3, 19, 2)
297         gtk_widget_path_iter_set_object_name(path.get(), -1, "slider");
298 #else
299         gtk_widget_path_iter_add_class(path.get(), -1, GTK_STYLE_CLASS_SCALE);
300         gtk_widget_path_iter_add_class(path.get(), -1, GTK_STYLE_CLASS_SLIDER);
301 #endif
302         break;
303     case ProgressBar:
304         gtk_widget_path_append_type(path.get(), GTK_TYPE_PROGRESS_BAR);
305 #if GTK_CHECK_VERSION(3, 19, 2)
306         gtk_widget_path_iter_set_object_name(path.get(), -1, "progressbar");
307 #else
308         gtk_widget_path_iter_add_class(path.get(), -1, GTK_STYLE_CLASS_PROGRESSBAR);
309 #endif
310         gtk_widget_path_iter_add_class(path.get(), -1, GTK_STYLE_CLASS_HORIZONTAL);
311         break;
312     case ProgressBarTrough:
313         gtk_widget_path_append_type(path.get(), GTK_TYPE_PROGRESS_BAR);
314 #if GTK_CHECK_VERSION(3, 19, 2)
315         gtk_widget_path_iter_set_object_name(path.get(), -1, "trough");
316 #else
317         gtk_widget_path_iter_add_class(path.get(), -1, GTK_STYLE_CLASS_PROGRESSBAR);
318         gtk_widget_path_iter_add_class(path.get(), -1, GTK_STYLE_CLASS_TROUGH);
319 #endif
320         break;
321     case ProgressBarProgress:
322         gtk_widget_path_append_type(path.get(), GTK_TYPE_PROGRESS_BAR);
323 #if GTK_CHECK_VERSION(3, 19, 2)
324         gtk_widget_path_iter_set_object_name(path.get(), -1, "progress");
325 #else
326         gtk_widget_path_iter_add_class(path.get(), -1, GTK_STYLE_CLASS_PROGRESSBAR);
327 #endif
328         break;
329     case ListBox:
330         gtk_widget_path_append_type(path.get(), GTK_TYPE_TREE_VIEW);
331 #if GTK_CHECK_VERSION(3, 19, 2)
332         gtk_widget_path_iter_set_object_name(path.get(), -1, "treeview");
333 #endif
334         gtk_widget_path_iter_add_class(path.get(), -1, GTK_STYLE_CLASS_VIEW);
335         break;
336     case ListBoxSelection:
337         gtk_widget_path_append_type(path.get(), GTK_TYPE_TREE_VIEW);
338 #if GTK_CHECK_VERSION(3, 19, 2)
339         gtk_widget_path_iter_set_object_name(path.get(), -1, "selection");
340 #else
341         gtk_widget_path_iter_add_class(path.get(), -1, GTK_STYLE_CLASS_VIEW);
342 #endif
343         break;
344     case SpinButton:
345         gtk_widget_path_append_type(path.get(), GTK_TYPE_SPIN_BUTTON);
346 #if GTK_CHECK_VERSION(3, 19, 2)
347         gtk_widget_path_iter_set_object_name(path.get(), -1, "spinbutton");
348 #else
349         gtk_widget_path_iter_add_class(path.get(), -1, GTK_STYLE_CLASS_SPINBUTTON);
350 #endif
351         gtk_widget_path_iter_add_class(path.get(), -1, GTK_STYLE_CLASS_HORIZONTAL);
352         break;
353     case SpinButtonUpButton:
354     case SpinButtonDownButton:
355         gtk_widget_path_append_type(path.get(), GTK_TYPE_SPIN_BUTTON);
356 #if GTK_CHECK_VERSION(3, 19, 2)
357         gtk_widget_path_iter_set_object_name(path.get(), -1, "button");
358         gtk_widget_path_iter_add_class(path.get(), -1, themePart == SpinButtonUpButton ? "up" : "down");
359 #else
360         gtk_widget_path_iter_add_class(path.get(), -1, GTK_STYLE_CLASS_SPINBUTTON);
361         gtk_widget_path_iter_add_class(path.get(), -1, GTK_STYLE_CLASS_BUTTON);
362 #endif
363         break;
364 #if ENABLE(VIDEO)
365     case MediaButton:
366         gtk_widget_path_append_type(path.get(), GTK_TYPE_IMAGE);
367 #if GTK_CHECK_VERSION(3, 19, 2)
368         gtk_widget_path_iter_set_object_name(path.get(), -1, "image");
369 #else
370         gtk_widget_path_iter_add_class(path.get(), -1, GTK_STYLE_CLASS_IMAGE);
371 #endif
372         break;
373 #endif // ENABLE(VIDEO)
374     }
375
376     GRefPtr<GtkStyleContext> context = adoptGRef(gtk_style_context_new());
377     gtk_style_context_set_path(context.get(), path.get());
378     gtk_style_context_set_parent(context.get(), parent);
379     return context;
380 }
381
382 static GRefPtr<GdkPixbuf> loadThemedIcon(GtkStyleContext* context, const char* iconName, GtkIconSize iconSize)
383 {
384     GRefPtr<GIcon> icon = adoptGRef(g_themed_icon_new(iconName));
385     unsigned lookupFlags = GTK_ICON_LOOKUP_USE_BUILTIN | GTK_ICON_LOOKUP_FORCE_SIZE | GTK_ICON_LOOKUP_FORCE_SVG;
386     GtkTextDirection direction = gtk_style_context_get_direction(context);
387     if (direction & GTK_TEXT_DIR_LTR)
388         lookupFlags |= GTK_ICON_LOOKUP_DIR_LTR;
389     else if (direction & GTK_TEXT_DIR_RTL)
390         lookupFlags |= GTK_ICON_LOOKUP_DIR_RTL;
391     int width, height;
392     gtk_icon_size_lookup(iconSize, &width, &height);
393     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)));
394     if (!iconInfo)
395         return nullptr;
396
397     return adoptGRef(gtk_icon_info_load_symbolic_for_context(iconInfo.get(), context, nullptr, nullptr));
398 }
399
400 static bool nodeHasPseudo(Node* node, const char* pseudo)
401 {
402     RefPtr<Node> attributeNode = node->attributes()->getNamedItem("pseudo");
403
404     return attributeNode ? attributeNode->nodeValue() == pseudo : false;
405 }
406
407 static bool nodeHasClass(Node* node, const char* className)
408 {
409     if (!is<Element>(*node))
410         return false;
411
412     Element& element = downcast<Element>(*node);
413
414     if (!element.hasClass())
415         return false;
416
417     return element.classNames().contains(className);
418 }
419
420 RenderThemeGtk::~RenderThemeGtk()
421 {
422 }
423
424 static bool supportsFocus(ControlPart appearance)
425 {
426     switch (appearance) {
427     case PushButtonPart:
428     case ButtonPart:
429     case TextFieldPart:
430     case TextAreaPart:
431     case SearchFieldPart:
432     case MenulistPart:
433     case RadioPart:
434     case CheckboxPart:
435     case SliderHorizontalPart:
436     case SliderVerticalPart:
437         return true;
438     default:
439         return false;
440     }
441 }
442
443 bool RenderThemeGtk::supportsFocusRing(const RenderStyle& style) const
444 {
445     return supportsFocus(style.appearance());
446 }
447
448 bool RenderThemeGtk::controlSupportsTints(const RenderObject& o) const
449 {
450     return isEnabled(o);
451 }
452
453 int RenderThemeGtk::baselinePosition(const RenderBox& box) const
454 {
455     // FIXME: This strategy is possibly incorrect for the GTK+ port.
456     if (box.style().appearance() == CheckboxPart || box.style().appearance() == RadioPart)
457         return box.marginTop() + box.height() - 2;
458     return RenderTheme::baselinePosition(box);
459 }
460
461 static GtkTextDirection gtkTextDirection(TextDirection direction)
462 {
463     switch (direction) {
464     case RTL:
465         return GTK_TEXT_DIR_RTL;
466     case LTR:
467         return GTK_TEXT_DIR_LTR;
468     default:
469         return GTK_TEXT_DIR_NONE;
470     }
471 }
472
473 static GtkStateFlags gtkIconStateFlags(RenderTheme* theme, const RenderObject& renderObject)
474 {
475     if (!theme->isEnabled(renderObject))
476         return GTK_STATE_FLAG_INSENSITIVE;
477     if (theme->isPressed(renderObject))
478         return GTK_STATE_FLAG_ACTIVE;
479     if (theme->isHovered(renderObject))
480         return GTK_STATE_FLAG_PRELIGHT;
481
482     return GTK_STATE_FLAG_NORMAL;
483 }
484
485 static void adjustRectForFocus(GtkStyleContext* context, FloatRect& rect)
486 {
487     gint focusWidth, focusPad;
488     gtk_style_context_get_style(context, "focus-line-width", &focusWidth, "focus-padding", &focusPad, nullptr);
489     rect.inflate(focusWidth + focusPad);
490 }
491
492 void RenderThemeGtk::adjustRepaintRect(const RenderObject& renderObject, FloatRect& rect)
493 {
494     GRefPtr<GtkStyleContext> context;
495     bool checkInteriorFocus = false;
496     ControlPart part = renderObject.style().appearance();
497     switch (part) {
498     case CheckboxPart:
499     case RadioPart:
500         context = createStyleContext(part == CheckboxPart ? CheckButton : RadioButton);
501
502         gint indicatorSpacing;
503         gtk_style_context_get_style(context.get(), "indicator-spacing", &indicatorSpacing, nullptr);
504         rect.inflate(indicatorSpacing);
505
506         return;
507     case SliderVerticalPart:
508     case SliderHorizontalPart:
509         context = createStyleContext(ScaleSlider);
510         break;
511     case ButtonPart:
512     case MenulistButtonPart:
513     case MenulistPart:
514         context = createStyleContext(Button);
515         checkInteriorFocus = true;
516         break;
517     case TextFieldPart:
518     case TextAreaPart:
519         context = createStyleContext(Entry);
520         checkInteriorFocus = true;
521         break;
522     default:
523         return;
524     }
525
526     ASSERT(context);
527     if (checkInteriorFocus) {
528         gboolean interiorFocus;
529         gtk_style_context_get_style(context.get(), "interior-focus", &interiorFocus, nullptr);
530         if (interiorFocus)
531             return;
532     }
533     adjustRectForFocus(context.get(), rect);
534 }
535
536 void RenderThemeGtk::adjustButtonStyle(StyleResolver&, RenderStyle& style, Element*) const
537 {
538     // Some layout tests check explicitly that buttons ignore line-height.
539     if (style.appearance() == PushButtonPart)
540         style.setLineHeight(RenderStyle::initialLineHeight());
541 }
542
543 static void setToggleSize(RenderThemePart themePart, RenderStyle& style)
544 {
545     // The width and height are both specified, so we shouldn't change them.
546     if (!style.width().isIntrinsicOrAuto() && !style.height().isAuto())
547         return;
548
549     GRefPtr<GtkStyleContext> context = createStyleContext(themePart);
550     // Other ports hard-code this to 13. GTK+ users tend to demand the native look.
551     gint indicatorSize;
552     gtk_style_context_get_style(context.get(), "indicator-size", &indicatorSize, nullptr);
553
554     if (style.width().isIntrinsicOrAuto())
555         style.setWidth(Length(indicatorSize, Fixed));
556
557     if (style.height().isAuto())
558         style.setHeight(Length(indicatorSize, Fixed));
559 }
560
561 static void paintToggle(const RenderThemeGtk* theme, RenderThemePart themePart, const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& fullRect)
562 {
563     GRefPtr<GtkStyleContext> parentContext = createStyleContext(themePart);
564     GRefPtr<GtkStyleContext> context = createStyleContext(themePart == CheckButton ? CheckButtonCheck : RadioButtonRadio, parentContext.get());
565     gtk_style_context_set_direction(context.get(), static_cast<GtkTextDirection>(gtkTextDirection(renderObject.style().direction())));
566
567     unsigned flags = 0;
568     if (!theme->isEnabled(renderObject))
569         flags |= GTK_STATE_FLAG_INSENSITIVE;
570     else if (theme->isHovered(renderObject))
571         flags |= GTK_STATE_FLAG_PRELIGHT;
572     if (theme->isIndeterminate(renderObject))
573         flags |= GTK_STATE_FLAG_INCONSISTENT;
574     else if (theme->isChecked(renderObject))
575 #if GTK_CHECK_VERSION(3, 13, 7)
576         flags |= GTK_STATE_FLAG_CHECKED;
577 #else
578         flags |= GTK_STATE_FLAG_ACTIVE;
579 #endif
580     if (theme->isPressed(renderObject))
581         flags |= GTK_STATE_FLAG_SELECTED;
582     gtk_style_context_set_state(context.get(), static_cast<GtkStateFlags>(flags));
583
584     // Some themes do not render large toggle buttons properly, so we simply
585     // shrink the rectangle back down to the default size and then center it
586     // in the full toggle button region. The reason for not simply forcing toggle
587     // buttons to be a smaller size is that we don't want to break site layouts.
588     gint indicatorSize;
589     gtk_style_context_get_style(context.get(), "indicator-size", &indicatorSize, nullptr);
590     IntRect rect(fullRect);
591     if (rect.width() > indicatorSize) {
592         rect.inflateX(-(rect.width() - indicatorSize) / 2);
593         rect.setWidth(indicatorSize); // In case rect.width() was equal to indicatorSize + 1.
594     }
595
596     if (rect.height() > indicatorSize) {
597         rect.inflateY(-(rect.height() - indicatorSize) / 2);
598         rect.setHeight(indicatorSize); // In case rect.height() was equal to indicatorSize + 1.
599     }
600
601     gtk_render_background(context.get(), paintInfo.context().platformContext()->cr(), rect.x(), rect.y(), rect.width(), rect.height());
602     gtk_render_frame(context.get(), paintInfo.context().platformContext()->cr(), rect.x(), rect.y(), rect.width(), rect.height());
603
604     if (themePart == CheckButton)
605         gtk_render_check(context.get(), paintInfo.context().platformContext()->cr(), rect.x(), rect.y(), rect.width(), rect.height());
606     else
607         gtk_render_option(context.get(), paintInfo.context().platformContext()->cr(), rect.x(), rect.y(), rect.width(), rect.height());
608
609     if (theme->isFocused(renderObject)) {
610         IntRect indicatorRect(rect);
611         gint indicatorSpacing;
612         gtk_style_context_get_style(context.get(), "indicator-spacing", &indicatorSpacing, nullptr);
613         indicatorRect.inflate(indicatorSpacing);
614         gtk_render_focus(context.get(), paintInfo.context().platformContext()->cr(), indicatorRect.x(), indicatorRect.y(),
615             indicatorRect.width(), indicatorRect.height());
616     }
617 }
618
619 void RenderThemeGtk::setCheckboxSize(RenderStyle& style) const
620 {
621     setToggleSize(CheckButton, style);
622 }
623
624 bool RenderThemeGtk::paintCheckbox(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
625 {
626     paintToggle(this, CheckButton, renderObject, paintInfo, rect);
627     return false;
628 }
629
630 void RenderThemeGtk::setRadioSize(RenderStyle& style) const
631 {
632     setToggleSize(RadioButton, style);
633 }
634
635 bool RenderThemeGtk::paintRadio(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
636 {
637     paintToggle(this, RadioButton, renderObject, paintInfo, rect);
638     return false;
639 }
640
641 static void renderButton(RenderTheme* theme, GtkStyleContext* context, const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
642 {
643     IntRect buttonRect(rect);
644
645     guint flags = 0;
646     if (!theme->isEnabled(renderObject))
647         flags |= GTK_STATE_FLAG_INSENSITIVE;
648     else if (theme->isHovered(renderObject))
649         flags |= GTK_STATE_FLAG_PRELIGHT;
650     if (theme->isPressed(renderObject))
651         flags |= GTK_STATE_FLAG_ACTIVE;
652     gtk_style_context_set_state(context, static_cast<GtkStateFlags>(flags));
653
654     if (theme->isDefault(renderObject)) {
655         GtkBorder* borderPtr = 0;
656         GtkBorder border = { 1, 1, 1, 1 };
657
658         gtk_style_context_get_style(context, "default-border", &borderPtr, nullptr);
659         if (borderPtr) {
660             border = *borderPtr;
661             gtk_border_free(borderPtr);
662         }
663
664         buttonRect.move(border.left, border.top);
665         buttonRect.setWidth(buttonRect.width() - (border.left + border.right));
666         buttonRect.setHeight(buttonRect.height() - (border.top + border.bottom));
667
668         gtk_style_context_add_class(context, GTK_STYLE_CLASS_DEFAULT);
669     }
670
671     gtk_render_background(context, paintInfo.context().platformContext()->cr(), buttonRect.x(), buttonRect.y(), buttonRect.width(), buttonRect.height());
672     gtk_render_frame(context, paintInfo.context().platformContext()->cr(), buttonRect.x(), buttonRect.y(), buttonRect.width(), buttonRect.height());
673
674     if (theme->isFocused(renderObject)) {
675         gint focusWidth, focusPad;
676         gboolean displaceFocus, interiorFocus;
677         gtk_style_context_get_style(
678             context,
679             "focus-line-width", &focusWidth,
680             "focus-padding", &focusPad,
681             "interior-focus", &interiorFocus,
682             "displace-focus", &displaceFocus,
683             nullptr);
684
685         if (interiorFocus) {
686             GtkBorder borderWidth;
687             gtk_style_context_get_border(context, gtk_style_context_get_state(context), &borderWidth);
688
689             buttonRect = IntRect(
690                 buttonRect.x() + borderWidth.left + focusPad,
691                 buttonRect.y() + borderWidth.top + focusPad,
692                 buttonRect.width() - (2 * focusPad + borderWidth.left + borderWidth.right),
693                 buttonRect.height() - (2 * focusPad + borderWidth.top + borderWidth.bottom));
694         } else
695             buttonRect.inflate(focusWidth + focusPad);
696
697         if (displaceFocus && theme->isPressed(renderObject)) {
698             gint childDisplacementX;
699             gint childDisplacementY;
700             gtk_style_context_get_style(context, "child-displacement-x", &childDisplacementX, "child-displacement-y", &childDisplacementY, nullptr);
701             buttonRect.move(childDisplacementX, childDisplacementY);
702         }
703
704         gtk_render_focus(context, paintInfo.context().platformContext()->cr(), buttonRect.x(), buttonRect.y(), buttonRect.width(), buttonRect.height());
705     }
706 }
707 bool RenderThemeGtk::paintButton(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
708 {
709     GRefPtr<GtkStyleContext> context = createStyleContext(Button);
710     gtk_style_context_set_direction(context.get(), static_cast<GtkTextDirection>(gtkTextDirection(renderObject.style().direction())));
711     renderButton(this, context.get(), renderObject, paintInfo, rect);
712     return false;
713 }
714
715 void RenderThemeGtk::adjustMenuListStyle(StyleResolver&, RenderStyle& style, Element*) const
716 {
717     // The tests check explicitly that select menu buttons ignore line height.
718     style.setLineHeight(RenderStyle::initialLineHeight());
719
720     // We cannot give a proper rendering when border radius is active, unfortunately.
721     style.resetBorderRadius();
722 }
723
724 void RenderThemeGtk::adjustMenuListButtonStyle(StyleResolver& styleResolver, RenderStyle& style, Element* e) const
725 {
726     adjustMenuListStyle(styleResolver, style, e);
727 }
728
729 static void getComboBoxMetrics(RenderStyle& style, GtkBorder& border, int& focus)
730 {
731     // If this menu list button isn't drawn using the native theme, we
732     // don't add any extra padding beyond what WebCore already uses.
733     if (style.appearance() == NoControlPart)
734         return;
735
736     GRefPtr<GtkStyleContext> parentContext = createStyleContext(ComboBox);
737     GRefPtr<GtkStyleContext> context = createStyleContext(ComboBoxButton, parentContext.get());
738     gtk_style_context_set_direction(context.get(), static_cast<GtkTextDirection>(gtkTextDirection(style.direction())));
739     gtk_style_context_set_state(context.get(), static_cast<GtkStateFlags>(0));
740     gtk_style_context_get_border(context.get(), gtk_style_context_get_state(context.get()), &border);
741
742     gboolean interiorFocus;
743     gint focusWidth, focusPad;
744     gtk_style_context_get_style(context.get(), "interior-focus", &interiorFocus, "focus-line-width", &focusWidth, "focus-padding", &focusPad, nullptr);
745     focus = interiorFocus ? focusWidth + focusPad : 0;
746 }
747
748 int RenderThemeGtk::popupInternalPaddingLeft(RenderStyle& style) const
749 {
750     GtkBorder borderWidth = { 0, 0, 0, 0 };
751     int focusWidth = 0;
752     getComboBoxMetrics(style, borderWidth, focusWidth);
753     int left = borderWidth.left + focusWidth;
754     if (style.direction() == RTL)
755         left += minArrowSize;
756     return left;
757 }
758
759 int RenderThemeGtk::popupInternalPaddingRight(RenderStyle& style) const
760 {
761     GtkBorder borderWidth = { 0, 0, 0, 0 };
762     int focusWidth = 0;
763     getComboBoxMetrics(style, borderWidth, focusWidth);
764     int right = borderWidth.right + focusWidth;
765     if (style.direction() == LTR)
766         right += minArrowSize;
767     return right;
768 }
769
770 int RenderThemeGtk::popupInternalPaddingTop(RenderStyle& style) const
771 {
772     GtkBorder borderWidth = { 0, 0, 0, 0 };
773     int focusWidth = 0;
774     getComboBoxMetrics(style, borderWidth, focusWidth);
775     return borderWidth.top + focusWidth;
776 }
777
778 int RenderThemeGtk::popupInternalPaddingBottom(RenderStyle& style) const
779 {
780     GtkBorder borderWidth = { 0, 0, 0, 0 };
781     int focusWidth = 0;
782     getComboBoxMetrics(style, borderWidth, focusWidth);
783     return borderWidth.bottom + focusWidth;
784 }
785
786 bool RenderThemeGtk::paintMenuList(const RenderObject& renderObject, const PaintInfo& paintInfo, const FloatRect& r)
787 {
788     // FIXME: adopt subpixel themes.
789     IntRect rect = IntRect(r);
790
791     cairo_t* cairoContext = paintInfo.context().platformContext()->cr();
792     GtkTextDirection direction = static_cast<GtkTextDirection>(gtkTextDirection(renderObject.style().direction()));
793
794     GRefPtr<GtkStyleContext> parentStyleContext = createStyleContext(ComboBox);
795
796     // Paint the button.
797     GRefPtr<GtkStyleContext> buttonStyleContext = createStyleContext(ComboBoxButton, parentStyleContext.get());
798     gtk_style_context_set_direction(buttonStyleContext.get(), direction);
799     renderButton(this, buttonStyleContext.get(), renderObject, paintInfo, rect);
800
801     // Get the inner rectangle.
802     gint focusWidth, focusPad;
803     GtkBorder* innerBorderPtr = 0;
804     GtkBorder innerBorder = { 1, 1, 1, 1 };
805     gtk_style_context_get_style(buttonStyleContext.get(), "inner-border", &innerBorderPtr, "focus-line-width", &focusWidth, "focus-padding", &focusPad, nullptr);
806     if (innerBorderPtr) {
807         innerBorder = *innerBorderPtr;
808         gtk_border_free(innerBorderPtr);
809     }
810
811     GtkBorder borderWidth;
812     GtkStateFlags state = gtk_style_context_get_state(buttonStyleContext.get());
813     gtk_style_context_get_border(buttonStyleContext.get(), state, &borderWidth);
814
815     focusWidth += focusPad;
816     IntRect innerRect(
817         rect.x() + innerBorder.left + borderWidth.left + focusWidth,
818         rect.y() + innerBorder.top + borderWidth.top + focusWidth,
819         rect.width() - borderWidth.left - borderWidth.right - innerBorder.left - innerBorder.right - (2 * focusWidth),
820         rect.height() - borderWidth.top - borderWidth.bottom - innerBorder.top - innerBorder.bottom - (2 * focusWidth));
821
822     if (isPressed(renderObject)) {
823         gint childDisplacementX;
824         gint childDisplacementY;
825         gtk_style_context_get_style(buttonStyleContext.get(), "child-displacement-x", &childDisplacementX, "child-displacement-y", &childDisplacementY, nullptr);
826         innerRect.move(childDisplacementX, childDisplacementY);
827     }
828     innerRect.setWidth(std::max(1, innerRect.width()));
829     innerRect.setHeight(std::max(1, innerRect.height()));
830
831     // Paint the arrow.
832     GRefPtr<GtkStyleContext> arrowStyleContext = createStyleContext(ComboBoxArrow, buttonStyleContext.get());
833     gtk_style_context_set_direction(arrowStyleContext.get(), direction);
834
835 #if GTK_CHECK_VERSION(3, 19, 2)
836     // arrow-scaling style property is now deprecated and ignored.
837     gfloat arrowScaling = 1.;
838 #else
839     gfloat arrowScaling;
840     gtk_style_context_get_style(parentStyleContext.get(), "arrow-scaling", &arrowScaling, nullptr);
841 #endif
842
843     IntSize arrowSize(minArrowSize, innerRect.height());
844     FloatPoint arrowPosition(innerRect.location());
845     if (direction == GTK_TEXT_DIR_LTR)
846         arrowPosition.move(innerRect.width() - arrowSize.width(), 0);
847
848     // GTK+ actually fetches the xalign and valign values from the widget, but since we
849     // don't have a widget here, we are just using the default xalign and valign values of 0.5.
850     gint extent = std::min(arrowSize.width(), arrowSize.height()) * arrowScaling;
851     arrowPosition.move((arrowSize.width() - extent) / 2, (arrowSize.height() - extent) / 2);
852
853     gtk_style_context_set_state(arrowStyleContext.get(), state);
854     gtk_render_arrow(arrowStyleContext.get(), cairoContext, G_PI, arrowPosition.x(), arrowPosition.y(), extent);
855
856     return false;
857 }
858
859 bool RenderThemeGtk::paintMenuListButtonDecorations(const RenderBox& object, const PaintInfo& info, const FloatRect& rect)
860 {
861     return paintMenuList(object, info, rect);
862 }
863
864 bool RenderThemeGtk::paintTextField(const RenderObject& renderObject, const PaintInfo& paintInfo, const FloatRect& rect)
865 {
866     GRefPtr<GtkStyleContext> context = createStyleContext(Entry);
867     gtk_style_context_set_direction(context.get(), static_cast<GtkTextDirection>(gtkTextDirection(renderObject.style().direction())));
868
869     guint flags = 0;
870     if (!isEnabled(renderObject) || isReadOnlyControl(renderObject))
871         flags |= GTK_STATE_FLAG_INSENSITIVE;
872     else if (isFocused(renderObject))
873         flags |= GTK_STATE_FLAG_FOCUSED;
874     gtk_style_context_set_state(context.get(), static_cast<GtkStateFlags>(flags));
875
876     gtk_render_background(context.get(), paintInfo.context().platformContext()->cr(), rect.x(), rect.y(), rect.width(), rect.height());
877     gtk_render_frame(context.get(), paintInfo.context().platformContext()->cr(), rect.x(), rect.y(), rect.width(), rect.height());
878
879     if (isFocused(renderObject) && isEnabled(renderObject)) {
880         gboolean interiorFocus;
881         gint focusWidth, focusPad;
882         gtk_style_context_get_style(context.get(), "interior-focus", &interiorFocus, "focus-line-width", &focusWidth, "focus-padding", &focusPad, nullptr);
883         if (!interiorFocus) {
884             IntRect focusRect(rect);
885             focusRect.inflate(focusWidth + focusPad);
886             gtk_render_focus(context.get(), paintInfo.context().platformContext()->cr(), focusRect.x(), focusRect.y(), focusRect.width(), focusRect.height());
887         }
888     }
889
890     return false;
891 }
892
893 bool RenderThemeGtk::paintTextArea(const RenderObject& o, const PaintInfo& i, const FloatRect& r)
894 {
895     return paintTextField(o, i, r);
896 }
897
898 // Defined in GTK+ (gtk/gtkiconfactory.c)
899 static const gint gtkIconSizeMenu = 16;
900 static const gint gtkIconSizeSmallToolbar = 18;
901 static const gint gtkIconSizeButton = 20;
902 static const gint gtkIconSizeLargeToolbar = 24;
903 static const gint gtkIconSizeDnd = 32;
904 static const gint gtkIconSizeDialog = 48;
905
906 static GtkIconSize getIconSizeForPixelSize(gint pixelSize)
907 {
908     if (pixelSize < gtkIconSizeSmallToolbar)
909         return GTK_ICON_SIZE_MENU;
910     if (pixelSize >= gtkIconSizeSmallToolbar && pixelSize < gtkIconSizeButton)
911         return GTK_ICON_SIZE_SMALL_TOOLBAR;
912     if (pixelSize >= gtkIconSizeButton && pixelSize < gtkIconSizeLargeToolbar)
913         return GTK_ICON_SIZE_BUTTON;
914     if (pixelSize >= gtkIconSizeLargeToolbar && pixelSize < gtkIconSizeDnd)
915         return GTK_ICON_SIZE_LARGE_TOOLBAR;
916     if (pixelSize >= gtkIconSizeDnd && pixelSize < gtkIconSizeDialog)
917         return GTK_ICON_SIZE_DND;
918
919     return GTK_ICON_SIZE_DIALOG;
920 }
921
922 void RenderThemeGtk::adjustSearchFieldResultsButtonStyle(StyleResolver& styleResolver, RenderStyle& style, Element* e) const
923 {
924     adjustSearchFieldCancelButtonStyle(styleResolver, style, e);
925 }
926
927 bool RenderThemeGtk::paintSearchFieldResultsButton(const RenderBox& o, const PaintInfo& i, const IntRect& rect)
928 {
929     return paintSearchFieldResultsDecorationPart(o, i, rect);
930 }
931
932 static void adjustSearchFieldIconStyle(RenderThemePart themePart, RenderStyle& style)
933 {
934     style.resetBorder();
935     style.resetPadding();
936
937     GRefPtr<GtkStyleContext> parentContext = createStyleContext(Entry);
938     GRefPtr<GtkStyleContext> context = createStyleContext(themePart, parentContext.get());
939
940     GtkBorder padding;
941     gtk_style_context_get_padding(context.get(), gtk_style_context_get_state(context.get()), &padding);
942
943     // Get the icon size based on the font size.
944     int fontSize = style.fontSize();
945     if (fontSize < gtkIconSizeMenu) {
946         style.setWidth(Length(fontSize + (padding.left + padding.right), Fixed));
947         style.setHeight(Length(fontSize + (padding.top + padding.bottom), Fixed));
948         return;
949     }
950     gint width = 0, height = 0;
951     gtk_icon_size_lookup(getIconSizeForPixelSize(fontSize), &width, &height);
952     style.setWidth(Length(width + (padding.left + padding.right), Fixed));
953     style.setHeight(Length(height + (padding.top + padding.bottom), Fixed));
954 }
955
956 void RenderThemeGtk::adjustSearchFieldResultsDecorationPartStyle(StyleResolver&, RenderStyle& style, Element*) const
957 {
958     adjustSearchFieldIconStyle(EntryIconLeft, style);
959 }
960
961 static IntRect centerRectVerticallyInParentInputElement(const RenderObject& renderObject, const IntRect& rect)
962 {
963     if (!renderObject.node())
964         return IntRect();
965
966     // Get the renderer of <input> element.
967     Node* input = renderObject.node()->shadowHost();
968     if (!input)
969         input = renderObject.node();
970     if (!is<RenderBox>(*input->renderer()))
971         return IntRect();
972
973     // If possible center the y-coordinate of the rect vertically in the parent input element.
974     // We also add one pixel here to ensure that the y coordinate is rounded up for box heights
975     // that are even, which looks in relation to the box text.
976     IntRect inputContentBox = downcast<RenderBox>(*input->renderer()).absoluteContentBox();
977
978     // Make sure the scaled decoration stays square and will fit in its parent's box.
979     int iconSize = std::min(inputContentBox.width(), std::min(inputContentBox.height(), rect.height()));
980     IntRect scaledRect(rect.x(), inputContentBox.y() + (inputContentBox.height() - iconSize + 1) / 2, iconSize, iconSize);
981     return scaledRect;
982 }
983
984 static bool paintIcon(GtkStyleContext* context, GraphicsContext& graphicsContext, const IntRect& rect, const char* iconName)
985 {
986     GRefPtr<GdkPixbuf> icon = loadThemedIcon(context, iconName, getIconSizeForPixelSize(rect.height()));
987     if (!icon)
988         return false;
989
990     if (gdk_pixbuf_get_width(icon.get()) > rect.width() || gdk_pixbuf_get_height(icon.get()) > rect.height())
991         icon = adoptGRef(gdk_pixbuf_scale_simple(icon.get(), rect.width(), rect.height(), GDK_INTERP_BILINEAR));
992
993     gtk_render_icon(context, graphicsContext.platformContext()->cr(), icon.get(), rect.x(), rect.y());
994     return true;
995 }
996
997 static bool paintEntryIcon(RenderThemePart themePart, const char* iconName, GraphicsContext& graphicsContext, const IntRect& rect, GtkTextDirection direction, GtkStateFlags state)
998 {
999     GRefPtr<GtkStyleContext> parentContext = createStyleContext(Entry);
1000     GRefPtr<GtkStyleContext> context = createStyleContext(themePart, parentContext.get());
1001     gtk_style_context_set_direction(context.get(), direction);
1002     gtk_style_context_set_state(context.get(), state);
1003     return paintIcon(context.get(), graphicsContext, rect, iconName);
1004 }
1005
1006 bool RenderThemeGtk::paintSearchFieldResultsDecorationPart(const RenderBox& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
1007 {
1008     IntRect iconRect = centerRectVerticallyInParentInputElement(renderObject, rect);
1009     if (iconRect.isEmpty())
1010         return true;
1011
1012     return !paintEntryIcon(EntryIconLeft, "edit-find-symbolic", paintInfo.context(), iconRect, gtkTextDirection(renderObject.style().direction()),
1013         gtkIconStateFlags(this, renderObject));
1014 }
1015
1016 void RenderThemeGtk::adjustSearchFieldCancelButtonStyle(StyleResolver&, RenderStyle& style, Element*) const
1017 {
1018     adjustSearchFieldIconStyle(EntryIconRight, style);
1019 }
1020
1021 bool RenderThemeGtk::paintSearchFieldCancelButton(const RenderBox& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
1022 {
1023     IntRect iconRect = centerRectVerticallyInParentInputElement(renderObject, rect);
1024     if (iconRect.isEmpty())
1025         return true;
1026
1027     return !paintEntryIcon(EntryIconRight, "edit-clear-symbolic", paintInfo.context(), iconRect, gtkTextDirection(renderObject.style().direction()),
1028         gtkIconStateFlags(this, renderObject));
1029 }
1030
1031 void RenderThemeGtk::adjustSearchFieldStyle(StyleResolver&, RenderStyle& style, Element*) const
1032 {
1033     // We cannot give a proper rendering when border radius is active, unfortunately.
1034     style.resetBorderRadius();
1035     style.setLineHeight(RenderStyle::initialLineHeight());
1036 }
1037
1038 bool RenderThemeGtk::paintSearchField(const RenderObject& o, const PaintInfo& i, const IntRect& rect)
1039 {
1040     return paintTextField(o, i, rect);
1041 }
1042
1043 bool RenderThemeGtk::shouldHaveCapsLockIndicator(HTMLInputElement& element) const
1044 {
1045     return element.isPasswordField();
1046 }
1047
1048 void RenderThemeGtk::adjustSliderTrackStyle(StyleResolver&, RenderStyle& style, Element*) const
1049 {
1050     style.setBoxShadow(nullptr);
1051 }
1052
1053 void RenderThemeGtk::adjustSliderThumbStyle(StyleResolver& styleResolver, RenderStyle& style, Element* element) const
1054 {
1055     RenderTheme::adjustSliderThumbStyle(styleResolver, style, element);
1056     style.setBoxShadow(nullptr);
1057 }
1058
1059 bool RenderThemeGtk::paintSliderTrack(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
1060 {
1061     ControlPart part = renderObject.style().appearance();
1062     ASSERT_UNUSED(part, part == SliderHorizontalPart || part == SliderVerticalPart);
1063
1064     GRefPtr<GtkStyleContext> parentContext = createStyleContext(Scale);
1065     gtk_style_context_add_class(parentContext.get(), part == SliderHorizontalPart ? GTK_STYLE_CLASS_HORIZONTAL : GTK_STYLE_CLASS_VERTICAL);
1066     GRefPtr<GtkStyleContext> context = createStyleContext(ScaleTrough, parentContext.get());
1067     gtk_style_context_set_direction(context.get(), gtkTextDirection(renderObject.style().direction()));
1068
1069     if (!isEnabled(renderObject))
1070         gtk_style_context_set_state(context.get(), GTK_STATE_FLAG_INSENSITIVE);
1071
1072     gtk_render_background(context.get(), paintInfo.context().platformContext()->cr(), rect.x(), rect.y(), rect.width(), rect.height());
1073     gtk_render_frame(context.get(), paintInfo.context().platformContext()->cr(), rect.x(), rect.y(), rect.width(), rect.height());
1074
1075     if (isFocused(renderObject)) {
1076         gint focusWidth, focusPad;
1077         gtk_style_context_get_style(context.get(), "focus-line-width", &focusWidth, "focus-padding", &focusPad, nullptr);
1078         IntRect focusRect(rect);
1079         focusRect.inflate(focusWidth + focusPad);
1080         gtk_render_focus(context.get(), paintInfo.context().platformContext()->cr(), focusRect.x(), focusRect.y(), focusRect.width(), focusRect.height());
1081     }
1082
1083     return false;
1084 }
1085
1086 bool RenderThemeGtk::paintSliderThumb(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
1087 {
1088     ControlPart part = renderObject.style().appearance();
1089     ASSERT(part == SliderThumbHorizontalPart || part == SliderThumbVerticalPart);
1090
1091     // FIXME: The entire slider is too wide, stretching the thumb into an oval rather than a circle.
1092     GRefPtr<GtkStyleContext> parentContext = createStyleContext(Scale);
1093     gtk_style_context_add_class(parentContext.get(), part == SliderThumbHorizontalPart ? GTK_STYLE_CLASS_HORIZONTAL : GTK_STYLE_CLASS_VERTICAL);
1094     GRefPtr<GtkStyleContext> troughContext = createStyleContext(ScaleTrough, parentContext.get());
1095     GRefPtr<GtkStyleContext> context = createStyleContext(ScaleSlider, troughContext.get());
1096     gtk_style_context_set_direction(context.get(), gtkTextDirection(renderObject.style().direction()));
1097
1098     guint flags = 0;
1099     if (!isEnabled(renderObject))
1100         flags |= GTK_STATE_FLAG_INSENSITIVE;
1101     else if (isHovered(renderObject))
1102         flags |= GTK_STATE_FLAG_PRELIGHT;
1103     if (isPressed(renderObject))
1104         flags |= GTK_STATE_FLAG_ACTIVE;
1105     gtk_style_context_set_state(context.get(), static_cast<GtkStateFlags>(flags));
1106
1107     gtk_render_slider(context.get(), paintInfo.context().platformContext()->cr(), rect.x(), rect.y(), rect.width(), rect.height(),
1108         part == SliderThumbHorizontalPart ? GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL);
1109
1110     return false;
1111 }
1112
1113 void RenderThemeGtk::adjustSliderThumbSize(RenderStyle& style, Element*) const
1114 {
1115     ControlPart part = style.appearance();
1116     if (part != SliderThumbHorizontalPart && part != SliderThumbVerticalPart)
1117         return;
1118
1119     GRefPtr<GtkStyleContext> context = createStyleContext(Scale);
1120     gint sliderWidth, sliderLength;
1121     gtk_style_context_get_style(context.get(), "slider-width", &sliderWidth, "slider-length", &sliderLength, nullptr);
1122     if (part == SliderThumbHorizontalPart) {
1123         style.setWidth(Length(sliderLength, Fixed));
1124         style.setHeight(Length(sliderWidth, Fixed));
1125         return;
1126     }
1127     ASSERT(part == SliderThumbVerticalPart);
1128     style.setWidth(Length(sliderWidth, Fixed));
1129     style.setHeight(Length(sliderLength, Fixed));
1130 }
1131
1132 bool RenderThemeGtk::paintProgressBar(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
1133 {
1134     if (!renderObject.isProgress())
1135         return true;
1136
1137     GRefPtr<GtkStyleContext> parentContext = createStyleContext(ProgressBar);
1138     GRefPtr<GtkStyleContext> troughContext = createStyleContext(ProgressBarTrough, parentContext.get());
1139     GRefPtr<GtkStyleContext> context = createStyleContext(ProgressBarProgress, troughContext.get());
1140
1141     gtk_render_background(troughContext.get(), paintInfo.context().platformContext()->cr(), rect.x(), rect.y(), rect.width(), rect.height());
1142     gtk_render_frame(troughContext.get(), paintInfo.context().platformContext()->cr(), rect.x(), rect.y(), rect.width(), rect.height());
1143
1144     gtk_style_context_set_state(context.get(), static_cast<GtkStateFlags>(0));
1145
1146     GtkBorder padding;
1147     gtk_style_context_get_padding(context.get(), gtk_style_context_get_state(context.get()), &padding);
1148     IntRect progressRect(
1149         rect.x() + padding.left,
1150         rect.y() + padding.top,
1151         rect.width() - (padding.left + padding.right),
1152         rect.height() - (padding.top + padding.bottom));
1153     progressRect = RenderThemeGtk::calculateProgressRect(renderObject, progressRect);
1154
1155     if (!progressRect.isEmpty()) {
1156 #if GTK_CHECK_VERSION(3, 13, 7)
1157         gtk_render_background(context.get(), paintInfo.context().platformContext()->cr(), progressRect.x(), progressRect.y(), progressRect.width(), progressRect.height());
1158         gtk_render_frame(context.get(), paintInfo.context().platformContext()->cr(), progressRect.x(), progressRect.y(), progressRect.width(), progressRect.height());
1159 #else
1160         gtk_render_activity(context.get(), paintInfo.context().platformContext()->cr(), progressRect.x(), progressRect.y(), progressRect.width(), progressRect.height());
1161 #endif
1162     }
1163
1164     return false;
1165 }
1166
1167 static gint spinButtonArrowSize(GtkStyleContext* context)
1168 {
1169     PangoFontDescription* fontDescription;
1170     gtk_style_context_get(context, gtk_style_context_get_state(context), "font", &fontDescription, nullptr);
1171     gint fontSize = pango_font_description_get_size(fontDescription);
1172     gint arrowSize = std::max(PANGO_PIXELS(fontSize), minSpinButtonArrowSize);
1173     pango_font_description_free(fontDescription);
1174
1175     return arrowSize - arrowSize % 2; // Force even.
1176 }
1177
1178 void RenderThemeGtk::adjustInnerSpinButtonStyle(StyleResolver&, RenderStyle& style, Element*) const
1179 {
1180     GRefPtr<GtkStyleContext> context = createStyleContext(SpinButton);
1181
1182     GtkBorder padding;
1183     gtk_style_context_get_padding(context.get(), gtk_style_context_get_state(context.get()), &padding);
1184
1185     int width = spinButtonArrowSize(context.get()) + padding.left + padding.right;
1186     style.setWidth(Length(width, Fixed));
1187     style.setMinWidth(Length(width, Fixed));
1188 }
1189
1190 static void paintSpinArrowButton(RenderTheme* theme, GtkStyleContext* parentContext, const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect, GtkArrowType arrowType)
1191 {
1192     ASSERT(arrowType == GTK_ARROW_UP || arrowType == GTK_ARROW_DOWN);
1193
1194     GRefPtr<GtkStyleContext> context = createStyleContext(arrowType == GTK_ARROW_UP ? SpinButtonUpButton : SpinButtonDownButton, parentContext);
1195     GtkTextDirection direction = gtk_style_context_get_direction(context.get());
1196     guint state = static_cast<guint>(gtk_style_context_get_state(context.get()));
1197     if (!(state & GTK_STATE_FLAG_INSENSITIVE)) {
1198         if (theme->isPressed(renderObject)) {
1199             if ((arrowType == GTK_ARROW_UP && theme->isSpinUpButtonPartPressed(renderObject))
1200                 || (arrowType == GTK_ARROW_DOWN && !theme->isSpinUpButtonPartPressed(renderObject)))
1201                 state |= GTK_STATE_FLAG_ACTIVE;
1202         } else if (theme->isHovered(renderObject)) {
1203             if ((arrowType == GTK_ARROW_UP && theme->isSpinUpButtonPartHovered(renderObject))
1204                 || (arrowType == GTK_ARROW_DOWN && !theme->isSpinUpButtonPartHovered(renderObject)))
1205                 state |= GTK_STATE_FLAG_PRELIGHT;
1206         }
1207     }
1208     gtk_style_context_set_state(context.get(), static_cast<GtkStateFlags>(state));
1209
1210     // Paint button.
1211     IntRect buttonRect(rect);
1212     guint junction = gtk_style_context_get_junction_sides(context.get());
1213     if (arrowType == GTK_ARROW_UP)
1214         junction |= GTK_JUNCTION_BOTTOM;
1215     else {
1216         junction |= GTK_JUNCTION_TOP;
1217         buttonRect.move(0, rect.height() / 2);
1218     }
1219     buttonRect.setHeight(rect.height() / 2);
1220     gtk_style_context_set_junction_sides(context.get(), static_cast<GtkJunctionSides>(junction));
1221
1222     gtk_render_background(context.get(), paintInfo.context().platformContext()->cr(), buttonRect.x(), buttonRect.y(), buttonRect.width(), buttonRect.height());
1223     gtk_render_frame(context.get(), paintInfo.context().platformContext()->cr(), buttonRect.x(), buttonRect.y(), buttonRect.width(), buttonRect.height());
1224
1225     // Paint arrow centered inside button.
1226     // This code is based on gtkspinbutton.c code.
1227     IntRect arrowRect;
1228     gdouble angle;
1229     if (arrowType == GTK_ARROW_UP) {
1230         angle = 0;
1231         arrowRect.setY(rect.y());
1232         arrowRect.setHeight(rect.height() / 2 - 2);
1233     } else {
1234         angle = G_PI;
1235         arrowRect.setY(rect.y() + buttonRect.y());
1236         arrowRect.setHeight(rect.height() - arrowRect.y() - 2);
1237     }
1238     arrowRect.setWidth(rect.width() - 3);
1239     if (direction == GTK_TEXT_DIR_LTR)
1240         arrowRect.setX(rect.x() + 1);
1241     else
1242         arrowRect.setX(rect.x() + 2);
1243
1244     gint width = arrowRect.width() / 2;
1245     width -= width % 2 - 1; // Force odd.
1246     gint height = (width + 1) / 2;
1247
1248     arrowRect.move((arrowRect.width() - width) / 2, (arrowRect.height() - height) / 2);
1249     gtk_render_arrow(context.get(), paintInfo.context().platformContext()->cr(), angle, arrowRect.x(), arrowRect.y(), width);
1250 }
1251
1252 bool RenderThemeGtk::paintInnerSpinButton(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
1253 {
1254     GRefPtr<GtkStyleContext> context = createStyleContext(SpinButton);
1255     gtk_style_context_set_direction(context.get(), gtkTextDirection(renderObject.style().direction()));
1256
1257     guint flags = 0;
1258     if (!isEnabled(renderObject) || isReadOnlyControl(renderObject))
1259         flags |= GTK_STATE_FLAG_INSENSITIVE;
1260     else if (isFocused(renderObject))
1261         flags |= GTK_STATE_FLAG_FOCUSED;
1262     gtk_style_context_set_state(context.get(), static_cast<GtkStateFlags>(flags));
1263
1264     paintSpinArrowButton(this, context.get(), renderObject, paintInfo, rect, GTK_ARROW_UP);
1265     paintSpinArrowButton(this, context.get(), renderObject, paintInfo, rect, GTK_ARROW_DOWN);
1266
1267     return false;
1268 }
1269
1270 double RenderThemeGtk::caretBlinkInterval() const
1271 {
1272     GtkSettings* settings = gtk_settings_get_default();
1273
1274     gboolean shouldBlink;
1275     gint time;
1276
1277     g_object_get(settings, "gtk-cursor-blink", &shouldBlink, "gtk-cursor-blink-time", &time, nullptr);
1278
1279     if (!shouldBlink)
1280         return 0;
1281
1282     return time / 2000.;
1283 }
1284
1285 enum StyleColorType { StyleColorBackground, StyleColorForeground };
1286
1287 static Color styleColor(RenderThemePart themePart, GtkStateFlags state, StyleColorType colorType)
1288 {
1289     GRefPtr<GtkStyleContext> parentContext;
1290     RenderThemePart part = themePart;
1291     if (state & GTK_STATE_FLAG_SELECTED) {
1292         parentContext = createStyleContext(themePart);
1293         ASSERT(themePart == Entry || themePart == ListBox);
1294         part = themePart == Entry ? EntrySelection : ListBoxSelection;
1295     }
1296
1297     GRefPtr<GtkStyleContext> context = createStyleContext(part, parentContext.get());
1298     gtk_style_context_set_state(context.get(), state);
1299
1300     GdkRGBA gdkRGBAColor;
1301     if (colorType == StyleColorBackground)
1302         gtk_style_context_get_background_color(context.get(), state, &gdkRGBAColor);
1303     else
1304         gtk_style_context_get_color(context.get(), state, &gdkRGBAColor);
1305     return gdkRGBAColor;
1306 }
1307
1308 Color RenderThemeGtk::platformActiveSelectionBackgroundColor() const
1309 {
1310     return styleColor(Entry, static_cast<GtkStateFlags>(GTK_STATE_FLAG_SELECTED | GTK_STATE_FLAG_FOCUSED), StyleColorBackground);
1311 }
1312
1313 Color RenderThemeGtk::platformInactiveSelectionBackgroundColor() const
1314 {
1315     return styleColor(Entry, GTK_STATE_FLAG_SELECTED, StyleColorBackground);
1316 }
1317
1318 Color RenderThemeGtk::platformActiveSelectionForegroundColor() const
1319 {
1320     return styleColor(Entry, static_cast<GtkStateFlags>(GTK_STATE_FLAG_SELECTED | GTK_STATE_FLAG_FOCUSED), StyleColorForeground);
1321 }
1322
1323 Color RenderThemeGtk::platformInactiveSelectionForegroundColor() const
1324 {
1325     return styleColor(Entry, GTK_STATE_FLAG_SELECTED, StyleColorForeground);
1326 }
1327
1328 Color RenderThemeGtk::platformActiveListBoxSelectionBackgroundColor() const
1329 {
1330     return styleColor(ListBox, static_cast<GtkStateFlags>(GTK_STATE_FLAG_SELECTED | GTK_STATE_FLAG_FOCUSED), StyleColorBackground);
1331 }
1332
1333 Color RenderThemeGtk::platformInactiveListBoxSelectionBackgroundColor() const
1334 {
1335     return styleColor(ListBox, GTK_STATE_FLAG_SELECTED, StyleColorBackground);
1336 }
1337
1338 Color RenderThemeGtk::platformActiveListBoxSelectionForegroundColor() const
1339 {
1340     return styleColor(ListBox, static_cast<GtkStateFlags>(GTK_STATE_FLAG_SELECTED | GTK_STATE_FLAG_FOCUSED), StyleColorForeground);
1341 }
1342
1343 Color RenderThemeGtk::platformInactiveListBoxSelectionForegroundColor() const
1344 {
1345     return styleColor(ListBox, GTK_STATE_FLAG_SELECTED, StyleColorForeground);
1346 }
1347
1348 Color RenderThemeGtk::systemColor(CSSValueID cssValueId) const
1349 {
1350     switch (cssValueId) {
1351     case CSSValueButtontext:
1352         return styleColor(Button, GTK_STATE_FLAG_ACTIVE, StyleColorForeground);
1353     case CSSValueCaptiontext:
1354         return styleColor(Entry, GTK_STATE_FLAG_ACTIVE, StyleColorForeground);
1355     default:
1356         return RenderTheme::systemColor(cssValueId);
1357     }
1358 }
1359
1360 void RenderThemeGtk::platformColorsDidChange()
1361 {
1362     RenderTheme::platformColorsDidChange();
1363 }
1364
1365 #if ENABLE(VIDEO)
1366 String RenderThemeGtk::extraMediaControlsStyleSheet()
1367 {
1368     return String(mediaControlsGtkUserAgentStyleSheet, sizeof(mediaControlsGtkUserAgentStyleSheet));
1369 }
1370
1371 #if ENABLE(FULLSCREEN_API)
1372 String RenderThemeGtk::extraFullScreenStyleSheet()
1373 {
1374     return String();
1375 }
1376 #endif
1377
1378 bool RenderThemeGtk::paintMediaButton(const RenderObject& renderObject, GraphicsContext& graphicsContext, const IntRect& rect, const char* iconName)
1379 {
1380     GRefPtr<GtkStyleContext> context = createStyleContext(MediaButton);
1381     gtk_style_context_set_direction(context.get(), gtkTextDirection(renderObject.style().direction()));
1382     gtk_style_context_set_state(context.get(), gtkIconStateFlags(this, renderObject));
1383     static const unsigned mediaIconSize = 16;
1384     IntRect iconRect(rect.x() + (rect.width() - mediaIconSize) / 2, rect.y() + (rect.height() - mediaIconSize) / 2, mediaIconSize, mediaIconSize);
1385     return !paintIcon(context.get(), graphicsContext, iconRect, iconName);
1386 }
1387
1388 bool RenderThemeGtk::hasOwnDisabledStateHandlingFor(ControlPart part) const
1389 {
1390     return (part != MediaMuteButtonPart);
1391 }
1392
1393 bool RenderThemeGtk::paintMediaFullscreenButton(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
1394 {
1395     return paintMediaButton(renderObject, paintInfo.context(), rect, "view-fullscreen-symbolic");
1396 }
1397
1398 bool RenderThemeGtk::paintMediaMuteButton(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
1399 {
1400     Node* node = renderObject.node();
1401     if (!node)
1402         return true;
1403     Node* mediaNode = node->shadowHost();
1404     if (!is<HTMLMediaElement>(mediaNode))
1405         return true;
1406
1407     HTMLMediaElement* mediaElement = downcast<HTMLMediaElement>(mediaNode);
1408     return paintMediaButton(renderObject, paintInfo.context(), rect, mediaElement->muted() ? "audio-volume-muted-symbolic" : "audio-volume-high-symbolic");
1409 }
1410
1411 bool RenderThemeGtk::paintMediaPlayButton(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
1412 {
1413     Node* node = renderObject.node();
1414     if (!node)
1415         return true;
1416     if (!nodeHasPseudo(node, "-webkit-media-controls-play-button"))
1417         return true;
1418
1419     return paintMediaButton(renderObject, paintInfo.context(), rect, nodeHasClass(node, "paused") ? "media-playback-start-symbolic" : "media-playback-pause-symbolic");
1420 }
1421
1422 bool RenderThemeGtk::paintMediaSeekBackButton(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
1423 {
1424     return paintMediaButton(renderObject, paintInfo.context(), rect, "media-seek-backward-symbolic");
1425 }
1426
1427 bool RenderThemeGtk::paintMediaSeekForwardButton(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
1428 {
1429     return paintMediaButton(renderObject, paintInfo.context(), rect, "media-seek-forward-symbolic");
1430 }
1431
1432 #if ENABLE(VIDEO_TRACK)
1433 bool RenderThemeGtk::paintMediaToggleClosedCaptionsButton(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
1434 {
1435     return paintMediaButton(renderObject, paintInfo.context(), rect, "media-view-subtitles-symbolic");
1436 }
1437 #endif
1438
1439 static FloatRoundedRect::Radii borderRadiiFromStyle(RenderStyle& style)
1440 {
1441     return FloatRoundedRect::Radii(
1442         IntSize(style.borderTopLeftRadius().width().intValue(), style.borderTopLeftRadius().height().intValue()),
1443         IntSize(style.borderTopRightRadius().width().intValue(), style.borderTopRightRadius().height().intValue()),
1444         IntSize(style.borderBottomLeftRadius().width().intValue(), style.borderBottomLeftRadius().height().intValue()),
1445         IntSize(style.borderBottomRightRadius().width().intValue(), style.borderBottomRightRadius().height().intValue()));
1446 }
1447
1448 bool RenderThemeGtk::paintMediaSliderTrack(const RenderObject& o, const PaintInfo& paintInfo, const IntRect& r)
1449 {
1450     HTMLMediaElement* mediaElement = parentMediaElement(o);
1451     if (!mediaElement)
1452         return true;
1453
1454     GraphicsContext& context = paintInfo.context();
1455     context.save();
1456     context.setStrokeStyle(NoStroke);
1457
1458     float mediaDuration = mediaElement->duration();
1459     float totalTrackWidth = r.width();
1460     RenderStyle& style = o.style();
1461     RefPtr<TimeRanges> timeRanges = mediaElement->buffered();
1462     for (unsigned index = 0; index < timeRanges->length(); ++index) {
1463         float start = timeRanges->start(index, IGNORE_EXCEPTION);
1464         float end = timeRanges->end(index, IGNORE_EXCEPTION);
1465         float startRatio = start / mediaDuration;
1466         float lengthRatio = (end - start) / mediaDuration;
1467         if (!lengthRatio)
1468             continue;
1469
1470         IntRect rangeRect(r);
1471         rangeRect.setWidth(lengthRatio * totalTrackWidth);
1472         if (index)
1473             rangeRect.move(startRatio * totalTrackWidth, 0);
1474         context.fillRoundedRect(FloatRoundedRect(rangeRect, borderRadiiFromStyle(style)), style.visitedDependentColor(CSSPropertyColor));
1475     }
1476
1477     context.restore();
1478     return false;
1479 }
1480
1481 bool RenderThemeGtk::paintMediaSliderThumb(const RenderObject& o, const PaintInfo& paintInfo, const IntRect& r)
1482 {
1483     RenderStyle& style = o.style();
1484     paintInfo.context().fillRoundedRect(FloatRoundedRect(r, borderRadiiFromStyle(style)), style.visitedDependentColor(CSSPropertyColor));
1485     return false;
1486 }
1487
1488 bool RenderThemeGtk::paintMediaVolumeSliderTrack(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
1489 {
1490     HTMLMediaElement* mediaElement = parentMediaElement(renderObject);
1491     if (!mediaElement)
1492         return true;
1493
1494     float volume = mediaElement->muted() ? 0.0f : mediaElement->volume();
1495     if (!volume)
1496         return true;
1497
1498     GraphicsContext& context = paintInfo.context();
1499     context.save();
1500     context.setStrokeStyle(NoStroke);
1501
1502     int rectHeight = rect.height();
1503     float trackHeight = rectHeight * volume;
1504     RenderStyle& style = renderObject.style();
1505     IntRect volumeRect(rect);
1506     volumeRect.move(0, rectHeight - trackHeight);
1507     volumeRect.setHeight(ceil(trackHeight));
1508
1509     context.fillRoundedRect(FloatRoundedRect(volumeRect, borderRadiiFromStyle(style)), style.visitedDependentColor(CSSPropertyColor));
1510     context.restore();
1511
1512     return false;
1513 }
1514
1515 bool RenderThemeGtk::paintMediaVolumeSliderThumb(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
1516 {
1517     return paintMediaSliderThumb(renderObject, paintInfo, rect);
1518 }
1519
1520 String RenderThemeGtk::formatMediaControlsCurrentTime(float currentTime, float duration) const
1521 {
1522     return formatMediaControlsTime(currentTime) + " / " + formatMediaControlsTime(duration);
1523 }
1524
1525 bool RenderThemeGtk::paintMediaCurrentTime(const RenderObject&, const PaintInfo&, const IntRect&)
1526 {
1527     return false;
1528 }
1529 #endif
1530
1531 void RenderThemeGtk::adjustProgressBarStyle(StyleResolver&, RenderStyle& style, Element*) const
1532 {
1533     style.setBoxShadow(nullptr);
1534 }
1535
1536 // These values have been copied from RenderThemeChromiumSkia.cpp
1537 static const int progressActivityBlocks = 5;
1538 static const int progressAnimationFrames = 10;
1539 static const double progressAnimationInterval = 0.125;
1540 double RenderThemeGtk::animationRepeatIntervalForProgressBar(RenderProgress&) const
1541 {
1542     return progressAnimationInterval;
1543 }
1544
1545 double RenderThemeGtk::animationDurationForProgressBar(RenderProgress&) const
1546 {
1547     return progressAnimationInterval * progressAnimationFrames * 2; // "2" for back and forth;
1548 }
1549
1550 IntRect RenderThemeGtk::calculateProgressRect(const RenderObject& renderObject, const IntRect& fullBarRect)
1551 {
1552     IntRect progressRect(fullBarRect);
1553     const auto& renderProgress = downcast<RenderProgress>(renderObject);
1554     if (renderProgress.isDeterminate()) {
1555         int progressWidth = progressRect.width() * renderProgress.position();
1556         if (renderObject.style().direction() == RTL)
1557             progressRect.setX(progressRect.x() + progressRect.width() - progressWidth);
1558         progressRect.setWidth(progressWidth);
1559         return progressRect;
1560     }
1561
1562     double animationProgress = renderProgress.animationProgress();
1563
1564     // Never let the progress rect shrink smaller than 2 pixels.
1565     int newWidth = std::max(2, progressRect.width() / progressActivityBlocks);
1566     int movableWidth = progressRect.width() - newWidth;
1567     progressRect.setWidth(newWidth);
1568
1569     // We want the first 0.5 units of the animation progress to represent the
1570     // forward motion and the second 0.5 units to represent the backward motion,
1571     // thus we multiply by two here to get the full sweep of the progress bar with
1572     // each direction.
1573     if (animationProgress < 0.5)
1574         progressRect.setX(progressRect.x() + (animationProgress * 2 * movableWidth));
1575     else
1576         progressRect.setX(progressRect.x() + ((1.0 - animationProgress) * 2 * movableWidth));
1577     return progressRect;
1578 }
1579
1580 String RenderThemeGtk::fileListNameForWidth(const FileList* fileList, const FontCascade& font, int width, bool multipleFilesAllowed) const
1581 {
1582     if (width <= 0)
1583         return String();
1584
1585     if (fileList->length() > 1)
1586         return StringTruncator::rightTruncate(multipleFileUploadText(fileList->length()), width, font, StringTruncator::EnableRoundingHacks);
1587
1588     String string;
1589     if (fileList->length())
1590         string = pathGetFileName(fileList->item(0)->path());
1591     else if (multipleFilesAllowed)
1592         string = fileButtonNoFilesSelectedLabel();
1593     else
1594         string = fileButtonNoFileSelectedLabel();
1595
1596     return StringTruncator::centerTruncate(string, width, font, StringTruncator::EnableRoundingHacks);
1597 }
1598
1599 #if ENABLE(VIDEO)
1600 String RenderThemeGtk::mediaControlsScript()
1601 {
1602     StringBuilder scriptBuilder;
1603     scriptBuilder.append(mediaControlsLocalizedStringsJavaScript, sizeof(mediaControlsLocalizedStringsJavaScript));
1604     scriptBuilder.append(mediaControlsBaseJavaScript, sizeof(mediaControlsBaseJavaScript));
1605     scriptBuilder.append(mediaControlsGtkJavaScript, sizeof(mediaControlsGtkJavaScript));
1606     return scriptBuilder.toString();
1607 }
1608 #endif // ENABLE(VIDEO)
1609
1610 #endif // GTK_API_VERSION_2
1611 }