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