Replace all RenderTheme::popupInternalPadding methods with a single one returning...
[WebKit-https.git] / Source / WebCore / rendering / 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 "GRefPtrGtk.h"
34 #include "GUniquePtrGtk.h"
35 #include "Gradient.h"
36 #include "GraphicsContext.h"
37 #include "GtkVersioning.h"
38 #include "HTMLInputElement.h"
39 #include "HTMLMediaElement.h"
40 #include "LocalizedStrings.h"
41 #include "MediaControlElements.h"
42 #include "NamedNodeMap.h"
43 #include "Page.h"
44 #include "PaintInfo.h"
45 #include "PlatformContextCairo.h"
46 #include "RenderBox.h"
47 #include "RenderObject.h"
48 #include "RenderProgress.h"
49 #include "ScrollbarThemeGtk.h"
50 #include "StringTruncator.h"
51 #include "TimeRanges.h"
52 #include "UserAgentScripts.h"
53 #include "UserAgentStyleSheets.h"
54 #include <cmath>
55 #include <gdk/gdk.h>
56 #include <glib.h>
57 #include <gtk/gtk.h>
58 #include <wtf/NeverDestroyed.h>
59 #include <wtf/glib/GRefPtr.h>
60 #include <wtf/glib/GUniquePtr.h>
61 #include <wtf/text/CString.h>
62 #include <wtf/text/StringBuilder.h>
63
64 namespace WebCore {
65
66 Ref<RenderTheme> RenderThemeGtk::create()
67 {
68     return adoptRef(*new RenderThemeGtk());
69 }
70
71 PassRefPtr<RenderTheme> RenderTheme::themeForPage(Page*)
72 {
73     static RenderTheme& rt = RenderThemeGtk::create().leakRef();
74     return &rt;
75 }
76
77 static double getScreenDPI()
78 {
79     // FIXME: Really this should be the widget's screen.
80     GdkScreen* screen = gdk_screen_get_default();
81     if (!screen)
82         return 96; // Default to 96 DPI.
83
84     float dpi = gdk_screen_get_resolution(screen);
85     if (dpi <= 0)
86         return 96;
87     return dpi;
88 }
89
90 void RenderThemeGtk::updateCachedSystemFontDescription(CSSValueID, FontCascadeDescription& fontDescription) const
91 {
92     GtkSettings* settings = gtk_settings_get_default();
93     if (!settings)
94         return;
95
96     // This will be a font selection string like "Sans 10" so we cannot use it as the family name.
97     GUniqueOutPtr<gchar> fontName;
98     g_object_get(settings, "gtk-font-name", &fontName.outPtr(), nullptr);
99     if (!fontName || !fontName.get()[0])
100         return;
101
102     PangoFontDescription* pangoDescription = pango_font_description_from_string(fontName.get());
103     if (!pangoDescription)
104         return;
105
106     fontDescription.setOneFamily(pango_font_description_get_family(pangoDescription));
107
108     int size = pango_font_description_get_size(pangoDescription) / PANGO_SCALE;
109     // If the size of the font is in points, we need to convert it to pixels.
110     if (!pango_font_description_get_size_is_absolute(pangoDescription))
111         size = size * (getScreenDPI() / 72.0);
112
113     fontDescription.setSpecifiedSize(size);
114     fontDescription.setIsAbsoluteSize(true);
115     fontDescription.setWeight(FontWeightNormal);
116     fontDescription.setItalic(FontItalicOff);
117     pango_font_description_free(pangoDescription);
118 }
119
120 #if ENABLE(DATALIST_ELEMENT)
121 IntSize RenderThemeGtk::sliderTickSize() const
122 {
123     // FIXME: We need to set this to the size of one tick mark.
124     return IntSize(0, 0);
125 }
126
127 int RenderThemeGtk::sliderTickOffsetFromTrackCenter() const
128 {
129     // FIXME: We need to set this to the position of the tick marks.
130     return 0;
131 }
132 #endif
133
134 #ifndef GTK_API_VERSION_2
135
136 // This is the default value defined by GTK+, where it was defined as MIN_ARROW_SIZE in gtkarrow.c.
137 static const int minArrowSize = 15;
138 // This is the default value defined by GTK+, where it was defined as MIN_ARROW_WIDTH in gtkspinbutton.c.
139 static const int minSpinButtonArrowSize = 6;
140
141 enum RenderThemePart {
142     Entry,
143     EntrySelection,
144     EntryIconLeft,
145     EntryIconRight,
146     Button,
147     CheckButton,
148     CheckButtonCheck,
149     RadioButton,
150     RadioButtonRadio,
151     ComboBox,
152     ComboBoxButton,
153     ComboBoxArrow,
154     Scale,
155     ScaleContents,
156     ScaleTrough,
157     ScaleSlider,
158     ScaleHighlight,
159     ProgressBar,
160     ProgressBarTrough,
161     ProgressBarProgress,
162     ListBox,
163     SpinButton,
164     SpinButtonUpButton,
165     SpinButtonDownButton,
166 #if ENABLE(VIDEO)
167     MediaButton,
168 #endif
169 };
170
171 static void gtkStyleChangedCallback(GObject*, GParamSpec*)
172 {
173     Page::updateStyleForAllPagesAfterGlobalChangeInEnvironment();
174 }
175
176 static GRefPtr<GtkStyleContext> createStyleContext(RenderThemePart themePart, GtkStyleContext* parent = nullptr)
177 {
178     static bool initialized = false;
179     if (!initialized) {
180         GtkSettings* settings = gtk_settings_get_default();
181         g_signal_connect(settings, "notify::gtk-theme-name", G_CALLBACK(gtkStyleChangedCallback), nullptr);
182         g_signal_connect(settings, "notify::gtk-color-scheme", G_CALLBACK(gtkStyleChangedCallback), nullptr);
183         initialized = true;
184     }
185
186     GRefPtr<GtkWidgetPath> path = adoptGRef(parent ? gtk_widget_path_copy(gtk_style_context_get_path(parent)) : gtk_widget_path_new());
187
188     switch (themePart) {
189     case Entry:
190         gtk_widget_path_append_type(path.get(), GTK_TYPE_ENTRY);
191 #if GTK_CHECK_VERSION(3, 19, 2)
192         gtk_widget_path_iter_set_object_name(path.get(), -1, "entry");
193 #else
194         gtk_widget_path_iter_add_class(path.get(), -1, GTK_STYLE_CLASS_ENTRY);
195 #endif
196         break;
197     case EntrySelection:
198         gtk_widget_path_append_type(path.get(), GTK_TYPE_ENTRY);
199 #if GTK_CHECK_VERSION(3, 19, 2)
200         gtk_widget_path_iter_set_object_name(path.get(), -1, "selection");
201 #else
202         gtk_widget_path_iter_add_class(path.get(), -1, GTK_STYLE_CLASS_ENTRY);
203 #endif
204         break;
205     case EntryIconLeft:
206     case EntryIconRight:
207         gtk_widget_path_append_type(path.get(), GTK_TYPE_ENTRY);
208 #if GTK_CHECK_VERSION(3, 19, 2)
209         gtk_widget_path_iter_set_object_name(path.get(), -1, "image");
210         gtk_widget_path_iter_add_class(path.get(), -1, themePart == EntryIconLeft ? "left" : "right");
211 #endif
212         break;
213     case Button:
214         gtk_widget_path_append_type(path.get(), GTK_TYPE_BUTTON);
215 #if GTK_CHECK_VERSION(3, 19, 2)
216         gtk_widget_path_iter_set_object_name(path.get(), -1, "button");
217 #else
218         gtk_widget_path_iter_add_class(path.get(), -1, GTK_STYLE_CLASS_BUTTON);
219 #endif
220         gtk_widget_path_iter_add_class(path.get(), -1, "text-button");
221         break;
222     case CheckButton:
223         gtk_widget_path_append_type(path.get(), GTK_TYPE_CHECK_BUTTON);
224 #if GTK_CHECK_VERSION(3, 19, 2)
225         gtk_widget_path_iter_set_object_name(path.get(), -1, "checkbutton");
226 #else
227         gtk_widget_path_iter_add_class(path.get(), -1, GTK_STYLE_CLASS_CHECK);
228 #endif
229         break;
230     case CheckButtonCheck:
231         gtk_widget_path_append_type(path.get(), GTK_TYPE_CHECK_BUTTON);
232 #if GTK_CHECK_VERSION(3, 19, 2)
233         gtk_widget_path_iter_set_object_name(path.get(), -1, "check");
234 #else
235         gtk_widget_path_iter_add_class(path.get(), -1, GTK_STYLE_CLASS_CHECK);
236 #endif
237         break;
238     case RadioButton:
239         gtk_widget_path_append_type(path.get(), GTK_TYPE_RADIO_BUTTON);
240 #if GTK_CHECK_VERSION(3, 19, 2)
241         gtk_widget_path_iter_set_object_name(path.get(), -1, "radiobutton");
242 #else
243         gtk_widget_path_iter_add_class(path.get(), -1, GTK_STYLE_CLASS_RADIO);
244 #endif
245         break;
246     case RadioButtonRadio:
247         gtk_widget_path_append_type(path.get(), GTK_TYPE_RADIO_BUTTON);
248 #if GTK_CHECK_VERSION(3, 19, 2)
249         gtk_widget_path_iter_set_object_name(path.get(), -1, "radio");
250 #else
251         gtk_widget_path_iter_add_class(path.get(), -1, GTK_STYLE_CLASS_RADIO);
252 #endif
253         break;
254     case ComboBox:
255         gtk_widget_path_append_type(path.get(), GTK_TYPE_COMBO_BOX);
256 #if GTK_CHECK_VERSION(3, 19, 2)
257         gtk_widget_path_iter_set_object_name(path.get(), -1, "combobox");
258 #endif
259         break;
260     case ComboBoxButton:
261         gtk_widget_path_append_type(path.get(), GTK_TYPE_BUTTON);
262 #if GTK_CHECK_VERSION(3, 19, 2)
263         gtk_widget_path_iter_set_object_name(path.get(), -1, "button");
264 #else
265         gtk_widget_path_iter_add_class(path.get(), -1, GTK_STYLE_CLASS_BUTTON);
266 #endif
267         gtk_widget_path_iter_add_class(path.get(), -1, "text-button");
268         gtk_widget_path_iter_add_class(path.get(), -1, "combo");
269         break;
270     case ComboBoxArrow:
271         gtk_widget_path_append_type(path.get(), GTK_TYPE_ARROW);
272 #if GTK_CHECK_VERSION(3, 19, 2)
273         gtk_widget_path_iter_set_object_name(path.get(), -1, "arrow");
274 #else
275         gtk_widget_path_iter_add_class(path.get(), -1, "arrow");
276 #endif
277         break;
278     case Scale:
279         gtk_widget_path_append_type(path.get(), GTK_TYPE_SCALE);
280 #if GTK_CHECK_VERSION(3, 19, 2)
281         gtk_widget_path_iter_set_object_name(path.get(), -1, "scale");
282 #else
283         gtk_widget_path_iter_add_class(path.get(), -1, GTK_STYLE_CLASS_SCALE);
284 #endif
285         break;
286     case ScaleContents:
287 #if GTK_CHECK_VERSION(3, 19, 11)
288         gtk_widget_path_append_type(path.get(), GTK_TYPE_SCALE);
289         gtk_widget_path_iter_set_object_name(path.get(), -1, "contents");
290         gtk_widget_path_iter_add_class(path.get(), -1, GTK_STYLE_CLASS_SCALE);
291 #else
292         ASSERT_NOT_REACHED();
293 #endif
294         break;
295     case ScaleTrough:
296         gtk_widget_path_append_type(path.get(), GTK_TYPE_SCALE);
297 #if GTK_CHECK_VERSION(3, 19, 2)
298         gtk_widget_path_iter_set_object_name(path.get(), -1, "trough");
299 #else
300         gtk_widget_path_iter_add_class(path.get(), -1, GTK_STYLE_CLASS_SCALE);
301         gtk_widget_path_iter_add_class(path.get(), -1, GTK_STYLE_CLASS_TROUGH);
302 #endif
303         break;
304     case ScaleSlider:
305         gtk_widget_path_append_type(path.get(), GTK_TYPE_SCALE);
306 #if GTK_CHECK_VERSION(3, 19, 2)
307         gtk_widget_path_iter_set_object_name(path.get(), -1, "slider");
308 #else
309         gtk_widget_path_iter_add_class(path.get(), -1, GTK_STYLE_CLASS_SCALE);
310         gtk_widget_path_iter_add_class(path.get(), -1, GTK_STYLE_CLASS_SLIDER);
311 #endif
312         break;
313     case ScaleHighlight:
314 #if GTK_CHECK_VERSION(3, 19, 11)
315         gtk_widget_path_append_type(path.get(), GTK_TYPE_SCALE);
316         gtk_widget_path_iter_set_object_name(path.get(), -1, "highlight");
317         gtk_widget_path_iter_add_class(path.get(), -1, GTK_STYLE_CLASS_SCALE);
318 #else
319         ASSERT_NOT_REACHED();
320 #endif
321         break;
322     case ProgressBar:
323         gtk_widget_path_append_type(path.get(), GTK_TYPE_PROGRESS_BAR);
324 #if GTK_CHECK_VERSION(3, 19, 2)
325         gtk_widget_path_iter_set_object_name(path.get(), -1, "progressbar");
326 #else
327         gtk_widget_path_iter_add_class(path.get(), -1, GTK_STYLE_CLASS_PROGRESSBAR);
328 #endif
329         gtk_widget_path_iter_add_class(path.get(), -1, GTK_STYLE_CLASS_HORIZONTAL);
330         break;
331     case ProgressBarTrough:
332         gtk_widget_path_append_type(path.get(), GTK_TYPE_PROGRESS_BAR);
333 #if GTK_CHECK_VERSION(3, 19, 2)
334         gtk_widget_path_iter_set_object_name(path.get(), -1, "trough");
335 #else
336         gtk_widget_path_iter_add_class(path.get(), -1, GTK_STYLE_CLASS_PROGRESSBAR);
337         gtk_widget_path_iter_add_class(path.get(), -1, GTK_STYLE_CLASS_TROUGH);
338 #endif
339         break;
340     case ProgressBarProgress:
341         gtk_widget_path_append_type(path.get(), GTK_TYPE_PROGRESS_BAR);
342 #if GTK_CHECK_VERSION(3, 19, 2)
343         gtk_widget_path_iter_set_object_name(path.get(), -1, "progress");
344 #else
345         gtk_widget_path_iter_add_class(path.get(), -1, GTK_STYLE_CLASS_PROGRESSBAR);
346 #endif
347         break;
348     case ListBox:
349         gtk_widget_path_append_type(path.get(), GTK_TYPE_TREE_VIEW);
350 #if GTK_CHECK_VERSION(3, 19, 2)
351         gtk_widget_path_iter_set_object_name(path.get(), -1, "treeview");
352 #endif
353         gtk_widget_path_iter_add_class(path.get(), -1, GTK_STYLE_CLASS_VIEW);
354         break;
355     case SpinButton:
356         gtk_widget_path_append_type(path.get(), GTK_TYPE_SPIN_BUTTON);
357 #if GTK_CHECK_VERSION(3, 19, 2)
358         gtk_widget_path_iter_set_object_name(path.get(), -1, "spinbutton");
359 #else
360         gtk_widget_path_iter_add_class(path.get(), -1, GTK_STYLE_CLASS_SPINBUTTON);
361 #endif
362         gtk_widget_path_iter_add_class(path.get(), -1, GTK_STYLE_CLASS_HORIZONTAL);
363         break;
364     case SpinButtonUpButton:
365     case SpinButtonDownButton:
366         gtk_widget_path_append_type(path.get(), GTK_TYPE_SPIN_BUTTON);
367 #if GTK_CHECK_VERSION(3, 19, 2)
368         gtk_widget_path_iter_set_object_name(path.get(), -1, "button");
369         gtk_widget_path_iter_add_class(path.get(), -1, themePart == SpinButtonUpButton ? "up" : "down");
370 #else
371         gtk_widget_path_iter_add_class(path.get(), -1, GTK_STYLE_CLASS_SPINBUTTON);
372         gtk_widget_path_iter_add_class(path.get(), -1, GTK_STYLE_CLASS_BUTTON);
373 #endif
374         break;
375 #if ENABLE(VIDEO)
376     case MediaButton:
377         gtk_widget_path_append_type(path.get(), GTK_TYPE_IMAGE);
378 #if GTK_CHECK_VERSION(3, 19, 2)
379         gtk_widget_path_iter_set_object_name(path.get(), -1, "image");
380 #else
381         gtk_widget_path_iter_add_class(path.get(), -1, GTK_STYLE_CLASS_IMAGE);
382 #endif
383         break;
384 #endif // ENABLE(VIDEO)
385     }
386
387     GRefPtr<GtkStyleContext> context = adoptGRef(gtk_style_context_new());
388     gtk_style_context_set_path(context.get(), path.get());
389     gtk_style_context_set_parent(context.get(), parent);
390     return context;
391 }
392
393 static GRefPtr<GdkPixbuf> loadThemedIcon(GtkStyleContext* context, const char* iconName, GtkIconSize iconSize)
394 {
395     GRefPtr<GIcon> icon = adoptGRef(g_themed_icon_new(iconName));
396     unsigned lookupFlags = GTK_ICON_LOOKUP_USE_BUILTIN | GTK_ICON_LOOKUP_FORCE_SIZE | GTK_ICON_LOOKUP_FORCE_SVG;
397 #if GTK_CHECK_VERSION(3, 14, 0)
398     GtkTextDirection direction = gtk_style_context_get_direction(context);
399     if (direction & GTK_TEXT_DIR_LTR)
400         lookupFlags |= GTK_ICON_LOOKUP_DIR_LTR;
401     else if (direction & GTK_TEXT_DIR_RTL)
402         lookupFlags |= GTK_ICON_LOOKUP_DIR_RTL;
403 #endif
404     int width, height;
405     gtk_icon_size_lookup(iconSize, &width, &height);
406     GRefPtr<GtkIconInfo> iconInfo = adoptGRef(gtk_icon_theme_lookup_by_gicon(gtk_icon_theme_get_default(), icon.get(), std::min(width, height), static_cast<GtkIconLookupFlags>(lookupFlags)));
407     if (!iconInfo)
408         return nullptr;
409
410     return adoptGRef(gtk_icon_info_load_symbolic_for_context(iconInfo.get(), context, nullptr, nullptr));
411 }
412
413 static bool nodeHasPseudo(Node* node, const char* pseudo)
414 {
415     RefPtr<Node> attributeNode = node->attributes()->getNamedItem("pseudo");
416
417     return attributeNode ? attributeNode->nodeValue() == pseudo : false;
418 }
419
420 static bool nodeHasClass(Node* node, const char* className)
421 {
422     if (!is<Element>(*node))
423         return false;
424
425     Element& element = downcast<Element>(*node);
426
427     if (!element.hasClass())
428         return false;
429
430     return element.classNames().contains(className);
431 }
432
433 RenderThemeGtk::~RenderThemeGtk()
434 {
435 }
436
437 static bool supportsFocus(ControlPart appearance)
438 {
439     switch (appearance) {
440     case PushButtonPart:
441     case ButtonPart:
442     case TextFieldPart:
443     case TextAreaPart:
444     case SearchFieldPart:
445     case MenulistPart:
446     case RadioPart:
447     case CheckboxPart:
448     case SliderHorizontalPart:
449     case SliderVerticalPart:
450         return true;
451     default:
452         return false;
453     }
454 }
455
456 bool RenderThemeGtk::supportsFocusRing(const RenderStyle& style) const
457 {
458     return supportsFocus(style.appearance());
459 }
460
461 bool RenderThemeGtk::controlSupportsTints(const RenderObject& o) const
462 {
463     return isEnabled(o);
464 }
465
466 int RenderThemeGtk::baselinePosition(const RenderBox& box) const
467 {
468     // FIXME: This strategy is possibly incorrect for the GTK+ port.
469     if (box.style().appearance() == CheckboxPart || box.style().appearance() == RadioPart)
470         return box.marginTop() + box.height() - 2;
471     return RenderTheme::baselinePosition(box);
472 }
473
474 static GtkTextDirection gtkTextDirection(TextDirection direction)
475 {
476     switch (direction) {
477     case RTL:
478         return GTK_TEXT_DIR_RTL;
479     case LTR:
480         return GTK_TEXT_DIR_LTR;
481     default:
482         return GTK_TEXT_DIR_NONE;
483     }
484 }
485
486 static GtkStateFlags gtkIconStateFlags(RenderTheme* theme, const RenderObject& renderObject)
487 {
488     if (!theme->isEnabled(renderObject))
489         return GTK_STATE_FLAG_INSENSITIVE;
490     if (theme->isPressed(renderObject))
491         return GTK_STATE_FLAG_ACTIVE;
492     if (theme->isHovered(renderObject))
493         return GTK_STATE_FLAG_PRELIGHT;
494
495     return GTK_STATE_FLAG_NORMAL;
496 }
497
498 static void adjustRectForFocus(GtkStyleContext* context, FloatRect& rect)
499 {
500     gint focusWidth, focusPad;
501     gtk_style_context_get_style(context, "focus-line-width", &focusWidth, "focus-padding", &focusPad, nullptr);
502     rect.inflate(focusWidth + focusPad);
503 }
504
505 void RenderThemeGtk::adjustRepaintRect(const RenderObject& renderObject, FloatRect& rect)
506 {
507     GRefPtr<GtkStyleContext> context;
508     bool checkInteriorFocus = false;
509     ControlPart part = renderObject.style().appearance();
510     switch (part) {
511     case CheckboxPart:
512     case RadioPart:
513         context = createStyleContext(part == CheckboxPart ? CheckButton : RadioButton);
514
515         gint indicatorSpacing;
516         gtk_style_context_get_style(context.get(), "indicator-spacing", &indicatorSpacing, nullptr);
517         rect.inflate(indicatorSpacing);
518
519         return;
520     case SliderVerticalPart:
521     case SliderHorizontalPart:
522         context = createStyleContext(ScaleSlider);
523         break;
524     case ButtonPart:
525     case MenulistButtonPart:
526     case MenulistPart:
527         context = createStyleContext(Button);
528         checkInteriorFocus = true;
529         break;
530     case TextFieldPart:
531     case TextAreaPart:
532         context = createStyleContext(Entry);
533         checkInteriorFocus = true;
534         break;
535     default:
536         return;
537     }
538
539     ASSERT(context);
540     if (checkInteriorFocus) {
541         gboolean interiorFocus;
542         gtk_style_context_get_style(context.get(), "interior-focus", &interiorFocus, nullptr);
543         if (interiorFocus)
544             return;
545     }
546     adjustRectForFocus(context.get(), rect);
547 }
548
549 void RenderThemeGtk::adjustButtonStyle(StyleResolver&, RenderStyle& style, Element*) const
550 {
551     // Some layout tests check explicitly that buttons ignore line-height.
552     if (style.appearance() == PushButtonPart)
553         style.setLineHeight(RenderStyle::initialLineHeight());
554 }
555
556 static void setToggleSize(RenderThemePart themePart, RenderStyle& style)
557 {
558     // The width and height are both specified, so we shouldn't change them.
559     if (!style.width().isIntrinsicOrAuto() && !style.height().isAuto())
560         return;
561
562     GRefPtr<GtkStyleContext> context = createStyleContext(themePart);
563     // Other ports hard-code this to 13. GTK+ users tend to demand the native look.
564     gint indicatorSize;
565     gtk_style_context_get_style(context.get(), "indicator-size", &indicatorSize, nullptr);
566     IntSize minSize(indicatorSize, indicatorSize);
567
568 #if GTK_CHECK_VERSION(3, 19, 7)
569     GRefPtr<GtkStyleContext> childContext = createStyleContext(themePart == CheckButton ? CheckButtonCheck : RadioButtonRadio, context.get());
570     gint minWidth, minHeight;
571     gtk_style_context_get(childContext.get(), gtk_style_context_get_state(childContext.get()), "min-width", &minWidth, "min-height", &minHeight, nullptr);
572     if (minWidth)
573         minSize.setWidth(minWidth);
574     if (minHeight)
575         minSize.setHeight(minHeight);
576 #endif
577
578     if (style.width().isIntrinsicOrAuto())
579         style.setWidth(Length(minSize.width(), Fixed));
580
581     if (style.height().isAuto())
582         style.setHeight(Length(minSize.height(), Fixed));
583 }
584
585 static void paintToggle(const RenderThemeGtk* theme, RenderThemePart themePart, const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& fullRect)
586 {
587     GRefPtr<GtkStyleContext> parentContext = createStyleContext(themePart);
588     GRefPtr<GtkStyleContext> context = createStyleContext(themePart == CheckButton ? CheckButtonCheck : RadioButtonRadio, parentContext.get());
589     gtk_style_context_set_direction(context.get(), static_cast<GtkTextDirection>(gtkTextDirection(renderObject.style().direction())));
590
591     unsigned flags = 0;
592     if (!theme->isEnabled(renderObject))
593         flags |= GTK_STATE_FLAG_INSENSITIVE;
594     else if (theme->isHovered(renderObject))
595         flags |= GTK_STATE_FLAG_PRELIGHT;
596     if (theme->isIndeterminate(renderObject))
597         flags |= GTK_STATE_FLAG_INCONSISTENT;
598     else if (theme->isChecked(renderObject))
599 #if GTK_CHECK_VERSION(3, 13, 7)
600         flags |= GTK_STATE_FLAG_CHECKED;
601 #else
602         flags |= GTK_STATE_FLAG_ACTIVE;
603 #endif
604     if (theme->isPressed(renderObject))
605         flags |= GTK_STATE_FLAG_SELECTED;
606     gtk_style_context_set_state(context.get(), static_cast<GtkStateFlags>(flags));
607
608     // Some themes do not render large toggle buttons properly, so we simply
609     // shrink the rectangle back down to the default size and then center it
610     // in the full toggle button region. The reason for not simply forcing toggle
611     // buttons to be a smaller size is that we don't want to break site layouts.
612     gint indicatorSize;
613     gtk_style_context_get_style(context.get(), "indicator-size", &indicatorSize, nullptr);
614     IntSize minSize(indicatorSize, indicatorSize);
615
616 #if GTK_CHECK_VERSION(3, 19, 7)
617     gint minWidth, minHeight;
618     gtk_style_context_get(context.get(), gtk_style_context_get_state(context.get()), "min-width", &minWidth, "min-height", &minHeight, nullptr);
619     if (minWidth)
620         minSize.setWidth(minWidth);
621     if (minHeight)
622         minSize.setHeight(minHeight);
623 #endif
624
625     IntRect rect(fullRect);
626     if (rect.width() > minSize.width()) {
627         rect.inflateX(-(rect.width() - minSize.width()) / 2);
628         rect.setWidth(minSize.width()); // In case rect.width() was equal to minSize.width() + 1.
629     }
630
631     if (rect.height() > minSize.height()) {
632         rect.inflateY(-(rect.height() - minSize.height()) / 2);
633         rect.setHeight(minSize.height()); // In case rect.height() was equal to minSize.height() + 1.
634     }
635
636     gtk_render_background(context.get(), paintInfo.context().platformContext()->cr(), rect.x(), rect.y(), rect.width(), rect.height());
637     gtk_render_frame(context.get(), paintInfo.context().platformContext()->cr(), rect.x(), rect.y(), rect.width(), rect.height());
638
639     if (themePart == CheckButton)
640         gtk_render_check(context.get(), paintInfo.context().platformContext()->cr(), rect.x(), rect.y(), rect.width(), rect.height());
641     else
642         gtk_render_option(context.get(), paintInfo.context().platformContext()->cr(), rect.x(), rect.y(), rect.width(), rect.height());
643
644     if (theme->isFocused(renderObject)) {
645         IntRect indicatorRect(rect);
646         gint indicatorSpacing;
647         gtk_style_context_get_style(context.get(), "indicator-spacing", &indicatorSpacing, nullptr);
648         indicatorRect.inflate(indicatorSpacing);
649         gtk_render_focus(context.get(), paintInfo.context().platformContext()->cr(), indicatorRect.x(), indicatorRect.y(),
650             indicatorRect.width(), indicatorRect.height());
651     }
652 }
653
654 void RenderThemeGtk::setCheckboxSize(RenderStyle& style) const
655 {
656     setToggleSize(CheckButton, style);
657 }
658
659 bool RenderThemeGtk::paintCheckbox(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
660 {
661     paintToggle(this, CheckButton, renderObject, paintInfo, rect);
662     return false;
663 }
664
665 void RenderThemeGtk::setRadioSize(RenderStyle& style) const
666 {
667     setToggleSize(RadioButton, style);
668 }
669
670 bool RenderThemeGtk::paintRadio(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
671 {
672     paintToggle(this, RadioButton, renderObject, paintInfo, rect);
673     return false;
674 }
675
676 static void renderButton(RenderTheme* theme, GtkStyleContext* context, const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
677 {
678     IntRect buttonRect(rect);
679
680     guint flags = 0;
681     if (!theme->isEnabled(renderObject))
682         flags |= GTK_STATE_FLAG_INSENSITIVE;
683     else if (theme->isHovered(renderObject))
684         flags |= GTK_STATE_FLAG_PRELIGHT;
685     if (theme->isPressed(renderObject))
686         flags |= GTK_STATE_FLAG_ACTIVE;
687     gtk_style_context_set_state(context, static_cast<GtkStateFlags>(flags));
688
689     if (theme->isDefault(renderObject)) {
690         GtkBorder* borderPtr = 0;
691         GtkBorder border = { 1, 1, 1, 1 };
692
693         gtk_style_context_get_style(context, "default-border", &borderPtr, nullptr);
694         if (borderPtr) {
695             border = *borderPtr;
696             gtk_border_free(borderPtr);
697         }
698
699         buttonRect.move(border.left, border.top);
700         buttonRect.setWidth(buttonRect.width() - (border.left + border.right));
701         buttonRect.setHeight(buttonRect.height() - (border.top + border.bottom));
702
703         gtk_style_context_add_class(context, GTK_STYLE_CLASS_DEFAULT);
704     }
705
706     gtk_render_background(context, paintInfo.context().platformContext()->cr(), buttonRect.x(), buttonRect.y(), buttonRect.width(), buttonRect.height());
707     gtk_render_frame(context, paintInfo.context().platformContext()->cr(), buttonRect.x(), buttonRect.y(), buttonRect.width(), buttonRect.height());
708
709     if (theme->isFocused(renderObject)) {
710         gint focusWidth, focusPad;
711         gboolean displaceFocus, interiorFocus;
712         gtk_style_context_get_style(
713             context,
714             "focus-line-width", &focusWidth,
715             "focus-padding", &focusPad,
716             "interior-focus", &interiorFocus,
717             "displace-focus", &displaceFocus,
718             nullptr);
719
720         if (interiorFocus) {
721             GtkBorder borderWidth;
722             gtk_style_context_get_border(context, gtk_style_context_get_state(context), &borderWidth);
723
724             buttonRect = IntRect(
725                 buttonRect.x() + borderWidth.left + focusPad,
726                 buttonRect.y() + borderWidth.top + focusPad,
727                 buttonRect.width() - (2 * focusPad + borderWidth.left + borderWidth.right),
728                 buttonRect.height() - (2 * focusPad + borderWidth.top + borderWidth.bottom));
729         } else
730             buttonRect.inflate(focusWidth + focusPad);
731
732         if (displaceFocus && theme->isPressed(renderObject)) {
733             gint childDisplacementX;
734             gint childDisplacementY;
735             gtk_style_context_get_style(context, "child-displacement-x", &childDisplacementX, "child-displacement-y", &childDisplacementY, nullptr);
736             buttonRect.move(childDisplacementX, childDisplacementY);
737         }
738
739         gtk_render_focus(context, paintInfo.context().platformContext()->cr(), buttonRect.x(), buttonRect.y(), buttonRect.width(), buttonRect.height());
740     }
741 }
742 bool RenderThemeGtk::paintButton(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
743 {
744     GRefPtr<GtkStyleContext> context = createStyleContext(Button);
745     gtk_style_context_set_direction(context.get(), static_cast<GtkTextDirection>(gtkTextDirection(renderObject.style().direction())));
746     renderButton(this, context.get(), renderObject, paintInfo, rect);
747     return false;
748 }
749
750 void RenderThemeGtk::adjustMenuListStyle(StyleResolver&, RenderStyle& style, Element*) const
751 {
752     // The tests check explicitly that select menu buttons ignore line height.
753     style.setLineHeight(RenderStyle::initialLineHeight());
754
755     // We cannot give a proper rendering when border radius is active, unfortunately.
756     style.resetBorderRadius();
757 }
758
759 void RenderThemeGtk::adjustMenuListButtonStyle(StyleResolver& styleResolver, RenderStyle& style, Element* e) const
760 {
761     adjustMenuListStyle(styleResolver, style, e);
762 }
763
764 LengthBox RenderThemeGtk::popupInternalPaddingBox(const RenderStyle& style) const
765 {
766     if (style.appearance() == NoControlPart)
767         return { 0, 0, 0, 0 };
768
769     GRefPtr<GtkStyleContext> parentContext = createStyleContext(ComboBox);
770     GRefPtr<GtkStyleContext> context = createStyleContext(ComboBoxButton, parentContext.get());
771     gtk_style_context_set_direction(context.get(), static_cast<GtkTextDirection>(gtkTextDirection(style.direction())));
772     gtk_style_context_set_state(context.get(), static_cast<GtkStateFlags>(0));
773     GtkBorder borderWidth = { 0, 0, 0, 0 };
774     gtk_style_context_get_border(context.get(), gtk_style_context_get_state(context.get()), &borderWidth);
775
776     gboolean interiorFocus;
777     gint focusWidth, focusPad;
778     gtk_style_context_get_style(context.get(), "interior-focus", &interiorFocus, "focus-line-width", &focusWidth, "focus-padding", &focusPad, nullptr);
779     focusWidth = interiorFocus ? focusWidth + focusPad : 0;
780
781     return { borderWidth.top + focusWidth, borderWidth.right + focusWidth + (style.direction() == LTR ? minArrowSize : 0),
782         borderWidth.bottom + focusWidth, borderWidth.left + focusWidth + (style.direction() == RTL ? minArrowSize : 0) };
783 }
784
785 bool RenderThemeGtk::paintMenuList(const RenderObject& renderObject, const PaintInfo& paintInfo, const FloatRect& r)
786 {
787     // FIXME: adopt subpixel themes.
788     IntRect rect = IntRect(r);
789
790     cairo_t* cairoContext = paintInfo.context().platformContext()->cr();
791     GtkTextDirection direction = static_cast<GtkTextDirection>(gtkTextDirection(renderObject.style().direction()));
792
793     GRefPtr<GtkStyleContext> parentStyleContext = createStyleContext(ComboBox);
794
795     // Paint the button.
796     GRefPtr<GtkStyleContext> buttonStyleContext = createStyleContext(ComboBoxButton, parentStyleContext.get());
797     gtk_style_context_set_direction(buttonStyleContext.get(), direction);
798     renderButton(this, buttonStyleContext.get(), renderObject, paintInfo, rect);
799
800     // Get the inner rectangle.
801     gint focusWidth, focusPad;
802     GtkBorder* innerBorderPtr = 0;
803     GtkBorder innerBorder = { 1, 1, 1, 1 };
804     gtk_style_context_get_style(buttonStyleContext.get(), "inner-border", &innerBorderPtr, "focus-line-width", &focusWidth, "focus-padding", &focusPad, nullptr);
805     if (innerBorderPtr) {
806         innerBorder = *innerBorderPtr;
807         gtk_border_free(innerBorderPtr);
808     }
809
810     GtkBorder borderWidth;
811     GtkStateFlags state = gtk_style_context_get_state(buttonStyleContext.get());
812     gtk_style_context_get_border(buttonStyleContext.get(), state, &borderWidth);
813
814     focusWidth += focusPad;
815     IntRect innerRect(
816         rect.x() + innerBorder.left + borderWidth.left + focusWidth,
817         rect.y() + innerBorder.top + borderWidth.top + focusWidth,
818         rect.width() - borderWidth.left - borderWidth.right - innerBorder.left - innerBorder.right - (2 * focusWidth),
819         rect.height() - borderWidth.top - borderWidth.bottom - innerBorder.top - innerBorder.bottom - (2 * focusWidth));
820
821     if (isPressed(renderObject)) {
822         gint childDisplacementX;
823         gint childDisplacementY;
824         gtk_style_context_get_style(buttonStyleContext.get(), "child-displacement-x", &childDisplacementX, "child-displacement-y", &childDisplacementY, nullptr);
825         innerRect.move(childDisplacementX, childDisplacementY);
826     }
827     innerRect.setWidth(std::max(1, innerRect.width()));
828     innerRect.setHeight(std::max(1, innerRect.height()));
829
830     // Paint the arrow.
831     GRefPtr<GtkStyleContext> arrowStyleContext = createStyleContext(ComboBoxArrow, buttonStyleContext.get());
832     gtk_style_context_set_direction(arrowStyleContext.get(), direction);
833
834 #if GTK_CHECK_VERSION(3, 19, 2)
835     // arrow-scaling style property is now deprecated and ignored.
836     gfloat arrowScaling = 1.;
837 #else
838     gfloat arrowScaling;
839     gtk_style_context_get_style(parentStyleContext.get(), "arrow-scaling", &arrowScaling, nullptr);
840 #endif
841
842     IntSize arrowSize(minArrowSize, innerRect.height());
843     FloatPoint arrowPosition(innerRect.location());
844     if (direction == GTK_TEXT_DIR_LTR)
845         arrowPosition.move(innerRect.width() - arrowSize.width(), 0);
846
847     // GTK+ actually fetches the xalign and valign values from the widget, but since we
848     // don't have a widget here, we are just using the default xalign and valign values of 0.5.
849     gint extent = std::min(arrowSize.width(), arrowSize.height()) * arrowScaling;
850     arrowPosition.move((arrowSize.width() - extent) / 2, (arrowSize.height() - extent) / 2);
851
852     gtk_style_context_set_state(arrowStyleContext.get(), state);
853     gtk_render_arrow(arrowStyleContext.get(), cairoContext, G_PI, arrowPosition.x(), arrowPosition.y(), extent);
854
855     return false;
856 }
857
858 bool RenderThemeGtk::paintMenuListButtonDecorations(const RenderBox& object, const PaintInfo& info, const FloatRect& rect)
859 {
860     return paintMenuList(object, info, rect);
861 }
862
863 bool RenderThemeGtk::paintTextField(const RenderObject& renderObject, const PaintInfo& paintInfo, const FloatRect& rect)
864 {
865     GRefPtr<GtkStyleContext> context = createStyleContext(Entry);
866     gtk_style_context_set_direction(context.get(), static_cast<GtkTextDirection>(gtkTextDirection(renderObject.style().direction())));
867
868     guint flags = 0;
869     if (!isEnabled(renderObject) || isReadOnlyControl(renderObject))
870         flags |= GTK_STATE_FLAG_INSENSITIVE;
871     else if (isFocused(renderObject))
872         flags |= GTK_STATE_FLAG_FOCUSED;
873     gtk_style_context_set_state(context.get(), static_cast<GtkStateFlags>(flags));
874
875     gtk_render_background(context.get(), paintInfo.context().platformContext()->cr(), rect.x(), rect.y(), rect.width(), rect.height());
876     gtk_render_frame(context.get(), paintInfo.context().platformContext()->cr(), rect.x(), rect.y(), rect.width(), rect.height());
877
878     if (isFocused(renderObject) && isEnabled(renderObject)) {
879         gboolean interiorFocus;
880         gint focusWidth, focusPad;
881         gtk_style_context_get_style(context.get(), "interior-focus", &interiorFocus, "focus-line-width", &focusWidth, "focus-padding", &focusPad, nullptr);
882         if (!interiorFocus) {
883             IntRect focusRect(rect);
884             focusRect.inflate(focusWidth + focusPad);
885             gtk_render_focus(context.get(), paintInfo.context().platformContext()->cr(), focusRect.x(), focusRect.y(), focusRect.width(), focusRect.height());
886         }
887     }
888
889     return false;
890 }
891
892 bool RenderThemeGtk::paintTextArea(const RenderObject& o, const PaintInfo& i, const FloatRect& r)
893 {
894     return paintTextField(o, i, r);
895 }
896
897 // Defined in GTK+ (gtk/gtkiconfactory.c)
898 static const gint gtkIconSizeMenu = 16;
899 static const gint gtkIconSizeSmallToolbar = 18;
900 static const gint gtkIconSizeButton = 20;
901 static const gint gtkIconSizeLargeToolbar = 24;
902 static const gint gtkIconSizeDnd = 32;
903 static const gint gtkIconSizeDialog = 48;
904
905 static GtkIconSize getIconSizeForPixelSize(gint pixelSize)
906 {
907     if (pixelSize < gtkIconSizeSmallToolbar)
908         return GTK_ICON_SIZE_MENU;
909     if (pixelSize >= gtkIconSizeSmallToolbar && pixelSize < gtkIconSizeButton)
910         return GTK_ICON_SIZE_SMALL_TOOLBAR;
911     if (pixelSize >= gtkIconSizeButton && pixelSize < gtkIconSizeLargeToolbar)
912         return GTK_ICON_SIZE_BUTTON;
913     if (pixelSize >= gtkIconSizeLargeToolbar && pixelSize < gtkIconSizeDnd)
914         return GTK_ICON_SIZE_LARGE_TOOLBAR;
915     if (pixelSize >= gtkIconSizeDnd && pixelSize < gtkIconSizeDialog)
916         return GTK_ICON_SIZE_DND;
917
918     return GTK_ICON_SIZE_DIALOG;
919 }
920
921 void RenderThemeGtk::adjustSearchFieldResultsButtonStyle(StyleResolver& styleResolver, RenderStyle& style, Element* e) const
922 {
923     adjustSearchFieldCancelButtonStyle(styleResolver, style, e);
924 }
925
926 bool RenderThemeGtk::paintSearchFieldResultsButton(const RenderBox& o, const PaintInfo& i, const IntRect& rect)
927 {
928     return paintSearchFieldResultsDecorationPart(o, i, rect);
929 }
930
931 static void adjustSearchFieldIconStyle(RenderThemePart themePart, RenderStyle& style)
932 {
933     style.resetBorder();
934     style.resetPadding();
935
936     GRefPtr<GtkStyleContext> parentContext = createStyleContext(Entry);
937     GRefPtr<GtkStyleContext> context = createStyleContext(themePart, parentContext.get());
938
939     GtkBorder padding;
940     gtk_style_context_get_padding(context.get(), gtk_style_context_get_state(context.get()), &padding);
941
942     // Get the icon size based on the font size.
943     int fontSize = style.fontSize();
944     if (fontSize < gtkIconSizeMenu) {
945         style.setWidth(Length(fontSize + (padding.left + padding.right), Fixed));
946         style.setHeight(Length(fontSize + (padding.top + padding.bottom), Fixed));
947         return;
948     }
949     gint width = 0, height = 0;
950     gtk_icon_size_lookup(getIconSizeForPixelSize(fontSize), &width, &height);
951     style.setWidth(Length(width + (padding.left + padding.right), Fixed));
952     style.setHeight(Length(height + (padding.top + padding.bottom), Fixed));
953 }
954
955 void RenderThemeGtk::adjustSearchFieldResultsDecorationPartStyle(StyleResolver&, RenderStyle& style, Element*) const
956 {
957     adjustSearchFieldIconStyle(EntryIconLeft, style);
958 }
959
960 static IntRect centerRectVerticallyInParentInputElement(const RenderObject& renderObject, const IntRect& rect)
961 {
962     if (!renderObject.node())
963         return IntRect();
964
965     // Get the renderer of <input> element.
966     Node* input = renderObject.node()->shadowHost();
967     if (!input)
968         input = renderObject.node();
969     if (!is<RenderBox>(*input->renderer()))
970         return IntRect();
971
972     // If possible center the y-coordinate of the rect vertically in the parent input element.
973     // We also add one pixel here to ensure that the y coordinate is rounded up for box heights
974     // that are even, which looks in relation to the box text.
975     IntRect inputContentBox = downcast<RenderBox>(*input->renderer()).absoluteContentBox();
976
977     // Make sure the scaled decoration stays square and will fit in its parent's box.
978     int iconSize = std::min(inputContentBox.width(), std::min(inputContentBox.height(), rect.height()));
979     IntRect scaledRect(rect.x(), inputContentBox.y() + (inputContentBox.height() - iconSize + 1) / 2, iconSize, iconSize);
980     return scaledRect;
981 }
982
983 static bool paintIcon(GtkStyleContext* context, GraphicsContext& graphicsContext, const IntRect& rect, const char* iconName)
984 {
985     GRefPtr<GdkPixbuf> icon = loadThemedIcon(context, iconName, getIconSizeForPixelSize(rect.height()));
986     if (!icon)
987         return false;
988
989     if (gdk_pixbuf_get_width(icon.get()) > rect.width() || gdk_pixbuf_get_height(icon.get()) > rect.height())
990         icon = adoptGRef(gdk_pixbuf_scale_simple(icon.get(), rect.width(), rect.height(), GDK_INTERP_BILINEAR));
991
992     gtk_render_icon(context, graphicsContext.platformContext()->cr(), icon.get(), rect.x(), rect.y());
993     return true;
994 }
995
996 static bool paintEntryIcon(RenderThemePart themePart, const char* iconName, GraphicsContext& graphicsContext, const IntRect& rect, GtkTextDirection direction, GtkStateFlags state)
997 {
998     GRefPtr<GtkStyleContext> parentContext = createStyleContext(Entry);
999     GRefPtr<GtkStyleContext> context = createStyleContext(themePart, parentContext.get());
1000     gtk_style_context_set_direction(context.get(), direction);
1001     gtk_style_context_set_state(context.get(), state);
1002     return paintIcon(context.get(), graphicsContext, rect, iconName);
1003 }
1004
1005 bool RenderThemeGtk::paintSearchFieldResultsDecorationPart(const RenderBox& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
1006 {
1007     IntRect iconRect = centerRectVerticallyInParentInputElement(renderObject, rect);
1008     if (iconRect.isEmpty())
1009         return true;
1010
1011     return !paintEntryIcon(EntryIconLeft, "edit-find-symbolic", paintInfo.context(), iconRect, gtkTextDirection(renderObject.style().direction()),
1012         gtkIconStateFlags(this, renderObject));
1013 }
1014
1015 void RenderThemeGtk::adjustSearchFieldCancelButtonStyle(StyleResolver&, RenderStyle& style, Element*) const
1016 {
1017     adjustSearchFieldIconStyle(EntryIconRight, style);
1018 }
1019
1020 bool RenderThemeGtk::paintSearchFieldCancelButton(const RenderBox& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
1021 {
1022     IntRect iconRect = centerRectVerticallyInParentInputElement(renderObject, rect);
1023     if (iconRect.isEmpty())
1024         return true;
1025
1026     return !paintEntryIcon(EntryIconRight, "edit-clear-symbolic", paintInfo.context(), iconRect, gtkTextDirection(renderObject.style().direction()),
1027         gtkIconStateFlags(this, renderObject));
1028 }
1029
1030 void RenderThemeGtk::adjustSearchFieldStyle(StyleResolver&, RenderStyle& style, Element*) const
1031 {
1032     // We cannot give a proper rendering when border radius is active, unfortunately.
1033     style.resetBorderRadius();
1034     style.setLineHeight(RenderStyle::initialLineHeight());
1035 }
1036
1037 bool RenderThemeGtk::paintSearchField(const RenderObject& o, const PaintInfo& i, const IntRect& rect)
1038 {
1039     return paintTextField(o, i, rect);
1040 }
1041
1042 bool RenderThemeGtk::shouldHaveCapsLockIndicator(HTMLInputElement& element) const
1043 {
1044     return element.isPasswordField();
1045 }
1046
1047 void RenderThemeGtk::adjustSliderTrackStyle(StyleResolver&, RenderStyle& style, Element*) const
1048 {
1049     style.setBoxShadow(nullptr);
1050 }
1051
1052 void RenderThemeGtk::adjustSliderThumbStyle(StyleResolver& styleResolver, RenderStyle& style, Element* element) const
1053 {
1054     RenderTheme::adjustSliderThumbStyle(styleResolver, style, element);
1055     style.setBoxShadow(nullptr);
1056 }
1057
1058 bool RenderThemeGtk::paintSliderTrack(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
1059 {
1060     ControlPart part = renderObject.style().appearance();
1061     ASSERT(part == SliderHorizontalPart || part == SliderVerticalPart);
1062
1063     GRefPtr<GtkStyleContext> parentContext = createStyleContext(Scale);
1064     gtk_style_context_add_class(parentContext.get(), part == SliderHorizontalPart ? GTK_STYLE_CLASS_HORIZONTAL : GTK_STYLE_CLASS_VERTICAL);
1065 #if GTK_CHECK_VERSION(3, 19, 11)
1066     GRefPtr<GtkStyleContext> contentsContext = createStyleContext(ScaleContents, parentContext.get());
1067     GRefPtr<GtkStyleContext> context = createStyleContext(ScaleTrough, contentsContext.get());
1068 #else
1069     GRefPtr<GtkStyleContext> context = createStyleContext(ScaleTrough, parentContext.get());
1070 #endif
1071     gtk_style_context_set_direction(context.get(), gtkTextDirection(renderObject.style().direction()));
1072
1073     if (!isEnabled(renderObject))
1074         gtk_style_context_set_state(context.get(), GTK_STATE_FLAG_INSENSITIVE);
1075
1076     IntRect sliderRect = rect;
1077     // GTK+ uses the slider thumb size and margins to calculate the trough size, but in WebKit we render the thumb and
1078     // the slider track separately and the track rectangle we receive here can't be used to apply the GTK+ CSS sizes
1079     // and margins. So we use a maximum fixed size for the trough to match at least Adwaita, but that should look
1080     // good in other themes as well.
1081     static const int sliderSize = 4;
1082
1083     if (part == SliderHorizontalPart) {
1084         sliderRect.setHeight(std::min(rect.height(), sliderSize));
1085         sliderRect.move(0, (rect.height() - sliderRect.height()) / 2);
1086     } else {
1087         sliderRect.setWidth(std::min(rect.width(), sliderSize));
1088         sliderRect.move((rect.width() - sliderRect.width()) / 2, 0);
1089     }
1090
1091     gtk_render_background(context.get(), paintInfo.context().platformContext()->cr(), sliderRect.x(), sliderRect.y(), sliderRect.width(), sliderRect.height());
1092     gtk_render_frame(context.get(), paintInfo.context().platformContext()->cr(), sliderRect.x(), sliderRect.y(), sliderRect.width(), sliderRect.height());
1093
1094 #if GTK_CHECK_VERSION(3, 19, 11)
1095     GRefPtr<GtkStyleContext> highlightContext = createStyleContext(ScaleHighlight, context.get());
1096     LayoutPoint thumbLocation;
1097     if (is<HTMLInputElement>(renderObject.node())) {
1098         auto& input = downcast<HTMLInputElement>(*renderObject.node());
1099         if (auto* element = input.sliderThumbElement())
1100             thumbLocation = element->renderBox()->location();
1101     }
1102
1103     IntRect highlightRect = sliderRect;
1104     if (part == SliderHorizontalPart)
1105         highlightRect.setWidth(thumbLocation.x());
1106     else
1107         highlightRect.setHeight(thumbLocation.y());
1108
1109     gtk_render_background(highlightContext.get(), paintInfo.context().platformContext()->cr(), highlightRect.x(), highlightRect.y(), highlightRect.width(), highlightRect.height());
1110     gtk_render_frame(highlightContext.get(), paintInfo.context().platformContext()->cr(), highlightRect.x(), highlightRect.y(), highlightRect.width(), highlightRect.height());
1111 #endif
1112
1113     if (isFocused(renderObject)) {
1114         gint focusWidth, focusPad;
1115         gtk_style_context_get_style(context.get(), "focus-line-width", &focusWidth, "focus-padding", &focusPad, nullptr);
1116         IntRect focusRect(sliderRect);
1117         focusRect.inflate(focusWidth + focusPad);
1118         gtk_render_focus(context.get(), paintInfo.context().platformContext()->cr(), focusRect.x(), focusRect.y(), focusRect.width(), focusRect.height());
1119     }
1120
1121     return false;
1122 }
1123
1124 bool RenderThemeGtk::paintSliderThumb(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
1125 {
1126     ControlPart part = renderObject.style().appearance();
1127     ASSERT(part == SliderThumbHorizontalPart || part == SliderThumbVerticalPart);
1128
1129     // FIXME: The entire slider is too wide, stretching the thumb into an oval rather than a circle.
1130     GRefPtr<GtkStyleContext> parentContext = createStyleContext(Scale);
1131     gtk_style_context_add_class(parentContext.get(), part == SliderThumbHorizontalPart ? GTK_STYLE_CLASS_HORIZONTAL : GTK_STYLE_CLASS_VERTICAL);
1132 #if GTK_CHECK_VERSION(3, 19, 11)
1133     GRefPtr<GtkStyleContext> contentsContext = createStyleContext(ScaleContents, parentContext.get());
1134     GRefPtr<GtkStyleContext> troughContext = createStyleContext(ScaleTrough, contentsContext.get());
1135 #else
1136     GRefPtr<GtkStyleContext> troughContext = createStyleContext(ScaleTrough, parentContext.get());
1137 #endif
1138     GRefPtr<GtkStyleContext> context = createStyleContext(ScaleSlider, troughContext.get());
1139     gtk_style_context_set_direction(context.get(), gtkTextDirection(renderObject.style().direction()));
1140
1141     guint flags = 0;
1142     if (!isEnabled(renderObject))
1143         flags |= GTK_STATE_FLAG_INSENSITIVE;
1144     else if (isHovered(renderObject))
1145         flags |= GTK_STATE_FLAG_PRELIGHT;
1146     if (isPressed(renderObject))
1147         flags |= GTK_STATE_FLAG_ACTIVE;
1148     gtk_style_context_set_state(context.get(), static_cast<GtkStateFlags>(flags));
1149
1150     gtk_render_slider(context.get(), paintInfo.context().platformContext()->cr(), rect.x(), rect.y(), rect.width(), rect.height(),
1151         part == SliderThumbHorizontalPart ? GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL);
1152
1153     return false;
1154 }
1155
1156 void RenderThemeGtk::adjustSliderThumbSize(RenderStyle& style, Element*) const
1157 {
1158     ControlPart part = style.appearance();
1159     if (part != SliderThumbHorizontalPart && part != SliderThumbVerticalPart)
1160         return;
1161
1162     GRefPtr<GtkStyleContext> context = createStyleContext(Scale);
1163     gint sliderWidth, sliderLength;
1164 #if GTK_CHECK_VERSION(3, 19, 11)
1165     GRefPtr<GtkStyleContext> contentsContext = createStyleContext(ScaleContents, context.get());
1166     GRefPtr<GtkStyleContext> troughContext = createStyleContext(ScaleTrough, contentsContext.get());
1167     GRefPtr<GtkStyleContext> sliderContext = createStyleContext(ScaleSlider, troughContext.get());
1168     gtk_style_context_get(sliderContext.get(), gtk_style_context_get_state(sliderContext.get()), "min-width", &sliderWidth, "min-height", &sliderLength, nullptr);
1169     GtkBorder border;
1170     gtk_style_context_get_border(sliderContext.get(), gtk_style_context_get_state(sliderContext.get()), &border);
1171     sliderWidth += border.left + border.right;
1172     sliderLength += border.top + border.bottom;
1173 #else
1174     gtk_style_context_get_style(context.get(), "slider-width", &sliderWidth, "slider-length", &sliderLength, nullptr);
1175 #endif
1176     if (part == SliderThumbHorizontalPart) {
1177         style.setWidth(Length(sliderLength, Fixed));
1178         style.setHeight(Length(sliderWidth, Fixed));
1179         return;
1180     }
1181     ASSERT(part == SliderThumbVerticalPart);
1182     style.setWidth(Length(sliderWidth, Fixed));
1183     style.setHeight(Length(sliderLength, Fixed));
1184 }
1185
1186 bool RenderThemeGtk::paintProgressBar(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
1187 {
1188     if (!renderObject.isProgress())
1189         return true;
1190
1191     GRefPtr<GtkStyleContext> parentContext = createStyleContext(ProgressBar);
1192     GRefPtr<GtkStyleContext> troughContext = createStyleContext(ProgressBarTrough, parentContext.get());
1193     GRefPtr<GtkStyleContext> context = createStyleContext(ProgressBarProgress, troughContext.get());
1194
1195     gtk_render_background(troughContext.get(), paintInfo.context().platformContext()->cr(), rect.x(), rect.y(), rect.width(), rect.height());
1196     gtk_render_frame(troughContext.get(), paintInfo.context().platformContext()->cr(), rect.x(), rect.y(), rect.width(), rect.height());
1197
1198     gtk_style_context_set_state(context.get(), static_cast<GtkStateFlags>(0));
1199
1200     GtkBorder padding;
1201     gtk_style_context_get_padding(context.get(), gtk_style_context_get_state(context.get()), &padding);
1202     IntRect progressRect(
1203         rect.x() + padding.left,
1204         rect.y() + padding.top,
1205         rect.width() - (padding.left + padding.right),
1206         rect.height() - (padding.top + padding.bottom));
1207     progressRect = RenderThemeGtk::calculateProgressRect(renderObject, progressRect);
1208
1209     if (!progressRect.isEmpty()) {
1210 #if GTK_CHECK_VERSION(3, 13, 7)
1211         gtk_render_background(context.get(), paintInfo.context().platformContext()->cr(), progressRect.x(), progressRect.y(), progressRect.width(), progressRect.height());
1212         gtk_render_frame(context.get(), paintInfo.context().platformContext()->cr(), progressRect.x(), progressRect.y(), progressRect.width(), progressRect.height());
1213 #else
1214         gtk_render_activity(context.get(), paintInfo.context().platformContext()->cr(), progressRect.x(), progressRect.y(), progressRect.width(), progressRect.height());
1215 #endif
1216     }
1217
1218     return false;
1219 }
1220
1221 static gint spinButtonArrowSize(GtkStyleContext* context)
1222 {
1223     PangoFontDescription* fontDescription;
1224     gtk_style_context_get(context, gtk_style_context_get_state(context), "font", &fontDescription, nullptr);
1225     gint fontSize = pango_font_description_get_size(fontDescription);
1226     gint arrowSize = std::max(PANGO_PIXELS(fontSize), minSpinButtonArrowSize);
1227     pango_font_description_free(fontDescription);
1228
1229     return arrowSize - arrowSize % 2; // Force even.
1230 }
1231
1232 void RenderThemeGtk::adjustInnerSpinButtonStyle(StyleResolver&, RenderStyle& style, Element*) const
1233 {
1234     GRefPtr<GtkStyleContext> context = createStyleContext(SpinButton);
1235
1236     GtkBorder padding;
1237     gtk_style_context_get_padding(context.get(), gtk_style_context_get_state(context.get()), &padding);
1238
1239     int width = spinButtonArrowSize(context.get()) + padding.left + padding.right;
1240     style.setWidth(Length(width, Fixed));
1241     style.setMinWidth(Length(width, Fixed));
1242 }
1243
1244 static void paintSpinArrowButton(RenderTheme* theme, GtkStyleContext* parentContext, const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect, GtkArrowType arrowType)
1245 {
1246     ASSERT(arrowType == GTK_ARROW_UP || arrowType == GTK_ARROW_DOWN);
1247
1248     GRefPtr<GtkStyleContext> context = createStyleContext(arrowType == GTK_ARROW_UP ? SpinButtonUpButton : SpinButtonDownButton, parentContext);
1249     GtkTextDirection direction = gtk_style_context_get_direction(context.get());
1250     guint state = static_cast<guint>(gtk_style_context_get_state(context.get()));
1251     if (!(state & GTK_STATE_FLAG_INSENSITIVE)) {
1252         if (theme->isPressed(renderObject)) {
1253             if ((arrowType == GTK_ARROW_UP && theme->isSpinUpButtonPartPressed(renderObject))
1254                 || (arrowType == GTK_ARROW_DOWN && !theme->isSpinUpButtonPartPressed(renderObject)))
1255                 state |= GTK_STATE_FLAG_ACTIVE;
1256         } else if (theme->isHovered(renderObject)) {
1257             if ((arrowType == GTK_ARROW_UP && theme->isSpinUpButtonPartHovered(renderObject))
1258                 || (arrowType == GTK_ARROW_DOWN && !theme->isSpinUpButtonPartHovered(renderObject)))
1259                 state |= GTK_STATE_FLAG_PRELIGHT;
1260         }
1261     }
1262     gtk_style_context_set_state(context.get(), static_cast<GtkStateFlags>(state));
1263
1264     // Paint button.
1265     IntRect buttonRect(rect);
1266     guint junction = gtk_style_context_get_junction_sides(context.get());
1267     if (arrowType == GTK_ARROW_UP)
1268         junction |= GTK_JUNCTION_BOTTOM;
1269     else {
1270         junction |= GTK_JUNCTION_TOP;
1271         buttonRect.move(0, rect.height() / 2);
1272     }
1273     buttonRect.setHeight(rect.height() / 2);
1274     gtk_style_context_set_junction_sides(context.get(), static_cast<GtkJunctionSides>(junction));
1275
1276     gtk_render_background(context.get(), paintInfo.context().platformContext()->cr(), buttonRect.x(), buttonRect.y(), buttonRect.width(), buttonRect.height());
1277     gtk_render_frame(context.get(), paintInfo.context().platformContext()->cr(), buttonRect.x(), buttonRect.y(), buttonRect.width(), buttonRect.height());
1278
1279     // Paint arrow centered inside button.
1280     // This code is based on gtkspinbutton.c code.
1281     IntRect arrowRect;
1282     gdouble angle;
1283     if (arrowType == GTK_ARROW_UP) {
1284         angle = 0;
1285         arrowRect.setY(rect.y());
1286         arrowRect.setHeight(rect.height() / 2 - 2);
1287     } else {
1288         angle = G_PI;
1289         arrowRect.setY(rect.y() + buttonRect.y());
1290         arrowRect.setHeight(rect.height() - arrowRect.y() - 2);
1291     }
1292     arrowRect.setWidth(rect.width() - 3);
1293     if (direction == GTK_TEXT_DIR_LTR)
1294         arrowRect.setX(rect.x() + 1);
1295     else
1296         arrowRect.setX(rect.x() + 2);
1297
1298     gint width = arrowRect.width() / 2;
1299     width -= width % 2 - 1; // Force odd.
1300     gint height = (width + 1) / 2;
1301
1302     arrowRect.move((arrowRect.width() - width) / 2, (arrowRect.height() - height) / 2);
1303     gtk_render_arrow(context.get(), paintInfo.context().platformContext()->cr(), angle, arrowRect.x(), arrowRect.y(), width);
1304 }
1305
1306 bool RenderThemeGtk::paintInnerSpinButton(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
1307 {
1308     GRefPtr<GtkStyleContext> context = createStyleContext(SpinButton);
1309     gtk_style_context_set_direction(context.get(), gtkTextDirection(renderObject.style().direction()));
1310
1311     guint flags = 0;
1312     if (!isEnabled(renderObject) || isReadOnlyControl(renderObject))
1313         flags |= GTK_STATE_FLAG_INSENSITIVE;
1314     else if (isFocused(renderObject))
1315         flags |= GTK_STATE_FLAG_FOCUSED;
1316     gtk_style_context_set_state(context.get(), static_cast<GtkStateFlags>(flags));
1317
1318     paintSpinArrowButton(this, context.get(), renderObject, paintInfo, rect, GTK_ARROW_UP);
1319     paintSpinArrowButton(this, context.get(), renderObject, paintInfo, rect, GTK_ARROW_DOWN);
1320
1321     return false;
1322 }
1323
1324 double RenderThemeGtk::caretBlinkInterval() const
1325 {
1326     GtkSettings* settings = gtk_settings_get_default();
1327
1328     gboolean shouldBlink;
1329     gint time;
1330
1331     g_object_get(settings, "gtk-cursor-blink", &shouldBlink, "gtk-cursor-blink-time", &time, nullptr);
1332
1333     if (!shouldBlink)
1334         return 0;
1335
1336     return time / 2000.;
1337 }
1338
1339 enum StyleColorType { StyleColorBackground, StyleColorForeground };
1340
1341 static Color styleColor(RenderThemePart themePart, GtkStateFlags state, StyleColorType colorType)
1342 {
1343     GRefPtr<GtkStyleContext> parentContext;
1344     RenderThemePart part = themePart;
1345     if (themePart == Entry && (state & GTK_STATE_FLAG_SELECTED)) {
1346         parentContext = createStyleContext(Entry);
1347         part = EntrySelection;
1348     }
1349
1350     GRefPtr<GtkStyleContext> context = createStyleContext(part, parentContext.get());
1351     gtk_style_context_set_state(context.get(), state);
1352
1353     GdkRGBA gdkRGBAColor;
1354     if (colorType == StyleColorBackground)
1355         gtk_style_context_get_background_color(context.get(), state, &gdkRGBAColor);
1356     else
1357         gtk_style_context_get_color(context.get(), state, &gdkRGBAColor);
1358     return gdkRGBAColor;
1359 }
1360
1361 Color RenderThemeGtk::platformActiveSelectionBackgroundColor() const
1362 {
1363     return styleColor(Entry, static_cast<GtkStateFlags>(GTK_STATE_FLAG_SELECTED | GTK_STATE_FLAG_FOCUSED), StyleColorBackground);
1364 }
1365
1366 Color RenderThemeGtk::platformInactiveSelectionBackgroundColor() const
1367 {
1368     return styleColor(Entry, GTK_STATE_FLAG_SELECTED, StyleColorBackground);
1369 }
1370
1371 Color RenderThemeGtk::platformActiveSelectionForegroundColor() const
1372 {
1373     return styleColor(Entry, static_cast<GtkStateFlags>(GTK_STATE_FLAG_SELECTED | GTK_STATE_FLAG_FOCUSED), StyleColorForeground);
1374 }
1375
1376 Color RenderThemeGtk::platformInactiveSelectionForegroundColor() const
1377 {
1378     return styleColor(Entry, GTK_STATE_FLAG_SELECTED, StyleColorForeground);
1379 }
1380
1381 Color RenderThemeGtk::platformActiveListBoxSelectionBackgroundColor() const
1382 {
1383     return styleColor(ListBox, static_cast<GtkStateFlags>(GTK_STATE_FLAG_SELECTED | GTK_STATE_FLAG_FOCUSED), StyleColorBackground);
1384 }
1385
1386 Color RenderThemeGtk::platformInactiveListBoxSelectionBackgroundColor() const
1387 {
1388     return styleColor(ListBox, GTK_STATE_FLAG_SELECTED, StyleColorBackground);
1389 }
1390
1391 Color RenderThemeGtk::platformActiveListBoxSelectionForegroundColor() const
1392 {
1393     return styleColor(ListBox, static_cast<GtkStateFlags>(GTK_STATE_FLAG_SELECTED | GTK_STATE_FLAG_FOCUSED), StyleColorForeground);
1394 }
1395
1396 Color RenderThemeGtk::platformInactiveListBoxSelectionForegroundColor() const
1397 {
1398     return styleColor(ListBox, GTK_STATE_FLAG_SELECTED, StyleColorForeground);
1399 }
1400
1401 Color RenderThemeGtk::systemColor(CSSValueID cssValueId) const
1402 {
1403     switch (cssValueId) {
1404     case CSSValueButtontext:
1405         return styleColor(Button, GTK_STATE_FLAG_ACTIVE, StyleColorForeground);
1406     case CSSValueCaptiontext:
1407         return styleColor(Entry, GTK_STATE_FLAG_ACTIVE, StyleColorForeground);
1408     default:
1409         return RenderTheme::systemColor(cssValueId);
1410     }
1411 }
1412
1413 void RenderThemeGtk::platformColorsDidChange()
1414 {
1415     RenderTheme::platformColorsDidChange();
1416 }
1417
1418 #if ENABLE(VIDEO)
1419 String RenderThemeGtk::extraMediaControlsStyleSheet()
1420 {
1421     return String(mediaControlsGtkUserAgentStyleSheet, sizeof(mediaControlsGtkUserAgentStyleSheet));
1422 }
1423
1424 #if ENABLE(FULLSCREEN_API)
1425 String RenderThemeGtk::extraFullScreenStyleSheet()
1426 {
1427     return String();
1428 }
1429 #endif
1430
1431 bool RenderThemeGtk::paintMediaButton(const RenderObject& renderObject, GraphicsContext& graphicsContext, const IntRect& rect, const char* iconName)
1432 {
1433     GRefPtr<GtkStyleContext> context = createStyleContext(MediaButton);
1434     gtk_style_context_set_direction(context.get(), gtkTextDirection(renderObject.style().direction()));
1435     gtk_style_context_set_state(context.get(), gtkIconStateFlags(this, renderObject));
1436     static const unsigned mediaIconSize = 16;
1437     IntRect iconRect(rect.x() + (rect.width() - mediaIconSize) / 2, rect.y() + (rect.height() - mediaIconSize) / 2, mediaIconSize, mediaIconSize);
1438     return !paintIcon(context.get(), graphicsContext, iconRect, iconName);
1439 }
1440
1441 bool RenderThemeGtk::hasOwnDisabledStateHandlingFor(ControlPart part) const
1442 {
1443     return (part != MediaMuteButtonPart);
1444 }
1445
1446 bool RenderThemeGtk::paintMediaFullscreenButton(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
1447 {
1448     return paintMediaButton(renderObject, paintInfo.context(), rect, "view-fullscreen-symbolic");
1449 }
1450
1451 bool RenderThemeGtk::paintMediaMuteButton(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
1452 {
1453     Node* node = renderObject.node();
1454     if (!node)
1455         return true;
1456     Node* mediaNode = node->shadowHost();
1457     if (!is<HTMLMediaElement>(mediaNode))
1458         return true;
1459
1460     HTMLMediaElement* mediaElement = downcast<HTMLMediaElement>(mediaNode);
1461     return paintMediaButton(renderObject, paintInfo.context(), rect, mediaElement->muted() ? "audio-volume-muted-symbolic" : "audio-volume-high-symbolic");
1462 }
1463
1464 bool RenderThemeGtk::paintMediaPlayButton(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
1465 {
1466     Node* node = renderObject.node();
1467     if (!node)
1468         return true;
1469     if (!nodeHasPseudo(node, "-webkit-media-controls-play-button"))
1470         return true;
1471
1472     return paintMediaButton(renderObject, paintInfo.context(), rect, nodeHasClass(node, "paused") ? "media-playback-start-symbolic" : "media-playback-pause-symbolic");
1473 }
1474
1475 bool RenderThemeGtk::paintMediaSeekBackButton(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
1476 {
1477     return paintMediaButton(renderObject, paintInfo.context(), rect, "media-seek-backward-symbolic");
1478 }
1479
1480 bool RenderThemeGtk::paintMediaSeekForwardButton(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
1481 {
1482     return paintMediaButton(renderObject, paintInfo.context(), rect, "media-seek-forward-symbolic");
1483 }
1484
1485 #if ENABLE(VIDEO_TRACK)
1486 bool RenderThemeGtk::paintMediaToggleClosedCaptionsButton(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
1487 {
1488     return paintMediaButton(renderObject, paintInfo.context(), rect, "media-view-subtitles-symbolic");
1489 }
1490 #endif
1491
1492 static FloatRoundedRect::Radii borderRadiiFromStyle(RenderStyle& style)
1493 {
1494     return FloatRoundedRect::Radii(
1495         IntSize(style.borderTopLeftRadius().width().intValue(), style.borderTopLeftRadius().height().intValue()),
1496         IntSize(style.borderTopRightRadius().width().intValue(), style.borderTopRightRadius().height().intValue()),
1497         IntSize(style.borderBottomLeftRadius().width().intValue(), style.borderBottomLeftRadius().height().intValue()),
1498         IntSize(style.borderBottomRightRadius().width().intValue(), style.borderBottomRightRadius().height().intValue()));
1499 }
1500
1501 bool RenderThemeGtk::paintMediaSliderTrack(const RenderObject& o, const PaintInfo& paintInfo, const IntRect& r)
1502 {
1503     HTMLMediaElement* mediaElement = parentMediaElement(o);
1504     if (!mediaElement)
1505         return true;
1506
1507     GraphicsContext& context = paintInfo.context();
1508     context.save();
1509     context.setStrokeStyle(NoStroke);
1510
1511     float mediaDuration = mediaElement->duration();
1512     float totalTrackWidth = r.width();
1513     RenderStyle& style = o.style();
1514     RefPtr<TimeRanges> timeRanges = mediaElement->buffered();
1515     for (unsigned index = 0; index < timeRanges->length(); ++index) {
1516         float start = timeRanges->start(index, IGNORE_EXCEPTION);
1517         float end = timeRanges->end(index, IGNORE_EXCEPTION);
1518         float startRatio = start / mediaDuration;
1519         float lengthRatio = (end - start) / mediaDuration;
1520         if (!lengthRatio)
1521             continue;
1522
1523         IntRect rangeRect(r);
1524         rangeRect.setWidth(lengthRatio * totalTrackWidth);
1525         if (index)
1526             rangeRect.move(startRatio * totalTrackWidth, 0);
1527         context.fillRoundedRect(FloatRoundedRect(rangeRect, borderRadiiFromStyle(style)), style.visitedDependentColor(CSSPropertyColor));
1528     }
1529
1530     context.restore();
1531     return false;
1532 }
1533
1534 bool RenderThemeGtk::paintMediaSliderThumb(const RenderObject& o, const PaintInfo& paintInfo, const IntRect& r)
1535 {
1536     RenderStyle& style = o.style();
1537     paintInfo.context().fillRoundedRect(FloatRoundedRect(r, borderRadiiFromStyle(style)), style.visitedDependentColor(CSSPropertyColor));
1538     return false;
1539 }
1540
1541 bool RenderThemeGtk::paintMediaVolumeSliderTrack(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
1542 {
1543     HTMLMediaElement* mediaElement = parentMediaElement(renderObject);
1544     if (!mediaElement)
1545         return true;
1546
1547     float volume = mediaElement->muted() ? 0.0f : mediaElement->volume();
1548     if (!volume)
1549         return true;
1550
1551     GraphicsContext& context = paintInfo.context();
1552     context.save();
1553     context.setStrokeStyle(NoStroke);
1554
1555     int rectHeight = rect.height();
1556     float trackHeight = rectHeight * volume;
1557     RenderStyle& style = renderObject.style();
1558     IntRect volumeRect(rect);
1559     volumeRect.move(0, rectHeight - trackHeight);
1560     volumeRect.setHeight(ceil(trackHeight));
1561
1562     context.fillRoundedRect(FloatRoundedRect(volumeRect, borderRadiiFromStyle(style)), style.visitedDependentColor(CSSPropertyColor));
1563     context.restore();
1564
1565     return false;
1566 }
1567
1568 bool RenderThemeGtk::paintMediaVolumeSliderThumb(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
1569 {
1570     return paintMediaSliderThumb(renderObject, paintInfo, rect);
1571 }
1572
1573 String RenderThemeGtk::formatMediaControlsCurrentTime(float currentTime, float duration) const
1574 {
1575     return formatMediaControlsTime(currentTime) + " / " + formatMediaControlsTime(duration);
1576 }
1577
1578 bool RenderThemeGtk::paintMediaCurrentTime(const RenderObject&, const PaintInfo&, const IntRect&)
1579 {
1580     return false;
1581 }
1582 #endif
1583
1584 void RenderThemeGtk::adjustProgressBarStyle(StyleResolver&, RenderStyle& style, Element*) const
1585 {
1586     style.setBoxShadow(nullptr);
1587 }
1588
1589 // These values have been copied from RenderThemeChromiumSkia.cpp
1590 static const int progressActivityBlocks = 5;
1591 static const int progressAnimationFrames = 10;
1592 static const double progressAnimationInterval = 0.125;
1593 double RenderThemeGtk::animationRepeatIntervalForProgressBar(RenderProgress&) const
1594 {
1595     return progressAnimationInterval;
1596 }
1597
1598 double RenderThemeGtk::animationDurationForProgressBar(RenderProgress&) const
1599 {
1600     return progressAnimationInterval * progressAnimationFrames * 2; // "2" for back and forth;
1601 }
1602
1603 IntRect RenderThemeGtk::calculateProgressRect(const RenderObject& renderObject, const IntRect& fullBarRect)
1604 {
1605     IntRect progressRect(fullBarRect);
1606     const auto& renderProgress = downcast<RenderProgress>(renderObject);
1607     if (renderProgress.isDeterminate()) {
1608         int progressWidth = progressRect.width() * renderProgress.position();
1609         if (renderObject.style().direction() == RTL)
1610             progressRect.setX(progressRect.x() + progressRect.width() - progressWidth);
1611         progressRect.setWidth(progressWidth);
1612         return progressRect;
1613     }
1614
1615     double animationProgress = renderProgress.animationProgress();
1616
1617     // Never let the progress rect shrink smaller than 2 pixels.
1618     int newWidth = std::max(2, progressRect.width() / progressActivityBlocks);
1619     int movableWidth = progressRect.width() - newWidth;
1620     progressRect.setWidth(newWidth);
1621
1622     // We want the first 0.5 units of the animation progress to represent the
1623     // forward motion and the second 0.5 units to represent the backward motion,
1624     // thus we multiply by two here to get the full sweep of the progress bar with
1625     // each direction.
1626     if (animationProgress < 0.5)
1627         progressRect.setX(progressRect.x() + (animationProgress * 2 * movableWidth));
1628     else
1629         progressRect.setX(progressRect.x() + ((1.0 - animationProgress) * 2 * movableWidth));
1630     return progressRect;
1631 }
1632
1633 String RenderThemeGtk::fileListNameForWidth(const FileList* fileList, const FontCascade& font, int width, bool multipleFilesAllowed) const
1634 {
1635     if (width <= 0)
1636         return String();
1637
1638     if (fileList->length() > 1)
1639         return StringTruncator::rightTruncate(multipleFileUploadText(fileList->length()), width, font);
1640
1641     String string;
1642     if (fileList->length())
1643         string = pathGetFileName(fileList->item(0)->path());
1644     else if (multipleFilesAllowed)
1645         string = fileButtonNoFilesSelectedLabel();
1646     else
1647         string = fileButtonNoFileSelectedLabel();
1648
1649     return StringTruncator::centerTruncate(string, width, font);
1650 }
1651
1652 #if ENABLE(VIDEO)
1653 String RenderThemeGtk::mediaControlsScript()
1654 {
1655     StringBuilder scriptBuilder;
1656     scriptBuilder.append(mediaControlsLocalizedStringsJavaScript, sizeof(mediaControlsLocalizedStringsJavaScript));
1657     scriptBuilder.append(mediaControlsBaseJavaScript, sizeof(mediaControlsBaseJavaScript));
1658     scriptBuilder.append(mediaControlsGtkJavaScript, sizeof(mediaControlsGtkJavaScript));
1659     return scriptBuilder.toString();
1660 }
1661 #endif // ENABLE(VIDEO)
1662
1663 #endif // GTK_API_VERSION_2
1664 }