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