Title: [199292] trunk
Revision
199292
Author
[email protected]
Date
2016-04-11 03:59:03 -0700 (Mon, 11 Apr 2016)

Log Message

[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.

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:

Modified Paths

Added Paths

Diff

Modified: trunk/ChangeLog (199291 => 199292)


--- trunk/ChangeLog	2016-04-11 09:13:06 UTC (rev 199291)
+++ trunk/ChangeLog	2016-04-11 10:59:03 UTC (rev 199292)
@@ -1,3 +1,14 @@
+2016-04-07  Carlos Garcia Campos  <[email protected]>
+
+        [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  <[email protected]>
 
         [CMake] Make FOLDER property INHERITED

Added: trunk/ManualTests/gtk/theme.html (0 => 199292)


--- trunk/ManualTests/gtk/theme.html	                        (rev 0)
+++ trunk/ManualTests/gtk/theme.html	2016-04-11 10:59:03 UTC (rev 199292)
@@ -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=""
+      <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>

Modified: trunk/Source/WebCore/ChangeLog (199291 => 199292)


--- trunk/Source/WebCore/ChangeLog	2016-04-11 09:13:06 UTC (rev 199291)
+++ trunk/Source/WebCore/ChangeLog	2016-04-11 10:59:03 UTC (rev 199292)
@@ -1,3 +1,96 @@
+2016-04-07  Carlos Garcia Campos  <[email protected]>
+
+        [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  <[email protected]>
 
         Implement functional :host() pseudo class

Modified: trunk/Source/WebCore/PlatformGTK.cmake (199291 => 199292)


--- trunk/Source/WebCore/PlatformGTK.cmake	2016-04-11 09:13:06 UTC (rev 199291)
+++ trunk/Source/WebCore/PlatformGTK.cmake	2016-04-11 10:59:03 UTC (rev 199292)
@@ -221,6 +221,7 @@
     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

Modified: trunk/Source/WebCore/html/shadow/SpinButtonElement.cpp (199291 => 199292)


--- trunk/Source/WebCore/html/shadow/SpinButtonElement.cpp	2016-04-11 09:13:06 UTC (rev 199291)
+++ trunk/Source/WebCore/html/shadow/SpinButtonElement.cpp	2016-04-11 10:59:03 UTC (rev 199292)
@@ -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 @@
                 }
             }
             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 {

Added: trunk/Source/WebCore/platform/gtk/RenderThemeGadget.cpp (0 => 199292)


--- trunk/Source/WebCore/platform/gtk/RenderThemeGadget.cpp	                        (rev 0)
+++ trunk/Source/WebCore/platform/gtk/RenderThemeGadget.cpp	2016-04-11 10:59:03 UTC (rev 199292)
@@ -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)

Added: trunk/Source/WebCore/platform/gtk/RenderThemeGadget.h (0 => 199292)


--- trunk/Source/WebCore/platform/gtk/RenderThemeGadget.h	                        (rev 0)
+++ trunk/Source/WebCore/platform/gtk/RenderThemeGadget.h	2016-04-11 10:59:03 UTC (rev 199292)
@@ -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)

Modified: trunk/Source/WebCore/rendering/RenderTheme.h (199291 => 199292)


--- trunk/Source/WebCore/rendering/RenderTheme.h	2016-04-11 09:13:06 UTC (rev 199291)
+++ trunk/Source/WebCore/rendering/RenderTheme.h	2016-04-11 10:59:03 UTC (rev 199292)
@@ -253,6 +253,9 @@
     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;

Modified: trunk/Source/WebCore/rendering/RenderThemeGtk.cpp (199291 => 199292)


--- trunk/Source/WebCore/rendering/RenderThemeGtk.cpp	2016-04-11 09:13:06 UTC (rev 199291)
+++ trunk/Source/WebCore/rendering/RenderThemeGtk.cpp	2016-04-11 10:59:03 UTC (rev 199292)
@@ -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,11 +134,22 @@
 
 #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,
     EntrySelection,
@@ -145,17 +157,13 @@
     EntryIconRight,
     Button,
     CheckButton,
-    CheckButtonCheck,
     RadioButton,
-    RadioButtonRadio,
     ComboBox,
     ComboBoxButton,
     ComboBoxArrow,
     Scale,
-    ScaleContents,
     ScaleTrough,
     ScaleSlider,
-    ScaleHighlight,
     ProgressBar,
     ProgressBarTrough,
     ProgressBarProgress,
@@ -168,220 +176,102 @@
 #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 @@
 
     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 @@
     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 @@
     }
     adjustRectForFocus(context.get(), rect);
 }
+#endif // GTK_CHECK_VERSION(3, 20, 0)
 
 void RenderThemeGtk::adjustButtonStyle(StyleResolver&, RenderStyle& style, Element*) const
 {
@@ -553,39 +510,111 @@
         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.
+    if (!style.width().isIntrinsicOrAuto() && !style.height().isAuto())
+        return;
+
     GRefPtr<GtkStyleContext> context = createStyleContext(themePart);
     // Other ports hard-code this to 13. GTK+ users tend to demand the native look.
     gint indicatorSize;
     gtk_style_context_get_style(context.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,30 +638,12 @@
     // shrink the rectangle back down to the default size and then center it
     // in the full toggle button region. The reason for not simply forcing toggle
     // buttons to be a smaller size is that we don't want to break site layouts.
+    FloatRect rect(fullRect);
     gint indicatorSize;
     gtk_style_context_get_style(context.get(), "indicator-size", &indicatorSize, nullptr);
     IntSize minSize(indicatorSize, indicatorSize);
+    shrinkToMinimumSizeAndCenterRectangle(rect, minSize);
 
-#if 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.
-    }
-
     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 @@
             indicatorRect.width(), indicatorRect.height());
     }
 }
