CSS JIT: add support for the pseudo classes :hover and :active
authorbenjamin@webkit.org <benjamin@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 30 May 2014 00:15:18 +0000 (00:15 +0000)
committerbenjamin@webkit.org <benjamin@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 30 May 2014 00:15:18 +0000 (00:15 +0000)
https://bugs.webkit.org/show_bug.cgi?id=133295

Reviewed by Andreas Kling.

This patch adds compilation support for :hover and :active. The code generation of both of them is trivial,
most of the patch is improving the infrastructure and safety.

The document compatibility mode is moved to its own typed enum. This ensure the values are exclusives and
the native type is known (unsigned char here). The values are changes to binary flags to take advantage of
test-and-branch on ARM64 (not used explicitely, it is automatically generated by the MacroAssembler).

The other important refactoring is the code updating the flags on RenderStyle::NonInheritedFlags.
The code was duplicated in some places so it is move into its own function addFlagToElementStyleFromContext().

The generators themself are very simple. First we check the quirks-mode exceptions, that is generally excluded
at compile time because the conditions are almost never met, even in strict mode.
Then we have two cases: style resolution and query selector. Almost everything is just function calls.

* cssjit/SelectorCompiler.cpp:
(WebCore::SelectorCompiler::SelectorFragment::SelectorFragment):
(WebCore::SelectorCompiler::addPseudoClassType):
(WebCore::SelectorCompiler::SelectorCodeGenerator::addFlagToElementStyleFromContext):
(WebCore::SelectorCompiler::fragmentOnlyMatchesLinksInQuirksMode):
(WebCore::SelectorCompiler::getDocument):
(WebCore::SelectorCompiler::SelectorCodeGenerator::generateSpecialFailureInQuirksModeForActiveAndHoverIfNeeded):
(WebCore::SelectorCompiler::SelectorCodeGenerator::generateElementMatching):
(WebCore::SelectorCompiler::elementIsActive):
(WebCore::SelectorCompiler::elementIsActiveForStyleResolution):
(WebCore::SelectorCompiler::SelectorCodeGenerator::generateElementIsActive):
(WebCore::SelectorCompiler::SelectorCodeGenerator::generateElementIsFirstChild):
(WebCore::SelectorCompiler::elementIsHovered):
(WebCore::SelectorCompiler::elementIsHoveredForStyleResolution):
(WebCore::SelectorCompiler::SelectorCodeGenerator::generateElementIsHovered):
(WebCore::SelectorCompiler::SelectorCodeGenerator::generateElementIsLastChild):
(WebCore::SelectorCompiler::SelectorCodeGenerator::generateElementIsOnlyChild):
(WebCore::SelectorCompiler::SelectorCodeGenerator::generateElementIsNthChild):
* dom/Document.cpp:
(WebCore::Document::Document):
(WebCore::Document::setCompatibilityMode):
(WebCore::Document::implicitOpen):
(WebCore::Document::cloneDataFromDocument):
* dom/Document.h:
(WebCore::Document::compatibilityModeMemoryOffset):
(WebCore::Document::inQuirksMode):
(WebCore::Document::inLimitedQuirksMode):
(WebCore::Document::inNoQuirksMode):
(WebCore::Document::compatibilityMode): Deleted.
* html/ImageDocument.cpp:
(WebCore::ImageDocument::ImageDocument):
* html/MediaDocument.cpp:
(WebCore::MediaDocument::MediaDocument):
* html/PluginDocument.cpp:
(WebCore::PluginDocument::PluginDocument):
* html/TextDocument.cpp:
(WebCore::TextDocument::TextDocument):
* html/parser/HTMLConstructionSite.cpp:
(WebCore::HTMLConstructionSite::setDefaultCompatibilityMode):
(WebCore::HTMLConstructionSite::setCompatibilityMode):
(WebCore::HTMLConstructionSite::setCompatibilityModeFromDoctype):
(WebCore::HTMLConstructionSite::insertDoctype):
* html/parser/HTMLConstructionSite.h:
* loader/DocumentWriter.cpp:
(WebCore::DocumentWriter::replaceDocument):
* loader/SinkDocument.cpp:
(WebCore::SinkDocument::SinkDocument):
* page/ios/FrameIOS.mm:
(WebCore::Frame::initWithSimpleHTMLDocument):
* rendering/style/RenderStyle.h:

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

14 files changed:
Source/WebCore/ChangeLog
Source/WebCore/cssjit/SelectorCompiler.cpp
Source/WebCore/dom/Document.cpp
Source/WebCore/dom/Document.h
Source/WebCore/html/ImageDocument.cpp
Source/WebCore/html/MediaDocument.cpp
Source/WebCore/html/PluginDocument.cpp
Source/WebCore/html/TextDocument.cpp
Source/WebCore/html/parser/HTMLConstructionSite.cpp
Source/WebCore/html/parser/HTMLConstructionSite.h
Source/WebCore/loader/DocumentWriter.cpp
Source/WebCore/loader/SinkDocument.cpp
Source/WebCore/page/ios/FrameIOS.mm
Source/WebCore/rendering/style/RenderStyle.h

