• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
49 namespace WebCore {
50 
51 using namespace HTMLNames;
52 
53 #if ENABLE(VIDEO)
getMediaElementFromRenderObject(RenderObject * o)54 static 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 
getMediaButtonIconSize(int mediaIconSize)64 static 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 
initMediaButtons()72 void 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 
create()99 PassRefPtr<RenderTheme> RenderThemeGtk::create()
100 {
101     return adoptRef(new RenderThemeGtk());
102 }
103 
themeForPage(Page * page)104 PassRefPtr<RenderTheme> RenderTheme::themeForPage(Page* page)
105 {
106     static RenderTheme* rt = RenderThemeGtk::create().releaseRef();
107     return rt;
108 }
109 
RenderThemeGtk()110 RenderThemeGtk::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 
supportsFocus(ControlPart appearance)126 static 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 
supportsFocusRing(const RenderStyle * style) const145 bool RenderThemeGtk::supportsFocusRing(const RenderStyle* style) const
146 {
147     return supportsFocus(style->appearance());
148 }
149 
controlSupportsTints(const RenderObject * o) const150 bool RenderThemeGtk::controlSupportsTints(const RenderObject* o) const
151 {
152     return isEnabled(o);
153 }
154 
baselinePosition(const RenderObject * o) const155 int 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.
gtkTextDirection(TextDirection direction)173 GtkTextDirection 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 
gtkIconState(RenderTheme * theme,RenderObject * renderObject)185 static 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 
adjustButtonStyle(CSSStyleSelector * selector,RenderStyle * style,WebCore::Element * e) const197 void 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 
adjustMenuListStyle(CSSStyleSelector * selector,RenderStyle * style,Element * e) const204 void 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 
adjustMenuListButtonStyle(CSSStyleSelector * selector,RenderStyle * style,Element * e) const213 void RenderThemeGtk::adjustMenuListButtonStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const
214 {
215     adjustMenuListStyle(selector, style, e);
216 }
217 
paintMenuListButton(RenderObject * object,const PaintInfo & info,const IntRect & rect)218 bool RenderThemeGtk::paintMenuListButton(RenderObject* object, const PaintInfo& info, const IntRect& rect)
219 {
220     return paintMenuList(object, info, rect);
221 }
222 
paintTextArea(RenderObject * o,const PaintInfo & i,const IntRect & r)223 bool RenderThemeGtk::paintTextArea(RenderObject* o, const PaintInfo& i, const IntRect& r)
224 {
225     return paintTextField(o, i, r);
226 }
227 
paintGdkPixbuf(GraphicsContext * context,const GdkPixbuf * icon,const IntRect & iconRect)228 static 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)
246 static const gint gtkIconSizeMenu = 16;
247 static const gint gtkIconSizeSmallToolbar = 18;
248 static const gint gtkIconSizeButton = 20;
249 static const gint gtkIconSizeLargeToolbar = 24;
250 static const gint gtkIconSizeDnd = 32;
251 static const gint gtkIconSizeDialog = 48;
252 
getIconSizeForPixelSize(gint pixelSize)253 static 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 
adjustSearchFieldResultsButtonStyle(CSSStyleSelector * selector,RenderStyle * style,Element * e) const269 void RenderThemeGtk::adjustSearchFieldResultsButtonStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const
270 {
271     adjustSearchFieldCancelButtonStyle(selector, style, e);
272 }
273 
paintSearchFieldResultsButton(RenderObject * o,const PaintInfo & i,const IntRect & rect)274 bool RenderThemeGtk::paintSearchFieldResultsButton(RenderObject* o, const PaintInfo& i, const IntRect& rect)
275 {
276     return paintSearchFieldResultsDecoration(o, i, rect);
277 }
278 
adjustSearchFieldIconStyle(RenderStyle * style)279 static 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 
adjustSearchFieldResultsDecorationStyle(CSSStyleSelector * selector,RenderStyle * style,Element * e) const297 void RenderThemeGtk::adjustSearchFieldResultsDecorationStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const
298 {
299     adjustSearchFieldIconStyle(style);
300 }
301 
centerRectVerticallyInParentInputElement(RenderObject * renderObject,const IntRect & rect)302 static 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 
paintSearchFieldResultsDecoration(RenderObject * renderObject,const PaintInfo & paintInfo,const IntRect & rect)320 bool 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 
adjustSearchFieldCancelButtonStyle(CSSStyleSelector * selector,RenderStyle * style,Element * e) const334 void RenderThemeGtk::adjustSearchFieldCancelButtonStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const
335 {
336     adjustSearchFieldIconStyle(style);
337 }
338 
paintSearchFieldCancelButton(RenderObject * renderObject,const PaintInfo & paintInfo,const IntRect & rect)339 bool 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 
adjustSearchFieldStyle(CSSStyleSelector * selector,RenderStyle * style,Element * e) const353 void 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 
paintSearchField(RenderObject * o,const PaintInfo & i,const IntRect & rect)360 bool RenderThemeGtk::paintSearchField(RenderObject* o, const PaintInfo& i, const IntRect& rect)
361 {
362     return paintTextField(o, i, rect);
363 }
364 
paintCapsLockIndicator(RenderObject * renderObject,const PaintInfo & paintInfo,const IntRect & rect)365 bool 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 
adjustSliderTrackStyle(CSSStyleSelector *,RenderStyle * style,Element *) const390 void RenderThemeGtk::adjustSliderTrackStyle(CSSStyleSelector*, RenderStyle* style, Element*) const
391 {
392     style->setBoxShadow(0);
393 }
394 
adjustSliderThumbStyle(CSSStyleSelector *,RenderStyle * style,Element *) const395 void RenderThemeGtk::adjustSliderThumbStyle(CSSStyleSelector*, RenderStyle* style, Element*) const
396 {
397     style->setBoxShadow(0);
398 }
399 
caretBlinkInterval() const400 double 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 
getScreenDPI()415 double 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 
systemFont(int,FontDescription & fontDescription) const428 void 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 
platformColorsDidChange()457 void RenderThemeGtk::platformColorsDidChange()
458 {
459 #if ENABLE(VIDEO)
460     initMediaColors();
461 #endif
462     RenderTheme::platformColorsDidChange();
463 }
464 
465 #if ENABLE(VIDEO)
extraMediaControlsStyleSheet()466 String RenderThemeGtk::extraMediaControlsStyleSheet()
467 {
468     return String(mediaControlsGtkUserAgentStyleSheet, sizeof(mediaControlsGtkUserAgentStyleSheet));
469 }
470 
adjustMediaSliderThumbSize(RenderObject * renderObject) const471 void 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 
paintMediaButton(RenderObject * renderObject,GraphicsContext * context,const IntRect & rect,const char * iconName)478 bool 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 
paintMediaFullscreenButton(RenderObject * renderObject,const PaintInfo & paintInfo,const IntRect & rect)492 bool RenderThemeGtk::paintMediaFullscreenButton(RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect)
493 {
494     return paintMediaButton(renderObject, paintInfo.context, rect, GTK_STOCK_FULLSCREEN);
495 }
496 
paintMediaMuteButton(RenderObject * renderObject,const PaintInfo & paintInfo,const IntRect & rect)497 bool 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 
paintMediaPlayButton(RenderObject * renderObject,const PaintInfo & paintInfo,const IntRect & rect)506 bool 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 
paintMediaSeekBackButton(RenderObject * renderObject,const PaintInfo & paintInfo,const IntRect & rect)516 bool RenderThemeGtk::paintMediaSeekBackButton(RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect)
517 {
518     return paintMediaButton(renderObject, paintInfo.context, rect, GTK_STOCK_MEDIA_REWIND);
519 }
520 
paintMediaSeekForwardButton(RenderObject * renderObject,const PaintInfo & paintInfo,const IntRect & rect)521 bool RenderThemeGtk::paintMediaSeekForwardButton(RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect)
522 {
523     return paintMediaButton(renderObject, paintInfo.context, rect, GTK_STOCK_MEDIA_FORWARD);
524 }
525 
paintMediaSliderTrack(RenderObject * o,const PaintInfo & paintInfo,const IntRect & r)526 bool 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 
paintMediaSliderThumb(RenderObject * o,const PaintInfo & paintInfo,const IntRect & r)586 bool 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 
paintMediaVolumeSliderContainer(RenderObject *,const PaintInfo & paintInfo,const IntRect & rect)593 bool 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 
paintMediaVolumeSliderTrack(RenderObject * renderObject,const PaintInfo & paintInfo,const IntRect & rect)600 bool RenderThemeGtk::paintMediaVolumeSliderTrack(RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect)
601 {
602     return paintSliderTrack(renderObject, paintInfo, rect);
603 }
604 
paintMediaVolumeSliderThumb(RenderObject * renderObject,const PaintInfo & paintInfo,const IntRect & rect)605 bool RenderThemeGtk::paintMediaVolumeSliderThumb(RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect)
606 {
607     return paintSliderThumb(renderObject, paintInfo, rect);
608 }
609 
formatMediaControlsCurrentTime(float currentTime,float duration) const610 String RenderThemeGtk::formatMediaControlsCurrentTime(float currentTime, float duration) const
611 {
612     return formatMediaControlsTime(currentTime) + " / " + formatMediaControlsTime(duration);
613 }
614 
paintMediaCurrentTime(RenderObject * renderObject,const PaintInfo & paintInfo,const IntRect & rect)615 bool 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)
adjustProgressBarStyle(CSSStyleSelector *,RenderStyle * style,Element *) const625 void RenderThemeGtk::adjustProgressBarStyle(CSSStyleSelector*, RenderStyle* style, Element*) const
626 {
627     style->setBoxShadow(0);
628 }
629 
630 // These values have been copied from RenderThemeChromiumSkia.cpp
631 static const int progressActivityBlocks = 5;
632 static const int progressAnimationFrames = 10;
633 static const double progressAnimationInterval = 0.125;
animationRepeatIntervalForProgressBar(RenderProgress *) const634 double RenderThemeGtk::animationRepeatIntervalForProgressBar(RenderProgress*) const
635 {
636     return progressAnimationInterval;
637 }
638 
animationDurationForProgressBar(RenderProgress *) const639 double RenderThemeGtk::animationDurationForProgressBar(RenderProgress*) const
640 {
641     return progressAnimationInterval * progressAnimationFrames * 2; // "2" for back and forth;
642 }
643 
calculateProgressRect(RenderObject * renderObject,const IntRect & fullBarRect)644 IntRect 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