[LFC] Implement minimum/maximum content width logic.
authorzalan@apple.com <zalan@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 3 Jul 2018 18:29:35 +0000 (18:29 +0000)
committerzalan@apple.com <zalan@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 3 Jul 2018 18:29:35 +0000 (18:29 +0000)
https://bugs.webkit.org/show_bug.cgi?id=187241

Reviewed by Antti Koivisto.

Compute the minimum/maximum content width if needed and cache the values on the formatting state.

* layout/FormattingContext.cpp:
(WebCore::Layout::FormattingContext::computeFloatingWidthAndMargin const):
(WebCore::Layout::FormattingContext::computeOutOfFlowHorizontalGeometry const):
(WebCore::Layout::FormattingContext::layoutOutOfFlowDescendants const):
* layout/FormattingContext.h:
* layout/FormattingContextGeometry.cpp:
(WebCore::Layout::FormattingContext::Geometry::shrinkToFitWidth):
(WebCore::Layout::FormattingContext::Geometry::outOfFlowNonReplacedHorizontalGeometry):
(WebCore::Layout::FormattingContext::Geometry::floatingNonReplacedWidthAndMargin):
(WebCore::Layout::FormattingContext::Geometry::outOfFlowHorizontalGeometry):
(WebCore::Layout::FormattingContext::Geometry::floatingWidthAndMargin):
(WebCore::Layout::shrinkToFitWidth): Deleted.
* layout/FormattingState.cpp:
(WebCore::Layout::FormattingState::FormattingState):
* layout/FormattingState.h:
(WebCore::Layout::FormattingState::setMinimumMaximumContentWidth):
(WebCore::Layout::FormattingState::clearMinimumMaximumContentWidth):
(WebCore::Layout::FormattingState::minimumMaximumContentWidth const):
* layout/LayoutContext.cpp:
(WebCore::Layout::LayoutContext::establishedFormattingState):
* layout/LayoutContext.h:
* layout/Verification.cpp:
(WebCore::Layout::LayoutContext::verifyAndOutputMismatchingLayoutTree const):
* layout/blockformatting/BlockFormattingContext.cpp:
(WebCore::Layout::BlockFormattingContext::createFormattingState const):
(WebCore::Layout::BlockFormattingContext::minimumMaximumContentWidth const):
* layout/blockformatting/BlockFormattingContext.h:
* layout/blockformatting/BlockFormattingContextGeometry.cpp:
(WebCore::Layout::BlockFormattingContext::Geometry::minimumMaximumContentWidthNeedsChildrenWidth):
(WebCore::Layout::BlockFormattingContext::Geometry::minimumMaximumContentWidth):
* layout/blockformatting/BlockFormattingState.cpp:
(WebCore::Layout::BlockFormattingState::BlockFormattingState):
* layout/blockformatting/BlockFormattingState.h:
* layout/inlineformatting/InlineFormattingContext.cpp:
(WebCore::Layout::InlineFormattingContext::createFormattingState const):
(WebCore::Layout::InlineFormattingContext::minimumMaximumContentWidth const):
* layout/inlineformatting/InlineFormattingContext.h:
* layout/inlineformatting/InlineFormattingState.cpp:
(WebCore::Layout::InlineFormattingState::InlineFormattingState):
* layout/inlineformatting/InlineFormattingState.h:
* page/FrameViewLayoutContext.cpp:

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

19 files changed:
Source/WebCore/ChangeLog
Source/WebCore/layout/FormattingContext.cpp
Source/WebCore/layout/FormattingContext.h
Source/WebCore/layout/FormattingContextGeometry.cpp
Source/WebCore/layout/FormattingState.cpp
Source/WebCore/layout/FormattingState.h
Source/WebCore/layout/LayoutContext.cpp
Source/WebCore/layout/LayoutContext.h
Source/WebCore/layout/Verification.cpp
Source/WebCore/layout/blockformatting/BlockFormattingContext.cpp
Source/WebCore/layout/blockformatting/BlockFormattingContext.h
Source/WebCore/layout/blockformatting/BlockFormattingContextGeometry.cpp
Source/WebCore/layout/blockformatting/BlockFormattingState.cpp
Source/WebCore/layout/blockformatting/BlockFormattingState.h
Source/WebCore/layout/inlineformatting/InlineFormattingContext.cpp
Source/WebCore/layout/inlineformatting/InlineFormattingContext.h
Source/WebCore/layout/inlineformatting/InlineFormattingState.cpp
Source/WebCore/layout/inlineformatting/InlineFormattingState.h
Source/WebCore/page/FrameViewLayoutContext.cpp

