Unreviewed. Fix scrollbars/custom-scrollbar-appearance-property.html after r191991.
[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     if (!renderObject.node())
984         return IntRect();
985
986     // Get the renderer of <input> element.
987     Node* input = renderObject.node()->shadowHost();
988     if (!input)
989         input = renderObject.node();
990     if (!is<RenderBox>(*input->renderer()))
991         return IntRect();
992
993     // If possible center the y-coordinate of the rect vertically in the parent input element.
994     // We also add one pixel here to ensure that the y coordinate is rounded up for box heights
995     // that are even, which looks in relation to the box text.
996     IntRect inputContentBox = downcast<RenderBox>(*input->renderer()).absoluteContentBox();
997
998     // Make sure the scaled decoration stays square and will fit in its parent's box.
999     int iconSize = std::min(inputContentBox.width(), std::min(inputContentBox.height(), rect.height()));
1000     IntRect scaledRect(rect.x(), inputContentBox.y() + (inputContentBox.height() - iconSize + 1) / 2, iconSize, iconSize);
1001     return scaledRect;
1002 }
1003
1004 bool RenderThemeGtk::paintSearchFieldResultsDecorationPart(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
1005 {
1006     IntRect iconRect = centerRectVerticallyInParentInputElement(renderObject, rect);
1007     if (iconRect.isEmpty())
1008         return false;
1009
1010     GRefPtr<GdkPixbuf> icon = getStockIconForWidgetType(GTK_TYPE_ENTRY, GTK_STOCK_FIND,
1011         gtkTextDirection(renderObject.style().direction()),
1012         gtkIconState(this, renderObject),
1013         getIconSizeForPixelSize(rect.height()));
1014     paintGdkPixbuf(paintInfo.context(), icon.get(), iconRect);
1015     return false;
1016 }
1017
1018 void RenderThemeGtk::adjustSearchFieldCancelButtonStyle(StyleResolver&, RenderStyle& style, Element*) const
1019 {
1020     adjustSearchFieldIconStyle(style);
1021 }
1022
1023 bool RenderThemeGtk::paintSearchFieldCancelButton(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
1024 {
1025     IntRect iconRect = centerRectVerticallyInParentInputElement(renderObject, rect);
1026     if (iconRect.isEmpty())
1027         return false;
1028
1029     GRefPtr<GdkPixbuf> icon = getStockIconForWidgetType(GTK_TYPE_ENTRY, GTK_STOCK_CLEAR,
1030         gtkTextDirection(renderObject.style().direction()),
1031         gtkIconState(this, renderObject),
1032         getIconSizeForPixelSize(rect.height()));
1033     paintGdkPixbuf(paintInfo.context(), icon.get(), iconRect);
1034     return false;
1035 }
1036
1037 void RenderThemeGtk::adjustSearchFieldStyle(StyleResolver&, RenderStyle& style, Element*) const
1038 {
1039     // We cannot give a proper rendering when border radius is active, unfortunately.
1040     style.resetBorderRadius();
1041     style.setLineHeight(RenderStyle::initialLineHeight());
1042 }
1043
1044 bool RenderThemeGtk::paintSearchField(const RenderObject& o, const PaintInfo& i, const IntRect& rect)
1045 {
1046     return paintTextField(o, i, rect);
1047 }
1048
1049 bool RenderThemeGtk::paintCapsLockIndicator(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
1050 {
1051     // The other paint methods don't need to check whether painting is disabled because RenderTheme already checks it
1052     // before calling them, but paintCapsLockIndicator() is called by RenderTextControlSingleLine which doesn't check it.
1053     if (paintInfo.context().paintingDisabled())
1054         return true;
1055
1056     int iconSize = std::min(rect.width(), rect.height());
1057     GRefPtr<GdkPixbuf> icon = getStockIconForWidgetType(GTK_TYPE_ENTRY, GTK_STOCK_CAPS_LOCK_WARNING, gtkTextDirection(renderObject.style().direction()), 0, getIconSizeForPixelSize(iconSize));
1058
1059     // Only re-scale the icon when it's smaller than the minimum icon size.
1060     if (iconSize >= gtkIconSizeMenu)
1061         iconSize = gdk_pixbuf_get_height(icon.get());
1062
1063     // GTK+ locates the icon right aligned in the entry. The given rectangle is already
1064     // centered vertically by RenderTextControlSingleLine.
1065     IntRect iconRect(
1066         rect.x() + rect.width() - iconSize,
1067         rect.y() + (rect.height() - iconSize) / 2,
1068         iconSize, iconSize);
1069     paintGdkPixbuf(paintInfo.context(), icon.get(), iconRect);
1070     return true;
1071 }
1072
1073 void RenderThemeGtk::adjustSliderTrackStyle(StyleResolver&, RenderStyle& style, Element*) const
1074 {
1075     style.setBoxShadow(nullptr);
1076 }
1077
1078 void RenderThemeGtk::adjustSliderThumbStyle(StyleResolver& styleResolver, RenderStyle& style, Element* element) const
1079 {
1080     RenderTheme::adjustSliderThumbStyle(styleResolver, style, element);
1081     style.setBoxShadow(nullptr);
1082 }
1083
1084 static void applySliderStyleContextClasses(GtkStyleContext* context, ControlPart part)
1085 {
1086     gtk_style_context_add_class(context, GTK_STYLE_CLASS_SCALE);
1087     if (part == SliderHorizontalPart || part == SliderThumbHorizontalPart)
1088         gtk_style_context_add_class(context, GTK_STYLE_CLASS_HORIZONTAL);
1089     else if (part == SliderVerticalPart || part == SliderThumbVerticalPart)
1090         gtk_style_context_add_class(context, GTK_STYLE_CLASS_VERTICAL);
1091 }
1092
1093 bool RenderThemeGtk::paintSliderTrack(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
1094 {
1095     ControlPart part = renderObject.style().appearance();
1096     ASSERT_UNUSED(part, part == SliderHorizontalPart || part == SliderVerticalPart || part == MediaVolumeSliderPart);
1097
1098     GtkStyleContext* context = getStyleContext(GTK_TYPE_SCALE);
1099     gtk_style_context_save(context);
1100
1101     gtk_style_context_set_direction(context, gtkTextDirection(renderObject.style().direction()));
1102     applySliderStyleContextClasses(context, part);
1103     gtk_style_context_add_class(context, GTK_STYLE_CLASS_TROUGH);
1104
1105     if (!isEnabled(renderObject))
1106         gtk_style_context_set_state(context, GTK_STATE_FLAG_INSENSITIVE);
1107
1108     gtk_render_background(context, paintInfo.context().platformContext()->cr(), rect.x(), rect.y(), rect.width(), rect.height());
1109     gtk_render_frame(context, paintInfo.context().platformContext()->cr(), rect.x(), rect.y(), rect.width(), rect.height());
1110
1111     if (isFocused(renderObject)) {
1112         gint focusWidth, focusPad;
1113         gtk_style_context_get_style(context, "focus-line-width", &focusWidth, "focus-padding", &focusPad, nullptr);
1114         IntRect focusRect(rect);
1115         focusRect.inflate(focusWidth + focusPad);
1116         gtk_render_focus(context, paintInfo.context().platformContext()->cr(), focusRect.x(), focusRect.y(), focusRect.width(), focusRect.height());
1117     }
1118
1119     gtk_style_context_restore(context);
1120     return false;
1121 }
1122
1123 bool RenderThemeGtk::paintSliderThumb(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
1124 {
1125     ControlPart part = renderObject.style().appearance();
1126     ASSERT(part == SliderThumbHorizontalPart || part == SliderThumbVerticalPart || part == MediaVolumeSliderThumbPart);
1127
1128     GtkStyleContext* context = getStyleContext(GTK_TYPE_SCALE);
1129     gtk_style_context_save(context);
1130
1131     gtk_style_context_set_direction(context, gtkTextDirection(renderObject.style().direction()));
1132     applySliderStyleContextClasses(context, part);
1133     gtk_style_context_add_class(context, GTK_STYLE_CLASS_SLIDER);
1134
1135     guint flags = 0;
1136     if (!isEnabled(renderObject))
1137         flags |= GTK_STATE_FLAG_INSENSITIVE;
1138     else if (isHovered(renderObject))
1139         flags |= GTK_STATE_FLAG_PRELIGHT;
1140     if (isPressed(renderObject))
1141         flags |= GTK_STATE_FLAG_ACTIVE;
1142     gtk_style_context_set_state(context, static_cast<GtkStateFlags>(flags));
1143
1144     gtk_render_slider(context, paintInfo.context().platformContext()->cr(), rect.x(), rect.y(), rect.width(), rect.height(),
1145         part == SliderThumbHorizontalPart ? GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL);
1146
1147     gtk_style_context_restore(context);
1148
1149     return false;
1150 }
1151
1152 void RenderThemeGtk::adjustSliderThumbSize(RenderStyle& style, Element*) const
1153 {
1154     ControlPart part = style.appearance();
1155     if (part != SliderThumbHorizontalPart && part != SliderThumbVerticalPart)
1156         return;
1157
1158     gint sliderWidth, sliderLength;
1159     gtk_style_context_get_style(getStyleContext(GTK_TYPE_SCALE), "slider-width", &sliderWidth, "slider-length", &sliderLength, nullptr);
1160     if (part == SliderThumbHorizontalPart) {
1161         style.setWidth(Length(sliderLength, Fixed));
1162         style.setHeight(Length(sliderWidth, Fixed));
1163         return;
1164     }
1165     ASSERT(part == SliderThumbVerticalPart || part == MediaVolumeSliderThumbPart);
1166     style.setWidth(Length(sliderWidth, Fixed));
1167     style.setHeight(Length(sliderLength, Fixed));
1168 }
1169
1170 bool RenderThemeGtk::paintProgressBar(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
1171 {
1172     if (!renderObject.isProgress())
1173         return true;
1174
1175     GtkStyleContext* context = getStyleContext(GTK_TYPE_PROGRESS_BAR);
1176     gtk_style_context_save(context);
1177
1178     gtk_style_context_add_class(context, GTK_STYLE_CLASS_TROUGH);
1179
1180     gtk_render_background(context, paintInfo.context().platformContext()->cr(), rect.x(), rect.y(), rect.width(), rect.height());
1181     gtk_render_frame(context, paintInfo.context().platformContext()->cr(), rect.x(), rect.y(), rect.width(), rect.height());
1182
1183     gtk_style_context_restore(context);
1184
1185     gtk_style_context_save(context);
1186     gtk_style_context_add_class(context, GTK_STYLE_CLASS_PROGRESSBAR);
1187
1188
1189     GtkBorder padding;
1190     gtk_style_context_get_padding(context, static_cast<GtkStateFlags>(0), &padding);
1191     IntRect progressRect(
1192         rect.x() + padding.left,
1193         rect.y() + padding.top,
1194         rect.width() - (padding.left + padding.right),
1195         rect.height() - (padding.top + padding.bottom));
1196     progressRect = RenderThemeGtk::calculateProgressRect(renderObject, progressRect);
1197
1198     if (!progressRect.isEmpty()) {
1199 #if GTK_CHECK_VERSION(3, 13, 7)
1200         gtk_render_background(context, paintInfo.context().platformContext()->cr(), progressRect.x(), progressRect.y(), progressRect.width(), progressRect.height());
1201         gtk_render_frame(context, paintInfo.context().platformContext()->cr(), progressRect.x(), progressRect.y(), progressRect.width(), progressRect.height());
1202 #else
1203         gtk_render_activity(context, paintInfo.context().platformContext()->cr(), progressRect.x(), progressRect.y(), progressRect.width(), progressRect.height());
1204 #endif
1205     }
1206
1207     gtk_style_context_restore(context);
1208     return false;
1209 }
1210
1211 static gint spinButtonArrowSize(GtkStyleContext* context)
1212 {
1213     PangoFontDescription* fontDescription;
1214     gtk_style_context_get(context, static_cast<GtkStateFlags>(0), "font", &fontDescription, nullptr);
1215     gint fontSize = pango_font_description_get_size(fontDescription);
1216     gint arrowSize = std::max(PANGO_PIXELS(fontSize), minSpinButtonArrowSize);
1217     pango_font_description_free(fontDescription);
1218
1219     return arrowSize - arrowSize % 2; // Force even.
1220 }
1221
1222 void RenderThemeGtk::adjustInnerSpinButtonStyle(StyleResolver&, RenderStyle& style, Element*) const
1223 {
1224     GtkStyleContext* context = getStyleContext(GTK_TYPE_SPIN_BUTTON);
1225
1226     GtkBorder padding;
1227     gtk_style_context_get_padding(context, static_cast<GtkStateFlags>(0), &padding);
1228
1229     int width = spinButtonArrowSize(context) + padding.left + padding.right;
1230     style.setWidth(Length(width, Fixed));
1231     style.setMinWidth(Length(width, Fixed));
1232 }
1233
1234 static void paintSpinArrowButton(RenderTheme* theme, GtkStyleContext* context, const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect, GtkArrowType arrowType)
1235 {
1236     ASSERT(arrowType == GTK_ARROW_UP || arrowType == GTK_ARROW_DOWN);
1237
1238     gtk_style_context_save(context);
1239     gtk_style_context_add_class(context, GTK_STYLE_CLASS_BUTTON);
1240
1241     GtkTextDirection direction = gtk_style_context_get_direction(context);
1242     guint state = static_cast<guint>(gtk_style_context_get_state(context));
1243     if (!(state & GTK_STATE_FLAG_INSENSITIVE)) {
1244         if (theme->isPressed(renderObject)) {
1245             if ((arrowType == GTK_ARROW_UP && theme->isSpinUpButtonPartPressed(renderObject))
1246                 || (arrowType == GTK_ARROW_DOWN && !theme->isSpinUpButtonPartPressed(renderObject)))
1247                 state |= GTK_STATE_FLAG_ACTIVE;
1248         } else if (theme->isHovered(renderObject)) {
1249             if ((arrowType == GTK_ARROW_UP && theme->isSpinUpButtonPartHovered(renderObject))
1250                 || (arrowType == GTK_ARROW_DOWN && !theme->isSpinUpButtonPartHovered(renderObject)))
1251                 state |= GTK_STATE_FLAG_PRELIGHT;
1252         }
1253     }
1254     gtk_style_context_set_state(context, static_cast<GtkStateFlags>(state));
1255
1256     // Paint button.
1257     IntRect buttonRect(rect);
1258     guint junction = gtk_style_context_get_junction_sides(context);
1259     if (arrowType == GTK_ARROW_UP)
1260         junction |= GTK_JUNCTION_BOTTOM;
1261     else {
1262         junction |= GTK_JUNCTION_TOP;
1263         buttonRect.move(0, rect.height() / 2);
1264     }
1265     buttonRect.setHeight(rect.height() / 2);
1266     gtk_style_context_set_junction_sides(context, static_cast<GtkJunctionSides>(junction));
1267
1268     gtk_render_background(context, paintInfo.context().platformContext()->cr(), buttonRect.x(), buttonRect.y(), buttonRect.width(), buttonRect.height());
1269     gtk_render_frame(context, paintInfo.context().platformContext()->cr(), buttonRect.x(), buttonRect.y(), buttonRect.width(), buttonRect.height());
1270
1271     // Paint arrow centered inside button.
1272     // This code is based on gtkspinbutton.c code.
1273     IntRect arrowRect;
1274     gdouble angle;
1275     if (arrowType == GTK_ARROW_UP) {
1276         angle = 0;
1277         arrowRect.setY(rect.y());
1278         arrowRect.setHeight(rect.height() / 2 - 2);
1279     } else {
1280         angle = G_PI;
1281         arrowRect.setY(rect.y() + buttonRect.y());
1282         arrowRect.setHeight(rect.height() - arrowRect.y() - 2);
1283     }
1284     arrowRect.setWidth(rect.width() - 3);
1285     if (direction == GTK_TEXT_DIR_LTR)
1286         arrowRect.setX(rect.x() + 1);
1287     else
1288         arrowRect.setX(rect.x() + 2);
1289
1290     gint width = arrowRect.width() / 2;
1291     width -= width % 2 - 1; // Force odd.
1292     gint height = (width + 1) / 2;
1293
1294     arrowRect.move((arrowRect.width() - width) / 2, (arrowRect.height() - height) / 2);
1295     gtk_render_arrow(context, paintInfo.context().platformContext()->cr(), angle, arrowRect.x(), arrowRect.y(), width);
1296
1297     gtk_style_context_restore(context);
1298 }
1299
1300 bool RenderThemeGtk::paintInnerSpinButton(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
1301 {
1302     GtkStyleContext* context = getStyleContext(GTK_TYPE_SPIN_BUTTON);
1303     gtk_style_context_save(context);
1304
1305     GtkTextDirection direction = static_cast<GtkTextDirection>(gtkTextDirection(renderObject.style().direction()));
1306     gtk_style_context_set_direction(context, direction);
1307
1308     guint flags = 0;
1309     if (!isEnabled(renderObject) || isReadOnlyControl(renderObject))
1310         flags |= GTK_STATE_FLAG_INSENSITIVE;
1311     else if (isFocused(renderObject))
1312         flags |= GTK_STATE_FLAG_FOCUSED;
1313     gtk_style_context_set_state(context, static_cast<GtkStateFlags>(flags));
1314     gtk_style_context_remove_class(context, GTK_STYLE_CLASS_ENTRY);
1315
1316     paintSpinArrowButton(this, context, renderObject, paintInfo, rect, GTK_ARROW_UP);
1317     paintSpinArrowButton(this, context, renderObject, paintInfo, rect, GTK_ARROW_DOWN);
1318
1319     gtk_style_context_restore(context);
1320
1321     return false;
1322 }
1323
1324 double RenderThemeGtk::caretBlinkInterval() const
1325 {
1326     GtkSettings* settings = gtk_settings_get_default();
1327
1328     gboolean shouldBlink;
1329     gint time;
1330
1331     g_object_get(settings, "gtk-cursor-blink", &shouldBlink, "gtk-cursor-blink-time", &time, nullptr);
1332
1333     if (!shouldBlink)
1334         return 0;
1335
1336     return time / 2000.;
1337 }
1338
1339 enum StyleColorType { StyleColorBackground, StyleColorForeground };
1340
1341 static Color styleColor(GType widgetType, GtkStateFlags state, StyleColorType colorType)
1342 {
1343
1344     GtkStyleContext* context = getStyleContext(widgetType);
1345     // Recent GTK+ versions (> 3.14) require to explicitly set the state before getting the color.
1346     gtk_style_context_set_state(context, state);
1347
1348     GdkRGBA gdkRGBAColor;
1349     if (colorType == StyleColorBackground)
1350         gtk_style_context_get_background_color(context, state, &gdkRGBAColor);
1351     else
1352         gtk_style_context_get_color(context, state, &gdkRGBAColor);
1353     return gdkRGBAColor;
1354 }
1355
1356 Color RenderThemeGtk::platformActiveSelectionBackgroundColor() const
1357 {
1358     return styleColor(GTK_TYPE_ENTRY, static_cast<GtkStateFlags>(GTK_STATE_FLAG_SELECTED | GTK_STATE_FLAG_FOCUSED), StyleColorBackground);
1359 }
1360
1361 Color RenderThemeGtk::platformInactiveSelectionBackgroundColor() const
1362 {
1363     return styleColor(GTK_TYPE_ENTRY, GTK_STATE_FLAG_SELECTED, StyleColorBackground);
1364 }
1365
1366 Color RenderThemeGtk::platformActiveSelectionForegroundColor() const
1367 {
1368     return styleColor(GTK_TYPE_ENTRY, static_cast<GtkStateFlags>(GTK_STATE_FLAG_SELECTED | GTK_STATE_FLAG_FOCUSED), StyleColorForeground);
1369 }
1370
1371 Color RenderThemeGtk::platformInactiveSelectionForegroundColor() const
1372 {
1373     return styleColor(GTK_TYPE_ENTRY, GTK_STATE_FLAG_SELECTED, StyleColorForeground);
1374 }
1375
1376 Color RenderThemeGtk::platformActiveListBoxSelectionBackgroundColor() const
1377 {
1378     return styleColor(GTK_TYPE_TREE_VIEW, static_cast<GtkStateFlags>(GTK_STATE_FLAG_SELECTED | GTK_STATE_FLAG_FOCUSED), StyleColorBackground);
1379 }
1380
1381 Color RenderThemeGtk::platformInactiveListBoxSelectionBackgroundColor() const
1382 {
1383     return styleColor(GTK_TYPE_TREE_VIEW, GTK_STATE_FLAG_SELECTED, StyleColorBackground);
1384 }
1385
1386 Color RenderThemeGtk::platformActiveListBoxSelectionForegroundColor() const
1387 {
1388     return styleColor(GTK_TYPE_TREE_VIEW, static_cast<GtkStateFlags>(GTK_STATE_FLAG_SELECTED | GTK_STATE_FLAG_FOCUSED), StyleColorForeground);
1389 }
1390
1391 Color RenderThemeGtk::platformInactiveListBoxSelectionForegroundColor() const
1392 {
1393     return styleColor(GTK_TYPE_TREE_VIEW, GTK_STATE_FLAG_SELECTED, StyleColorForeground);
1394 }
1395
1396 Color RenderThemeGtk::systemColor(CSSValueID cssValueId) const
1397 {
1398     switch (cssValueId) {
1399     case CSSValueButtontext:
1400         return styleColor(GTK_TYPE_BUTTON, GTK_STATE_FLAG_ACTIVE, StyleColorForeground);
1401     case CSSValueCaptiontext:
1402         return styleColor(GTK_TYPE_ENTRY, GTK_STATE_FLAG_ACTIVE, StyleColorForeground);
1403     default:
1404         return RenderTheme::systemColor(cssValueId);
1405     }
1406 }
1407
1408 void RenderThemeGtk::platformColorsDidChange()
1409 {
1410 #if ENABLE(VIDEO)
1411     initMediaColors();
1412 #endif
1413     RenderTheme::platformColorsDidChange();
1414 }
1415
1416 #if ENABLE(VIDEO)
1417 String RenderThemeGtk::extraMediaControlsStyleSheet()
1418 {
1419     return String(mediaControlsGtkUserAgentStyleSheet, sizeof(mediaControlsGtkUserAgentStyleSheet));
1420 }
1421
1422 #if ENABLE(FULLSCREEN_API)
1423 String RenderThemeGtk::extraFullScreenStyleSheet()
1424 {
1425     return String();
1426 }
1427 #endif
1428
1429 bool RenderThemeGtk::paintMediaButton(const RenderObject& renderObject, GraphicsContext& context, const IntRect& rect, const char* symbolicIconName, const char* fallbackStockIconName)
1430 {
1431     IntRect iconRect(
1432         rect.x() + (rect.width() - m_mediaIconSize) / 2,
1433         rect.y() + (rect.height() - m_mediaIconSize) / 2,
1434         m_mediaIconSize, m_mediaIconSize);
1435     GRefPtr<GdkPixbuf> icon = getStockSymbolicIconForWidgetType(GTK_TYPE_CONTAINER, symbolicIconName, fallbackStockIconName,
1436         gtkTextDirection(renderObject.style().direction()), gtkIconState(this, renderObject), iconRect.width());
1437     paintGdkPixbuf(context, icon.get(), iconRect);
1438     return true;
1439 }
1440
1441 bool RenderThemeGtk::hasOwnDisabledStateHandlingFor(ControlPart part) const
1442 {
1443     return (part != MediaMuteButtonPart);
1444 }
1445
1446 bool RenderThemeGtk::paintMediaFullscreenButton(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
1447 {
1448     return paintMediaButton(renderObject, paintInfo.context(), rect, "view-fullscreen-symbolic", GTK_STOCK_FULLSCREEN);
1449 }
1450
1451 bool RenderThemeGtk::paintMediaMuteButton(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
1452 {
1453     HTMLMediaElement* mediaElement = getMediaElementFromRenderObject(renderObject);
1454     if (!mediaElement)
1455         return false;
1456
1457     bool muted = mediaElement->muted();
1458     return paintMediaButton(renderObject, paintInfo.context(), rect,
1459         muted ? "audio-volume-muted-symbolic" : "audio-volume-high-symbolic",
1460         muted ? "audio-volume-muted" : "audio-volume-high");
1461 }
1462
1463 bool RenderThemeGtk::paintMediaPlayButton(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
1464 {
1465     Node* node = renderObject.node();
1466     if (!node)
1467         return false;
1468
1469     if (!nodeHasPseudo(node, "-webkit-media-controls-play-button"))
1470         return false;
1471     bool showPlayButton = nodeHasClass(node, "paused");
1472
1473     return paintMediaButton(renderObject, paintInfo.context(), rect,
1474         showPlayButton ? "media-playback-start-symbolic" : "media-playback-pause-symbolic",
1475         showPlayButton ? GTK_STOCK_MEDIA_PLAY : GTK_STOCK_MEDIA_PAUSE);
1476 }
1477
1478 bool RenderThemeGtk::paintMediaSeekBackButton(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
1479 {
1480     return paintMediaButton(renderObject, paintInfo.context(), rect, "media-seek-backward-symbolic", GTK_STOCK_MEDIA_REWIND);
1481 }
1482
1483 bool RenderThemeGtk::paintMediaSeekForwardButton(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
1484 {
1485     return paintMediaButton(renderObject, paintInfo.context(), rect, "media-seek-forward-symbolic", GTK_STOCK_MEDIA_FORWARD);
1486 }
1487
1488 #if ENABLE(VIDEO_TRACK)
1489 bool RenderThemeGtk::paintMediaToggleClosedCaptionsButton(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
1490 {
1491     IntRect iconRect(rect.x() + (rect.width() - m_mediaIconSize) / 2, rect.y() + (rect.height() - m_mediaIconSize) / 2,
1492         m_mediaIconSize, m_mediaIconSize);
1493     GRefPtr<GdkPixbuf> icon = getStockSymbolicIconForWidgetType(GTK_TYPE_CONTAINER, "media-view-subtitles-symbolic", nullptr,
1494         gtkTextDirection(renderObject.style().direction()), gtkIconState(this, renderObject), iconRect.width());
1495     if (!icon) {
1496         icon = getStockSymbolicIconForWidgetType(GTK_TYPE_CONTAINER, "user-invisible-symbolic", GTK_STOCK_JUSTIFY_FILL,
1497             gtkTextDirection(renderObject.style().direction()), gtkIconState(this, renderObject), iconRect.width());
1498     }
1499     paintGdkPixbuf(paintInfo.context(), icon.get(), iconRect);
1500     return true;
1501 }
1502 #endif
1503
1504 static FloatRoundedRect::Radii borderRadiiFromStyle(RenderStyle& style)
1505 {
1506     return FloatRoundedRect::Radii(
1507         IntSize(style.borderTopLeftRadius().width().intValue(), style.borderTopLeftRadius().height().intValue()),
1508         IntSize(style.borderTopRightRadius().width().intValue(), style.borderTopRightRadius().height().intValue()),
1509         IntSize(style.borderBottomLeftRadius().width().intValue(), style.borderBottomLeftRadius().height().intValue()),
1510         IntSize(style.borderBottomRightRadius().width().intValue(), style.borderBottomRightRadius().height().intValue()));
1511 }
1512
1513 bool RenderThemeGtk::paintMediaSliderTrack(const RenderObject& o, const PaintInfo& paintInfo, const IntRect& r)
1514 {
1515     HTMLMediaElement* mediaElement = parentMediaElement(o);
1516     if (!mediaElement)
1517         return false;
1518
1519     GraphicsContext& context = paintInfo.context();
1520     context.save();
1521     context.setStrokeStyle(NoStroke);
1522
1523     float mediaDuration = mediaElement->duration();
1524     float totalTrackWidth = r.width();
1525     RenderStyle& style = o.style();
1526     RefPtr<TimeRanges> timeRanges = mediaElement->buffered();
1527     for (unsigned index = 0; index < timeRanges->length(); ++index) {
1528         float start = timeRanges->start(index, IGNORE_EXCEPTION);
1529         float end = timeRanges->end(index, IGNORE_EXCEPTION);
1530         float startRatio = start / mediaDuration;
1531         float lengthRatio = (end - start) / mediaDuration;
1532         if (!lengthRatio)
1533             continue;
1534
1535         IntRect rangeRect(r);
1536         rangeRect.setWidth(lengthRatio * totalTrackWidth);
1537         if (index)
1538             rangeRect.move(startRatio * totalTrackWidth, 0);
1539         context.fillRoundedRect(FloatRoundedRect(rangeRect, borderRadiiFromStyle(style)), style.visitedDependentColor(CSSPropertyColor));
1540     }
1541
1542     context.restore();
1543     return false;
1544 }
1545
1546 bool RenderThemeGtk::paintMediaSliderThumb(const RenderObject& o, const PaintInfo& paintInfo, const IntRect& r)
1547 {
1548     RenderStyle& style = o.style();
1549     paintInfo.context().fillRoundedRect(FloatRoundedRect(r, borderRadiiFromStyle(style)), style.visitedDependentColor(CSSPropertyColor));
1550     return false;
1551 }
1552
1553 bool RenderThemeGtk::paintMediaVolumeSliderContainer(const RenderObject&, const PaintInfo&, const IntRect&)
1554 {
1555     return true;
1556 }
1557
1558 bool RenderThemeGtk::paintMediaVolumeSliderTrack(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
1559 {
1560     HTMLMediaElement* mediaElement = parentMediaElement(renderObject);
1561     if (!mediaElement)
1562         return true;
1563
1564     float volume = mediaElement->muted() ? 0.0f : mediaElement->volume();
1565     if (!volume)
1566         return true;
1567
1568     GraphicsContext& context = paintInfo.context();
1569     context.save();
1570     context.setStrokeStyle(NoStroke);
1571
1572     int rectHeight = rect.height();
1573     float trackHeight = rectHeight * volume;
1574     RenderStyle& style = renderObject.style();
1575     IntRect volumeRect(rect);
1576     volumeRect.move(0, rectHeight - trackHeight);
1577     volumeRect.setHeight(ceil(trackHeight));
1578
1579     context.fillRoundedRect(FloatRoundedRect(volumeRect, borderRadiiFromStyle(style)), style.visitedDependentColor(CSSPropertyColor));
1580     context.restore();
1581
1582     return false;
1583 }
1584
1585 bool RenderThemeGtk::paintMediaVolumeSliderThumb(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
1586 {
1587     return paintMediaSliderThumb(renderObject, paintInfo, rect);
1588 }
1589
1590 String RenderThemeGtk::formatMediaControlsCurrentTime(float currentTime, float duration) const
1591 {
1592     return formatMediaControlsTime(currentTime) + " / " + formatMediaControlsTime(duration);
1593 }
1594
1595 bool RenderThemeGtk::paintMediaCurrentTime(const RenderObject&, const PaintInfo&, const IntRect&)
1596 {
1597     return false;
1598 }
1599 #endif
1600
1601 void RenderThemeGtk::adjustProgressBarStyle(StyleResolver&, RenderStyle& style, Element*) const
1602 {
1603     style.setBoxShadow(nullptr);
1604 }
1605
1606 // These values have been copied from RenderThemeChromiumSkia.cpp
1607 static const int progressActivityBlocks = 5;
1608 static const int progressAnimationFrames = 10;
1609 static const double progressAnimationInterval = 0.125;
1610 double RenderThemeGtk::animationRepeatIntervalForProgressBar(RenderProgress&) const
1611 {
1612     return progressAnimationInterval;
1613 }
1614
1615 double RenderThemeGtk::animationDurationForProgressBar(RenderProgress&) const
1616 {
1617     return progressAnimationInterval * progressAnimationFrames * 2; // "2" for back and forth;
1618 }
1619
1620 IntRect RenderThemeGtk::calculateProgressRect(const RenderObject& renderObject, const IntRect& fullBarRect)
1621 {
1622     IntRect progressRect(fullBarRect);
1623     const auto& renderProgress = downcast<RenderProgress>(renderObject);
1624     if (renderProgress.isDeterminate()) {
1625         int progressWidth = progressRect.width() * renderProgress.position();
1626         if (renderObject.style().direction() == RTL)
1627             progressRect.setX(progressRect.x() + progressRect.width() - progressWidth);
1628         progressRect.setWidth(progressWidth);
1629         return progressRect;
1630     }
1631
1632     double animationProgress = renderProgress.animationProgress();
1633
1634     // Never let the progress rect shrink smaller than 2 pixels.
1635     int newWidth = std::max(2, progressRect.width() / progressActivityBlocks);
1636     int movableWidth = progressRect.width() - newWidth;
1637     progressRect.setWidth(newWidth);
1638
1639     // We want the first 0.5 units of the animation progress to represent the
1640     // forward motion and the second 0.5 units to represent the backward motion,
1641     // thus we multiply by two here to get the full sweep of the progress bar with
1642     // each direction.
1643     if (animationProgress < 0.5)
1644         progressRect.setX(progressRect.x() + (animationProgress * 2 * movableWidth));
1645     else
1646         progressRect.setX(progressRect.x() + ((1.0 - animationProgress) * 2 * movableWidth));
1647     return progressRect;
1648 }
1649
1650 String RenderThemeGtk::fileListNameForWidth(const FileList* fileList, const FontCascade& font, int width, bool multipleFilesAllowed) const
1651 {
1652     if (width <= 0)
1653         return String();
1654
1655     if (fileList->length() > 1)
1656         return StringTruncator::rightTruncate(multipleFileUploadText(fileList->length()), width, font, StringTruncator::EnableRoundingHacks);
1657
1658     String string;
1659     if (fileList->length())
1660         string = pathGetFileName(fileList->item(0)->path());
1661     else if (multipleFilesAllowed)
1662         string = fileButtonNoFilesSelectedLabel();
1663     else
1664         string = fileButtonNoFileSelectedLabel();
1665
1666     return StringTruncator::centerTruncate(string, width, font, StringTruncator::EnableRoundingHacks);
1667 }
1668
1669 #if ENABLE(VIDEO)
1670 String RenderThemeGtk::mediaControlsScript()
1671 {
1672     StringBuilder scriptBuilder;
1673     scriptBuilder.append(mediaControlsLocalizedStringsJavaScript, sizeof(mediaControlsLocalizedStringsJavaScript));
1674     scriptBuilder.append(mediaControlsBaseJavaScript, sizeof(mediaControlsBaseJavaScript));
1675     scriptBuilder.append(mediaControlsGtkJavaScript, sizeof(mediaControlsGtkJavaScript));
1676     return scriptBuilder.toString();
1677 }
1678 #endif // ENABLE(VIDEO)
1679
1680 #endif // GTK_API_VERSION_2
1681 }