[LFC][IFC] Add support for intrinsic width calculation
authorzalan@apple.com <zalan@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 2 Nov 2018 14:23:54 +0000 (14:23 +0000)
committerzalan@apple.com <zalan@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 2 Nov 2018 14:23:54 +0000 (14:23 +0000)
https://bugs.webkit.org/show_bug.cgi?id=191144

Reviewed by Antti Koivisto.

Source/WebCore:

This is the inline formatting version of the shrink-to-fit computation. It generates inline runs
and uses InlineLineBreaker to compute min/max width. This is very similar to ::layout.

Test: fast/inline/simple-shrink-to-fit-inline-block.html

* layout/inlineformatting/InlineFormattingContext.cpp:
(WebCore::Layout::InlineFormattingContext::layout const):
(WebCore::Layout::InlineFormattingContext::computeWidthAndMargin const):
(WebCore::Layout::InlineFormattingContext::computeHeightAndMargin const):
(WebCore::Layout::InlineFormattingContext::layoutFormattingContextRoot const):
(WebCore::Layout::InlineFormattingContext::computeWidthAndHeightForReplacedInlineBox const):
(WebCore::Layout::InlineFormattingContext::collectInlineContent const):
(WebCore::Layout::InlineFormattingContext::instrinsicWidthConstraints const):
(WebCore::Layout::InlineFormattingContext::computeWidthAndHeightForInlineBox const): Deleted.
* layout/inlineformatting/InlineFormattingContext.h:

Tools:

* LayoutReloaded/misc/LFC-passing-tests.txt:

LayoutTests:

* fast/inline/simple-shrink-to-fit-inline-block-expected.txt: Added.
* fast/inline/simple-shrink-to-fit-inline-block.html: Added.
* platform/ios/TestExpectations:

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

LayoutTests/ChangeLog
LayoutTests/fast/inline/simple-shrink-to-fit-inline-block-expected.txt [new file with mode: 0644]
LayoutTests/fast/inline/simple-shrink-to-fit-inline-block.html [new file with mode: 0644]
LayoutTests/platform/ios/TestExpectations
Source/WebCore/ChangeLog
Source/WebCore/layout/inlineformatting/InlineFormattingContext.cpp
Source/WebCore/layout/inlineformatting/InlineFormattingContext.h
Tools/ChangeLog
Tools/LayoutReloaded/misc/LFC-passing-tests.txt

index d3b2bfb..1b3b536 100644 (file)
@@ -1,3 +1,14 @@
+2018-11-02  Zalan Bujtas  <zalan@apple.com>
+
+        [LFC][IFC] Add support for intrinsic width calculation
+        https://bugs.webkit.org/show_bug.cgi?id=191144
+
+        Reviewed by Antti Koivisto.
+
+        * fast/inline/simple-shrink-to-fit-inline-block-expected.txt: Added.
+        * fast/inline/simple-shrink-to-fit-inline-block.html: Added.
+        * platform/ios/TestExpectations:
+
 2018-11-01  Philippe Normand  <pnormand@igalia.com>
 
         [Apache] Self-signed SSL certificate RSA key is considered too weak
