[GTK] Menu list button doesn't use the text color from the theme
[WebKit-https.git] / Source / WebCore / rendering / RenderThemeGtk.cpp
index c59875a..f4694da 100644 (file)
 #include "FileList.h"
 #include "FileSystem.h"
 #include "FontDescription.h"
+#include "GRefPtrGtk.h"
+#include "GUniquePtrGtk.h"
 #include "Gradient.h"
 #include "GraphicsContext.h"
 #include "GtkVersioning.h"
+#include "HTMLInputElement.h"
 #include "HTMLMediaElement.h"
 #include "LocalizedStrings.h"
 #include "MediaControlElements.h"
@@ -43,6 +46,7 @@
 #include "RenderBox.h"
 #include "RenderObject.h"
 #include "RenderProgress.h"
+#include "RenderThemeGadget.h"
 #include "ScrollbarThemeGtk.h"
 #include "StringTruncator.h"
 #include "TimeRanges.h"
 #include <glib.h>
 #include <gtk/gtk.h>
 #include <wtf/NeverDestroyed.h>
-#include <wtf/gobject/GRefPtr.h>
-#include <wtf/gobject/GUniquePtr.h>
+#include <wtf/glib/GRefPtr.h>
+#include <wtf/glib/GUniquePtr.h>
 #include <wtf/text/CString.h>
 #include <wtf/text/StringBuilder.h>
 
 namespace WebCore {
 
-PassRefPtr<RenderTheme> RenderThemeGtk::create()
+Ref<RenderTheme> RenderThemeGtk::create()
 {
-    return adoptRef(new RenderThemeGtk());
+    return adoptRef(*new RenderThemeGtk());
 }
 
 PassRefPtr<RenderTheme> RenderTheme::themeForPage(Page*)
 {
-    static RenderTheme* rt = RenderThemeGtk::create().leakRef();
-    return rt;
+    static RenderTheme& rt = RenderThemeGtk::create().leakRef();
+    return &rt;
 }
 
 static double getScreenDPI()
@@ -84,7 +88,7 @@ static double getScreenDPI()
     return dpi;
 }
 
-void RenderThemeGtk::systemFont(CSSValueID, FontDescription& fontDescription) const
+void RenderThemeGtk::updateCachedSystemFontDescription(CSSValueID, FontCascadeDescription& fontDescription) const
 {
     GtkSettings* settings = gtk_settings_get_default();
     if (!settings)
@@ -93,6 +97,8 @@ void RenderThemeGtk::systemFont(CSSValueID, FontDescription& fontDescription) co
     // This will be a font selection string like "Sans 10" so we cannot use it as the family name.
     GUniqueOutPtr<gchar> fontName;
     g_object_get(settings, "gtk-font-name", &fontName.outPtr(), nullptr);
+    if (!fontName || !fontName.get()[0])
+        return;
 
     PangoFontDescription* pangoDescription = pango_font_description_from_string(fontName.get());
     if (!pangoDescription)
@@ -107,9 +113,8 @@ void RenderThemeGtk::systemFont(CSSValueID, FontDescription& fontDescription) co
 
     fontDescription.setSpecifiedSize(size);
     fontDescription.setIsAbsoluteSize(true);
-    fontDescription.setGenericFamily(FontDescription::NoFamily);
     fontDescription.setWeight(FontWeightNormal);
-    fontDescription.setItalic(false);
+    fontDescription.setItalic(FontItalicOff);
     pango_font_description_free(pangoDescription);
 }
 
@@ -129,183 +134,172 @@ int RenderThemeGtk::sliderTickOffsetFromTrackCenter() const
 
 #ifndef GTK_API_VERSION_2
 
-// This is the default value defined by GTK+, where it was defined as MIN_ARROW_SIZE in gtkarrow.c.
-static const int minArrowSize = 15;
-// This is the default value defined by GTK+, where it was defined as MIN_ARROW_WIDTH in gtkspinbutton.c.
-static const int minSpinButtonArrowSize = 6;
-
-typedef HashMap<GType, GRefPtr<GtkStyleContext>> StyleContextMap;
-static StyleContextMap& styleContextMap();
-
-static void gtkStyleChangedCallback(GObject*, GParamSpec*)
+static void themeChangedCallback()
 {
-    for (const auto& styleContext : styleContextMap())
-        gtk_style_context_invalidate(styleContext.value.get());
-    static_cast<ScrollbarThemeGtk*>(ScrollbarTheme::theme())->themeChanged();
     Page::updateStyleForAllPagesAfterGlobalChangeInEnvironment();
 }
 
-static StyleContextMap& styleContextMap()
+RenderThemeGtk::RenderThemeGtk()
 {
-    static NeverDestroyed<StyleContextMap> map;
-    static bool initialized = false;
-    if (!initialized) {
+    static bool themeMonitorInitialized = false;
+    if (!themeMonitorInitialized) {
         GtkSettings* settings = gtk_settings_get_default();
-        g_signal_connect(settings, "notify::gtk-theme-name", G_CALLBACK(gtkStyleChangedCallback), nullptr);
-        g_signal_connect(settings, "notify::gtk-color-scheme", G_CALLBACK(gtkStyleChangedCallback), nullptr);
-        initialized = true;
+        g_signal_connect(settings, "notify::gtk-theme-name", G_CALLBACK(themeChangedCallback), nullptr);
+        g_signal_connect(settings, "notify::gtk-color-scheme", G_CALLBACK(themeChangedCallback), nullptr);
+        themeMonitorInitialized = true;
     }
-    return map;
-}
-
-static GtkStyleContext* getStyleContext(GType widgetType)
-{
-    StyleContextMap::AddResult result = styleContextMap().add(widgetType, nullptr);
-    if (!result.isNewEntry)
-        return result.iterator->value.get();
-
-    GtkWidgetPath* path = gtk_widget_path_new();
-    gtk_widget_path_append_type(path, widgetType);
-
-    if (widgetType == GTK_TYPE_ENTRY)
-        gtk_widget_path_iter_add_class(path, 0, GTK_STYLE_CLASS_ENTRY);
-    else if (widgetType == GTK_TYPE_ARROW)
-        gtk_widget_path_iter_add_class(path, 0, "arrow");
-    else if (widgetType == GTK_TYPE_BUTTON) {
-        gtk_widget_path_iter_add_class(path, 0, GTK_STYLE_CLASS_BUTTON);
-        gtk_widget_path_iter_add_class(path, 1, "text-button");
-    } else if (widgetType == GTK_TYPE_SCALE)
-        gtk_widget_path_iter_add_class(path, 0, GTK_STYLE_CLASS_SCALE);
-    else if (widgetType == GTK_TYPE_SEPARATOR)
-        gtk_widget_path_iter_add_class(path, 0, GTK_STYLE_CLASS_SEPARATOR);
-    else if (widgetType == GTK_TYPE_PROGRESS_BAR)
-        gtk_widget_path_iter_add_class(path, 0, GTK_STYLE_CLASS_PROGRESSBAR);
-    else if (widgetType == GTK_TYPE_SPIN_BUTTON)
-        gtk_widget_path_iter_add_class(path, 0, GTK_STYLE_CLASS_SPINBUTTON);
-    else if (widgetType == GTK_TYPE_TREE_VIEW)
-        gtk_widget_path_iter_add_class(path, 0, GTK_STYLE_CLASS_VIEW);
-    else if (widgetType == GTK_TYPE_CHECK_BUTTON)
-        gtk_widget_path_iter_add_class(path, 0, GTK_STYLE_CLASS_CHECK);
-    else if (widgetType == GTK_TYPE_RADIO_BUTTON)
-        gtk_widget_path_iter_add_class(path, 0, GTK_STYLE_CLASS_RADIO);
-
-    GRefPtr<GtkStyleContext> context = adoptGRef(gtk_style_context_new());
-    gtk_style_context_set_path(context.get(), path);
-    gtk_widget_path_free(path);
-
-    result.iterator->value = context;
-    return context.get();
 }
 
-static GRefPtr<GdkPixbuf> getStockIconForWidgetType(GType widgetType, const char* iconName, gint direction, gint state, gint iconSize)
-{
-    ASSERT(iconName);
-
-    GtkStyleContext* context = getStyleContext(widgetType);
-    GtkIconSet* iconSet = gtk_style_context_lookup_icon_set(context, iconName);
-
-    gtk_style_context_save(context);
-
-    guint flags = 0;
-    if (state == GTK_STATE_PRELIGHT)
-        flags |= GTK_STATE_FLAG_PRELIGHT;
-    else if (state == GTK_STATE_INSENSITIVE)
-        flags |= GTK_STATE_FLAG_INSENSITIVE;
-
-    gtk_style_context_set_state(context, static_cast<GtkStateFlags>(flags));
-    gtk_style_context_set_direction(context, static_cast<GtkTextDirection>(direction));
-    GdkPixbuf* icon = gtk_icon_set_render_icon_pixbuf(iconSet, context, static_cast<GtkIconSize>(iconSize));
-
-    gtk_style_context_restore(context);
+enum RenderThemePart {
+    Entry,
+    EntrySelection,
+    EntryIconLeft,
+    EntryIconRight,
+    Button,
+    CheckButton,
+    RadioButton,
+    ComboBox,
+    ComboBoxButton,
+    ComboBoxArrow,
+    Scale,
+    ScaleTrough,
+    ScaleSlider,
+    ProgressBar,
+    ProgressBarTrough,
+    ProgressBarProgress,
+    ListBox,
+    SpinButton,
+    SpinButtonUpButton,
+    SpinButtonDownButton,
+#if ENABLE(VIDEO)
+    MediaButton,
+#endif
+};
 
-    return adoptGRef(icon);
-}
+#if !GTK_CHECK_VERSION(3, 20, 0)
+// This is the default value defined by GTK+, where it was defined as MIN_ARROW_SIZE in gtkarrow.c.
+static const int minArrowSize = 15;
+// This is the default value defined by GTK+, where it was defined as MIN_ARROW_WIDTH in gtkspinbutton.c.
+static const int minSpinButtonArrowSize = 6;
 
-static GRefPtr<GdkPixbuf> getStockSymbolicIconForWidgetType(GType widgetType, const char* symbolicIconName, const char* fallbackStockIconName, gint direction, gint state, gint iconSize)
+static GRefPtr<GtkStyleContext> createStyleContext(RenderThemePart themePart, GtkStyleContext* parent = nullptr)
 {
-    GtkStyleContext* context = getStyleContext(widgetType);
-
-    gtk_style_context_save(context);
-
-    guint flags = 0;
-    if (state == GTK_STATE_PRELIGHT)
-        flags |= GTK_STATE_FLAG_PRELIGHT;
-    else if (state == GTK_STATE_INSENSITIVE)
-        flags |= GTK_STATE_FLAG_INSENSITIVE;
-
-    gtk_style_context_set_state(context, static_cast<GtkStateFlags>(flags));
-    gtk_style_context_set_direction(context, static_cast<GtkTextDirection>(direction));
-    GtkIconInfo* info = gtk_icon_theme_lookup_icon(gtk_icon_theme_get_default(), symbolicIconName, iconSize,
-        static_cast<GtkIconLookupFlags>(GTK_ICON_LOOKUP_FORCE_SVG | GTK_ICON_LOOKUP_FORCE_SIZE));
-    GdkPixbuf* icon = 0;
-    if (info) {
-        icon = gtk_icon_info_load_symbolic_for_context(info, context, 0, 0);
-        gtk_icon_info_free(info);
-    }
+    GRefPtr<GtkWidgetPath> path = adoptGRef(parent ? gtk_widget_path_copy(gtk_style_context_get_path(parent)) : gtk_widget_path_new());
 
-    gtk_style_context_restore(context);
-
-    if (!icon) {
-        if (!fallbackStockIconName)
-            return nullptr;
-        return getStockIconForWidgetType(widgetType, fallbackStockIconName, direction, state, iconSize);
+    switch (themePart) {
+    case Entry:
+        gtk_widget_path_append_type(path.get(), GTK_TYPE_ENTRY);
+        gtk_widget_path_iter_add_class(path.get(), -1, GTK_STYLE_CLASS_ENTRY);
+        break;
+    case EntryIconLeft:
+    case EntryIconRight:
+        gtk_widget_path_append_type(path.get(), GTK_TYPE_ENTRY);
+        break;
+    case Button:
+        gtk_widget_path_append_type(path.get(), GTK_TYPE_BUTTON);
+        gtk_widget_path_iter_add_class(path.get(), -1, GTK_STYLE_CLASS_BUTTON);
+        gtk_widget_path_iter_add_class(path.get(), -1, "text-button");
+        break;
+    case CheckButton:
+        gtk_widget_path_append_type(path.get(), GTK_TYPE_CHECK_BUTTON);
+        gtk_widget_path_iter_add_class(path.get(), -1, GTK_STYLE_CLASS_CHECK);
+        break;
+    case RadioButton:
+        gtk_widget_path_append_type(path.get(), GTK_TYPE_RADIO_BUTTON);
+        gtk_widget_path_iter_add_class(path.get(), -1, GTK_STYLE_CLASS_RADIO);
+        break;
+    case ComboBox:
+        gtk_widget_path_append_type(path.get(), GTK_TYPE_COMBO_BOX);
+        break;
+    case ComboBoxButton:
+        gtk_widget_path_append_type(path.get(), GTK_TYPE_BUTTON);
+        gtk_widget_path_iter_add_class(path.get(), -1, GTK_STYLE_CLASS_BUTTON);
+        gtk_widget_path_iter_add_class(path.get(), -1, "text-button");
+        gtk_widget_path_iter_add_class(path.get(), -1, "combo");
+        break;
+    case ComboBoxArrow:
+        gtk_widget_path_append_type(path.get(), GTK_TYPE_ARROW);
+        gtk_widget_path_iter_add_class(path.get(), -1, "arrow");
+        break;
+    case Scale:
+        gtk_widget_path_append_type(path.get(), GTK_TYPE_SCALE);
+        gtk_widget_path_iter_add_class(path.get(), -1, GTK_STYLE_CLASS_SCALE);
+        break;
+    case ScaleTrough:
+        gtk_widget_path_append_type(path.get(), GTK_TYPE_SCALE);
+        gtk_widget_path_iter_add_class(path.get(), -1, GTK_STYLE_CLASS_SCALE);
+        gtk_widget_path_iter_add_class(path.get(), -1, GTK_STYLE_CLASS_TROUGH);
+        break;
+    case ScaleSlider:
+        gtk_widget_path_append_type(path.get(), GTK_TYPE_SCALE);
+        gtk_widget_path_iter_add_class(path.get(), -1, GTK_STYLE_CLASS_SCALE);
+        gtk_widget_path_iter_add_class(path.get(), -1, GTK_STYLE_CLASS_SLIDER);
+        break;
+    case ProgressBar:
+        gtk_widget_path_append_type(path.get(), GTK_TYPE_PROGRESS_BAR);
+        gtk_widget_path_iter_add_class(path.get(), -1, GTK_STYLE_CLASS_PROGRESSBAR);
+        gtk_widget_path_iter_add_class(path.get(), -1, GTK_STYLE_CLASS_HORIZONTAL);
+        break;
+    case ProgressBarTrough:
+        gtk_widget_path_append_type(path.get(), GTK_TYPE_PROGRESS_BAR);
+        gtk_widget_path_iter_add_class(path.get(), -1, GTK_STYLE_CLASS_TROUGH);
+        break;
+    case ProgressBarProgress:
+        gtk_widget_path_append_type(path.get(), GTK_TYPE_PROGRESS_BAR);
+        gtk_widget_path_iter_add_class(path.get(), -1, GTK_STYLE_CLASS_PROGRESSBAR);
+        break;
+    case ListBox:
+        gtk_widget_path_append_type(path.get(), GTK_TYPE_TREE_VIEW);
+        gtk_widget_path_iter_add_class(path.get(), -1, GTK_STYLE_CLASS_VIEW);
+        break;
+    case SpinButton:
+        gtk_widget_path_append_type(path.get(), GTK_TYPE_SPIN_BUTTON);
+        gtk_widget_path_iter_add_class(path.get(), -1, GTK_STYLE_CLASS_SPINBUTTON);
+        gtk_widget_path_iter_add_class(path.get(), -1, GTK_STYLE_CLASS_HORIZONTAL);
+        break;
+    case SpinButtonUpButton:
+    case SpinButtonDownButton:
+        gtk_widget_path_append_type(path.get(), GTK_TYPE_SPIN_BUTTON);
+        gtk_widget_path_iter_add_class(path.get(), -1, GTK_STYLE_CLASS_SPINBUTTON);
+        gtk_widget_path_iter_add_class(path.get(), -1, GTK_STYLE_CLASS_BUTTON);
+        break;
+#if ENABLE(VIDEO)
+    case MediaButton:
+        gtk_widget_path_append_type(path.get(), GTK_TYPE_IMAGE);
+        gtk_widget_path_iter_add_class(path.get(), -1, GTK_STYLE_CLASS_IMAGE);
+        break;
+#endif // ENABLE(VIDEO)
+    default:
+        ASSERT_NOT_REACHED();
+        break;
     }
 
-    return adoptGRef(icon);
+    GRefPtr<GtkStyleContext> context = adoptGRef(gtk_style_context_new());
+    gtk_style_context_set_path(context.get(), path.get());
+    gtk_style_context_set_parent(context.get(), parent);
+    return context;
 }
 
-#if ENABLE(VIDEO)
-static HTMLMediaElement* getMediaElementFromRenderObject(const RenderObject& o)
+static GRefPtr<GdkPixbuf> loadThemedIcon(GtkStyleContext* context, const char* iconName, GtkIconSize iconSize)
 {
-    Node* node = o.node();
-    Node* mediaNode = node ? node->shadowHost() : nullptr;
-    if (!mediaNode)
-        mediaNode = node;
-    if (!is<HTMLMediaElement>(mediaNode))
+    GRefPtr<GIcon> icon = adoptGRef(g_themed_icon_new(iconName));
+    unsigned lookupFlags = GTK_ICON_LOOKUP_USE_BUILTIN | GTK_ICON_LOOKUP_FORCE_SIZE | GTK_ICON_LOOKUP_FORCE_SVG;
+#if GTK_CHECK_VERSION(3, 14, 0)
+    GtkTextDirection direction = gtk_style_context_get_direction(context);
+    if (direction & GTK_TEXT_DIR_LTR)
+        lookupFlags |= GTK_ICON_LOOKUP_DIR_LTR;
+    else if (direction & GTK_TEXT_DIR_RTL)
+        lookupFlags |= GTK_ICON_LOOKUP_DIR_RTL;
+#endif
+    int width, height;
+    gtk_icon_size_lookup(iconSize, &width, &height);
+    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)));
+    if (!iconInfo)
         return nullptr;
 
