The spec at https://www.w3.org/TR/css-flexbox-1/ states that when
calculating specific spaces and sizes inside a flex container, the outer
size of a flex item needs to be taken into account.
This patch adds the margins in the main dimension of a flex item to
these calculations such that their margins are actually painted in a lot
of common cases.
It makes our Github page look marginally better.
CSS floats are now emitted by the InlineLevelIterator. When this
happens, IFC coordinates with the parent BFC to float the box to the
side, using the current LineBuilder state for vertical placement.
This makes the "instructions" text on Acid3 render as a single
contiguous flow of inline content.
This was implemented too rigidly, which made it impossible to place
floats correctly when they occurred in inline flow.
The new invariant is "all in-flow children must be either inline or
block". Out-of-flow children like floating and absolutely positioned
boxes are ignored when deciding when to generate anonymous boxes.
There's no actual need to build the stacking context tree before
performing layout. Instead, make it lazy and build the tree when it's
actually needed for something.
This avoids a bunch of work in situations where multiple synchronous
layouts are forced (typically by JavaScript) without painting or hit
testing taking place in between.
It also opens up for style invalidations that only target the stacking
context tree.
The old mode names, while mechanically accurate, didn't really reflect
their relationship to the CSS specifications.
This patch renames them as follows:
Default => Normal
AllPossibleLineBreaks => MinContent
OnlyRequiredLineBreaks => MaxContent
There's also now an explainer comment with the LayoutMode enum about the
specific implications of layout in each mode.
After accounting for left-side floats, we have to subtract the offset of
the IFC's containing block again, to get the real starting X offset
for the current line.
This was done correctly in leftmost_x_offset_at() but incorrectly in
available_space_for_line(), causing IFC to break lines too early in
cases where the containing block had a non-zero X offset from the BFC
root block.
This makes SVG-in-HTML behave quite a bit better by following general
replaced layout rules. It also turns <svg> elements into inline-level
boxes instead of block-level boxes.
Since there is currently no easy way to handle rotations and skews
with LibGfx this only implements translation and scaling by first
constructing a general 4x4 transformation matrix like outlined in
the css-transforms-1 specification. This is then downgraded to a
Gfx::AffineTransform in order to transform the destination rectangle
used with draw_scaled_bitmap()
While rotation would be nice this already looks pretty good :^)
The previous implementation used relative X offsets for both left and
right-side floats. This made right-side floats super awkward, since we
could only determine their X position once the width of the BFC root was
known, and for BFC roots with automatic width, this was not even working
at all most of the time.
This patch changes the way we deal with floats so that BFC keeps track
of the offset-from-edge for each float. The offset is the distance from
the BFC root edge (left or right, depending on float direction) to the
"innermost" margin edge of the floating box.
Floating box are now laid out in two passes: while going through the
normal flow layout, we put floats in their *static* position (i.e the
position they would have occupied if they weren't floating) and then
update the Y position value to the final one.
The second pass occurs later on, when the BFC root has had its width
assigned by the parent context. Once we know the root width, we can
set the X position value of floating boxes. (Because the X position of
right-side floats is relative to the right edge of the BFC root.)
Using the intrinsic size cache means we only perform the nested layout
to determine intrinsic size *once* per root layout pass.
Furthermore, by using a throwaway FormattingState, details of the nested
layout can't leak into and mutate the outer layout.
Instead of caching them with the current state, we can cache them at the
root of the state tree. Since intrinsic sizes are immutable during the
same layout, this allows layout to take advantage of intrinsic sizes
discovered during nested layout (and avoids a *lot* of duplicate work.)
FormattingStates can have parents, in case we're performing nested
layouts to determine something's intrinsic size. In those cases, it will
soon be useful to find the outermost (root) state.
This is preparation for allowing blocks with their own internal BFC to
flow around floating boxes in the parent BFC.
Note that IFC still has the available_space_for_line() API, which
returns space available within the IFC's own containing block, while the
BFC available_space_for_line() returns space available within its root.
This commit is messy due to the Paintable and Layout classes being
tangled together.
The RadioButton, CheckBox and ButtonBox classes are now subclasses of
FormAssociatedLabelableNode. This subclass separates these layout nodes
from LabelableNode, which is also the superclass of non-form associated
labelable nodes (Progress).
ButtonPaintable, CheckBoxPaintable and RadioButtonPaintable no longer
call events on DOM nodes directly from their mouse event handlers;
instead, all the functionality is now directly in EventHandler, which
dispatches the related events. handle_mousedown and related methods
return a bool indicating whether the event handling should proceed.
Paintable classes can now return an alternative DOM::Node which should
be the target of the mouse event. Labels use this to indicate that the
labeled control should be the target of the mouse events.
HTMLInputElement put its activation behavior on run_activation_behavior,
which wasn't actually called anywhere and had to be manually called by
other places. We now use activation_behavior which is used by
EventDispatcher.
This commit also brings HTMLInputElement closer to spec by removing the
did_foo functions that did ad-hoc event dispatching and unifies the
behavior under run_input_activation_behavior.
TreeBuilder wasn't taking advantage of the fact that we already have
computed style cached on each DOM::Element by the time we're
constructing a layout tree.
So instead of using the cached style, we recomputed it from scratch for
every element. This was done because invalidation was broken in many
places, but now that it's more or less trustworthy, stop recomputing
style on the fly in TreeBuilder and use what the preceding style update
pass gave us instead.
This basically cuts style computation work in half. :^)
Let's make it very clear that these are *computed* values, and not at
all the specified values. The specified values are currently discarded
by the CSS cascade algorithm.
Instead of calling quick_sort() every time a StackingContext child
is added to a parent, we now do a single pass of sorting work after the
full StackingContext tree has been built.
Before this change, the quick_sort() was ~13.5% of the profile while
hovering links on GitHub in the Browser. After the change, it's down to
~0.6%. Pretty good! :^)
Simplify automatic cross sizing of items in flex-direction:column by
using the fit-content width directly.
There's obviously a lot more nuance to this, but for now this makes
flex items with both width:auto and height:auto actually get some height
in column flex layouts.
I'm a little confused about intrinsic heights *really* work, and I'm
struggling to extract that information from the spec. In the meantime,
let's ensure that min-content is always smaller than (or equal to)
max-content so that other math works as expected.
Previously, each NodeState in a FormattingState was shared with the
parent FormattingState, but the HashMap of NodeState had to be copied
when making FormattingState copies.
This patch makes copying instant by keeping a pointer to the parent
FormattingState instead. When fetching immutable state via get(), we may
now return a reference to a NodeState owned by a parent FormattingState.
get_mutable() will copy any NodeState found in the ancestor chain before
making a brand new one.
The flexbox specification barely even handwaves about automatically
sized items, but there's actually a lot of work to do in order for them
to get the correct size.
This patch is messy, but does make significant progress on supporting
flex items with indefinite width and/or height.
There's a fair amount of nested layout going on here, but do note that
we'll be hitting the intrinsic sizes cache whenever possible.
FormattingContext can now calculate the intrinsic sizes (min-content and
max-content in both axes) for a given Layout::Box.
This is a rather expensive operation, as it necessitates performing two
throwaway layouts of the subtree rooted at the box. Fortunately, we can
cache the results of these calculations, as intrinsic sizes don't change
based on other context around the box. They are intrinsic after all. :^)
This wasn't worth the headache of trying to make SVG boxes work together
with BFC right now. Let's just make it a block container once again, and
have its corresponding SVGPaintable inherit from PaintableWithLines.
We'll have to revisit this as SVG support improves.
Although something has a definite size, we may still have to "resolve"
it, since FFC is quite liberal in what it considers to be definite.
Let's put that logic in a set of helper functions.
1. Make this decision *after* we've inserted the layout node into the
layout tree. Otherwise, we can't reach its containing block!
2. Per css-sizing-3, consider auto-sized blocks whose sizes resolve
solely against other definite sizes as definite themselves.
In particular, (2) makes us consider width:auto block children of a
definite-size containing block as having definite size. This becomes
very important in flex layout.
If there's some non-block-level box (like an SVG element of some kind)
between to blocks, just skip over the non-block for purposes of margin
collapsing. This is basically a hack, and something we'll need to
improve as part of our general SVG support.
For single-line flex containers, center the only flex line along the
cross axis. Alignment of multi-line flex containers are left as a FIXME.
This patch also moves out the assignment of final metrics to the
FormattingState from align_all_flex_lines() to a separate function.
- Avoid performing inside layout on definite-size flex items (since
their computed size can be used as-is.)
- Use FormattingState::clone() to generate a throwaway layout instead of
mutating the tree in-place.
- Update spec link & comments based on current CSSWG draft. The latest
version is quite a bit clearer on how this should work.