Make it possible to query scrollbar pseudo-style without having a scrollbar
https://bugs.webkit.org/show_bug.cgi?id=203174

Reviewed by Tim Horton.

A future patch needs the ability to query scrollbar pseudo-style without having
a scrollbar, and it was ugly to pass a live RenderScrollbar into CSS style resolution
functions. Instead, pass in a pure-data object that has information about the scrollbar.

* css/ElementRuleCollector.cpp:
(WebCore::ElementRuleCollector::ruleMatches):
* css/SelectorChecker.h:
* css/SelectorCheckerTestFunctions.h:
(WebCore::scrollbarMatchesEnabledPseudoClass):
(WebCore::scrollbarMatchesDisabledPseudoClass):
(WebCore::scrollbarMatchesHoverPseudoClass):
(WebCore::scrollbarMatchesActivePseudoClass):
(WebCore::scrollbarMatchesHorizontalPseudoClass):
(WebCore::scrollbarMatchesVerticalPseudoClass):
(WebCore::scrollbarMatchesDecrementPseudoClass):
(WebCore::scrollbarMatchesIncrementPseudoClass):
(WebCore::scrollbarMatchesStartPseudoClass):
(WebCore::scrollbarMatchesEndPseudoClass):
(WebCore::scrollbarMatchesDoubleButtonPseudoClass):
(WebCore::scrollbarMatchesSingleButtonPseudoClass):
(WebCore::scrollbarMatchesNoButtonPseudoClass):
(WebCore::scrollbarMatchesCornerPresentPseudoClass):
* css/StyleResolver.h:
(WebCore::PseudoStyleRequest::PseudoStyleRequest):
* rendering/RenderScrollbar.cpp:
(WebCore::RenderScrollbar::getScrollbarPseudoStyle):


git-svn-id: http://svn.webkit.org/repository/webkit/trunk@251319 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/Source/WebCore/ChangeLog b/Source/WebCore/ChangeLog
index d5bf3a4..c4d1b64 100644
--- a/Source/WebCore/ChangeLog
+++ b/Source/WebCore/ChangeLog
@@ -1,3 +1,37 @@
+2019-10-18  Simon Fraser  <simon.fraser@apple.com>
+
+        Make it possible to query scrollbar pseudo-style without having a scrollbar
+        https://bugs.webkit.org/show_bug.cgi?id=203174
+
+        Reviewed by Tim Horton.
+
+        A future patch needs the ability to query scrollbar pseudo-style without having
+        a scrollbar, and it was ugly to pass a live RenderScrollbar into CSS style resolution
+        functions. Instead, pass in a pure-data object that has information about the scrollbar.
+
+        * css/ElementRuleCollector.cpp:
+        (WebCore::ElementRuleCollector::ruleMatches):
+        * css/SelectorChecker.h:
+        * css/SelectorCheckerTestFunctions.h:
+        (WebCore::scrollbarMatchesEnabledPseudoClass):
+        (WebCore::scrollbarMatchesDisabledPseudoClass):
+        (WebCore::scrollbarMatchesHoverPseudoClass):
+        (WebCore::scrollbarMatchesActivePseudoClass):
+        (WebCore::scrollbarMatchesHorizontalPseudoClass):
+        (WebCore::scrollbarMatchesVerticalPseudoClass):
+        (WebCore::scrollbarMatchesDecrementPseudoClass):
+        (WebCore::scrollbarMatchesIncrementPseudoClass):
+        (WebCore::scrollbarMatchesStartPseudoClass):
+        (WebCore::scrollbarMatchesEndPseudoClass):
+        (WebCore::scrollbarMatchesDoubleButtonPseudoClass):
+        (WebCore::scrollbarMatchesSingleButtonPseudoClass):
+        (WebCore::scrollbarMatchesNoButtonPseudoClass):
+        (WebCore::scrollbarMatchesCornerPresentPseudoClass):
+        * css/StyleResolver.h:
+        (WebCore::PseudoStyleRequest::PseudoStyleRequest):
+        * rendering/RenderScrollbar.cpp:
+        (WebCore::RenderScrollbar::getScrollbarPseudoStyle):
+
 2019-10-18  Said Abou-Hallawa  <sabouhallawa@apple.com>
 
         [SVG2]: Remove the SVGExternalResourcesRequired interface