index 4f430c9..c6cb44e 100644 (file)
@@ -1,3 +1,75 @@
+2014-05-29  Benjamin Poulain  <benjamin@webkit.org>
+
+        CSS JIT: add support for the pseudo classes :hover and :active
+        https://bugs.webkit.org/show_bug.cgi?id=133295
+
+        Reviewed by Andreas Kling.
+
+        This patch adds compilation support for :hover and :active. The code generation of both of them is trivial,
+        most of the patch is improving the infrastructure and safety.
+
+        The document compatibility mode is moved to its own typed enum. This ensure the values are exclusives and
+        the native type is known (unsigned char here). The values are changes to binary flags to take advantage of
+        test-and-branch on ARM64 (not used explicitely, it is automatically generated by the MacroAssembler).
+
+        The other important refactoring is the code updating the flags on RenderStyle::NonInheritedFlags.
+        The code was duplicated in some places so it is move into its own function addFlagToElementStyleFromContext().
+
+        The generators themself are very simple. First we check the quirks-mode exceptions, that is generally excluded
+        at compile time because the conditions are almost never met, even in strict mode.
+        Then we have two cases: style resolution and query selector. Almost everything is just function calls.
+
+        * cssjit/SelectorCompiler.cpp:
+        (WebCore::SelectorCompiler::SelectorFragment::SelectorFragment):
+        (WebCore::SelectorCompiler::addPseudoClassType):
+        (WebCore::SelectorCompiler::SelectorCodeGenerator::addFlagToElementStyleFromContext):
+        (WebCore::SelectorCompiler::fragmentOnlyMatchesLinksInQuirksMode):
+        (WebCore::SelectorCompiler::getDocument):
+        (WebCore::SelectorCompiler::SelectorCodeGenerator::generateSpecialFailureInQuirksModeForActiveAndHoverIfNeeded):
+        (WebCore::SelectorCompiler::SelectorCodeGenerator::generateElementMatching):
+        (WebCore::SelectorCompiler::elementIsActive):
+        (WebCore::SelectorCompiler::elementIsActiveForStyleResolution):
+        (WebCore::SelectorCompiler::SelectorCodeGenerator::generateElementIsActive):
+        (WebCore::SelectorCompiler::SelectorCodeGenerator::generateElementIsFirstChild):
+        (WebCore::SelectorCompiler::elementIsHovered):
+        (WebCore::SelectorCompiler::elementIsHoveredForStyleResolution):
+        (WebCore::SelectorCompiler::SelectorCodeGenerator::generateElementIsHovered):
+        (WebCore::SelectorCompiler::SelectorCodeGenerator::generateElementIsLastChild):
+        (WebCore::SelectorCompiler::SelectorCodeGenerator::generateElementIsOnlyChild):
+        (WebCore::SelectorCompiler::SelectorCodeGenerator::generateElementIsNthChild):
+        * dom/Document.cpp:
+        (WebCore::Document::Document):
+        (WebCore::Document::setCompatibilityMode):
+        (WebCore::Document::implicitOpen):
+        (WebCore::Document::cloneDataFromDocument):
+        * dom/Document.h:
+        (WebCore::Document::compatibilityModeMemoryOffset):
+        (WebCore::Document::inQuirksMode):
+        (WebCore::Document::inLimitedQuirksMode):
+        (WebCore::Document::inNoQuirksMode):
+        (WebCore::Document::compatibilityMode): Deleted.
+        * html/ImageDocument.cpp:
+        (WebCore::ImageDocument::ImageDocument):
+        * html/MediaDocument.cpp:
+        (WebCore::MediaDocument::MediaDocument):
+        * html/PluginDocument.cpp:
+        (WebCore::PluginDocument::PluginDocument):
+        * html/TextDocument.cpp:
+        (WebCore::TextDocument::TextDocument):
+        * html/parser/HTMLConstructionSite.cpp:
+        (WebCore::HTMLConstructionSite::setDefaultCompatibilityMode):
+        (WebCore::HTMLConstructionSite::setCompatibilityMode):
+        (WebCore::HTMLConstructionSite::setCompatibilityModeFromDoctype):
+        (WebCore::HTMLConstructionSite::insertDoctype):
+        * html/parser/HTMLConstructionSite.h:
+        * loader/DocumentWriter.cpp:
+        (WebCore::DocumentWriter::replaceDocument):
+        * loader/SinkDocument.cpp:
+        (WebCore::SinkDocument::SinkDocument):
+        * page/ios/FrameIOS.mm:
+        (WebCore::Frame::initWithSimpleHTMLDocument):
+        * rendering/style/RenderStyle.h:
+
 2014-05-28  Jon Honeycutt  <jhoneycutt@apple.com>
 
         Roll r168668 back in.
index d3c03f7..d771a4e 100644 (file)
@@ -36,6 +36,7 @@
 #include "FunctionCall.h"
 #include "HTMLDocument.h"
 #include "HTMLNames.h"
+#include "InspectorInstrumentation.h"
 #include "NodeRenderStyle.h"
 #include "QualifiedName.h"
 #include "RegisterAllocator.h"