diff --git a/LayoutTests/fast/inline/simple-shrink-to-fit-inline-block-expected.txt b/LayoutTests/fast/inline/simple-shrink-to-fit-inline-block-expected.txt
new file mode 100644 (file)
index 0000000..8116677
--- /dev/null
@@ -0,0 +1,13 @@
+layer at (0,0) size 800x600
+  RenderView at (0,0) size 800x600
+layer at (0,0) size 800x600
+  RenderBlock {HTML} at (0,0) size 800x600
+    RenderBody {BODY} at (8,8) size 784x584
+      RenderBlock {DIV} at (0,0) size 300x102
+        RenderText {#text} at (0,1) size 86x18
+          text run at (0,1) width 86: "first line with"
+        RenderBlock {DIV} at (85,0) size 77x102 [border: (1px solid #FF0000)]
+          RenderText {#text} at (1,1) size 74x18
+            text run at (1,1) width 74: "shrink to fit"
+        RenderText {#text} at (161,1) size 48x18
+          text run at (161,1) width 48: "content"
diff --git a/LayoutTests/fast/inline/simple-shrink-to-fit-inline-block.html b/LayoutTests/fast/inline/simple-shrink-to-fit-inline-block.html
new file mode 100644 (file)
index 0000000..988d5c7
--- /dev/null
@@ -0,0 +1,14 @@
+<style>
+#main {
+       width: 300px;
+}
+
+#inline-b {
+       border: 1px solid red;
+       display: inline-block;
+       height: 100px;
+}
+
+</style>
+
+<div id=main>first line with<div id=inline-b>shrink to fit</div>content</div>
index 48cc99e..236ebee 100644 (file)
@@ -3114,6 +3114,7 @@ fast/inline/simple-intruding-float1.html [ Failure ]
 fast/inline/simple-intruding-floats2.html [ Failure ]
 fast/inline/simple-intruding-floats3.html [ Failure ]
 fast/inline/simple-inline-block.html [ Failure ]
+fast/inline/simple-shrink-to-fit-inline-block.html [ Failure ]
 
 # Datalist
 webkit.org/b/186714 fast/forms/datalist/datalist-textinput-keydown.html [ Skip ]
index c8de181..600fde7 100644 (file)
@@ -1,5 +1,28 @@
 2018-11-02  Zalan Bujtas  <zalan@apple.com>
 
+        [LFC][IFC] Add support for intrinsic width calculation
+        https://bugs.webkit.org/show_bug.cgi?id=191144
+
+        Reviewed by Antti Koivisto.
+
+        This is the inline formatting version of the shrink-to-fit computation. It generates inline runs
+        and uses InlineLineBreaker to compute min/max width. This is very similar to ::layout.
+
+        Test: fast/inline/simple-shrink-to-fit-inline-block.html
+
+        * layout/inlineformatting/InlineFormattingContext.cpp:
+        (WebCore::Layout::InlineFormattingContext::layout const):
+        (WebCore::Layout::InlineFormattingContext::computeWidthAndMargin const):
+        (WebCore::Layout::InlineFormattingContext::computeHeightAndMargin const):
+        (WebCore::Layout::InlineFormattingContext::layoutFormattingContextRoot const):
+        (WebCore::Layout::InlineFormattingContext::computeWidthAndHeightForReplacedInlineBox const):
+        (WebCore::Layout::InlineFormattingContext::collectInlineContent const):
+        (WebCore::Layout::InlineFormattingContext::instrinsicWidthConstraints const):
+        (WebCore::Layout::InlineFormattingContext::computeWidthAndHeightForInlineBox const): Deleted.
+        * layout/inlineformatting/InlineFormattingContext.h:
+
+2018-11-02  Zalan Bujtas  <zalan@apple.com>
+
         [LFC][BFC] BlockFormattingContext::instrinsicWidthConstraints logic should look similar to ::layout
         https://bugs.webkit.org/show_bug.cgi?id=191181
 
index 004f0d1..2212071 100644 (file)
@@ -60,43 +60,22 @@ void InlineFormattingContext::layout() const
 
     auto& inlineFormattingState = downcast<InlineFormattingState>(formattingState());
     InlineRunProvider inlineRunProvider(inlineFormattingState);
-    auto& formattingRoot = downcast<Container>(root());
-    auto* layoutBox = formattingRoot.firstInFlowOrFloatingChild();
-    // Casually walk through the block's descendants and place the inline boxes one after the other as much as we can (yeah, I am looking at you floats).
-    while (layoutBox) {
 
-        if (layoutBox->establishesFormattingContext()) {
-            layoutFormattingContextRoot(*layoutBox);
-            // Formatting context roots take care of their entire subtree. Continue with next sibling.
-            inlineRunProvider.append(*layoutBox);
-            layoutBox = layoutBox->nextInFlowOrFloatingSibling();
+    collectInlineContent(inlineRunProvider);
+    // Compute width/height for non-text content.
+    for (auto& inlineRun : inlineRunProvider.runs()) {
+        if (inlineRun.isText())
             continue;
-        }
 
-        if (is<Container>(layoutBox)) {
-            ASSERT(is<InlineContainer>(layoutBox));
-            layoutBox = downcast<Container>(*layoutBox).firstInFlowOrFloatingChild();
+        auto& layoutBox = inlineRun.inlineItem().layoutBox();
+        if (layoutBox.establishesFormattingContext()) {
+            layoutFormattingContextRoot(layoutBox);
             continue;
         }
-
-        inlineRunProvider.append(*layoutBox);
-        computeWidthAndHeightForInlineBox(*layoutBox);
-
-        for (; layoutBox; layoutBox = layoutBox->parent()) {
-            if (layoutBox == &formattingRoot) {
-                layoutBox = nullptr;
-                break;
-            }
-            if (auto* nextSibling = layoutBox->nextInFlowOrFloatingSibling()) {
-                layoutBox = nextSibling;
-                break;
-            }
-        }
-        ASSERT(!layoutBox || layoutBox->isDescendantOf(formattingRoot));
+        computeWidthAndHeightForReplacedInlineBox(layoutBox);
     }
 
     layoutInlineContent(inlineRunProvider);
-
     LOG_WITH_STREAM(FormattingContextLayout, stream << "[End] -> inline formatting context -> formatting root(" << &root() << ")");
 }
 
@@ -204,81 +183,69 @@ void InlineFormattingContext::layoutInlineContent(const InlineRunProvider& inlin
     line.close(Line::LastLine::Yes);
 }
 
-void InlineFormattingContext::layoutFormattingContextRoot(const Box& layoutBox) const
+void InlineFormattingContext::computeWidthAndMargin(const Box& layoutBox) const
 {
     auto& layoutState = this->layoutState();
 
-    ASSERT(layoutBox.isFloatingPositioned() || layoutBox.isInlineBlockBox());
-    auto& displayBox = layoutState.displayBoxForLayoutBox(layoutBox);
-
-    auto computeWidthAndMargin = [&]() {
-        WidthAndMargin widthAndMargin;
+    WidthAndMargin widthAndMargin;
+    if (layoutBox.isFloatingPositioned())
+        widthAndMargin = Geometry::floatingWidthAndMargin(layoutState, layoutBox);
+    else if (layoutBox.isInlineBlockBox())
+        widthAndMargin = Geometry::inlineBlockWidthAndMargin(layoutState, layoutBox);
+    else if (layoutBox.replaced())
+        widthAndMargin = Geometry::inlineReplacedWidthAndMargin(layoutState, layoutBox);
+    else
+        ASSERT_NOT_REACHED();
 
-        if (layoutBox.isFloatingPositioned())
-            widthAndMargin = Geometry::floatingWidthAndMargin(layoutState, layoutBox);
-        else if (layoutBox.isInlineBlockBox())
-            widthAndMargin = Geometry::inlineBlockWidthAndMargin(layoutState, layoutBox);
-        else
-            ASSERT_NOT_REACHED();
+    auto& displayBox = layoutState.displayBoxForLayoutBox(layoutBox);
+    displayBox.setContentBoxWidth(widthAndMargin.width);
+    displayBox.setHorizontalMargin(widthAndMargin.margin);
+    displayBox.setHorizontalNonComputedMargin(widthAndMargin.nonComputedMargin);
+}
 
-        displayBox.setContentBoxWidth(widthAndMargin.width);
-        displayBox.setHorizontalMargin(widthAndMargin.margin);
-        displayBox.setHorizontalNonComputedMargin(widthAndMargin.nonComputedMargin);
-    };
+void InlineFormattingContext::computeHeightAndMargin(const Box& layoutBox) const
+{
+    auto& layoutState = this->layoutState();
 
-    auto computeHeightAndMargin = [&]() {
-        HeightAndMargin heightAndMargin;
+    HeightAndMargin heightAndMargin;
+    if (layoutBox.isFloatingPositioned())
+        heightAndMargin = Geometry::floatingHeightAndMargin(layoutState, layoutBox);
+    else if (layoutBox.isInlineBlockBox())
+        heightAndMargin = Geometry::inlineBlockHeightAndMargin(layoutState, layoutBox);
+    else if (layoutBox.replaced())
+        heightAndMargin = Geometry::inlineReplacedHeightAndMargin(layoutState, layoutBox);
+    else
+        ASSERT_NOT_REACHED();
 
-        if (layoutBox.isFloatingPositioned())
-            heightAndMargin = Geometry::floatingHeightAndMargin(layoutState, layoutBox);
-        else if (layoutBox.isInlineBlockBox())
-            heightAndMargin = Geometry::inlineBlockHeightAndMargin(layoutState, layoutBox);
-        else
-            ASSERT_NOT_REACHED();
+    auto& displayBox = layoutState.displayBoxForLayoutBox(layoutBox);
+    displayBox.setContentBoxHeight(heightAndMargin.height);
+    displayBox.setVerticalNonCollapsedMargin(heightAndMargin.margin);
+    displayBox.setVerticalMargin(heightAndMargin.collapsedMargin.value_or(heightAndMargin.margin));
+}
 
-        displayBox.setContentBoxHeight(heightAndMargin.height);
-        displayBox.setVerticalNonCollapsedMargin(heightAndMargin.margin);
-        displayBox.setVerticalMargin(heightAndMargin.collapsedMargin.value_or(heightAndMargin.margin));
-    };
+void InlineFormattingContext::layoutFormattingContextRoot(const Box& layoutBox) const
+{
+    ASSERT(layoutBox.isFloatingPositioned() || layoutBox.isInlineBlockBox());
 
-    layoutState.createFormattingStateForFormattingRootIfNeeded(layoutBox);
+    auto& layoutState = this->layoutState();
+    auto& formattingState = layoutState.createFormattingStateForFormattingRootIfNeeded(layoutBox);
     computeBorderAndPadding(layoutBox);
-    computeWidthAndMargin();
-
+    computeWidthAndMargin(layoutBox);
     // Swich over to the new formatting context (the one that the root creates).
-    layoutState.establishedFormattingState(layoutBox).formattingContext(layoutBox)->layout();
-
+    formattingState.formattingContext(layoutBox)->layout();
     // Come back and finalize the root's height and margin.
-    computeHeightAndMargin();
+    computeHeightAndMargin(layoutBox);
 }
 
-void InlineFormattingContext::computeWidthAndHeightForInlineBox(const Box& layoutBox) const
+void InlineFormattingContext::computeWidthAndHeightForReplacedInlineBox(const Box& layoutBox) const
 {
     ASSERT(!layoutBox.isContainer());
     ASSERT(!layoutBox.establishesFormattingContext());
-
-    if (is<InlineBox>(layoutBox) && downcast<InlineBox>(layoutBox).hasTextContent()) {
-        // Text content width is computed during text run generation. -It does not make any sense to measure unprocessed text here, since it will likely be
-        // split up (or concatenated).
-        return;
-    }
-
-    auto& layoutState = this->layoutState();
-    // This is pretty much only for replaced inline boxes atm.
     ASSERT(layoutBox.replaced());
-    computeBorderAndPadding(layoutBox);
-
-    auto widthAndMargin = Geometry::inlineReplacedWidthAndMargin(layoutState, layoutBox);
-    auto heightAndMargin = Geometry::inlineReplacedHeightAndMargin(layoutState, layoutBox);
-
-    auto& displayBox = layoutState.displayBoxForLayoutBox(layoutBox);
-    displayBox.setContentBoxWidth(widthAndMargin.width);
-    displayBox.setHorizontalMargin(widthAndMargin.margin);
-    displayBox.setHorizontalNonComputedMargin(widthAndMargin.nonComputedMargin);
 
-    displayBox.setContentBoxHeight(heightAndMargin.height);
-    displayBox.setVerticalNonCollapsedMargin(heightAndMargin.margin);
-    displayBox.setVerticalMargin(heightAndMargin.collapsedMargin.value_or(heightAndMargin.margin));
+    computeBorderAndPadding(layoutBox);
+    computeWidthAndMargin(layoutBox);
+    computeHeightAndMargin(layoutBox);
 }
 
 void InlineFormattingContext::computeFloatPosition(const FloatingContext& floatingContext, Line& line, const Box& floatBox) const
@@ -301,9 +268,79 @@ void InlineFormattingContext::computeInFlowPositionedPosition(const Box&) const
 {
 }
 
+void InlineFormattingContext::collectInlineContent(InlineRunProvider& inlineRunProvider) const
+{
+    if (!is<Container>(root()))
+        return;
+
+    auto& formattingRoot = downcast<Container>(root());
+    auto* layoutBox = formattingRoot.firstInFlowOrFloatingChild();
+
+    while (layoutBox) {
+        ASSERT(layoutBox->isDescendantOf(formattingRoot));
+
+        if (layoutBox->establishesFormattingContext()) {
+            inlineRunProvider.append(*layoutBox);
+            layoutBox = layoutBox->nextInFlowOrFloatingSibling();
+            continue;
+        }
+
+        if (is<Container>(layoutBox)) {
+            layoutBox = downcast<Container>(*layoutBox).firstInFlowOrFloatingChild();
+            continue;
+        }
+
+        inlineRunProvider.append(*layoutBox);
+
+        while (true) {
+            if (auto* nextSibling = layoutBox->nextInFlowOrFloatingSibling()) {
+                layoutBox = nextSibling;
+                break;
+            }
+
+            layoutBox = layoutBox->parent();
+
+            if (layoutBox == &formattingRoot)
+                return;
+        }
+    }
+}
+
 FormattingContext::InstrinsicWidthConstraints InlineFormattingContext::instrinsicWidthConstraints() const
 {
-    return { };
+    auto& formattingStateForRoot = layoutState().formattingStateForBox(root());
+    if (auto instrinsicWidthConstraints = formattingStateForRoot.instrinsicWidthConstraints(root()))
+        return *instrinsicWidthConstraints;
+
+    auto& inlineFormattingState = downcast<InlineFormattingState>(formattingState());
+    InlineRunProvider inlineRunProvider(inlineFormattingState);
+    collectInlineContent(inlineRunProvider);
+
+    // Compute width for non-text content.
+    for (auto& inlineRun : inlineRunProvider.runs()) {
+        if (inlineRun.isText())
+            continue;
+
+        computeWidthAndMargin(inlineRun.inlineItem().layoutBox());
+    }
+
+    auto maximumLineWidth = [&](auto availableWidth) {
+        LayoutUnit maxContentLogicalRight;
+        InlineLineBreaker lineBreaker(layoutState(), inlineFormattingState.inlineContent(), inlineRunProvider.runs());
+        LayoutUnit lineLogicalRight;
+        while (auto run = lineBreaker.nextRun(lineLogicalRight, availableWidth, !lineLogicalRight)) {
+            if (run->position == InlineLineBreaker::Run::Position::LineBegin)
+                lineLogicalRight = 0;
+            lineLogicalRight += run->width;
+
+            maxContentLogicalRight = std::max(maxContentLogicalRight, lineLogicalRight);
+        }
+        return maxContentLogicalRight;
+    };
+
+    auto instrinsicWidthConstraints = FormattingContext::InstrinsicWidthConstraints { maximumLineWidth(0), maximumLineWidth(LayoutUnit::max()) };
+    formattingStateForRoot.setInstrinsicWidthConstraints(root(), instrinsicWidthConstraints);
+    return instrinsicWidthConstraints;
 }
 
 }
index f7d65ec..fa90aa4 100644 (file)
@@ -106,11 +106,14 @@ private:
     void initializeNewLine(Line&) const;
 
     void layoutFormattingContextRoot(const Box&) const;
-    void computeWidthAndHeightForInlineBox(const Box&) const;
+    void computeWidthAndHeightForReplacedInlineBox(const Box&) const;
+    void computeHeightAndMargin(const Box&) const;
+    void computeWidthAndMargin(const Box&) const;
     void computeFloatPosition(const FloatingContext&, Line&, const Box&) const;
     void computeStaticPosition(const Box&) const override;
     void computeInFlowPositionedPosition(const Box&) const override;
 
+    void collectInlineContent(InlineRunProvider&) const;
     InstrinsicWidthConstraints instrinsicWidthConstraints() const override;
 };
 
index d48d74c..163e302 100644 (file)
@@ -1,3 +1,12 @@
+2018-11-02  Zalan Bujtas  <zalan@apple.com>
+
+        [LFC][IFC] Add support for intrinsic width calculation
+        https://bugs.webkit.org/show_bug.cgi?id=191144
+
+        Reviewed by Antti Koivisto.
+
+        * LayoutReloaded/misc/LFC-passing-tests.txt:
+
 2018-11-01  Philippe Normand  <pnormand@igalia.com>
 
         [Apache] Self-signed SSL certificate RSA key is considered too weak
index bcf068b..a5996de 100644 (file)
@@ -68,3 +68,4 @@ fast/inline/simple-intruding-float1.html
 fast/inline/simple-intruding-floats2.html
 fast/inline/simple-intruding-floats3.html
 fast/inline/simple-inline-block.html
+fast/inline/simple-shrink-to-fit-inline-block.html