For stacking contexts that have opacity between 0 and 1, and also
contexts with a 2D transform, we first paint them into a temporary layer
buffer. Then we blend that buffer with the contents in one go.
Before this patch, we were only drawing the content box of the stacking
context into this layer buffer, which led to padding and borders missing
from elements painted this way.
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 :^)
No need to call the expensive establishes_stacking_context() here, as
we've already built the stacking context tree and can simply test for
the presence of existing stacking contexts.
By the time we're painting, we've already built the stacking context
tree. So instead of asking if a box establishes a stacking context, we
can ask if its paintable *has* a stacking context.
This was taking up ~6% of the profile when mousing around on the HTML
specification. With this change, it disappears completely. :^)
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.
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! :^)
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.
When doing viewbox transforms, elliptical always had large arc and
sweep flag set to false. Preserve these flags so they can be set
correctly when applying viewbox transformations.
The absolute rect of a paintable is somewhat expensive to compute. This
is because all coordinates are relative to the nearest containing block,
so we have to traverse the containing block chain and apply each offset
to get the final rect.
Paintables will never move between containing blocks, nor will their
absolute rect change. If anything changes, we'll simpl make a new
paintable and replace the old one.
Take advantage of this by caching the containing block and absolute rect
after first access.
Everything related to hit testing is better off using the painting tree.
The thing being mousemoved over is a paintable, so let's hand that out
directly instead of the corresponding layout node.
Input events have nothing to do with layout, so let's not send them to
layout nodes.
The job of Paintable starts to become clear. It represents a paintable
item that can be rendered into the viewport, which means it can also
be targeted by the mouse cursor.
This patch adds a bunch of Paintable subclasses, each corresponding to
the Layout::Node subclasses that had a paint() override. All painting
logic is moved from layout nodes into their corresponding paintables.
Paintables are now created by asking a Layout::Box to produce one:
static NonnullOwnPtr<Paintable> Layout::Box::create_paintable()
Note that inline nodes still have their painting logic. Since they
are not boxes, and all paintables have a corresponding box, we'll need
to come up with some other solution for them.
BlockContainer paint boxes are the only ones that have line boxes
associated, so let's not waste memory on line boxes in all the other
types of boxes.
This also adds Layout::Box::paint_box() and the more tightly typed
Layout::BlockContainer::paint_box() to get at the paint box from the
corresponding layout box.
The "paintable" state in Layout::Box was actually not safe to access
until after layout had been performed.
As a first step towards making this harder to mess up accidentally,
this patch moves painting information from Layout::Box to a new class:
Painting::Box. Every layout can have a corresponding paint box, and
it holds the final used metrics determined by layout.
The paint box is created and populated by FormattingState::commit().
I've also added DOM::Node::paint_box() as a convenient way to access
the paint box (if available) of a given DOM node.
Going forward, I believe this will allow us to better separate data
that belongs to layout vs painting, and also open up opportunities
for naturally invalidating caches in the paint box (since it's
reconstituted by every layout.)
We'll have to do something more proper to support this scenario
eventually, but for now let's at least not crash just because somebody
put an SVG <path> inside an HTML element.
Nobody makes undefined Lengths now, (although actually removing
Undefined will come in a later commit) so we can remove this parameter,
and `resolved_or_auto()`/`resolved_or_zero()`.
Our previous code roughly did this:
1. Generate a bitmap as large as the shadow would end up.
2. Paint a rectangle onto it.
3. Blur the whole bitmap.
4. Split it up and render each section.
This patch takes advantage of the fact that (aside from corners) each
horizontal or vertical strip of a box-shadow is identical to the
others, to generate and blur a much smaller bitmap - only large enough
for the four corners and 1px of central "side" in each direction. This
greatly reduces the memory footprint, and should also speed things up,
since there is much less to blur.
We also pass whether the shadow goes inside or outside the element. Only
outer shadows are rendered currently, and inner ones may want to be
handled separately from them, as they will never interfere with each
other.
Most of the time, we cannot resolve a `calc()` expression until we go to
use it. Since any `<length-percentage>` can legally be a `calc
()`, let's store it in `LengthPercentage` rather than make every single
user care about this distinction.
This is a bit of a hack to get box content to stop bleeding 1px outside
the border sometimes. We will need to come up with a more general
solution for this problem eventually.
Despite looking like it was still needed, it was only used for passing
to other calls to Length::resolved() recursively. This makes the
various `foo.resolved().resolved()` calls a lot less awkward.
(Though, still quite awkward.)
I think we'd need to separate calculated lengths out to properly tidy
these calls up, but one yak at a time. :^)