+#endif // GTK_CHECK_VERSION(3, 20, 0)
 
 void RenderThemeGtk::setCheckboxSize(RenderStyle& style) const
 {
@@ -673,6 +685,19 @@
     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 @@
     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,9 +787,86 @@
     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)
         return { 0, 0, 0, 0 };
 
     GRefPtr<GtkStyleContext> parentContext = createStyleContext(ComboBox);
@@ -831,13 +934,8 @@
     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,14 +952,67 @@
 
     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);
     gtk_style_context_set_direction(context.get(), static_cast<GtkTextDirection>(gtkTextDirection(renderObject.style().direction())));
 
@@ -888,12 +1039,34 @@
 
     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 @@
     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 @@
     style.setWidth(Length(width + (padding.left + padding.right), Fixed));
     style.setHeight(Length(height + (padding.top + padding.bottom), Fixed));
 }
+#endif
 
+bool RenderThemeGtk::paintTextArea(const RenderObject& o, const PaintInfo& i, const FloatRect& r)
+{
+    return paintTextField(o, i, r);
+}
+
+void RenderThemeGtk::adjustSearchFieldResultsButtonStyle(StyleResolver& styleResolver, RenderStyle& style, Element* e) const
+{
+    adjustSearchFieldCancelButtonStyle(styleResolver, style, e);
+}
+
+bool RenderThemeGtk::paintSearchFieldResultsButton(const RenderBox& o, const PaintInfo& i, const IntRect& rect)
+{
+    return paintSearchFieldResultsDecorationPart(o, i, rect);
+}
+
 void RenderThemeGtk::adjustSearchFieldResultsDecorationPartStyle(StyleResolver&, RenderStyle& style, Element*) const
 {
     adjustSearchFieldIconStyle(EntryIconLeft, style);
 }
 
-static IntRect centerRectVerticallyInParentInputElement(const RenderObject& renderObject, const IntRect& rect)
+void RenderThemeGtk::adjustSearchFieldCancelButtonStyle(StyleResolver&, RenderStyle& style, Element*) const
 {
-    if (!renderObject.node())
-        return IntRect();
+    adjustSearchFieldIconStyle(EntryIconRight, style);
+}
 
-    // Get the renderer of <input> element.
-    Node* input = renderObject.node()->shadowHost();
-    if (!input)
-        input = renderObject.node();
-    if (!is<RenderBox>(*input->renderer()))
-        return IntRect();
+#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);
+}
 
-    // 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::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 @@
     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 @@
         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 @@
     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,19 +1274,167 @@
     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();
+    ASSERT(part == SliderHorizontalPart || part == SliderVerticalPart);
+
     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 @@
     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 @@
     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 @@
     // 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 @@
 
     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
 {
-    ControlPart part = style.appearance();
-    if (part != SliderThumbHorizontalPart && part != SliderThumbVerticalPart)
-        return;
+    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());
+}
 
-    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;
+bool RenderThemeGtk::paintProgressBar(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
+{
+    if (!renderObject.isProgress())
+        return true;
+
+    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 @@
 
     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 @@
 
     return false;
 }
+#endif // GTK_CHECK_VERSION(3, 20, 0)
 
 double RenderThemeGtk::caretBlinkInterval() const
 {
@@ -1338,16 +1799,44 @@
 
 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 @@
         gtk_style_context_get_color(context.get(), state, &gdkRGBAColor);
     return gdkRGBAColor;
 }
+#endif // GTK_CHECK_VERSION(3, 20, 0)
 
 Color RenderThemeGtk::platformActiveSelectionBackgroundColor() const
 {
@@ -1428,8 +1918,19 @@
 }
 #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);
     gtk_style_context_set_direction(context.get(), gtkTextDirection(renderObject.style().direction()));
     gtk_style_context_set_state(context.get(), gtkIconStateFlags(this, renderObject));
@@ -1437,6 +1938,7 @@
     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
 {

Modified: trunk/Source/WebCore/rendering/RenderThemeGtk.h (199291 => 199292)


--- trunk/Source/WebCore/rendering/RenderThemeGtk.h	2016-04-11 09:13:06 UTC (rev 199291)
+++ trunk/Source/WebCore/rendering/RenderThemeGtk.h	2016-04-11 10:59:03 UTC (rev 199292)
@@ -105,6 +105,7 @@
     bool shouldHaveCapsLockIndicator(HTMLInputElement&) const override;
 
 private:
+    RenderThemeGtk();
     virtual ~RenderThemeGtk();
 
     bool paintCheckbox(const RenderObject&, const PaintInfo&, const IntRect&) override;
@@ -116,6 +117,7 @@
     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 @@
     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;
 
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to