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