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