[GTK] Rework the theming code for GTK+ 3.20
authorcarlosgc@webkit.org <carlosgc@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 11 Apr 2016 10:59:03 +0000 (10:59 +0000)
committercarlosgc@webkit.org <carlosgc@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 11 Apr 2016 10:59:03 +0000 (10:59 +0000)
https://bugs.webkit.org/show_bug.cgi?id=156333

Reviewed by Michael Catanzaro.

.:

Add a manual test to check how themed elements are rendered.

* ManualTests/gtk/theme.html: Added.

Source/WebCore:

During the 3.19 GTK+ release cycle, the GTK+ css system was reworked, making themes and programs rendering
themed widgets, incompatible with the new system. We were trying to fix our rendering every time GTK+ broke
something, but we were just changing whatever it was needed to make our rendering look like current GTK+ with
the default theme Adwaita. This means that our rendering will be broken for other themes or that changes in
Adwaita can break our rendering. This solution was good enough to ensure WebKitGTK+ 2.12 looked good with GTK+
3.20, but it doesn't work in the long term. We need to ensure that our theming code honors the new GTK+ CSS
properties (max-width, min-width, margin, padding, border, ...) in all the cases, not only the cases where
Adwaita uses them like we currently do.
This patch splits all rendering methods to keep the current code for previous GTK+ versions and adds new code
for GTK+ >= 3.20 using the new RenderThemeGadget classes. This makes the code easier to read, since there aren't
ifdef blocks in the functions, and we ensure we don't break previous rendering.

* PlatformGTK.cmake: Add new files to compilation.
* html/shadow/SpinButtonElement.cpp:
(WebCore::SpinButtonElement::defaultEventHandler): Check the button layout used by the theme to decide the
current buttons state.
* platform/gtk/RenderThemeGadget.cpp: Added.
(WebCore::RenderThemeGadget::create):
(WebCore::createStyleContext):
(WebCore::appendElementToPath):
(WebCore::RenderThemeGadget::RenderThemeGadget):
(WebCore::RenderThemeGadget::~RenderThemeGadget):
(WebCore::RenderThemeGadget::marginBox):
(WebCore::RenderThemeGadget::borderBox):
(WebCore::RenderThemeGadget::paddingBox):
(WebCore::RenderThemeGadget::contentsBox):
(WebCore::RenderThemeGadget::color):
(WebCore::RenderThemeGadget::backgroundColor):
(WebCore::RenderThemeGadget::minimumSize):
(WebCore::RenderThemeGadget::preferredSize):
(WebCore::RenderThemeGadget::render):
(WebCore::RenderThemeGadget::renderFocus):
(WebCore::RenderThemeBoxGadget::RenderThemeBoxGadget):
(WebCore::RenderThemeTextFieldGadget::RenderThemeTextFieldGadget):
(WebCore::RenderThemeTextFieldGadget::minimumSize):
(WebCore::RenderThemeToggleGadget::RenderThemeToggleGadget):
(WebCore::RenderThemeToggleGadget::render):
(WebCore::RenderThemeArrowGadget::RenderThemeArrowGadget):
(WebCore::RenderThemeArrowGadget::render):
(WebCore::RenderThemeIconGadget::RenderThemeIconGadget):
(WebCore::RenderThemeIconGadget::gtkIconSizeForPixelSize):
(WebCore::RenderThemeIconGadget::render):
(WebCore::RenderThemeIconGadget::minimumSize):
* platform/gtk/RenderThemeGadget.h: Added.
(WebCore::RenderThemeGadget::context):
* rendering/RenderTheme.h:
(WebCore::RenderTheme::innerSpinButtonLayout): Added this method to allow themes use a different layout for the
buttons.
* rendering/RenderThemeGtk.cpp:
(WebCore::themeChangedCallback): Just moved this code to a common place.
(WebCore::RenderThemeGtk::RenderThemeGtk): Initialize the theme monitor in the constructor.
(WebCore::createStyleContext): Remove the render parts that are specific to GTK+ 3.20.
(WebCore::RenderThemeGtk::adjustRepaintRect): Moved inside a GTK+ < 3.20 ifdef block.
(WebCore::themePartStateFlags): Helper function to get the GtkStateFlags of a theme part for a given RenderObject.
(WebCore::shrinkToMinimumSizeAndCenterRectangle): Move this common code to a helper function.
(WebCore::setToggleSize):
(WebCore::paintToggle):
(WebCore::RenderThemeGtk::paintButton):
(WebCore::RenderThemeGtk::popupInternalPaddingBox):
(WebCore::RenderThemeGtk::paintMenuList):
(WebCore::RenderThemeGtk::adjustTextFieldStyle): For GTK+ 3.20 we need to ensure a minimum size for spin buttons,
so if the text field is for a spin button, we adjust the desired size here.
(WebCore::RenderThemeGtk::paintTextField): In GTK+ 3.20 the CSS gadgets used to render spin buttons are
different, so we check here if this is the entry of a spin button to use the right gadgets.
(WebCore::adjustSearchFieldIconStyle):
(WebCore::RenderThemeGtk::paintTextArea):
(WebCore::RenderThemeGtk::adjustSearchFieldResultsButtonStyle):
(WebCore::RenderThemeGtk::paintSearchFieldResultsButton):
(WebCore::RenderThemeGtk::adjustSearchFieldResultsDecorationPartStyle):
(WebCore::RenderThemeGtk::adjustSearchFieldCancelButtonStyle):
(WebCore::paintSearchFieldIcon):
(WebCore::RenderThemeGtk::paintSearchFieldResultsDecorationPart):
(WebCore::RenderThemeGtk::paintSearchFieldCancelButton):
(WebCore::centerRectVerticallyInParentInputElement): Moved inside a GTK+ < 3.20 ifdef block.
(WebCore::RenderThemeGtk::paintSliderTrack):
(WebCore::RenderThemeGtk::adjustSliderThumbSize):
(WebCore::RenderThemeGtk::paintSliderThumb):
(WebCore::RenderThemeGtk::progressBarRectForBounds): Ensure a minimum size of progress bars in GTK+ 3.20.
(WebCore::RenderThemeGtk::paintProgressBar):
(WebCore::RenderThemeGtk::innerSpinButtonLayout): Use an horizontal layout for spin buttons.
(WebCore::RenderThemeGtk::adjustInnerSpinButtonStyle):
(WebCore::RenderThemeGtk::paintInnerSpinButton):
(WebCore::styleColor):
(WebCore::RenderThemeGtk::paintMediaButton):
* rendering/RenderThemeGtk.h:

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@199292 268f45cc-cd09-0410-ab3c-d52691b4dbfc

ChangeLog
ManualTests/gtk/theme.html [new file with mode: 0644]
Source/WebCore/ChangeLog
Source/WebCore/PlatformGTK.cmake
Source/WebCore/html/shadow/SpinButtonElement.cpp
Source/WebCore/platform/gtk/RenderThemeGadget.cpp [new file with mode: 0644]
Source/WebCore/platform/gtk/RenderThemeGadget.h [new file with mode: 0644]
Source/WebCore/rendering/RenderTheme.h
Source/WebCore/rendering/RenderThemeGtk.cpp
Source/WebCore/rendering/RenderThemeGtk.h

index 6f590be..c704c7d 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,14 @@
+2016-04-07  Carlos Garcia Campos  <cgarcia@igalia.com>
+
+        [GTK] Rework the theming code for GTK+ 3.20
+        https://bugs.webkit.org/show_bug.cgi?id=156333
+
+        Reviewed by Michael Catanzaro.
+
+        Add a manual test to check how themed elements are rendered.
+
+        * ManualTests/gtk/theme.html: Added.
+
 2016-04-11  Fujii Hironori  <Hironori.Fujii@jp.sony.com>
 
         [CMake] Make FOLDER property INHERITED
