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