diff --git a/Source/WebCore/css/ElementRuleCollector.cpp b/Source/WebCore/css/ElementRuleCollector.cpp
index 8e03009..a2a59d2 100644
--- a/Source/WebCore/css/ElementRuleCollector.cpp
+++ b/Source/WebCore/css/ElementRuleCollector.cpp
@@ -481,8 +481,7 @@
 
     SelectorChecker::CheckingContext context(m_mode);
     context.pseudoId = m_pseudoStyleRequest.pseudoId;
-    context.scrollbar = m_pseudoStyleRequest.scrollbar;
-    context.scrollbarPart = m_pseudoStyleRequest.scrollbarPart;
+    context.scrollbarState = m_pseudoStyleRequest.scrollbarState;
     context.isMatchingHostPseudoClass = m_isMatchingHostPseudoClass;
     context.shadowHostInPartRuleScope = m_shadowHostInPartRuleScope.get();
 
diff --git a/Source/WebCore/css/SelectorChecker.h b/Source/WebCore/css/SelectorChecker.h
index 22ceb07..ce31016 100644
--- a/Source/WebCore/css/SelectorChecker.h
+++ b/Source/WebCore/css/SelectorChecker.h
@@ -38,6 +38,16 @@
 class RenderScrollbar;
 class RenderStyle;
 
