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