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 "LocalizedStrings.h"
39#include "MediaControlElements.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 "UserAgentStyleSheets.h"
47#include <cmath>
48#include <gdk/gdk.h>
49#include <glib.h>
50#include <gtk/gtk.h>
51#include <wtf/text/CString.h>
52
53#if ENABLE(PROGRESS_ELEMENT)
54#include "RenderProgress.h"
55#endif
56
57namespace 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.
61extern GRefPtr<GdkPixbuf> getStockIconForWidgetType(GType, const char* iconName, gint direction, gint state, gint iconSize);
62extern GRefPtr<GdkPixbuf> getStockSymbolicIconForWidgetType(GType widgetType, const char* symbolicIconName, const char *fallbackStockIconName, gint direction, gint state, gint iconSize);
63
64#if ENABLE(VIDEO)
65static 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 static_cast<HTMLMediaElement*>(mediaNode);
75}
76
77void 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
104PassRefPtr<RenderTheme> RenderThemeGtk::create()
105{
106    return adoptRef(new RenderThemeGtk());
107}
108
109PassRefPtr<RenderTheme> RenderTheme::themeForPage(Page* page)
110{
111    static RenderTheme* rt = RenderThemeGtk::create().leakRef();
112    return rt;
113}
114
115RenderThemeGtk::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
129static 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
153bool RenderThemeGtk::supportsFocusRing(const RenderStyle* style) const
154{
155    return supportsFocus(style->appearance());
156}
157
158bool RenderThemeGtk::controlSupportsTints(const RenderObject* o) const
159{
160    return isEnabled(o);
161}
162
163int 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.
181GtkTextDirection 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
193static 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
205void 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
212void 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
221void RenderThemeGtk::adjustMenuListButtonStyle(StyleResolver* styleResolver, RenderStyle* style, Element* e) const
222{
223    adjustMenuListStyle(styleResolver, style, e);
224}
225
226bool RenderThemeGtk::paintMenuListButton(RenderObject* object, const PaintInfo& info, const IntRect& rect)
227{
228    return paintMenuList(object, info, rect);
229}
230
231bool RenderThemeGtk::paintTextArea(RenderObject* o, const PaintInfo& i, const IntRect& r)
232{
233    return paintTextField(o, i, r);
234}
235
236static 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)
255static const gint gtkIconSizeMenu = 16;
256static const gint gtkIconSizeSmallToolbar = 18;
257static const gint gtkIconSizeButton = 20;
258static const gint gtkIconSizeLargeToolbar = 24;
259static const gint gtkIconSizeDnd = 32;
260static const gint gtkIconSizeDialog = 48;
261
262static 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
278void RenderThemeGtk::adjustSearchFieldResultsButtonStyle(StyleResolver* styleResolver, RenderStyle* style, Element* e) const
279{
280    adjustSearchFieldCancelButtonStyle(styleResolver, style, e);
281}
282
283bool RenderThemeGtk::paintSearchFieldResultsButton(RenderObject* o, const PaintInfo& i, const IntRect& rect)
284{
285    return paintSearchFieldResultsDecoration(o, i, rect);
286}
287
288static 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
306void RenderThemeGtk::adjustSearchFieldResultsDecorationStyle(StyleResolver*, RenderStyle* style, Element*) const
307{
308    adjustSearchFieldIconStyle(style);
309}
310
311static 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
331bool RenderThemeGtk::paintSearchFieldResultsDecoration(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
345void RenderThemeGtk::adjustSearchFieldCancelButtonStyle(StyleResolver*, RenderStyle* style, Element*) const
346{
347    adjustSearchFieldIconStyle(style);
348}
349
350bool 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
364void 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
371bool RenderThemeGtk::paintSearchField(RenderObject* o, const PaintInfo& i, const IntRect& rect)
372{
373    return paintTextField(o, i, rect);
374}
375
376bool 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
401void RenderThemeGtk::adjustSliderTrackStyle(StyleResolver*, RenderStyle* style, Element*) const
402{
403    style->setBoxShadow(nullptr);
404}
405
406void RenderThemeGtk::adjustSliderThumbStyle(StyleResolver* styleResolver, RenderStyle* style, Element* element) const
407{
408    RenderTheme::adjustSliderThumbStyle(styleResolver, style, element);
409    style->setBoxShadow(nullptr);
410}
411
412double 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
427double 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
440void RenderThemeGtk::systemFont(int, 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    GOwnPtr<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
469void RenderThemeGtk::platformColorsDidChange()
470{
471#if ENABLE(VIDEO)
472    initMediaColors();
473#endif
474    RenderTheme::platformColorsDidChange();
475}
476
477#if ENABLE(VIDEO)
478String RenderThemeGtk::extraMediaControlsStyleSheet()
479{
480    return String(mediaControlsGtkUserAgentStyleSheet, sizeof(mediaControlsGtkUserAgentStyleSheet));
481}
482
483#if ENABLE(FULLSCREEN_API)
484String RenderThemeGtk::extraFullScreenStyleSheet()
485{
486    return String();
487}
488#endif
489
490bool 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
501bool RenderThemeGtk::hasOwnDisabledStateHandlingFor(ControlPart part) const
502{
503    return (part != MediaMuteButtonPart);
504}
505
506bool 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
511bool 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
523bool 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
537bool 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
542bool 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
547static 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
556bool RenderThemeGtk::paintMediaSliderTrack(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r)
557{
558    HTMLMediaElement* mediaElement = toParentMediaElement(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
589bool 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
596bool RenderThemeGtk::paintMediaVolumeSliderContainer(RenderObject*, const PaintInfo& paintInfo, const IntRect& rect)
597{
598    return true;
599}
600
601bool RenderThemeGtk::paintMediaVolumeSliderTrack(RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect)
602{
603    HTMLMediaElement* mediaElement = toParentMediaElement(renderObject);
604    if (!mediaElement)
605        return true;
606
607    float volume = 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
629bool RenderThemeGtk::paintMediaVolumeSliderThumb(RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect)
630{
631    return paintMediaSliderThumb(renderObject, paintInfo, rect);
632}
633
634String RenderThemeGtk::formatMediaControlsCurrentTime(float currentTime, float duration) const
635{
636    return formatMediaControlsTime(currentTime) + " / " + formatMediaControlsTime(duration);
637}
638
639bool RenderThemeGtk::paintMediaCurrentTime(RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect)
640{
641    return false;
642}
643#endif
644
645#if ENABLE(PROGRESS_ELEMENT)
646void RenderThemeGtk::adjustProgressBarStyle(StyleResolver*, RenderStyle* style, Element*) const
647{
648    style->setBoxShadow(nullptr);
649}
650
651// These values have been copied from RenderThemeChromiumSkia.cpp
652static const int progressActivityBlocks = 5;
653static const int progressAnimationFrames = 10;
654static const double progressAnimationInterval = 0.125;
655double RenderThemeGtk::animationRepeatIntervalForProgressBar(RenderProgress*) const
656{
657    return progressAnimationInterval;
658}
659
660double RenderThemeGtk::animationDurationForProgressBar(RenderProgress*) const
661{
662    return progressAnimationInterval * progressAnimationFrames * 2; // "2" for back and forth;
663}
664
665IntRect 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 = 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
696String 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)
716IntSize 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
722int 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}
730