+struct StyleScrollbarState {
+    ScrollbarPart scrollbarPart { NoPart };
+    ScrollbarPart hoveredPart { NoPart };
+    ScrollbarPart pressedPart { NoPart };
+    ScrollbarOrientation orientation { VerticalScrollbar };
+    ScrollbarButtonsPlacement buttonsPlacement { ScrollbarButtonsNone };
+    bool enabled { false };
+    bool scrollCornerIsVisible { false };
+};
+
 class SelectorChecker {
     WTF_MAKE_NONCOPYABLE(SelectorChecker);
     enum class Match { SelectorMatches, SelectorFailsLocally, SelectorFailsAllSiblings, SelectorFailsCompletely };
@@ -80,8 +90,7 @@
 
         const SelectorChecker::Mode resolvingMode;
         PseudoId pseudoId { PseudoId::None };
-        RenderScrollbar* scrollbar { nullptr };
-        ScrollbarPart scrollbarPart { NoPart };
+        Optional<StyleScrollbarState> scrollbarState;
         const ContainerNode* scope { nullptr };
         bool isMatchingHostPseudoClass { false };
         const Element* shadowHostInPartRuleScope { nullptr };
diff --git a/Source/WebCore/css/SelectorCheckerTestFunctions.h b/Source/WebCore/css/SelectorCheckerTestFunctions.h
index 8fa80cc..d592866 100644
--- a/Source/WebCore/css/SelectorCheckerTestFunctions.h
+++ b/Source/WebCore/css/SelectorCheckerTestFunctions.h
@@ -32,9 +32,7 @@
 #include "HTMLIFrameElement.h"
 #include "HTMLInputElement.h"
 #include "HTMLOptionElement.h"
-#include "RenderScrollbar.h"
-#include "ScrollableArea.h"
-#include "ScrollbarTheme.h"
+#include "SelectorChecker.h"
 #include <wtf/Compiler.h>
 
 #if ENABLE(ATTACHMENT_ELEMENT)
@@ -249,105 +247,122 @@
 
 ALWAYS_INLINE bool scrollbarMatchesEnabledPseudoClass(const SelectorChecker::CheckingContext& context)
 {
-    return context.scrollbar && context.scrollbar->enabled();
+    return context.scrollbarState && context.scrollbarState->enabled;
 }
 
 ALWAYS_INLINE bool scrollbarMatchesDisabledPseudoClass(const SelectorChecker::CheckingContext& context)
 {
-    return context.scrollbar && !context.scrollbar->enabled();
+    return context.scrollbarState && !context.scrollbarState->enabled;
 }
 
 ALWAYS_INLINE bool scrollbarMatchesHoverPseudoClass(const SelectorChecker::CheckingContext& context)
 {
-    if (!context.scrollbar)
+    if (!context.scrollbarState)
         return false;
-    ScrollbarPart hoveredPart = context.scrollbar->hoveredPart();
-    if (context.scrollbarPart == ScrollbarBGPart)
+    auto scrollbarPart = context.scrollbarState->scrollbarPart;
+    auto hoveredPart = context.scrollbarState->hoveredPart;
+    if (scrollbarPart == ScrollbarBGPart)
         return hoveredPart != NoPart;
-    if (context.scrollbarPart == TrackBGPart)
+    if (scrollbarPart == TrackBGPart)
         return hoveredPart == BackTrackPart || hoveredPart == ForwardTrackPart || hoveredPart == ThumbPart;
-    return context.scrollbarPart == hoveredPart;
+    return scrollbarPart == hoveredPart;
 }
 
 ALWAYS_INLINE bool scrollbarMatchesActivePseudoClass(const SelectorChecker::CheckingContext& context)
 {
-    if (!context.scrollbar)
+    if (!context.scrollbarState)
         return false;
-    ScrollbarPart pressedPart = context.scrollbar->pressedPart();
-    if (context.scrollbarPart == ScrollbarBGPart)
+    auto scrollbarPart = context.scrollbarState->scrollbarPart;
+    auto pressedPart = context.scrollbarState->pressedPart;
+    if (scrollbarPart == ScrollbarBGPart)
         return pressedPart != NoPart;
-    if (context.scrollbarPart == TrackBGPart)
+    if (scrollbarPart == TrackBGPart)
         return pressedPart == BackTrackPart || pressedPart == ForwardTrackPart || pressedPart == ThumbPart;
-    return context.scrollbarPart == pressedPart;
+    return scrollbarPart == pressedPart;
 }
 
 ALWAYS_INLINE bool scrollbarMatchesHorizontalPseudoClass(const SelectorChecker::CheckingContext& context)
 {
-    return context.scrollbar && context.scrollbar->orientation() == HorizontalScrollbar;
+    return context.scrollbarState && context.scrollbarState->orientation == HorizontalScrollbar;
 }
 
 ALWAYS_INLINE bool scrollbarMatchesVerticalPseudoClass(const SelectorChecker::CheckingContext& context)
 {
-    return context.scrollbar && context.scrollbar->orientation() == VerticalScrollbar;
+    return context.scrollbarState && context.scrollbarState->orientation == VerticalScrollbar;
 }
 
 ALWAYS_INLINE bool scrollbarMatchesDecrementPseudoClass(const SelectorChecker::CheckingContext& context)
 {
-    return context.scrollbarPart == BackButtonStartPart || context.scrollbarPart == BackButtonEndPart || context.scrollbarPart == BackTrackPart;
+    if (!context.scrollbarState)
+        return false;
+    auto scrollbarPart = context.scrollbarState->scrollbarPart;
+    return scrollbarPart == BackButtonStartPart || scrollbarPart == BackButtonEndPart || scrollbarPart == BackTrackPart;
 }
 
 ALWAYS_INLINE bool scrollbarMatchesIncrementPseudoClass(const SelectorChecker::CheckingContext& context)
 {
-    return context.scrollbarPart == ForwardButtonStartPart || context.scrollbarPart == ForwardButtonEndPart || context.scrollbarPart == ForwardTrackPart;
+    if (!context.scrollbarState)
+        return false;
+    auto scrollbarPart = context.scrollbarState->scrollbarPart;
+    return scrollbarPart == ForwardButtonStartPart || scrollbarPart == ForwardButtonEndPart || scrollbarPart == ForwardTrackPart;
 }
 
 ALWAYS_INLINE bool scrollbarMatchesStartPseudoClass(const SelectorChecker::CheckingContext& context)
 {
-    return context.scrollbarPart == BackButtonStartPart || context.scrollbarPart == ForwardButtonStartPart || context.scrollbarPart == BackTrackPart;
+    if (!context.scrollbarState)
+        return false;
+    auto scrollbarPart = context.scrollbarState->scrollbarPart;
+    return scrollbarPart == BackButtonStartPart || scrollbarPart == ForwardButtonStartPart || scrollbarPart == BackTrackPart;
 }
 
 ALWAYS_INLINE bool scrollbarMatchesEndPseudoClass(const SelectorChecker::CheckingContext& context)
 {
-    return context.scrollbarPart == BackButtonEndPart || context.scrollbarPart == ForwardButtonEndPart || context.scrollbarPart == ForwardTrackPart;
+    if (!context.scrollbarState)
+        return false;
+    auto scrollbarPart = context.scrollbarState->scrollbarPart;
+    return scrollbarPart == BackButtonEndPart || scrollbarPart == ForwardButtonEndPart || scrollbarPart == ForwardTrackPart;
 }
 
 ALWAYS_INLINE bool scrollbarMatchesDoubleButtonPseudoClass(const SelectorChecker::CheckingContext& context)
 {
-    if (!context.scrollbar)
+    if (!context.scrollbarState)
         return false;
-    ScrollbarButtonsPlacement buttonsPlacement = context.scrollbar->theme().buttonsPlacement();
-    if (context.scrollbarPart == BackButtonStartPart || context.scrollbarPart == ForwardButtonStartPart || context.scrollbarPart == BackTrackPart)
+    auto scrollbarPart = context.scrollbarState->scrollbarPart;
+    auto buttonsPlacement = context.scrollbarState->buttonsPlacement;
+    if (scrollbarPart == BackButtonStartPart || scrollbarPart == ForwardButtonStartPart || scrollbarPart == BackTrackPart)
         return buttonsPlacement == ScrollbarButtonsDoubleStart || buttonsPlacement == ScrollbarButtonsDoubleBoth;
-    if (context.scrollbarPart == BackButtonEndPart || context.scrollbarPart == ForwardButtonEndPart || context.scrollbarPart == ForwardTrackPart)
+    if (scrollbarPart == BackButtonEndPart || scrollbarPart == ForwardButtonEndPart || scrollbarPart == ForwardTrackPart)
         return buttonsPlacement == ScrollbarButtonsDoubleEnd || buttonsPlacement == ScrollbarButtonsDoubleBoth;
     return false;
 }
 
 ALWAYS_INLINE bool scrollbarMatchesSingleButtonPseudoClass(const SelectorChecker::CheckingContext& context)
 {
-    if (!context.scrollbar)
+    if (!context.scrollbarState)
         return false;
-    ScrollbarButtonsPlacement buttonsPlacement = context.scrollbar->theme().buttonsPlacement();
-    if (context.scrollbarPart == BackButtonStartPart || context.scrollbarPart == ForwardButtonEndPart || context.scrollbarPart == BackTrackPart || context.scrollbarPart == ForwardTrackPart)
+    auto scrollbarPart = context.scrollbarState->scrollbarPart;
+    auto buttonsPlacement = context.scrollbarState->buttonsPlacement;
+    if (scrollbarPart == BackButtonStartPart || scrollbarPart == ForwardButtonEndPart || scrollbarPart == BackTrackPart || scrollbarPart == ForwardTrackPart)
         return buttonsPlacement == ScrollbarButtonsSingle;
     return false;
 }
 
 ALWAYS_INLINE bool scrollbarMatchesNoButtonPseudoClass(const SelectorChecker::CheckingContext& context)
 {
-    if (!context.scrollbar)
+    if (!context.scrollbarState)
         return false;
-    ScrollbarButtonsPlacement buttonsPlacement = context.scrollbar->theme().buttonsPlacement();
-    if (context.scrollbarPart == BackTrackPart)
+    auto scrollbarPart = context.scrollbarState->scrollbarPart;
+    auto buttonsPlacement = context.scrollbarState->buttonsPlacement;
+    if (scrollbarPart == BackTrackPart)
         return buttonsPlacement == ScrollbarButtonsNone || buttonsPlacement == ScrollbarButtonsDoubleEnd;
-    if (context.scrollbarPart == ForwardTrackPart)
+    if (scrollbarPart == ForwardTrackPart)
         return buttonsPlacement == ScrollbarButtonsNone || buttonsPlacement == ScrollbarButtonsDoubleStart;
     return false;
 }
 
 ALWAYS_INLINE bool scrollbarMatchesCornerPresentPseudoClass(const SelectorChecker::CheckingContext& context)
 {
-    return context.scrollbar && context.scrollbar->scrollableArea().isScrollCornerVisible();
+    return context.scrollbarState && context.scrollbarState->scrollCornerIsVisible;
 }
 
 #if ENABLE(FULLSCREEN_API)
diff --git a/Source/WebCore/css/StyleResolver.h b/Source/WebCore/css/StyleResolver.h
index e0c318e..52941ba 100644
--- a/Source/WebCore/css/StyleResolver.h
+++ b/Source/WebCore/css/StyleResolver.h
@@ -101,16 +101,14 @@
 
 class PseudoStyleRequest {
 public:
-    PseudoStyleRequest(PseudoId pseudoId, RenderScrollbar* scrollbar = nullptr, ScrollbarPart scrollbarPart = NoPart)
+    PseudoStyleRequest(PseudoId pseudoId, Optional<StyleScrollbarState> scrollbarState = WTF::nullopt)
         : pseudoId(pseudoId)
-        , scrollbarPart(scrollbarPart)
-        , scrollbar(scrollbar)
+        , scrollbarState(scrollbarState)
     {
     }
 
     PseudoId pseudoId;
-    ScrollbarPart scrollbarPart;
-    RenderScrollbar* scrollbar;
+    Optional<StyleScrollbarState> scrollbarState;
 };
 
 struct ElementStyle {
diff --git a/Source/WebCore/rendering/RenderScrollbar.cpp b/Source/WebCore/rendering/RenderScrollbar.cpp
index 422ba92..2bee864 100644
--- a/Source/WebCore/rendering/RenderScrollbar.cpp
+++ b/Source/WebCore/rendering/RenderScrollbar.cpp
@@ -139,9 +139,18 @@
 std::unique_ptr<RenderStyle> RenderScrollbar::getScrollbarPseudoStyle(ScrollbarPart partType, PseudoId pseudoId)
 {
     if (!owningRenderer())
-        return 0;
+        return nullptr;
 
-    std::unique_ptr<RenderStyle> result = owningRenderer()->getUncachedPseudoStyle(PseudoStyleRequest(pseudoId, this, partType), &owningRenderer()->style());
+    StyleScrollbarState scrollbarState;
+    scrollbarState.scrollbarPart = partType;
+    scrollbarState.hoveredPart = hoveredPart();
+    scrollbarState.pressedPart = pressedPart();
+    scrollbarState.orientation = orientation();
+    scrollbarState.buttonsPlacement = theme().buttonsPlacement();
+    scrollbarState.enabled = enabled();
+    scrollbarState.scrollCornerIsVisible = scrollableArea().isScrollCornerVisible();
+    
+    std::unique_ptr<RenderStyle> result = owningRenderer()->getUncachedPseudoStyle(PseudoStyleRequest(pseudoId, scrollbarState), &owningRenderer()->style());
     // Scrollbars for root frames should always have background color 
     // unless explicitly specified as transparent. So we force it.
     // This is because WebKit assumes scrollbar to be always painted and missing background