index 3ca2d1a..c34d729 100644 (file)
@@ -1,3 +1,54 @@
+2018-07-03  Zalan Bujtas  <zalan@apple.com>
+
+        [LFC] Implement minimum/maximum content width logic.
+        https://bugs.webkit.org/show_bug.cgi?id=187241
+
+        Reviewed by Antti Koivisto.
+
+        Compute the minimum/maximum content width if needed and cache the values on the formatting state.
+
+        * layout/FormattingContext.cpp:
+        (WebCore::Layout::FormattingContext::computeFloatingWidthAndMargin const):
+        (WebCore::Layout::FormattingContext::computeOutOfFlowHorizontalGeometry const):
+        (WebCore::Layout::FormattingContext::layoutOutOfFlowDescendants const):
+        * layout/FormattingContext.h:
+        * layout/FormattingContextGeometry.cpp:
+        (WebCore::Layout::FormattingContext::Geometry::shrinkToFitWidth):
+        (WebCore::Layout::FormattingContext::Geometry::outOfFlowNonReplacedHorizontalGeometry):
+        (WebCore::Layout::FormattingContext::Geometry::floatingNonReplacedWidthAndMargin):
+        (WebCore::Layout::FormattingContext::Geometry::outOfFlowHorizontalGeometry):
+        (WebCore::Layout::FormattingContext::Geometry::floatingWidthAndMargin):
+        (WebCore::Layout::shrinkToFitWidth): Deleted.
+        * layout/FormattingState.cpp:
+        (WebCore::Layout::FormattingState::FormattingState):
+        * layout/FormattingState.h:
+        (WebCore::Layout::FormattingState::setMinimumMaximumContentWidth):
+        (WebCore::Layout::FormattingState::clearMinimumMaximumContentWidth):
+        (WebCore::Layout::FormattingState::minimumMaximumContentWidth const):
+        * layout/LayoutContext.cpp:
+        (WebCore::Layout::LayoutContext::establishedFormattingState):
+        * layout/LayoutContext.h:
+        * layout/Verification.cpp:
+        (WebCore::Layout::LayoutContext::verifyAndOutputMismatchingLayoutTree const):
+        * layout/blockformatting/BlockFormattingContext.cpp:
+        (WebCore::Layout::BlockFormattingContext::createFormattingState const):
+        (WebCore::Layout::BlockFormattingContext::minimumMaximumContentWidth const):
+        * layout/blockformatting/BlockFormattingContext.h:
+        * layout/blockformatting/BlockFormattingContextGeometry.cpp:
+        (WebCore::Layout::BlockFormattingContext::Geometry::minimumMaximumContentWidthNeedsChildrenWidth):
+        (WebCore::Layout::BlockFormattingContext::Geometry::minimumMaximumContentWidth):
+        * layout/blockformatting/BlockFormattingState.cpp:
+        (WebCore::Layout::BlockFormattingState::BlockFormattingState):
+        * layout/blockformatting/BlockFormattingState.h:
+        * layout/inlineformatting/InlineFormattingContext.cpp:
+        (WebCore::Layout::InlineFormattingContext::createFormattingState const):
+        (WebCore::Layout::InlineFormattingContext::minimumMaximumContentWidth const):
+        * layout/inlineformatting/InlineFormattingContext.h:
+        * layout/inlineformatting/InlineFormattingState.cpp:
+        (WebCore::Layout::InlineFormattingState::InlineFormattingState):
+        * layout/inlineformatting/InlineFormattingState.h:
+        * page/FrameViewLayoutContext.cpp:
+
 2018-07-03  Jonathan Bedard  <jbedard@apple.com>
 
         Unreviewed, rolling out r233461.
