[iOS] Input type=time elements styled with SVG fonts have 0 width
[WebKit-https.git] / Source / WebCore / platform / gtk / 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 "PaintInfo.h"
41 #include "PlatformContextCairo.h"
42 #include "RenderBox.h"
43 #include "RenderObject.h"
44 #include "StringTruncator.h"
45 #include "TimeRanges.h"
46 #include "UserAgentScripts.h"
47 #include "UserAgentStyleSheets.h"
48 #include <cmath>
49 #include <gdk/gdk.h>
50 #include <glib.h>
51 #include <gtk/gtk.h>
52 #include <wtf/gobject/GUniquePtr.h>
53 #include <wtf/text/CString.h>
54 #include <wtf/text/StringBuilder.h>
55
56 #if ENABLE(PROGRESS_ELEMENT)
57 #include "RenderProgress.h"
58 #endif
59
60 namespace WebCore {
61
62 // This would be a static method, except that forward declaring GType is tricky, since its
63 // definition depends on including glib.h, negating the benefit of using a forward declaration.
64 extern GRefPtr<GdkPixbuf> getStockIconForWidgetType(GType, const char* iconName, gint direction, gint state, gint iconSize);
65 extern GRefPtr<GdkPixbuf> getStockSymbolicIconForWidgetType(GType widgetType, const char* symbolicIconName, const char* fallbackStockIconName, gint direction, gint state, gint iconSize);
66
67 #if ENABLE(VIDEO)
68 static HTMLMediaElement* getMediaElementFromRenderObject(const RenderObject& o)
69 {
70     Node* node = o.node();
71     Node* mediaNode = node ? node->shadowHost() : 0;
72     if (!mediaNode)
73         mediaNode = node;
74     if (!mediaNode || !mediaNode->isElementNode() || !toElement(mediaNode)->isMediaElement())
75         return 0;
76
77     return toHTMLMediaElement(mediaNode);
78 }
79
80 void RenderThemeGtk::initMediaButtons()
81 {
82     static bool iconsInitialized = false;
83
84     if (iconsInitialized)
85         return;
86
87     GRefPtr<GtkIconFactory> iconFactory = adoptGRef(gtk_icon_factory_new());
88     GtkIconSource* iconSource = gtk_icon_source_new();
89     const char* icons[] = { "audio-volume-high", "audio-volume-muted" };
90
91     gtk_icon_factory_add_default(iconFactory.get());
92
93     for (size_t i = 0; i < G_N_ELEMENTS(icons); ++i) {
94         gtk_icon_source_set_icon_name(iconSource, icons[i]);
95         GtkIconSet* iconSet = gtk_icon_set_new();
96         gtk_icon_set_add_source(iconSet, iconSource);
97         gtk_icon_factory_add(iconFactory.get(), icons[i], iconSet);
98         gtk_icon_set_unref(iconSet);
99     }
100
101     gtk_icon_source_free(iconSource);
102
103     iconsInitialized = true;
104 }
105 #endif
106
107 static bool nodeHasPseudo(Node* node, const char* pseudo)
108 {
109     RefPtr<Node> attributeNode = node->attributes()->getNamedItem("pseudo");
110
111     return attributeNode ? attributeNode->nodeValue() == pseudo : false;
112 }
113
114 static bool nodeHasClass(Node* node, const char* className)
115 {
116     if (!node->isElementNode())
117         return false;
118
119     Element* element = toElement(node);
120
121     if (!element->hasClass())
122         return false;
123
124     return element->classNames().contains(className);
125 }
126
127 PassRefPtr<RenderTheme> RenderThemeGtk::create()
128 {
129     return adoptRef(new RenderThemeGtk());
130 }
131
132 PassRefPtr<RenderTheme> RenderTheme::themeForPage(Page*)
133 {
134     static RenderTheme* rt = RenderThemeGtk::create().leakRef();
135     return rt;
136 }
137
138 RenderThemeGtk::RenderThemeGtk()
139     : m_panelColor(Color::white)
140     , m_sliderColor(Color::white)
141     , m_sliderThumbColor(Color::white)
142     , m_mediaIconSize(16)
143     , m_mediaSliderHeight(14)
144 {
145     platformInit();
146 #if ENABLE(VIDEO)
147     initMediaColors();
148     initMediaButtons();
149 #endif
150 }
151
152 static bool supportsFocus(ControlPart appearance)
153 {
154     switch (appearance) {
155     case PushButtonPart:
156     case ButtonPart:
157     case TextFieldPart:
158     case TextAreaPart:
159     case SearchFieldPart:
160     case MenulistPart:
161     case RadioPart:
162     case CheckboxPart:
163     case SliderHorizontalPart:
164     case SliderVerticalPart:
165         return true;
166     default:
167         return false;
168     }
169 }
170
171 bool RenderThemeGtk::supportsFocusRing(const RenderStyle* style) const
172 {
173     return supportsFocus(style->appearance());
174 }
175
176 bool RenderThemeGtk::controlSupportsTints(const RenderObject& o) const
177 {
178     return isEnabled(o);
179 }
180
181 int RenderThemeGtk::baselinePosition(const RenderObject& o) const
182 {
183     if (!o.isBox())
184         return 0;
185
186     // FIXME: This strategy is possibly incorrect for the GTK+ port.
187     if (o.style().appearance() == CheckboxPart
188         || o.style().appearance() == RadioPart) {
189         const RenderBox* box = toRenderBox(&o);
190         return box->marginTop() + box->height() - 2;
191     }
192
193     return RenderTheme::baselinePosition(o);
194 }
195
196 // This is used in RenderThemeGtk2 and RenderThemeGtk3. Normally, it would be in
197 // the RenderThemeGtk header (perhaps as a static method), but we want to avoid
198 // having to include GTK+ headers only for the GtkTextDirection enum.
199 GtkTextDirection gtkTextDirection(TextDirection direction)
200 {
201     switch (direction) {
202     case RTL:
203         return GTK_TEXT_DIR_RTL;
204     case LTR:
205         return GTK_TEXT_DIR_LTR;
206     default:
207         return GTK_TEXT_DIR_NONE;
208     }
209 }
210
211 static GtkStateType gtkIconState(RenderTheme* theme, const RenderObject& renderObject)
212 {
213     if (!theme->isEnabled(renderObject))
214         return GTK_STATE_INSENSITIVE;
215     if (theme->isPressed(renderObject))
216         return GTK_STATE_ACTIVE;
217     if (theme->isHovered(renderObject))
218         return GTK_STATE_PRELIGHT;
219
220     return GTK_STATE_NORMAL;
221 }
222
223 void RenderThemeGtk::adjustButtonStyle(StyleResolver*, RenderStyle* style, WebCore::Element*) const
224 {
225     // Some layout tests check explicitly that buttons ignore line-height.
226     if (style->appearance() == PushButtonPart)
227         style->setLineHeight(RenderStyle::initialLineHeight());
228 }
229
230 void RenderThemeGtk::adjustMenuListStyle(StyleResolver*, RenderStyle* style, Element*) const
231 {
232     // The tests check explicitly that select menu buttons ignore line height.
233     style->setLineHeight(RenderStyle::initialLineHeight());
234
235     // We cannot give a proper rendering when border radius is active, unfortunately.
236     style->resetBorderRadius();
237 }
238
239 void RenderThemeGtk::adjustMenuListButtonStyle(StyleResolver& styleResolver, RenderStyle& style, Element& e) const
240 {
241     adjustMenuListStyle(&styleResolver, &style, &e);
242 }
243
244 bool RenderThemeGtk::paintMenuListButtonDecorations(const RenderObject& object, const PaintInfo& info, const FloatRect& rect)
245 {
246     return paintMenuList(object, info, rect);
247 }
248
249 bool RenderThemeGtk::paintTextArea(const RenderObject& o, const PaintInfo& i, const FloatRect& r)
250 {
251     return paintTextField(o, i, r);
252 }
253
254 static void paintGdkPixbuf(GraphicsContext* context, const GdkPixbuf* icon, const IntRect& iconRect)
255 {
256     IntSize iconSize(gdk_pixbuf_get_width(icon), gdk_pixbuf_get_height(icon));
257     GRefPtr<GdkPixbuf> scaledIcon;
258     if (iconRect.size() != iconSize) {
259         // We could use cairo_scale() here but cairo/pixman downscale quality is quite bad.
260         scaledIcon = adoptGRef(gdk_pixbuf_scale_simple(icon, iconRect.width(), iconRect.height(),
261                                                        GDK_INTERP_BILINEAR));
262         icon = scaledIcon.get();
263     }
264
265     cairo_t* cr = context->platformContext()->cr();
266     cairo_save(cr);
267     gdk_cairo_set_source_pixbuf(cr, icon, iconRect.x(), iconRect.y());
268     cairo_paint(cr);
269     cairo_restore(cr);
270 }
271
272 // Defined in GTK+ (gtk/gtkiconfactory.c)
273 static const gint gtkIconSizeMenu = 16;
274 static const gint gtkIconSizeSmallToolbar = 18;
275 static const gint gtkIconSizeButton = 20;
276 static const gint gtkIconSizeLargeToolbar = 24;
277 static const gint gtkIconSizeDnd = 32;
278 static const gint gtkIconSizeDialog = 48;
279
280 static GtkIconSize getIconSizeForPixelSize(gint pixelSize)
281 {
282     if (pixelSize < gtkIconSizeSmallToolbar)
283         return GTK_ICON_SIZE_MENU;
284     if (pixelSize >= gtkIconSizeSmallToolbar && pixelSize < gtkIconSizeButton)
285         return GTK_ICON_SIZE_SMALL_TOOLBAR;
286     if (pixelSize >= gtkIconSizeButton && pixelSize < gtkIconSizeLargeToolbar)
287         return GTK_ICON_SIZE_BUTTON;
288     if (pixelSize >= gtkIconSizeLargeToolbar && pixelSize < gtkIconSizeDnd)
289         return GTK_ICON_SIZE_LARGE_TOOLBAR;
290     if (pixelSize >= gtkIconSizeDnd && pixelSize < gtkIconSizeDialog)
291         return GTK_ICON_SIZE_DND;
292
293     return GTK_ICON_SIZE_DIALOG;
294 }
295
296 void RenderThemeGtk::adjustSearchFieldResultsButtonStyle(StyleResolver* styleResolver, RenderStyle* style, Element* e) const
297 {
298     adjustSearchFieldCancelButtonStyle(styleResolver, style, e);
299 }
300
301 bool RenderThemeGtk::paintSearchFieldResultsButton(const RenderObject& o, const PaintInfo& i, const IntRect& rect)
302 {
303     return paintSearchFieldResultsDecorationPart(o, i, rect);
304 }
305
306 static void adjustSearchFieldIconStyle(RenderStyle* style)
307 {
308     style->resetBorder();
309     style->resetPadding();
310
311     // Get the icon size based on the font size.
312     int fontSize = style->fontSize();
313     if (fontSize < gtkIconSizeMenu) {
314         style->setWidth(Length(fontSize, Fixed));
315         style->setHeight(Length(fontSize, Fixed));
316         return;
317     }
318     gint width = 0, height = 0;
319     gtk_icon_size_lookup(getIconSizeForPixelSize(fontSize), &width, &height);
320     style->setWidth(Length(width, Fixed));
321     style->setHeight(Length(height, Fixed));
322 }
323
324 void RenderThemeGtk::adjustSearchFieldResultsDecorationPartStyle(StyleResolver*, RenderStyle* style, Element*) const
325 {
326     adjustSearchFieldIconStyle(style);
327 }
328
329 static IntRect centerRectVerticallyInParentInputElement(const RenderObject& renderObject, const IntRect& rect)
330 {
331     // Get the renderer of <input> element.
332     Node* input = renderObject.node()->shadowHost();
333     if (!input)
334         input = renderObject.node();
335     if (!input->renderer()->isBox())
336         return IntRect();
337
338     // If possible center the y-coordinate of the rect vertically in the parent input element.
339     // We also add one pixel here to ensure that the y coordinate is rounded up for box heights
340     // that are even, which looks in relation to the box text.
341     IntRect inputContentBox = toRenderBox(input->renderer())->absoluteContentBox();
342
343     // Make sure the scaled decoration stays square and will fit in its parent's box.
344     int iconSize = std::min(inputContentBox.width(), std::min(inputContentBox.height(), rect.height()));
345     IntRect scaledRect(rect.x(), inputContentBox.y() + (inputContentBox.height() - iconSize + 1) / 2, iconSize, iconSize);
346     return scaledRect;
347 }
348
349 bool RenderThemeGtk::paintSearchFieldResultsDecorationPart(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
350 {
351     IntRect iconRect = centerRectVerticallyInParentInputElement(renderObject, rect);
352     if (iconRect.isEmpty())
353         return false;
354
355     GRefPtr<GdkPixbuf> icon = getStockIconForWidgetType(GTK_TYPE_ENTRY, GTK_STOCK_FIND,
356         gtkTextDirection(renderObject.style().direction()),
357         gtkIconState(this, renderObject),
358         getIconSizeForPixelSize(rect.height()));
359     paintGdkPixbuf(paintInfo.context, icon.get(), iconRect);
360     return false;
361 }
362
363 void RenderThemeGtk::adjustSearchFieldCancelButtonStyle(StyleResolver*, RenderStyle* style, Element*) const
364 {
365     adjustSearchFieldIconStyle(style);
366 }
367
368 bool RenderThemeGtk::paintSearchFieldCancelButton(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
369 {
370     IntRect iconRect = centerRectVerticallyInParentInputElement(renderObject, rect);
371     if (iconRect.isEmpty())
372         return false;
373
374     GRefPtr<GdkPixbuf> icon = getStockIconForWidgetType(GTK_TYPE_ENTRY, GTK_STOCK_CLEAR,
375         gtkTextDirection(renderObject.style().direction()),
376         gtkIconState(this, renderObject),
377         getIconSizeForPixelSize(rect.height()));
378     paintGdkPixbuf(paintInfo.context, icon.get(), iconRect);
379     return false;
380 }
381
382 void RenderThemeGtk::adjustSearchFieldStyle(StyleResolver*, RenderStyle* style, Element*) const
383 {
384     // We cannot give a proper rendering when border radius is active, unfortunately.
385     style->resetBorderRadius();
386     style->setLineHeight(RenderStyle::initialLineHeight());
387 }
388
389 bool RenderThemeGtk::paintSearchField(const RenderObject& o, const PaintInfo& i, const IntRect& rect)
390 {
391     return paintTextField(o, i, rect);
392 }
393
394 bool RenderThemeGtk::paintCapsLockIndicator(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
395 {
396     // The other paint methods don't need to check whether painting is disabled because RenderTheme already checks it
397     // before calling them, but paintCapsLockIndicator() is called by RenderTextControlSingleLine which doesn't check it.
398     if (paintInfo.context->paintingDisabled())
399         return true;
400
401     int iconSize = std::min(rect.width(), rect.height());
402     GRefPtr<GdkPixbuf> icon = getStockIconForWidgetType(GTK_TYPE_ENTRY, GTK_STOCK_CAPS_LOCK_WARNING, gtkTextDirection(renderObject.style().direction()), 0, getIconSizeForPixelSize(iconSize));
403
404     // Only re-scale the icon when it's smaller than the minimum icon size.
405     if (iconSize >= gtkIconSizeMenu)
406         iconSize = gdk_pixbuf_get_height(icon.get());
407
408     // GTK+ locates the icon right aligned in the entry. The given rectangle is already
409     // centered vertically by RenderTextControlSingleLine.
410     IntRect iconRect(rect.x() + rect.width() - iconSize,
411                      rect.y() + (rect.height() - iconSize) / 2,
412                      iconSize, iconSize);
413     paintGdkPixbuf(paintInfo.context, icon.get(), iconRect);
414     return true;
415 }
416
417 void RenderThemeGtk::adjustSliderTrackStyle(StyleResolver*, RenderStyle* style, Element*) const
418 {
419     style->setBoxShadow(nullptr);
420 }
421
422 void RenderThemeGtk::adjustSliderThumbStyle(StyleResolver* styleResolver, RenderStyle* style, Element* element) const
423 {
424     RenderTheme::adjustSliderThumbStyle(styleResolver, style, element);
425     style->setBoxShadow(nullptr);
426 }
427
428 double RenderThemeGtk::caretBlinkInterval() const
429 {
430     GtkSettings* settings = gtk_settings_get_default();
431
432     gboolean shouldBlink;
433     gint time;
434
435     g_object_get(settings, "gtk-cursor-blink", &shouldBlink, "gtk-cursor-blink-time", &time, NULL);
436
437     if (!shouldBlink)
438         return 0;
439
440     return time / 2000.;
441 }
442
443 double RenderThemeGtk::getScreenDPI()
444 {
445     // FIXME: Really this should be the widget's screen.
446     GdkScreen* screen = gdk_screen_get_default();
447     if (!screen)
448         return 96; // Default to 96 DPI.
449
450     float dpi = gdk_screen_get_resolution(screen);
451     if (dpi <= 0)
452         return 96;
453     return dpi;
454 }
455
456 void RenderThemeGtk::systemFont(CSSValueID, FontDescription& fontDescription) const
457 {
458     GtkSettings* settings = gtk_settings_get_default();
459     if (!settings)
460         return;
461
462     // This will be a font selection string like "Sans 10" so we cannot use it as the family name.
463     GUniqueOutPtr<gchar> fontName;
464     g_object_get(settings, "gtk-font-name", &fontName.outPtr(), NULL);
465
466     PangoFontDescription* pangoDescription = pango_font_description_from_string(fontName.get());
467     if (!pangoDescription)
468         return;
469
470     fontDescription.setOneFamily(pango_font_description_get_family(pangoDescription));
471
472     int size = pango_font_description_get_size(pangoDescription) / PANGO_SCALE;
473     // If the size of the font is in points, we need to convert it to pixels.
474     if (!pango_font_description_get_size_is_absolute(pangoDescription))
475         size = size * (getScreenDPI() / 72.0);
476
477     fontDescription.setSpecifiedSize(size);
478     fontDescription.setIsAbsoluteSize(true);
479     fontDescription.setGenericFamily(FontDescription::NoFamily);
480     fontDescription.setWeight(FontWeightNormal);
481     fontDescription.setItalic(false);
482     pango_font_description_free(pangoDescription);
483 }
484
485 void RenderThemeGtk::platformColorsDidChange()
486 {
487 #if ENABLE(VIDEO)
488     initMediaColors();
489 #endif
490     RenderTheme::platformColorsDidChange();
491 }
492
493 #if ENABLE(VIDEO)
494 String RenderThemeGtk::extraMediaControlsStyleSheet()
495 {
496     return String(mediaControlsGtkUserAgentStyleSheet, sizeof(mediaControlsGtkUserAgentStyleSheet));
497 }
498
499 #if ENABLE(FULLSCREEN_API)
500 String RenderThemeGtk::extraFullScreenStyleSheet()
501 {
502     return String();
503 }
504 #endif
505
506 bool RenderThemeGtk::paintMediaButton(const RenderObject& renderObject, GraphicsContext* context, const IntRect& rect, const char* symbolicIconName, const char* fallbackStockIconName)
507 {
508     IntRect iconRect(rect.x() + (rect.width() - m_mediaIconSize) / 2,
509                      rect.y() + (rect.height() - m_mediaIconSize) / 2,
510                      m_mediaIconSize, m_mediaIconSize);
511     GRefPtr<GdkPixbuf> icon = getStockSymbolicIconForWidgetType(GTK_TYPE_CONTAINER, symbolicIconName, fallbackStockIconName,
512         gtkTextDirection(renderObject.style().direction()), gtkIconState(this, renderObject), iconRect.width());
513     paintGdkPixbuf(context, icon.get(), iconRect);
514     return true;
515 }
516
517 bool RenderThemeGtk::hasOwnDisabledStateHandlingFor(ControlPart part) const
518 {
519     return (part != MediaMuteButtonPart);
520 }
521
522 bool RenderThemeGtk::paintMediaFullscreenButton(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
523 {
524     return paintMediaButton(renderObject, paintInfo.context, rect, "view-fullscreen-symbolic", GTK_STOCK_FULLSCREEN);
525 }
526
527 bool RenderThemeGtk::paintMediaMuteButton(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
528 {
529     HTMLMediaElement* mediaElement = getMediaElementFromRenderObject(renderObject);
530     if (!mediaElement)
531         return false;
532
533     bool muted = mediaElement->muted();
534     return paintMediaButton(renderObject, paintInfo.context, rect,
535         muted ? "audio-volume-muted-symbolic" : "audio-volume-high-symbolic",
536         muted ? "audio-volume-muted" : "audio-volume-high");
537 }
538
539 bool RenderThemeGtk::paintMediaPlayButton(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
540 {
541     Node* node = renderObject.node();
542     if (!node)
543         return false;
544
545     if (!nodeHasPseudo(node, "-webkit-media-controls-play-button"))
546         return false;
547     bool showPlayButton = nodeHasClass(node, "paused");
548
549     return paintMediaButton(renderObject, paintInfo.context, rect,
550         showPlayButton ? "media-playback-start-symbolic" : "media-playback-pause-symbolic",
551         showPlayButton ? GTK_STOCK_MEDIA_PLAY : GTK_STOCK_MEDIA_PAUSE);
552 }
553
554 bool RenderThemeGtk::paintMediaSeekBackButton(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
555 {
556     return paintMediaButton(renderObject, paintInfo.context, rect, "media-seek-backward-symbolic", GTK_STOCK_MEDIA_REWIND);
557 }
558
559 bool RenderThemeGtk::paintMediaSeekForwardButton(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
560 {
561     return paintMediaButton(renderObject, paintInfo.context, rect, "media-seek-forward-symbolic", GTK_STOCK_MEDIA_FORWARD);
562 }
563
564 #if ENABLE(VIDEO_TRACK)
565 bool RenderThemeGtk::paintMediaToggleClosedCaptionsButton(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
566 {
567     IntRect iconRect(rect.x() + (rect.width() - m_mediaIconSize) / 2, rect.y() + (rect.height() - m_mediaIconSize) / 2,
568         m_mediaIconSize, m_mediaIconSize);
569     GRefPtr<GdkPixbuf> icon = getStockSymbolicIconForWidgetType(GTK_TYPE_CONTAINER, "media-view-subtitles-symbolic", nullptr,
570         gtkTextDirection(renderObject.style().direction()), gtkIconState(this, renderObject), iconRect.width());
571     if (!icon)
572         icon = getStockSymbolicIconForWidgetType(GTK_TYPE_CONTAINER, "user-invisible-symbolic", GTK_STOCK_JUSTIFY_FILL,
573             gtkTextDirection(renderObject.style().direction()), gtkIconState(this, renderObject), iconRect.width());
574     paintGdkPixbuf(paintInfo.context, icon.get(), iconRect);
575     return true;
576 }
577 #endif
578
579 static FloatRoundedRect::Radii borderRadiiFromStyle(RenderStyle* style)
580 {
581     return FloatRoundedRect::Radii(
582         IntSize(style->borderTopLeftRadius().width().intValue(), style->borderTopLeftRadius().height().intValue()),
583         IntSize(style->borderTopRightRadius().width().intValue(), style->borderTopRightRadius().height().intValue()),
584         IntSize(style->borderBottomLeftRadius().width().intValue(), style->borderBottomLeftRadius().height().intValue()),
585         IntSize(style->borderBottomRightRadius().width().intValue(), style->borderBottomRightRadius().height().intValue()));
586 }
587
588 bool RenderThemeGtk::paintMediaSliderTrack(const RenderObject& o, const PaintInfo& paintInfo, const IntRect& r)
589 {
590     HTMLMediaElement* mediaElement = parentMediaElement(o);
591     if (!mediaElement)
592         return false;
593
594     GraphicsContext* context = paintInfo.context;
595     context->save();
596     context->setStrokeStyle(NoStroke);
597
598     float mediaDuration = mediaElement->duration();
599     float totalTrackWidth = r.width();
600     RenderStyle* style = &o.style();
601     RefPtr<TimeRanges> timeRanges = mediaElement->buffered();
602     for (unsigned index = 0; index < timeRanges->length(); ++index) {
603         float start = timeRanges->start(index, IGNORE_EXCEPTION);
604         float end = timeRanges->end(index, IGNORE_EXCEPTION);
605         float startRatio = start / mediaDuration;
606         float lengthRatio = (end - start) / mediaDuration;
607         if (!lengthRatio)
608             continue;
609
610         IntRect rangeRect(r);
611         rangeRect.setWidth(lengthRatio * totalTrackWidth);
612         if (index)
613             rangeRect.move(startRatio * totalTrackWidth, 0);
614         context->fillRoundedRect(FloatRoundedRect(rangeRect, borderRadiiFromStyle(style)), style->visitedDependentColor(CSSPropertyColor), style->colorSpace());
615     }
616
617     context->restore();
618     return false;
619 }
620
621 bool RenderThemeGtk::paintMediaSliderThumb(const RenderObject& o, const PaintInfo& paintInfo, const IntRect& r)
622 {
623     RenderStyle* style = &o.style();
624     paintInfo.context->fillRoundedRect(FloatRoundedRect(r, borderRadiiFromStyle(style)), style->visitedDependentColor(CSSPropertyColor), style->colorSpace());
625     return false;
626 }
627
628 bool RenderThemeGtk::paintMediaVolumeSliderContainer(const RenderObject&, const PaintInfo&, const IntRect&)
629 {
630     return true;
631 }
632
633 bool RenderThemeGtk::paintMediaVolumeSliderTrack(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
634 {
635     HTMLMediaElement* mediaElement = parentMediaElement(renderObject);
636     if (!mediaElement)
637         return true;
638
639     float volume = mediaElement->muted() ? 0.0f : mediaElement->volume();
640     if (!volume)
641         return true;
642
643     GraphicsContext* context = paintInfo.context;
644     context->save();
645     context->setStrokeStyle(NoStroke);
646
647     int rectHeight = rect.height();
648     float trackHeight = rectHeight * volume;
649     RenderStyle* style = &renderObject.style();
650     IntRect volumeRect(rect);
651     volumeRect.move(0, rectHeight - trackHeight);
652     volumeRect.setHeight(ceil(trackHeight));
653
654     context->fillRoundedRect(FloatRoundedRect(volumeRect, borderRadiiFromStyle(style)),
655         style->visitedDependentColor(CSSPropertyColor), style->colorSpace());
656     context->restore();
657
658     return false;
659 }
660
661 bool RenderThemeGtk::paintMediaVolumeSliderThumb(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
662 {
663     return paintMediaSliderThumb(renderObject, paintInfo, rect);
664 }
665
666 String RenderThemeGtk::formatMediaControlsCurrentTime(float currentTime, float duration) const
667 {
668     return formatMediaControlsTime(currentTime) + " / " + formatMediaControlsTime(duration);
669 }
670
671 bool RenderThemeGtk::paintMediaCurrentTime(const RenderObject&, const PaintInfo&, const IntRect&)
672 {
673     return false;
674 }
675 #endif
676
677 #if ENABLE(PROGRESS_ELEMENT)
678 void RenderThemeGtk::adjustProgressBarStyle(StyleResolver*, RenderStyle* style, Element*) const
679 {
680     style->setBoxShadow(nullptr);
681 }
682
683 // These values have been copied from RenderThemeChromiumSkia.cpp
684 static const int progressActivityBlocks = 5;
685 static const int progressAnimationFrames = 10;
686 static const double progressAnimationInterval = 0.125;
687 double RenderThemeGtk::animationRepeatIntervalForProgressBar(RenderProgress*) const
688 {
689     return progressAnimationInterval;
690 }
691
692 double RenderThemeGtk::animationDurationForProgressBar(RenderProgress*) const
693 {
694     return progressAnimationInterval * progressAnimationFrames * 2; // "2" for back and forth;
695 }
696
697 IntRect RenderThemeGtk::calculateProgressRect(const RenderObject& renderObject, const IntRect& fullBarRect)
698 {
699     IntRect progressRect(fullBarRect);
700     const RenderProgress* renderProgress = toRenderProgress(&renderObject);
701     if (renderProgress->isDeterminate()) {
702         int progressWidth = progressRect.width() * renderProgress->position();
703         if (renderObject.style().direction() == RTL)
704             progressRect.setX(progressRect.x() + progressRect.width() - progressWidth);
705         progressRect.setWidth(progressWidth);
706         return progressRect;
707     }
708
709     double animationProgress = renderProgress->animationProgress();
710
711     // Never let the progress rect shrink smaller than 2 pixels.
712     int newWidth = std::max(2, progressRect.width() / progressActivityBlocks);
713     int movableWidth = progressRect.width() - newWidth;
714     progressRect.setWidth(newWidth);
715
716     // We want the first 0.5 units of the animation progress to represent the
717     // forward motion and the second 0.5 units to represent the backward motion,
718     // thus we multiply by two here to get the full sweep of the progress bar with
719     // each direction.
720     if (animationProgress < 0.5)
721         progressRect.setX(progressRect.x() + (animationProgress * 2 * movableWidth));
722     else
723         progressRect.setX(progressRect.x() + ((1.0 - animationProgress) * 2 * movableWidth));
724     return progressRect;
725 }
726 #endif
727
728 String RenderThemeGtk::fileListNameForWidth(const FileList* fileList, const Font& font, int width, bool multipleFilesAllowed) const
729 {
730     if (width <= 0)
731         return String();
732
733     if (fileList->length() > 1)
734         return StringTruncator::rightTruncate(multipleFileUploadText(fileList->length()), width, font, StringTruncator::EnableRoundingHacks);
735
736     String string;
737     if (fileList->length())
738         string = pathGetFileName(fileList->item(0)->path());
739     else if (multipleFilesAllowed)
740         string = fileButtonNoFilesSelectedLabel();
741     else
742         string = fileButtonNoFileSelectedLabel();
743
744     return StringTruncator::centerTruncate(string, width, font, StringTruncator::EnableRoundingHacks);
745 }
746
747 #if ENABLE(DATALIST_ELEMENT)
748 IntSize RenderThemeGtk::sliderTickSize() const
749 {
750     // FIXME: We need to set this to the size of one tick mark.
751     return IntSize(0, 0);
752 }
753
754 int RenderThemeGtk::sliderTickOffsetFromTrackCenter() const
755 {
756     // FIXME: We need to set this to the position of the tick marks.
757     return 0;
758 }
759 #endif
760
761 String RenderThemeGtk::mediaControlsScript()
762 {
763     StringBuilder scriptBuilder;
764     scriptBuilder.append(mediaControlsLocalizedStringsJavaScript, sizeof(mediaControlsLocalizedStringsJavaScript));
765     scriptBuilder.append(mediaControlsAppleJavaScript, sizeof(mediaControlsAppleJavaScript));
766     scriptBuilder.append(mediaControlsGtkJavaScript, sizeof(mediaControlsGtkJavaScript));
767     return scriptBuilder.toString();
768 }
769 }