-    return downcast<HTMLMediaElement>(mediaNode);
-}
-
-void RenderThemeGtk::initMediaColors()
-{
-    GdkRGBA color;
-    GtkStyleContext* containerContext = getStyleContext(GTK_TYPE_CONTAINER);
-
-    gtk_style_context_get_background_color(containerContext, GTK_STATE_FLAG_NORMAL, &color);
-    m_panelColor = color;
-    gtk_style_context_get_background_color(containerContext, GTK_STATE_FLAG_ACTIVE, &color);
-    m_sliderColor = color;
-    gtk_style_context_get_background_color(containerContext, GTK_STATE_FLAG_SELECTED, &color);
-    m_sliderThumbColor = color;
-}
-
-void RenderThemeGtk::initMediaButtons()
-{
-    static bool iconsInitialized = false;
-
-    if (iconsInitialized)
-        return;
-
-    GRefPtr<GtkIconFactory> iconFactory = adoptGRef(gtk_icon_factory_new());
-    GtkIconSource* iconSource = gtk_icon_source_new();
-    const char* icons[] = { "audio-volume-high", "audio-volume-muted" };
-
-    gtk_icon_factory_add_default(iconFactory.get());
-
-    for (size_t i = 0; i < G_N_ELEMENTS(icons); ++i) {
-        gtk_icon_source_set_icon_name(iconSource, icons[i]);
-        GtkIconSet* iconSet = gtk_icon_set_new();
-        gtk_icon_set_add_source(iconSet, iconSource);
-        gtk_icon_factory_add(iconFactory.get(), icons[i], iconSet);
-        gtk_icon_set_unref(iconSet);
-    }
-
-    gtk_icon_source_free(iconSource);
-
-    iconsInitialized = true;
+    return adoptGRef(gtk_icon_info_load_symbolic_for_context(iconInfo.get(), context, nullptr, nullptr));
 }
-#endif
+#endif // !GTK_CHECK_VERSION(3, 20, 0)
 
 static bool nodeHasPseudo(Node* node, const char* pseudo)
 {
@@ -327,19 +321,6 @@ static bool nodeHasClass(Node* node, const char* className)
     return element.classNames().contains(className);
 }
 
-RenderThemeGtk::RenderThemeGtk()
-    : m_panelColor(Color::white)
-    , m_sliderColor(Color::white)
-    , m_sliderThumbColor(Color::white)
-    , m_mediaIconSize(16)
-    , m_mediaSliderHeight(14)
-{
-#if ENABLE(VIDEO)
-    initMediaColors();
-    initMediaButtons();
-#endif
-}
-
 RenderThemeGtk::~RenderThemeGtk()
 {
 }
@@ -373,20 +354,79 @@ bool RenderThemeGtk::controlSupportsTints(const RenderObject& o) const
     return isEnabled(o);
 }
 
-int RenderThemeGtk::baselinePosition(const RenderObject& renderer) const
+int RenderThemeGtk::baselinePosition(const RenderBox& box) const
 {
-    if (!is<RenderBox>(renderer))
-        return 0;
-
     // FIXME: This strategy is possibly incorrect for the GTK+ port.
-    if (renderer.style().appearance() == CheckboxPart || renderer.style().appearance() == RadioPart) {
-        const auto& box = downcast<RenderBox>(renderer);
+    if (box.style().appearance() == CheckboxPart || box.style().appearance() == RadioPart)
         return box.marginTop() + box.height() - 2;
-    }
+    return RenderTheme::baselinePosition(box);
+}
 
-    return RenderTheme::baselinePosition(renderer);
+#if GTK_CHECK_VERSION(3, 20, 0)
+void RenderThemeGtk::adjustRepaintRect(const RenderObject&, FloatRect&)
+{
 }
+static GtkStateFlags themePartStateFlags(const RenderThemeGtk& theme, RenderThemePart themePart, const RenderObject& renderObject)
+{
+    unsigned stateFlags = 0;
+    switch (renderObject.style().direction()) {
+    case RTL:
+        stateFlags |= GTK_STATE_FLAG_DIR_RTL;
+        break;
+    case LTR:
+        stateFlags |= GTK_STATE_FLAG_DIR_LTR;
+        break;
+    }
 
+    if (!theme.isEnabled(renderObject) || (themePart == Entry && theme.isReadOnlyControl(renderObject)))
+        stateFlags |= GTK_STATE_FLAG_INSENSITIVE;
+    else {
+        if (theme.isHovered(renderObject))
+            stateFlags |= GTK_STATE_FLAG_PRELIGHT;
+        if (theme.isFocused(renderObject))
+            stateFlags |= GTK_STATE_FLAG_FOCUSED;
+    }
+
+    switch (themePart) {
+    case CheckButton:
+    case RadioButton:
+        if (theme.isChecked(renderObject))
+            stateFlags |= GTK_STATE_FLAG_CHECKED;
+        if (theme.isIndeterminate(renderObject))
+            stateFlags |= GTK_STATE_FLAG_INCONSISTENT;
+        if (theme.isPressed(renderObject))
+            stateFlags |= GTK_STATE_FLAG_SELECTED;
+        break;
+    case Button:
+    case ComboBoxButton:
+    case ScaleSlider:
+    case EntryIconLeft:
+    case EntryIconRight:
+#if ENABLE(VIDEO)
+    case MediaButton:
+#endif
+        if (theme.isPressed(renderObject))
+            stateFlags |= GTK_STATE_FLAG_ACTIVE;
+        break;
+    case SpinButtonUpButton:
+        if (theme.isPressed(renderObject) && theme.isSpinUpButtonPartPressed(renderObject))
+            stateFlags |= GTK_STATE_FLAG_ACTIVE;
+        if (theme.isHovered(renderObject) && !theme.isSpinUpButtonPartHovered(renderObject))
+            stateFlags &= ~GTK_STATE_FLAG_PRELIGHT;
+        break;
+    case SpinButtonDownButton:
+        if (theme.isPressed(renderObject) && !theme.isSpinUpButtonPartPressed(renderObject))
+            stateFlags |= GTK_STATE_FLAG_ACTIVE;
+        if (theme.isHovered(renderObject) && theme.isSpinUpButtonPartHovered(renderObject))
+            stateFlags &= ~GTK_STATE_FLAG_PRELIGHT;
+        break;
+    default:
+        break;
+    }
+
+    return static_cast<GtkStateFlags>(stateFlags);
+}
+#else
 static GtkTextDirection gtkTextDirection(TextDirection direction)
 {
     switch (direction) {
@@ -399,16 +439,16 @@ static GtkTextDirection gtkTextDirection(TextDirection direction)
     }
 }
 
-static GtkStateType gtkIconState(RenderTheme* theme, const RenderObject& renderObject)
+static GtkStateFlags gtkIconStateFlags(RenderTheme* theme, const RenderObject& renderObject)
 {
     if (!theme->isEnabled(renderObject))
-        return GTK_STATE_INSENSITIVE;
+        return GTK_STATE_FLAG_INSENSITIVE;
     if (theme->isPressed(renderObject))
-        return GTK_STATE_ACTIVE;
+        return GTK_STATE_FLAG_ACTIVE;
     if (theme->isHovered(renderObject))
-        return GTK_STATE_PRELIGHT;
+        return GTK_STATE_FLAG_PRELIGHT;
 
-    return GTK_STATE_NORMAL;
+    return GTK_STATE_FLAG_NORMAL;
 }
 
 static void adjustRectForFocus(GtkStyleContext* context, FloatRect& rect)