index 876bcab..2b2d747 100644 (file)
@@ -63,7 +63,7 @@ void FormattingContext::computeFloatingHeightAndMargin(LayoutContext& layoutCont
 
 void FormattingContext::computeFloatingWidthAndMargin(LayoutContext& layoutContext, const Box& layoutBox, Display::Box& displayBox) const
 {
-    auto widthAndMargin = Geometry::floatingWidthAndMargin(layoutContext, layoutBox);
+    auto widthAndMargin = Geometry::floatingWidthAndMargin(layoutContext, *this, layoutBox);
     displayBox.setContentBoxWidth(widthAndMargin.width);
     displayBox.moveHorizontally(widthAndMargin.margin.left);
     displayBox.setHorizontalMargin(widthAndMargin.margin);
@@ -71,7 +71,7 @@ void FormattingContext::computeFloatingWidthAndMargin(LayoutContext& layoutConte
 
 void FormattingContext::computeOutOfFlowHorizontalGeometry(LayoutContext& layoutContext, const Box& layoutBox, Display::Box& displayBox) const
 {
-    auto horizontalGeometry = Geometry::outOfFlowHorizontalGeometry(layoutContext, layoutBox);
+    auto horizontalGeometry = Geometry::outOfFlowHorizontalGeometry(layoutContext, *this, layoutBox);
     displayBox.setLeft(horizontalGeometry.left + horizontalGeometry.widthAndMargin.margin.left);
     displayBox.setContentBoxWidth(horizontalGeometry.widthAndMargin.width);
     displayBox.setHorizontalMargin(horizontalGeometry.widthAndMargin.margin);
@@ -128,16 +128,14 @@ void FormattingContext::layoutOutOfFlowDescendants(LayoutContext& layoutContext,
         auto& layoutBox = *outOfFlowBox;
         auto& displayBox = layoutContext.createDisplayBox(layoutBox);
 
-        // The term "static position" (of an element) refers, roughly, to the position an element would have had in the normal flow.
-        // More precisely, the static position for 'top' is the distance from the top edge of the containing block to the top margin edge
-        // of a hypothetical box that would have been the first box of the element if its specified 'position' value had been 'static' and
-        // its specified 'float' had been 'none' and its specified 'clear' had been 'none'.
+        ASSERT(layoutBox.establishesFormattingContext());
+        auto formattingContext = layoutContext.formattingContext(layoutBox);
+        auto& establishedFormattingState = layoutContext.establishedFormattingState(layoutBox, *formattingContext);
+
         computeBorderAndPadding(layoutContext, layoutBox, displayBox);
         computeOutOfFlowHorizontalGeometry(layoutContext, layoutBox, displayBox);
 
-        ASSERT(layoutBox.establishesFormattingContext());
-        auto formattingContext = layoutContext.formattingContext(layoutBox);
-        formattingContext->layout(layoutContext, layoutContext.establishedFormattingState(layoutBox, *formattingContext));
+        formattingContext->layout(layoutContext, establishedFormattingState);
 
         computeOutOfFlowVerticalGeometry(layoutContext, layoutBox, displayBox);
         layoutOutOfFlowDescendants(layoutContext, layoutBox);
index 05b8288..1f2c150 100644 (file)
@@ -52,9 +52,15 @@ public:
 
     virtual void layout(LayoutContext&, FormattingState&) const = 0;
     void layoutOutOfFlowDescendants(LayoutContext&, const Box&) const;
-    virtual std::unique_ptr<FormattingState> createFormattingState(Ref<FloatingState>&&) const = 0;
+    virtual std::unique_ptr<FormattingState> createFormattingState(Ref<FloatingState>&&, const LayoutContext&) const = 0;
     virtual Ref<FloatingState> createOrFindFloatingState(LayoutContext&) const = 0;
 
+    struct InstrinsicWidthConstraints {
+        LayoutUnit minimum;
+        LayoutUnit maximum;
+    };
+    virtual InstrinsicWidthConstraints instrinsicWidthConstraints(LayoutContext&, const Box&) const = 0;
+
 protected:
     struct LayoutPair {
         const Box& layoutBox;
@@ -112,10 +118,10 @@ protected:
         };
 
         static VerticalGeometry outOfFlowVerticalGeometry(LayoutContext&, const Box&);
-        static HorizontalGeometry outOfFlowHorizontalGeometry(LayoutContext&, const Box&);
+        static HorizontalGeometry outOfFlowHorizontalGeometry(LayoutContext&, const FormattingContext&, const Box&);
 
         static HeightAndMargin floatingHeightAndMargin(LayoutContext&, const Box&);
-        static WidthAndMargin floatingWidthAndMargin(LayoutContext&, const Box&);
+        static WidthAndMargin floatingWidthAndMargin(LayoutContext&, const FormattingContext&, const Box&);
 
         static HeightAndMargin inlineReplacedHeightAndMargin(LayoutContext&, const Box&);
         static WidthAndMargin inlineReplacedWidthAndMargin(LayoutContext&, const Box&, std::optional<LayoutUnit> precomputedMarginLeft = { },
@@ -128,19 +134,22 @@ protected:
         static Display::Box::VerticalEdges computedNonCollapsedVerticalMarginValue(const LayoutContext&, const Box&);
 
         static std::optional<LayoutUnit> computedValueIfNotAuto(const Length& geometryProperty, LayoutUnit containingBlockWidth);
+        static std::optional<LayoutUnit> fixedValue(const Length& geometryProperty);
 
     private:
         static VerticalGeometry outOfFlowReplacedVerticalGeometry(LayoutContext&, const Box&);
         static HorizontalGeometry outOfFlowReplacedHorizontalGeometry(LayoutContext&, const Box&);
 
         static VerticalGeometry outOfFlowNonReplacedVerticalGeometry(LayoutContext&, const Box&);
-        static HorizontalGeometry outOfFlowNonReplacedHorizontalGeometry(LayoutContext&, const Box&);
+        static HorizontalGeometry outOfFlowNonReplacedHorizontalGeometry(LayoutContext&, const FormattingContext&, const Box&);
 
         static HeightAndMargin floatingReplacedHeightAndMargin(LayoutContext&, const Box&);
         static WidthAndMargin floatingReplacedWidthAndMargin(LayoutContext&, const Box&);
 
         static HeightAndMargin floatingNonReplacedHeightAndMargin(LayoutContext&, const Box&);
-        static WidthAndMargin floatingNonReplacedWidthAndMargin(LayoutContext&, const Box&);
+        static WidthAndMargin floatingNonReplacedWidthAndMargin(LayoutContext&, const FormattingContext&, const Box&);
+
+        static LayoutUnit shrinkToFitWidth(LayoutContext&, const FormattingContext&, const Box&);
     };
 
 private:
index 8adcda3..ec2c051 100644 (file)
@@ -62,12 +62,6 @@ static LayoutUnit contentHeightForFormattingContextRoot(LayoutContext& layoutCon
     return computedHeight;
 }
 
-static LayoutUnit shrinkToFitWidth(LayoutContext&, const Box& layoutBox)
-{
-    LOG_WITH_STREAM(FormattingContextLayout, stream << "[Width] -> shrink to fit -> unsupported -> width(" << LayoutUnit { } << "px) layoutBox: " << &layoutBox << ")");
-    return { };
-}
-
 std::optional<LayoutUnit> FormattingContext::Geometry::computedValueIfNotAuto(const Length& geometryProperty, LayoutUnit containingBlockWidth)
 {
     if (geometryProperty.isAuto())
@@ -75,6 +69,13 @@ std::optional<LayoutUnit> FormattingContext::Geometry::computedValueIfNotAuto(co
     return valueForLength(geometryProperty, containingBlockWidth);
 }
 
+std::optional<LayoutUnit> FormattingContext::Geometry::fixedValue(const Length& geometryProperty)
+{
+    if (!geometryProperty.isFixed())
+        return std::nullopt;
+    return { geometryProperty.value() };
+}
+
 static LayoutUnit staticVerticalPositionForOutOfFlowPositioned(const LayoutContext& layoutContext, const Box& layoutBox)
 {
     ASSERT(layoutBox.isOutOfFlowPositioned());
@@ -129,6 +130,21 @@ static LayoutUnit staticHorizontalPositionForOutOfFlowPositioned(const LayoutCon
     return left;
 }
 
+LayoutUnit FormattingContext::Geometry::shrinkToFitWidth(LayoutContext& layoutContext, const FormattingContext& formattingContext, const Box& layoutBox)
+{
+    LOG_WITH_STREAM(FormattingContextLayout, stream << "[Width] -> shrink to fit -> unsupported -> width(" << LayoutUnit { } << "px) layoutBox: " << &layoutBox << ")");
+    // Calculation of the shrink-to-fit width is similar to calculating the width of a table cell using the automatic table layout algorithm.
+    // Roughly: calculate the preferred width by formatting the content without breaking lines other than where explicit line breaks occur,
+    // and also calculate the preferred minimum width, e.g., by trying all possible line breaks. CSS 2.2 does not define the exact algorithm.
+    // Thirdly, find the available width: in this case, this is the width of the containing block minus the used values of 'margin-left', 'border-left-width',
+    // 'padding-left', 'padding-right', 'border-right-width', 'margin-right', and the widths of any relevant scroll bars.
+
+    // Then the shrink-to-fit width is: min(max(preferred minimum width, available width), preferred width).
+    auto availableWidth = layoutContext.displayBoxForLayoutBox(*layoutBox.containingBlock())->width();
+    auto instrinsicWidthConstraints = formattingContext.instrinsicWidthConstraints(layoutContext, layoutBox);
+    return std::min(std::max(instrinsicWidthConstraints.minimum, availableWidth), instrinsicWidthConstraints.maximum);
+}
+
 FormattingContext::Geometry::VerticalGeometry FormattingContext::Geometry::outOfFlowNonReplacedVerticalGeometry(LayoutContext& layoutContext, const Box& layoutBox)
 {
     ASSERT(layoutBox.isOutOfFlowPositioned() && !layoutBox.replaced());
@@ -245,7 +261,7 @@ FormattingContext::Geometry::VerticalGeometry FormattingContext::Geometry::outOf
     return { *top, *bottom, { *height, { *marginTop, *marginBottom }, { } } };
 }
 
-FormattingContext::Geometry::HorizontalGeometry FormattingContext::Geometry::outOfFlowNonReplacedHorizontalGeometry(LayoutContext& layoutContext, const Box& layoutBox)
+FormattingContext::Geometry::HorizontalGeometry FormattingContext::Geometry::outOfFlowNonReplacedHorizontalGeometry(LayoutContext& layoutContext, const FormattingContext& formattingContext, const Box& layoutBox)
 {
     ASSERT(layoutBox.isOutOfFlowPositioned() && !layoutBox.replaced());
     
@@ -352,7 +368,7 @@ FormattingContext::Geometry::HorizontalGeometry FormattingContext::Geometry::out
 
     if (!left && !width && right) {
         // #1
-        width = shrinkToFitWidth(layoutContext, layoutBox);
+        width = shrinkToFitWidth(layoutContext, formattingContext, layoutBox);
         left = containingBlockWidth - (*marginLeft + borderLeft + paddingLeft + *width + paddingRight  + borderRight + *marginRight + *right);
     } else if (!left && !right && width) {
         // #2
@@ -366,7 +382,7 @@ FormattingContext::Geometry::HorizontalGeometry FormattingContext::Geometry::out
         }
     } else if (!width && !right && left) {
         // #3
-        width = shrinkToFitWidth(layoutContext, layoutBox);
+        width = shrinkToFitWidth(layoutContext, formattingContext, layoutBox);
         right = containingBlockWidth - (*left + *marginLeft + borderLeft + paddingLeft + *width + paddingRight + borderRight + *marginRight);
     } else if (!left && width && right) {
         // #4
@@ -588,7 +604,7 @@ FormattingContext::Geometry::HeightAndMargin FormattingContext::Geometry::floati
     return FormattingContext::Geometry::HeightAndMargin { *height, { *marginTop, *marginBottom }, { } };
 }
 
-FormattingContext::Geometry::WidthAndMargin FormattingContext::Geometry::floatingNonReplacedWidthAndMargin(LayoutContext& layoutContext, const Box& layoutBox)
+FormattingContext::Geometry::WidthAndMargin FormattingContext::Geometry::floatingNonReplacedWidthAndMargin(LayoutContext& layoutContext, const FormattingContext& formattingContext, const Box& layoutBox)
 {
     ASSERT(layoutBox.isFloatingPositioned() && !layoutBox.replaced());
 
@@ -605,7 +621,7 @@ FormattingContext::Geometry::WidthAndMargin FormattingContext::Geometry::floatin
       // #2
     auto width = computedValueIfNotAuto(layoutBox.style().logicalWidth(), containingBlockWidth);
     if (!width)
-        width = shrinkToFitWidth(layoutContext, layoutBox);
+        width = shrinkToFitWidth(layoutContext, formattingContext, layoutBox);
 
     LOG_WITH_STREAM(FormattingContextLayout, stream << "[Width][Margin] -> floating non-replaced -> width(" << *width << "px) margin(" << margin.left << "px, " << margin.right << "px) -> layoutBox(" << &layoutBox << ")");
     return FormattingContext::Geometry::WidthAndMargin { *width, margin };
@@ -644,12 +660,12 @@ FormattingContext::Geometry::VerticalGeometry FormattingContext::Geometry::outOf
     return outOfFlowReplacedVerticalGeometry(layoutContext, layoutBox);
 }
 
-FormattingContext::Geometry::HorizontalGeometry FormattingContext::Geometry::outOfFlowHorizontalGeometry(LayoutContext& layoutContext, const Box& layoutBox)
+FormattingContext::Geometry::HorizontalGeometry FormattingContext::Geometry::outOfFlowHorizontalGeometry(LayoutContext& layoutContext, const FormattingContext& formattingContext, const Box& layoutBox)
 {
     ASSERT(layoutBox.isOutOfFlowPositioned());
 
     if (!layoutBox.replaced())
-        return outOfFlowNonReplacedHorizontalGeometry(layoutContext, layoutBox);
+        return outOfFlowNonReplacedHorizontalGeometry(layoutContext, formattingContext, layoutBox);
     return outOfFlowReplacedHorizontalGeometry(layoutContext, layoutBox);
 }
 
@@ -662,12 +678,12 @@ FormattingContext::Geometry::HeightAndMargin FormattingContext::Geometry::floati
     return floatingReplacedHeightAndMargin(layoutContext, layoutBox);
 }
 
-FormattingContext::Geometry::WidthAndMargin FormattingContext::Geometry::floatingWidthAndMargin(LayoutContext& layoutContext, const Box& layoutBox)
+FormattingContext::Geometry::WidthAndMargin FormattingContext::Geometry::floatingWidthAndMargin(LayoutContext& layoutContext, const FormattingContext& formattingContext, const Box& layoutBox)
 {
     ASSERT(layoutBox.isFloatingPositioned());
 
     if (!layoutBox.replaced())
-        return floatingNonReplacedWidthAndMargin(layoutContext, layoutBox);
+        return floatingNonReplacedWidthAndMargin(layoutContext, formattingContext, layoutBox);
     return floatingReplacedWidthAndMargin(layoutContext, layoutBox);
 }
 
index 6180bd7..d4e738f 100644 (file)
@@ -35,8 +35,9 @@ namespace Layout {
 
 WTF_MAKE_ISO_ALLOCATED_IMPL(FormattingState);
 
-FormattingState::FormattingState(Ref<FloatingState>&& floatingState, Type type)
-    : m_floatingState(WTFMove(floatingState))
+FormattingState::FormattingState(Ref<FloatingState>&& floatingState, Type type, const LayoutContext& layoutContext)
+    : m_layoutContext(layoutContext)
+    , m_floatingState(WTFMove(floatingState))
     , m_type(type)
 {
 }
index cf61693..59c5538 100644 (file)
@@ -28,6 +28,9 @@
 #if ENABLE(LAYOUT_FORMATTING_CONTEXT)
 
 #include "FloatingState.h"
+#include "LayoutBox.h"
+#include "LayoutContext.h"
+#include "LayoutUnit.h"
 #include <wtf/IsoMalloc.h>
 
 namespace WebCore {
@@ -47,18 +50,46 @@ public:
     void markNeedsLayout(const Box&, StyleDiff);
     bool needsLayout(const Box&);
 
+    void setInstrinsicWidthConstraints(const Box&,  FormattingContext::InstrinsicWidthConstraints);
+    void clearInstrinsicWidthConstraints(const Box&);
+    std::optional<FormattingContext::InstrinsicWidthConstraints> instrinsicWidthConstraints(const Box&) const;
+
     bool isBlockFormattingState() const { return m_type == Type::Block; }
     bool isInlineFormattingState() const { return m_type == Type::Inline; }
 
 protected:
     enum class Type { Block, Inline };
-    FormattingState(Ref<FloatingState>&&, Type);
+    FormattingState(Ref<FloatingState>&&, Type, const LayoutContext&);
+
+    const LayoutContext& m_layoutContext;
 
 private:
     Ref<FloatingState> m_floatingState;
+    HashMap<const Box*, FormattingContext::InstrinsicWidthConstraints> m_instrinsicWidthConstraints;
     Type m_type;
 };
 
+inline void FormattingState::setInstrinsicWidthConstraints(const Box& layoutBox, FormattingContext::InstrinsicWidthConstraints instrinsicWidthConstraints)
+{
+    ASSERT(!m_instrinsicWidthConstraints.contains(&layoutBox));
+    ASSERT(&m_layoutContext.formattingStateForBox(layoutBox) == this);
+    m_instrinsicWidthConstraints.set(&layoutBox, instrinsicWidthConstraints);
+}
+
+inline void FormattingState::clearInstrinsicWidthConstraints(const Box& layoutBox)
+{
+    m_instrinsicWidthConstraints.remove(&layoutBox);
+}
+
+inline std::optional<FormattingContext::InstrinsicWidthConstraints> FormattingState::instrinsicWidthConstraints(const Box& layoutBox) const
+{
+    ASSERT(&m_layoutContext.formattingStateForBox(layoutBox) == this);
+    auto iterator = m_instrinsicWidthConstraints.find(&layoutBox);
+    if (iterator == m_instrinsicWidthConstraints.end())
+        return { };
+    return iterator->value;
+}
+
 }
 }
 
index 751eb62..8c44ea6 100644 (file)
@@ -122,7 +122,7 @@ FormattingState& LayoutContext::formattingStateForBox(const Box& layoutBox) cons
 FormattingState& LayoutContext::establishedFormattingState(const Box& formattingContextRoot, const FormattingContext& context)
 {
     return *m_formattingStates.ensure(&formattingContextRoot, [this, &context] {
-        return context.createFormattingState(context.createOrFindFloatingState(*this));
+        return context.createFormattingState(context.createOrFindFloatingState(*this), *this);
     }).iterator->value;
 }
 
index 1f84ac6..9928d55 100644 (file)
@@ -28,7 +28,6 @@
 #if ENABLE(LAYOUT_FORMATTING_CONTEXT)
 
 #include "FormattingContext.h"
-#include "FormattingState.h"
 #include <wtf/HashSet.h>
 #include <wtf/IsoMalloc.h>
 #include <wtf/OptionSet.h>
@@ -48,6 +47,7 @@ namespace Layout {
 enum class StyleDiff;
 class Box;
 class Container;
+class FormattingState;
 
 // LayoutContext is the entry point for layout. It takes a (formatting root)container which acts as the root of the layout context.
 // LayoutContext::layout() generates the display tree for the root container's subtree (it does not run layout on the root though).
index 48164a0..6bab581 100644 (file)
@@ -120,11 +120,11 @@ void LayoutContext::verifyAndOutputMismatchingLayoutTree(const RenderView& rende
 {
     TextStream stream;
     auto mismatchingGeometry = verifyAndOutputSubtree(stream, *this, renderView, *m_root.get());
+    if (!mismatchingGeometry)
+        return;
 #if ENABLE(TREE_DEBUGGING)
-    if (mismatchingGeometry) {
-        showRenderTree(&renderView);
-        TreeBuilder::showLayoutTree(*this, *m_root.get());
-    }
+    showRenderTree(&renderView);
+    TreeBuilder::showLayoutTree(*this, *m_root.get());
 #endif
     WTFLogAlways("%s", stream.release().utf8().data());
 }
index fb7454d..8e86ab3 100644 (file)
@@ -152,9 +152,9 @@ void BlockFormattingContext::layoutFormattingContextRoot(LayoutContext& layoutCo
     formattingContext->layoutOutOfFlowDescendants(layoutContext, layoutBox);
 }
 
-std::unique_ptr<FormattingState> BlockFormattingContext::createFormattingState(Ref<FloatingState>&& floatingState) const
+std::unique_ptr<FormattingState> BlockFormattingContext::createFormattingState(Ref<FloatingState>&& floatingState, const LayoutContext& layoutContext) const
 {
-    return std::make_unique<BlockFormattingState>(WTFMove(floatingState));
+    return std::make_unique<BlockFormattingState>(WTFMove(floatingState), layoutContext);
 }
 
 Ref<FloatingState> BlockFormattingContext::createOrFindFloatingState(LayoutContext&) const
@@ -212,6 +212,75 @@ void BlockFormattingContext::computeInFlowWidthAndMargin(LayoutContext& layoutCo
     displayBox.setHorizontalMargin(widthAndMargin.margin);
 }
 
+FormattingContext::InstrinsicWidthConstraints BlockFormattingContext::instrinsicWidthConstraints(LayoutContext& layoutContext, const Box& layoutBox) const
+{
+    auto& formattingState = layoutContext.formattingStateForBox(layoutBox);
+    ASSERT(formattingState.isBlockFormattingState());
+    if (auto instrinsicWidthConstraints = formattingState.instrinsicWidthConstraints(layoutBox))
+        return *instrinsicWidthConstraints;
+
+    // Can we just compute them without checking the children?
+    if (!Geometry::instrinsicWidthConstraintsNeedChildrenWidth(layoutBox)) {
+        auto instrinsicWidthConstraints = Geometry::instrinsicWidthConstraints(layoutContext, layoutBox);
+        formattingState.setInstrinsicWidthConstraints(layoutBox, instrinsicWidthConstraints);
+        return instrinsicWidthConstraints;
+    }
+
+    // Visit the in-flow descendants and compute their min/max intrinsic width if needed.
+    // 1. Go all the way down to the leaf node
+    // 2. Check if actually need to visit all the boxes as we traverse down (already computed, container's min/max does not depend on descendants etc)
+    // 3. As we climb back on the tree, compute min/max intrinsic width
+    // (Any subtrees with new formatting contexts need to layout synchronously)
+    Vector<const Box*> queue;
+    // Non-containers early return.
+    ASSERT(is<Container>(layoutBox));
+    if (auto* firstChild = downcast<Container>(layoutBox).firstInFlowOrFloatingChild())
+        queue.append(firstChild);
+
+    auto& formattingStateForChildren = layoutBox.establishesFormattingContext() ? layoutContext.establishedFormattingState(layoutBox, *this) : formattingState;
+    while (!queue.isEmpty()) {
+        while (true) {
+            auto& childBox = *queue.last(); 
+            // Already computed?
+            auto instrinsicWidthConstraints = formattingStateForChildren.instrinsicWidthConstraints(childBox);
+            // Can we just compute them without checking the children?
+            if (!instrinsicWidthConstraints && !Geometry::instrinsicWidthConstraintsNeedChildrenWidth(childBox))
+                instrinsicWidthConstraints = Geometry::instrinsicWidthConstraints(layoutContext, childBox);
+            // Is it a formatting context root?
+            if (!instrinsicWidthConstraints && childBox.establishesFormattingContext())
+                instrinsicWidthConstraints = layoutContext.formattingContext(childBox)->instrinsicWidthConstraints(layoutContext, childBox);
+            // Go to the next sibling (and skip the descendants) if this box's min/max width is computed.
+            if (instrinsicWidthConstraints) {
+                formattingStateForChildren.setInstrinsicWidthConstraints(childBox, *instrinsicWidthConstraints); 
+                queue.removeLast();
+                if (!childBox.nextInFlowOrFloatingSibling())
+                    break;
+                queue.append(childBox.nextInFlowOrFloatingSibling());
+                continue;
+            }
+
+            if (!is<Container>(childBox) || !downcast<Container>(childBox).hasInFlowOrFloatingChild())
+                break;
+
+            queue.append(downcast<Container>(childBox).firstInFlowOrFloatingChild());
+        }
+
+        // Compute min/max intrinsic width bottom up.
+        while (!queue.isEmpty()) {
+            auto& childBox = *queue.takeLast();
+            formattingStateForChildren.setInstrinsicWidthConstraints(childBox, Geometry::instrinsicWidthConstraints(layoutContext, childBox)); 
+            // Move over to the next sibling or take the next box in the queue.
+            if (!is<Container>(childBox) || !downcast<Container>(childBox).nextInFlowOrFloatingSibling())
+                continue;
+            queue.append(downcast<Container>(childBox).nextInFlowOrFloatingSibling());
+        }
+    }
+
+    auto instrinsicWidthConstraints = Geometry::instrinsicWidthConstraints(layoutContext, layoutBox);
+    formattingState.setInstrinsicWidthConstraints(layoutBox, instrinsicWidthConstraints); 
+    return instrinsicWidthConstraints;
+}
+
 }
 }
 
index 062fc09..bc845cf 100644 (file)
@@ -47,7 +47,7 @@ public:
     BlockFormattingContext(const Box& formattingContextRoot);
 
     void layout(LayoutContext&, FormattingState&) const override;
-    std::unique_ptr<FormattingState> createFormattingState(Ref<FloatingState>&&) const override;
+    std::unique_ptr<FormattingState> createFormattingState(Ref<FloatingState>&&, const LayoutContext&) const override;
     Ref<FloatingState> createOrFindFloatingState(LayoutContext&) const override;
 
 private:
@@ -61,6 +61,8 @@ private:
     void computeInFlowWidthAndMargin(LayoutContext&, const Box&, Display::Box&) const;
     void computeInFlowHeightAndMargin(LayoutContext&, const Box&, Display::Box&) const;
 
+    FormattingContext::InstrinsicWidthConstraints instrinsicWidthConstraints(LayoutContext&, const Box&) const override;
+
     // This class implements positioning and sizing for boxes participating in a block formatting context.
     class Geometry {
     public:
@@ -70,6 +72,9 @@ private:
         static FormattingContext::Geometry::Position staticPosition(LayoutContext&, const Box&);
         static FormattingContext::Geometry::Position inFlowPositionedPosition(LayoutContext&, const Box&);
 
+        static bool instrinsicWidthConstraintsNeedChildrenWidth(const Box&);
+        static FormattingContext::InstrinsicWidthConstraints instrinsicWidthConstraints(LayoutContext&, const Box&);
+
     private:
         static FormattingContext::Geometry::HeightAndMargin inFlowNonReplacedHeightAndMargin(LayoutContext&, const Box&);
         static FormattingContext::Geometry::WidthAndMargin inFlowNonReplacedWidthAndMargin(LayoutContext&, const Box&, std::optional<LayoutUnit> precomputedWidth = std::nullopt);
index aabbf67..1a66adf 100644 (file)
@@ -29,6 +29,7 @@
 #if ENABLE(LAYOUT_FORMATTING_CONTEXT)
 
 #include "FormattingContext.h"
+#include "LayoutChildIterator.h"
 #include "Logging.h"
 #include <wtf/text/TextStream.h>
 
@@ -373,6 +374,52 @@ FormattingContext::Geometry::WidthAndMargin BlockFormattingContext::Geometry::in
     return inFlowReplacedWidthAndMargin(layoutContext, layoutBox);
 }
 
+bool BlockFormattingContext::Geometry::instrinsicWidthConstraintsNeedChildrenWidth(const Box& layoutBox)
+{
+    if (!is<Container>(layoutBox) || !downcast<Container>(layoutBox).hasInFlowOrFloatingChild())
+        return false;
+    return layoutBox.style().width().isAuto();
+}
+
+FormattingContext::InstrinsicWidthConstraints BlockFormattingContext::Geometry::instrinsicWidthConstraints(LayoutContext& layoutContext, const Box& layoutBox)
+{
+    auto& style = layoutBox.style();
+    if (auto width = FormattingContext::Geometry::fixedValue(style.logicalWidth()))
+        return { *width, *width };
+
+    // Minimum/maximum width can't be depending on the containing block's width.
+    if (!style.logicalWidth().isAuto())
+        return { };
+
+    if (!is<Container>(layoutBox))
+        return { };
+
+    LayoutUnit minimumIntrinsicWidth;
+    LayoutUnit maximumIntrinsicWidth;
+
+    for (auto& child : childrenOfType<Box>(downcast<Container>(layoutBox))) {
+        if (child.isOutOfFlowPositioned())
+            continue;
+        auto& formattingState = layoutContext.formattingStateForBox(child);
+        ASSERT(formattingState.isBlockFormattingState());
+        auto childInstrinsicWidthConstraints = formattingState.instrinsicWidthConstraints(child);
+        ASSERT(childInstrinsicWidthConstraints);
+        
+        auto& style = child.style();
+        auto horizontalMarginBorderAndPadding = FormattingContext::Geometry::fixedValue(style.marginLeft()).value_or(0)
+            + LayoutUnit { style.borderLeftWidth() }
+            + FormattingContext::Geometry::fixedValue(style.paddingLeft()).value_or(0)
+            + FormattingContext::Geometry::fixedValue(style.paddingRight()).value_or(0)
+            + LayoutUnit { style.borderRightWidth() }
+            + FormattingContext::Geometry::fixedValue(style.marginRight()).value_or(0);
+
+        minimumIntrinsicWidth = std::max(minimumIntrinsicWidth, childInstrinsicWidthConstraints->minimum + horizontalMarginBorderAndPadding); 
+        maximumIntrinsicWidth = std::max(maximumIntrinsicWidth, childInstrinsicWidthConstraints->maximum + horizontalMarginBorderAndPadding); 
+    }
+
+    return { minimumIntrinsicWidth, maximumIntrinsicWidth };
+}
+
 }
 }
 
index 7ebbf46..06ca6d9 100644 (file)
@@ -28,6 +28,8 @@
 
 #if ENABLE(LAYOUT_FORMATTING_CONTEXT)
 
+#include "FormattingContext.h"
+#include "LayoutBox.h"
 #include <wtf/IsoMallocInlines.h>
 
 namespace WebCore {
@@ -35,8 +37,8 @@ namespace Layout {
 
 WTF_MAKE_ISO_ALLOCATED_IMPL(BlockFormattingState);
 
-BlockFormattingState::BlockFormattingState(Ref<FloatingState>&& floatingState)
-    : FormattingState(WTFMove(floatingState), Type::Block)
+BlockFormattingState::BlockFormattingState(Ref<FloatingState>&& floatingState, const LayoutContext& layoutContext)
+    : FormattingState(WTFMove(floatingState), Type::Block, layoutContext)
 {
 }
 
index dae1473..c196f5d 100644 (file)
@@ -38,7 +38,7 @@ namespace Layout {
 class BlockFormattingState : public FormattingState {
     WTF_MAKE_ISO_ALLOCATED(BlockFormattingState);
 public:
-    BlockFormattingState(Ref<FloatingState>&&);
+    BlockFormattingState(Ref<FloatingState>&&, const LayoutContext&);
     virtual ~BlockFormattingState();
 };
 
index caa004d..6ef0e39 100644 (file)
@@ -48,9 +48,9 @@ void InlineFormattingContext::layout(LayoutContext&, FormattingState&) const
 {
 }
 
-std::unique_ptr<FormattingState> InlineFormattingContext::createFormattingState(Ref<FloatingState>&& floatingState) const
+std::unique_ptr<FormattingState> InlineFormattingContext::createFormattingState(Ref<FloatingState>&& floatingState, const LayoutContext& layoutContext) const
 {
-    return std::make_unique<InlineFormattingState>(WTFMove(floatingState));
+    return std::make_unique<InlineFormattingState>(WTFMove(floatingState), layoutContext);
 }
 
 Ref<FloatingState> InlineFormattingContext::createOrFindFloatingState(LayoutContext& layoutContext) const
@@ -74,6 +74,11 @@ void InlineFormattingContext::computeInFlowPositionedPosition(LayoutContext&, co
 {
 }
 
+FormattingContext::InstrinsicWidthConstraints InlineFormattingContext::instrinsicWidthConstraints(LayoutContext&, const Box&) const
+{
+    return { };
+}
+
 }
 }
 
index 135626f..42a344e 100644 (file)
@@ -44,13 +44,14 @@ public:
     InlineFormattingContext(const Box& formattingContextRoot);
 
     void layout(LayoutContext&, FormattingState&) const override;
-    std::unique_ptr<FormattingState> createFormattingState(Ref<FloatingState>&&) const override;
+    std::unique_ptr<FormattingState> createFormattingState(Ref<FloatingState>&&, const LayoutContext&) const override;
     Ref<FloatingState> createOrFindFloatingState(LayoutContext&) const override;
 
 private:
     void computeStaticPosition(LayoutContext&, const Box&, Display::Box&) const override;
     void computeInFlowPositionedPosition(LayoutContext&, const Box&, Display::Box&) const override;
 
+    InstrinsicWidthConstraints instrinsicWidthConstraints(LayoutContext&, const Box&) const override;
 };
 
 }
index a00d5dc..f0289b8 100644 (file)
@@ -35,8 +35,8 @@ namespace Layout {
 
 WTF_MAKE_ISO_ALLOCATED_IMPL(InlineFormattingState);
 
-InlineFormattingState::InlineFormattingState(Ref<FloatingState>&& floatingState)
-    : FormattingState(WTFMove(floatingState), Type::Inline)
+InlineFormattingState::InlineFormattingState(Ref<FloatingState>&& floatingState, const LayoutContext& layoutContext)
+    : FormattingState(WTFMove(floatingState), Type::Inline, layoutContext)
 {
 }
 
index 6aaf747..997d33c 100644 (file)
@@ -38,7 +38,7 @@ namespace Layout {
 class InlineFormattingState : public FormattingState {
     WTF_MAKE_ISO_ALLOCATED(InlineFormattingState);
 public:
-    InlineFormattingState(Ref<FloatingState>&&);
+    InlineFormattingState(Ref<FloatingState>&&, const LayoutContext&);
     virtual ~InlineFormattingState();
 };
 
index 14193c4..1f204a5 100644 (file)
@@ -40,6 +40,7 @@
 #include "Settings.h"
 
 #if ENABLE(LAYOUT_FORMATTING_CONTEXT)
+#include "FormattingState.h"
 #include "LayoutContainer.h"
 #include "LayoutContext.h"
 #include "LayoutTreeBuilder.h"