43a23f2acd9787f9899d760fd0298bcef5efc04b
[WebKit-https.git] / 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 "AffineTransform.h"
29 #include "CSSValueKeywords.h"
30 #include "GOwnPtr.h"
31 #include "Gradient.h"
32 #include "GraphicsContext.h"
33 #include "GtkVersioning.h"
34 #include "HTMLMediaElement.h"
35 #include "HTMLNames.h"
36 #include "MediaControlElements.h"
37 #include "NotImplemented.h"
38 #include "PlatformMouseEvent.h"
39 #include "RenderBox.h"
40 #include "RenderObject.h"
41 #include "Scrollbar.h"
42 #include "TimeRanges.h"
43 #include "UserAgentStyleSheets.h"
44 #include "WidgetRenderingContext.h"
45 #include "gtkdrawing.h"
46 #include <gdk/gdk.h>
47 #include <gtk/gtk.h>
48 #include <wtf/text/CString.h>
49
50 #if ENABLE(PROGRESS_TAG)
51 #include "RenderProgress.h"
52 #endif
53
54 namespace WebCore {
55
56 using namespace HTMLNames;
57
58 static void paintStockIcon(GraphicsContext* context, const IntPoint& iconPoint, GtkStyle* style, const char* iconName,
59                            GtkTextDirection direction, GtkStateType state, GtkIconSize iconSize)
60 {
61     GtkIconSet* iconSet = gtk_style_lookup_icon_set(style, iconName);
62     PlatformRefPtr<GdkPixbuf> icon = adoptPlatformRef(gtk_icon_set_render_icon(iconSet, style, direction, state, iconSize, 0, 0));
63
64     cairo_t* cr = context->platformContext();
65     cairo_save(cr);
66     gdk_cairo_set_source_pixbuf(cr, icon.get(), iconPoint.x(), iconPoint.y());
67     cairo_paint(cr);
68     cairo_restore(cr);
69 }
70
71 #if ENABLE(VIDEO)
72 static HTMLMediaElement* getMediaElementFromRenderObject(RenderObject* o)
73 {
74     Node* node = o->node();
75     Node* mediaNode = node ? node->shadowAncestorNode() : 0;
76     if (!mediaNode || (!mediaNode->hasTagName(videoTag) && !mediaNode->hasTagName(audioTag)))
77         return 0;
78
79     return static_cast<HTMLMediaElement*>(mediaNode);
80 }
81
82 static GtkIconSize getMediaButtonIconSize(int mediaIconSize)
83 {
84     GtkIconSize iconSize = gtk_icon_size_from_name("webkit-media-button-size");
85     if (!iconSize)
86         iconSize = gtk_icon_size_register("webkit-media-button-size", mediaIconSize, mediaIconSize);
87     return iconSize;
88 }
89
90 void RenderThemeGtk::initMediaColors()
91 {
92     GtkStyle* style = gtk_widget_get_style(GTK_WIDGET(gtkContainer()));
93     m_panelColor = style->bg[GTK_STATE_NORMAL];
94     m_sliderColor = style->bg[GTK_STATE_ACTIVE];
95     m_sliderThumbColor = style->bg[GTK_STATE_SELECTED];
96 }
97
98 void RenderThemeGtk::initMediaButtons()
99 {
100     static bool iconsInitialized = false;
101
102     if (iconsInitialized)
103         return;
104
105     PlatformRefPtr<GtkIconFactory> iconFactory = adoptPlatformRef(gtk_icon_factory_new());
106     GtkIconSource* iconSource = gtk_icon_source_new();
107     const char* icons[] = { "audio-volume-high", "audio-volume-muted" };
108
109     gtk_icon_factory_add_default(iconFactory.get());
110
111     for (size_t i = 0; i < G_N_ELEMENTS(icons); ++i) {
112         gtk_icon_source_set_icon_name(iconSource, icons[i]);
113         GtkIconSet* iconSet = gtk_icon_set_new();
114         gtk_icon_set_add_source(iconSet, iconSource);
115         gtk_icon_factory_add(iconFactory.get(), icons[i], iconSet);
116         gtk_icon_set_unref(iconSet);
117     }
118
119     gtk_icon_source_free(iconSource);
120
121     iconsInitialized = true;
122 }
123 #endif
124
125 PassRefPtr<RenderTheme> RenderThemeGtk::create()
126 {
127     return adoptRef(new RenderThemeGtk());
128 }
129
130 PassRefPtr<RenderTheme> RenderTheme::themeForPage(Page* page)
131 {
132     static RenderTheme* rt = RenderThemeGtk::create().releaseRef();
133     return rt;
134 }
135
136 static int mozGtkRefCount = 0;
137
138 RenderThemeGtk::RenderThemeGtk()
139     : m_gtkWindow(0)
140     , m_gtkContainer(0)
141     , m_gtkButton(0)
142     , m_gtkEntry(0)
143     , m_gtkTreeView(0)
144     , m_panelColor(Color::white)
145     , m_sliderColor(Color::white)
146     , m_sliderThumbColor(Color::white)
147     , m_mediaIconSize(16)
148     , m_mediaSliderHeight(14)
149     , m_mediaSliderThumbWidth(12)
150     , m_mediaSliderThumbHeight(12)
151 #ifdef GTK_API_VERSION_2
152     , m_themePartsHaveRGBAColormap(true)
153 #endif
154 {
155
156     memset(&m_themeParts, 0, sizeof(GtkThemeParts));
157 #ifdef GTK_API_VERSION_2
158     GdkColormap* colormap = gdk_screen_get_rgba_colormap(gdk_screen_get_default());
159     if (!colormap) {
160         m_themePartsHaveRGBAColormap = false;
161         colormap = gdk_screen_get_default_colormap(gdk_screen_get_default());
162     }
163     m_themeParts.colormap = colormap;
164 #endif
165
166     // Initialize the Mozilla theme drawing code.
167     if (!mozGtkRefCount) {
168         moz_gtk_init();
169         moz_gtk_use_theme_parts(&m_themeParts);
170     }
171     ++mozGtkRefCount;
172
173 #if ENABLE(VIDEO)
174     initMediaColors();
175     initMediaButtons();
176 #endif
177 }
178
179 RenderThemeGtk::~RenderThemeGtk()
180 {
181     --mozGtkRefCount;
182
183     if (!mozGtkRefCount)
184         moz_gtk_shutdown();
185
186     gtk_widget_destroy(m_gtkWindow);
187 }
188
189 void RenderThemeGtk::getIndicatorMetrics(ControlPart part, int& indicatorSize, int& indicatorSpacing) const
190 {
191     ASSERT(part == CheckboxPart || part == RadioPart);
192     if (part == CheckboxPart) {
193         moz_gtk_checkbox_get_metrics(&indicatorSize, &indicatorSpacing);
194         return;
195     }
196
197     // RadioPart
198     moz_gtk_radio_get_metrics(&indicatorSize, &indicatorSpacing);
199 }
200
201 static bool supportsFocus(ControlPart appearance)
202 {
203     switch (appearance) {
204     case PushButtonPart:
205     case ButtonPart:
206     case TextFieldPart:
207     case TextAreaPart:
208     case SearchFieldPart:
209     case MenulistPart:
210     case RadioPart:
211     case CheckboxPart:
212     case SliderHorizontalPart:
213     case SliderVerticalPart:
214         return true;
215     default:
216         return false;
217     }
218 }
219
220 GtkStateType RenderThemeGtk::getGtkStateType(RenderObject* object)
221 {
222     if (!isEnabled(object) || isReadOnlyControl(object))
223         return GTK_STATE_INSENSITIVE;
224     if (isPressed(object))
225         return GTK_STATE_ACTIVE;
226     if (isHovered(object))
227         return GTK_STATE_PRELIGHT;
228     return GTK_STATE_NORMAL;
229 }
230
231 bool RenderThemeGtk::supportsFocusRing(const RenderStyle* style) const
232 {
233     return supportsFocus(style->appearance());
234 }
235
236 bool RenderThemeGtk::controlSupportsTints(const RenderObject* o) const
237 {
238     return isEnabled(o);
239 }
240
241 int RenderThemeGtk::baselinePosition(const RenderObject* o) const
242 {
243     if (!o->isBox())
244         return 0;
245
246     // FIXME: This strategy is possibly incorrect for the GTK+ port.
247     if (o->style()->appearance() == CheckboxPart
248         || o->style()->appearance() == RadioPart) {
249         const RenderBox* box = toRenderBox(o);
250         return box->marginTop() + box->height() - 2;
251     }
252
253     return RenderTheme::baselinePosition(o);
254 }
255
256 static GtkTextDirection gtkTextDirection(TextDirection direction)
257 {
258     switch (direction) {
259     case RTL:
260         return GTK_TEXT_DIR_RTL;
261     case LTR:
262         return GTK_TEXT_DIR_LTR;
263     default:
264         return GTK_TEXT_DIR_NONE;
265     }
266 }
267
268 GtkStateType RenderThemeGtk::gtkIconState(RenderObject* renderObject)
269 {
270     if (!isEnabled(renderObject))
271         return GTK_STATE_INSENSITIVE;
272     if (isPressed(renderObject))
273         return GTK_STATE_ACTIVE;
274     if (isHovered(renderObject))
275         return GTK_STATE_PRELIGHT;
276
277     return GTK_STATE_NORMAL;
278 }
279
280 bool RenderThemeGtk::paintRenderObject(GtkThemeWidgetType type, RenderObject* renderObject, GraphicsContext* context, const IntRect& rect, int flags)
281 {
282     // Painting is disabled so just claim to have succeeded
283     if (context->paintingDisabled())
284         return false;
285
286     GtkWidgetState widgetState;
287     widgetState.active = isPressed(renderObject);
288     widgetState.focused = isFocused(renderObject);
289
290     // https://bugs.webkit.org/show_bug.cgi?id=18364
291     // The Mozilla theme drawing code, only paints a button as pressed when it's pressed 
292     // while hovered. Until we move away from the Mozila code, work-around the issue by
293     // forcing a pressed button into the hovered state. This ensures that buttons activated
294     // via the keyboard have the proper rendering.
295     widgetState.inHover = isHovered(renderObject) || (type == MOZ_GTK_BUTTON && isPressed(renderObject));
296
297     // FIXME: Disabled does not always give the correct appearance for ReadOnly
298     widgetState.disabled = !isEnabled(renderObject) || isReadOnlyControl(renderObject);
299     widgetState.isDefault = false;
300     widgetState.canDefault = false;
301
302     WidgetRenderingContext widgetContext(context, rect);
303     return !widgetContext.paintMozillaWidget(type, &widgetState, flags, gtkTextDirection(renderObject->style()->direction()));
304 }
305
306 static void setToggleSize(const RenderThemeGtk* theme, RenderStyle* style, ControlPart appearance)
307 {
308     // The width and height are both specified, so we shouldn't change them.
309     if (!style->width().isIntrinsicOrAuto() && !style->height().isAuto())
310         return;
311
312     // FIXME: This is probably not correct use of indicatorSize and indicatorSpacing.
313     gint indicatorSize, indicatorSpacing;
314     theme->getIndicatorMetrics(appearance, indicatorSize, indicatorSpacing);
315
316     // Other ports hard-code this to 13, but GTK+ users tend to demand the native look.
317     // It could be made a configuration option values other than 13 actually break site compatibility.
318     int length = indicatorSize + indicatorSpacing;
319     if (style->width().isIntrinsicOrAuto())
320         style->setWidth(Length(length, Fixed));
321
322     if (style->height().isAuto())
323         style->setHeight(Length(length, Fixed));
324 }
325
326 void RenderThemeGtk::setCheckboxSize(RenderStyle* style) const
327 {
328     setToggleSize(this, style, RadioPart);
329 }
330
331 bool RenderThemeGtk::paintCheckbox(RenderObject* o, const PaintInfo& i, const IntRect& rect)
332 {
333     return paintRenderObject(MOZ_GTK_CHECKBUTTON, o, i.context, rect, isChecked(o));
334 }
335
336 void RenderThemeGtk::setRadioSize(RenderStyle* style) const
337 {
338     setToggleSize(this, style, RadioPart);
339 }
340
341 bool RenderThemeGtk::paintRadio(RenderObject* o, const PaintInfo& i, const IntRect& rect)
342 {
343     return paintRenderObject(MOZ_GTK_RADIOBUTTON, o, i.context, rect, isChecked(o));
344 }
345
346 void RenderThemeGtk::adjustButtonStyle(CSSStyleSelector* selector, RenderStyle* style, WebCore::Element* e) const
347 {
348     // Some layout tests check explicitly that buttons ignore line-height.
349     if (style->appearance() == PushButtonPart)
350         style->setLineHeight(RenderStyle::initialLineHeight());
351 }
352
353 bool RenderThemeGtk::paintButton(RenderObject* o, const PaintInfo& i, const IntRect& rect)
354 {
355     return paintRenderObject(MOZ_GTK_BUTTON, o, i.context, rect, GTK_RELIEF_NORMAL);
356 }
357
358 static void getComboBoxPadding(RenderStyle* style, int& left, int& top, int& right, int& bottom)
359 {
360     // If this menu list button isn't drawn using the native theme, we
361     // don't add any extra padding beyond what WebCore already uses.
362     if (style->appearance() == NoControlPart)
363         return;
364     moz_gtk_get_widget_border(MOZ_GTK_DROPDOWN, &left, &top, &right, &bottom,
365                               gtkTextDirection(style->direction()), TRUE);
366 }
367
368 int RenderThemeGtk::popupInternalPaddingLeft(RenderStyle* style) const
369 {
370     int left = 0, top = 0, right = 0, bottom = 0;
371     getComboBoxPadding(style, left, top, right, bottom);
372     return left;
373 }
374
375 int RenderThemeGtk::popupInternalPaddingRight(RenderStyle* style) const
376 {
377     int left = 0, top = 0, right = 0, bottom = 0;
378     getComboBoxPadding(style, left, top, right, bottom);
379     return right;
380 }
381
382 int RenderThemeGtk::popupInternalPaddingTop(RenderStyle* style) const
383 {
384     int left = 0, top = 0, right = 0, bottom = 0;
385     getComboBoxPadding(style, left, top, right, bottom);
386     return top;
387 }
388
389 int RenderThemeGtk::popupInternalPaddingBottom(RenderStyle* style) const
390 {
391     int left = 0, top = 0, right = 0, bottom = 0;
392     getComboBoxPadding(style, left, top, right, bottom);
393     return bottom;
394 }
395
396 void RenderThemeGtk::adjustMenuListStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const
397 {
398     // The tests check explicitly that select menu buttons ignore line height.
399     style->setLineHeight(RenderStyle::initialLineHeight());
400
401     // We cannot give a proper rendering when border radius is active, unfortunately.
402     style->resetBorderRadius();
403 }
404
405 void RenderThemeGtk::adjustMenuListButtonStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const
406 {
407     adjustMenuListStyle(selector, style, e);
408 }
409
410 bool RenderThemeGtk::paintMenuList(RenderObject* o, const PaintInfo& i, const IntRect& rect)
411 {
412     return paintRenderObject(MOZ_GTK_DROPDOWN, o, i.context, rect);
413 }
414
415 bool RenderThemeGtk::paintMenuListButton(RenderObject* object, const PaintInfo& info, const IntRect& rect)
416 {
417     return paintMenuList(object, info, rect);
418 }
419
420 static void setTextInputBorders(RenderStyle* style)
421 {
422     // If this control isn't drawn using the native theme, we don't touch the borders.
423     if (style->appearance() == NoControlPart)
424         return;
425
426     // We cannot give a proper rendering when border radius is active, unfortunately.
427     style->resetBorderRadius();
428
429     int left = 0, top = 0, right = 0, bottom = 0;
430     moz_gtk_get_widget_border(MOZ_GTK_ENTRY, &left, &top, &right, &bottom,
431                               gtkTextDirection(style->direction()), TRUE);
432     style->setBorderLeftWidth(left);
433     style->setBorderTopWidth(top);
434     style->setBorderRightWidth(right);
435     style->setBorderBottomWidth(bottom);
436 }
437
438 void RenderThemeGtk::adjustTextFieldStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const
439 {
440     setTextInputBorders(style);
441 }
442
443 bool RenderThemeGtk::paintTextField(RenderObject* o, const PaintInfo& i, const IntRect& rect)
444 {
445     return paintRenderObject(MOZ_GTK_ENTRY, o, i.context, rect);
446 }
447
448 void RenderThemeGtk::adjustTextAreaStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const
449 {
450     setTextInputBorders(style);
451 }
452
453 bool RenderThemeGtk::paintTextArea(RenderObject* o, const PaintInfo& i, const IntRect& r)
454 {
455     return paintTextField(o, i, r);
456 }
457
458 void RenderThemeGtk::adjustSearchFieldResultsButtonStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const
459 {
460     adjustSearchFieldCancelButtonStyle(selector, style, e);
461 }
462
463 bool RenderThemeGtk::paintSearchFieldResultsButton(RenderObject* o, const PaintInfo& i, const IntRect& rect)
464 {
465     return paintSearchFieldResultsDecoration(o, i, rect);
466 }
467
468 void RenderThemeGtk::adjustSearchFieldResultsDecorationStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const
469 {
470     style->resetBorder();
471     style->resetPadding();
472
473     gint width = 0, height = 0;
474     gtk_icon_size_lookup(GTK_ICON_SIZE_MENU, &width, &height);
475     style->setWidth(Length(width, Fixed));
476     style->setHeight(Length(height, Fixed));
477 }
478
479 static IntPoint centerRectVerticallyInParentInputElement(RenderObject* object, const IntRect& rect)
480 {
481     Node* input = object->node()->shadowAncestorNode(); // Get the renderer of <input> element.
482     if (!input->renderer()->isBox())
483         return rect.topLeft();
484
485     // If possible center the y-coordinate of the rect vertically in the parent input element.
486     // We also add one pixel here to ensure that the y coordinate is rounded up for box heights
487     // that are even, which looks in relation to the box text.
488     IntRect inputContentBox = toRenderBox(input->renderer())->absoluteContentBox();
489
490     return IntPoint(rect.x(), inputContentBox.y() + (inputContentBox.height() - rect.height() + 1) / 2);
491 }
492
493 bool RenderThemeGtk::paintSearchFieldResultsDecoration(RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect)
494 {
495     GtkStyle* style = gtk_widget_get_style(GTK_WIDGET(gtkEntry()));
496     IntPoint iconPoint(centerRectVerticallyInParentInputElement(renderObject, rect));
497     paintStockIcon(paintInfo.context, iconPoint, style, GTK_STOCK_FIND,
498                    gtkTextDirection(renderObject->style()->direction()),
499                    gtkIconState(renderObject), GTK_ICON_SIZE_MENU);
500     return false;
501 }
502
503 void RenderThemeGtk::adjustSearchFieldCancelButtonStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const
504 {
505     style->resetBorder();
506     style->resetPadding();
507
508     gint width = 0, height = 0;
509     gtk_icon_size_lookup(GTK_ICON_SIZE_MENU, &width, &height);
510     style->setWidth(Length(width, Fixed));
511     style->setHeight(Length(height, Fixed));
512 }
513
514 bool RenderThemeGtk::paintSearchFieldCancelButton(RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect)
515 {
516     GtkStyle* style = gtk_widget_get_style(GTK_WIDGET(gtkEntry()));
517     IntPoint iconPoint(centerRectVerticallyInParentInputElement(renderObject, rect));
518     paintStockIcon(paintInfo.context, iconPoint, style, GTK_STOCK_CLEAR,
519                    gtkTextDirection(renderObject->style()->direction()),
520                    gtkIconState(renderObject), GTK_ICON_SIZE_MENU);
521     return false;
522 }
523
524 void RenderThemeGtk::adjustSearchFieldStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const
525 {
526     style->setLineHeight(RenderStyle::initialLineHeight());
527     setTextInputBorders(style);
528 }
529
530 bool RenderThemeGtk::paintSearchField(RenderObject* o, const PaintInfo& i, const IntRect& rect)
531 {
532     return paintTextField(o, i, rect);
533 }
534
535 bool RenderThemeGtk::paintSliderTrack(RenderObject* object, const PaintInfo& info, const IntRect& rect)
536 {
537     if (info.context->paintingDisabled())
538         return false;
539
540     ControlPart part = object->style()->appearance();
541     ASSERT(part == SliderHorizontalPart || part == SliderVerticalPart);
542
543     // We shrink the trough rect slightly to make room for the focus indicator.
544     IntRect troughRect(IntPoint(), rect.size()); // This is relative to rect.
545     GtkWidget* widget = 0;
546     if (part == SliderVerticalPart) {
547         widget = gtkVScale();
548         troughRect.inflateY(-gtk_widget_get_style(widget)->ythickness);
549     } else {
550         widget = gtkHScale();
551         troughRect.inflateX(-gtk_widget_get_style(widget)->xthickness);
552     }
553     gtk_widget_set_direction(widget, gtkTextDirection(object->style()->direction()));
554
555     WidgetRenderingContext widgetContext(info.context, rect);
556     widgetContext.gtkPaintBox(troughRect, widget, GTK_STATE_ACTIVE, GTK_SHADOW_OUT, "trough");
557     if (isFocused(object))
558         widgetContext.gtkPaintFocus(IntRect(IntPoint(), rect.size()), widget, getGtkStateType(object), "trough");
559
560     return false;
561 }
562
563 void RenderThemeGtk::adjustSliderTrackStyle(CSSStyleSelector*, RenderStyle* style, Element*) const
564 {
565     style->setBoxShadow(0);
566 }
567
568 bool RenderThemeGtk::paintSliderThumb(RenderObject* object, const PaintInfo& info, const IntRect& rect)
569 {
570     if (info.context->paintingDisabled())
571         return false;
572
573     ControlPart part = object->style()->appearance();
574     ASSERT(part == SliderThumbHorizontalPart || part == SliderThumbVerticalPart);
575
576     GtkWidget* widget = 0;
577     const char* detail = 0;
578     GtkOrientation orientation;
579     if (part == SliderThumbVerticalPart) {
580         widget = gtkVScale();
581         detail = "vscale";
582         orientation = GTK_ORIENTATION_VERTICAL;
583     } else {
584         widget = gtkHScale();
585         detail = "hscale";
586         orientation = GTK_ORIENTATION_HORIZONTAL;
587     }
588     gtk_widget_set_direction(widget, gtkTextDirection(object->style()->direction()));
589
590     // Only some themes have slider thumbs respond to clicks and some don't. This information is
591     // gathered via the 'activate-slider' property, but it's deprecated in GTK+ 2.22 and removed in
592     // GTK+ 3.x. The drawback of not honoring it is that slider thumbs change color when you click
593     // on them. 
594     IntRect thumbRect(IntPoint(), rect.size());
595     WidgetRenderingContext widgetContext(info.context, rect);
596     widgetContext.gtkPaintSlider(thumbRect, widget, getGtkStateType(object), GTK_SHADOW_OUT, detail, orientation);
597     return false;
598 }
599
600 void RenderThemeGtk::adjustSliderThumbStyle(CSSStyleSelector*, RenderStyle* style, Element*) const
601 {
602     style->setBoxShadow(0);
603 }
604
605 void RenderThemeGtk::adjustSliderThumbSize(RenderObject* o) const
606 {
607     ControlPart part = o->style()->appearance();
608 #if ENABLE(VIDEO)
609     if (part == MediaSliderThumbPart) {
610         o->style()->setWidth(Length(m_mediaSliderThumbWidth, Fixed));
611         o->style()->setHeight(Length(m_mediaSliderThumbHeight, Fixed));
612         return;
613     }
614     if (part == MediaVolumeSliderThumbPart)
615         return;
616 #endif
617
618     GtkWidget* widget = part == SliderThumbHorizontalPart ? gtkHScale() : gtkVScale();
619     int length = 0, width = 0;
620     gtk_widget_style_get(widget,
621                          "slider_length", &length,
622                          "slider_width", &width,
623                          NULL);
624
625     if (part == SliderThumbHorizontalPart) {
626         o->style()->setWidth(Length(length, Fixed));
627         o->style()->setHeight(Length(width, Fixed));
628         return;
629     }
630     ASSERT(part == SliderThumbVerticalPart);
631     o->style()->setWidth(Length(width, Fixed));
632     o->style()->setHeight(Length(length, Fixed));
633 }
634
635 Color RenderThemeGtk::platformActiveSelectionBackgroundColor() const
636 {
637     GtkWidget* widget = gtkEntry();
638     return gtk_widget_get_style(widget)->base[GTK_STATE_SELECTED];
639 }
640
641 Color RenderThemeGtk::platformInactiveSelectionBackgroundColor() const
642 {
643     GtkWidget* widget = gtkEntry();
644     return gtk_widget_get_style(widget)->base[GTK_STATE_ACTIVE];
645 }
646
647 Color RenderThemeGtk::platformActiveSelectionForegroundColor() const
648 {
649     GtkWidget* widget = gtkEntry();
650     return gtk_widget_get_style(widget)->text[GTK_STATE_SELECTED];
651 }
652
653 Color RenderThemeGtk::platformInactiveSelectionForegroundColor() const
654 {
655     GtkWidget* widget = gtkEntry();
656     return gtk_widget_get_style(widget)->text[GTK_STATE_ACTIVE];
657 }
658
659 Color RenderThemeGtk::activeListBoxSelectionBackgroundColor() const
660 {
661     GtkWidget* widget = gtkTreeView();
662     return gtk_widget_get_style(widget)->base[GTK_STATE_SELECTED];
663 }
664
665 Color RenderThemeGtk::inactiveListBoxSelectionBackgroundColor() const
666 {
667     GtkWidget* widget = gtkTreeView();
668     return gtk_widget_get_style(widget)->base[GTK_STATE_ACTIVE];
669 }
670
671 Color RenderThemeGtk::activeListBoxSelectionForegroundColor() const
672 {
673     GtkWidget* widget = gtkTreeView();
674     return gtk_widget_get_style(widget)->text[GTK_STATE_SELECTED];
675 }
676
677 Color RenderThemeGtk::inactiveListBoxSelectionForegroundColor() const
678 {
679     GtkWidget* widget = gtkTreeView();
680     return gtk_widget_get_style(widget)->text[GTK_STATE_ACTIVE];
681 }
682
683 double RenderThemeGtk::caretBlinkInterval() const
684 {
685     GtkSettings* settings = gtk_settings_get_default();
686
687     gboolean shouldBlink;
688     gint time;
689
690     g_object_get(settings, "gtk-cursor-blink", &shouldBlink, "gtk-cursor-blink-time", &time, NULL);
691
692     if (!shouldBlink)
693         return 0;
694
695     return time / 2000.;
696 }
697
698 static double getScreenDPI()
699 {
700     // FIXME: Really this should be the widget's screen.
701     GdkScreen* screen = gdk_screen_get_default();
702     if (!screen)
703         return 96; // Default to 96 DPI.
704
705     float dpi = gdk_screen_get_resolution(screen);
706     if (dpi <= 0)
707         return 96;
708     return dpi;
709 }
710
711 void RenderThemeGtk::systemFont(int, FontDescription& fontDescription) const
712 {
713     GtkSettings* settings = gtk_settings_get_default();
714     if (!settings)
715         return;
716
717     // This will be a font selection string like "Sans 10" so we cannot use it as the family name.
718     GOwnPtr<gchar> fontName;
719     g_object_get(settings, "gtk-font-name", &fontName.outPtr(), NULL);
720
721     PangoFontDescription* pangoDescription = pango_font_description_from_string(fontName.get());
722     if (!pangoDescription)
723         return;
724
725     fontDescription.firstFamily().setFamily(pango_font_description_get_family(pangoDescription));
726
727     int size = pango_font_description_get_size(pangoDescription) / PANGO_SCALE;
728     // If the size of the font is in points, we need to convert it to pixels.
729     if (!pango_font_description_get_size_is_absolute(pangoDescription))
730         size = size * (getScreenDPI() / 72.0);
731
732     fontDescription.setSpecifiedSize(size);
733     fontDescription.setIsAbsoluteSize(true);
734     fontDescription.setGenericFamily(FontDescription::NoFamily);
735     fontDescription.setWeight(FontWeightNormal);
736     fontDescription.setItalic(false);
737     pango_font_description_free(pangoDescription);
738 }
739
740 Color RenderThemeGtk::systemColor(int cssValueId) const
741 {
742     switch (cssValueId) {
743     case CSSValueButtontext:
744         return Color(gtk_widget_get_style(gtkButton())->fg[GTK_STATE_NORMAL]);
745     case CSSValueCaptiontext:
746         return Color(gtk_widget_get_style(gtkEntry())->fg[GTK_STATE_NORMAL]);
747     default:
748         return RenderTheme::systemColor(cssValueId);
749     }
750 }
751
752 static void gtkStyleSetCallback(GtkWidget* widget, GtkStyle* previous, RenderTheme* renderTheme)
753 {
754     // FIXME: Make sure this function doesn't get called many times for a single GTK+ style change signal.
755     renderTheme->platformColorsDidChange();
756 }
757
758 void RenderThemeGtk::setupWidgetAndAddToContainer(GtkWidget* widget, GtkWidget* window) const
759 {
760     gtk_container_add(GTK_CONTAINER(window), widget);
761     gtk_widget_realize(widget);
762     g_object_set_data(G_OBJECT(widget), "transparent-bg-hint", GINT_TO_POINTER(TRUE));
763
764     // FIXME: Perhaps this should only be called for the containing window or parent container.
765     g_signal_connect(widget, "style-set", G_CALLBACK(gtkStyleSetCallback), const_cast<RenderThemeGtk*>(this));
766 }
767
768 GtkWidget* RenderThemeGtk::gtkContainer() const
769 {
770     if (m_gtkContainer)
771         return m_gtkContainer;
772
773     m_gtkWindow = gtk_window_new(GTK_WINDOW_POPUP);
774 #if GTK_API_VERSION_2
775     gtk_widget_set_colormap(m_gtkWindow, m_themeParts.colormap);
776 #endif
777     gtk_widget_realize(m_gtkWindow);
778     gtk_widget_set_name(m_gtkWindow, "MozillaGtkWidget");
779
780     m_gtkContainer = gtk_fixed_new();
781     setupWidgetAndAddToContainer(m_gtkContainer, m_gtkWindow);
782     return m_gtkContainer;
783 }
784
785 GtkWidget* RenderThemeGtk::gtkButton() const
786 {
787     if (m_gtkButton)
788         return m_gtkButton;
789     m_gtkButton = gtk_button_new();
790     setupWidgetAndAddToContainer(m_gtkButton, gtkContainer());
791     return m_gtkButton;
792 }
793
794 GtkWidget* RenderThemeGtk::gtkEntry() const
795 {
796     if (m_gtkEntry)
797         return m_gtkEntry;
798     m_gtkEntry = gtk_entry_new();
799     setupWidgetAndAddToContainer(m_gtkEntry, gtkContainer());
800     return m_gtkEntry;
801 }
802
803 GtkWidget* RenderThemeGtk::gtkTreeView() const
804 {
805     if (m_gtkTreeView)
806         return m_gtkTreeView;
807     m_gtkTreeView = gtk_tree_view_new();
808     setupWidgetAndAddToContainer(m_gtkTreeView, gtkContainer());
809     return m_gtkTreeView;
810 }
811
812 GtkWidget* RenderThemeGtk::gtkVScale() const
813 {
814     if (m_gtkVScale)
815         return m_gtkVScale;
816     m_gtkVScale = gtk_vscale_new(0);
817     setupWidgetAndAddToContainer(m_gtkVScale, gtkContainer());
818     return m_gtkVScale;
819 }
820
821 GtkWidget* RenderThemeGtk::gtkHScale() const
822 {
823     if (m_gtkHScale)
824         return m_gtkHScale;
825     m_gtkHScale = gtk_hscale_new(0);
826     setupWidgetAndAddToContainer(m_gtkHScale, gtkContainer());
827     return m_gtkHScale;
828 }
829
830 GtkWidget* RenderThemeGtk::gtkScrollbar()
831 {
832     return moz_gtk_get_scrollbar_widget();
833 }
834
835 void RenderThemeGtk::platformColorsDidChange()
836 {
837 #if ENABLE(VIDEO)
838     initMediaColors();
839 #endif
840     RenderTheme::platformColorsDidChange();
841 }
842
843 #if ENABLE(VIDEO)
844 String RenderThemeGtk::extraMediaControlsStyleSheet()
845 {
846     return String(mediaControlsGtkUserAgentStyleSheet, sizeof(mediaControlsGtkUserAgentStyleSheet));
847 }
848
849 bool RenderThemeGtk::paintMediaButton(RenderObject* renderObject, GraphicsContext* context, const IntRect& rect, const char* iconName)
850 {
851     GtkStyle* style = gtk_widget_get_style(GTK_WIDGET(gtkContainer()));
852     IntPoint iconPoint(rect.x() + (rect.width() - m_mediaIconSize) / 2,
853                        rect.y() + (rect.height() - m_mediaIconSize) / 2);
854     context->fillRect(FloatRect(rect), m_panelColor, ColorSpaceDeviceRGB);
855     paintStockIcon(context, iconPoint, style, iconName, gtkTextDirection(renderObject->style()->direction()),
856                    gtkIconState(renderObject), getMediaButtonIconSize(m_mediaIconSize));
857
858     return false;
859 }
860
861 bool RenderThemeGtk::paintMediaFullscreenButton(RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect)
862 {
863     return paintMediaButton(renderObject, paintInfo.context, rect, GTK_STOCK_FULLSCREEN);
864 }
865
866 bool RenderThemeGtk::paintMediaMuteButton(RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect)
867 {
868     HTMLMediaElement* mediaElement = getMediaElementFromRenderObject(renderObject);
869     if (!mediaElement)
870         return false;
871
872     return paintMediaButton(renderObject, paintInfo.context, rect, mediaElement->muted() ? "audio-volume-muted" : "audio-volume-high");
873 }
874
875 bool RenderThemeGtk::paintMediaPlayButton(RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect)
876 {
877     Node* node = renderObject->node();
878     if (!node)
879         return false;
880
881     MediaControlPlayButtonElement* button = static_cast<MediaControlPlayButtonElement*>(node);
882     return paintMediaButton(renderObject, paintInfo.context, rect, button->displayType() == MediaPlayButton ? GTK_STOCK_MEDIA_PLAY : GTK_STOCK_MEDIA_PAUSE);
883 }
884
885 bool RenderThemeGtk::paintMediaSeekBackButton(RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect)
886 {
887     return paintMediaButton(renderObject, paintInfo.context, rect, GTK_STOCK_MEDIA_REWIND);
888 }
889
890 bool RenderThemeGtk::paintMediaSeekForwardButton(RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect)
891 {
892     return paintMediaButton(renderObject, paintInfo.context, rect, GTK_STOCK_MEDIA_FORWARD);
893 }
894
895 bool RenderThemeGtk::paintMediaSliderTrack(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r)
896 {
897     GraphicsContext* context = paintInfo.context;
898
899     context->fillRect(FloatRect(r), m_panelColor, ColorSpaceDeviceRGB);
900     context->fillRect(FloatRect(IntRect(r.x(), r.y() + (r.height() - m_mediaSliderHeight) / 2,
901                                         r.width(), m_mediaSliderHeight)), m_sliderColor, ColorSpaceDeviceRGB);
902
903     RenderStyle* style = o->style();
904     HTMLMediaElement* mediaElement = toParentMediaElement(o);
905
906     if (!mediaElement)
907         return false;
908
909     // Draw the buffered ranges. This code is highly inspired from
910     // Chrome for the gradient code.
911     float mediaDuration = mediaElement->duration();
912     RefPtr<TimeRanges> timeRanges = mediaElement->buffered();
913     IntRect trackRect = r;
914     int totalWidth = trackRect.width();
915
916     trackRect.inflate(-style->borderLeftWidth());
917     context->save();
918     context->setStrokeStyle(NoStroke);
919
920     for (unsigned index = 0; index < timeRanges->length(); ++index) {
921         ExceptionCode ignoredException;
922         float start = timeRanges->start(index, ignoredException);
923         float end = timeRanges->end(index, ignoredException);
924         int width = ((end - start) * totalWidth) / mediaDuration;
925         IntRect rangeRect;
926         if (!index) {
927             rangeRect = trackRect;
928             rangeRect.setWidth(width);
929         } else {
930             rangeRect.setLocation(IntPoint(trackRect.x() + start / mediaDuration* totalWidth, trackRect.y()));
931             rangeRect.setSize(IntSize(width, trackRect.height()));
932         }
933
934         // Don't bother drawing empty range.
935         if (rangeRect.isEmpty())
936             continue;
937
938         IntPoint sliderTopLeft = rangeRect.location();
939         IntPoint sliderTopRight = sliderTopLeft;
940         sliderTopRight.move(0, rangeRect.height());
941
942         RefPtr<Gradient> gradient = Gradient::create(sliderTopLeft, sliderTopRight);
943         Color startColor = m_panelColor;
944         gradient->addColorStop(0.0, startColor);
945         gradient->addColorStop(1.0, Color(startColor.red() / 2, startColor.green() / 2, startColor.blue() / 2, startColor.alpha()));
946
947         context->setFillGradient(gradient);
948         context->fillRect(rangeRect);
949     }
950
951     context->restore();
952     return false;
953 }
954
955 bool RenderThemeGtk::paintMediaSliderThumb(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r)
956 {
957     // Make the thumb nicer with rounded corners.
958     paintInfo.context->fillRoundedRect(r, IntSize(3, 3), IntSize(3, 3), IntSize(3, 3), IntSize(3, 3), m_sliderThumbColor, ColorSpaceDeviceRGB);
959     return false;
960 }
961 #endif
962
963 #if ENABLE(PROGRESS_TAG)
964 double RenderThemeGtk::animationRepeatIntervalForProgressBar(RenderProgress*) const
965 {
966     // FIXME: It doesn't look like there is a good way yet to support animated
967     // progress bars with the Mozilla theme drawing code.
968     return 0;
969 }
970
971 double RenderThemeGtk::animationDurationForProgressBar(RenderProgress*) const
972 {
973     // FIXME: It doesn't look like there is a good way yet to support animated
974     // progress bars with the Mozilla theme drawing code.
975     return 0;
976 }
977
978 void RenderThemeGtk::adjustProgressBarStyle(CSSStyleSelector*, RenderStyle* style, Element*) const
979 {
980     style->setBoxShadow(0);
981 }
982
983 bool RenderThemeGtk::paintProgressBar(RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect)
984 {
985     if (!renderObject->isProgress())
986         return true;
987
988     GtkWidget* progressBarWidget = moz_gtk_get_progress_widget();
989     if (!progressBarWidget)
990         return true;
991
992     if (paintRenderObject(MOZ_GTK_PROGRESSBAR, renderObject, paintInfo.context, rect))
993         return true;
994
995     IntRect chunkRect(rect);
996     RenderProgress* renderProgress = toRenderProgress(renderObject);
997
998     GtkStyle* style = gtk_widget_get_style(progressBarWidget);
999     chunkRect.setHeight(chunkRect.height() - (2 * style->ythickness));
1000     chunkRect.setY(chunkRect.y() + style->ythickness);
1001     chunkRect.setWidth((chunkRect.width() - (2 * style->xthickness)) * renderProgress->position());
1002     if (renderObject->style()->direction() == RTL)
1003         chunkRect.setX(rect.x() + rect.width() - chunkRect.width() - style->xthickness);
1004     else
1005         chunkRect.setX(chunkRect.x() + style->xthickness);
1006
1007     return paintRenderObject(MOZ_GTK_PROGRESS_CHUNK, renderObject, paintInfo.context, chunkRect);
1008 }
1009 #endif
1010
1011 }