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