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