diff --git a/ManualTests/gtk/theme.html b/ManualTests/gtk/theme.html
new file mode 100644 (file)
index 0000000..0da5e79
--- /dev/null
@@ -0,0 +1,151 @@
+<html>
+
+  <body id="body">
+  <h1>GTK+ themed elements</h1>
+
+  <p>This is a manual tests to check how all elements having a native appearance are rendered by WebKitGTK+.
+    To test different themes, open this test in MiniBrowser (or any other WebKitGTK+ based browser) passing
+    GTK_THEME=theme-name[:variant] environment variable.
+  </p>
+
+  <p>Text direction: <select onchange="body = document.getElementById('body'); if (this.selectedIndex == 0) body.style.direction='ltr'; else body.style.direction='rtl';">
+      <option>Left to right</option><option>Right to left</option></select>
+  </p>
+
+  <h1>Buttons</h1>
+  <table>
+    <tr>
+      <td><button type="button">Button</button></td>
+      <td><button type="button" disabled>Disabled</button></td>
+    </tr>
+    <tr>
+      <td><input type="radio" name="radio"/>Radio 1</td>
+      <td><input type="radio" name="radio"/>Radio 2</td>
+    </tr>
+    <tr>
+      <td><input type="radio" name="radio-disabled" disabled/>Disabled</td>
+      <td><input type="radio" name="radio-disabled" disabled checked/>Checked Disabled</td>
+    </tr>
+    <tr>
+      <td><input type="checkbox" name="check"/>Check</td>
+      <td><input type="checkbox" name="check-disabled" checked disabled/>Check Disabled</td>
+    </tr>
+  </table>
+
+  <h1>Combos</h1>
+  <table>
+    <tr>
+      <td><select><option>A</option><option selected>B</option><option>C</option></td>
+      <td><select><option>Combo option 1</option><option>Combo option 2</option><option>Combo option 3</option></td>
+    </tr>
+  </table>
+
+  <h1>Text areas</h1>
+  <table>
+    <tr>
+      <td><input value="Entry"></td>
+      <td><input value="Disabled" disabled></td>
+      <td><input value="Read only" readonly></td>
+    </tr>
+    <tr>
+      <td><input type="password" value="Password"></td>
+      <td><input type="password" value="Disabled" disabled></td>
+      <td><input type="password" value="Read only" readonly></td>
+    </tr>
+    <tr>
+      <td><textarea rows="1">Single row text area</textarea></td>
+      <td><textarea rows="1" disabled>Disabled</textarea></td>
+      <td><textarea rows="1" readonly>Read only</textarea></td>
+    </tr>
+    <tr>
+      <td><textarea rows="5">No scrollbars initially</textarea></td>
+      <td><textarea rows="5">With vertical scrollbar
+
+
+
+
+
+
+      </textarea></td>
+      <td><textarea rows="5" style='white-space: nowrap'>With horizontal scrollbars..................</textarea></td>
+    </tr>
+  </table>
+
+  <h1>Search field</h1>
+  <table>
+    <tr>
+      <td><input type="search" results value="Small" style="font-size: 8px;"></td>
+      <td><input type="search" results value="Big" style="font-size: 32px;"></td>
+    </tr>
+  </table>
+
+  <h1>Spin buttons</h1>
+  <table>
+    <tr>
+      <td><input type="number" value="Small" style="font-size: 8px;"></td>
+      <td><input type="number" value="Big" style="font-size: 32px;"></td>
+    </tr>
+  </table>
+
+  <h1>Option lists</h1>
+  <table>
+    <tr>
+      <td>
+        <select size="5">
+          <option>Option 1</option><option>Option 2</option><option>Option 3</option><option selected>Option 4</option><option>Option 5</option>
+        </select>
+      </td>
+      <td>
+        <select size="3">
+          <option>Option 1</option><option>Option 2</option><option>Option 3</option><option selected>Option 4</option><option>Option 5</option>
+        </select>
+      </td>
+      <td>
+        <select size="3">
+          <option disabled>Option 1</option><option disabled>Option 2</option><option>Option 3</option><option selected>Option 4</option><option>Option 5</option>
+        </select>
+      </td>
+      <td>
+        <select size="3" disabled>
+          <option>Option 1</option><option>Option 2</option><option>Option 3</option><option selected>Option 4</option><option>Option 5</option>
+        </select>
+      </td>
+    </tr>
+  </table>
+
+  <h1>Progress bars</h1>
+  <table>
+    <tr>
+      <td><progress value="50" max="100"></progress></td>
+      <td><progress indeterminate="true"></progress></td>
+      <td><progress value="50" max="100" style="width: 150px; height: 16px;"></progress></td>
+      <td><progress indeterminate="true" style="width: 150px; height: 16px;"></progress></td>
+    </tr>
+  </table>
+
+  <h1>Range selector</h1>
+  <table>
+    <tr>
+      <td><input type="range" value="50" max="100"></input></td>
+      <td><input type="range" value="50" max="100" style="-webkit-appearance: slider-vertical"></input></td>
+      <td><input type="range" value="50" max="100" disabled></input></td>
+      <td><input type="range" value="50" max="100" style="-webkit-appearance: slider-vertical" disabled></input></td>
+    </tr>
+  </table>
+
+  <h1>Iframe scrollbars</h1>
+  <iframe width="200" height="100" scrolling="yes" src="data:text/html,
+      <html>
+        <body>
+          Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
+          Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure
+          dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
+          proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
+        </body>
+      </html>">
+  </iframe>
+
+  <h1>Media controls</h1>
+  <video controls></video>
+  </body>
+</html>
index a36d539..e8b100c 100644 (file)
@@ -1,3 +1,96 @@
+2016-04-07  Carlos Garcia Campos  <cgarcia@igalia.com>
+
+        [GTK] Rework the theming code for GTK+ 3.20
+        https://bugs.webkit.org/show_bug.cgi?id=156333
+
+        Reviewed by Michael Catanzaro.
+
+        During the 3.19 GTK+ release cycle, the GTK+ css system was reworked, making themes and programs rendering
+        themed widgets, incompatible with the new system. We were trying to fix our rendering every time GTK+ broke
+        something, but we were just changing whatever it was needed to make our rendering look like current GTK+ with
+        the default theme Adwaita. This means that our rendering will be broken for other themes or that changes in
+        Adwaita can break our rendering. This solution was good enough to ensure WebKitGTK+ 2.12 looked good with GTK+
+        3.20, but it doesn't work in the long term. We need to ensure that our theming code honors the new GTK+ CSS
+        properties (max-width, min-width, margin, padding, border, ...) in all the cases, not only the cases where
+        Adwaita uses them like we currently do.
+        This patch splits all rendering methods to keep the current code for previous GTK+ versions and adds new code
+        for GTK+ >= 3.20 using the new RenderThemeGadget classes. This makes the code easier to read, since there aren't
+        ifdef blocks in the functions, and we ensure we don't break previous rendering.
+
+        * PlatformGTK.cmake: Add new files to compilation.
+        * html/shadow/SpinButtonElement.cpp:
+        (WebCore::SpinButtonElement::defaultEventHandler): Check the button layout used by the theme to decide the
+        current buttons state.
+        * platform/gtk/RenderThemeGadget.cpp: Added.
+        (WebCore::RenderThemeGadget::create):
+        (WebCore::createStyleContext):
+        (WebCore::appendElementToPath):
+        (WebCore::RenderThemeGadget::RenderThemeGadget):
+        (WebCore::RenderThemeGadget::~RenderThemeGadget):
+        (WebCore::RenderThemeGadget::marginBox):
+        (WebCore::RenderThemeGadget::borderBox):
+        (WebCore::RenderThemeGadget::paddingBox):
+        (WebCore::RenderThemeGadget::contentsBox):
+        (WebCore::RenderThemeGadget::color):
+        (WebCore::RenderThemeGadget::backgroundColor):
+        (WebCore::RenderThemeGadget::minimumSize):
+        (WebCore::RenderThemeGadget::preferredSize):
+        (WebCore::RenderThemeGadget::render):
+        (WebCore::RenderThemeGadget::renderFocus):
+        (WebCore::RenderThemeBoxGadget::RenderThemeBoxGadget):
+        (WebCore::RenderThemeTextFieldGadget::RenderThemeTextFieldGadget):
+        (WebCore::RenderThemeTextFieldGadget::minimumSize):
+        (WebCore::RenderThemeToggleGadget::RenderThemeToggleGadget):
+        (WebCore::RenderThemeToggleGadget::render):
+        (WebCore::RenderThemeArrowGadget::RenderThemeArrowGadget):
+        (WebCore::RenderThemeArrowGadget::render):
+        (WebCore::RenderThemeIconGadget::RenderThemeIconGadget):
+        (WebCore::RenderThemeIconGadget::gtkIconSizeForPixelSize):
+        (WebCore::RenderThemeIconGadget::render):
+        (WebCore::RenderThemeIconGadget::minimumSize):
+        * platform/gtk/RenderThemeGadget.h: Added.
+        (WebCore::RenderThemeGadget::context):
+        * rendering/RenderTheme.h:
+        (WebCore::RenderTheme::innerSpinButtonLayout): Added this method to allow themes use a different layout for the
+        buttons.
+        * rendering/RenderThemeGtk.cpp:
+        (WebCore::themeChangedCallback): Just moved this code to a common place.
+        (WebCore::RenderThemeGtk::RenderThemeGtk): Initialize the theme monitor in the constructor.
+        (WebCore::createStyleContext): Remove the render parts that are specific to GTK+ 3.20.
+        (WebCore::RenderThemeGtk::adjustRepaintRect): Moved inside a GTK+ < 3.20 ifdef block.
+        (WebCore::themePartStateFlags): Helper function to get the GtkStateFlags of a theme part for a given RenderObject.
+        (WebCore::shrinkToMinimumSizeAndCenterRectangle): Move this common code to a helper function.
+        (WebCore::setToggleSize):
+        (WebCore::paintToggle):
+        (WebCore::RenderThemeGtk::paintButton):
+        (WebCore::RenderThemeGtk::popupInternalPaddingBox):
+        (WebCore::RenderThemeGtk::paintMenuList):
+        (WebCore::RenderThemeGtk::adjustTextFieldStyle): For GTK+ 3.20 we need to ensure a minimum size for spin buttons,
+        so if the text field is for a spin button, we adjust the desired size here.
+        (WebCore::RenderThemeGtk::paintTextField): In GTK+ 3.20 the CSS gadgets used to render spin buttons are
+        different, so we check here if this is the entry of a spin button to use the right gadgets.
+        (WebCore::adjustSearchFieldIconStyle):
+        (WebCore::RenderThemeGtk::paintTextArea):
+        (WebCore::RenderThemeGtk::adjustSearchFieldResultsButtonStyle):
+        (WebCore::RenderThemeGtk::paintSearchFieldResultsButton):
+        (WebCore::RenderThemeGtk::adjustSearchFieldResultsDecorationPartStyle):
+        (WebCore::RenderThemeGtk::adjustSearchFieldCancelButtonStyle):
+        (WebCore::paintSearchFieldIcon):
+        (WebCore::RenderThemeGtk::paintSearchFieldResultsDecorationPart):
+        (WebCore::RenderThemeGtk::paintSearchFieldCancelButton):
+        (WebCore::centerRectVerticallyInParentInputElement): Moved inside a GTK+ < 3.20 ifdef block.
+        (WebCore::RenderThemeGtk::paintSliderTrack):
+        (WebCore::RenderThemeGtk::adjustSliderThumbSize):
+        (WebCore::RenderThemeGtk::paintSliderThumb):
+        (WebCore::RenderThemeGtk::progressBarRectForBounds): Ensure a minimum size of progress bars in GTK+ 3.20.
+        (WebCore::RenderThemeGtk::paintProgressBar):
+        (WebCore::RenderThemeGtk::innerSpinButtonLayout): Use an horizontal layout for spin buttons.
+        (WebCore::RenderThemeGtk::adjustInnerSpinButtonStyle):
+        (WebCore::RenderThemeGtk::paintInnerSpinButton):
+        (WebCore::styleColor):
+        (WebCore::RenderThemeGtk::paintMediaButton):
+        * rendering/RenderThemeGtk.h:
+
 2016-04-11  Antti Koivisto  <antti@apple.com>
 
         Implement functional :host() pseudo class
