If a LayoutNode is split into line box fragments, we need to walk our
fragments and invalidate them. It was not enough to do this only for
LayoutBox nodes.
To streamline the layout tree and remove irrelevant data from classes
that don't need it, this patch adds two new LayoutNode subclasses.
LayoutNodeWithStyleAndBoxModelMetrics should be inherited by any layout
node that cares about box model metrics (margin, border, and padding.)
LayoutBox should be inherited by any layout node that can have a rect.
This makes LayoutText significantly smaller (from 140 to 40 bytes) and
clarifies a lot of things about the layout tree.
I'm also adding next_sibling() and previous_sibling() overloads to
LayoutBlock that return a LayoutBlock*. This is okay since blocks only
ever have block siblings.
Do also note that the semantics of is<T> slightly change in this patch:
is<T>(nullptr) now returns true, to facilitate allowing to<T>(nullptr).
This patch makes it possible to call Node::invalidate_style() and have
that node and all of its ancestors recompute their style.
We then figure out if the new style is visually different from the old
style, and if so do a paint invalidation with set_needs_display().
Note that the "are they visually different" code is very incomplete!
Use this to make hover effects a lot more efficient. They no longer
cause a full relayout+repaint, but only a style invalidation.
Style invalidations are still quite heavy though, and there's a lot of
room for improvement there. :^)
This is currently very aggressive. Whenever the Document's hovered node
changes, we invalidate all style and do a full relayout.
It does look cool though. So cool that I'm adding it to the default
stylesheet. :^)
Replaced elements will now properly create line breaks when they use up
the available horizontal space.
This fixes an issue with <img>'s lining up instead of breaking.
If you do a layout and it turns out that the page contents don't fit in
the viewport vertically, we add a vertical scrollbar. Since the
scrollbar takes up some horizontal space, this reduces the amount of
space available to the page. So we have to do a second layout pass. :^)
Fixes#650.
When style is invalidated (for example when an external stylesheet
finishes loading) we delete the whole layout tree and build a new one.
This is necessary since the new style information may result in a
different layout tree.
When layout is invalidated (window resized, image dimensions learned,
etc..) we keep the existing layout tree but run the layout algorithm
once again.
There's obviously lots of room for improvement here. :^)
The layout root is now kept alive via Document::m_layout_root.
This will allow us to do more layout-related things inside the inner
layer of LibHTML without reaching out to the HtmlView.
I'd like to keep HtmlView at a slightly higher level, to prevent it
from getting too complex.
This patch also fixes accidental disconnection of the layout tree from
the DOM after doing a layout tree rebuild. ~LayoutNode() now only
unsets the DOM node's layout_node() if it's itself.
This patch adds the CharacterData subclass of Node, which is now the
parent class of Text and a new Comment class.
A Comment node is one of these in HTML: <!--hello friends-->
Since these occur somewhat frequently on the web, we need to be able
to parse them.
This patch also adds a child rejection mechanism to the DOM tree.
Nodes can now override is_child_allowed(Node) and return false if they
don't want a particular Node to become a child of theirs. This is used
to prevent Document from taking on unwanted children.
The <br> element will produce a special LayoutBreak node in the layout
tree, which forces a break in the line layout whenever encountered.
This patch also makes LayoutBlock use the current line-height as the
minimum effective height for each line box. This ensures that having
multiple <br> elements in a row doesn't create 0-height line boxes.
We currently hard-code the line height to 140% of the font glyph height
and this patch doesn't fix the hard-coding, but at least moves it out
of LayoutText and into StyleProperties where it can be re-used until
the day we go and do a proper implementation of CSS line-height. :^)
This patch removes the hard-coded hack for "display: list-item" from
LayoutBlock and adds LayoutListItem and LayoutListItemMarker.
Elements with "display: list-item" now generate a LayoutListItem, which
may then also generate a LayoutListItemMarker if appropriate. :^)
For now this is simply a counter+hook exposed by ResourceLoader and
shown in the Browser status bar.
This is not very nuanced, and it would be nice to expose more info so
we could eventually do something like a progress bar.
This is definitely not the ideal ownership model here, but it's
something we'll have to iterate on as the engine grows.
At least this prevents us from leaking the entire world. :^)
We don't want to deal with document().frame() being null inside layout
tree code, so this makes sure we tear it down before the frame has a
chance to get nulled out.
Just in time for Serenity's 1st birthday, here is the <blink> element!
This patch adds a bunch of different mechanisms to enable partial
repaints of the layout tree (LayoutNode::set_needs_display()))
It also adds LayoutNode::is_visible(), which can be toggled to prevent
a LayoutNode from rendering anything (it still takes up space though.)
Node.normalize() is a standard DOM API that coalesces Text nodes.
To avoid clashing with that, rename it to fixup().
This patch also makes it happen automagically as part of parsing.
Before breaking the Text node contents into words, collapse all of the
whitespace into single space (' ') characters. This fixes broken
rendering of paragraphs with newlines in them. :^)
This makes it consistent with how http:// callbacks are fired.
It would probably be fine to have file:// be synchronous, but at the
same time it's nice to have consistency.
Instead of using string everywhere, have the CSS parser produce enum
values, since they are a lot nicer to work with.
In the future we should generate most of this code based on a list of
supported CSS properties.
This patch adds basic support for external stylesheets. It currently
only works with file:// URLs.
We do a synchronous full relayout after loading a stylesheet, which is
definitely on the aggressive side, but it gives us something to work
on improving. :^)
Since LayoutText always inherits style, it shouldn't store any style of
its own. This patch adds a LayoutNodeWithStyle class to sit between
LayoutNode and everyone who wants to inherit from LayoutNode except
LayoutText :^)
Since LayoutText can never have children, we also know that the parent
of any LayoutNode is always going to be a LayoutNodeWithStyle.
So this patch makes LayoutNode::parent() return LayoutNodeWithStyle*.
This is a lot nicer than first_child_with_tag_name(...).
The is<T>(Node) functions are obviously unoptimized at the moment,
and this is about establishing pleasant patterns right now. :^)
This patch implements two more selector features:
- "div + p" matches the <p> sibling immediately after a <div>.
- "div ~ p" matches all <p> siblings after a <div>.
These helpers return the next/previous sibling Node that's actually an
element. This will be useful in the CSS engine since CSS doesn't care
about text nodes.
This allows any external actor to signal that the document layout may be
stale. This can be used when loading resources, changing the size or
placement of an element, adding/removing nodes, or really any time.
Since LayoutText inherits all of its style information from its parent
Element anyway, it makes more sense to load the font at a higher level.
And since the font depends only on the style and nothing else, this
patch moves font loading (and caching) into StyleProperties. This could
be made a lot smarter to avoid loading the same font many times, etc.
There's no need for LayoutText to inherit from LayoutInline.
I had the wrong idea here: I was thinking that everything that can be
laid out inline should inherit from LayoutInline, but that's clearly
not sufficient for something like LayoutReplaced which can be laid out
in either way.
"div p" now generates a Selector with two components where the second
component is a type=TagName, value="p", relation=Descendant.
We still don't handle matching of these, but at least we parse them.
The default style for "a" tags now has { color: -libhtml-link; }.
We implement this vendor-specific property by querying the containing
document for the appropriate link color.
Currently we only use the basic link color, but in the future this can
be extended to remember visited links, etc.
The parser now kinda recognizes immediate child selectors, !important,
and various other little things.
It's still extremely far from robust and/or correct. :^)
Okay, I got that a bit wrong. Here's what CSS 2.1 says:
"The properties of anonymous boxes are inherited from the enclosing
non-anonymous box. Non-inherited properties have their initial value."
This patch implements a better behavior by only copying the inherited
properties from the parent style.
LayoutReplaced objects can now participate in inline layout.
It's very hackish, but basically LayoutReplaced will just add itself to
the last line in the containing block.
This patch gets rid of the idea that only LayoutInline subclasses can
be split into lines, by moving the split_into_lines() virtual up to
LayoutNode and overriding it in LayoutReplaced.
Instead of branching on the Node type, let subclasses decide how their
layout nodes get constructed.
This will allow elements to create custom layout nodes if they want.
This patch adds parsing of <img> into HTMLImageElement objects.
It also adds LayoutImage and its parent class LayoutReplaced, which is
going to represent CSS "replaced elements."
And do the filling before translating the painter.
This fixes a bug where scrolling down in the Help app would render the
bottom part of the page with a different background color.
You can now pass a file:/// URL to HtmlView and it will take care of
the loading logic for you. Each Document remembers the URL it was
loaded from, which allows us to also have reload().
This patch also adds a very simple function for resolving relative
URL's into absolute ones.
Instead of HtmlView clients having to worry about parsing and loading
the default CSS, just take care of it inside StyleResolver.
The default style is automatically inserted into the stylesheet list,
at the very start, so everyone else gets a chance to override it.
Add a simple is_inherited_property(name) lookup function and use that
to implement CSS property inheritance.
It's a bug that "text-decoration" is inherited, but we currently rely
on this in order to propagate e.g underline into linkified line boxes.
This patch implements basic support for presentational hints, which are
old-school HTML attributes that affect style.
You add support for a presentational hint attribute by overriding
Element::apply_presentational_hints(StyleProperties&) and setting all
of the corresponding CSS properties as appropriate.
To make the background color fill the entire document, not just the
bounds of the <body> element's LayoutNode, we special-case it in the
HtmlView::paint_event() code for now. I'm not entirely sure what the
nicest solution would be, but I'm sure we'll discover it eventually.
There was nothing left in ComputedStyle except the box model metrics,
so this patch gives it a more representative name.
Note that style information is fetched directly from StyleProperties,
which is basically the CSS property name/value pairs that apply to
an element.
Each HtmlView now has a main_frame(), which represents the main frame
of the web page. Frame inherits from TreeNode<Frame>, which will allow
us to someday implement a Frame tree (to support the <frame> element.)
This patch makes StyleProperties heap-allocated and ref-counted so that
a LayoutNode can be without one. The ref-counting also allows anonymous
blocks to share style with their parent block.
LayoutText never needs a StyleProperties, since text always inherits
style from its parent element. This is handled by style_properties().
This is not ideal, as refusing to break lines will easily overflow the
containing block, and we have no way of dealing with overflow yet.
But as long as you provide enough width, it does look nice. :^)
Font::width(string) will subtract one Font::glyph_spacing() from the
result, since we don't put any spacing after the last glyph.
This was messing up text layout here, since we assumed each glyph
width also included the glyph spacing.
Inline layout is now done by LayoutBlock. Blocks with inline children
will split them into line boxes during layout.
A LayoutBlock can have zero or more LineBox objects. Each LineBox
represents one visual line.
A LineBox can have any number of LineBoxFragment children. A fragment
is an offset+length into a specific LayoutNode.
To paint a LayoutBlock with inline children, we walk its line boxes,
and walk their fragments, painting each fragment at a time by calling
LineBoxFragment::render(), which in turn calls the LayoutNode via
LayoutText::render_fragment(). Hit testing works similarly.
This is very incomplete and has many bugs, but should make it easier
for us to move forward with this code.
You can now query Document::title() to get a String containing whatever
is inside the document's <title> tag.
In support of this, this patch adds the <html>, <head> and <title>
elements.
We now show a tooltip for the hovered node's enclosing HTML element's
title attribute, if one is present.
This patch also adds HTMLHeadingElement. The tags h1-h6 will now create
the right kind of objects.
Every LayoutNode indirectly belongs to some Document. For anonymous
LayoutNodes, we simply traverse the parent chain until we find someone
with a Node from which we can get a Document&.
LayoutText can't simply rely on its LayoutNode::rect() for hit testing.
Instead, we have to iterate over the individual runs and see if we're
hitting any of them.
Also, LayoutNode::hit_test() now always recurses into children, since
we can't trust the rect() to tell the truth (inline rects are wrong.)
Also remove the color values from the ComputedStyle object and get them
via StyleProperties instead.
At the moment, we only handle colors that Color::from_string() parses.
This is a simple command that can be used to display HTML from a given
file, or from the standard input, in an HtmlView. It replaces the `tho`
(test HTML output) command.
This currently uses a gross hack where it subtracts 11px from the
previous sibling bottom to calculate its top. This should be fixed
by switching to a proper two-phase line layouting model, were we
first distribute inline elements into lines and figure out their
horizontal positions and heights; then compute the needed line
heights and position inline elements there vertically.
This also fixes another bug with inline wrappers. Namely,
we should only add inline wrappers if a block node has
both non-block (inline or text) and block children.
LayoutBlock::inline_wrapper() is supposed to return an inline wrapper,
a special anonymous block element intended to wrap inline children of
a block element that also has block children. Add a check for whether
the existing block child element is anonymous (refers to a DOM node),
and if it's not create a new anonymous wrapper.
This was a workaround to be able to build on case-insensitive file
systems where it might get confused about <string.h> vs <String.h>.
Let's just not support building that way, so String.h can have an
objectively nicer name. :^)
Instead of LibGUI and WindowServer building their own copies of the drawing
and graphics code, let's it in a separate LibDraw library.
This avoids building the code twice, and will encourage better separation
of concerns. :^)