[WPE][GTK] Bump minimum versions of GLib, GTK, libsoup, ATK, GStreamer, and Cairo
[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 "FileList.h"
30 #include "FloatRoundedRect.h"
31 #include "FontDescription.h"
32 #include "GRefPtrGtk.h"
33 #include "GUniquePtrGtk.h"
34 #include "Gradient.h"
35 #include "GraphicsContext.h"
36 #include "HTMLInputElement.h"
37 #include "HTMLMediaElement.h"
38 #include "LocalizedStrings.h"
39 #include "MediaControlElements.h"
40 #include "Page.h"
41 #include "PaintInfo.h"
42 #include "PlatformContextCairo.h"
43 #include "RenderBox.h"
44 #include "RenderObject.h"
45 #include "RenderProgress.h"
46 #include "RenderThemeWidget.h"
47 #include "ScrollbarThemeGtk.h"
48 #include "StringTruncator.h"
49 #include "TimeRanges.h"
50 #include "UserAgentScripts.h"
51 #include "UserAgentStyleSheets.h"
52 #include <cmath>
53 #include <gdk/gdk.h>
54 #include <glib.h>
55 #include <gtk/gtk.h>
56 #include <wtf/FileSystem.h>
57 #include <wtf/glib/GRefPtr.h>
58 #include <wtf/glib/GUniquePtr.h>
59 #include <wtf/text/CString.h>
60 #include <wtf/text/StringBuilder.h>
61
62 namespace WebCore {
63
64 RenderTheme& RenderTheme::singleton()
65 {
66     static NeverDestroyed<RenderThemeGtk> theme;
67     return theme;
68 }
69
70 static double getScreenDPI()
71 {
72     // FIXME: Really this should be the widget's screen.
73     GdkScreen* screen = gdk_screen_get_default();
74     if (!screen)
75         return 96; // Default to 96 DPI.
76
77     float dpi = gdk_screen_get_resolution(screen);
78     if (dpi <= 0)
79         return 96;
80     return dpi;
81 }
82
83 void RenderThemeGtk::updateCachedSystemFontDescription(CSSValueID, FontCascadeDescription& fontDescription) const
84 {
85     GtkSettings* settings = gtk_settings_get_default();
86     if (!settings)
87         return;
88
89     // This will be a font selection string like "Sans 10" so we cannot use it as the family name.
90     GUniqueOutPtr<gchar> fontName;
91     g_object_get(settings, "gtk-font-name", &fontName.outPtr(), nullptr);
92     if (!fontName || !fontName.get()[0])
93         return;
94
95     PangoFontDescription* pangoDescription = pango_font_description_from_string(fontName.get());
96     if (!pangoDescription)
97         return;
98
99     fontDescription.setOneFamily(pango_font_description_get_family(pangoDescription));
100
101     int size = pango_font_description_get_size(pangoDescription) / PANGO_SCALE;
102     // If the size of the font is in points, we need to convert it to pixels.
103     if (!pango_font_description_get_size_is_absolute(pangoDescription))
104         size = size * (getScreenDPI() / 72.0);
105
106     fontDescription.setSpecifiedSize(size);
107     fontDescription.setIsAbsoluteSize(true);
108     fontDescription.setWeight(normalWeightValue());
109     fontDescription.setItalic(FontSelectionValue());
110     pango_font_description_free(pangoDescription);
111 }
112
113 #if ENABLE(DATALIST_ELEMENT)
114 IntSize RenderThemeGtk::sliderTickSize() const
115 {
116     // FIXME: We need to set this to the size of one tick mark.
117     return IntSize(0, 0);
118 }
119
120 int RenderThemeGtk::sliderTickOffsetFromTrackCenter() const
121 {
122     // FIXME: We need to set this to the position of the tick marks.
123     return 0;
124 }
125 #endif
126
127 static void themeChangedCallback()
128 {
129     Page::updateStyleForAllPagesAfterGlobalChangeInEnvironment();
130 }
131
132 RenderThemeGtk::RenderThemeGtk()
133 {
134     static bool themeMonitorInitialized = false;
135     if (!themeMonitorInitialized) {
136         GtkSettings* settings = gtk_settings_get_default();
137         g_signal_connect(settings, "notify::gtk-theme-name", G_CALLBACK(themeChangedCallback), nullptr);
138         g_signal_connect(settings, "notify::gtk-color-scheme", G_CALLBACK(themeChangedCallback), nullptr);
139         themeMonitorInitialized = true;
140     }
141 }
142
143 enum RenderThemePart {
144     Entry,
145     EntrySelection,
146     EntryIconLeft,
147     EntryIconRight,
148     Button,
149     CheckButton,
150     RadioButton,
151     ComboBox,
152     ComboBoxButton,
153     ComboBoxArrow,
154     Scale,
155     ScaleTrough,
156     ScaleSlider,
157     ProgressBar,
158     ProgressBarTrough,
159     ProgressBarProgress,
160     ListBox,
161     SpinButton,
162     SpinButtonUpButton,
163     SpinButtonDownButton,
164 #if ENABLE(VIDEO)
165     MediaButton,
166 #endif
167     Window,
168 };
169
170 #if ENABLE(VIDEO)
171 static bool nodeHasPseudo(Node& node, const char* pseudo)
172 {
173     return is<Element>(node) && downcast<Element>(node).pseudo() == pseudo;
174 }
175
176 static bool nodeHasClass(Node* node, const char* className)
177 {
178     if (!is<Element>(*node))
179         return false;
180
181     Element& element = downcast<Element>(*node);
182
183     if (!element.hasClass())
184         return false;
185
186     return element.classNames().contains(className);
187 }
188 #endif // ENABLE(VIDEO)
189
190 RenderThemeGtk::~RenderThemeGtk() = default;
191
192 static bool supportsFocus(ControlPart appearance)
193 {
194     switch (appearance) {
195     case PushButtonPart:
196     case ButtonPart:
197     case TextFieldPart:
198     case TextAreaPart:
199     case SearchFieldPart:
200     case MenulistPart:
201     case RadioPart:
202     case CheckboxPart:
203     case SliderHorizontalPart:
204     case SliderVerticalPart:
205         return true;
206     default:
207         return false;
208     }
209 }
210
211 bool RenderThemeGtk::supportsFocusRing(const RenderStyle& style) const
212 {
213     return supportsFocus(style.appearance());
214 }
215
216 bool RenderThemeGtk::controlSupportsTints(const RenderObject& o) const
217 {
218     return isEnabled(o);
219 }
220
221 int RenderThemeGtk::baselinePosition(const RenderBox& box) const
222 {
223     // FIXME: This strategy is possibly incorrect for the GTK+ port.
224     if (box.style().appearance() == CheckboxPart || box.style().appearance() == RadioPart)
225         return box.marginTop() + box.height() - 2;
226     return RenderTheme::baselinePosition(box);
227 }
228
229 void RenderThemeGtk::adjustRepaintRect(const RenderObject&, FloatRect&)
230 {
231 }
232 static GtkStateFlags themePartStateFlags(const RenderThemeGtk& theme, RenderThemePart themePart, const RenderObject& renderObject)
233 {
234     unsigned stateFlags = 0;
235     switch (renderObject.style().direction()) {
236     case TextDirection::RTL:
237         stateFlags |= GTK_STATE_FLAG_DIR_RTL;
238         break;
239     case TextDirection::LTR:
240         stateFlags |= GTK_STATE_FLAG_DIR_LTR;
241         break;
242     }
243
244     if (!theme.isEnabled(renderObject) || (themePart == Entry && theme.isReadOnlyControl(renderObject)))
245         stateFlags |= GTK_STATE_FLAG_INSENSITIVE;
246     else {
247         if (theme.isHovered(renderObject))
248             stateFlags |= GTK_STATE_FLAG_PRELIGHT;
249         if (theme.isFocused(renderObject))
250             stateFlags |= GTK_STATE_FLAG_FOCUSED;
251     }
252
253     switch (themePart) {
254     case CheckButton:
255     case RadioButton:
256         if (theme.isChecked(renderObject))
257             stateFlags |= GTK_STATE_FLAG_CHECKED;
258         if (theme.isIndeterminate(renderObject))
259             stateFlags |= GTK_STATE_FLAG_INCONSISTENT;
260         if (theme.isPressed(renderObject))
261             stateFlags |= GTK_STATE_FLAG_SELECTED;
262         break;
263     case Button:
264     case ComboBoxButton:
265     case ScaleSlider:
266     case EntryIconLeft:
267     case EntryIconRight:
268 #if ENABLE(VIDEO)
269     case MediaButton:
270 #endif
271         if (theme.isPressed(renderObject))
272             stateFlags |= GTK_STATE_FLAG_ACTIVE;
273         break;
274     case SpinButtonUpButton:
275         if (theme.isPressed(renderObject) && theme.isSpinUpButtonPartPressed(renderObject))
276             stateFlags |= GTK_STATE_FLAG_ACTIVE;
277         if (theme.isHovered(renderObject) && !theme.isSpinUpButtonPartHovered(renderObject))
278             stateFlags &= ~GTK_STATE_FLAG_PRELIGHT;
279         break;
280     case SpinButtonDownButton:
281         if (theme.isPressed(renderObject) && !theme.isSpinUpButtonPartPressed(renderObject))
282             stateFlags |= GTK_STATE_FLAG_ACTIVE;
283         if (theme.isHovered(renderObject) && theme.isSpinUpButtonPartHovered(renderObject))
284             stateFlags &= ~GTK_STATE_FLAG_PRELIGHT;
285         break;
286     default:
287         break;
288     }
289
290     return static_cast<GtkStateFlags>(stateFlags);
291 }
292
293 void RenderThemeGtk::adjustButtonStyle(StyleResolver&, RenderStyle& style, const Element*) const
294 {
295     // Some layout tests check explicitly that buttons ignore line-height.
296     if (style.appearance() == PushButtonPart)
297         style.setLineHeight(RenderStyle::initialLineHeight());
298 }
299
300 static void shrinkToMinimumSizeAndCenterRectangle(FloatRect& rect, const IntSize& minSize)
301 {
302     if (rect.width() > minSize.width()) {
303         rect.inflateX(-(rect.width() - minSize.width()) / 2);
304         rect.setWidth(minSize.width()); // In case rect.width() was equal to minSize.width() + 1.
305     }
306
307     if (rect.height() > minSize.height()) {
308         rect.inflateY(-(rect.height() - minSize.height()) / 2);
309         rect.setHeight(minSize.height()); // In case rect.height() was equal to minSize.height() + 1.
310     }
311 }
312
313 static void setToggleSize(RenderThemePart themePart, RenderStyle& style)
314 {
315     ASSERT(themePart == CheckButton || themePart == RadioButton);
316
317     // The width and height are both specified, so we shouldn't change them.
318     if (!style.width().isIntrinsicOrAuto() && !style.height().isAuto())
319         return;
320
321     auto& toggleWidget = static_cast<RenderThemeToggleButton&>(RenderThemeWidget::getOrCreate(themePart == CheckButton ? RenderThemeWidget::Type::CheckButton : RenderThemeWidget::Type::RadioButton));
322     toggleWidget.button().setState(GTK_STATE_FLAG_NORMAL);
323     toggleWidget.toggle().setState(GTK_STATE_FLAG_NORMAL);
324     IntSize preferredSize = toggleWidget.button().preferredSize();
325     preferredSize = preferredSize.expandedTo(toggleWidget.toggle().preferredSize());
326
327     if (style.width().isIntrinsicOrAuto())
328         style.setWidth(Length(preferredSize.width(), Fixed));
329
330     if (style.height().isAuto())
331         style.setHeight(Length(preferredSize.height(), Fixed));
332 }
333
334 static void paintToggle(const RenderThemeGtk* theme, RenderThemePart themePart, const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& fullRect)
335 {
336     ASSERT(themePart == CheckButton || themePart == RadioButton);
337
338     auto& toggleWidget = static_cast<RenderThemeToggleButton&>(RenderThemeWidget::getOrCreate(themePart == CheckButton ? RenderThemeWidget::Type::CheckButton : RenderThemeWidget::Type::RadioButton));
339     auto toggleState = themePartStateFlags(*theme, themePart, renderObject);
340     toggleWidget.button().setState(toggleState);
341     toggleWidget.toggle().setState(toggleState);
342
343     FloatRect rect = fullRect;
344     // Some themes do not render large toggle buttons properly, so we simply
345     // shrink the rectangle back down to the default size and then center it
346     // in the full toggle button region. The reason for not simply forcing toggle
347     // buttons to be a smaller size is that we don't want to break site layouts.
348     IntSize preferredSize = toggleWidget.button().preferredSize();
349     preferredSize = preferredSize.expandedTo(toggleWidget.toggle().preferredSize());
350     shrinkToMinimumSizeAndCenterRectangle(rect, preferredSize);
351     toggleWidget.button().render(paintInfo.context().platformContext()->cr(), rect);
352     toggleWidget.toggle().render(paintInfo.context().platformContext()->cr(), rect);
353
354     if (theme->isFocused(renderObject))
355         toggleWidget.button().renderFocus(paintInfo.context().platformContext()->cr(), rect);
356 }
357
358 void RenderThemeGtk::setCheckboxSize(RenderStyle& style) const
359 {
360     setToggleSize(CheckButton, style);
361 }
362
363 bool RenderThemeGtk::paintCheckbox(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
364 {
365     paintToggle(this, CheckButton, renderObject, paintInfo, rect);
366     return false;
367 }
368
369 void RenderThemeGtk::setRadioSize(RenderStyle& style) const
370 {
371     setToggleSize(RadioButton, style);
372 }
373
374 bool RenderThemeGtk::paintRadio(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
375 {
376     paintToggle(this, RadioButton, renderObject, paintInfo, rect);
377     return false;
378 }
379
380 bool RenderThemeGtk::paintButton(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
381 {
382     auto& buttonWidget = static_cast<RenderThemeButton&>(RenderThemeWidget::getOrCreate(isDefault(renderObject) ? RenderThemeWidget::Type::ButtonDefault : RenderThemeWidget::Type::Button));
383     buttonWidget.button().setState(themePartStateFlags(*this, Button, renderObject));
384     buttonWidget.button().render(paintInfo.context().platformContext()->cr(), rect);
385     if (isFocused(renderObject))
386         buttonWidget.button().renderFocus(paintInfo.context().platformContext()->cr(), rect);
387     return false;
388 }
389
390 static Color menuListColor(const Element* element)
391 {
392     auto& comboWidget = static_cast<RenderThemeComboBox&>(RenderThemeWidget::getOrCreate(RenderThemeWidget::Type::ComboBox));
393     GtkStateFlags state = element->isDisabledFormControl() ? GTK_STATE_FLAG_INSENSITIVE : GTK_STATE_FLAG_NORMAL;
394     comboWidget.comboBox().setState(state);
395     comboWidget.button().setState(state);
396     return comboWidget.button().color();
397 }
398
399 void RenderThemeGtk::adjustMenuListStyle(StyleResolver&, RenderStyle& style, const Element* element) const
400 {
401     // The tests check explicitly that select menu buttons ignore line height.
402     style.setLineHeight(RenderStyle::initialLineHeight());
403
404     // We cannot give a proper rendering when border radius is active, unfortunately.
405     style.resetBorderRadius();
406
407     if (element)
408         style.setColor(menuListColor(element));
409 }
410
411 void RenderThemeGtk::adjustMenuListButtonStyle(StyleResolver& styleResolver, RenderStyle& style, const Element* e) const
412 {
413     adjustMenuListStyle(styleResolver, style, e);
414 }
415
416 /*
417  * GtkComboBox gadgets tree
418  *
419  * combobox
420  * ├── box.linked
421  * │   ╰── button.combo
422  * │       ╰── box
423  * │           ├── cellview
424  * │           ╰── arrow
425  * ╰── window.popup
426  */
427 LengthBox RenderThemeGtk::popupInternalPaddingBox(const RenderStyle& style) const
428 {
429     if (style.appearance() == NoControlPart)
430         return LengthBox(0);
431
432     auto& comboWidget = static_cast<RenderThemeComboBox&>(RenderThemeWidget::getOrCreate(RenderThemeWidget::Type::ComboBox));
433     comboWidget.comboBox().setState(GTK_STATE_FLAG_NORMAL);
434     comboWidget.button().setState(GTK_STATE_FLAG_NORMAL);
435     comboWidget.arrow().setState(GTK_STATE_FLAG_NORMAL);
436     GtkBorder comboContentsBox = comboWidget.comboBox().contentsBox();
437     GtkBorder boxContentsBox = comboWidget.box().contentsBox();
438     GtkBorder buttonContentsBox = comboWidget.button().contentsBox();
439     GtkBorder buttonBoxContentsBox = comboWidget.buttonBox().contentsBox();
440     GtkBorder padding;
441     padding.left = comboContentsBox.left + boxContentsBox.left + buttonContentsBox.left + buttonBoxContentsBox.left;
442     padding.right = comboContentsBox.right + boxContentsBox.right + buttonContentsBox.right + buttonBoxContentsBox.right;
443     padding.top = comboContentsBox.top + boxContentsBox.top + buttonContentsBox.top + buttonBoxContentsBox.top;
444     padding.bottom = comboContentsBox.bottom + boxContentsBox.bottom + buttonContentsBox.bottom + buttonBoxContentsBox.bottom;
445
446     auto arrowSize = comboWidget.arrow().preferredSize();
447     return LengthBox(padding.top, padding.right + (style.direction() == TextDirection::LTR ? arrowSize.width() : 0),
448         padding.bottom, padding.left + (style.direction() == TextDirection::RTL ? arrowSize.width() : 0));
449 }
450
451 bool RenderThemeGtk::paintMenuList(const RenderObject& renderObject, const PaintInfo& paintInfo, const FloatRect& rect)
452 {
453     auto& comboWidget = static_cast<RenderThemeComboBox&>(RenderThemeWidget::getOrCreate(RenderThemeWidget::Type::ComboBox));
454     auto comboState = themePartStateFlags(*this, ComboBoxButton, renderObject);
455     comboWidget.comboBox().setState(comboState);
456     comboWidget.button().setState(comboState);
457     comboWidget.arrow().setState(comboState);
458
459     cairo_t* cr = paintInfo.context().platformContext()->cr();
460     comboWidget.comboBox().render(cr, rect);
461     comboWidget.box().render(cr, rect);
462     FloatRect contentsRect;
463     comboWidget.button().render(cr, rect, &contentsRect);
464     comboWidget.buttonBox().render(cr, contentsRect);
465     comboWidget.arrow().render(cr, contentsRect);
466     if (isFocused(renderObject))
467         comboWidget.button().renderFocus(cr, rect);
468
469     return false;
470 }
471
472 bool RenderThemeGtk::paintMenuListButtonDecorations(const RenderBox& object, const PaintInfo& info, const FloatRect& rect)
473 {
474     return paintMenuList(object, info, rect);
475 }
476
477 static IntSize spinButtonSize()
478 {
479     auto& spinButtonWidget = static_cast<RenderThemeSpinButton&>(RenderThemeWidget::getOrCreate(RenderThemeWidget::Type::SpinButton));
480     spinButtonWidget.spinButton().setState(GTK_STATE_FLAG_NORMAL);
481     spinButtonWidget.entry().setState(GTK_STATE_FLAG_NORMAL);
482     spinButtonWidget.up().setState(GTK_STATE_FLAG_NORMAL);
483     spinButtonWidget.down().setState(GTK_STATE_FLAG_NORMAL);
484
485     IntSize preferredSize = spinButtonWidget.spinButton().preferredSize();
486     preferredSize = preferredSize.expandedTo(spinButtonWidget.entry().preferredSize());
487     IntSize upPreferredSize = preferredSize.expandedTo(spinButtonWidget.up().preferredSize());
488     IntSize downPreferredSize = preferredSize.expandedTo(spinButtonWidget.down().preferredSize());
489
490     return IntSize(upPreferredSize.width() + downPreferredSize.width(), std::max(upPreferredSize.height(), downPreferredSize.height()));
491 }
492
493
494 void RenderThemeGtk::adjustTextFieldStyle(StyleResolver&, RenderStyle& style, const Element* element) const
495 {
496     if (!is<HTMLInputElement>(element) || !shouldHaveSpinButton(downcast<HTMLInputElement>(*element)))
497         return;
498
499     style.setMinHeight(Length(spinButtonSize().height(), Fixed));
500
501     // The default theme for the GTK+ port uses very wide spin buttons (66px) compared to what other
502     // browsers use (~13 px). And unfortunately, most of the web developers won't test how their site
503     // renders on WebKitGTK+. To ensure that spin buttons don't end up covering the values of the input
504     // field, we override the width of the input element and always increment it with the width needed
505     // for the spinbutton (when drawing the spinbutton).
506     int minimumWidth = style.width().intValue() + spinButtonSize().width();
507     style.setMinWidth(Length(minimumWidth, Fixed));
508 }
509
510 bool RenderThemeGtk::paintTextField(const RenderObject& renderObject, const PaintInfo& paintInfo, const FloatRect& rect)
511 {
512     if (is<HTMLInputElement>(renderObject.node()) && shouldHaveSpinButton(downcast<HTMLInputElement>(*renderObject.node()))) {
513         auto& spinButtonWidget = static_cast<RenderThemeSpinButton&>(RenderThemeWidget::getOrCreate(RenderThemeWidget::Type::SpinButton));
514         auto spinButtonState = themePartStateFlags(*this, Entry, renderObject);
515         spinButtonWidget.spinButton().setState(spinButtonState);
516         spinButtonWidget.entry().setState(spinButtonState);
517         spinButtonWidget.spinButton().render(paintInfo.context().platformContext()->cr(), rect);
518         spinButtonWidget.entry().render(paintInfo.context().platformContext()->cr(), rect);
519     } else {
520         auto& entryWidget = static_cast<RenderThemeEntry&>(RenderThemeWidget::getOrCreate(RenderThemeWidget::Type::Entry));
521         entryWidget.entry().setState(themePartStateFlags(*this, Entry, renderObject));
522         entryWidget.entry().render(paintInfo.context().platformContext()->cr(), rect);
523     }
524     return false;
525 }
526
527 static void adjustSearchFieldIconStyle(RenderThemePart themePart, RenderStyle& style)
528 {
529     ASSERT(themePart == EntryIconLeft || themePart == EntryIconRight);
530     auto& searchEntryWidget = static_cast<RenderThemeSearchEntry&>(RenderThemeWidget::getOrCreate(RenderThemeWidget::Type::SearchEntry));
531     searchEntryWidget.entry().setState(GTK_STATE_FLAG_NORMAL);
532     searchEntryWidget.leftIcon().setState(GTK_STATE_FLAG_NORMAL);
533     searchEntryWidget.rightIcon().setState(GTK_STATE_FLAG_NORMAL);
534
535     // Get the icon size based on the font size.
536     auto& icon = static_cast<RenderThemeIconGadget&>(themePart == EntryIconLeft ? searchEntryWidget.leftIcon() : searchEntryWidget.rightIcon());
537     icon.setIconSize(style.computedFontPixelSize());
538     IntSize preferredSize = icon.preferredSize();
539     GtkBorder contentsBox = searchEntryWidget.entry().contentsBox();
540     if (themePart == EntryIconLeft)
541         preferredSize.expand(contentsBox.left, contentsBox.top + contentsBox.bottom);
542     else
543         preferredSize.expand(contentsBox.right, contentsBox.top + contentsBox.bottom);
544     style.setWidth(Length(preferredSize.width(), Fixed));
545     style.setHeight(Length(preferredSize.height(), Fixed));
546 }
547
548 bool RenderThemeGtk::paintTextArea(const RenderObject& o, const PaintInfo& i, const FloatRect& r)
549 {
550     return paintTextField(o, i, r);
551 }
552
553 void RenderThemeGtk::adjustSearchFieldResultsButtonStyle(StyleResolver& styleResolver, RenderStyle& style, const Element* e) const
554 {
555     adjustSearchFieldCancelButtonStyle(styleResolver, style, e);
556 }
557
558 bool RenderThemeGtk::paintSearchFieldResultsButton(const RenderBox& o, const PaintInfo& i, const IntRect& rect)
559 {
560     return paintSearchFieldResultsDecorationPart(o, i, rect);
561 }
562
563 void RenderThemeGtk::adjustSearchFieldResultsDecorationPartStyle(StyleResolver&, RenderStyle& style, const Element*) const
564 {
565     adjustSearchFieldIconStyle(EntryIconLeft, style);
566 }
567
568 void RenderThemeGtk::adjustSearchFieldCancelButtonStyle(StyleResolver&, RenderStyle& style, const Element*) const
569 {
570     adjustSearchFieldIconStyle(EntryIconRight, style);
571 }
572
573 static bool paintSearchFieldIcon(RenderThemeGtk* theme, RenderThemePart themePart, const RenderBox& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
574 {
575     ASSERT(themePart == EntryIconLeft || themePart == EntryIconRight);
576     auto& searchEntryWidget = static_cast<RenderThemeSearchEntry&>(RenderThemeWidget::getOrCreate(RenderThemeWidget::Type::SearchEntry));
577     searchEntryWidget.entry().setState(themePartStateFlags(*theme, Entry, renderObject));
578     auto& icon = static_cast<RenderThemeIconGadget&>(themePart == EntryIconLeft ? searchEntryWidget.leftIcon() : searchEntryWidget.rightIcon());
579     icon.setState(themePartStateFlags(*theme, themePart, renderObject));
580     icon.setIconSize(renderObject.style().computedFontPixelSize());
581     GtkBorder contentsBox = searchEntryWidget.entry().contentsBox();
582     IntRect iconRect = rect;
583     if (themePart == EntryIconLeft) {
584         iconRect.move(contentsBox.left, contentsBox.top);
585         iconRect.contract(contentsBox.left, contentsBox.top + contentsBox.bottom);
586     } else
587         iconRect.contract(contentsBox.right, contentsBox.top + contentsBox.bottom);
588     return !icon.render(paintInfo.context().platformContext()->cr(), iconRect);
589 }
590 bool RenderThemeGtk::paintSearchFieldResultsDecorationPart(const RenderBox& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
591 {
592     return paintSearchFieldIcon(this, EntryIconLeft, renderObject, paintInfo, rect);
593 }
594
595 bool RenderThemeGtk::paintSearchFieldCancelButton(const RenderBox& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
596 {
597     return paintSearchFieldIcon(this, EntryIconRight, renderObject, paintInfo, rect);
598 }
599
600 void RenderThemeGtk::adjustSearchFieldStyle(StyleResolver&, RenderStyle& style, const Element*) const
601 {
602     // We cannot give a proper rendering when border radius is active, unfortunately.
603     style.resetBorderRadius();
604     style.setLineHeight(RenderStyle::initialLineHeight());
605 }
606
607 bool RenderThemeGtk::paintSearchField(const RenderObject& o, const PaintInfo& i, const IntRect& rect)
608 {
609     return paintTextField(o, i, rect);
610 }
611
612 bool RenderThemeGtk::shouldHaveCapsLockIndicator(const HTMLInputElement& element) const
613 {
614     return element.isPasswordField();
615 }
616
617 void RenderThemeGtk::adjustSliderTrackStyle(StyleResolver&, RenderStyle& style, const Element*) const
618 {
619     style.setBoxShadow(nullptr);
620 }
621
622 void RenderThemeGtk::adjustSliderThumbStyle(StyleResolver& styleResolver, RenderStyle& style, const Element* element) const
623 {
624     RenderTheme::adjustSliderThumbStyle(styleResolver, style, element);
625     style.setBoxShadow(nullptr);
626 }
627
628 /*
629  * GtkScale
630  *
631  * scale
632  * ╰── contents
633  *     ╰── trough
634  *         ├── slider
635  *         ╰── [highlight]
636  */
637 bool RenderThemeGtk::paintSliderTrack(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
638 {
639     ControlPart part = renderObject.style().appearance();
640     ASSERT(part == SliderHorizontalPart || part == SliderVerticalPart);
641
642     auto& sliderWidget = static_cast<RenderThemeSlider&>(RenderThemeWidget::getOrCreate(part == SliderHorizontalPart ? RenderThemeWidget::Type::HorizontalSlider : RenderThemeWidget::Type::VerticalSlider));
643     auto scaleState = themePartStateFlags(*this, Scale, renderObject);
644     auto& scale = sliderWidget.scale();
645     scale.setState(scaleState);
646     auto& contents = sliderWidget.contents();
647     auto& trough = sliderWidget.trough();
648     trough.setState(scaleState);
649     auto& slider = sliderWidget.slider();
650     auto& highlight = sliderWidget.highlight();
651
652     // The given rectangle is not calculated based on the scale size, but all the margins and paddings are based on it.
653     IntSize preferredSize = scale.preferredSize();
654     preferredSize = preferredSize.expandedTo(contents.preferredSize());
655     preferredSize = preferredSize.expandedTo(trough.preferredSize());
656     FloatRect trackRect = rect;
657     if (part == SliderHorizontalPart) {
658         trackRect.move(0, rect.height() / 2 - (preferredSize.height() / 2));
659         trackRect.setHeight(preferredSize.height());
660     } else {
661         trackRect.move(rect.width() / 2 - (preferredSize.width() / 2), 0);
662         trackRect.setWidth(preferredSize.width());
663     }
664
665     FloatRect contentsRect;
666     scale.render(paintInfo.context().platformContext()->cr(), trackRect, &contentsRect);
667     contents.render(paintInfo.context().platformContext()->cr(), contentsRect, &contentsRect);
668     // Scale trough defines its size querying slider and highlight.
669     if (part == SliderHorizontalPart)
670         contentsRect.setHeight(trough.preferredSize().height() + std::max(slider.preferredSize().height(), highlight.preferredSize().height()));
671     else
672         contentsRect.setWidth(trough.preferredSize().width() + std::max(slider.preferredSize().width(), highlight.preferredSize().width()));
673     FloatRect troughRect = contentsRect;
674     trough.render(paintInfo.context().platformContext()->cr(), troughRect, &contentsRect);
675     if (isFocused(renderObject))
676         trough.renderFocus(paintInfo.context().platformContext()->cr(), troughRect);
677
678     LayoutPoint thumbLocation;
679     if (is<HTMLInputElement>(renderObject.node())) {
680         auto& input = downcast<HTMLInputElement>(*renderObject.node());
681         if (auto* element = input.sliderThumbElement())
682             thumbLocation = element->renderBox()->location();
683     }
684
685     if (part == SliderHorizontalPart) {
686         if (renderObject.style().direction() == TextDirection::RTL) {
687             contentsRect.move(thumbLocation.x(), 0);
688             contentsRect.setWidth(contentsRect.width() - thumbLocation.x());
689         } else
690             contentsRect.setWidth(thumbLocation.x());
691     } else
692         contentsRect.setHeight(thumbLocation.y());
693     highlight.render(paintInfo.context().platformContext()->cr(), contentsRect);
694
695     return false;
696 }
697
698 void RenderThemeGtk::adjustSliderThumbSize(RenderStyle& style, const Element*) const
699 {
700     ControlPart part = style.appearance();
701     if (part != SliderThumbHorizontalPart && part != SliderThumbVerticalPart)
702         return;
703
704     auto& sliderWidget = static_cast<RenderThemeSlider&>(RenderThemeWidget::getOrCreate(part == SliderThumbHorizontalPart ? RenderThemeWidget::Type::HorizontalSlider : RenderThemeWidget::Type::VerticalSlider));
705     sliderWidget.scale().setState(GTK_STATE_FLAG_NORMAL);
706     sliderWidget.trough().setState(GTK_STATE_FLAG_NORMAL);
707
708     IntSize preferredSize = sliderWidget.scale().preferredSize();
709     preferredSize = preferredSize.expandedTo(sliderWidget.contents().preferredSize());
710     preferredSize = preferredSize.expandedTo(sliderWidget.trough().preferredSize());
711     preferredSize = preferredSize.expandedTo(sliderWidget.slider().preferredSize());
712     if (part == SliderThumbHorizontalPart) {
713         style.setWidth(Length(preferredSize.width(), Fixed));
714         style.setHeight(Length(preferredSize.height(), Fixed));
715         return;
716     }
717     ASSERT(part == SliderThumbVerticalPart);
718     style.setWidth(Length(preferredSize.height(), Fixed));
719     style.setHeight(Length(preferredSize.width(), Fixed));
720 }
721
722 bool RenderThemeGtk::paintSliderThumb(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
723 {
724     ControlPart part = renderObject.style().appearance();
725     ASSERT(part == SliderThumbHorizontalPart || part == SliderThumbVerticalPart);
726
727     auto& sliderWidget = static_cast<RenderThemeSlider&>(RenderThemeWidget::getOrCreate(part == SliderThumbHorizontalPart ? RenderThemeWidget::Type::HorizontalSlider : RenderThemeWidget::Type::VerticalSlider));
728     auto scaleState = themePartStateFlags(*this, Scale, renderObject);
729     auto& scale = sliderWidget.scale();
730     scale.setState(scaleState);
731     auto& contents = sliderWidget.contents();
732     auto& trough = sliderWidget.trough();
733     trough.setState(scaleState);
734     auto& slider = sliderWidget.slider();
735     slider.setState(themePartStateFlags(*this, ScaleSlider, renderObject));
736     auto& highlight = sliderWidget.highlight();
737
738     GtkBorder scaleContentsBox = scale.contentsBox();
739     GtkBorder contentsContentsBox = contents.contentsBox();
740     GtkBorder troughContentsBox = trough.contentsBox();
741     GtkBorder padding;
742     padding.left = scaleContentsBox.left + contentsContentsBox.left + troughContentsBox.left;
743     padding.right = scaleContentsBox.right + contentsContentsBox.right + troughContentsBox.right;
744     padding.top = scaleContentsBox.top + contentsContentsBox.top + troughContentsBox.top;
745     padding.bottom = scaleContentsBox.bottom + contentsContentsBox.bottom + troughContentsBox.bottom;
746
747     // Scale trough defines its size querying slider and highlight.
748     int troughHeight = trough.preferredSize().height() + std::max(slider.preferredSize().height(), highlight.preferredSize().height());
749     IntRect sliderRect(rect.location(), IntSize(troughHeight, troughHeight));
750     sliderRect.move(padding.left, padding.top);
751     sliderRect.contract(padding.left + padding.right, padding.top + padding.bottom);
752     slider.render(paintInfo.context().platformContext()->cr(), sliderRect);
753     return false;
754 }
755
756 IntRect RenderThemeGtk::progressBarRectForBounds(const RenderObject& renderObject, const IntRect& bounds) const
757 {
758     const auto& renderProgress = downcast<RenderProgress>(renderObject);
759     auto& progressBarWidget = static_cast<RenderThemeProgressBar&>(RenderThemeWidget::getOrCreate(renderProgress.isDeterminate() ? RenderThemeProgressBar::Type::ProgressBar : RenderThemeProgressBar::Type::IndeterminateProgressBar));
760     IntSize preferredSize = progressBarWidget.progressBar().preferredSize();
761     preferredSize = preferredSize.expandedTo(progressBarWidget.trough().preferredSize());
762     preferredSize = preferredSize.expandedTo(progressBarWidget.progress().preferredSize());
763     return IntRect(bounds.x(), bounds.y(), bounds.width(), preferredSize.height());
764 }
765
766 bool RenderThemeGtk::paintProgressBar(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
767 {
768     if (!renderObject.isProgress())
769         return true;
770
771     const auto& renderProgress = downcast<RenderProgress>(renderObject);
772     auto& progressBarWidget = static_cast<RenderThemeProgressBar&>(RenderThemeWidget::getOrCreate(renderProgress.isDeterminate() ? RenderThemeProgressBar::Type::ProgressBar : RenderThemeProgressBar::Type::IndeterminateProgressBar));
773     progressBarWidget.progressBar().render(paintInfo.context().platformContext()->cr(), rect);
774     progressBarWidget.trough().render(paintInfo.context().platformContext()->cr(), rect);
775     progressBarWidget.progress().render(paintInfo.context().platformContext()->cr(), calculateProgressRect(renderObject, rect));
776     return false;
777 }
778
779 RenderTheme::InnerSpinButtonLayout RenderThemeGtk::innerSpinButtonLayout(const RenderObject& renderObject) const
780 {
781     return renderObject.style().direction() == TextDirection::RTL ? InnerSpinButtonLayout::HorizontalUpLeft : InnerSpinButtonLayout::HorizontalUpRight;
782 }
783
784 void RenderThemeGtk::adjustInnerSpinButtonStyle(StyleResolver&, RenderStyle& style, const Element*) const
785 {
786     style.setWidth(Length(spinButtonSize().width(), Fixed));
787     style.setHeight(Length(spinButtonSize().height(), Fixed));
788 }
789
790 bool RenderThemeGtk::paintInnerSpinButton(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
791 {
792     auto& spinButtonWidget = static_cast<RenderThemeSpinButton&>(RenderThemeWidget::getOrCreate(RenderThemeWidget::Type::SpinButton));
793     auto spinButtonState = themePartStateFlags(*this, SpinButton, renderObject);
794     spinButtonWidget.spinButton().setState(spinButtonState);
795     spinButtonWidget.entry().setState(spinButtonState);
796     auto& up = spinButtonWidget.up();
797     up.setState(themePartStateFlags(*this, SpinButtonUpButton, renderObject));
798     auto& down = spinButtonWidget.down();
799     down.setState(themePartStateFlags(*this, SpinButtonDownButton, renderObject));
800
801     IntRect iconRect = rect;
802     iconRect.setWidth(iconRect.width() / 2);
803     if (renderObject.style().direction() == TextDirection::RTL)
804         up.render(paintInfo.context().platformContext()->cr(), iconRect);
805     else
806         down.render(paintInfo.context().platformContext()->cr(), iconRect);
807     iconRect.move(iconRect.width(), 0);
808     if (renderObject.style().direction() == TextDirection::RTL)
809         down.render(paintInfo.context().platformContext()->cr(), iconRect);
810     else
811         up.render(paintInfo.context().platformContext()->cr(), iconRect);
812
813     return false;
814 }
815
816 Seconds RenderThemeGtk::caretBlinkInterval() const
817 {
818     GtkSettings* settings = gtk_settings_get_default();
819
820     gboolean shouldBlink;
821     gint time;
822
823     g_object_get(settings, "gtk-cursor-blink", &shouldBlink, "gtk-cursor-blink-time", &time, nullptr);
824
825     if (!shouldBlink)
826         return 0_s;
827
828     return 500_us * time;
829 }
830
831 enum StyleColorType { StyleColorBackground, StyleColorForeground };
832
833 static Color styleColor(RenderThemePart themePart, GtkStateFlags state, StyleColorType colorType)
834 {
835     RenderThemeGadget* gadget = nullptr;
836     switch (themePart) {
837     default:
838         ASSERT_NOT_REACHED();
839         FALLTHROUGH;
840     case Entry:
841         gadget = &static_cast<RenderThemeEntry&>(RenderThemeWidget::getOrCreate(RenderThemeWidget::Type::Entry)).entry();
842         break;
843     case EntrySelection:
844         gadget = static_cast<RenderThemeEntry&>(RenderThemeWidget::getOrCreate(RenderThemeWidget::Type::SelectedEntry)).selection();
845         break;
846     case ListBox:
847         gadget = &static_cast<RenderThemeListView&>(RenderThemeWidget::getOrCreate(RenderThemeWidget::Type::ListView)).treeview();
848         break;
849     case Button:
850         gadget = &static_cast<RenderThemeButton&>(RenderThemeWidget::getOrCreate(RenderThemeWidget::Type::Button)).button();
851         break;
852     case Window:
853         gadget = &static_cast<RenderThemeWindow&>(RenderThemeWidget::getOrCreate(RenderThemeWidget::Type::Window)).window();
854         break;
855     }
856
857     ASSERT(gadget);
858     gadget->setState(state);
859     return colorType == StyleColorBackground ? gadget->backgroundColor() : gadget->color();
860 }
861
862 Color RenderThemeGtk::platformActiveSelectionBackgroundColor(OptionSet<StyleColor::Options>) const
863 {
864     return styleColor(EntrySelection, static_cast<GtkStateFlags>(GTK_STATE_FLAG_SELECTED | GTK_STATE_FLAG_FOCUSED), StyleColorBackground);
865 }
866
867 Color RenderThemeGtk::platformInactiveSelectionBackgroundColor(OptionSet<StyleColor::Options>) const
868 {
869     return styleColor(EntrySelection, GTK_STATE_FLAG_SELECTED, StyleColorBackground);
870 }
871
872 Color RenderThemeGtk::platformActiveSelectionForegroundColor(OptionSet<StyleColor::Options>) const
873 {
874     return styleColor(EntrySelection, static_cast<GtkStateFlags>(GTK_STATE_FLAG_SELECTED | GTK_STATE_FLAG_FOCUSED), StyleColorForeground);
875 }
876
877 Color RenderThemeGtk::platformInactiveSelectionForegroundColor(OptionSet<StyleColor::Options>) const
878 {
879     return styleColor(EntrySelection, GTK_STATE_FLAG_SELECTED, StyleColorForeground);
880 }
881
882 Color RenderThemeGtk::platformActiveListBoxSelectionBackgroundColor(OptionSet<StyleColor::Options>) const
883 {
884     return styleColor(ListBox, static_cast<GtkStateFlags>(GTK_STATE_FLAG_SELECTED | GTK_STATE_FLAG_FOCUSED), StyleColorBackground);
885 }
886
887 Color RenderThemeGtk::platformInactiveListBoxSelectionBackgroundColor(OptionSet<StyleColor::Options>) const
888 {
889     return styleColor(ListBox, GTK_STATE_FLAG_SELECTED, StyleColorBackground);
890 }
891
892 Color RenderThemeGtk::platformActiveListBoxSelectionForegroundColor(OptionSet<StyleColor::Options>) const
893 {
894     return styleColor(ListBox, static_cast<GtkStateFlags>(GTK_STATE_FLAG_SELECTED | GTK_STATE_FLAG_FOCUSED), StyleColorForeground);
895 }
896
897 Color RenderThemeGtk::platformInactiveListBoxSelectionForegroundColor(OptionSet<StyleColor::Options>) const
898 {
899     return styleColor(ListBox, GTK_STATE_FLAG_SELECTED, StyleColorForeground);
900 }
901
902 Color RenderThemeGtk::disabledTextColor(const Color&, const Color&) const
903 {
904     return styleColor(Entry, GTK_STATE_FLAG_INSENSITIVE, StyleColorForeground);
905 }
906
907 Color RenderThemeGtk::systemColor(CSSValueID cssValueId, OptionSet<StyleColor::Options> options) const
908 {
909     switch (cssValueId) {
910     case CSSValueButtontext:
911         return styleColor(Button, GTK_STATE_FLAG_ACTIVE, StyleColorForeground);
912     case CSSValueCaptiontext:
913         return styleColor(Entry, GTK_STATE_FLAG_ACTIVE, StyleColorForeground);
914     case CSSValueText:
915         return styleColor(Entry, GTK_STATE_FLAG_ACTIVE, StyleColorForeground);
916     case CSSValueGraytext:
917         return styleColor(Entry, GTK_STATE_FLAG_INSENSITIVE, StyleColorForeground);
918     case CSSValueWebkitControlBackground:
919         return styleColor(Entry, GTK_STATE_FLAG_ACTIVE, StyleColorBackground);
920     case CSSValueWindow: {
921         // Only get window color from the theme in dark mode.
922         gboolean preferDarkTheme = FALSE;
923         if (auto* settings = gtk_settings_get_default())
924             g_object_get(settings, "gtk-application-prefer-dark-theme", &preferDarkTheme, nullptr);
925         if (preferDarkTheme)
926             return styleColor(Window, GTK_STATE_FLAG_ACTIVE, StyleColorBackground);
927         break;
928     }
929     default:
930         break;
931     }
932
933     return RenderTheme::systemColor(cssValueId, options);
934 }
935
936 void RenderThemeGtk::platformColorsDidChange()
937 {
938     RenderTheme::platformColorsDidChange();
939 }
940
941 #if ENABLE(VIDEO)
942 String RenderThemeGtk::extraMediaControlsStyleSheet()
943 {
944     return String(mediaControlsGtkUserAgentStyleSheet, sizeof(mediaControlsGtkUserAgentStyleSheet));
945 }
946
947 #if ENABLE(FULLSCREEN_API)
948 String RenderThemeGtk::extraFullScreenStyleSheet()
949 {
950     return String();
951 }
952 #endif
953
954 bool RenderThemeGtk::paintMediaButton(const RenderObject& renderObject, GraphicsContext& graphicsContext, const IntRect& rect, const char* iconName)
955 {
956     auto& iconWidget = static_cast<RenderThemeIcon&>(RenderThemeWidget::getOrCreate(RenderThemeWidget::Type::Icon));
957     auto& icon = static_cast<RenderThemeIconGadget&>(iconWidget.icon());
958     icon.setState(themePartStateFlags(*this, MediaButton, renderObject));
959     icon.setIconSize(RenderThemeIconGadget::IconSizeGtk::Menu);
960     icon.setIconName(iconName);
961     return !icon.render(graphicsContext.platformContext()->cr(), rect);
962 }
963
964 bool RenderThemeGtk::hasOwnDisabledStateHandlingFor(ControlPart part) const
965 {
966     return (part != MediaMuteButtonPart);
967 }
968
969 bool RenderThemeGtk::paintMediaFullscreenButton(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
970 {
971     return paintMediaButton(renderObject, paintInfo.context(), rect, "view-fullscreen-symbolic");
972 }
973
974 bool RenderThemeGtk::paintMediaMuteButton(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
975 {
976     Node* node = renderObject.node();
977     if (!node)
978         return true;
979     Node* mediaNode = node->shadowHost();
980     if (!is<HTMLMediaElement>(mediaNode))
981         return true;
982
983     HTMLMediaElement* mediaElement = downcast<HTMLMediaElement>(mediaNode);
984     return paintMediaButton(renderObject, paintInfo.context(), rect, mediaElement->muted() ? "audio-volume-muted-symbolic" : "audio-volume-high-symbolic");
985 }
986
987 bool RenderThemeGtk::paintMediaPlayButton(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
988 {
989     Node* node = renderObject.node();
990     if (!node)
991         return true;
992     if (!nodeHasPseudo(*node, "-webkit-media-controls-play-button"))
993         return true;
994
995     return paintMediaButton(renderObject, paintInfo.context(), rect, nodeHasClass(node, "paused") ? "media-playback-start-symbolic" : "media-playback-pause-symbolic");
996 }
997
998 bool RenderThemeGtk::paintMediaSeekBackButton(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
999 {
1000     return paintMediaButton(renderObject, paintInfo.context(), rect, "media-seek-backward-symbolic");
1001 }
1002
1003 bool RenderThemeGtk::paintMediaSeekForwardButton(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
1004 {
1005     return paintMediaButton(renderObject, paintInfo.context(), rect, "media-seek-forward-symbolic");
1006 }
1007
1008 #if ENABLE(VIDEO_TRACK)
1009 bool RenderThemeGtk::paintMediaToggleClosedCaptionsButton(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
1010 {
1011     return paintMediaButton(renderObject, paintInfo.context(), rect, "media-view-subtitles-symbolic");
1012 }
1013 #endif
1014
1015 static FloatRoundedRect::Radii borderRadiiFromStyle(const RenderStyle& style)
1016 {
1017     return FloatRoundedRect::Radii(
1018         IntSize(style.borderTopLeftRadius().width.intValue(), style.borderTopLeftRadius().height.intValue()),
1019         IntSize(style.borderTopRightRadius().width.intValue(), style.borderTopRightRadius().height.intValue()),
1020         IntSize(style.borderBottomLeftRadius().width.intValue(), style.borderBottomLeftRadius().height.intValue()),
1021         IntSize(style.borderBottomRightRadius().width.intValue(), style.borderBottomRightRadius().height.intValue()));
1022 }
1023
1024 bool RenderThemeGtk::paintMediaSliderTrack(const RenderObject& o, const PaintInfo& paintInfo, const IntRect& r)
1025 {
1026     auto mediaElement = parentMediaElement(o);
1027     if (!mediaElement)
1028         return true;
1029
1030     GraphicsContext& context = paintInfo.context();
1031     context.save();
1032     context.setStrokeStyle(NoStroke);
1033
1034     float mediaDuration = mediaElement->duration();
1035     float totalTrackWidth = r.width();
1036     auto& style = o.style();
1037     RefPtr<TimeRanges> timeRanges = mediaElement->buffered();
1038     for (unsigned index = 0; index < timeRanges->length(); ++index) {
1039         float start = timeRanges->start(index).releaseReturnValue();
1040         float end = timeRanges->end(index).releaseReturnValue();
1041         float startRatio = start / mediaDuration;
1042         float lengthRatio = (end - start) / mediaDuration;
1043         if (!lengthRatio)
1044             continue;
1045
1046         IntRect rangeRect(r);
1047         rangeRect.setWidth(lengthRatio * totalTrackWidth);
1048         if (index)
1049             rangeRect.move(startRatio * totalTrackWidth, 0);
1050         context.fillRoundedRect(FloatRoundedRect(rangeRect, borderRadiiFromStyle(style)), style.visitedDependentColor(CSSPropertyColor));
1051     }
1052
1053     context.restore();
1054     return false;
1055 }
1056
1057 bool RenderThemeGtk::paintMediaSliderThumb(const RenderObject& o, const PaintInfo& paintInfo, const IntRect& r)
1058 {
1059     auto& style = o.style();
1060     paintInfo.context().fillRoundedRect(FloatRoundedRect(r, borderRadiiFromStyle(style)), style.visitedDependentColor(CSSPropertyColor));
1061     return false;
1062 }
1063
1064 bool RenderThemeGtk::paintMediaVolumeSliderTrack(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
1065 {
1066     auto mediaElement = parentMediaElement(renderObject);
1067     if (!mediaElement)
1068         return true;
1069
1070     float volume = mediaElement->muted() ? 0.0f : mediaElement->volume();
1071     if (!volume)
1072         return true;
1073
1074     GraphicsContext& context = paintInfo.context();
1075     context.save();
1076     context.setStrokeStyle(NoStroke);
1077
1078     int rectHeight = rect.height();
1079     float trackHeight = rectHeight * volume;
1080     auto& style = renderObject.style();
1081     IntRect volumeRect(rect);
1082     volumeRect.move(0, rectHeight - trackHeight);
1083     volumeRect.setHeight(ceil(trackHeight));
1084
1085     context.fillRoundedRect(FloatRoundedRect(volumeRect, borderRadiiFromStyle(style)), style.visitedDependentColor(CSSPropertyColor));
1086     context.restore();
1087
1088     return false;
1089 }
1090
1091 bool RenderThemeGtk::paintMediaVolumeSliderThumb(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
1092 {
1093     return paintMediaSliderThumb(renderObject, paintInfo, rect);
1094 }
1095
1096 String RenderThemeGtk::formatMediaControlsCurrentTime(float currentTime, float duration) const
1097 {
1098     return formatMediaControlsTime(currentTime) + " / " + formatMediaControlsTime(duration);
1099 }
1100
1101 bool RenderThemeGtk::paintMediaCurrentTime(const RenderObject&, const PaintInfo&, const IntRect&)
1102 {
1103     return false;
1104 }
1105 #endif
1106
1107 void RenderThemeGtk::adjustProgressBarStyle(StyleResolver&, RenderStyle& style, const Element*) const
1108 {
1109     style.setBoxShadow(nullptr);
1110 }
1111
1112 // These values have been copied from RenderThemeChromiumSkia.cpp
1113 static const int progressActivityBlocks = 5;
1114 static const int progressAnimationFrames = 10;
1115 static const Seconds progressAnimationInterval { 125_ms };
1116 Seconds RenderThemeGtk::animationRepeatIntervalForProgressBar(RenderProgress&) const
1117 {
1118     return progressAnimationInterval;
1119 }
1120
1121 Seconds RenderThemeGtk::animationDurationForProgressBar(RenderProgress&) const
1122 {
1123     return progressAnimationInterval * progressAnimationFrames * 2; // "2" for back and forth;
1124 }
1125
1126 IntRect RenderThemeGtk::calculateProgressRect(const RenderObject& renderObject, const IntRect& fullBarRect)
1127 {
1128     IntRect progressRect(fullBarRect);
1129     const auto& renderProgress = downcast<RenderProgress>(renderObject);
1130     if (renderProgress.isDeterminate()) {
1131         int progressWidth = progressRect.width() * renderProgress.position();
1132         if (renderObject.style().direction() == TextDirection::RTL)
1133             progressRect.setX(progressRect.x() + progressRect.width() - progressWidth);
1134         progressRect.setWidth(progressWidth);
1135         return progressRect;
1136     }
1137
1138     double animationProgress = renderProgress.animationProgress();
1139
1140     // Never let the progress rect shrink smaller than 2 pixels.
1141     int newWidth = std::max(2, progressRect.width() / progressActivityBlocks);
1142     int movableWidth = progressRect.width() - newWidth;
1143     progressRect.setWidth(newWidth);
1144
1145     // We want the first 0.5 units of the animation progress to represent the
1146     // forward motion and the second 0.5 units to represent the backward motion,
1147     // thus we multiply by two here to get the full sweep of the progress bar with
1148     // each direction.
1149     if (animationProgress < 0.5)
1150         progressRect.setX(progressRect.x() + (animationProgress * 2 * movableWidth));
1151     else
1152         progressRect.setX(progressRect.x() + ((1.0 - animationProgress) * 2 * movableWidth));
1153     return progressRect;
1154 }
1155
1156 String RenderThemeGtk::fileListNameForWidth(const FileList* fileList, const FontCascade& font, int width, bool multipleFilesAllowed) const
1157 {
1158     if (width <= 0)
1159         return String();
1160
1161     if (fileList->length() > 1)
1162         return StringTruncator::rightTruncate(multipleFileUploadText(fileList->length()), width, font);
1163
1164     String string;
1165     if (fileList->length())
1166         string = FileSystem::pathGetFileName(fileList->item(0)->path());
1167     else if (multipleFilesAllowed)
1168         string = fileButtonNoFilesSelectedLabel();
1169     else
1170         string = fileButtonNoFileSelectedLabel();
1171
1172     return StringTruncator::centerTruncate(string, width, font);
1173 }
1174
1175 #if ENABLE(VIDEO)
1176 String RenderThemeGtk::mediaControlsScript()
1177 {
1178     StringBuilder scriptBuilder;
1179     scriptBuilder.append(mediaControlsLocalizedStringsJavaScript, sizeof(mediaControlsLocalizedStringsJavaScript));
1180     scriptBuilder.append(mediaControlsBaseJavaScript, sizeof(mediaControlsBaseJavaScript));
1181     scriptBuilder.append(mediaControlsGtkJavaScript, sizeof(mediaControlsGtkJavaScript));
1182     return scriptBuilder.toString();
1183 }
1184 #endif // ENABLE(VIDEO)
1185 }