index f8d515e..260164f 100644 (file)
@@ -221,6 +221,7 @@ list(APPEND WebCorePlatformGTK_SOURCES
     platform/gtk/PlatformMouseEventGtk.cpp
     platform/gtk/PlatformScreenGtk.cpp
     platform/gtk/PlatformWheelEventGtk.cpp
+    platform/gtk/RenderThemeGadget.cpp
     platform/gtk/ScrollbarThemeGtk.cpp
     platform/gtk/SoundGtk.cpp
     platform/gtk/WidgetGtk.cpp
index b74626f..f8277a0 100644 (file)
@@ -35,6 +35,7 @@
 #include "MouseEvent.h"
 #include "Page.h"
 #include "RenderBox.h"
+#include "RenderTheme.h"
 #include "ScrollbarTheme.h"
 #include "WheelEvent.h"
 #include <wtf/Ref.h>
@@ -122,7 +123,17 @@ void SpinButtonElement::defaultEventHandler(Event* event)
                 }
             }
             UpDownState oldUpDownState = m_upDownState;
-            m_upDownState = local.y() < box->height() / 2 ? Up : Down;
+            switch (renderer()->theme().innerSpinButtonLayout(*renderer())) {
+            case RenderTheme::InnerSpinButtonLayout::Vertical:
+                m_upDownState = local.y() < box->height() / 2 ? Up : Down;
+                break;
+            case RenderTheme::InnerSpinButtonLayout::HorizontalUpLeft:
+                m_upDownState = local.x() < box->width() / 2 ? Up : Down;
+                break;
+            case RenderTheme::InnerSpinButtonLayout::HorizontalUpRight:
+                m_upDownState = local.x() > box->width() / 2 ? Up : Down;
+                break;
+            }
             if (m_upDownState != oldUpDownState)
                 renderer()->repaint();
         } else {
diff --git a/Source/WebCore/platform/gtk/RenderThemeGadget.cpp b/Source/WebCore/platform/gtk/RenderThemeGadget.cpp
new file mode 100644 (file)
index 0000000..8d5e398
--- /dev/null
@@ -0,0 +1,327 @@
+/*
+ * Copyright (C) 2016 Igalia S.L.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "RenderThemeGadget.h"
+
+#if GTK_CHECK_VERSION(3, 20, 0)
+
+#include "FloatRect.h"
+#include "GRefPtrGtk.h"
+
+namespace WebCore {
+
+std::unique_ptr<RenderThemeGadget> RenderThemeGadget::create(const RenderThemeGadget::Info& info, RenderThemeGadget* parent, const Vector<RenderThemeGadget::Info> siblings, unsigned position)
+{
+    switch (info.type) {
+    case RenderThemeGadget::Type::Generic:
+        return std::make_unique<RenderThemeGadget>(info, parent, siblings, position);
+    case RenderThemeGadget::Type::TextField:
+        return std::make_unique<RenderThemeTextFieldGadget>(info, parent, siblings, position);
+    case RenderThemeGadget::Type::Radio:
+    case RenderThemeGadget::Type::Check:
+        return std::make_unique<RenderThemeToggleGadget>(info, parent, siblings, position);
+    case RenderThemeGadget::Type::Arrow:
+        return std::make_unique<RenderThemeArrowGadget>(info, parent, siblings, position);
+    case RenderThemeGadget::Type::Icon:
+        return std::make_unique<RenderThemeIconGadget>(info, parent, siblings, position);
+    }
+
+    ASSERT_NOT_REACHED();
+    return nullptr;
+}
+
+static GRefPtr<GtkStyleContext> createStyleContext(GtkWidgetPath* path, GtkStyleContext* parent)
+{
+    GRefPtr<GtkStyleContext> context = adoptGRef(gtk_style_context_new());
+    gtk_style_context_set_path(context.get(), path);
+    gtk_style_context_set_parent(context.get(), parent);
+    // Unfortunately, we have to explicitly set the state again here for it to take effect.
+    gtk_style_context_set_state(context.get(), gtk_widget_path_iter_get_state(path, -1));
+    return context;
+}
+
+static void appendElementToPath(GtkWidgetPath* path, const RenderThemeGadget::Info& info)
+{
+    gtk_widget_path_append_type(path, G_TYPE_NONE);
+    gtk_widget_path_iter_set_object_name(path, -1, info.name);
+    for (const auto* className : info.classList)
+        gtk_widget_path_iter_add_class(path, -1, className);
+    gtk_widget_path_iter_set_state(path, -1, static_cast<GtkStateFlags>(gtk_widget_path_iter_get_state(path, -1) | info.state));
+}
+
+RenderThemeGadget::RenderThemeGadget(const RenderThemeGadget::Info& info, RenderThemeGadget* parent, const Vector<RenderThemeGadget::Info> siblings, unsigned position)
+{
+    GRefPtr<GtkWidgetPath> path = parent ? adoptGRef(gtk_widget_path_copy(gtk_style_context_get_path(parent->context()))) : adoptGRef(gtk_widget_path_new());
+    if (!siblings.isEmpty()) {
+        GRefPtr<GtkWidgetPath> siblingsPath = adoptGRef(gtk_widget_path_new());
+        for (const auto& siblingInfo : siblings)
+            appendElementToPath(siblingsPath.get(), siblingInfo);
+        gtk_widget_path_append_with_siblings(path.get(), siblingsPath.get(), position);
+    } else
+        appendElementToPath(path.get(), info);
+    m_context = createStyleContext(path.get(), parent ? parent->context() : nullptr);
+}
+
+RenderThemeGadget::~RenderThemeGadget()
+{
+}
+
+GtkBorder RenderThemeGadget::marginBox() const
+{
+    GtkBorder returnValue;
+    gtk_style_context_get_margin(m_context.get(), gtk_style_context_get_state(m_context.get()), &returnValue);
+    return returnValue;
+}
+
+GtkBorder RenderThemeGadget::borderBox() const
+{
+    GtkBorder returnValue;
+    gtk_style_context_get_border(m_context.get(), gtk_style_context_get_state(m_context.get()), &returnValue);
+    return returnValue;
+}
+
+GtkBorder RenderThemeGadget::paddingBox() const
+{
+    GtkBorder returnValue;
+    gtk_style_context_get_padding(m_context.get(), gtk_style_context_get_state(m_context.get()), &returnValue);
+    return returnValue;
+}
+
+GtkBorder RenderThemeGadget::contentsBox() const
+{
+    auto margin = marginBox();
+    auto border = borderBox();
+    auto padding = paddingBox();
+    padding.left += margin.left + border.left;
+    padding.right += margin.right + border.right;
+    padding.top += margin.top + border.top;
+    padding.bottom += margin.bottom + border.bottom;
+    return padding;
+}
+
+Color RenderThemeGadget::color() const
+{
+    GdkRGBA returnValue;
+    gtk_style_context_get_color(m_context.get(), gtk_style_context_get_state(m_context.get()), &returnValue);
+    return returnValue;
+}
+
+Color RenderThemeGadget::backgroundColor() const
+{
+    GdkRGBA returnValue;
+    gtk_style_context_get_background_color(m_context.get(), gtk_style_context_get_state(m_context.get()), &returnValue);
+    return returnValue;
+}
+
+IntSize RenderThemeGadget::minimumSize() const
+{
+    int width, height;
+    gtk_style_context_get(m_context.get(), gtk_style_context_get_state(m_context.get()), "min-width", &width, "min-height", &height, nullptr);
+    return IntSize(width, height);
+}
+
+IntSize RenderThemeGadget::preferredSize() const
+{
+    auto margin = marginBox();
+    auto border = borderBox();
+    auto padding = paddingBox();
+    auto minSize = minimumSize();
+    minSize.expand(margin.left + margin.right + border.left + border.right + padding.left + padding.right,
+        margin.top + margin.bottom + border.top + border.bottom + padding.top + padding.bottom);
+    return minSize;
+}
+
+bool RenderThemeGadget::render(cairo_t* cr, const FloatRect& paintRect, FloatRect* contentsRect)
+{
+    FloatRect rect = paintRect;
+
+    auto margin = marginBox();
+    rect.move(margin.left, margin.top);
+    rect.contract(margin.left + margin.right, margin.top + margin.bottom);
+
+    auto minSize = minimumSize();
+    rect.setWidth(std::max<float>(rect.width(), minSize.width()));
+    rect.setHeight(std::max<float>(rect.height(), minSize.height()));
+
+    gtk_render_background(m_context.get(), cr, rect.x(), rect.y(), rect.width(), rect.height());
+    gtk_render_frame(m_context.get(), cr, rect.x(), rect.y(), rect.width(), rect.height());
+
+    if (contentsRect) {
+        auto border = borderBox();
+        auto padding = paddingBox();
+        *contentsRect = rect;
+        contentsRect->move(border.left + padding.left, border.top + padding.top);
+        contentsRect->contract(border.left + border.right + padding.left + padding.right, border.top + border.bottom + padding.top + padding.bottom);
+    }
+
+    return true;
+}
+
+void RenderThemeGadget::renderFocus(cairo_t* cr, const FloatRect& focusRect)
+{
+    FloatRect rect = focusRect;
+    auto margin = marginBox();
+    rect.move(margin.left, margin.top);
+    rect.contract(margin.left + margin.right, margin.top + margin.bottom);
+    gtk_render_focus(m_context.get(), cr, rect.x(), rect.y(), rect.width(), rect.height());
+}
+
+RenderThemeBoxGadget::RenderThemeBoxGadget(const RenderThemeGadget::Info& info, const Vector<RenderThemeGadget::Info> children, RenderThemeGadget* parent)
+    : RenderThemeGadget(info, parent, Vector<RenderThemeGadget::Info>(), 0)
+{
+    m_children.reserveCapacity(children.size());
+    unsigned index = 0;
+    for (const auto& childInfo : children)
+        m_children.uncheckedAppend(RenderThemeGadget::create(childInfo, this, children, index++));
+}
+
+IntSize RenderThemeBoxGadget::preferredSize() const
+{
+    IntSize minSize = RenderThemeGadget::preferredSize();
+    for (const auto& child : m_children)
+        minSize += child->preferredSize();
+    return minSize;
+}
+
+RenderThemeTextFieldGadget::RenderThemeTextFieldGadget(const RenderThemeGadget::Info& info, RenderThemeGadget* parent, const Vector<RenderThemeGadget::Info> siblings, unsigned position)
+    : RenderThemeGadget(info, parent, siblings, position)
+{
+}
+
+IntSize RenderThemeTextFieldGadget::minimumSize() const
+{
+    // We allow text fields smaller than the min size set on themes.
+    return IntSize();
+}
+
+RenderThemeToggleGadget::RenderThemeToggleGadget(const RenderThemeGadget::Info& info, RenderThemeGadget* parent, const Vector<RenderThemeGadget::Info> siblings, unsigned position)
+    : RenderThemeGadget(info, parent, siblings, position)
+    , m_type(info.type)
+{
+    ASSERT(m_type == RenderThemeGadget::Type::Radio || m_type == RenderThemeGadget::Type::Check);
+}
+
+bool RenderThemeToggleGadget::render(cairo_t* cr, const FloatRect& paintRect, FloatRect*)
+{
+    FloatRect contentsRect;
+    RenderThemeGadget::render(cr, paintRect, &contentsRect);
+    if (m_type == RenderThemeGadget::Type::Radio)
+        gtk_render_option(m_context.get(), cr, contentsRect.x(), contentsRect.y(), contentsRect.width(), contentsRect.height());
+    else
+        gtk_render_check(m_context.get(), cr, contentsRect.x(), contentsRect.y(), contentsRect.width(), contentsRect.height());
+    return true;
+}
+
+RenderThemeArrowGadget::RenderThemeArrowGadget(const RenderThemeGadget::Info& info, RenderThemeGadget* parent, const Vector<RenderThemeGadget::Info> siblings, unsigned position)
+    : RenderThemeGadget(info, parent, siblings, position)
+{
+}
+
+bool RenderThemeArrowGadget::render(cairo_t* cr, const FloatRect& paintRect, FloatRect*)
+{
+    FloatRect contentsRect;
+    RenderThemeGadget::render(cr, paintRect, &contentsRect);
+    IntSize minSize = minimumSize();
+    int arrowSize = std::min(minSize.width(), minSize.height());
+    FloatPoint arrowPosition(contentsRect.x(), contentsRect.y() + (contentsRect.height() - arrowSize) / 2);
+    if (gtk_style_context_get_state(m_context.get()) & GTK_STATE_FLAG_DIR_LTR)
+        arrowPosition.move(contentsRect.width() - arrowSize, 0);
+    gtk_render_arrow(m_context.get(), cr, G_PI / 2, arrowPosition.x(), arrowPosition.y(), arrowSize);
+    return true;
+}
+
+RenderThemeIconGadget::RenderThemeIconGadget(const RenderThemeGadget::Info& info, RenderThemeGadget* parent, const Vector<RenderThemeGadget::Info> siblings, unsigned position)
+    : RenderThemeGadget(info, parent, siblings, position)
+{
+}
+
+GtkIconSize RenderThemeIconGadget::gtkIconSizeForPixelSize(unsigned pixelSize) const
+{
+    if (pixelSize < IconSizeGtk::SmallToolbar)
+        return GTK_ICON_SIZE_MENU;
+    if (pixelSize >= IconSizeGtk::SmallToolbar && pixelSize < IconSizeGtk::Button)
+        return GTK_ICON_SIZE_SMALL_TOOLBAR;
+    if (pixelSize >= IconSizeGtk::Button && pixelSize < IconSizeGtk::LargeToolbar)
+        return GTK_ICON_SIZE_BUTTON;
+    if (pixelSize >= IconSizeGtk::LargeToolbar && pixelSize < IconSizeGtk::DragAndDrop)
+        return GTK_ICON_SIZE_LARGE_TOOLBAR;
+    if (pixelSize >= IconSizeGtk::DragAndDrop && pixelSize < IconSizeGtk::Dialog)
+        return GTK_ICON_SIZE_DND;
+
+    return GTK_ICON_SIZE_DIALOG;
+}
+
+bool RenderThemeIconGadget::render(cairo_t* cr, const FloatRect& paintRect, FloatRect*)
+{
+    ASSERT(!m_iconName.isNull());
+    GRefPtr<GIcon> icon = adoptGRef(g_themed_icon_new(m_iconName.data()));
+    unsigned lookupFlags = GTK_ICON_LOOKUP_USE_BUILTIN | GTK_ICON_LOOKUP_FORCE_SIZE | GTK_ICON_LOOKUP_FORCE_SVG;
+    GtkTextDirection direction = gtk_style_context_get_direction(m_context.get());
+    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;
+    int iconWidth, iconHeight;
+    if (!gtk_icon_size_lookup(gtkIconSizeForPixelSize(m_iconSize), &iconWidth, &iconHeight))
+        iconWidth = iconHeight = m_iconSize;
+    GRefPtr<GtkIconInfo> iconInfo = adoptGRef(gtk_icon_theme_lookup_by_gicon(gtk_icon_theme_get_default(), icon.get(),
+        std::min(iconWidth, iconHeight), static_cast<GtkIconLookupFlags>(lookupFlags)));
+    if (!iconInfo)
+        return false;
+
+    GRefPtr<GdkPixbuf> iconPixbuf = adoptGRef(gtk_icon_info_load_symbolic_for_context(iconInfo.get(), m_context.get(), nullptr, nullptr));
+    if (!iconPixbuf)
+        return false;
+
+    FloatSize pixbufSize(gdk_pixbuf_get_width(iconPixbuf.get()), gdk_pixbuf_get_height(iconPixbuf.get()));
+    FloatRect contentsRect;
+    RenderThemeGadget::render(cr, paintRect, &contentsRect);
+    if (pixbufSize.width() > contentsRect.width() || pixbufSize.height() > contentsRect.height()) {
+        iconWidth = iconHeight = std::min(contentsRect.width(), contentsRect.height());
+        pixbufSize = FloatSize(iconWidth, iconHeight);
+        iconPixbuf = adoptGRef(gdk_pixbuf_scale_simple(iconPixbuf.get(), pixbufSize.width(), pixbufSize.height(), GDK_INTERP_BILINEAR));
+    }
+
+    gtk_render_icon(m_context.get(), cr, iconPixbuf.get(), contentsRect.x() + (contentsRect.width() - pixbufSize.width()) / 2,
+        contentsRect.y() + (contentsRect.height() - pixbufSize.height()) / 2);
+    return true;
+}
+
+IntSize RenderThemeIconGadget::minimumSize() const
+{
+    if (m_iconSize < IconSizeGtk::Menu)
+        return IntSize(m_iconSize, m_iconSize);
+
+    int iconWidth, iconHeight;
+    if (gtk_icon_size_lookup(gtkIconSizeForPixelSize(m_iconSize), &iconWidth, &iconHeight))
+        return IntSize(iconWidth, iconHeight);
+
+    return IntSize(m_iconSize, m_iconSize);
+}
+
+} // namespace WebCore
+
+#endif // GTK_CHECK_VERSION(3, 20, 0)
diff --git a/Source/WebCore/platform/gtk/RenderThemeGadget.h b/Source/WebCore/platform/gtk/RenderThemeGadget.h
new file mode 100644 (file)
index 0000000..19d2ddc
--- /dev/null
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2016 Igalia S.L.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <gtk/gtk.h>
+
+#if GTK_CHECK_VERSION(3, 20, 0)
+
+#include "Color.h"
+#include "IntSize.h"
+#include <wtf/Vector.h>
+#include <wtf/glib/GRefPtr.h>
+#include <wtf/text/CString.h>
+
+namespace WebCore {
+class FloatRect;
+
+class RenderThemeGadget {
+    WTF_MAKE_FAST_ALLOCATED;
+    WTF_MAKE_NONCOPYABLE(RenderThemeGadget);
+public:
+    enum class Type {
+        Generic,
+        TextField,
+        Check,
+        Radio,
+        Arrow,
+        Icon
+    };
+
+    struct Info {
+        Type type;
+        const char* name;
+        GtkStateFlags state;
+        Vector<const char*> classList;
+    };
+
+    static std::unique_ptr<RenderThemeGadget> create(const Info&, RenderThemeGadget* parent = nullptr, const Vector<RenderThemeGadget::Info> siblings = Vector<RenderThemeGadget::Info>(), unsigned position = 0);
+    RenderThemeGadget(const Info&, RenderThemeGadget* parent, const Vector<RenderThemeGadget::Info> siblings, unsigned position);
+    virtual ~RenderThemeGadget();
+
+    virtual IntSize preferredSize() const;
+    virtual bool render(cairo_t*, const FloatRect&, FloatRect* = nullptr);
+    virtual IntSize minimumSize() const;
+
+    void renderFocus(cairo_t*, const FloatRect&);
+
+    GtkBorder contentsBox() const;
+    Color color() const;
+    Color backgroundColor() const;
+
+protected:
+    GtkStyleContext* context() const { return m_context.get(); }
+    GtkBorder marginBox() const;
+    GtkBorder borderBox() const;
+    GtkBorder paddingBox() const;
+
+    GRefPtr<GtkStyleContext> m_context;
+};
+
+class RenderThemeBoxGadget final : public RenderThemeGadget {
+public:
+    RenderThemeBoxGadget(const RenderThemeGadget::Info&, const Vector<RenderThemeGadget::Info> children, RenderThemeGadget* parent = nullptr);
+
+    IntSize preferredSize() const override;
+
+    RenderThemeGadget* child(unsigned index) const { return m_children[index].get(); }
+
+private:
+    Vector<std::unique_ptr<RenderThemeGadget>> m_children;
+};
+
+class RenderThemeTextFieldGadget final : public RenderThemeGadget {
+public:
+    RenderThemeTextFieldGadget(const Info&, RenderThemeGadget* parent, const Vector<RenderThemeGadget::Info> siblings, unsigned position);
+
+    IntSize minimumSize() const override;
+};
+
+class RenderThemeToggleGadget final : public RenderThemeGadget {
+public:
+    RenderThemeToggleGadget(const Info&, RenderThemeGadget* parent, const Vector<RenderThemeGadget::Info> siblings, unsigned position);
+
+    bool render(cairo_t*, const FloatRect&, FloatRect* = nullptr) override;
+
+private:
+    RenderThemeGadget::Type m_type;
+};
+
+class RenderThemeArrowGadget final : public RenderThemeGadget {
+public:
+    RenderThemeArrowGadget(const Info&, RenderThemeGadget* parent, const Vector<RenderThemeGadget::Info> siblings, unsigned position);
+
+    bool render(cairo_t*, const FloatRect&, FloatRect* = nullptr) override;
+};
+
+class RenderThemeIconGadget final : public RenderThemeGadget {
+public:
+    RenderThemeIconGadget(const Info&, RenderThemeGadget* parent, const Vector<RenderThemeGadget::Info> siblings, unsigned position);
+
+    bool render(cairo_t*, const FloatRect&, FloatRect* = nullptr) override;
+    IntSize minimumSize() const override;
+
+    void setIconName(const char* iconName) { m_iconName = iconName; }
+    void setIconSize(unsigned iconSize) { m_iconSize = iconSize; }
+
+    // Defined in GTK+ (gtk/gtkiconfactory.c).
+    enum IconSizeGtk {
+        Menu = 16,
+        SmallToolbar = 18,
+        Button = 20,
+        LargeToolbar = 24,
+        DragAndDrop = 32,
+        Dialog = 48
+    };
+
+private:
+    GtkIconSize gtkIconSizeForPixelSize(unsigned) const;
+
+    CString m_iconName;
+    unsigned m_iconSize { 0 };
+};
+
+} // namespace WebCore
+
+#endif // GTK_CHECK_VERSION(3, 20, 0)
index 5d3acc2..0c14e35 100644 (file)
@@ -253,6 +253,9 @@ public:
     virtual int attachmentBaseline(const RenderAttachment&) const { return -1; }
 #endif
 
+    enum class InnerSpinButtonLayout { Vertical, HorizontalUpLeft, HorizontalUpRight };
+    virtual InnerSpinButtonLayout innerSpinButtonLayout(const RenderObject&) const { return InnerSpinButtonLayout::Vertical; }
+
 protected:
     virtual FontCascadeDescription& cachedSystemFontDescription(CSSValueID systemFontID) const;
     virtual void updateCachedSystemFontDescription(CSSValueID systemFontID, FontCascadeDescription&) const = 0;
index 7d19ff4..64e4945 100644 (file)
@@ -46,6 +46,7 @@
 #include "RenderBox.h"
 #include "RenderObject.h"
 #include "RenderProgress.h"
+#include "RenderThemeGadget.h"
 #include "ScrollbarThemeGtk.h"
 #include "StringTruncator.h"
 #include "TimeRanges.h"
@@ -133,10 +134,21 @@ 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;
+static void themeChangedCallback()
+{
+    Page::updateStyleForAllPagesAfterGlobalChangeInEnvironment();
+}
+
+RenderThemeGtk::RenderThemeGtk()
+{
+    static bool themeMonitorInitialized = false;
+    if (!themeMonitorInitialized) {
+        GtkSettings* settings = gtk_settings_get_default();
+        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;
+    }
+}
 
 enum RenderThemePart {
     Entry,
@@ -145,17 +157,13 @@ enum RenderThemePart {
     EntryIconRight,
     Button,
     CheckButton,
-    CheckButtonCheck,
     RadioButton,
-    RadioButtonRadio,
     ComboBox,
     ComboBoxButton,
     ComboBoxArrow,
     Scale,
-    ScaleContents,
     ScaleTrough,
     ScaleSlider,
-    ScaleHighlight,
     ProgressBar,
     ProgressBarTrough,
     ProgressBarProgress,
@@ -168,220 +176,102 @@ enum RenderThemePart {
 #endif
 };
 
-static void gtkStyleChangedCallback(GObject*, GParamSpec*)
-{
-    Page::updateStyleForAllPagesAfterGlobalChangeInEnvironment();
-}
+#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<GtkStyleContext> createStyleContext(RenderThemePart themePart, GtkStyleContext* parent = nullptr)
 {
-    static bool initialized = false;
-    if (!initialized) {
-        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;
-    }
-
     GRefPtr<GtkWidgetPath> path = adoptGRef(parent ? gtk_widget_path_copy(gtk_style_context_get_path(parent)) : gtk_widget_path_new());
 
     switch (themePart) {
     case Entry:
         gtk_widget_path_append_type(path.get(), GTK_TYPE_ENTRY);
-#if GTK_CHECK_VERSION(3, 19, 2)
-        gtk_widget_path_iter_set_object_name(path.get(), -1, "entry");
-#else
         gtk_widget_path_iter_add_class(path.get(), -1, GTK_STYLE_CLASS_ENTRY);
-#endif
-        break;
-    case EntrySelection:
-        gtk_widget_path_append_type(path.get(), GTK_TYPE_ENTRY);
-#if GTK_CHECK_VERSION(3, 19, 2)
-        gtk_widget_path_iter_set_object_name(path.get(), -1, "selection");
-#else
-        gtk_widget_path_iter_add_class(path.get(), -1, GTK_STYLE_CLASS_ENTRY);
-#endif
         break;
     case EntryIconLeft:
     case EntryIconRight:
         gtk_widget_path_append_type(path.get(), GTK_TYPE_ENTRY);
-#if GTK_CHECK_VERSION(3, 19, 2)
-        gtk_widget_path_iter_set_object_name(path.get(), -1, "image");
-        gtk_widget_path_iter_add_class(path.get(), -1, themePart == EntryIconLeft ? "left" : "right");
-#endif
         break;
     case Button:
         gtk_widget_path_append_type(path.get(), GTK_TYPE_BUTTON);
-#if GTK_CHECK_VERSION(3, 19, 2)
-        gtk_widget_path_iter_set_object_name(path.get(), -1, "button");
-#else
         gtk_widget_path_iter_add_class(path.get(), -1, GTK_STYLE_CLASS_BUTTON);
-#endif
         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);
-#if GTK_CHECK_VERSION(3, 19, 2)
-        gtk_widget_path_iter_set_object_name(path.get(), -1, "checkbutton");
-#else
         gtk_widget_path_iter_add_class(path.get(), -1, GTK_STYLE_CLASS_CHECK);
-#endif
-        break;
-    case CheckButtonCheck:
-        gtk_widget_path_append_type(path.get(), GTK_TYPE_CHECK_BUTTON);
-#if GTK_CHECK_VERSION(3, 19, 2)
-        gtk_widget_path_iter_set_object_name(path.get(), -1, "check");
-#else
-        gtk_widget_path_iter_add_class(path.get(), -1, GTK_STYLE_CLASS_CHECK);
-#endif
         break;
     case RadioButton:
         gtk_widget_path_append_type(path.get(), GTK_TYPE_RADIO_BUTTON);
-#if GTK_CHECK_VERSION(3, 19, 2)
-        gtk_widget_path_iter_set_object_name(path.get(), -1, "radiobutton");
-#else
         gtk_widget_path_iter_add_class(path.get(), -1, GTK_STYLE_CLASS_RADIO);
-#endif
-        break;
-    case RadioButtonRadio:
-        gtk_widget_path_append_type(path.get(), GTK_TYPE_RADIO_BUTTON);
-#if GTK_CHECK_VERSION(3, 19, 2)
-        gtk_widget_path_iter_set_object_name(path.get(), -1, "radio");
-#else
-        gtk_widget_path_iter_add_class(path.get(), -1, GTK_STYLE_CLASS_RADIO);
-#endif
         break;
     case ComboBox:
         gtk_widget_path_append_type(path.get(), GTK_TYPE_COMBO_BOX);
-#if GTK_CHECK_VERSION(3, 19, 2)
-        gtk_widget_path_iter_set_object_name(path.get(), -1, "combobox");
-#endif
         break;
     case ComboBoxButton:
         gtk_widget_path_append_type(path.get(), GTK_TYPE_BUTTON);
-#if GTK_CHECK_VERSION(3, 19, 2)
-        gtk_widget_path_iter_set_object_name(path.get(), -1, "button");
-#else
         gtk_widget_path_iter_add_class(path.get(), -1, GTK_STYLE_CLASS_BUTTON);
-#endif
         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);
-#if GTK_CHECK_VERSION(3, 19, 2)
-        gtk_widget_path_iter_set_object_name(path.get(), -1, "arrow");
-#else
         gtk_widget_path_iter_add_class(path.get(), -1, "arrow");
-#endif
         break;
     case Scale:
         gtk_widget_path_append_type(path.get(), GTK_TYPE_SCALE);
-#if GTK_CHECK_VERSION(3, 19, 2)
-        gtk_widget_path_iter_set_object_name(path.get(), -1, "scale");
-#else
         gtk_widget_path_iter_add_class(path.get(), -1, GTK_STYLE_CLASS_SCALE);
-#endif
-        break;
-    case ScaleContents:
-#if GTK_CHECK_VERSION(3, 19, 11)
-        gtk_widget_path_append_type(path.get(), GTK_TYPE_SCALE);
-        gtk_widget_path_iter_set_object_name(path.get(), -1, "contents");
-        gtk_widget_path_iter_add_class(path.get(), -1, GTK_STYLE_CLASS_SCALE);
-#else
-        ASSERT_NOT_REACHED();
-#endif
         break;
     case ScaleTrough:
         gtk_widget_path_append_type(path.get(), GTK_TYPE_SCALE);
-#if GTK_CHECK_VERSION(3, 19, 2)
-        gtk_widget_path_iter_set_object_name(path.get(), -1, "trough");
-#else
         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);
-#endif
         break;
     case ScaleSlider:
         gtk_widget_path_append_type(path.get(), GTK_TYPE_SCALE);
-#if GTK_CHECK_VERSION(3, 19, 2)
-        gtk_widget_path_iter_set_object_name(path.get(), -1, "slider");
-#else
         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);
-#endif
-        break;
-    case ScaleHighlight:
-#if GTK_CHECK_VERSION(3, 19, 11)
-        gtk_widget_path_append_type(path.get(), GTK_TYPE_SCALE);
-        gtk_widget_path_iter_set_object_name(path.get(), -1, "highlight");
-        gtk_widget_path_iter_add_class(path.get(), -1, GTK_STYLE_CLASS_SCALE);
-#else
-        ASSERT_NOT_REACHED();
-#endif
         break;
     case ProgressBar:
         gtk_widget_path_append_type(path.get(), GTK_TYPE_PROGRESS_BAR);
-#if GTK_CHECK_VERSION(3, 19, 2)
-        gtk_widget_path_iter_set_object_name(path.get(), -1, "progressbar");
-#else
         gtk_widget_path_iter_add_class(path.get(), -1, GTK_STYLE_CLASS_PROGRESSBAR);
-#endif
         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);
-#if GTK_CHECK_VERSION(3, 19, 2)
-        gtk_widget_path_iter_set_object_name(path.get(), -1, "trough");
-#else
-        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_TROUGH);
-#endif
         break;
     case ProgressBarProgress:
         gtk_widget_path_append_type(path.get(), GTK_TYPE_PROGRESS_BAR);
-#if GTK_CHECK_VERSION(3, 19, 2)
-        gtk_widget_path_iter_set_object_name(path.get(), -1, "progress");
-#else
         gtk_widget_path_iter_add_class(path.get(), -1, GTK_STYLE_CLASS_PROGRESSBAR);
-#endif
         break;
     case ListBox:
         gtk_widget_path_append_type(path.get(), GTK_TYPE_TREE_VIEW);
-#if GTK_CHECK_VERSION(3, 19, 2)
-        gtk_widget_path_iter_set_object_name(path.get(), -1, "treeview");
-#endif
         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);
-#if GTK_CHECK_VERSION(3, 19, 2)
-        gtk_widget_path_iter_set_object_name(path.get(), -1, "spinbutton");
-#else
         gtk_widget_path_iter_add_class(path.get(), -1, GTK_STYLE_CLASS_SPINBUTTON);
-#endif
         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);
-#if GTK_CHECK_VERSION(3, 19, 2)
-        gtk_widget_path_iter_set_object_name(path.get(), -1, "button");
-        gtk_widget_path_iter_add_class(path.get(), -1, themePart == SpinButtonUpButton ? "up" : "down");
-#else
         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);
-#endif
         break;
 #if ENABLE(VIDEO)
     case MediaButton:
         gtk_widget_path_append_type(path.get(), GTK_TYPE_IMAGE);
-#if GTK_CHECK_VERSION(3, 19, 2)
-        gtk_widget_path_iter_set_object_name(path.get(), -1, "image");
-#else
         gtk_widget_path_iter_add_class(path.get(), -1, GTK_STYLE_CLASS_IMAGE);
-#endif
         break;
 #endif // ENABLE(VIDEO)
+    default:
+        ASSERT_NOT_REACHED();
+        break;
     }
 
     GRefPtr<GtkStyleContext> context = adoptGRef(gtk_style_context_new());
@@ -409,6 +299,7 @@ static GRefPtr<GdkPixbuf> loadThemedIcon(GtkStyleContext* context, const char* i
 
     return adoptGRef(gtk_icon_info_load_symbolic_for_context(iconInfo.get(), context, nullptr, nullptr));
 }
+#endif // !GTK_CHECK_VERSION(3, 20, 0)
 
 static bool nodeHasPseudo(Node* node, const char* pseudo)
 {
@@ -471,6 +362,71 @@ int RenderThemeGtk::baselinePosition(const RenderBox& box) const
     return RenderTheme::baselinePosition(box);
 }
 
+#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) {
@@ -545,6 +501,7 @@ void RenderThemeGtk::adjustRepaintRect(const RenderObject& renderObject, FloatRe
     }
     adjustRectForFocus(context.get(), rect);
 }
+#endif // GTK_CHECK_VERSION(3, 20, 0)
 
 void RenderThemeGtk::adjustButtonStyle(StyleResolver&, RenderStyle& style, Element*) const
 {
@@ -553,6 +510,90 @@ void RenderThemeGtk::adjustButtonStyle(StyleResolver&, RenderStyle& style, Eleme
         style.setLineHeight(RenderStyle::initialLineHeight());
 }
 
+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;
+
+    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(preferredSize.width(), Fixed));
+
+    if (style.height().isAuto())
+        style.setHeight(Length(preferredSize.height(), Fixed));
+}
+
+static void paintToggle(const RenderThemeGtk* theme, RenderThemePart themePart, const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& fullRect)
+{
+    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.
@@ -563,29 +604,17 @@ static void setToggleSize(RenderThemePart themePart, RenderStyle& style)
     // Other ports hard-code this to 13. GTK+ users tend to demand the native look.
     gint indicatorSize;
     gtk_style_context_get_style(context.get(), "indicator-size", &indicatorSize, nullptr);
-    IntSize minSize(indicatorSize, indicatorSize);
-
-#if GTK_CHECK_VERSION(3, 19, 7)
-    GRefPtr<GtkStyleContext> childContext = createStyleContext(themePart == CheckButton ? CheckButtonCheck : RadioButtonRadio, context.get());
-    gint minWidth, minHeight;
-    gtk_style_context_get(childContext.get(), gtk_style_context_get_state(childContext.get()), "min-width", &minWidth, "min-height", &minHeight, nullptr);
-    if (minWidth)
-        minSize.setWidth(minWidth);
-    if (minHeight)
-        minSize.setHeight(minHeight);
-#endif
 
     if (style.width().isIntrinsicOrAuto())
-        style.setWidth(Length(minSize.width(), Fixed));
+        style.setWidth(Length(indicatorSize, Fixed));
 
     if (style.height().isAuto())
-        style.setHeight(Length(minSize.height(), Fixed));
+        style.setHeight(Length(indicatorSize, Fixed));
 }
 
 static void paintToggle(const RenderThemeGtk* theme, RenderThemePart themePart, const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& fullRect)
 {
-    GRefPtr<GtkStyleContext> parentContext = createStyleContext(themePart);
-    GRefPtr<GtkStyleContext> context = createStyleContext(themePart == CheckButton ? CheckButtonCheck : RadioButtonRadio, parentContext.get());
+    GRefPtr<GtkStyleContext> context = createStyleContext(themePart);
     gtk_style_context_set_direction(context.get(), static_cast<GtkTextDirection>(gtkTextDirection(renderObject.style().direction())));
 
     unsigned flags = 0;
@@ -609,29 +638,11 @@ static void paintToggle(const RenderThemeGtk* theme, RenderThemePart themePart,
     // 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);
-
-#if GTK_CHECK_VERSION(3, 19, 7)
-    gint minWidth, minHeight;
-    gtk_style_context_get(context.get(), gtk_style_context_get_state(context.get()), "min-width", &minWidth, "min-height", &minHeight, nullptr);
-    if (minWidth)
-        minSize.setWidth(minWidth);
-    if (minHeight)
-        minSize.setHeight(minHeight);
-#endif
-
-    IntRect rect(fullRect);
-    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.
-    }
+    shrinkToMinimumSizeAndCenterRectangle(rect, minSize);
 
     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());
@@ -650,6 +661,7 @@ static void paintToggle(const RenderThemeGtk* theme, RenderThemePart themePart,
             indicatorRect.width(), indicatorRect.height());
     }
 }
+#endif // GTK_CHECK_VERSION(3, 20, 0)
 
 void RenderThemeGtk::setCheckboxSize(RenderStyle& style) const
 {
@@ -673,6 +685,19 @@ bool RenderThemeGtk::paintRadio(const RenderObject& renderObject, const PaintInf
     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);
@@ -746,6 +771,7 @@ bool RenderThemeGtk::paintButton(const RenderObject& renderObject, const PaintIn
     renderButton(this, context.get(), renderObject, paintInfo, rect);
     return false;
 }
+#endif // GTK_CHECK_VERSION(3, 20, 0)
 
 void RenderThemeGtk::adjustMenuListStyle(StyleResolver&, RenderStyle& style, Element*) const
 {
@@ -761,6 +787,83 @@ void RenderThemeGtk::adjustMenuListButtonStyle(StyleResolver& styleResolver, Ren
     adjustMenuListStyle(styleResolver, style, e);
 }
 
+#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 (style.appearance() == NoControlPart)
+        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);
+
+    return false;
+}
+#else
 LengthBox RenderThemeGtk::popupInternalPaddingBox(const RenderStyle& style) const
 {
     if (style.appearance() == NoControlPart)
@@ -831,13 +934,8 @@ bool RenderThemeGtk::paintMenuList(const RenderObject& renderObject, const Paint
     GRefPtr<GtkStyleContext> arrowStyleContext = createStyleContext(ComboBoxArrow, buttonStyleContext.get());
     gtk_style_context_set_direction(arrowStyleContext.get(), direction);
 
-#if GTK_CHECK_VERSION(3, 19, 2)
-    // arrow-scaling style property is now deprecated and ignored.
-    gfloat arrowScaling = 1.;
-#else
     gfloat arrowScaling;
     gtk_style_context_get_style(parentStyleContext.get(), "arrow-scaling", &arrowScaling, nullptr);
-#endif
 
     IntSize arrowSize(minArrowSize, innerRect.height());
     FloatPoint arrowPosition(innerRect.location());
@@ -854,12 +952,65 @@ bool RenderThemeGtk::paintMenuList(const RenderObject& renderObject, const Paint
 
     return false;
 }
+#endif // GTK_CHECK_VERSION(3, 20, 0)
 
 bool RenderThemeGtk::paintMenuListButtonDecorations(const RenderBox& object, const PaintInfo& info, const FloatRect& rect)
 {
     return paintMenuList(object, info, rect);
 }
 
+#if GTK_CHECK_VERSION(3, 20, 0)
+void RenderThemeGtk::adjustTextFieldStyle(StyleResolver&, RenderStyle& style, Element* element) const
+{
+    if (!is<HTMLInputElement>(element) || !shouldHaveSpinButton(downcast<HTMLInputElement>(*element)))
+        return;
+
+    // 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));
+}
+
+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();
+    }
+
+    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;
+}
+#else
+void RenderThemeGtk::adjustTextFieldStyle(StyleResolver&, RenderStyle&, Element*) const
+{
+}
+
 bool RenderThemeGtk::paintTextField(const RenderObject& renderObject, const PaintInfo& paintInfo, const FloatRect& rect)
 {
     GRefPtr<GtkStyleContext> context = createStyleContext(Entry);
@@ -888,12 +1039,34 @@ bool RenderThemeGtk::paintTextField(const RenderObject& renderObject, const Pain
 
     return false;
 }
+#endif
 
-bool RenderThemeGtk::paintTextArea(const RenderObject& o, const PaintInfo& i, const FloatRect& r)
+#if GTK_CHECK_VERSION(3, 20, 0)
+static void adjustSearchFieldIconStyle(RenderThemePart themePart, RenderStyle& style)
 {
-    return paintTextField(o, i, r);
-}
+    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());
 
+    // 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;
@@ -918,16 +1091,6 @@ 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 RenderBox& o, const PaintInfo& i, const IntRect& rect)
-{
-    return paintSearchFieldResultsDecorationPart(o, i, rect);
-}
-
 static void adjustSearchFieldIconStyle(RenderThemePart themePart, RenderStyle& style)
 {
     style.resetBorder();
@@ -951,35 +1114,72 @@ static void adjustSearchFieldIconStyle(RenderThemePart themePart, RenderStyle& s
     style.setWidth(Length(width + (padding.left + padding.right), Fixed));
     style.setHeight(Length(height + (padding.top + padding.bottom), Fixed));
 }
+#endif
 
-void RenderThemeGtk::adjustSearchFieldResultsDecorationPartStyle(StyleResolver&, RenderStyle& style, Element*) const
+bool RenderThemeGtk::paintTextArea(const RenderObject& o, const PaintInfo& i, const FloatRect& r)
 {
-    adjustSearchFieldIconStyle(EntryIconLeft, style);
+    return paintTextField(o, i, r);
 }
 
-static IntRect centerRectVerticallyInParentInputElement(const RenderObject& renderObject, const IntRect& rect)
+void RenderThemeGtk::adjustSearchFieldResultsButtonStyle(StyleResolver& styleResolver, RenderStyle& style, Element* e) const
 {
-    if (!renderObject.node())
-        return IntRect();
+    adjustSearchFieldCancelButtonStyle(styleResolver, style, e);
+}
 
-    // Get the renderer of <input> element.
-    Node* input = renderObject.node()->shadowHost();
-    if (!input)
-        input = renderObject.node();
-    if (!is<RenderBox>(*input->renderer()))
-        return IntRect();
+bool RenderThemeGtk::paintSearchFieldResultsButton(const RenderBox& o, const PaintInfo& i, const IntRect& rect)
+{
+    return paintSearchFieldResultsDecorationPart(o, i, rect);
+}
 
-    // If possible center the y-coordinate of the rect vertically in the parent input element.
-    // We also add one pixel here to ensure that the y coordinate is rounded up for box heights
-    // that are even, which looks in relation to the box text.
-    IntRect inputContentBox = downcast<RenderBox>(*input->renderer()).absoluteContentBox();
+void RenderThemeGtk::adjustSearchFieldResultsDecorationPartStyle(StyleResolver&, RenderStyle& style, Element*) const
+{
+    adjustSearchFieldIconStyle(EntryIconLeft, style);
+}
 
-    // Make sure the scaled decoration stays square and will fit in its parent's box.
-    int iconSize = std::min(inputContentBox.width(), std::min(inputContentBox.height(), rect.height()));
-    IntRect scaledRect(rect.x(), inputContentBox.y() + (inputContentBox.height() - iconSize + 1) / 2, iconSize, iconSize);
-    return scaledRect;
+void RenderThemeGtk::adjustSearchFieldCancelButtonStyle(StyleResolver&, RenderStyle& style, 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()));
@@ -1002,6 +1202,29 @@ static bool paintEntryIcon(RenderThemePart themePart, const char* iconName, Grap
     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)
+        input = renderObject.node();
+    if (!is<RenderBox>(*input->renderer()))
+        return IntRect();
+
+    // If possible center the y-coordinate of the rect vertically in the parent input element.
+    // We also add one pixel here to ensure that the y coordinate is rounded up for box heights
+    // that are even, which looks in relation to the box text.
+    IntRect inputContentBox = downcast<RenderBox>(*input->renderer()).absoluteContentBox();
+
+    // Make sure the scaled decoration stays square and will fit in its parent's box.
+    int iconSize = std::min(inputContentBox.width(), std::min(inputContentBox.height(), rect.height()));
+    IntRect scaledRect(rect.x(), inputContentBox.y() + (inputContentBox.height() - iconSize + 1) / 2, iconSize, iconSize);
+    return scaledRect;
+}
+
 bool RenderThemeGtk::paintSearchFieldResultsDecorationPart(const RenderBox& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
 {
     IntRect iconRect = centerRectVerticallyInParentInputElement(renderObject, rect);
@@ -1012,11 +1235,6 @@ bool RenderThemeGtk::paintSearchFieldResultsDecorationPart(const RenderBox& rend
         gtkIconStateFlags(this, renderObject));
 }
 
-void RenderThemeGtk::adjustSearchFieldCancelButtonStyle(StyleResolver&, RenderStyle& style, Element*) const
-{
-    adjustSearchFieldIconStyle(EntryIconRight, style);
-}
-
 bool RenderThemeGtk::paintSearchFieldCancelButton(const RenderBox& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
 {
     IntRect iconRect = centerRectVerticallyInParentInputElement(renderObject, rect);
@@ -1026,6 +1244,7 @@ bool RenderThemeGtk::paintSearchFieldCancelButton(const RenderBox& renderObject,
     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
 {
@@ -1055,6 +1274,159 @@ void RenderThemeGtk::adjustSliderThumbStyle(StyleResolver& styleResolver, Render
     style.setBoxShadow(nullptr);
 }
 
+#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(part == SliderHorizontalPart || part == SliderVerticalPart);
+
+    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());
+    }
+
+    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 (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);
+
+    return false;
+}
+
+void RenderThemeGtk::adjustSliderThumbSize(RenderStyle& style, Element*) const
+{
+    ControlPart part = style.appearance();
+    if (part != SliderThumbHorizontalPart && part != SliderThumbVerticalPart)
+        return;
+
+    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);
+
+    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();
@@ -1062,12 +1434,7 @@ bool RenderThemeGtk::paintSliderTrack(const RenderObject& renderObject, const Pa
 
     GRefPtr<GtkStyleContext> parentContext = createStyleContext(Scale);
     gtk_style_context_add_class(parentContext.get(), part == SliderHorizontalPart ? GTK_STYLE_CLASS_HORIZONTAL : GTK_STYLE_CLASS_VERTICAL);
-#if GTK_CHECK_VERSION(3, 19, 11)
-    GRefPtr<GtkStyleContext> contentsContext = createStyleContext(ScaleContents, parentContext.get());
-    GRefPtr<GtkStyleContext> context = createStyleContext(ScaleTrough, contentsContext.get());
-#else
     GRefPtr<GtkStyleContext> context = createStyleContext(ScaleTrough, parentContext.get());
-#endif
     gtk_style_context_set_direction(context.get(), gtkTextDirection(renderObject.style().direction()));
 
     if (!isEnabled(renderObject))
@@ -1091,25 +1458,6 @@ bool RenderThemeGtk::paintSliderTrack(const RenderObject& renderObject, const Pa
     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());
 
-#if GTK_CHECK_VERSION(3, 19, 11)
-    GRefPtr<GtkStyleContext> highlightContext = createStyleContext(ScaleHighlight, context.get());
-    LayoutPoint thumbLocation;
-    if (is<HTMLInputElement>(renderObject.node())) {
-        auto& input = downcast<HTMLInputElement>(*renderObject.node());
-        if (auto* element = input.sliderThumbElement())
-            thumbLocation = element->renderBox()->location();
-    }
-
-    IntRect highlightRect = sliderRect;
-    if (part == SliderHorizontalPart)
-        highlightRect.setWidth(thumbLocation.x());
-    else
-        highlightRect.setHeight(thumbLocation.y());
-
-    gtk_render_background(highlightContext.get(), paintInfo.context().platformContext()->cr(), highlightRect.x(), highlightRect.y(), highlightRect.width(), highlightRect.height());
-    gtk_render_frame(highlightContext.get(), paintInfo.context().platformContext()->cr(), highlightRect.x(), highlightRect.y(), highlightRect.width(), highlightRect.height());
-#endif
-
     if (isFocused(renderObject)) {
         gint focusWidth, focusPad;
         gtk_style_context_get_style(context.get(), "focus-line-width", &focusWidth, "focus-padding", &focusPad, nullptr);
@@ -1121,6 +1469,26 @@ bool RenderThemeGtk::paintSliderTrack(const RenderObject& renderObject, const Pa
     return false;
 }
 
+void RenderThemeGtk::adjustSliderThumbSize(RenderStyle& style, 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(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);
+    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();
@@ -1129,12 +1497,7 @@ bool RenderThemeGtk::paintSliderThumb(const RenderObject& renderObject, const Pa
     // 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);
-#if GTK_CHECK_VERSION(3, 19, 11)
-    GRefPtr<GtkStyleContext> contentsContext = createStyleContext(ScaleContents, parentContext.get());
-    GRefPtr<GtkStyleContext> troughContext = createStyleContext(ScaleTrough, contentsContext.get());
-#else
     GRefPtr<GtkStyleContext> troughContext = createStyleContext(ScaleTrough, parentContext.get());
-#endif
     GRefPtr<GtkStyleContext> context = createStyleContext(ScaleSlider, troughContext.get());
     gtk_style_context_set_direction(context.get(), gtkTextDirection(renderObject.style().direction()));
 
@@ -1152,35 +1515,60 @@ bool RenderThemeGtk::paintSliderThumb(const RenderObject& renderObject, const Pa
 
     return false;
 }
+#endif
 
-void RenderThemeGtk::adjustSliderThumbSize(RenderStyle& style, Element*) const
+#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)
 {
-    ControlPart part = style.appearance();
-    if (part != SliderThumbHorizontalPart && part != SliderThumbVerticalPart)
-        return;
+    if (!renderObject.isProgress())
+        return true;
 
-    GRefPtr<GtkStyleContext> context = createStyleContext(Scale);
-    gint sliderWidth, sliderLength;
-#if GTK_CHECK_VERSION(3, 19, 11)
-    GRefPtr<GtkStyleContext> contentsContext = createStyleContext(ScaleContents, context.get());
-    GRefPtr<GtkStyleContext> troughContext = createStyleContext(ScaleTrough, contentsContext.get());
-    GRefPtr<GtkStyleContext> sliderContext = createStyleContext(ScaleSlider, troughContext.get());
-    gtk_style_context_get(sliderContext.get(), gtk_style_context_get_state(sliderContext.get()), "min-width", &sliderWidth, "min-height", &sliderLength, nullptr);
-    GtkBorder border;
-    gtk_style_context_get_border(sliderContext.get(), gtk_style_context_get_state(sliderContext.get()), &border);
-    sliderWidth += border.left + border.right;
-    sliderLength += border.top + border.bottom;
+    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
-    gtk_style_context_get_style(context.get(), "slider-width", &sliderWidth, "slider-length", &sliderLength, nullptr);
-#endif
-    if (part == SliderThumbHorizontalPart) {
-        style.setWidth(Length(sliderLength, Fixed));
-        style.setHeight(Length(sliderWidth, Fixed));
-        return;
-    }
-    ASSERT(part == SliderThumbVerticalPart);
-    style.setWidth(Length(sliderWidth, Fixed));
-    style.setHeight(Length(sliderLength, Fixed));
+IntRect RenderThemeGtk::progressBarRectForBounds(const RenderObject&, const IntRect& bounds) const
+{
+    return bounds;
 }
 
 bool RenderThemeGtk::paintProgressBar(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
@@ -1217,7 +1605,79 @@ bool RenderThemeGtk::paintProgressBar(const RenderObject& renderObject, const Pa
 
     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, 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;
@@ -1320,6 +1780,7 @@ bool RenderThemeGtk::paintInnerSpinButton(const RenderObject& renderObject, cons
 
     return false;
 }
+#endif // GTK_CHECK_VERSION(3, 20, 0)
 
 double RenderThemeGtk::caretBlinkInterval() const
 {
@@ -1338,16 +1799,44 @@ double RenderThemeGtk::caretBlinkInterval() const
 
 enum StyleColorType { StyleColorBackground, StyleColorForeground };
 
+#if GTK_CHECK_VERSION(3, 20, 0)
 static Color styleColor(RenderThemePart themePart, GtkStateFlags state, StyleColorType colorType)
 {
-    GRefPtr<GtkStyleContext> parentContext;
+    RenderThemeGadget::Info info = { RenderThemeGadget::Type::Generic, nullptr, state, { } };
+    std::unique_ptr<RenderThemeGadget> parentGadget;
     RenderThemePart part = themePart;
     if (themePart == Entry && (state & GTK_STATE_FLAG_SELECTED)) {
-        parentContext = createStyleContext(Entry);
+        info.name = "entry";
+        parentGadget = RenderThemeGadget::create(info);
         part = EntrySelection;
     }
 
-    GRefPtr<GtkStyleContext> context = createStyleContext(part, parentContext.get());
+    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;
@@ -1357,6 +1846,7 @@ static Color styleColor(RenderThemePart themePart, GtkStateFlags state, StyleCol
         gtk_style_context_get_color(context.get(), state, &gdkRGBAColor);
     return gdkRGBAColor;
 }
+#endif // GTK_CHECK_VERSION(3, 20, 0)
 
 Color RenderThemeGtk::platformActiveSelectionBackgroundColor() const
 {
@@ -1428,6 +1918,17 @@ String RenderThemeGtk::extraFullScreenStyleSheet()
 }
 #endif
 
+#if GTK_CHECK_VERSION(3, 20, 0)
+bool RenderThemeGtk::paintMediaButton(const RenderObject& renderObject, GraphicsContext& graphicsContext, const IntRect& rect, const char* iconName)
+{
+    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);
@@ -1437,6 +1938,7 @@ bool RenderThemeGtk::paintMediaButton(const RenderObject& renderObject, Graphics
     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
 {
index 4197c76..6d12461 100644 (file)
@@ -105,6 +105,7 @@ public:
     bool shouldHaveCapsLockIndicator(HTMLInputElement&) const override;
 
 private:
+    RenderThemeGtk();
     virtual ~RenderThemeGtk();
 
     bool paintCheckbox(const RenderObject&, const PaintInfo&, const IntRect&) override;
@@ -116,6 +117,7 @@ private:
     void adjustButtonStyle(StyleResolver&, RenderStyle&, Element*) const override;
     bool paintButton(const RenderObject&, const PaintInfo&, const IntRect&) override;
 
+    void adjustTextFieldStyle(StyleResolver&, RenderStyle&, Element*) const override;
     bool paintTextField(const RenderObject&, const PaintInfo&, const FloatRect&) override;
     bool paintTextArea(const RenderObject&, const PaintInfo&, const FloatRect&) override;
 
@@ -170,8 +172,10 @@ private:
     double animationRepeatIntervalForProgressBar(RenderProgress&) const override;
     double animationDurationForProgressBar(RenderProgress&) const override;
     void adjustProgressBarStyle(StyleResolver&, RenderStyle&, Element*) const override;
+    IntRect progressBarRectForBounds(const RenderObject&, const IntRect&) const override;
     bool paintProgressBar(const RenderObject&, const PaintInfo&, const IntRect&) override;
 
+    InnerSpinButtonLayout innerSpinButtonLayout(const RenderObject&) const override;
     void adjustInnerSpinButtonStyle(StyleResolver&, RenderStyle&, Element*) const override;
     bool paintInnerSpinButton(const RenderObject&, const PaintInfo&, const IntRect&) override;