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