@@ -130,6 +131,7 @@ struct SelectorFragment {
         , widthFromIndirectAdjacent(0)
         , tagName(nullptr)
         , id(nullptr)
+        , inFunctionalPseudoClass(false)
     {
     }
     FragmentRelation relationToLeftFragment;
@@ -153,6 +155,8 @@ struct SelectorFragment {
     Vector<JSC::FunctionPtr> unoptimizedPseudoClasses;
     Vector<AttributeMatchingInfo> attributes;
     Vector<std::pair<int, int>> nthChildfilters;
+
+    bool inFunctionalPseudoClass;
 };
 
 struct TagNamePattern {
@@ -207,7 +211,9 @@ private:
     void generateElementDataMatching(Assembler::JumpList& failureCases, const SelectorFragment&);
     void generateElementFunctionCallTest(Assembler::JumpList& failureCases, JSC::FunctionPtr);
     Assembler::JumpList jumpIfNoPreviousAdjacentElement();
+    void generateElementIsActive(Assembler::JumpList& failureCases, const SelectorFragment&);
     void generateElementIsFirstChild(Assembler::JumpList& failureCases, const SelectorFragment&);
+    void generateElementIsHovered(Assembler::JumpList& failureCases, const SelectorFragment&);
     Assembler::JumpList jumpIfNoNextAdjacentElement();
     void generateElementIsLastChild(Assembler::JumpList& failureCases, const SelectorFragment&);
     void generateElementIsOnlyChild(Assembler::JumpList& failureCases, const SelectorFragment&);
@@ -227,7 +233,9 @@ private:
     void generateElementIsTarget(Assembler::JumpList& failureCases);
 
     // Helpers.
+    void addFlagsToElementStyleFromContext(Assembler::RegisterID checkingContext, int64_t);
     Assembler::Jump jumpIfNotResolvingStyle(Assembler::RegisterID checkingContextRegister);
+    void generateSpecialFailureInQuirksModeForActiveAndHoverIfNeeded(Assembler::JumpList& failureCases, const SelectorFragment&);
     Assembler::Jump modulo(JSC::MacroAssembler::ResultCondition, Assembler::RegisterID inputDividend, int divisor);
     void moduloIsZero(Assembler::JumpList& failureCases, Assembler::RegisterID inputDividend, int divisor);
 
@@ -367,7 +375,9 @@ static inline FunctionType addPseudoClassType(const CSSSelector& selector, Selec
         fragment.pseudoClasses.add(type);
         return FunctionType::SimpleSelectorChecker;
 
+    case CSSSelector::PseudoClassActive:
     case CSSSelector::PseudoClassFirstChild:
+    case CSSSelector::PseudoClassHover:
     case CSSSelector::PseudoClassLastChild:
     case CSSSelector::PseudoClassOnlyChild:
         fragment.pseudoClasses.add(type);
@@ -1208,6 +1218,22 @@ void SelectorCodeGenerator::generateIndirectAdjacentTreeWalker(Assembler::JumpLi
     localFailureCases.linkTo(loopStart, &m_assembler);
 }
 
+
+void SelectorCodeGenerator::addFlagsToElementStyleFromContext(Assembler::RegisterID checkingContext, int64_t newFlag)
+{
+    LocalRegister childStyle(m_registerAllocator);
+    m_assembler.loadPtr(Assembler::Address(checkingContext, OBJECT_OFFSETOF(CheckingContext, elementStyle)), childStyle);
+
+    // FIXME: We should look into doing something smart in MacroAssembler instead.
+    LocalRegister flags(m_registerAllocator);
+    Assembler::Address flagAddress(childStyle, RenderStyle::noninheritedFlagsMemoryOffset() + RenderStyle::NonInheritedFlags::flagsMemoryOffset());
+    m_assembler.load64(flagAddress, flags);
+    LocalRegister isFirstChildStateFlagImmediate(m_registerAllocator);
+    m_assembler.move(Assembler::TrustedImm64(newFlag), isFirstChildStateFlagImmediate);
+    m_assembler.or64(isFirstChildStateFlagImmediate, flags);
+    m_assembler.store64(flags, flagAddress);
+}
+
 Assembler::Jump SelectorCodeGenerator::jumpIfNotResolvingStyle(Assembler::RegisterID checkingContext)
 {
     RELEASE_ASSERT(m_selectorContext == SelectorContext::RuleCollector);
@@ -1220,6 +1246,67 @@ Assembler::Jump SelectorCodeGenerator::jumpIfNotResolvingStyle(Assembler::Regist
     return m_assembler.branch8(Assembler::NotEqual, Assembler::Address(checkingContext, OBJECT_OFFSETOF(CheckingContext, resolvingMode)), Assembler::TrustedImm32(SelectorChecker::ResolvingStyle));
 }
 
+static bool fragmentOnlyMatchesLinksInQuirksMode(const SelectorFragment& fragment)
+{
+    // For quirks mode, follow this: http://quirks.spec.whatwg.org/#the-:active-and-:hover-quirk
+    // In quirks mode, a compound selector 'selector' that matches the following conditions must not match elements that would not also match the ':any-link' selector.
+    //
+    //    selector uses the ':active' or ':hover' pseudo-classes.
+    //    selector does not use a type selector.
+    //    selector does not use an attribute selector.
+    //    selector does not use an ID selector.
+    //    selector does not use a class selector.
+    //    selector does not use a pseudo-class selector other than ':active' and ':hover'.
+    //    selector does not use a pseudo-element selector.
+    //    selector is not part of an argument to a functional pseudo-class or pseudo-element.
+    if (fragment.tagName && *fragment.tagName != anyQName())
+        return false;
+
+    if (!fragment.attributes.isEmpty())
+        return false;
+
+    if (fragment.id)
+        return false;
+
+    if (!fragment.classNames.isEmpty())
+        return false;
+
+    if (!fragment.unoptimizedPseudoClasses.isEmpty() || !fragment.nthChildfilters.isEmpty())
+        return false;
+
+    for (unsigned pseudoClassType : fragment.pseudoClasses) {
+        if (pseudoClassType != CSSSelector::PseudoClassHover && pseudoClassType != CSSSelector::PseudoClassActive)
+            return false;
+    }
+
+    if (fragment.inFunctionalPseudoClass)
+        return false;
+
+    return true;
+}
+
+static void getDocument(Assembler& assembler, Assembler::RegisterID element, Assembler::RegisterID output)
+{
+    assembler.loadPtr(Assembler::Address(element, Node::treeScopeMemoryOffset()), output);
+    assembler.loadPtr(Assembler::Address(output, TreeScope::documentScopeMemoryOffset()), output);
+}
+
+void SelectorCodeGenerator::generateSpecialFailureInQuirksModeForActiveAndHoverIfNeeded(Assembler::JumpList& failureCases, const SelectorFragment& fragment)
+{
+    if (fragmentOnlyMatchesLinksInQuirksMode(fragment)) {
+        // If the element is a link, it can always match :hover or :active.
+        Assembler::Jump isLink = m_assembler.branchTest32(Assembler::NonZero, Assembler::Address(elementAddressRegister, Node::nodeFlagsMemoryOffset()), Assembler::TrustedImm32(Node::flagIsLink()));
+
+        // Only quirks mode restrict :hover and :active.
+        static_assert(sizeof(DocumentCompatibilityMode) == 1, "We generate a byte load/test for the compatibility mode.");
+        LocalRegister documentAddress(m_registerAllocator);
+        getDocument(m_assembler, elementAddressRegister, documentAddress);
+        failureCases.append(m_assembler.branchTest8(Assembler::NonZero, Assembler::Address(documentAddress, Document::compatibilityModeMemoryOffset()), Assembler::TrustedImm32(static_cast<std::underlying_type<DocumentCompatibilityMode>::type>(DocumentCompatibilityMode::QuirksMode))));
+
+        isLink.link(&m_assembler);
+    }
+}
+
 // The value in inputDividend is destroyed by the modulo operation.
 Assembler::Jump SelectorCodeGenerator::modulo(Assembler::ResultCondition condition, Assembler::RegisterID inputDividend, int divisor)
 {
@@ -1475,6 +1562,10 @@ void SelectorCodeGenerator::generateElementMatching(Assembler::JumpList& matchin
 
     generateElementDataMatching(matchingPostTagNameFailureCases, fragment);
 
+    if (fragment.pseudoClasses.contains(CSSSelector::PseudoClassActive))
+        generateElementIsActive(matchingPostTagNameFailureCases, fragment);
+    if (fragment.pseudoClasses.contains(CSSSelector::PseudoClassHover))
+        generateElementIsHovered(matchingPostTagNameFailureCases, fragment);
     if (fragment.pseudoClasses.contains(CSSSelector::PseudoClassOnlyChild))
         generateElementIsOnlyChild(matchingPostTagNameFailureCases, fragment);
     if (fragment.pseudoClasses.contains(CSSSelector::PseudoClassFirstChild))
@@ -1813,12 +1904,6 @@ static inline Assembler::Jump testIsHTMLClassOnDocument(Assembler::ResultConditi
     return assembler.branchTest32(condition, Assembler::Address(documentAddress, Document::documentClassesMemoryOffset()), Assembler::TrustedImm32(Document::isHTMLDocumentClassFlag()));
 }
 
-static void getDocument(Assembler& assembler, Assembler::RegisterID element, Assembler::RegisterID output)
-{
-    assembler.loadPtr(Assembler::Address(element, Node::treeScopeMemoryOffset()), output);
-    assembler.loadPtr(Assembler::Address(output, TreeScope::documentScopeMemoryOffset()), output);
-}
-
 void SelectorCodeGenerator::generateElementAttributeValueExactMatching(Assembler::JumpList& failureCases, Assembler::RegisterID currentAttributeAddress, const AtomicString& expectedValue, bool canDefaultToCaseSensitiveValueMatch)
 {
     LocalRegister expectedValueRegister(m_registerAllocator);
@@ -1919,6 +2004,51 @@ inline Assembler::JumpList SelectorCodeGenerator::jumpIfNoPreviousAdjacentElemen
     return successCase;
 }
 
+static bool elementIsActive(Element* element)
+{
+    return element->active() || InspectorInstrumentation::forcePseudoState(element, CSSSelector::PseudoClassActive);
+}
+
+static bool elementIsActiveForStyleResolution(Element* element, const CheckingContext* checkingContext)
+{
+    if (checkingContext->resolvingMode == SelectorChecker::ResolvingStyle)
+        element->setChildrenAffectedByActive();
+    return element->active() || InspectorInstrumentation::forcePseudoState(element, CSSSelector::PseudoClassActive);
+}
+
+void SelectorCodeGenerator::generateElementIsActive(Assembler::JumpList& failureCases, const SelectorFragment& fragment)
+{
+    generateSpecialFailureInQuirksModeForActiveAndHoverIfNeeded(failureCases, fragment);
+    if (m_selectorContext == SelectorContext::QuerySelector) {
+        FunctionCall functionCall(m_assembler, m_registerAllocator, m_stackAllocator, m_functionCalls);
+        functionCall.setFunctionAddress(elementIsActive);
+        functionCall.setOneArgument(elementAddressRegister);
+        failureCases.append(functionCall.callAndBranchOnCondition(Assembler::Zero));
+    } else {
+        if (fragment.relationToRightFragment == FragmentRelation::Rightmost) {
+            LocalRegister checkingContext(m_registerAllocator);
+            Assembler::Jump notResolvingStyle = jumpIfNotResolvingStyle(checkingContext);
+            addFlagsToElementStyleFromContext(checkingContext, RenderStyle::NonInheritedFlags::flagIsaffectedByActive());
+            notResolvingStyle.link(&m_assembler);
+
+            FunctionCall functionCall(m_assembler, m_registerAllocator, m_stackAllocator, m_functionCalls);
+            functionCall.setFunctionAddress(elementIsActive);
+            functionCall.setOneArgument(elementAddressRegister);
+            failureCases.append(functionCall.callAndBranchOnCondition(Assembler::Zero));
+        } else {
+            unsigned offsetToCheckingContext = m_stackAllocator.offsetToStackReference(m_checkingContextStackReference);
+            Assembler::RegisterID checkingContext = m_registerAllocator.allocateRegister();
+            m_assembler.loadPtr(Assembler::Address(Assembler::stackPointerRegister, offsetToCheckingContext), checkingContext);
+            m_registerAllocator.deallocateRegister(checkingContext);
+
+            FunctionCall functionCall(m_assembler, m_registerAllocator, m_stackAllocator, m_functionCalls);
+            functionCall.setFunctionAddress(elementIsActiveForStyleResolution);
+            functionCall.setTwoArguments(elementAddressRegister, checkingContext);
+            failureCases.append(functionCall.callAndBranchOnCondition(Assembler::Zero));
+        }
+    }
+}
+
 void SelectorCodeGenerator::generateElementIsFirstChild(Assembler::JumpList& failureCases, const SelectorFragment& fragment)
 {
     if (m_selectorContext == SelectorContext::QuerySelector) {
@@ -1956,19 +2086,9 @@ void SelectorCodeGenerator::generateElementIsFirstChild(Assembler::JumpList& fai
     // Otherwise we need to apply setFirstChildState() on the RenderStyle.
     failureCases.append(m_assembler.branchTest32(Assembler::NonZero, isFirstChildRegister));
 
-    if (fragment.relationToRightFragment == FragmentRelation::Rightmost) {
-        LocalRegister childStyle(m_registerAllocator);
-        m_assembler.loadPtr(Assembler::Address(checkingContext, OBJECT_OFFSETOF(CheckingContext, elementStyle)), childStyle);
-
-        // FIXME: We should look into doing something smart in MacroAssembler instead.
-        LocalRegister flags(m_registerAllocator);
-        Assembler::Address flagAddress(childStyle, RenderStyle::noninheritedFlagsMemoryOffset() + RenderStyle::NonInheritedFlags::flagsMemoryOffset());
-        m_assembler.load64(flagAddress, flags);
-        LocalRegister isFirstChildStateFlagImmediate(m_registerAllocator);
-        m_assembler.move(Assembler::TrustedImm64(RenderStyle::NonInheritedFlags::setFirstChildStateFlags()), isFirstChildStateFlagImmediate);
-        m_assembler.or64(isFirstChildStateFlagImmediate, flags);
-        m_assembler.store64(flags, flagAddress);
-    } else {
+    if (fragment.relationToRightFragment == FragmentRelation::Rightmost)
+        addFlagsToElementStyleFromContext(checkingContext, RenderStyle::NonInheritedFlags::setFirstChildStateFlags());
+    else {
         FunctionCall functionCall(m_assembler, m_registerAllocator, m_stackAllocator, m_functionCalls);
         functionCall.setFunctionAddress(setFirstChildState);
         Assembler::RegisterID elementAddress = elementAddressRegister;
@@ -1980,6 +2100,51 @@ void SelectorCodeGenerator::generateElementIsFirstChild(Assembler::JumpList& fai
     failureCases.append(m_assembler.branchTest32(Assembler::NonZero, isFirstChildRegister));
 }
 
+static bool elementIsHovered(Element* element)
+{
+    return element->hovered() || InspectorInstrumentation::forcePseudoState(element, CSSSelector::PseudoClassHover);
+}
+
+static bool elementIsHoveredForStyleResolution(Element* element, const CheckingContext* checkingContext)
+{
+    if (checkingContext->resolvingMode == SelectorChecker::ResolvingStyle)
+        element->setChildrenAffectedByHover();
+    return element->hovered() || InspectorInstrumentation::forcePseudoState(element, CSSSelector::PseudoClassHover);
+}
+
+void SelectorCodeGenerator::generateElementIsHovered(Assembler::JumpList& failureCases, const SelectorFragment& fragment)
+{
+    generateSpecialFailureInQuirksModeForActiveAndHoverIfNeeded(failureCases, fragment);
+    if (m_selectorContext == SelectorContext::QuerySelector) {
+        FunctionCall functionCall(m_assembler, m_registerAllocator, m_stackAllocator, m_functionCalls);
+        functionCall.setFunctionAddress(elementIsHovered);
+        functionCall.setOneArgument(elementAddressRegister);
+        failureCases.append(functionCall.callAndBranchOnCondition(Assembler::Zero));
+    } else {
+        if (fragment.relationToRightFragment == FragmentRelation::Rightmost) {
+            LocalRegister checkingContext(m_registerAllocator);
+            Assembler::Jump notResolvingStyle = jumpIfNotResolvingStyle(checkingContext);
+            addFlagsToElementStyleFromContext(checkingContext, RenderStyle::NonInheritedFlags::flagIsaffectedByHover());
+            notResolvingStyle.link(&m_assembler);
+
+            FunctionCall functionCall(m_assembler, m_registerAllocator, m_stackAllocator, m_functionCalls);
+            functionCall.setFunctionAddress(elementIsHovered);
+            functionCall.setOneArgument(elementAddressRegister);
+            failureCases.append(functionCall.callAndBranchOnCondition(Assembler::Zero));
+        } else {
+            unsigned offsetToCheckingContext = m_stackAllocator.offsetToStackReference(m_checkingContextStackReference);
+            Assembler::RegisterID checkingContext = m_registerAllocator.allocateRegister();
+            m_assembler.loadPtr(Assembler::Address(Assembler::stackPointerRegister, offsetToCheckingContext), checkingContext);
+            m_registerAllocator.deallocateRegister(checkingContext);
+
+            FunctionCall functionCall(m_assembler, m_registerAllocator, m_stackAllocator, m_functionCalls);
+            functionCall.setFunctionAddress(elementIsHoveredForStyleResolution);
+            functionCall.setTwoArguments(elementAddressRegister, checkingContext);
+            failureCases.append(functionCall.callAndBranchOnCondition(Assembler::Zero));
+        }
+    }
+}
+
 Assembler::JumpList SelectorCodeGenerator::jumpIfNoNextAdjacentElement()
 {
     Assembler::JumpList successCase;
@@ -2038,19 +2203,9 @@ void SelectorCodeGenerator::generateElementIsLastChild(Assembler::JumpList& fail
     // Otherwise we need to apply setLastChildState() on the RenderStyle.
     failureCases.append(m_assembler.branchTest32(Assembler::NonZero, isLastChildRegister));
 
-    if (fragment.relationToRightFragment == FragmentRelation::Rightmost) {
-        LocalRegister childStyle(m_registerAllocator);
-        m_assembler.loadPtr(Assembler::Address(checkingContext, OBJECT_OFFSETOF(CheckingContext, elementStyle)), childStyle);
-
-        // FIXME: We should look into doing something smart in MacroAssembler instead.
-        LocalRegister flags(m_registerAllocator);
-        Assembler::Address flagAddress(childStyle, RenderStyle::noninheritedFlagsMemoryOffset() + RenderStyle::NonInheritedFlags::flagsMemoryOffset());
-        m_assembler.load64(flagAddress, flags);
-        LocalRegister isLastChildStateFlagImmediate(m_registerAllocator);
-        m_assembler.move(Assembler::TrustedImm64(RenderStyle::NonInheritedFlags::setLastChildStateFlags()), isLastChildStateFlagImmediate);
-        m_assembler.or64(isLastChildStateFlagImmediate, flags);
-        m_assembler.store64(flags, flagAddress);
-    } else {
+    if (fragment.relationToRightFragment == FragmentRelation::Rightmost)
+        addFlagsToElementStyleFromContext(checkingContext, RenderStyle::NonInheritedFlags::setLastChildStateFlags());
+    else {
         FunctionCall functionCall(m_assembler, m_registerAllocator, m_stackAllocator, m_functionCalls);
         functionCall.setFunctionAddress(setLastChildState);
         Assembler::RegisterID elementAddress = elementAddressRegister;
@@ -2123,19 +2278,9 @@ void SelectorCodeGenerator::generateElementIsOnlyChild(Assembler::JumpList& fail
     // Otherwise we need to apply setLastChildState() on the RenderStyle.
     failureCases.append(m_assembler.branchTest32(Assembler::NonZero, isOnlyChildRegister));
 
-    if (fragment.relationToRightFragment == FragmentRelation::Rightmost) {
-        LocalRegister childStyle(m_registerAllocator);
-        m_assembler.loadPtr(Assembler::Address(checkingContext, OBJECT_OFFSETOF(CheckingContext, elementStyle)), childStyle);
-
-        // FIXME: We should look into doing something smart in MacroAssembler instead.
-        LocalRegister flags(m_registerAllocator);
-        Assembler::Address flagAddress(childStyle, RenderStyle::noninheritedFlagsMemoryOffset() + RenderStyle::NonInheritedFlags::flagsMemoryOffset());
-        m_assembler.load64(flagAddress, flags);
-        LocalRegister isLastChildStateFlagImmediate(m_registerAllocator);
-        m_assembler.move(Assembler::TrustedImm64(RenderStyle::NonInheritedFlags::setFirstChildStateFlags() | RenderStyle::NonInheritedFlags::setLastChildStateFlags()), isLastChildStateFlagImmediate);
-        m_assembler.or64(isLastChildStateFlagImmediate, flags);
-        m_assembler.store64(flags, flagAddress);
-    } else {
+    if (fragment.relationToRightFragment == FragmentRelation::Rightmost)
+        addFlagsToElementStyleFromContext(checkingContext, RenderStyle::NonInheritedFlags::setFirstChildStateFlags() | RenderStyle::NonInheritedFlags::setLastChildStateFlags());
+    else {
         FunctionCall functionCall(m_assembler, m_registerAllocator, m_stackAllocator, m_functionCalls);
         functionCall.setFunctionAddress(setOnlyChildState);
         Assembler::RegisterID elementAddress = elementAddressRegister;
@@ -2304,16 +2449,7 @@ void SelectorCodeGenerator::generateElementIsNthChild(Assembler::JumpList& failu
         functionCall.call();
 
         if (fragment.relationToRightFragment == FragmentRelation::Rightmost) {
-            LocalRegister childStyle(m_registerAllocator);
-            m_assembler.loadPtr(Assembler::Address(checkingContext, OBJECT_OFFSETOF(CheckingContext, elementStyle)), childStyle);
-
-            LocalRegister flags(m_registerAllocator);
-            Assembler::Address flagAddress(childStyle, RenderStyle::noninheritedFlagsMemoryOffset() + RenderStyle::NonInheritedFlags::flagsMemoryOffset());
-            m_assembler.load64(flagAddress, flags);
-            LocalRegister isUniqueFlagImmediate(m_registerAllocator);
-            m_assembler.move(Assembler::TrustedImm64(RenderStyle::NonInheritedFlags::flagIsUnique()), isUniqueFlagImmediate);
-            m_assembler.or64(isUniqueFlagImmediate, flags);
-            m_assembler.store64(flags, flagAddress);
+            addFlagsToElementStyleFromContext(checkingContext, RenderStyle::NonInheritedFlags::flagIsUnique());
 
             Assembler::RegisterID elementAddress = elementAddressRegister;
             FunctionCall functionCall(m_assembler, m_registerAllocator, m_stackAllocator, m_functionCalls);
index 9908e6e..00c9fb7 100644 (file)
@@ -419,7 +419,7 @@ Document::Document(Frame* frame, const URL& url, unsigned documentClasses, unsig
     , m_printing(false)
     , m_paginatedForScreen(false)
     , m_ignoreAutofocus(false)
-    , m_compatibilityMode(NoQuirksMode)
+    , m_compatibilityMode(DocumentCompatibilityMode::NoQuirksMode)
     , m_compatibilityModeLocked(false)
     , m_textColor(Color::black)
     , m_domTreeVersion(++s_globalTreeVersion)
@@ -766,7 +766,7 @@ MediaQueryMatcher& Document::mediaQueryMatcher()
     return *m_mediaQueryMatcher;
 }
 
-void Document::setCompatibilityMode(CompatibilityMode mode)
+void Document::setCompatibilityMode(DocumentCompatibilityMode mode)
 {
     if (m_compatibilityModeLocked || mode == m_compatibilityMode)
         return;
@@ -2261,7 +2261,7 @@ void Document::implicitOpen()
 
     removeChildren();
 
-    setCompatibilityMode(NoQuirksMode);
+    setCompatibilityMode(DocumentCompatibilityMode::NoQuirksMode);
 
     m_parser = createParser();
     setParsing(true);
@@ -3129,7 +3129,7 @@ void Document::cloneDataFromDocument(const Document& other)
     m_baseURLOverride = other.baseURLOverride();
     m_documentURI = other.documentURI();
 
-    setCompatibilityMode(other.compatibilityMode());
+    setCompatibilityMode(other.m_compatibilityMode);
     setSecurityOrigin(other.securityOrigin());
     setDecoder(other.decoder());
 }
index b6fc9da..781b935 100644 (file)
@@ -248,6 +248,12 @@ enum DocumentClass {
 
 typedef unsigned char DocumentClassFlags;
 
+enum class DocumentCompatibilityMode : unsigned char {
+    NoQuirksMode = 1,
+    QuirksMode = 1 << 1,
+    LimitedQuirksMode = 1 << 2
+};
+
 class Document : public ContainerNode, public TreeScope, public ScriptExecutionContext {
 public:
     static PassRefPtr<Document> create(Frame* frame, const URL& url)
@@ -686,17 +692,15 @@ public:
     
     bool paginated() const { return printing() || paginatedForScreen(); }
 
-    enum CompatibilityMode { QuirksMode, LimitedQuirksMode, NoQuirksMode };
-
-    void setCompatibilityMode(CompatibilityMode m);
+    void setCompatibilityMode(DocumentCompatibilityMode);
     void lockCompatibilityMode() { m_compatibilityModeLocked = true; }
-    CompatibilityMode compatibilityMode() const { return m_compatibilityMode; }
+    static ptrdiff_t compatibilityModeMemoryOffset() { return OBJECT_OFFSETOF(Document, m_compatibilityMode); }
 
     String compatMode() const;
 
-    bool inQuirksMode() const { return m_compatibilityMode == QuirksMode; }
-    bool inLimitedQuirksMode() const { return m_compatibilityMode == LimitedQuirksMode; }
-    bool inNoQuirksMode() const { return m_compatibilityMode == NoQuirksMode; }
+    bool inQuirksMode() const { return m_compatibilityMode == DocumentCompatibilityMode::QuirksMode; }
+    bool inLimitedQuirksMode() const { return m_compatibilityMode == DocumentCompatibilityMode::LimitedQuirksMode; }
+    bool inNoQuirksMode() const { return m_compatibilityMode == DocumentCompatibilityMode::NoQuirksMode; }
 
     enum ReadyState {
         Loading,
@@ -1412,7 +1416,7 @@ private:
 
     bool m_ignoreAutofocus;
 
-    CompatibilityMode m_compatibilityMode;
+    DocumentCompatibilityMode m_compatibilityMode;
     bool m_compatibilityModeLocked; // This is cheaper than making setCompatibilityMode virtual.
 
     Color m_textColor;
index 8abbf5b..b9c8a99 100644 (file)
@@ -198,7 +198,7 @@ ImageDocument::ImageDocument(Frame& frame, const URL& url)
 #endif
     , m_shouldShrinkImage(frame.settings().shrinksStandaloneImagesToFit() && frame.isMainFrame())
 {
-    setCompatibilityMode(QuirksMode);
+    setCompatibilityMode(DocumentCompatibilityMode::QuirksMode);
     lockCompatibilityMode();
 }
     
index f22fc5b..5b6aaa2 100644 (file)
@@ -129,7 +129,7 @@ MediaDocument::MediaDocument(Frame* frame, const URL& url)
     : HTMLDocument(frame, url, MediaDocumentClass)
     , m_replaceMediaElementTimer(this, &MediaDocument::replaceMediaElementTimerFired)
 {
-    setCompatibilityMode(QuirksMode);
+    setCompatibilityMode(DocumentCompatibilityMode::QuirksMode);
     lockCompatibilityMode();
 }
 
index 73e6ed4..f716b27 100644 (file)
@@ -144,7 +144,7 @@ PluginDocument::PluginDocument(Frame* frame, const URL& url)
     : HTMLDocument(frame, url, PluginDocumentClass)
     , m_shouldLoadPluginManually(true)
 {
-    setCompatibilityMode(QuirksMode);
+    setCompatibilityMode(DocumentCompatibilityMode::QuirksMode);
     lockCompatibilityMode();
 }
 
index 0923f21..47b69c7 100644 (file)
@@ -32,7 +32,7 @@ namespace WebCore {
 TextDocument::TextDocument(Frame* frame, const URL& url)
     : HTMLDocument(frame, url, TextDocumentClass)
 {
-    setCompatibilityMode(QuirksMode);
+    setCompatibilityMode(DocumentCompatibilityMode::QuirksMode);
     lockCompatibilityMode();
 }
 
index 0cfe602..cba6a5f 100644 (file)
@@ -293,12 +293,12 @@ void HTMLConstructionSite::setDefaultCompatibilityMode()
         return;
     if (m_document->isSrcdocDocument())
         return;
-    setCompatibilityMode(Document::QuirksMode);
+    setCompatibilityMode(DocumentCompatibilityMode::QuirksMode);
 }
 
-void HTMLConstructionSite::setCompatibilityMode(Document::CompatibilityMode mode)
+void HTMLConstructionSite::setCompatibilityMode(DocumentCompatibilityMode mode)
 {
-    m_inQuirksMode = (mode == Document::QuirksMode);
+    m_inQuirksMode = (mode == DocumentCompatibilityMode::QuirksMode);
     m_document->setCompatibilityMode(mode);
 }
 
@@ -373,7 +373,7 @@ void HTMLConstructionSite::setCompatibilityModeFromDoctype(const String& name, c
         || equalIgnoringCase(systemId, "http://www.ibm.com/data/dtd/v11/ibmxhtml1-transitional.dtd")
         || (systemId.isEmpty() && publicId.startsWith("-//W3C//DTD HTML 4.01 Frameset//", false))
         || (systemId.isEmpty() && publicId.startsWith("-//W3C//DTD HTML 4.01 Transitional//", false))) {
-        setCompatibilityMode(Document::QuirksMode);
+        setCompatibilityMode(DocumentCompatibilityMode::QuirksMode);
         return;
     }
 
@@ -382,12 +382,12 @@ void HTMLConstructionSite::setCompatibilityModeFromDoctype(const String& name, c
         || publicId.startsWith("-//W3C//DTD XHTML 1.0 Transitional//", false)
         || (!systemId.isEmpty() && publicId.startsWith("-//W3C//DTD HTML 4.01 Frameset//", false))
         || (!systemId.isEmpty() && publicId.startsWith("-//W3C//DTD HTML 4.01 Transitional//", false))) {
-        setCompatibilityMode(Document::LimitedQuirksMode);
+        setCompatibilityMode(DocumentCompatibilityMode::LimitedQuirksMode);
         return;
     }
 
     // Otherwise we are No Quirks Mode.
-    setCompatibilityMode(Document::NoQuirksMode);
+    setCompatibilityMode(DocumentCompatibilityMode::NoQuirksMode);
 }
 
 void HTMLConstructionSite::finishedParsing()
@@ -414,7 +414,7 @@ void HTMLConstructionSite::insertDoctype(AtomicHTMLToken* token)
         return;
 
     if (token->forceQuirks())
-        setCompatibilityMode(Document::QuirksMode);
+        setCompatibilityMode(DocumentCompatibilityMode::QuirksMode);
     else {
         setCompatibilityModeFromDoctype(token->name(), publicId, systemId);
     }
index 8709fc1..1bcff8d 100644 (file)
@@ -187,7 +187,7 @@ private:
     // tokens produce only one DOM mutation.
     typedef Vector<HTMLConstructionSiteTask, 1> TaskQueue;
 
-    void setCompatibilityMode(Document::CompatibilityMode);
+    void setCompatibilityMode(DocumentCompatibilityMode);
     void setCompatibilityModeFromDoctype(const String& name, const String& publicId, const String& systemId);
 
     void attachLater(ContainerNode* parent, PassRefPtr<Node> child, bool selfClosing = false);
index faf79af..4d10d37 100644 (file)
@@ -77,7 +77,7 @@ void DocumentWriter::replaceDocument(const String& source, Document* ownerDocume
     if (!source.isNull()) {
         if (!m_hasReceivedSomeData) {
             m_hasReceivedSomeData = true;
-            m_frame->document()->setCompatibilityMode(Document::NoQuirksMode);
+            m_frame->document()->setCompatibilityMode(DocumentCompatibilityMode::NoQuirksMode);
         }
 
         // FIXME: This should call DocumentParser::appendBytes instead of append
index 71534ec..6c55032 100644 (file)
@@ -52,7 +52,7 @@ private:
 SinkDocument::SinkDocument(Frame* frame, const URL& url)
     : HTMLDocument(frame, url)
 {
-    setCompatibilityMode(QuirksMode);
+    setCompatibilityMode(DocumentCompatibilityMode::QuirksMode);
     lockCompatibilityMode();
 }
 
index 776af17..245a999 100644 (file)
@@ -84,7 +84,7 @@ void Frame::initWithSimpleHTMLDocument(const String& style, const URL& url)
     m_loader.initForSynthesizedDocument(url);
 
     RefPtr<HTMLDocument> document = HTMLDocument::createSynthesizedDocument(this, url);
-    document->setCompatibilityMode(Document::LimitedQuirksMode);
+    document->setCompatibilityMode(DocumentCompatibilityMode::LimitedQuirksMode);
     document->createDOMWindow();
     setDocument(document);
 
index 4b6840d..1ec21fe 100644 (file)
@@ -283,6 +283,8 @@ public:
 
         static ptrdiff_t flagsMemoryOffset() { return OBJECT_OFFSETOF(NonInheritedFlags, m_flags); }
         static uint64_t flagIsUnique() { return oneBitMask << isUniqueOffset; }
+        static uint64_t flagIsaffectedByActive() { return oneBitMask << affectedByActiveOffset; }
+        static uint64_t flagIsaffectedByHover() { return oneBitMask << affectedByHoverOffset; }
         static uint64_t setFirstChildStateFlags() { return flagFirstChildState() | flagIsUnique(); }
         static uint64_t setLastChildStateFlags() { return flagLastChildState() | flagIsUnique(); }
     private: