Source/WebCore:
authorantti@apple.com <antti@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 7 Apr 2016 10:11:39 +0000 (10:11 +0000)
committerantti@apple.com <antti@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 7 Apr 2016 10:11:39 +0000 (10:11 +0000)
Shadow DOM: Implement display: contents for slots
https://bugs.webkit.org/show_bug.cgi?id=149439
<rdar://problem/22731922>

Reviewed by Ryosuke Niwa.

This patch adds support for value 'contents' of the 'display' property for <slot> elements only. The value is ignored
for other elements for now. With this display value the element does not generate a box for itself but its descendants
generate them normally.

Slots already have implicit "display: contents". With this patch the value comes from the user agent stylesheet and can
be overriden by the author.

* css/CSSParser.cpp:
(WebCore::isValidKeywordPropertyAndValue):
* css/CSSPrimitiveValueMappings.h:
(WebCore::CSSPrimitiveValue::CSSPrimitiveValue):
* css/CSSValueKeywords.in:

    Suport parsing display: contents.

* css/StyleResolver.cpp:
(WebCore::equivalentBlockDisplay):
(WebCore::StyleResolver::adjustRenderStyle):

    Disallow for non-slots for now.

* css/html.css:
(slot):

    Add "slot { display: contents }" to the UA sheet.

