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