@@ -420,32 +460,32 @@ static void adjustRectForFocus(GtkStyleContext* context, FloatRect& rect)
 
 void RenderThemeGtk::adjustRepaintRect(const RenderObject& renderObject, FloatRect& rect)
 {
-    GtkStyleContext* context = 0;
+    GRefPtr<GtkStyleContext> context;
     bool checkInteriorFocus = false;
     ControlPart part = renderObject.style().appearance();
     switch (part) {
     case CheckboxPart:
     case RadioPart:
-        context = getStyleContext(part == CheckboxPart ? GTK_TYPE_CHECK_BUTTON : GTK_TYPE_RADIO_BUTTON);
+        context = createStyleContext(part == CheckboxPart ? CheckButton : RadioButton);
 
         gint indicatorSpacing;
-        gtk_style_context_get_style(context, "indicator-spacing", &indicatorSpacing, nullptr);
+        gtk_style_context_get_style(context.get(), "indicator-spacing", &indicatorSpacing, nullptr);
         rect.inflate(indicatorSpacing);
 
         return;
     case SliderVerticalPart:
     case SliderHorizontalPart:
-        context = getStyleContext(GTK_TYPE_SCALE);
+        context = createStyleContext(ScaleSlider);
         break;
     case ButtonPart:
     case MenulistButtonPart:
     case MenulistPart:
-        context = getStyleContext(GTK_TYPE_BUTTON);
+        context = createStyleContext(Button);
         checkInteriorFocus = true;
         break;
     case TextFieldPart:
     case TextAreaPart:
-        context = getStyleContext(GTK_TYPE_ENTRY);
+        context = createStyleContext(Entry);
         checkInteriorFocus = true;
         break;
     default:
@@ -455,65 +495,129 @@ void RenderThemeGtk::adjustRepaintRect(const RenderObject& renderObject, FloatRe
     ASSERT(context);
     if (checkInteriorFocus) {
         gboolean interiorFocus;
-        gtk_style_context_get_style(context, "interior-focus", &interiorFocus, nullptr);
+        gtk_style_context_get_style(context.get(), "interior-focus", &interiorFocus, nullptr);
         if (interiorFocus)
             return;
     }
-    adjustRectForFocus(context, rect);
+    adjustRectForFocus(context.get(), rect);
 }
+#endif // GTK_CHECK_VERSION(3, 20, 0)
 
-void RenderThemeGtk::adjustButtonStyle(StyleResolver&, RenderStyle& style, Element*) const
+void RenderThemeGtk::adjustButtonStyle(StyleResolver&, RenderStyle& style, const Element*) const
 {
     // Some layout tests check explicitly that buttons ignore line-height.
     if (style.appearance() == PushButtonPart)
         style.setLineHeight(RenderStyle::initialLineHeight());
 }
 
-static void setToggleSize(GtkStyleContext* context, RenderStyle& style)
+static void shrinkToMinimumSizeAndCenterRectangle(FloatRect& rect, const IntSize& minSize)
+{
+    if (rect.width() > minSize.width()) {
+        rect.inflateX(-(rect.width() - minSize.width()) / 2);
+        rect.setWidth(minSize.width()); // In case rect.width() was equal to minSize.width() + 1.
+    }
+
+    if (rect.height() > minSize.height()) {
+        rect.inflateY(-(rect.height() - minSize.height()) / 2);
+        rect.setHeight(minSize.height()); // In case rect.height() was equal to minSize.height() + 1.
+    }
+}
+
+#if GTK_CHECK_VERSION(3, 20, 0)
+static void setToggleSize(RenderThemePart themePart, RenderStyle& style)
 {
+    ASSERT(themePart == CheckButton || themePart == RadioButton);
+
     // The width and height are both specified, so we shouldn't change them.
     if (!style.width().isIntrinsicOrAuto() && !style.height().isAuto())
         return;
 
-    // Other ports hard-code this to 13 which is also the default value defined by GTK+.
-    // GTK+ users tend to demand the native look.
-    // It could be made a configuration option values other than 13 actually break site compatibility.
-    gint indicatorSize;
-    gtk_style_context_get_style(context, "indicator-size", &indicatorSize, nullptr);
+    RenderThemeGadget::Info info = {
+        .type = RenderThemeGadget::Type::Generic,
+        .name = themePart == CheckButton ? "checkbutton" : "radiobutton",
+        .state = GTK_STATE_FLAG_NORMAL,
+        .classList = { }
+    };
+    auto parentGadget = RenderThemeGadget::create(info);
+    if (themePart == CheckButton) {
+        info.type = RenderThemeGadget::Type::Check;
+        info.name = "check";
+    } else {
+        info.type = RenderThemeGadget::Type::Radio;
+        info.name = "radio";
+    }
+    auto gadget = RenderThemeToggleGadget::create(info);
+    IntSize preferredSize = parentGadget->preferredSize();
+    preferredSize = preferredSize.expandedTo(gadget->preferredSize());
 
     if (style.width().isIntrinsicOrAuto())
-        style.setWidth(Length(indicatorSize, Fixed));
+        style.setWidth(Length(preferredSize.width(), Fixed));
 
     if (style.height().isAuto())
-        style.setHeight(Length(indicatorSize, Fixed));
+        style.setHeight(Length(preferredSize.height(), Fixed));
 }
 
-static void paintToggle(const RenderThemeGtk* theme, GType widgetType, const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& fullRect)
+static void paintToggle(const RenderThemeGtk* theme, RenderThemePart themePart, const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& fullRect)
 {
-    GtkStyleContext* context = getStyleContext(widgetType);
-    gtk_style_context_save(context);
+    ASSERT(themePart == CheckButton || themePart == RadioButton);
 
+    RenderThemeGadget::Info parentInfo = {
+        .type = RenderThemeGadget::Type::Generic,
+        .name = themePart == CheckButton ? "checkbutton" : "radiobutton",
+        .state = themePartStateFlags(*theme, themePart, renderObject),
+        .classList = { "text-button" }
+    };
+    auto parentGadget = RenderThemeGadget::create(parentInfo);
+    RenderThemeGadget::Info info;
+    info.state = parentInfo.state;
+    if (themePart == CheckButton) {
+        info.type = RenderThemeGadget::Type::Check;
+        info.name = "check";
+    } else {
+        info.type = RenderThemeGadget::Type::Radio;
+        info.name = "radio";
+    }
+    auto gadget = RenderThemeGadget::create(info, parentGadget.get());
+
+    FloatRect rect = fullRect;
     // Some themes do not render large toggle buttons properly, so we simply
     // shrink the rectangle back down to the default size and then center it
     // in the full toggle button region. The reason for not simply forcing toggle
     // buttons to be a smaller size is that we don't want to break site layouts.
+    IntSize preferredSize = parentGadget->preferredSize();
+    preferredSize = preferredSize.expandedTo(gadget->preferredSize());
+    shrinkToMinimumSizeAndCenterRectangle(rect, preferredSize);
+    parentGadget->render(paintInfo.context().platformContext()->cr(), rect);
+    gadget->render(paintInfo.context().platformContext()->cr(), rect);
+
+    if (theme->isFocused(renderObject))
+        parentGadget->renderFocus(paintInfo.context().platformContext()->cr(), rect);
+}
+#else
+static void setToggleSize(RenderThemePart themePart, RenderStyle& style)
+{
+    // The width and height are both specified, so we shouldn't change them.
+    if (!style.width().isIntrinsicOrAuto() && !style.height().isAuto())
+        return;
+
+    GRefPtr<GtkStyleContext> context = createStyleContext(themePart);
+    // Other ports hard-code this to 13. GTK+ users tend to demand the native look.
     gint indicatorSize;
-    gtk_style_context_get_style(context, "indicator-size", &indicatorSize, nullptr);
-    IntRect rect(fullRect);
-    if (rect.width() > indicatorSize) {
-        rect.inflateX(-(rect.width() - indicatorSize) / 2);
-        rect.setWidth(indicatorSize); // In case rect.width() was equal to indicatorSize + 1.
-    }
+    gtk_style_context_get_style(context.get(), "indicator-size", &indicatorSize, nullptr);
 
-    if (rect.height() > indicatorSize) {
-        rect.inflateY(-(rect.height() - indicatorSize) / 2);
-        rect.setHeight(indicatorSize); // In case rect.height() was equal to indicatorSize + 1.
-    }
+    if (style.width().isIntrinsicOrAuto())
+        style.setWidth(Length(indicatorSize, Fixed));
 
-    gtk_style_context_set_direction(context, static_cast<GtkTextDirection>(gtkTextDirection(renderObject.style().direction())));
-    gtk_style_context_add_class(context, widgetType == GTK_TYPE_CHECK_BUTTON ? GTK_STYLE_CLASS_CHECK : GTK_STYLE_CLASS_RADIO);
+    if (style.height().isAuto())
+        style.setHeight(Length(indicatorSize, Fixed));
+}
 
-    guint flags = 0;
+static void paintToggle(const RenderThemeGtk* theme, RenderThemePart themePart, const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& fullRect)
+{
+    GRefPtr<GtkStyleContext> context = createStyleContext(themePart);
+    gtk_style_context_set_direction(context.get(), static_cast<GtkTextDirection>(gtkTextDirection(renderObject.style().direction())));
+
+    unsigned flags = 0;
     if (!theme->isEnabled(renderObject))
         flags |= GTK_STATE_FLAG_INSENSITIVE;
     else if (theme->isHovered(renderObject))
@@ -528,47 +632,72 @@ static void paintToggle(const RenderThemeGtk* theme, GType widgetType, const Ren
 #endif
     if (theme->isPressed(renderObject))
         flags |= GTK_STATE_FLAG_SELECTED;
-    gtk_style_context_set_state(context, static_cast<GtkStateFlags>(flags));
+    gtk_style_context_set_state(context.get(), static_cast<GtkStateFlags>(flags));
+
+    // Some themes do not render large toggle buttons properly, so we simply
+    // shrink the rectangle back down to the default size and then center it
+    // in the full toggle button region. The reason for not simply forcing toggle
+    // buttons to be a smaller size is that we don't want to break site layouts.
+    FloatRect rect(fullRect);
+    gint indicatorSize;
+    gtk_style_context_get_style(context.get(), "indicator-size", &indicatorSize, nullptr);
+    IntSize minSize(indicatorSize, indicatorSize);
+    shrinkToMinimumSizeAndCenterRectangle(rect, minSize);
 
-    if (widgetType == GTK_TYPE_CHECK_BUTTON)
-        gtk_render_check(context, paintInfo.context->platformContext()->cr(), rect.x(), rect.y(), rect.width(), rect.height());
+    gtk_render_background(context.get(), paintInfo.context().platformContext()->cr(), rect.x(), rect.y(), rect.width(), rect.height());
+    gtk_render_frame(context.get(), paintInfo.context().platformContext()->cr(), rect.x(), rect.y(), rect.width(), rect.height());
+
+    if (themePart == CheckButton)
+        gtk_render_check(context.get(), paintInfo.context().platformContext()->cr(), rect.x(), rect.y(), rect.width(), rect.height());
     else
-        gtk_render_option(context, paintInfo.context->platformContext()->cr(), rect.x(), rect.y(), rect.width(), rect.height());
+        gtk_render_option(context.get(), paintInfo.context().platformContext()->cr(), rect.x(), rect.y(), rect.width(), rect.height());
 
     if (theme->isFocused(renderObject)) {
         IntRect indicatorRect(rect);
         gint indicatorSpacing;
-        gtk_style_context_get_style(context, "indicator-spacing", &indicatorSpacing, nullptr);
+        gtk_style_context_get_style(context.get(), "indicator-spacing", &indicatorSpacing, nullptr);
         indicatorRect.inflate(indicatorSpacing);
-        gtk_render_focus(context, paintInfo.context->platformContext()->cr(), indicatorRect.x(), indicatorRect.y(),
+        gtk_render_focus(context.get(), paintInfo.context().platformContext()->cr(), indicatorRect.x(), indicatorRect.y(),
             indicatorRect.width(), indicatorRect.height());
     }
-
-    gtk_style_context_restore(context);
 }
+#endif // GTK_CHECK_VERSION(3, 20, 0)
 
 void RenderThemeGtk::setCheckboxSize(RenderStyle& style) const
 {
-    setToggleSize(getStyleContext(GTK_TYPE_CHECK_BUTTON), style);
+    setToggleSize(CheckButton, style);
 }
 
 bool RenderThemeGtk::paintCheckbox(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
 {
-    paintToggle(this, GTK_TYPE_CHECK_BUTTON, renderObject, paintInfo, rect);
+    paintToggle(this, CheckButton, renderObject, paintInfo, rect);
     return false;
 }
 
 void RenderThemeGtk::setRadioSize(RenderStyle& style) const
 {
-    setToggleSize(getStyleContext(GTK_TYPE_RADIO_BUTTON), style);
+    setToggleSize(RadioButton, style);
 }
 
 bool RenderThemeGtk::paintRadio(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
 {
-    paintToggle(this, GTK_TYPE_RADIO_BUTTON, renderObject, paintInfo, rect);
+    paintToggle(this, RadioButton, renderObject, paintInfo, rect);
     return false;
 }
 
+#if GTK_CHECK_VERSION(3, 20, 0)
+bool RenderThemeGtk::paintButton(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
+{
+    RenderThemeGadget::Info info = { RenderThemeGadget::Type::Generic, "button", themePartStateFlags(*this, Button, renderObject), { "text-button" } };
+    if (isDefault(renderObject))
+        info.classList.append("default");
+    auto gadget = RenderThemeGadget::create(info);
+    gadget->render(paintInfo.context().platformContext()->cr(), rect);
+    if (isFocused(renderObject))
+        gadget->renderFocus(paintInfo.context().platformContext()->cr(), rect);
+    return false;
+}
+#else
 static void renderButton(RenderTheme* theme, GtkStyleContext* context, const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
 {
     IntRect buttonRect(rect);
@@ -599,8 +728,8 @@ static void renderButton(RenderTheme* theme, GtkStyleContext* context, const Ren
         gtk_style_context_add_class(context, GTK_STYLE_CLASS_DEFAULT);
     }
 
-    gtk_render_background(context, paintInfo.context->platformContext()->cr(), buttonRect.x(), buttonRect.y(), buttonRect.width(), buttonRect.height());
-    gtk_render_frame(context, paintInfo.context->platformContext()->cr(), buttonRect.x(), buttonRect.y(), buttonRect.width(), buttonRect.height());
+    gtk_render_background(context, paintInfo.context().platformContext()->cr(), buttonRect.x(), buttonRect.y(), buttonRect.width(), buttonRect.height());
+    gtk_render_frame(context, paintInfo.context().platformContext()->cr(), buttonRect.x(), buttonRect.y(), buttonRect.width(), buttonRect.height());
 
     if (theme->isFocused(renderObject)) {
         gint focusWidth, focusPad;
@@ -615,7 +744,7 @@ static void renderButton(RenderTheme* theme, GtkStyleContext* context, const Ren
 
         if (interiorFocus) {
             GtkBorder borderWidth;
-            gtk_style_context_get_border(context, static_cast<GtkStateFlags>(flags), &borderWidth);
+            gtk_style_context_get_border(context, gtk_style_context_get_state(context), &borderWidth);
 
             buttonRect = IntRect(
                 buttonRect.x() + borderWidth.left + focusPad,
@@ -632,116 +761,152 @@ static void renderButton(RenderTheme* theme, GtkStyleContext* context, const Ren
             buttonRect.move(childDisplacementX, childDisplacementY);
         }
 
-        gtk_render_focus(context, paintInfo.context->platformContext()->cr(), buttonRect.x(), buttonRect.y(), buttonRect.width(), buttonRect.height());
+        gtk_render_focus(context, paintInfo.context().platformContext()->cr(), buttonRect.x(), buttonRect.y(), buttonRect.width(), buttonRect.height());
     }
 }
 bool RenderThemeGtk::paintButton(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
 {
-    GtkStyleContext* context = getStyleContext(GTK_TYPE_BUTTON);
-    gtk_style_context_save(context);
-
-    gtk_style_context_set_direction(context, static_cast<GtkTextDirection>(gtkTextDirection(renderObject.style().direction())));
-    gtk_style_context_add_class(context, GTK_STYLE_CLASS_BUTTON);
-
-    renderButton(this, context, renderObject, paintInfo, rect);
+    GRefPtr<GtkStyleContext> context = createStyleContext(Button);
+    gtk_style_context_set_direction(context.get(), static_cast<GtkTextDirection>(gtkTextDirection(renderObject.style().direction())));
+    renderButton(this, context.get(), renderObject, paintInfo, rect);
+    return false;
+}
+#endif // GTK_CHECK_VERSION(3, 20, 0)
 
-    gtk_style_context_restore(context);
+static Color menuListColor(const Element* element)
+{
+#if GTK_CHECK_VERSION(3, 20, 0)
+    RenderThemeGadget::Info info { RenderThemeGadget::Type::Generic, "combobox", element->isDisabledFormControl() ? GTK_STATE_FLAG_INSENSITIVE : GTK_STATE_FLAG_NORMAL, { } };
+    auto comboGadget = RenderThemeGadget::create(info);
+    Vector<RenderThemeGadget::Info> children {
+        { RenderThemeGadget::Type::Generic, "button", info.state, { "combo" } }
+    };
+    info.name = "box";
+    info.classList = { "horizontal", "linked" };
+    return RenderThemeBoxGadget(info, children, comboGadget.get()).child(0)->color();
+#else
+    GRefPtr<GtkStyleContext> parentStyleContext = createStyleContext(ComboBox);
+    GRefPtr<GtkStyleContext> buttonStyleContext = createStyleContext(ComboBoxButton, parentStyleContext.get());
+    gtk_style_context_set_state(buttonStyleContext.get(), element->isDisabledFormControl() ? GTK_STATE_FLAG_INSENSITIVE : GTK_STATE_FLAG_NORMAL);
 
-    return false;
+    GdkRGBA gdkRGBAColor;
+    gtk_style_context_get_color(buttonStyleContext.get(), gtk_style_context_get_state(buttonStyleContext.get()), &gdkRGBAColor);
+    return gdkRGBAColor;
+#endif // GTK_CHECK_VERSION(3, 20, 0)
 }
 
-void RenderThemeGtk::adjustMenuListStyle(StyleResolver&, RenderStyle& style, Element*) const
+void RenderThemeGtk::adjustMenuListStyle(StyleResolver&, RenderStyle& style, const Element* element) const
 {
     // The tests check explicitly that select menu buttons ignore line height.
     style.setLineHeight(RenderStyle::initialLineHeight());
 
     // We cannot give a proper rendering when border radius is active, unfortunately.
     style.resetBorderRadius();
+
+    style.setColor(menuListColor(element));
 }
 
-void RenderThemeGtk::adjustMenuListButtonStyle(StyleResolver& styleResolver, RenderStyle& style, Element* e) const
+void RenderThemeGtk::adjustMenuListButtonStyle(StyleResolver& styleResolver, RenderStyle& style, const Element* e) const
 {
     adjustMenuListStyle(styleResolver, style, e);
 }
 
-static void getComboBoxMetrics(RenderStyle& style, GtkBorder& border, int& focus, int& separator)
+#if GTK_CHECK_VERSION(3, 20, 0)
+/*
+ * GtkComboBox gadgets tree
+ *
+ * combobox
+ * ├── box.linked
+ * │   ╰── button.combo
+ * │       ╰── box
+ * │           ├── cellview
+ * │           ╰── arrow
+ * ╰── window.popup
+ */
+LengthBox RenderThemeGtk::popupInternalPaddingBox(const RenderStyle& style) const
 {
-    // If this menu list button isn't drawn using the native theme, we
-    // don't add any extra padding beyond what WebCore already uses.
     if (style.appearance() == NoControlPart)
-        return;
-
-    GtkStyleContext* context = getStyleContext(GTK_TYPE_COMBO_BOX);
-    gtk_style_context_save(context);
-
-    gtk_style_context_add_class(context, GTK_STYLE_CLASS_BUTTON);
-    gtk_style_context_set_direction(context, static_cast<GtkTextDirection>(gtkTextDirection(style.direction())));
-
-    gtk_style_context_get_border(context, static_cast<GtkStateFlags>(0), &border);
-
-    gboolean interiorFocus;
-    gint focusWidth, focusPad;
-    gtk_style_context_get_style(context, "interior-focus", &interiorFocus, "focus-line-width", &focusWidth, "focus-padding", &focusPad, nullptr);
-    focus = interiorFocus ? focusWidth + focusPad : 0;
-
-    gtk_style_context_restore(context);
-
-    context = getStyleContext(GTK_TYPE_SEPARATOR);
-    gtk_style_context_save(context);
-
-    GtkTextDirection direction = static_cast<GtkTextDirection>(gtkTextDirection(style.direction()));
-    gtk_style_context_set_direction(context, direction);
-    gtk_style_context_add_class(context, "separator");
-
-    gboolean wideSeparators;
-    gint separatorWidth;
-    gtk_style_context_get_style(context, "wide-separators", &wideSeparators, "separator-width", &separatorWidth, nullptr);
-
-    // GTK+ always uses border.left, regardless of text direction. See gtkseperator.c.
-    if (!wideSeparators)
-        separatorWidth = border.left;
-
-    separator = separatorWidth;
+        return LengthBox(0);
+
+    RenderThemeGadget::Info info = { RenderThemeGadget::Type::Generic, "combobox", GTK_STATE_FLAG_NORMAL, { } };
+    auto comboGadget = RenderThemeGadget::create(info);
+    Vector<RenderThemeGadget::Info> children = {
+        { RenderThemeGadget::Type::Generic, "button", GTK_STATE_FLAG_NORMAL, { "combo" } }
+    };
+    info.name = "box";
+    info.classList = { "horizontal", "linked" };
+    auto boxGadget = std::make_unique<RenderThemeBoxGadget>(info, children, comboGadget.get());
+    RenderThemeGadget* buttonGadget = boxGadget->child(0);
+    info.classList.removeLast();
+    auto buttonBoxGadget = RenderThemeGadget::create(info, buttonGadget);
+    info.name = "arrow";
+    info.classList = { };
+    auto arrowGadget = RenderThemeGadget::create(info, buttonBoxGadget.get());
+    GtkBorder comboContentsBox = comboGadget->contentsBox();
+    GtkBorder boxContentsBox = boxGadget->contentsBox();
+    GtkBorder buttonContentsBox = buttonGadget->contentsBox();
+    GtkBorder buttonBoxContentsBox = buttonBoxGadget->contentsBox();
+    GtkBorder padding;
+    padding.left = comboContentsBox.left + boxContentsBox.left + buttonContentsBox.left + buttonBoxContentsBox.left;
+    padding.right = comboContentsBox.right + boxContentsBox.right + buttonContentsBox.right + buttonBoxContentsBox.right;
+    padding.top = comboContentsBox.top + boxContentsBox.top + buttonContentsBox.top + buttonBoxContentsBox.top;
+    padding.bottom = comboContentsBox.bottom + boxContentsBox.bottom + buttonContentsBox.bottom + buttonBoxContentsBox.bottom;
+
+    auto arrowSize = arrowGadget->preferredSize();
+    return LengthBox(padding.top, padding.right + (style.direction() == LTR ? arrowSize.width() : 0),
+        padding.bottom, padding.left + (style.direction() == RTL ? arrowSize.width() : 0));
+}
+
+bool RenderThemeGtk::paintMenuList(const RenderObject& renderObject, const PaintInfo& paintInfo, const FloatRect& rect)
+{
+    RenderThemeGadget::Info info = { RenderThemeGadget::Type::Generic, "combobox", themePartStateFlags(*this, ComboBoxButton, renderObject), { } };
+    auto comboGadget = RenderThemeGadget::create(info);
+    Vector<RenderThemeGadget::Info> children = {
+        { RenderThemeGadget::Type::Generic, "button", info.state, { "combo" } }
+    };
+    info.name = "box";
+    info.classList = { "horizontal", "linked" };
+    auto boxGadget = std::make_unique<RenderThemeBoxGadget>(info, children, comboGadget.get());
+    RenderThemeGadget* buttonGadget = boxGadget->child(0);
+    info.classList.removeLast();
+    auto buttonBoxGadget = RenderThemeGadget::create(info, buttonGadget);
+    info.type = RenderThemeGadget::Type::Arrow;
+    info.name = "arrow";
+    info.classList = { };
+    auto arrowGadget = RenderThemeGadget::create(info, buttonBoxGadget.get());
+
+    cairo_t* cr = paintInfo.context().platformContext()->cr();
+    comboGadget->render(cr, rect);
+    boxGadget->render(cr, rect);
+    FloatRect contentsRect;
+    buttonGadget->render(cr, rect, &contentsRect);
+    buttonBoxGadget->render(cr, contentsRect);
+    arrowGadget->render(cr, contentsRect);
+    if (isFocused(renderObject))
+        buttonGadget->renderFocus(cr, rect);
 
-    gtk_style_context_restore(context);
+    return false;
 }
-
-int RenderThemeGtk::popupInternalPaddingLeft(RenderStyle& style) const
+#else
+LengthBox RenderThemeGtk::popupInternalPaddingBox(const RenderStyle& style) const
 {
-    GtkBorder borderWidth = { 0, 0, 0, 0 };
-    int focusWidth = 0, separatorWidth = 0;
-    getComboBoxMetrics(style, borderWidth, focusWidth, separatorWidth);
-    int left = borderWidth.left + focusWidth;
-    if (style.direction() == RTL)
-        left += separatorWidth + minArrowSize;
-    return left;
-}
+    if (style.appearance() == NoControlPart)
+        return { 0, 0, 0, 0 };
 
-int RenderThemeGtk::popupInternalPaddingRight(RenderStyle& style) const
-{
+    GRefPtr<GtkStyleContext> parentContext = createStyleContext(ComboBox);
+    GRefPtr<GtkStyleContext> context = createStyleContext(ComboBoxButton, parentContext.get());
+    gtk_style_context_set_direction(context.get(), static_cast<GtkTextDirection>(gtkTextDirection(style.direction())));
+    gtk_style_context_set_state(context.get(), static_cast<GtkStateFlags>(0));
     GtkBorder borderWidth = { 0, 0, 0, 0 };
-    int focusWidth = 0, separatorWidth = 0;
-    getComboBoxMetrics(style, borderWidth, focusWidth, separatorWidth);
-    int right = borderWidth.right + focusWidth;
-    if (style.direction() == LTR)
-        right += separatorWidth + minArrowSize;
-    return right;
-}
+    gtk_style_context_get_border(context.get(), gtk_style_context_get_state(context.get()), &borderWidth);
 
-int RenderThemeGtk::popupInternalPaddingTop(RenderStyle& style) const
-{
-    GtkBorder borderWidth = { 0, 0, 0, 0 };
-    int focusWidth = 0, separatorWidth = 0;
-    getComboBoxMetrics(style, borderWidth, focusWidth, separatorWidth);
-    return borderWidth.top + focusWidth;
-}
+    gboolean interiorFocus;
+    gint focusWidth, focusPad;
+    gtk_style_context_get_style(context.get(), "interior-focus", &interiorFocus, "focus-line-width", &focusWidth, "focus-padding", &focusPad, nullptr);
+    focusWidth = interiorFocus ? focusWidth + focusPad : 0;
 
-int RenderThemeGtk::popupInternalPaddingBottom(RenderStyle& style) const
-{
-    GtkBorder borderWidth = { 0, 0, 0, 0 };
-    int focusWidth = 0, separatorWidth = 0;
-    getComboBoxMetrics(style, borderWidth, focusWidth, separatorWidth);
-    return borderWidth.bottom + focusWidth;
+    return { borderWidth.top + focusWidth, borderWidth.right + focusWidth + (style.direction() == LTR ? minArrowSize : 0),
+        borderWidth.bottom + focusWidth, borderWidth.left + focusWidth + (style.direction() == RTL ? minArrowSize : 0) };
 }
 
 bool RenderThemeGtk::paintMenuList(const RenderObject& renderObject, const PaintInfo& paintInfo, const FloatRect& r)
@@ -749,29 +914,29 @@ bool RenderThemeGtk::paintMenuList(const RenderObject& renderObject, const Paint
     // FIXME: adopt subpixel themes.
     IntRect rect = IntRect(r);
 
-    cairo_t* cairoContext = paintInfo.context->platformContext()->cr();
+    cairo_t* cairoContext = paintInfo.context().platformContext()->cr();
     GtkTextDirection direction = static_cast<GtkTextDirection>(gtkTextDirection(renderObject.style().direction()));
 
+    GRefPtr<GtkStyleContext> parentStyleContext = createStyleContext(ComboBox);
+
     // Paint the button.
-    GtkStyleContext* buttonStyleContext = getStyleContext(GTK_TYPE_BUTTON);
-    gtk_style_context_save(buttonStyleContext);
-    gtk_style_context_set_direction(buttonStyleContext, direction);
-    gtk_style_context_add_class(buttonStyleContext, GTK_STYLE_CLASS_BUTTON);
-    renderButton(this, buttonStyleContext, renderObject, paintInfo, rect);
+    GRefPtr<GtkStyleContext> buttonStyleContext = createStyleContext(ComboBoxButton, parentStyleContext.get());
+    gtk_style_context_set_direction(buttonStyleContext.get(), direction);
+    renderButton(this, buttonStyleContext.get(), renderObject, paintInfo, rect);
 
     // Get the inner rectangle.
     gint focusWidth, focusPad;
     GtkBorder* innerBorderPtr = 0;
     GtkBorder innerBorder = { 1, 1, 1, 1 };
-    gtk_style_context_get_style(buttonStyleContext, "inner-border", &innerBorderPtr, "focus-line-width", &focusWidth, "focus-padding", &focusPad, nullptr);
+    gtk_style_context_get_style(buttonStyleContext.get(), "inner-border", &innerBorderPtr, "focus-line-width", &focusWidth, "focus-padding", &focusPad, nullptr);
     if (innerBorderPtr) {
         innerBorder = *innerBorderPtr;
         gtk_border_free(innerBorderPtr);
     }
 
     GtkBorder borderWidth;
-    GtkStateFlags state = gtk_style_context_get_state(buttonStyleContext);
-    gtk_style_context_get_border(buttonStyleContext, state, &borderWidth);
+    GtkStateFlags state = gtk_style_context_get_state(buttonStyleContext.get());
+    gtk_style_context_get_border(buttonStyleContext.get(), state, &borderWidth);
 
     focusWidth += focusPad;
     IntRect innerRect(
@@ -783,24 +948,18 @@ bool RenderThemeGtk::paintMenuList(const RenderObject& renderObject, const Paint
     if (isPressed(renderObject)) {
         gint childDisplacementX;
         gint childDisplacementY;
-        gtk_style_context_get_style(buttonStyleContext, "child-displacement-x", &childDisplacementX, "child-displacement-y", &childDisplacementY, nullptr);
+        gtk_style_context_get_style(buttonStyleContext.get(), "child-displacement-x", &childDisplacementX, "child-displacement-y", &childDisplacementY, nullptr);
         innerRect.move(childDisplacementX, childDisplacementY);
     }
     innerRect.setWidth(std::max(1, innerRect.width()));
     innerRect.setHeight(std::max(1, innerRect.height()));
 
-    gtk_style_context_restore(buttonStyleContext);
-
     // Paint the arrow.
-    GtkStyleContext* arrowStyleContext = getStyleContext(GTK_TYPE_ARROW);
-    gtk_style_context_save(arrowStyleContext);
-
-    gtk_style_context_set_direction(arrowStyleContext, direction);
-    gtk_style_context_add_class(arrowStyleContext, "arrow");
-    gtk_style_context_add_class(arrowStyleContext, GTK_STYLE_CLASS_BUTTON);
+    GRefPtr<GtkStyleContext> arrowStyleContext = createStyleContext(ComboBoxArrow, buttonStyleContext.get());
+    gtk_style_context_set_direction(arrowStyleContext.get(), direction);
 
     gfloat arrowScaling;
-    gtk_style_context_get_style(arrowStyleContext, "arrow-scaling", &arrowScaling, nullptr);
+    gtk_style_context_get_style(parentStyleContext.get(), "arrow-scaling", &arrowScaling, nullptr);
 
     IntSize arrowSize(minArrowSize, innerRect.height());
     FloatPoint arrowPosition(innerRect.location());
@@ -812,120 +971,126 @@ bool RenderThemeGtk::paintMenuList(const RenderObject& renderObject, const Paint
     gint extent = std::min(arrowSize.width(), arrowSize.height()) * arrowScaling;
     arrowPosition.move((arrowSize.width() - extent) / 2, (arrowSize.height() - extent) / 2);
 
-    gtk_style_context_set_state(arrowStyleContext, state);
-    gtk_render_arrow(arrowStyleContext, cairoContext, G_PI, arrowPosition.x(), arrowPosition.y(), extent);
-
-    gtk_style_context_restore(arrowStyleContext);
+    gtk_style_context_set_state(arrowStyleContext.get(), state);
+    gtk_render_arrow(arrowStyleContext.get(), cairoContext, G_PI, arrowPosition.x(), arrowPosition.y(), extent);
 
-    // Paint the separator if needed.
-    GtkStyleContext* separatorStyleContext = getStyleContext(GTK_TYPE_COMBO_BOX);
-    gtk_style_context_save(separatorStyleContext);
+    return false;
+}
+#endif // GTK_CHECK_VERSION(3, 20, 0)
 
-    gtk_style_context_set_direction(separatorStyleContext, direction);
-    gtk_style_context_add_class(separatorStyleContext, "separator");
+bool RenderThemeGtk::paintMenuListButtonDecorations(const RenderBox& object, const PaintInfo& info, const FloatRect& rect)
+{
+    return paintMenuList(object, info, rect);
+}
 
-    gboolean wideSeparators;
-    gint separatorWidth;
-    gtk_style_context_get_style(separatorStyleContext, "wide-separators", &wideSeparators, "separator-width", &separatorWidth, nullptr);
-    if (wideSeparators && !separatorWidth) {
-        gtk_style_context_restore(separatorStyleContext);
-        return false;
-    }
+#if GTK_CHECK_VERSION(3, 20, 0)
+void RenderThemeGtk::adjustTextFieldStyle(StyleResolver&, RenderStyle& style, const Element* element) const
+{
+    if (!is<HTMLInputElement>(element) || !shouldHaveSpinButton(downcast<HTMLInputElement>(*element)))
+        return;
 
-    gtk_style_context_set_state(separatorStyleContext, state);
-    IntPoint separatorPosition(arrowPosition.x(), innerRect.y());
-    if (wideSeparators) {
-        if (direction == GTK_TEXT_DIR_LTR)
-            separatorPosition.move(-separatorWidth, 0);
-        else
-            separatorPosition.move(arrowSize.width(), 0);
+    // Spinbuttons need a minimum height to be rendered correctly.
+    RenderThemeGadget::Info info = { RenderThemeGadget::Type::Generic, "spinbutton", GTK_STATE_FLAG_NORMAL, { "horizontal" } };
+    auto spinbuttonGadget = RenderThemeGadget::create(info);
+    info.type = RenderThemeGadget::Type::TextField;
+    info.name = "entry";
+    info.classList.clear();
+    auto entryGadget = RenderThemeGadget::create(info, spinbuttonGadget.get());
+    info.type = RenderThemeGadget::Type::Icon;
+    info.name = "button";
+    info.classList.append("up");
+    auto buttonUpGadget = RenderThemeGadget::create(info, spinbuttonGadget.get());
+    static_cast<RenderThemeIconGadget*>(buttonUpGadget.get())->setIconSize(RenderThemeIconGadget::IconSizeGtk::Menu);
+    info.classList[0] = "down";
+    auto buttonDownGadget = RenderThemeGadget::create(info, spinbuttonGadget.get());
+    static_cast<RenderThemeIconGadget*>(buttonDownGadget.get())->setIconSize(RenderThemeIconGadget::IconSizeGtk::Menu);
+    IntSize preferredSize = spinbuttonGadget->preferredSize();
+    preferredSize = preferredSize.expandedTo(entryGadget->preferredSize());
+    IntSize upPreferredSize = preferredSize.expandedTo(buttonUpGadget->preferredSize());
+    IntSize downPreferredSize = preferredSize.expandedTo(buttonDownGadget->preferredSize());
+    int height = std::max(upPreferredSize.height(), downPreferredSize.height());
+    style.setMinHeight(Length(height, Fixed));
+}
 
-        gtk_render_frame(separatorStyleContext, cairoContext, separatorPosition.x(), separatorPosition.y(), separatorWidth, innerRect.height());
-    } else {
-        GtkBorder padding;
-        gtk_style_context_get_padding(separatorStyleContext, state, &padding);
-        GtkBorder border;
-        gtk_style_context_get_border(separatorStyleContext, state, &border);
-
-        if (direction == GTK_TEXT_DIR_LTR)
-            separatorPosition.move(-(padding.left + border.left), 0);
-        else
-            separatorPosition.move(arrowSize.width(), 0);
-
-        cairo_save(cairoContext);
-
-        // An extra clip prevents the separator bleeding outside of the specified rectangle because of subpixel positioning.
-        cairo_rectangle(cairoContext, separatorPosition.x(), separatorPosition.y(), border.left, innerRect.height());
-        cairo_clip(cairoContext);
-        gtk_render_line(separatorStyleContext, cairoContext, separatorPosition.x(), separatorPosition.y(), separatorPosition.x(), innerRect.maxY());
-        cairo_restore(cairoContext);
+bool RenderThemeGtk::paintTextField(const RenderObject& renderObject, const PaintInfo& paintInfo, const FloatRect& rect)
+{
+    RenderThemeGadget::Info info = { RenderThemeGadget::Type::TextField, "entry", themePartStateFlags(*this, Entry, renderObject), { } };
+    std::unique_ptr<RenderThemeGadget> parentGadget;
+    if (is<HTMLInputElement>(renderObject.node()) && shouldHaveSpinButton(downcast<HTMLInputElement>(*renderObject.node()))) {
+        info.name = "spinbutton";
+        info.classList.append("horizontal");
+        parentGadget = RenderThemeTextFieldGadget::create(info);
+        info.name = "entry";
+        info.classList.clear();
     }
 
-    gtk_style_context_restore(separatorStyleContext);
+    auto entryGadget = RenderThemeTextFieldGadget::create(info, parentGadget.get());
+    if (parentGadget)
+        parentGadget->render(paintInfo.context().platformContext()->cr(), rect);
+    entryGadget->render(paintInfo.context().platformContext()->cr(), rect);
     return false;
 }
-
-bool RenderThemeGtk::paintMenuListButtonDecorations(const RenderObject& object, const PaintInfo& info, const FloatRect& rect)
+#else
+void RenderThemeGtk::adjustTextFieldStyle(StyleResolver&, RenderStyle&, const Element*) const
 {
-    return paintMenuList(object, info, rect);
 }
 
 bool RenderThemeGtk::paintTextField(const RenderObject& renderObject, const PaintInfo& paintInfo, const FloatRect& rect)
 {
-    GtkStyleContext* context = getStyleContext(GTK_TYPE_ENTRY);
-    gtk_style_context_save(context);
-
-    gtk_style_context_set_direction(context, static_cast<GtkTextDirection>(gtkTextDirection(renderObject.style().direction())));
-    gtk_style_context_add_class(context, GTK_STYLE_CLASS_ENTRY);
+    GRefPtr<GtkStyleContext> context = createStyleContext(Entry);
+    gtk_style_context_set_direction(context.get(), static_cast<GtkTextDirection>(gtkTextDirection(renderObject.style().direction())));
 
     guint flags = 0;
     if (!isEnabled(renderObject) || isReadOnlyControl(renderObject))
         flags |= GTK_STATE_FLAG_INSENSITIVE;
     else if (isFocused(renderObject))
         flags |= GTK_STATE_FLAG_FOCUSED;
-    gtk_style_context_set_state(context, static_cast<GtkStateFlags>(flags));
+    gtk_style_context_set_state(context.get(), static_cast<GtkStateFlags>(flags));
 
-    gtk_render_background(context, paintInfo.context->platformContext()->cr(), rect.x(), rect.y(), rect.width(), rect.height());
-    gtk_render_frame(context, paintInfo.context->platformContext()->cr(), rect.x(), rect.y(), rect.width(), rect.height());
+    gtk_render_background(context.get(), paintInfo.context().platformContext()->cr(), rect.x(), rect.y(), rect.width(), rect.height());
+    gtk_render_frame(context.get(), paintInfo.context().platformContext()->cr(), rect.x(), rect.y(), rect.width(), rect.height());
 
     if (isFocused(renderObject) && isEnabled(renderObject)) {
         gboolean interiorFocus;
         gint focusWidth, focusPad;
-        gtk_style_context_get_style(context, "interior-focus", &interiorFocus, "focus-line-width", &focusWidth, "focus-padding", &focusPad, nullptr);
+        gtk_style_context_get_style(context.get(), "interior-focus", &interiorFocus, "focus-line-width", &focusWidth, "focus-padding", &focusPad, nullptr);
         if (!interiorFocus) {
             IntRect focusRect(rect);
             focusRect.inflate(focusWidth + focusPad);
-            gtk_render_focus(context, paintInfo.context->platformContext()->cr(), focusRect.x(), focusRect.y(), focusRect.width(), focusRect.height());
+            gtk_render_focus(context.get(), paintInfo.context().platformContext()->cr(), focusRect.x(), focusRect.y(), focusRect.width(), focusRect.height());
         }
     }
 
-    gtk_style_context_restore(context);
-
     return false;
 }
+#endif
 
-bool RenderThemeGtk::paintTextArea(const RenderObject& o, const PaintInfo& i, const FloatRect& r)
-{
-    return paintTextField(o, i, r);
-}
-
-static void paintGdkPixbuf(GraphicsContext* context, const GdkPixbuf* icon, const IntRect& iconRect)
+#if GTK_CHECK_VERSION(3, 20, 0)
+static void adjustSearchFieldIconStyle(RenderThemePart themePart, RenderStyle& style)
 {
-    IntSize iconSize(gdk_pixbuf_get_width(icon), gdk_pixbuf_get_height(icon));
-    GRefPtr<GdkPixbuf> scaledIcon;
-    if (iconRect.size() != iconSize) {
-        // We could use cairo_scale() here but cairo/pixman downscale quality is quite bad.
-        scaledIcon = adoptGRef(gdk_pixbuf_scale_simple(icon, iconRect.width(), iconRect.height(), GDK_INTERP_BILINEAR));
-        icon = scaledIcon.get();
-    }
+    ASSERT(themePart == EntryIconLeft || themePart == EntryIconRight);
+    RenderThemeGadget::Info info = { RenderThemeGadget::Type::TextField, "entry", GTK_STATE_FLAG_NORMAL, { } };
+    auto parentGadget = RenderThemeGadget::create(info);
+    info.type = RenderThemeGadget::Type::Icon;
+    info.name = "image";
+    if (themePart == EntryIconLeft)
+        info.classList.append("left");
+    else
+        info.classList.append("right");
+    auto gadget = RenderThemeIconGadget::create(info, parentGadget.get());
 
-    cairo_t* cr = context->platformContext()->cr();
-    cairo_save(cr);
-    gdk_cairo_set_source_pixbuf(cr, icon, iconRect.x(), iconRect.y());
-    cairo_paint(cr);
-    cairo_restore(cr);
+    // Get the icon size based on the font size.
+    static_cast<RenderThemeIconGadget*>(gadget.get())->setIconSize(style.fontSize());
+    IntSize preferredSize = gadget->preferredSize();
+    GtkBorder contentsBox = parentGadget->contentsBox();
+    if (themePart == EntryIconLeft)
+        preferredSize.expand(contentsBox.left, contentsBox.top + contentsBox.bottom);
+    else
+        preferredSize.expand(contentsBox.right, contentsBox.top + contentsBox.bottom);
+    style.setWidth(Length(preferredSize.width(), Fixed));
+    style.setHeight(Length(preferredSize.height(), Fixed));
 }
-
+#else
 // Defined in GTK+ (gtk/gtkiconfactory.c)
 static const gint gtkIconSizeMenu = 16;
 static const gint gtkIconSizeSmallToolbar = 18;
@@ -950,41 +1115,122 @@ static GtkIconSize getIconSizeForPixelSize(gint pixelSize)
     return GTK_ICON_SIZE_DIALOG;
 }
 
-void RenderThemeGtk::adjustSearchFieldResultsButtonStyle(StyleResolver& styleResolver, RenderStyle& style, Element* e) const
-{
-    adjustSearchFieldCancelButtonStyle(styleResolver, style, e);
-}
-
-bool RenderThemeGtk::paintSearchFieldResultsButton(const RenderObject& o, const PaintInfo& i, const IntRect& rect)
-{
-    return paintSearchFieldResultsDecorationPart(o, i, rect);
-}
-
-static void adjustSearchFieldIconStyle(RenderStyle& style)
+static void adjustSearchFieldIconStyle(RenderThemePart themePart, RenderStyle& style)
 {
     style.resetBorder();
     style.resetPadding();
 
+    GRefPtr<GtkStyleContext> parentContext = createStyleContext(Entry);
+    GRefPtr<GtkStyleContext> context = createStyleContext(themePart, parentContext.get());
+
+    GtkBorder padding;
+    gtk_style_context_get_padding(context.get(), gtk_style_context_get_state(context.get()), &padding);
+
     // Get the icon size based on the font size.
     int fontSize = style.fontSize();
     if (fontSize < gtkIconSizeMenu) {
-        style.setWidth(Length(fontSize, Fixed));
-        style.setHeight(Length(fontSize, Fixed));
+        style.setWidth(Length(fontSize + (padding.left + padding.right), Fixed));
+        style.setHeight(Length(fontSize + (padding.top + padding.bottom), Fixed));
         return;
     }
     gint width = 0, height = 0;
     gtk_icon_size_lookup(getIconSizeForPixelSize(fontSize), &width, &height);
-    style.setWidth(Length(width, Fixed));
-    style.setHeight(Length(height, Fixed));
+    style.setWidth(Length(width + (padding.left + padding.right), Fixed));
+    style.setHeight(Length(height + (padding.top + padding.bottom), Fixed));
+}
+#endif
+
+bool RenderThemeGtk::paintTextArea(const RenderObject& o, const PaintInfo& i, const FloatRect& r)
+{
+    return paintTextField(o, i, r);
+}
+
+void RenderThemeGtk::adjustSearchFieldResultsButtonStyle(StyleResolver& styleResolver, RenderStyle& style, const Element* e) const
+{
+    adjustSearchFieldCancelButtonStyle(styleResolver, style, e);
+}
+
+bool RenderThemeGtk::paintSearchFieldResultsButton(const RenderBox& o, const PaintInfo& i, const IntRect& rect)
+{
+    return paintSearchFieldResultsDecorationPart(o, i, rect);
+}
+
+void RenderThemeGtk::adjustSearchFieldResultsDecorationPartStyle(StyleResolver&, RenderStyle& style, const Element*) const
+{
+    adjustSearchFieldIconStyle(EntryIconLeft, style);
+}
+
+void RenderThemeGtk::adjustSearchFieldCancelButtonStyle(StyleResolver&, RenderStyle& style, const Element*) const
+{
+    adjustSearchFieldIconStyle(EntryIconRight, style);
+}
+
+#if GTK_CHECK_VERSION(3, 20, 0)
+static bool paintSearchFieldIcon(RenderThemeGtk* theme, RenderThemePart themePart, const RenderBox& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
+{
+    ASSERT(themePart == EntryIconLeft || themePart == EntryIconRight);
+    RenderThemeGadget::Info info = { RenderThemeGadget::Type::TextField, "entry", themePartStateFlags(*theme, Entry, renderObject), { } };
+    auto parentGadget = RenderThemeGadget::create(info);
+    info.type = RenderThemeGadget::Type::Icon;
+    info.state = themePartStateFlags(*theme, themePart, renderObject);
+    info.name = "image";
+    if (themePart == EntryIconLeft)
+        info.classList.append("left");
+    else
+        info.classList.append("right");
+    auto gadget = RenderThemeGadget::create(info, parentGadget.get());
+    auto* gadgetIcon = static_cast<RenderThemeIconGadget*>(gadget.get());
+    gadgetIcon->setIconSize(renderObject.style().fontSize());
+    if (themePart == EntryIconLeft)
+        gadgetIcon->setIconName("edit-find-symbolic");
+    else
+        gadgetIcon->setIconName("edit-clear-symbolic");
+    GtkBorder contentsBox = parentGadget->contentsBox();
+    IntRect iconRect = rect;
+    if (themePart == EntryIconLeft) {
+        iconRect.move(contentsBox.left, contentsBox.top);
+        iconRect.contract(contentsBox.left, contentsBox.top + contentsBox.bottom);
+    } else
+        iconRect.contract(contentsBox.right, contentsBox.top + contentsBox.bottom);
+    return !gadget->render(paintInfo.context().platformContext()->cr(), iconRect);
+}
+bool RenderThemeGtk::paintSearchFieldResultsDecorationPart(const RenderBox& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
+{
+    return paintSearchFieldIcon(this, EntryIconLeft, renderObject, paintInfo, rect);
+}
+
+bool RenderThemeGtk::paintSearchFieldCancelButton(const RenderBox& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
+{
+    return paintSearchFieldIcon(this, EntryIconRight, renderObject, paintInfo, rect);
+}
+#else
+static bool paintIcon(GtkStyleContext* context, GraphicsContext& graphicsContext, const IntRect& rect, const char* iconName)
+{
+    GRefPtr<GdkPixbuf> icon = loadThemedIcon(context, iconName, getIconSizeForPixelSize(rect.height()));
+    if (!icon)
+        return false;
+
+    if (gdk_pixbuf_get_width(icon.get()) > rect.width() || gdk_pixbuf_get_height(icon.get()) > rect.height())
+        icon = adoptGRef(gdk_pixbuf_scale_simple(icon.get(), rect.width(), rect.height(), GDK_INTERP_BILINEAR));
+
+    gtk_render_icon(context, graphicsContext.platformContext()->cr(), icon.get(), rect.x(), rect.y());
+    return true;
 }
 
-void RenderThemeGtk::adjustSearchFieldResultsDecorationPartStyle(StyleResolver&, RenderStyle& style, Element*) const
+static bool paintEntryIcon(RenderThemePart themePart, const char* iconName, GraphicsContext& graphicsContext, const IntRect& rect, GtkTextDirection direction, GtkStateFlags state)
 {
-    adjustSearchFieldIconStyle(style);
+    GRefPtr<GtkStyleContext> parentContext = createStyleContext(Entry);
+    GRefPtr<GtkStyleContext> context = createStyleContext(themePart, parentContext.get());
+    gtk_style_context_set_direction(context.get(), direction);
+    gtk_style_context_set_state(context.get(), state);
+    return paintIcon(context.get(), graphicsContext, rect, iconName);
 }
 
 static IntRect centerRectVerticallyInParentInputElement(const RenderObject& renderObject, const IntRect& rect)
 {
+    if (!renderObject.node())
+        return IntRect();
+
     // Get the renderer of <input> element.
     Node* input = renderObject.node()->shadowHost();
     if (!input)
@@ -1003,40 +1249,28 @@ static IntRect centerRectVerticallyInParentInputElement(const RenderObject& rend
     return scaledRect;
 }
 
-bool RenderThemeGtk::paintSearchFieldResultsDecorationPart(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
+bool RenderThemeGtk::paintSearchFieldResultsDecorationPart(const RenderBox& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
 {
     IntRect iconRect = centerRectVerticallyInParentInputElement(renderObject, rect);
     if (iconRect.isEmpty())
-        return false;
+        return true;
 
-    GRefPtr<GdkPixbuf> icon = getStockIconForWidgetType(GTK_TYPE_ENTRY, GTK_STOCK_FIND,
-        gtkTextDirection(renderObject.style().direction()),
-        gtkIconState(this, renderObject),
-        getIconSizeForPixelSize(rect.height()));
-    paintGdkPixbuf(paintInfo.context, icon.get(), iconRect);
-    return false;
+    return !paintEntryIcon(EntryIconLeft, "edit-find-symbolic", paintInfo.context(), iconRect, gtkTextDirection(renderObject.style().direction()),
+        gtkIconStateFlags(this, renderObject));
 }
 
-void RenderThemeGtk::adjustSearchFieldCancelButtonStyle(StyleResolver&, RenderStyle& style, Element*) const
-{
-    adjustSearchFieldIconStyle(style);
-}
-
-bool RenderThemeGtk::paintSearchFieldCancelButton(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
+bool RenderThemeGtk::paintSearchFieldCancelButton(const RenderBox& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
 {
     IntRect iconRect = centerRectVerticallyInParentInputElement(renderObject, rect);
     if (iconRect.isEmpty())
-        return false;
+        return true;
 
-    GRefPtr<GdkPixbuf> icon = getStockIconForWidgetType(GTK_TYPE_ENTRY, GTK_STOCK_CLEAR,
-        gtkTextDirection(renderObject.style().direction()),
-        gtkIconState(this, renderObject),
-        getIconSizeForPixelSize(rect.height()));
-    paintGdkPixbuf(paintInfo.context, icon.get(), iconRect);
-    return false;
+    return !paintEntryIcon(EntryIconRight, "edit-clear-symbolic", paintInfo.context(), iconRect, gtkTextDirection(renderObject.style().direction()),
+        gtkIconStateFlags(this, renderObject));
 }
+#endif // GTK_CHECK_VERSION(3, 20, 0)
 
-void RenderThemeGtk::adjustSearchFieldStyle(StyleResolver&, RenderStyle& style, Element*) const
+void RenderThemeGtk::adjustSearchFieldStyle(StyleResolver&, RenderStyle& style, const Element*) const
 {
     // We cannot give a proper rendering when border radius is active, unfortunately.
     style.resetBorderRadius();
@@ -1048,148 +1282,335 @@ bool RenderThemeGtk::paintSearchField(const RenderObject& o, const PaintInfo& i,
     return paintTextField(o, i, rect);
 }
 
-bool RenderThemeGtk::paintCapsLockIndicator(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
+bool RenderThemeGtk::shouldHaveCapsLockIndicator(const HTMLInputElement& element) const
 {
-    // The other paint methods don't need to check whether painting is disabled because RenderTheme already checks it
-    // before calling them, but paintCapsLockIndicator() is called by RenderTextControlSingleLine which doesn't check it.
-    if (paintInfo.context->paintingDisabled())
-        return true;
-
-    int iconSize = std::min(rect.width(), rect.height());
-    GRefPtr<GdkPixbuf> icon = getStockIconForWidgetType(GTK_TYPE_ENTRY, GTK_STOCK_CAPS_LOCK_WARNING, gtkTextDirection(renderObject.style().direction()), 0, getIconSizeForPixelSize(iconSize));
-
-    // Only re-scale the icon when it's smaller than the minimum icon size.
-    if (iconSize >= gtkIconSizeMenu)
-        iconSize = gdk_pixbuf_get_height(icon.get());
-
-    // GTK+ locates the icon right aligned in the entry. The given rectangle is already
-    // centered vertically by RenderTextControlSingleLine.
-    IntRect iconRect(
-        rect.x() + rect.width() - iconSize,
-        rect.y() + (rect.height() - iconSize) / 2,
-        iconSize, iconSize);
-    paintGdkPixbuf(paintInfo.context, icon.get(), iconRect);
-    return true;
+    return element.isPasswordField();
 }
 
-void RenderThemeGtk::adjustSliderTrackStyle(StyleResolver&, RenderStyle& style, Element*) const
+void RenderThemeGtk::adjustSliderTrackStyle(StyleResolver&, RenderStyle& style, const Element*) const
 {
     style.setBoxShadow(nullptr);
 }
 
-void RenderThemeGtk::adjustSliderThumbStyle(StyleResolver& styleResolver, RenderStyle& style, Element* element) const
+void RenderThemeGtk::adjustSliderThumbStyle(StyleResolver& styleResolver, RenderStyle& style, const Element* element) const
 {
     RenderTheme::adjustSliderThumbStyle(styleResolver, style, element);
     style.setBoxShadow(nullptr);
 }
 
-static void applySliderStyleContextClasses(GtkStyleContext* context, ControlPart part)
-{
-    gtk_style_context_add_class(context, GTK_STYLE_CLASS_SCALE);
-    if (part == SliderHorizontalPart || part == SliderThumbHorizontalPart)
-        gtk_style_context_add_class(context, GTK_STYLE_CLASS_HORIZONTAL);
-    else if (part == SliderVerticalPart || part == SliderThumbVerticalPart)
-        gtk_style_context_add_class(context, GTK_STYLE_CLASS_VERTICAL);
-}
-
+#if GTK_CHECK_VERSION(3, 20, 0)
+/*
+ * GtkScale
+ *
+ * scale
+ * ╰── contents
+ *     ╰── trough
+ *         ├── slider
+ *         ╰── [highlight]
+ */
 bool RenderThemeGtk::paintSliderTrack(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
 {
     ControlPart part = renderObject.style().appearance();
-    ASSERT_UNUSED(part, part == SliderHorizontalPart || part == SliderVerticalPart || part == MediaVolumeSliderPart);
+    ASSERT(part == SliderHorizontalPart || part == SliderVerticalPart);
 
-    GtkStyleContext* context = getStyleContext(GTK_TYPE_SCALE);
-    gtk_style_context_save(context);
+    RenderThemeGadget::Info info = { RenderThemeGadget::Type::Generic, "scale", themePartStateFlags(*this, Scale, renderObject), { } };
+    if (part == SliderHorizontalPart)
+        info.classList.append("horizontal");
+    else
+        info.classList.append("vertical");
+    auto scaleGadget = RenderThemeGadget::create(info);
+    info.name = "contents";
+    info.classList.clear();
+    auto contentsGadget = RenderThemeGadget::create(info, scaleGadget.get());
+    info.name = "trough";
+    auto troughGadget = RenderThemeGadget::create(info, contentsGadget.get());
+    info.name = "slider";
+    auto sliderGadget = RenderThemeGadget::create(info, troughGadget.get());
+    info.name = "highlight";
+    auto highlightGadget = RenderThemeGadget::create(info, troughGadget.get());
+
+    // The given rectangle is not calculated based on the scale size, but all the margins and paddings are based on it.
+    IntSize preferredSize = scaleGadget->preferredSize();
+    preferredSize = preferredSize.expandedTo(contentsGadget->preferredSize());
+    preferredSize = preferredSize.expandedTo(troughGadget->preferredSize());
+    FloatRect trackRect = rect;
+    if (part == SliderHorizontalPart) {
+        trackRect.move(0, rect.height() / 2 - (preferredSize.height() / 2));
+        trackRect.setHeight(preferredSize.height());
+    } else {
+        trackRect.move(rect.width() / 2 - (preferredSize.width() / 2), 0);
+        trackRect.setWidth(preferredSize.width());
+    }
 
-    gtk_style_context_set_direction(context, gtkTextDirection(renderObject.style().direction()));
-    applySliderStyleContextClasses(context, part);
-    gtk_style_context_add_class(context, GTK_STYLE_CLASS_TROUGH);
+    FloatRect contentsRect;
+    scaleGadget->render(paintInfo.context().platformContext()->cr(), trackRect, &contentsRect);
+    contentsGadget->render(paintInfo.context().platformContext()->cr(), contentsRect, &contentsRect);
+    // Scale trough defines its size querying slider and highlight.
+    if (part == SliderHorizontalPart)
+        contentsRect.setHeight(troughGadget->preferredSize().height() + std::max(sliderGadget->preferredSize().height(), highlightGadget->preferredSize().height()));
+    else
+        contentsRect.setWidth(troughGadget->preferredSize().width() + std::max(sliderGadget->preferredSize().width(), highlightGadget->preferredSize().width()));
+    FloatRect troughRect = contentsRect;
+    troughGadget->render(paintInfo.context().platformContext()->cr(), troughRect, &contentsRect);
+    if (isFocused(renderObject))
+        troughGadget->renderFocus(paintInfo.context().platformContext()->cr(), troughRect);
+
+    LayoutPoint thumbLocation;
+    if (is<HTMLInputElement>(renderObject.node())) {
+        auto& input = downcast<HTMLInputElement>(*renderObject.node());
+        if (auto* element = input.sliderThumbElement())
+            thumbLocation = element->renderBox()->location();
+    }
 
-    if (!isEnabled(renderObject))
-        gtk_style_context_set_state(context, GTK_STATE_FLAG_INSENSITIVE);
+    if (part == SliderHorizontalPart) {
+        if (renderObject.style().direction() == RTL) {
+            contentsRect.move(thumbLocation.x(), 0);
+            contentsRect.setWidth(contentsRect.width() - thumbLocation.x());
+        } else
+            contentsRect.setWidth(thumbLocation.x());
+    } else
+        contentsRect.setHeight(thumbLocation.y());
+    highlightGadget->render(paintInfo.context().platformContext()->cr(), contentsRect);
 
-    gtk_render_background(context, paintInfo.context->platformContext()->cr(), rect.x(), rect.y(), rect.width(), rect.height());
-    gtk_render_frame(context, paintInfo.context->platformContext()->cr(), rect.x(), rect.y(), rect.width(), rect.height());
+    return false;
+}
 
-    if (isFocused(renderObject)) {
-        gint focusWidth, focusPad;
-        gtk_style_context_get_style(context, "focus-line-width", &focusWidth, "focus-padding", &focusPad, nullptr);
-        IntRect focusRect(rect);
-        focusRect.inflate(focusWidth + focusPad);
-        gtk_render_focus(context, paintInfo.context->platformContext()->cr(), focusRect.x(), focusRect.y(), focusRect.width(), focusRect.height());
-    }
+void RenderThemeGtk::adjustSliderThumbSize(RenderStyle& style, const Element*) const
+{
+    ControlPart part = style.appearance();
+    if (part != SliderThumbHorizontalPart && part != SliderThumbVerticalPart)
+        return;
 
-    gtk_style_context_restore(context);
-    return false;
+    RenderThemeGadget::Info info = { RenderThemeGadget::Type::Generic, "scale", GTK_STATE_FLAG_NORMAL, { } };
+    if (part == SliderHorizontalPart)
+        info.classList.append("horizontal");
+    else
+        info.classList.append("vertical");
+    auto scaleGadget = RenderThemeGadget::create(info);
+    info.name = "contents";
+    info.classList.clear();
+    auto contentsGadget = RenderThemeGadget::create(info, scaleGadget.get());
+    info.name = "trough";
+    auto troughGadget = RenderThemeGadget::create(info, contentsGadget.get());
+    info.name = "slider";
+    auto sliderGadget = RenderThemeGadget::create(info, troughGadget.get());
+    info.name = "highlight";
+    auto highlightGadget = RenderThemeGadget::create(info, troughGadget.get());
+
+    IntSize preferredSize = scaleGadget->preferredSize();
+    preferredSize = preferredSize.expandedTo(contentsGadget->preferredSize());
+    preferredSize = preferredSize.expandedTo(troughGadget->preferredSize());
+    preferredSize = preferredSize.expandedTo(sliderGadget->preferredSize());
+    if (part == SliderThumbHorizontalPart) {
+        style.setWidth(Length(preferredSize.width(), Fixed));
+        style.setHeight(Length(preferredSize.height(), Fixed));
+        return;
+    }
+    ASSERT(part == SliderThumbVerticalPart);
+    style.setWidth(Length(preferredSize.height(), Fixed));
+    style.setHeight(Length(preferredSize.width(), Fixed));
 }
 
 bool RenderThemeGtk::paintSliderThumb(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
 {
     ControlPart part = renderObject.style().appearance();
-    ASSERT(part == SliderThumbHorizontalPart || part == SliderThumbVerticalPart || part == MediaVolumeSliderThumbPart);
+    ASSERT(part == SliderThumbHorizontalPart || part == SliderThumbVerticalPart);
 
-    GtkStyleContext* context = getStyleContext(GTK_TYPE_SCALE);
-    gtk_style_context_save(context);
+    RenderThemeGadget::Info info = { RenderThemeGadget::Type::Generic, "scale", themePartStateFlags(*this, Scale, renderObject), { } };
+    if (part == SliderHorizontalPart)
+        info.classList.append("horizontal");
+    else
+        info.classList.append("vertical");
+    auto scaleGadget = RenderThemeGadget::create(info);
+    info.name = "contents";
+    info.classList.clear();
+    auto contentsGadget = RenderThemeGadget::create(info, scaleGadget.get());
+    info.name = "trough";
+    auto troughGadget = RenderThemeGadget::create(info, contentsGadget.get());
+    info.name = "slider";
+    info.state = themePartStateFlags(*this, ScaleSlider, renderObject);
+    auto sliderGadget = RenderThemeGadget::create(info, troughGadget.get());
+    info.name = "highlight";
+    auto highlightGadget = RenderThemeGadget::create(info, troughGadget.get());
+
+    GtkBorder scaleContentsBox = scaleGadget->contentsBox();
+    GtkBorder contentsContentsBox = contentsGadget->contentsBox();
+    GtkBorder troughContentsBox = troughGadget->contentsBox();
+    GtkBorder padding;
+    padding.left = scaleContentsBox.left + contentsContentsBox.left + troughContentsBox.left;
+    padding.right = scaleContentsBox.right + contentsContentsBox.right + troughContentsBox.right;
+    padding.top = scaleContentsBox.top + contentsContentsBox.top + troughContentsBox.top;
+    padding.bottom = scaleContentsBox.bottom + contentsContentsBox.bottom + troughContentsBox.bottom;
+
+    // Scale trough defines its size querying slider and highlight.
+    int troughHeight = troughGadget->preferredSize().height() + std::max(sliderGadget->preferredSize().height(), highlightGadget->preferredSize().height());
+    IntRect sliderRect(rect.location(), IntSize(troughHeight, troughHeight));
+    sliderRect.move(padding.left, padding.top);
+    sliderRect.contract(padding.left + padding.right, padding.top + padding.bottom);
+    sliderGadget->render(paintInfo.context().platformContext()->cr(), sliderRect);
+    return false;
+}
+#else
+bool RenderThemeGtk::paintSliderTrack(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
+{
+    ControlPart part = renderObject.style().appearance();
+    ASSERT(part == SliderHorizontalPart || part == SliderVerticalPart);
 
-    gtk_style_context_set_direction(context, gtkTextDirection(renderObject.style().direction()));
-    applySliderStyleContextClasses(context, part);
-    gtk_style_context_add_class(context, GTK_STYLE_CLASS_SLIDER);
+    GRefPtr<GtkStyleContext> parentContext = createStyleContext(Scale);
+    gtk_style_context_add_class(parentContext.get(), part == SliderHorizontalPart ? GTK_STYLE_CLASS_HORIZONTAL : GTK_STYLE_CLASS_VERTICAL);
+    GRefPtr<GtkStyleContext> context = createStyleContext(ScaleTrough, parentContext.get());
+    gtk_style_context_set_direction(context.get(), gtkTextDirection(renderObject.style().direction()));
 
-    guint flags = 0;
     if (!isEnabled(renderObject))
-        flags |= GTK_STATE_FLAG_INSENSITIVE;
-    else if (isHovered(renderObject))
-        flags |= GTK_STATE_FLAG_PRELIGHT;
-    if (isPressed(renderObject))
-        flags |= GTK_STATE_FLAG_ACTIVE;
-    gtk_style_context_set_state(context, static_cast<GtkStateFlags>(flags));
+        gtk_style_context_set_state(context.get(), GTK_STATE_FLAG_INSENSITIVE);
+
+    IntRect sliderRect = rect;
+    // GTK+ uses the slider thumb size and margins to calculate the trough size, but in WebKit we render the thumb and
+    // the slider track separately and the track rectangle we receive here can't be used to apply the GTK+ CSS sizes
+    // and margins. So we use a maximum fixed size for the trough to match at least Adwaita, but that should look
+    // good in other themes as well.
+    static const int sliderSize = 4;
+
+    if (part == SliderHorizontalPart) {
+        sliderRect.setHeight(std::min(rect.height(), sliderSize));
+        sliderRect.move(0, (rect.height() - sliderRect.height()) / 2);
+    } else {
+        sliderRect.setWidth(std::min(rect.width(), sliderSize));
+        sliderRect.move((rect.width() - sliderRect.width()) / 2, 0);
+    }
 
-    gtk_render_slider(context, paintInfo.context->platformContext()->cr(), rect.x(), rect.y(), rect.width(), rect.height(),
-        part == SliderThumbHorizontalPart ? GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL);
+    gtk_render_background(context.get(), paintInfo.context().platformContext()->cr(), sliderRect.x(), sliderRect.y(), sliderRect.width(), sliderRect.height());
+    gtk_render_frame(context.get(), paintInfo.context().platformContext()->cr(), sliderRect.x(), sliderRect.y(), sliderRect.width(), sliderRect.height());
 
-    gtk_style_context_restore(context);
+    if (isFocused(renderObject)) {
+        gint focusWidth, focusPad;
+        gtk_style_context_get_style(context.get(), "focus-line-width", &focusWidth, "focus-padding", &focusPad, nullptr);
+        IntRect focusRect(sliderRect);
+        focusRect.inflate(focusWidth + focusPad);
+        gtk_render_focus(context.get(), paintInfo.context().platformContext()->cr(), focusRect.x(), focusRect.y(), focusRect.width(), focusRect.height());
+    }
 
     return false;
 }
 
-void RenderThemeGtk::adjustSliderThumbSize(RenderStyle& style, Element*) const
+void RenderThemeGtk::adjustSliderThumbSize(RenderStyle& style, const Element*) const
 {
     ControlPart part = style.appearance();
     if (part != SliderThumbHorizontalPart && part != SliderThumbVerticalPart)
         return;
 
+    GRefPtr<GtkStyleContext> context = createStyleContext(Scale);
     gint sliderWidth, sliderLength;
-    gtk_style_context_get_style(getStyleContext(GTK_TYPE_SCALE), "slider-width", &sliderWidth, "slider-length", &sliderLength, nullptr);
+    gtk_style_context_get_style(context.get(), "slider-width", &sliderWidth, "slider-length", &sliderLength, nullptr);
+
     if (part == SliderThumbHorizontalPart) {
         style.setWidth(Length(sliderLength, Fixed));
         style.setHeight(Length(sliderWidth, Fixed));
         return;
     }
-    ASSERT(part == SliderThumbVerticalPart || part == MediaVolumeSliderThumbPart);
+    ASSERT(part == SliderThumbVerticalPart);
     style.setWidth(Length(sliderWidth, Fixed));
     style.setHeight(Length(sliderLength, Fixed));
 }
 
+bool RenderThemeGtk::paintSliderThumb(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
+{
+    ControlPart part = renderObject.style().appearance();
+    ASSERT(part == SliderThumbHorizontalPart || part == SliderThumbVerticalPart);
+
+    // FIXME: The entire slider is too wide, stretching the thumb into an oval rather than a circle.
+    GRefPtr<GtkStyleContext> parentContext = createStyleContext(Scale);
+    gtk_style_context_add_class(parentContext.get(), part == SliderThumbHorizontalPart ? GTK_STYLE_CLASS_HORIZONTAL : GTK_STYLE_CLASS_VERTICAL);
+    GRefPtr<GtkStyleContext> troughContext = createStyleContext(ScaleTrough, parentContext.get());
+    GRefPtr<GtkStyleContext> context = createStyleContext(ScaleSlider, troughContext.get());
+    gtk_style_context_set_direction(context.get(), gtkTextDirection(renderObject.style().direction()));
+
+    guint flags = 0;
+    if (!isEnabled(renderObject))
+        flags |= GTK_STATE_FLAG_INSENSITIVE;
+    else if (isHovered(renderObject))
+        flags |= GTK_STATE_FLAG_PRELIGHT;
+    if (isPressed(renderObject))
+        flags |= GTK_STATE_FLAG_ACTIVE;
+    gtk_style_context_set_state(context.get(), static_cast<GtkStateFlags>(flags));
+
+    gtk_render_slider(context.get(), paintInfo.context().platformContext()->cr(), rect.x(), rect.y(), rect.width(), rect.height(),
+        part == SliderThumbHorizontalPart ? GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL);
+
+    return false;
+}
+#endif
+
+#if GTK_CHECK_VERSION(3, 20, 0)
+IntRect RenderThemeGtk::progressBarRectForBounds(const RenderObject& renderObject, const IntRect& bounds) const
+{
+    RenderThemeGadget::Info info = { RenderThemeGadget::Type::Generic, "progressbar", GTK_STATE_FLAG_NORMAL, { "horizontal" } };
+    auto progressBarGadget = RenderThemeGadget::create(info);
+    info.name = "trough";
+    info.classList.clear();
+    auto troughGadget = RenderThemeGadget::create(info, progressBarGadget.get());
+    info.name = "progress";
+    if (renderObject.style().direction() == RTL)
+        info.classList.append("right");
+    else
+        info.classList.append("left");
+    const auto& renderProgress = downcast<RenderProgress>(renderObject);
+    if (renderProgress.isDeterminate())
+        info.classList.append("pulse");
+    auto progressGadget = RenderThemeGadget::create(info, troughGadget.get());
+    IntSize preferredSize = progressBarGadget->preferredSize();
+    preferredSize = preferredSize.expandedTo(troughGadget->preferredSize());
+    preferredSize = preferredSize.expandedTo(progressGadget->preferredSize());
+    return IntRect(bounds.x(), bounds.y(), bounds.width(), preferredSize.height());
+}
+
 bool RenderThemeGtk::paintProgressBar(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
 {
     if (!renderObject.isProgress())
         return true;
 
-    GtkStyleContext* context = getStyleContext(GTK_TYPE_PROGRESS_BAR);
-    gtk_style_context_save(context);
-
-    gtk_style_context_add_class(context, GTK_STYLE_CLASS_TROUGH);
+    RenderThemeGadget::Info info = { RenderThemeGadget::Type::Generic, "progressbar", GTK_STATE_FLAG_NORMAL, { "horizontal" } };
+    auto progressBarGadget = RenderThemeGadget::create(info);
+    info.name = "trough";
+    info.classList.clear();
+    auto troughGadget = RenderThemeGadget::create(info, progressBarGadget.get());
+    info.name = "progress";
+    if (renderObject.style().direction() == RTL)
+        info.classList.append("right");
+    else
+        info.classList.append("left");
+    const auto& renderProgress = downcast<RenderProgress>(renderObject);
+    if (renderProgress.isDeterminate())
+        info.classList.append("pulse");
+    auto progressGadget = RenderThemeGadget::create(info, troughGadget.get());
+
+    progressBarGadget->render(paintInfo.context().platformContext()->cr(), rect);
+    troughGadget->render(paintInfo.context().platformContext()->cr(), rect);
+    progressGadget->render(paintInfo.context().platformContext()->cr(), calculateProgressRect(renderObject, rect));
+    return false;
+}
+#else
+IntRect RenderThemeGtk::progressBarRectForBounds(const RenderObject&, const IntRect& bounds) const
+{
+    return bounds;
+}
 
-    gtk_render_background(context, paintInfo.context->platformContext()->cr(), rect.x(), rect.y(), rect.width(), rect.height());
-    gtk_render_frame(context, paintInfo.context->platformContext()->cr(), rect.x(), rect.y(), rect.width(), rect.height());
+bool RenderThemeGtk::paintProgressBar(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
+{
+    if (!renderObject.isProgress())
+        return true;
 
-    gtk_style_context_restore(context);
+    GRefPtr<GtkStyleContext> parentContext = createStyleContext(ProgressBar);
+    GRefPtr<GtkStyleContext> troughContext = createStyleContext(ProgressBarTrough, parentContext.get());
+    GRefPtr<GtkStyleContext> context = createStyleContext(ProgressBarProgress, troughContext.get());
 
-    gtk_style_context_save(context);
-    gtk_style_context_add_class(context, GTK_STYLE_CLASS_PROGRESSBAR);
+    gtk_render_background(troughContext.get(), paintInfo.context().platformContext()->cr(), rect.x(), rect.y(), rect.width(), rect.height());
+    gtk_render_frame(troughContext.get(), paintInfo.context().platformContext()->cr(), rect.x(), rect.y(), rect.width(), rect.height());
 
+    gtk_style_context_set_state(context.get(), static_cast<GtkStateFlags>(0));
 
     GtkBorder padding;
-    gtk_style_context_get_padding(context, static_cast<GtkStateFlags>(0), &padding);
+    gtk_style_context_get_padding(context.get(), gtk_style_context_get_state(context.get()), &padding);
     IntRect progressRect(
         rect.x() + padding.left,
         rect.y() + padding.top,
@@ -1197,17 +1618,94 @@ bool RenderThemeGtk::paintProgressBar(const RenderObject& renderObject, const Pa
         rect.height() - (padding.top + padding.bottom));
     progressRect = RenderThemeGtk::calculateProgressRect(renderObject, progressRect);
 
-    if (!progressRect.isEmpty())
-        gtk_render_activity(context, paintInfo.context->platformContext()->cr(), progressRect.x(), progressRect.y(), progressRect.width(), progressRect.height());
+    if (!progressRect.isEmpty()) {
+#if GTK_CHECK_VERSION(3, 13, 7)
+        gtk_render_background(context.get(), paintInfo.context().platformContext()->cr(), progressRect.x(), progressRect.y(), progressRect.width(), progressRect.height());
+        gtk_render_frame(context.get(), paintInfo.context().platformContext()->cr(), progressRect.x(), progressRect.y(), progressRect.width(), progressRect.height());
+#else
+        gtk_render_activity(context.get(), paintInfo.context().platformContext()->cr(), progressRect.x(), progressRect.y(), progressRect.width(), progressRect.height());
+#endif
+    }
 
-    gtk_style_context_restore(context);
     return false;
 }
+#endif // GTK_CHECK_VERSION(3, 20, 0)
+
+#if GTK_CHECK_VERSION(3, 20, 0)
+RenderTheme::InnerSpinButtonLayout RenderThemeGtk::innerSpinButtonLayout(const RenderObject& renderObject) const
+{
+    return renderObject.style().direction() == RTL ? InnerSpinButtonLayout::HorizontalUpLeft : InnerSpinButtonLayout::HorizontalUpRight;
+}
 
+void RenderThemeGtk::adjustInnerSpinButtonStyle(StyleResolver&, RenderStyle& style, const Element*) const
+{
+    RenderThemeGadget::Info info = { RenderThemeGadget::Type::Generic, "spinbutton", GTK_STATE_FLAG_NORMAL, { "horizontal" } };
+    auto spinbuttonGadget = RenderThemeGadget::create(info);
+    info.type = RenderThemeGadget::Type::TextField;
+    info.name = "entry";
+    info.classList.clear();
+    auto entryGadget = RenderThemeGadget::create(info, spinbuttonGadget.get());
+    info.type = RenderThemeGadget::Type::Icon;
+    info.name = "button";
+    info.classList.append("up");
+    auto buttonUpGadget = RenderThemeGadget::create(info, spinbuttonGadget.get());
+    static_cast<RenderThemeIconGadget*>(buttonUpGadget.get())->setIconSize(RenderThemeIconGadget::IconSizeGtk::Menu);
+    info.classList[0] = "down";
+    auto buttonDownGadget = RenderThemeGadget::create(info, spinbuttonGadget.get());
+    static_cast<RenderThemeIconGadget*>(buttonDownGadget.get())->setIconSize(RenderThemeIconGadget::IconSizeGtk::Menu);
+    IntSize upPreferredSize = buttonUpGadget->preferredSize();
+    IntSize downPreferredSize = buttonDownGadget->preferredSize();
+    int buttonSize = std::max(std::max(upPreferredSize.width(), downPreferredSize.width()), std::max(upPreferredSize.height(), downPreferredSize.height()));
+    style.setWidth(Length(buttonSize * 2, Fixed));
+    style.setHeight(Length(buttonSize, Fixed));
+}
+
+bool RenderThemeGtk::paintInnerSpinButton(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
+{
+    RenderThemeGadget::Info info = { RenderThemeGadget::Type::Generic, "spinbutton", themePartStateFlags(*this, SpinButton, renderObject), { } };
+    auto spinbuttonGadget = RenderThemeGadget::create(info);
+    info.type = RenderThemeGadget::Type::TextField;
+    info.name = "entry";
+    info.classList.clear();
+    auto entryGadget = RenderThemeGadget::create(info, spinbuttonGadget.get());
+    info.type = RenderThemeGadget::Type::Icon;
+    info.name = "button";
+    info.classList.append("up");
+    info.state = themePartStateFlags(*this, SpinButtonUpButton, renderObject);
+    auto buttonUpGadget = RenderThemeGadget::create(info, spinbuttonGadget.get());
+    auto* gadgetIcon = static_cast<RenderThemeIconGadget*>(buttonUpGadget.get());
+    gadgetIcon->setIconSize(RenderThemeIconGadget::IconSizeGtk::Menu);
+    gadgetIcon->setIconName("list-add-symbolic");
+    info.classList[0] = "down";
+    info.state = themePartStateFlags(*this, SpinButtonDownButton, renderObject);
+    auto buttonDownGadget = RenderThemeGadget::create(info, spinbuttonGadget.get());
+    gadgetIcon = static_cast<RenderThemeIconGadget*>(buttonDownGadget.get());
+    gadgetIcon->setIconSize(RenderThemeIconGadget::IconSizeGtk::Menu);
+    gadgetIcon->setIconName("list-remove-symbolic");
+
+    IntRect iconRect = rect;
+    iconRect.setWidth(iconRect.width() / 2);
+    if (renderObject.style().direction() == RTL)
+        buttonUpGadget->render(paintInfo.context().platformContext()->cr(), iconRect);
+    else
+        buttonDownGadget->render(paintInfo.context().platformContext()->cr(), iconRect);
+    iconRect.move(iconRect.width(), 0);
+    if (renderObject.style().direction() == RTL)
+        buttonDownGadget->render(paintInfo.context().platformContext()->cr(), iconRect);
+    else
+        buttonUpGadget->render(paintInfo.context().platformContext()->cr(), iconRect);
+
+    return false;
+}
+#else
+RenderTheme::InnerSpinButtonLayout RenderThemeGtk::innerSpinButtonLayout(const RenderObject&) const
+{
+    return InnerSpinButtonLayout::Vertical;
+}
 static gint spinButtonArrowSize(GtkStyleContext* context)
 {
     PangoFontDescription* fontDescription;
-    gtk_style_context_get(context, static_cast<GtkStateFlags>(0), "font", &fontDescription, nullptr);
+    gtk_style_context_get(context, gtk_style_context_get_state(context), "font", &fontDescription, nullptr);
     gint fontSize = pango_font_description_get_size(fontDescription);
     gint arrowSize = std::max(PANGO_PIXELS(fontSize), minSpinButtonArrowSize);
     pango_font_description_free(fontDescription);
@@ -1215,27 +1713,25 @@ static gint spinButtonArrowSize(GtkStyleContext* context)
     return arrowSize - arrowSize % 2; // Force even.
 }
 
-void RenderThemeGtk::adjustInnerSpinButtonStyle(StyleResolver&, RenderStyle& style, Element*) const
+void RenderThemeGtk::adjustInnerSpinButtonStyle(StyleResolver&, RenderStyle& style, const Element*) const
 {
-    GtkStyleContext* context = getStyleContext(GTK_TYPE_SPIN_BUTTON);
+    GRefPtr<GtkStyleContext> context = createStyleContext(SpinButton);
 
     GtkBorder padding;
-    gtk_style_context_get_padding(context, static_cast<GtkStateFlags>(0), &padding);
+    gtk_style_context_get_padding(context.get(), gtk_style_context_get_state(context.get()), &padding);
 
-    int width = spinButtonArrowSize(context) + padding.left + padding.right;
+    int width = spinButtonArrowSize(context.get()) + padding.left + padding.right;
     style.setWidth(Length(width, Fixed));
     style.setMinWidth(Length(width, Fixed));
 }
 
-static void paintSpinArrowButton(RenderTheme* theme, GtkStyleContext* context, const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect, GtkArrowType arrowType)
+static void paintSpinArrowButton(RenderTheme* theme, GtkStyleContext* parentContext, const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect, GtkArrowType arrowType)
 {
     ASSERT(arrowType == GTK_ARROW_UP || arrowType == GTK_ARROW_DOWN);
 
-    gtk_style_context_save(context);
-    gtk_style_context_add_class(context, GTK_STYLE_CLASS_BUTTON);
-
-    GtkTextDirection direction = gtk_style_context_get_direction(context);
-    guint state = static_cast<guint>(gtk_style_context_get_state(context));
+    GRefPtr<GtkStyleContext> context = createStyleContext(arrowType == GTK_ARROW_UP ? SpinButtonUpButton : SpinButtonDownButton, parentContext);
+    GtkTextDirection direction = gtk_style_context_get_direction(context.get());
+    guint state = static_cast<guint>(gtk_style_context_get_state(context.get()));
     if (!(state & GTK_STATE_FLAG_INSENSITIVE)) {
         if (theme->isPressed(renderObject)) {
             if ((arrowType == GTK_ARROW_UP && theme->isSpinUpButtonPartPressed(renderObject))
@@ -1247,11 +1743,11 @@ static void paintSpinArrowButton(RenderTheme* theme, GtkStyleContext* context, c
                 state |= GTK_STATE_FLAG_PRELIGHT;
         }
     }
-    gtk_style_context_set_state(context, static_cast<GtkStateFlags>(state));
+    gtk_style_context_set_state(context.get(), static_cast<GtkStateFlags>(state));
 
     // Paint button.
     IntRect buttonRect(rect);
-    guint junction = gtk_style_context_get_junction_sides(context);
+    guint junction = gtk_style_context_get_junction_sides(context.get());
     if (arrowType == GTK_ARROW_UP)
         junction |= GTK_JUNCTION_BOTTOM;
     else {
@@ -1259,10 +1755,10 @@ static void paintSpinArrowButton(RenderTheme* theme, GtkStyleContext* context, c
         buttonRect.move(0, rect.height() / 2);
     }
     buttonRect.setHeight(rect.height() / 2);
-    gtk_style_context_set_junction_sides(context, static_cast<GtkJunctionSides>(junction));
+    gtk_style_context_set_junction_sides(context.get(), static_cast<GtkJunctionSides>(junction));
 
-    gtk_render_background(context, paintInfo.context->platformContext()->cr(), buttonRect.x(), buttonRect.y(), buttonRect.width(), buttonRect.height());
-    gtk_render_frame(context, paintInfo.context->platformContext()->cr(), buttonRect.x(), buttonRect.y(), buttonRect.width(), buttonRect.height());
+    gtk_render_background(context.get(), paintInfo.context().platformContext()->cr(), buttonRect.x(), buttonRect.y(), buttonRect.width(), buttonRect.height());
+    gtk_render_frame(context.get(), paintInfo.context().platformContext()->cr(), buttonRect.x(), buttonRect.y(), buttonRect.width(), buttonRect.height());
 
     // Paint arrow centered inside button.
     // This code is based on gtkspinbutton.c code.
@@ -1288,34 +1784,27 @@ static void paintSpinArrowButton(RenderTheme* theme, GtkStyleContext* context, c
     gint height = (width + 1) / 2;
 
     arrowRect.move((arrowRect.width() - width) / 2, (arrowRect.height() - height) / 2);
-    gtk_render_arrow(context, paintInfo.context->platformContext()->cr(), angle, arrowRect.x(), arrowRect.y(), width);
-
-    gtk_style_context_restore(context);
+    gtk_render_arrow(context.get(), paintInfo.context().platformContext()->cr(), angle, arrowRect.x(), arrowRect.y(), width);
 }
 
 bool RenderThemeGtk::paintInnerSpinButton(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
 {
-    GtkStyleContext* context = getStyleContext(GTK_TYPE_SPIN_BUTTON);
-    gtk_style_context_save(context);
-
-    GtkTextDirection direction = static_cast<GtkTextDirection>(gtkTextDirection(renderObject.style().direction()));
-    gtk_style_context_set_direction(context, direction);
+    GRefPtr<GtkStyleContext> context = createStyleContext(SpinButton);
+    gtk_style_context_set_direction(context.get(), gtkTextDirection(renderObject.style().direction()));
 
     guint flags = 0;
     if (!isEnabled(renderObject) || isReadOnlyControl(renderObject))
         flags |= GTK_STATE_FLAG_INSENSITIVE;
     else if (isFocused(renderObject))
         flags |= GTK_STATE_FLAG_FOCUSED;
-    gtk_style_context_set_state(context, static_cast<GtkStateFlags>(flags));
-    gtk_style_context_remove_class(context, GTK_STYLE_CLASS_ENTRY);
-
-    paintSpinArrowButton(this, context, renderObject, paintInfo, rect, GTK_ARROW_UP);
-    paintSpinArrowButton(this, context, renderObject, paintInfo, rect, GTK_ARROW_DOWN);
+    gtk_style_context_set_state(context.get(), static_cast<GtkStateFlags>(flags));
 
-    gtk_style_context_restore(context);
+    paintSpinArrowButton(this, context.get(), renderObject, paintInfo, rect, GTK_ARROW_UP);
+    paintSpinArrowButton(this, context.get(), renderObject, paintInfo, rect, GTK_ARROW_DOWN);
 
     return false;
 }
+#endif // GTK_CHECK_VERSION(3, 20, 0)
 
 double RenderThemeGtk::caretBlinkInterval() const
 {
@@ -1332,73 +1821,104 @@ double RenderThemeGtk::caretBlinkInterval() const
     return time / 2000.;
 }
 
-Color RenderThemeGtk::platformActiveSelectionBackgroundColor() const
+enum StyleColorType { StyleColorBackground, StyleColorForeground };
+
+#if GTK_CHECK_VERSION(3, 20, 0)
+static Color styleColor(RenderThemePart themePart, GtkStateFlags state, StyleColorType colorType)
+{
+    RenderThemeGadget::Info info = { RenderThemeGadget::Type::Generic, nullptr, state, { } };
+    std::unique_ptr<RenderThemeGadget> parentGadget;
+    RenderThemePart part = themePart;
+    if (themePart == Entry && (state & GTK_STATE_FLAG_SELECTED)) {
+        info.name = "entry";
+        parentGadget = RenderThemeGadget::create(info);
+        part = EntrySelection;
+    }
+
+    switch (part) {
+    case Entry:
+        info.name = "entry";
+        break;
+    case EntrySelection:
+        info.name = "selection";
+        break;
+    case ListBox:
+        info.name = "treeview";
+        info.classList.append("view");
+        break;
+    case Button:
+        info.name = "button";
+        break;
+    default:
+        ASSERT_NOT_REACHED();
+        info.name = "entry";
+    }
+
+    auto gadget = RenderThemeGadget::create(info, parentGadget.get());
+    return colorType == StyleColorBackground ? gadget->backgroundColor() : gadget->color();
+}
+#else
+static Color styleColor(RenderThemePart themePart, GtkStateFlags state, StyleColorType colorType)
 {
+    GRefPtr<GtkStyleContext> context = createStyleContext(themePart);
+    gtk_style_context_set_state(context.get(), state);
+
     GdkRGBA gdkRGBAColor;
-    gtk_style_context_get_background_color(getStyleContext(GTK_TYPE_ENTRY), static_cast<GtkStateFlags>(GTK_STATE_FLAG_SELECTED | GTK_STATE_FLAG_FOCUSED), &gdkRGBAColor);
+    if (colorType == StyleColorBackground)
+        gtk_style_context_get_background_color(context.get(), state, &gdkRGBAColor);
+    else
+        gtk_style_context_get_color(context.get(), state, &gdkRGBAColor);
     return gdkRGBAColor;
 }
+#endif // GTK_CHECK_VERSION(3, 20, 0)
+
+Color RenderThemeGtk::platformActiveSelectionBackgroundColor() const
+{
+    return styleColor(Entry, static_cast<GtkStateFlags>(GTK_STATE_FLAG_SELECTED | GTK_STATE_FLAG_FOCUSED), StyleColorBackground);
+}
 
 Color RenderThemeGtk::platformInactiveSelectionBackgroundColor() const
 {
-    GdkRGBA gdkRGBAColor;
-    gtk_style_context_get_background_color(getStyleContext(GTK_TYPE_ENTRY), GTK_STATE_FLAG_SELECTED, &gdkRGBAColor);
-    return gdkRGBAColor;
+    return styleColor(Entry, GTK_STATE_FLAG_SELECTED, StyleColorBackground);
 }
 
 Color RenderThemeGtk::platformActiveSelectionForegroundColor() const
 {
-    GdkRGBA gdkRGBAColor;
-    gtk_style_context_get_color(getStyleContext(GTK_TYPE_ENTRY), static_cast<GtkStateFlags>(GTK_STATE_FLAG_SELECTED | GTK_STATE_FLAG_FOCUSED), &gdkRGBAColor);
-    return gdkRGBAColor;
+    return styleColor(Entry, static_cast<GtkStateFlags>(GTK_STATE_FLAG_SELECTED | GTK_STATE_FLAG_FOCUSED), StyleColorForeground);
 }
 
 Color RenderThemeGtk::platformInactiveSelectionForegroundColor() const
 {
-    GdkRGBA gdkRGBAColor;
-    gtk_style_context_get_color(getStyleContext(GTK_TYPE_ENTRY), GTK_STATE_FLAG_SELECTED, &gdkRGBAColor);
-    return gdkRGBAColor;
+    return styleColor(Entry, GTK_STATE_FLAG_SELECTED, StyleColorForeground);
 }
 
 Color RenderThemeGtk::platformActiveListBoxSelectionBackgroundColor() const
 {
-    GdkRGBA gdkRGBAColor;
-    gtk_style_context_get_background_color(getStyleContext(GTK_TYPE_TREE_VIEW), static_cast<GtkStateFlags>(GTK_STATE_FLAG_SELECTED | GTK_STATE_FLAG_FOCUSED), &gdkRGBAColor);
-    return gdkRGBAColor;
+    return styleColor(ListBox, static_cast<GtkStateFlags>(GTK_STATE_FLAG_SELECTED | GTK_STATE_FLAG_FOCUSED), StyleColorBackground);
 }
 
 Color RenderThemeGtk::platformInactiveListBoxSelectionBackgroundColor() const
 {
-    GdkRGBA gdkRGBAColor;
-    gtk_style_context_get_background_color(getStyleContext(GTK_TYPE_TREE_VIEW), GTK_STATE_FLAG_SELECTED, &gdkRGBAColor);
-    return gdkRGBAColor;
+    return styleColor(ListBox, GTK_STATE_FLAG_SELECTED, StyleColorBackground);
 }
 
 Color RenderThemeGtk::platformActiveListBoxSelectionForegroundColor() const
 {
-    GdkRGBA gdkRGBAColor;
-    gtk_style_context_get_color(getStyleContext(GTK_TYPE_TREE_VIEW), static_cast<GtkStateFlags>(GTK_STATE_FLAG_SELECTED | GTK_STATE_FLAG_FOCUSED), &gdkRGBAColor);
-    return gdkRGBAColor;
+    return styleColor(ListBox, static_cast<GtkStateFlags>(GTK_STATE_FLAG_SELECTED | GTK_STATE_FLAG_FOCUSED), StyleColorForeground);
 }
 
 Color RenderThemeGtk::platformInactiveListBoxSelectionForegroundColor() const
 {
-    GdkRGBA gdkRGBAColor;
-    gtk_style_context_get_color(getStyleContext(GTK_TYPE_TREE_VIEW), GTK_STATE_FLAG_SELECTED, &gdkRGBAColor);
-    return gdkRGBAColor;
+    return styleColor(ListBox, GTK_STATE_FLAG_SELECTED, StyleColorForeground);
 }
 
 Color RenderThemeGtk::systemColor(CSSValueID cssValueId) const
 {
-    GdkRGBA gdkRGBAColor;
-
     switch (cssValueId) {
     case CSSValueButtontext:
-        gtk_style_context_get_color(getStyleContext(GTK_TYPE_BUTTON), GTK_STATE_FLAG_ACTIVE, &gdkRGBAColor);
-        return gdkRGBAColor;
+        return styleColor(Button, GTK_STATE_FLAG_ACTIVE, StyleColorForeground);
     case CSSValueCaptiontext:
-        gtk_style_context_get_color(getStyleContext(GTK_TYPE_ENTRY), static_cast<GtkStateFlags>(0), &gdkRGBAColor);
-        return gdkRGBAColor;
+        return styleColor(Entry, GTK_STATE_FLAG_ACTIVE, StyleColorForeground);
     default:
         return RenderTheme::systemColor(cssValueId);
     }
@@ -1406,9 +1926,6 @@ Color RenderThemeGtk::systemColor(CSSValueID cssValueId) const
 
 void RenderThemeGtk::platformColorsDidChange()
 {
-#if ENABLE(VIDEO)
-    initMediaColors();
-#endif
     RenderTheme::platformColorsDidChange();
 }
 
@@ -1425,17 +1942,27 @@ String RenderThemeGtk::extraFullScreenStyleSheet()
 }
 #endif
 
-bool RenderThemeGtk::paintMediaButton(const RenderObject& renderObject, GraphicsContext* context, const IntRect& rect, const char* symbolicIconName, const char* fallbackStockIconName)
+#if GTK_CHECK_VERSION(3, 20, 0)
+bool RenderThemeGtk::paintMediaButton(const RenderObject& renderObject, GraphicsContext& graphicsContext, const IntRect& rect, const char* iconName)
 {
-    IntRect iconRect(
-        rect.x() + (rect.width() - m_mediaIconSize) / 2,
-        rect.y() + (rect.height() - m_mediaIconSize) / 2,
-        m_mediaIconSize, m_mediaIconSize);
-    GRefPtr<GdkPixbuf> icon = getStockSymbolicIconForWidgetType(GTK_TYPE_CONTAINER, symbolicIconName, fallbackStockIconName,
-        gtkTextDirection(renderObject.style().direction()), gtkIconState(this, renderObject), iconRect.width());
-    paintGdkPixbuf(context, icon.get(), iconRect);
-    return true;
+    RenderThemeGadget::Info info = { RenderThemeGadget::Type::Icon, "image", themePartStateFlags(*this, MediaButton, renderObject), { } };
+    auto gadget = RenderThemeGadget::create(info);
+    auto* gadgetIcon = static_cast<RenderThemeIconGadget*>(gadget.get());
+    gadgetIcon->setIconSize(RenderThemeIconGadget::IconSizeGtk::Menu);
+    gadgetIcon->setIconName(iconName);
+    return !gadget->render(graphicsContext.platformContext()->cr(), rect);
+}
+#else
+bool RenderThemeGtk::paintMediaButton(const RenderObject& renderObject, GraphicsContext& graphicsContext, const IntRect& rect, const char* iconName)
+{
+    GRefPtr<GtkStyleContext> context = createStyleContext(MediaButton);
+    gtk_style_context_set_direction(context.get(), gtkTextDirection(renderObject.style().direction()));
+    gtk_style_context_set_state(context.get(), gtkIconStateFlags(this, renderObject));
+    static const unsigned mediaIconSize = 16;
+    IntRect iconRect(rect.x() + (rect.width() - mediaIconSize) / 2, rect.y() + (rect.height() - mediaIconSize) / 2, mediaIconSize, mediaIconSize);
+    return !paintIcon(context.get(), graphicsContext, iconRect, iconName);
 }
+#endif // GTK_CHECK_VERSION(3, 20, 0)
 
 bool RenderThemeGtk::hasOwnDisabledStateHandlingFor(ControlPart part) const
 {
@@ -1444,59 +1971,47 @@ bool RenderThemeGtk::hasOwnDisabledStateHandlingFor(ControlPart part) const
 
 bool RenderThemeGtk::paintMediaFullscreenButton(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
 {
-    return paintMediaButton(renderObject, paintInfo.context, rect, "view-fullscreen-symbolic", GTK_STOCK_FULLSCREEN);
+    return paintMediaButton(renderObject, paintInfo.context(), rect, "view-fullscreen-symbolic");
 }
 
 bool RenderThemeGtk::paintMediaMuteButton(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
 {
-    HTMLMediaElement* mediaElement = getMediaElementFromRenderObject(renderObject);
-    if (!mediaElement)
-        return false;
+    Node* node = renderObject.node();
+    if (!node)
+        return true;
+    Node* mediaNode = node->shadowHost();
+    if (!is<HTMLMediaElement>(mediaNode))
+        return true;
 
-    bool muted = mediaElement->muted();
-    return paintMediaButton(renderObject, paintInfo.context, rect,
-        muted ? "audio-volume-muted-symbolic" : "audio-volume-high-symbolic",
-        muted ? "audio-volume-muted" : "audio-volume-high");
+    HTMLMediaElement* mediaElement = downcast<HTMLMediaElement>(mediaNode);
+    return paintMediaButton(renderObject, paintInfo.context(), rect, mediaElement->muted() ? "audio-volume-muted-symbolic" : "audio-volume-high-symbolic");
 }
 
 bool RenderThemeGtk::paintMediaPlayButton(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
 {
     Node* node = renderObject.node();
     if (!node)
-        return false;
-
+        return true;
     if (!nodeHasPseudo(node, "-webkit-media-controls-play-button"))
-        return false;
-    bool showPlayButton = nodeHasClass(node, "paused");
+        return true;
 
-    return paintMediaButton(renderObject, paintInfo.context, rect,
-        showPlayButton ? "media-playback-start-symbolic" : "media-playback-pause-symbolic",
-        showPlayButton ? GTK_STOCK_MEDIA_PLAY : GTK_STOCK_MEDIA_PAUSE);
+    return paintMediaButton(renderObject, paintInfo.context(), rect, nodeHasClass(node, "paused") ? "media-playback-start-symbolic" : "media-playback-pause-symbolic");
 }
 
 bool RenderThemeGtk::paintMediaSeekBackButton(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
 {
-    return paintMediaButton(renderObject, paintInfo.context, rect, "media-seek-backward-symbolic", GTK_STOCK_MEDIA_REWIND);
+    return paintMediaButton(renderObject, paintInfo.context(), rect, "media-seek-backward-symbolic");
 }
 
 bool RenderThemeGtk::paintMediaSeekForwardButton(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
 {
-    return paintMediaButton(renderObject, paintInfo.context, rect, "media-seek-forward-symbolic", GTK_STOCK_MEDIA_FORWARD);
+    return paintMediaButton(renderObject, paintInfo.context(), rect, "media-seek-forward-symbolic");
 }
 
 #if ENABLE(VIDEO_TRACK)
 bool RenderThemeGtk::paintMediaToggleClosedCaptionsButton(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
 {
-    IntRect iconRect(rect.x() + (rect.width() - m_mediaIconSize) / 2, rect.y() + (rect.height() - m_mediaIconSize) / 2,
-        m_mediaIconSize, m_mediaIconSize);
-    GRefPtr<GdkPixbuf> icon = getStockSymbolicIconForWidgetType(GTK_TYPE_CONTAINER, "media-view-subtitles-symbolic", nullptr,
-        gtkTextDirection(renderObject.style().direction()), gtkIconState(this, renderObject), iconRect.width());
-    if (!icon) {
-        icon = getStockSymbolicIconForWidgetType(GTK_TYPE_CONTAINER, "user-invisible-symbolic", GTK_STOCK_JUSTIFY_FILL,
-            gtkTextDirection(renderObject.style().direction()), gtkIconState(this, renderObject), iconRect.width());
-    }
-    paintGdkPixbuf(paintInfo.context, icon.get(), iconRect);
-    return true;
+    return paintMediaButton(renderObject, paintInfo.context(), rect, "media-view-subtitles-symbolic");
 }
 #endif
 
@@ -1513,11 +2028,11 @@ bool RenderThemeGtk::paintMediaSliderTrack(const RenderObject& o, const PaintInf
 {
     HTMLMediaElement* mediaElement = parentMediaElement(o);
     if (!mediaElement)
-        return false;
+        return true;
 
-    GraphicsContext* context = paintInfo.context;
-    context->save();
-    context->setStrokeStyle(NoStroke);
+    GraphicsContext& context = paintInfo.context();
+    context.save();
+    context.setStrokeStyle(NoStroke);
 
     float mediaDuration = mediaElement->duration();
     float totalTrackWidth = r.width();
@@ -1535,25 +2050,20 @@ bool RenderThemeGtk::paintMediaSliderTrack(const RenderObject& o, const PaintInf
         rangeRect.setWidth(lengthRatio * totalTrackWidth);
         if (index)
             rangeRect.move(startRatio * totalTrackWidth, 0);
-        context->fillRoundedRect(FloatRoundedRect(rangeRect, borderRadiiFromStyle(style)), style.visitedDependentColor(CSSPropertyColor), style.colorSpace());
+        context.fillRoundedRect(FloatRoundedRect(rangeRect, borderRadiiFromStyle(style)), style.visitedDependentColor(CSSPropertyColor));
     }
 
-    context->restore();
+    context.restore();
     return false;
 }
 
 bool RenderThemeGtk::paintMediaSliderThumb(const RenderObject& o, const PaintInfo& paintInfo, const IntRect& r)
 {
     RenderStyle& style = o.style();
-    paintInfo.context->fillRoundedRect(FloatRoundedRect(r, borderRadiiFromStyle(style)), style.visitedDependentColor(CSSPropertyColor), style.colorSpace());
+    paintInfo.context().fillRoundedRect(FloatRoundedRect(r, borderRadiiFromStyle(style)), style.visitedDependentColor(CSSPropertyColor));
     return false;
 }
 
-bool RenderThemeGtk::paintMediaVolumeSliderContainer(const RenderObject&, const PaintInfo&, const IntRect&)
-{
-    return true;
-}
-
 bool RenderThemeGtk::paintMediaVolumeSliderTrack(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
 {
     HTMLMediaElement* mediaElement = parentMediaElement(renderObject);
@@ -1564,9 +2074,9 @@ bool RenderThemeGtk::paintMediaVolumeSliderTrack(const RenderObject& renderObjec
     if (!volume)
         return true;
 
-    GraphicsContext* context = paintInfo.context;
-    context->save();
-    context->setStrokeStyle(NoStroke);
+    GraphicsContext& context = paintInfo.context();
+    context.save();
+    context.setStrokeStyle(NoStroke);
 
     int rectHeight = rect.height();
     float trackHeight = rectHeight * volume;
@@ -1575,9 +2085,8 @@ bool RenderThemeGtk::paintMediaVolumeSliderTrack(const RenderObject& renderObjec
     volumeRect.move(0, rectHeight - trackHeight);
     volumeRect.setHeight(ceil(trackHeight));
 
-    context->fillRoundedRect(FloatRoundedRect(volumeRect, borderRadiiFromStyle(style)),
-        style.visitedDependentColor(CSSPropertyColor), style.colorSpace());
-    context->restore();
+    context.fillRoundedRect(FloatRoundedRect(volumeRect, borderRadiiFromStyle(style)), style.visitedDependentColor(CSSPropertyColor));
+    context.restore();
 
     return false;
 }
@@ -1598,7 +2107,7 @@ bool RenderThemeGtk::paintMediaCurrentTime(const RenderObject&, const PaintInfo&
 }
 #endif
 
-void RenderThemeGtk::adjustProgressBarStyle(StyleResolver&, RenderStyle& style, Element*) const
+void RenderThemeGtk::adjustProgressBarStyle(StyleResolver&, RenderStyle& style, const Element*) const
 {
     style.setBoxShadow(nullptr);
 }
@@ -1620,7 +2129,7 @@ double RenderThemeGtk::animationDurationForProgressBar(RenderProgress&) const
 IntRect RenderThemeGtk::calculateProgressRect(const RenderObject& renderObject, const IntRect& fullBarRect)
 {
     IntRect progressRect(fullBarRect);
-    const RenderProgress& renderProgress = toRenderProgress(renderObject);
+    const auto& renderProgress = downcast<RenderProgress>(renderObject);
     if (renderProgress.isDeterminate()) {
         int progressWidth = progressRect.width() * renderProgress.position();
         if (renderObject.style().direction() == RTL)
@@ -1647,13 +2156,13 @@ IntRect RenderThemeGtk::calculateProgressRect(const RenderObject& renderObject,
     return progressRect;
 }
 
-String RenderThemeGtk::fileListNameForWidth(const FileList* fileList, const Font& font, int width, bool multipleFilesAllowed) const
+String RenderThemeGtk::fileListNameForWidth(const FileList* fileList, const FontCascade& font, int width, bool multipleFilesAllowed) const
 {
     if (width <= 0)
         return String();
 
     if (fileList->length() > 1)
-        return StringTruncator::rightTruncate(multipleFileUploadText(fileList->length()), width, font, StringTruncator::EnableRoundingHacks);
+        return StringTruncator::rightTruncate(multipleFileUploadText(fileList->length()), width, font);
 
     String string;
     if (fileList->length())
@@ -1663,9 +2172,10 @@ String RenderThemeGtk::fileListNameForWidth(const FileList* fileList, const Font
     else
         string = fileButtonNoFileSelectedLabel();
 
-    return StringTruncator::centerTruncate(string, width, font, StringTruncator::EnableRoundingHacks);
+    return StringTruncator::centerTruncate(string, width, font);
 }
 
+#if ENABLE(VIDEO)
 String RenderThemeGtk::mediaControlsScript()
 {
     StringBuilder scriptBuilder;
@@ -1674,6 +2184,7 @@ String RenderThemeGtk::mediaControlsScript()
     scriptBuilder.append(mediaControlsGtkJavaScript, sizeof(mediaControlsGtkJavaScript));
     return scriptBuilder.toString();
 }
+#endif // ENABLE(VIDEO)
 
 #endif // GTK_API_VERSION_2
 }