* dom/Element.cpp:
(WebCore::Element::resolveStyle):
(WebCore::Element::hasDisplayContents):
(WebCore::Element::setHasDisplayContents):

    Add a rare data bit for elements with display:contents (as we don't save the RenderStyle for them).

(WebCore::Element::rendererIsNeeded):

    Don't need renderer for display:contents.

(WebCore::Element::createElementRenderer):
* dom/Element.h:
(WebCore::Element::isVisibleInViewportChanged):
* dom/ElementAndTextDescendantIterator.h:
(WebCore::ElementAndTextDescendantIterator::operator!):
(WebCore::ElementAndTextDescendantIterator::operator bool):
(WebCore::ElementAndTextDescendantIterator::ElementAndTextDescendantIterator):
(WebCore::ElementAndTextDescendantIterator::operator==):
(WebCore::ElementAndTextDescendantIterator::operator!=):

    Support initializing ElementAndTextDescendantIterator with root==current so that m_current is not nulled.
    This is needed for ComposedTreeIterator to be initialized correctly when root is a slot and the current node
    is a slotted node. The case happens in RenderTreePosition::previousSiblingRenderer when slot display is overriden
    to something else than 'contents'.

* dom/ElementRareData.h:
(WebCore::ElementRareData::hasDisplayContents):
(WebCore::ElementRareData::setHasDisplayContents):
(WebCore::ElementRareData::ElementRareData):
* rendering/RenderElement.cpp:
(WebCore::RenderElement::createFor):
* rendering/style/RenderStyleConstants.h:
* style/RenderTreePosition.cpp:
(WebCore::RenderTreePosition::nextSiblingRenderer):

    Test for dynamic display:contents.

* style/RenderTreeUpdater.cpp:
(WebCore::findRenderingRoot):
(WebCore::RenderTreeUpdater::updateRenderTree):
(WebCore::RenderTreeUpdater::updateElementRenderer):

    Test for dynamic display:contents.

* style/StyleTreeResolver.cpp:
(WebCore::Style::affectsRenderedSubtree):

    No need for special case.

(WebCore::Style::TreeResolver::resolveComposedTree):

    Test for dynamic display:contents.

LayoutTests:
REGRESSION (r188591): thingiverse.com direct messaging UI is not rendered properly
https://bugs.webkit.org/show_bug.cgi?id=156241
<rdar://problem/25262213>

Patch by Myles C. Maxfield <mmaxfield@apple.com> on 2016-04-06
Reviewed by Simon Fraser.

* fast/text/zero-sized-fonts-expected.txt: Added.
* fast/text/zero-sized-fonts.html: Added.

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

18 files changed:
LayoutTests/ChangeLog
LayoutTests/platform/mac/TestExpectations
Source/WebCore/ChangeLog
Source/WebCore/css/CSSParser.cpp
Source/WebCore/css/CSSPrimitiveValueMappings.h
Source/WebCore/css/CSSValueKeywords.in
Source/WebCore/css/StyleResolver.cpp
Source/WebCore/css/html.css
Source/WebCore/dom/Element.cpp
Source/WebCore/dom/Element.h
Source/WebCore/dom/ElementAndTextDescendantIterator.h
Source/WebCore/dom/ElementRareData.h
Source/WebCore/html/HTMLSlotElement.h
Source/WebCore/rendering/RenderElement.cpp
Source/WebCore/rendering/style/RenderStyleConstants.h
Source/WebCore/style/RenderTreePosition.cpp
Source/WebCore/style/RenderTreeUpdater.cpp
Source/WebCore/style/StyleTreeResolver.cpp

index 2d7056a..fb3d40c 100644 (file)
 
 2016-04-06  Antti Koivisto  <antti@apple.com>
 
+        Shadow DOM: Implement display: contents for slots
+        https://bugs.webkit.org/show_bug.cgi?id=149439
+        <rdar://problem/22731922>
+
+        Reviewed by Ryosuke Niwa.
+
+        * platform/mac/TestExpectations:
+
+        Enable fast/shadow-dom/css-scoping-shadow-slot-display-override.html, the test for overriding slot display value.
+
+2016-04-06  Antti Koivisto  <antti@apple.com>
+
         REGRESSION(r196629): Messages text size only changes for sending text, conversation text size does not change
         https://bugs.webkit.org/show_bug.cgi?id=156287
         <rdar://problem/24264756>
index bdb748c..b39fc11 100644 (file)
@@ -1229,7 +1229,6 @@ webkit.org/b/149128 fast/text/control-characters [ ImageOnlyFailure ]
 
 webkit.org/b/148695 fast/shadow-dom [ Pass ]
 webkit.org/b/149440 fast/shadow-dom/css-scoping-shadow-host-functional-rule.html [ ImageOnlyFailure ]
-webkit.org/b/149441 fast/shadow-dom/css-scoping-shadow-slot-display-override.html [ ImageOnlyFailure ]
 
 # Touch events is not enabled on Mac
 webkit.org/b/149592 fast/shadow-dom/touch-event-ios.html [ Failure ]
index ee6900d..8dabcf0 100644 (file)
@@ -1,3 +1,91 @@
+2016-04-06  Antti Koivisto  <antti@apple.com>
+
+        Shadow DOM: Implement display: contents for slots
+        https://bugs.webkit.org/show_bug.cgi?id=149439
+        <rdar://problem/22731922>
+
+        Reviewed by Ryosuke Niwa.
+
+        This patch adds support for value 'contents' of the 'display' property for <slot> elements only. The value is ignored
+        for other elements for now. With this display value the element does not generate a box for itself but its descendants
+        generate them normally.
+
+        Slots already have implicit "display: contents". With this patch the value comes from the user agent stylesheet and can
+        be overriden by the author.
+
+        * css/CSSParser.cpp:
+        (WebCore::isValidKeywordPropertyAndValue):
+        * css/CSSPrimitiveValueMappings.h:
+        (WebCore::CSSPrimitiveValue::CSSPrimitiveValue):
+        * css/CSSValueKeywords.in:
+
+            Suport parsing display: contents.
+
+        * css/StyleResolver.cpp:
+        (WebCore::equivalentBlockDisplay):
+        (WebCore::StyleResolver::adjustRenderStyle):
+
+            Disallow for non-slots for now.
+
+        * css/html.css:
+        (slot):
+
+            Add "slot { display: contents }" to the UA sheet.
+
+        * dom/Element.cpp:
+        (WebCore::Element::resolveStyle):
+        (WebCore::Element::hasDisplayContents):
+        (WebCore::Element::setHasDisplayContents):
+
+            Add a rare data bit for elements with display:contents (as we don't save the RenderStyle for them).
+
+        (WebCore::Element::rendererIsNeeded):
+
+            Don't need renderer for display:contents.
+
+        (WebCore::Element::createElementRenderer):
+        * dom/Element.h:
+        (WebCore::Element::isVisibleInViewportChanged):
+        * dom/ElementAndTextDescendantIterator.h:
+        (WebCore::ElementAndTextDescendantIterator::operator!):
+        (WebCore::ElementAndTextDescendantIterator::operator bool):
+        (WebCore::ElementAndTextDescendantIterator::ElementAndTextDescendantIterator):
+        (WebCore::ElementAndTextDescendantIterator::operator==):
+        (WebCore::ElementAndTextDescendantIterator::operator!=):
+
+            Support initializing ElementAndTextDescendantIterator with root==current so that m_current is not nulled.
+            This is needed for ComposedTreeIterator to be initialized correctly when root is a slot and the current node
+            is a slotted node. The case happens in RenderTreePosition::previousSiblingRenderer when slot display is overriden
+            to something else than 'contents'.
+
+        * dom/ElementRareData.h:
+        (WebCore::ElementRareData::hasDisplayContents):
+        (WebCore::ElementRareData::setHasDisplayContents):
+        (WebCore::ElementRareData::ElementRareData):
+        * rendering/RenderElement.cpp:
+        (WebCore::RenderElement::createFor):
+        * rendering/style/RenderStyleConstants.h:
+        * style/RenderTreePosition.cpp:
+        (WebCore::RenderTreePosition::nextSiblingRenderer):
+
+            Test for dynamic display:contents.
+
+        * style/RenderTreeUpdater.cpp:
+        (WebCore::findRenderingRoot):
+        (WebCore::RenderTreeUpdater::updateRenderTree):
+        (WebCore::RenderTreeUpdater::updateElementRenderer):
+
+            Test for dynamic display:contents.
+
+        * style/StyleTreeResolver.cpp:
+        (WebCore::Style::affectsRenderedSubtree):
+
+            No need for special case.
+
+        (WebCore::Style::TreeResolver::resolveComposedTree):
+
+            Test for dynamic display:contents.
+
 2016-04-06  Myles C. Maxfield  <mmaxfield@apple.com>
 
         REGRESSION (r188591): thingiverse.com direct messaging UI is not rendered properly
index 496f4f9..c60711a 100644 (file)
@@ -669,8 +669,8 @@ static inline bool isValidKeywordPropertyAndValue(CSSPropertyID propertyId, int
         // inline | block | list-item | inline-block | table |
         // inline-table | table-row-group | table-header-group | table-footer-group | table-row |
         // table-column-group | table-column | table-cell | table-caption | -webkit-box | -webkit-inline-box | none | inherit
-        // flex | -webkit-flex | inline-flex | -webkit-inline-flex | -webkit-grid | -webkit-inline-grid
-        if ((valueID >= CSSValueInline && valueID <= CSSValueWebkitInlineFlex) || valueID == CSSValueNone)
+        // flex | -webkit-flex | inline-flex | -webkit-inline-flex | -webkit-grid | -webkit-inline-grid | contents
+        if ((valueID >= CSSValueInline && valueID <= CSSValueContents) || valueID == CSSValueNone)
             return true;
 #if ENABLE(CSS_GRID_LAYOUT)
         if (valueID == CSSValueWebkitGrid || valueID == CSSValueWebkitInlineGrid)
index 5b40d20..3478818 100644 (file)
@@ -1388,6 +1388,9 @@ template<> inline CSSPrimitiveValue::CSSPrimitiveValue(EDisplay e)
     case NONE:
         m_value.valueID = CSSValueNone;
         break;
+    case CONTENTS:
+        m_value.valueID = CSSValueContents;
+        break;
     }
 }
 
index 396f7f5..fb79bf2 100644 (file)
@@ -417,6 +417,7 @@ flex
 -webkit-flex
 inline-flex
 -webkit-inline-flex
+contents
 -webkit-grid
 -webkit-inline-grid
 //none
@@ -1184,7 +1185,7 @@ style
 
 // will-change
 scroll-position
-contents
+//contents
 
 #if defined(ENABLE_TOUCH_EVENTS) && ENABLE_TOUCH_EVENTS
 // touch-action
index f2f2852..58475a2 100644 (file)
@@ -723,6 +723,7 @@ static EDisplay equivalentBlockDisplay(EDisplay display, bool isFloating, bool s
     case TABLE_COLUMN:
     case TABLE_CELL:
     case TABLE_CAPTION:
+    case CONTENTS:
         return BLOCK;
     case NONE:
         ASSERT_NOT_REACHED();
@@ -765,6 +766,15 @@ void StyleResolver::adjustRenderStyle(RenderStyle& style, const RenderStyle& par
     style.setOriginalDisplay(style.display());
 
     if (style.display() != NONE) {
+        if (style.display() == CONTENTS) {
+            // FIXME: Enable for all elements.
+            bool elementSupportsDisplayContents = false;
+#if ENABLE(SHADOW_DOM) || ENABLE(DETAILS_ELEMENT)
+            elementSupportsDisplayContents = is<HTMLSlotElement>(e);
+#endif
+            if (!elementSupportsDisplayContents)
+                style.setDisplay(INLINE);
+        }
         // If we have a <td> that specifies a float property, in quirks mode we just drop the float
         // property.
         // Sites also commonly use display:inline/block on <td>s and <table>s. In quirks mode we force
index 2b63518..e756b6c 100644 (file)
@@ -1223,6 +1223,10 @@ bdo {
     unicode-bidi: bidi-override;
 }
 
+slot {
+    display: contents;
+}
+
 #if defined(WTF_PLATFORM_IOS) && WTF_PLATFORM_IOS
 applet, embed, object, img {
     -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
index 0e0949d..82cde2c 100644 (file)
@@ -1375,6 +1375,18 @@ ElementStyle Element::resolveStyle(RenderStyle* parentStyle)
     return styleResolver().styleForElement(*this, parentStyle);
 }
 
+bool Element::hasDisplayContents() const
+{
+    return hasRareData() && elementRareData()->hasDisplayContents();
+}
+
+void Element::setHasDisplayContents(bool value)
+{
+    if (hasDisplayContents() == value)
+        return;
+    ensureElementRareData().setHasDisplayContents(value);
+}
+
 // Returns true is the given attribute is an event handler.
 // We consider an event handler any attribute that begins with "on".
 // It is a simple solution that has the advantage of not requiring any
@@ -1478,7 +1490,7 @@ const AtomicString& Element::imageSourceURL() const
 
 bool Element::rendererIsNeeded(const RenderStyle& style)
 {
-    return style.display() != NONE;
+    return style.display() != NONE && style.display() != CONTENTS;
 }
 
 RenderPtr<RenderElement> Element::createElementRenderer(Ref<RenderStyle>&& style, const RenderTreePosition&)
index e7ddf82..e16f7de 100644 (file)
@@ -501,6 +501,9 @@ public:
     StyleResolver& styleResolver();
     ElementStyle resolveStyle(RenderStyle* parentStyle);
 
+    bool hasDisplayContents() const;
+    void setHasDisplayContents(bool);
+
     virtual void isVisibleInViewportChanged() { }
 
     using ContainerNode::setAttributeEventListener;
index 3bfcd03..f83afe8 100644 (file)
@@ -50,8 +50,8 @@ public:
     bool operator==(const ElementAndTextDescendantIterator& other) const;
     bool operator!=(const ElementAndTextDescendantIterator& other) const;
 
-    bool operator!() const { return !m_current; }
-    explicit operator bool() const { return m_current; }
+    bool operator!() const { return !m_depth; }
+    explicit operator bool() const { return m_depth; }
 
     void dropAssertions();
 
@@ -115,7 +115,7 @@ inline ElementAndTextDescendantIterator::ElementAndTextDescendantIterator(Contai
 }
 
 inline ElementAndTextDescendantIterator::ElementAndTextDescendantIterator(ContainerNode& root, Node* current)
-    : m_current(current != &root ? current : nullptr)
+    : m_current(current)
 #if !ASSERT_DISABLED
     , m_assertions(m_current)
 #endif
@@ -123,6 +123,8 @@ inline ElementAndTextDescendantIterator::ElementAndTextDescendantIterator(Contai
     if (!m_current)
         return;
     ASSERT(isElementOrText(*m_current));
+    if (m_current == &root)
+        return;
 
     Vector<Node*, 20> ancestorStack;
     auto* ancestor = m_current->parentNode();
@@ -285,7 +287,7 @@ inline const Node* ElementAndTextDescendantIterator::operator->() const
 inline bool ElementAndTextDescendantIterator::operator==(const ElementAndTextDescendantIterator& other) const
 {
     ASSERT(!m_assertions.domTreeHasMutated());
-    return m_current == other.m_current;
+    return m_current == other.m_current || (!m_depth && !other.m_depth);
 }
 
 inline bool ElementAndTextDescendantIterator::operator!=(const ElementAndTextDescendantIterator& other) const
index 598391a..7d7c7fc 100644 (file)
@@ -107,6 +107,9 @@ public:
     bool hasPendingResources() const { return m_hasPendingResources; }
     void setHasPendingResources(bool has) { m_hasPendingResources = has; }
 
+    bool hasDisplayContents() const { return m_hasDisplayContents; }
+    void setHasDisplayContents(bool value) { m_hasDisplayContents = value; }
+
 private:
     int m_tabIndex;
     unsigned short m_childIndex;
@@ -126,6 +129,7 @@ private:
     unsigned m_childrenAffectedByLastChildRules : 1;
     unsigned m_childrenAffectedByBackwardPositionalRules : 1;
     unsigned m_childrenAffectedByPropertyBasedBackwardPositionalRules : 1;
+    unsigned m_hasDisplayContents : 1;
 
     RegionOversetState m_regionOversetState;
 
@@ -166,6 +170,7 @@ inline ElementRareData::ElementRareData(RenderElement* renderer)
     , m_childrenAffectedByLastChildRules(false)
     , m_childrenAffectedByBackwardPositionalRules(false)
     , m_childrenAffectedByPropertyBasedBackwardPositionalRules(false)
+    , m_hasDisplayContents(false)
     , m_regionOversetState(RegionUndefined)
     , m_minimumSizeForResizing(defaultMinimumSizeForResizing())
 {
@@ -193,6 +198,7 @@ inline void ElementRareData::setAfterPseudoElement(RefPtr<PseudoElement>&& pseud
 inline void ElementRareData::resetComputedStyle()
 {
     m_computedStyle = nullptr;
+    m_hasDisplayContents = false;
     setStyleAffectedByEmpty(false);
     setChildIndex(0);
 }
index c6ac29d..8a4484e 100644 (file)
@@ -53,17 +53,6 @@ private:
     bool m_hasEnqueuedSlotChangeEvent { false };
 };
 
-// Slots have implicit display:contents until it is supported for reals.
-inline bool hasImplicitDisplayContents(const Element& element) { return is<HTMLSlotElement>(element); }
-
-}
-
-#else
-
-namespace WebCore {
-
-inline bool hasImplicitDisplayContents(const Element&) { return false; }
-
 }
 
 #endif
index 3220ddb..b16ffc2 100644 (file)
@@ -161,6 +161,7 @@ RenderPtr<RenderElement> RenderElement::createFor(Element& element, Ref<RenderSt
 
     switch (style.get().display()) {
     case NONE:
+    case CONTENTS:
         return nullptr;
     case INLINE:
         return createRenderer<RenderInline>(element, WTFMove(style));
index b9b0dce..eb706f6 100644 (file)
@@ -563,6 +563,7 @@ enum EDisplay {
     TABLE_COLUMN_GROUP, TABLE_COLUMN, TABLE_CELL,
     TABLE_CAPTION, BOX, INLINE_BOX,
     FLEX, WEBKIT_FLEX, INLINE_FLEX, WEBKIT_INLINE_FLEX,
+    CONTENTS,
 #if ENABLE(CSS_GRID_LAYOUT)
     GRID, INLINE_GRID,
 #endif
index ca50036..82e7f2a 100644 (file)
@@ -89,7 +89,7 @@ RenderObject* RenderTreePosition::nextSiblingRenderer(const Node& node) const
 
     while (it != end) {
         auto& node = *it;
-        bool hasDisplayContents = is<Element>(node) && hasImplicitDisplayContents(downcast<Element>(node));
+        bool hasDisplayContents = is<Element>(node) && downcast<Element>(node).hasDisplayContents();
         if (hasDisplayContents) {
             it.traverseNext();
             continue;
index ebecfdc..1007c7c 100644 (file)
@@ -68,7 +68,7 @@ static ContainerNode* findRenderingRoot(ContainerNode& node)
     for (auto& ancestor : composedTreeAncestors(node)) {
         if (ancestor.renderer())
             return &ancestor;
-        if (!hasImplicitDisplayContents(ancestor))
+        if (!ancestor.hasDisplayContents())
             return nullptr;
     }
     return &node.document();
@@ -151,7 +151,7 @@ void RenderTreeUpdater::updateRenderTree(ContainerNode& root)
 
         updateElementRenderer(element, *elementUpdate);
 
-        bool mayHaveRenderedDescendants = element.renderer() || (hasImplicitDisplayContents(element) && shouldCreateRenderer(element, renderTreePosition().parent()));
+        bool mayHaveRenderedDescendants = element.renderer() || (element.hasDisplayContents() && shouldCreateRenderer(element, renderTreePosition().parent()));
         if (!mayHaveRenderedDescendants) {
             it.traverseNextSkippingChildren();
             continue;
@@ -244,7 +244,10 @@ void RenderTreeUpdater::updateElementRenderer(Element& element, const Style::Ele
     if (shouldTearDownRenderers)
         tearDownRenderers(element, TeardownType::KeepHoverAndActive);
 
-    bool shouldCreateNewRenderer = !element.renderer() && update.style && !hasImplicitDisplayContents(element);
+    bool hasDisplayContest = update.style && update.style->display() == CONTENTS;
+    element.setHasDisplayContents(hasDisplayContest);
+
+    bool shouldCreateNewRenderer = !element.renderer() && update.style && !hasDisplayContest;
     if (shouldCreateNewRenderer) {
         if (element.hasCustomStyleResolveCallbacks())
             element.willAttachRenderers();
index b16125f..19e1ecd 100644 (file)
@@ -172,9 +172,6 @@ static bool affectsRenderedSubtree(Element& element, const RenderStyle& newStyle
         return true;
     if (newStyle.display() != NONE)
         return true;
-    // FIXME: Make 'contents' an actual display property value.
-    if (hasImplicitDisplayContents(element))
-        return true;
     if (element.rendererIsNeeded(newStyle))
         return true;
     if (element.shouldMoveToFlowThread(newStyle))
@@ -376,7 +373,7 @@ void TreeResolver::resolveComposedTree()
         ElementUpdate update;
         update.style = element.renderStyle();
 
-        bool shouldResolve = parent.change >= Inherit || element.needsStyleRecalc() || shouldResolveForPseudoElement || affectedByPreviousSibling || hasImplicitDisplayContents(element);
+        bool shouldResolve = parent.change >= Inherit || element.needsStyleRecalc() || shouldResolveForPseudoElement || affectedByPreviousSibling || element.hasDisplayContents();
         if (shouldResolve) {
 #if PLATFORM(IOS)
             CheckForVisibilityChangeOnRecalcStyle checkForVisibilityChange(&element, element.renderStyle());