Undefined length values can default to auto in all length boxes and
we'll get the values we need. This saves us from having to deal with
undefined lengths later on in layout.
At some point we should break the style application process into
a few more formal steps, but this at least simplifies some things.
Now that we attach the document to the frame before parsing, we have
to make sure we set the encoding on the document before parsing, or
things may not turn out well.
FrameLoader now begins by constructing a DOM::Document, and then builds
a document tree inside it based on the MIME type. For text/html we pass
control to the HTMLDocumentParser as before.
This gives us access to things like window.alert() during parsing.
Fixes#3973.
After you mark a node as needing new style, there's no situation in
which we don't want a style update to happen, so just take care of
scheduling it automatically.
Instead of storing them as CSS::Lengths, we now store the resolved
values for margin/padding/border/offset top/right/bottom/left with
each Layout::NodeWithStyleAndBoxModelMetrics.
This simplifies a lot of code since it's no longer necessary to
resolve values before using them.
Just bail when encountering some unexpected character. This makes it
much more tolerable to type a stylesheet into TextEditor with live
HTML preview enabled.
This is definitely not 100% correct but I tried implementing the basic
algorithms described in CSS 2.2. It's good enough to render the penguin
on @linusg's homepage at the right size. :^)
Replaced elements are now laid out by the current formatting context.
Since the logic is almost identical in BFC and IFC, it's implemented
by static helpers in FormattingContext.
From the Web IDL spec: https://heycam.github.io/webidl/#idl-undefined
[...]
undefined constant values in IDL are represented with the `undefined`
token.
[...]
Note: This value was previously spelled `void`, and more limited in how
it was allowed to be used.
add_constructor() will already allocate an XMLHttpRequestConstructor and
update m_xhr_constructor accordingly, we don't have to do it ourselves.
This is now in line with how all the LibJS constructors work. Also make
the XHR constructor responsible for setting its "prototype" property
itself, for consistency and fail-proofing.
Previously we would only set it on the constructor we allocated manually
but which was then thrown away, leading to the property never being set
properly.
This isn't entirely symmetrical with on_load_start as it will also fire
on reloads and back/forward navigations. However, it's good enough for
some basic use cases, and we can do more sophisticated notifications
later on when we need them.
The width of a line box is the distance from the left edge of the first
fragment to the right edge of the last fragment. We don't have to loop
over all the fragments to figure this out. :^)
Instead of invoking the CSS parser every time we compute the style for
an element that has a "style" attribute, we now cache the result of
parsing the inline style whenever the "style" attribute is set.
This is a nice boost to relayout performance since we no longer hit the
CSS parser at all.
This patch removes a bunch of the less generally useful information
from layout tree dumps and puts it behind some optional bool params.
We also show layout units as integers instead of floats for now,
since fractional layout almost never happen anyway (yet) and this makes
it much easier to read.
I didn't generalize this into a helper since the HTML spec doesn't
seem to use this particular algorithm for anything else.
This makes the ACID1 test title show up correctly. :^)
Once we've generated enough lines to make it past all the floating
boxes on either side, just forget those boxes. This simplifies the
available space computation since we don't have to consider boxes
that can't vertically intersect the current line anyway.
After we've cleared past some floating elements, we should not keep
stacking new floats horizontally.
Instead, new floats after the clear should once again start at the
left or right edge of their containing block.
Within the same stacking context, positioned elements must be painted
after non-positioned ones.
I added a Layout::Node::for_each_child_in_paint_order() to help with
this since it's also needed for hit testing.
The BFC "context box" is now the outer box of the block formatting
context. Previously the context box was always the current target box,
which made it hard to reason about who was really the containing block
of whom in various places.
Note that IFC still has the containing block as its context box, this
change only affects BFC. However, to clarify the situation in IFC,
I've added a containing_block() getter than returns the context_box().
Instead of plowing through all the floating boxes within a BFC and
doing all the layout up front, do the children of each block as we go.
This will allow us to know the vertical position of the containing
block when placing floats.
I realized that we're supposed to float the boxes sideways, but not
always to y=0, so that makes it logical to share the placement logic
with other normal non-replaced blocks.
This is still pretty buggy but we're getting closer. :^)
This is definitely not fully-featured, but basically we now handle
the clear property by forcing the cleared box below the bottom-most
floated box on the relevant side.
I don't know why basing the available space between floats on the y
coordinate in the middle of each line seemed like a good idea. It just
creates situations with a few pixels of floats overlapping text!
I'm not sure what this was trying to achieve, but it was moving all
line fragments upwards and a lot of things look a lot better if we
just stop doing that.
I had guessed that floating boxes should somehow be hoisted up to the
nearest block ancestor that creates a block formatting context, but
that's just wrong. They move up to the nearest block ancestor like any
other box that's not absolutely (or fixed) positioned. :^)
Boxes can now be floated left or right, which makes text within the
same block formatting context flow around them.
We were creating way too many block formatting contexts. As it turns
out, we don't need one for every new block, but rather there's a set
of rules that determines whether a given block creates a new block
formatting context.
Each BFC keeps track of the floating boxes within it, and IFC's can
then query it to find the available space for line boxes.
There's a huge hack in here where we assume all lines are the exact
line-height. Making this work with vertically non-uniform lines will
require some architectural changes.
We were not accounting for space occupied by borders when computing
the vertical (y) position of blocks. This meant that blocks with wide
top/bottom borders could bleed into each other incorrectly.
Fix this by using the combined padding+border geometry instead of just
the padding when placing blocks on the y axis.
We were incorrectly resolving relative length units (ex, em, etc.)
against the containing block in many cases. Fix this to resolve them
against the descendant box we're currently processing.
Instead of doing a CSS property lookup for the line style of each
border edge during paint, we now cache the final CSS::LineStyle to use
in the Layout::BorderData.
In order for inline elements (e.g <span>) to contribute padding etc.
to line boxes, we now create special "leading" and "trailing" fragments
for Layout::InlineNode and size them according to the horizontal
padding values.
The height of these fragments is taken from the tallest fragment on the
line. (Perhaps we should stop having per-fragment heights and just keep
a single height per line box, but that's a separate issue.)
In order to make things look nice, we now also adjust the height of all
fragments on a line so that nobody is shorter than the CSS line-height.
Fragment painting was very limited by only being called during the
foreground paint phase. We now paint fragments as part of every phase
(and the phase is passed to paint_fragment() of course!)
We were rejecting perfectly valid z-index values like '1000' since we
were passing all CSS values through the length parser and unit-less
lengths are not valid in this context.
It's yet another hack for the ad-hoc CSS parser (its days are numbered)
but this makes the top header links on google.com actually work. :^)
Calling Frame::set_size() already triggered a relayout, so calling
layout() again right after meant we did all the work one more time.
Not being dumb like this makes resizing significantly smoother. :^)
Layout was using an outdated viewport rect that we set *after* doing
a layout due to resize. That meant that layout-in-response-to-resize
was always lagging behind the current size of the view.
The root of this problem was how Frame kept both a viewport rect
(with both scroll offset and size) and a frame size. To fix this,
only store the viewport scroll offset, and always use the frame size.
This way they can't get out of sync and the problem goes away. :^)
Fixes#4250.
Note that we're taking a shortcut here and returning the elements as an
Array instead of HTMLCollection. One day we'll have to bite the bullet
and deal with HTMLCollection, but not today.
Note that this is the old CSS2 syntax, we don't support the CSS3 syntax
just yet. Also we don't actually implement the pseudo-elements, this is
really just to make the selectors distinct from the same ones without
these pseudo-elements.
Proxy is an "exotic object" and doesn't have its own prototype. Use the
regular object prototype instead, but most stuff is happening on the
target object anyway. :^)
We were incorrectly hoisting non-inline children of inline-block boxes
to the nearest non-inline ancestor.
Since inline-block boxes are only inline on the *outside*, it's fine
for them to have non-inline children.
Eventually we should clarify these relationships by making the inside
and outside display types more explicit.
Instead of hiding JS exceptions raised on the web, we now print them to
the debug log. This will make it a bit easier to work out why some web
pages aren't working right. :^)
We didn't notice that the layout tree had disappeared after dispatching
a mousedown event, because we only checked EventHandler::layout_root()
which happily returned the *new* layout tree after a window.reload().
This patch fixes that by verifying that the frame is still showing the
same DOM's layout tree after event dispatch.
Fixes#4224.
We can now build partial layout trees (this happens for example when an
element's "display" property is programmatically toggled from "none" to
something else.)
We can't say that "no replaced boxes can have children", since that
breaks SVG. Instead, let each LayoutNode decide whether it's allowed
to have children.
Fixes#4223.
We were messing up the box tree for tables by hoisting cells up to
become children of the table row group (instead of the table row.)
Table rows are non-block boxes, and it's fine for them to have cell
(block) children.
Fixes#4225.
We already have a wrap() in EventWrapperFactory.cpp. It would be nice
to generate that at some point but it will require a lot more work on
the wrapper generator.
Inline layout nodes cannot have block children (except inline-block,
of course.)
When encountering a block box child of an inline, we now hoist the
block up to the inline's containing block, and also wrap any preceding
inline siblings in an anonymous wrapper block.
This improves the ACID2 situation quite a bit (although we still need
floats to really bring it home.)
I also took this opportunity to move all tree building logic into
Layout::TreeBuilder, to continue the theme of absolving our LayoutNode
objects of responsibilities. :^)
Specification: https://dom.spec.whatwg.org/#concept-event-dispatch
This also introduces shadow roots due to it being a requirement of
the event dispatcher.
However, it does not introduce the full shadow DOM, that can be
left for future work.
This changes some event dispatches which require certain attributes
to be initialised to a value.
Bring the names of various boxes closer to spec language. This should
hopefully make things easier to understand and hack on. :^)
Some notable changes:
- LayoutNode -> Layout::Node
- LayoutBox -> Layout::Box
- LayoutBlock -> Layout::BlockBox
- LayoutReplaced -> Layout::ReplacedBox
- LayoutDocument -> Layout::InitialContainingBlockBox
- LayoutText -> Layout::TextNode
- LayoutInline -> Layout::InlineNode
Note that this is not strictly a "box tree" as we also hang inline/text
nodes in the same tree, and they don't generate boxes. (Instead, they
contribute line box fragments to their containing block!)
This is a first (huge) step towards modernizing the layout architecture
and bringing it closer to spec language.
Layout is now performed by a stack of formatting contexts, operating on
the box tree (or layout tree, if you will.)
There are currently three types of formatting context:
- BlockFormattingContext (BFC)
- InlineFormattingContext (IFC)
- TableFormattingContext (TFC)
Document::layout() creates the initial BlockFormattingContext (BFC)
which lays out the initial containing block (ICB), and then we recurse
through the tree, creating BFC, IFC or TFC as appropriate and handing
over control at the context boundaries.
The majority of this patch is just refactoring the old logic spread out
in LayoutBlock and LayoutTableRowGroup, and turning into these context
classes instead. A lot more cleanup will be needed.
There are many architectural wins here, the main one being that layout
is no longer performed by boxes themselves, which gives us much greater
flexibility in the outer/inner layout of a given box.
As per this line in the specification:
Unless stated otherwise, a document’s encoding is the utf-8 encoding,
content type is "application/xml", URL is "about:blank", origin is an
opaque origin, type is "xml", and its mode is "no-quirks".
https://dom.spec.whatwg.org/#document
This patch makes Page weakable and allows page-less frames to exist.
Page is single-owner, and Frame is multiple-owner, so it's not sound
for Frame to assume its containing Page will stick around for its own
entire lifetime.
Fixes#3976.
This makes most operations thread safe, especially so that they
can safely be used in the Kernel. This includes obtaining a strong
reference from a weak reference, which now requires an explicit
call to WeakPtr::strong_ref(). Another major change is that
Weakable::make_weak_ref() may require the explicit target type.
Previously we used reinterpret_cast in WeakPtr, assuming that it
can be properly converted. But WeakPtr does not necessarily have
the knowledge to be able to do this. Instead, we now ask the class
itself to deliver a WeakPtr to the type that we want.
Also, WeakLink is no longer specific to a target type. The reason
for this is that we want to be able to safely convert e.g. WeakPtr<T>
to WeakPtr<U>, and before this we just reinterpret_cast the internal
WeakLink<T> to WeakLink<U>, which is a bold assumption that it would
actually produce the correct code. Instead, WeakLink now operates
on just a raw pointer and we only make those constructors/operators
available if we can verify that it can be safely cast.
In order to guarantee thread safety, we now use the least significant
bit in the pointer for locking purposes. This also means that only
properly aligned pointers can be used.
It's not possible to construct a Gfx::Bitmap with empty size. Let the
client know the new viewport rect and return before even attempting to
create new front and back bitmaps.
Also consider that we might have to paint the widget but not have a
front/back bitmap available (e.g. when only part of a scrollbar is
visible, and the inner rect is empty).
Form submissions to file:// URLs are now permitted only if the
submitting document is also a file:// URL and the form method is "get".
Form submissions to URLs with a http(s):// URL protocol are permitted.
Form submissions for all other URL protocols are rejected.
This looks at three things:
- if the type has a typedef `AllowOwnPtr', respect that
- if not, disallow construction if both of `ref()' and `unref()' are
present.
Note that in the second case, if a type only defines `ref()' or only
defines `unref()', an OwnPtr can be created, as a RefPtr of that type
would be ill-formed.
Also marks a `Performance' to explicitly allow OwnPtrs.
`DOM::XMLHttpRequest` now checks if the requested URL has the same
`Origin` as the requesting `Document`. If the requested URL is in
violation of SOP the request is rejected and an "error" `DOM::Event`
is dispatched.
Base/res/fonts/CsillaThin7x10.font was renamed to
Base/res/fonts/CsillaRegular10.font in 5abc03d, breaking the default
styles of <code> and <pre>.
The font lookup should still find a font variant when a non-existent
weight is specified, but that's another issue for another day.
Every widget now has a GUI::FocusPolicy that determines how it can
receive focus:
- NoFocus: The widget is not focusable (default)
- TabFocus: The widget can be focused using the tab key.
- ClickFocus: The widget can be focused by clicking on it.
- StrongFocus: Both of the above.
For widgets that have a focus proxy, getting/setting the focus policy
will affect the proxy instead.
This fixes an assertion in TextEditor when changing the system theme,
since that would trigger a repaint request for the HTML preview widget
which may not have backing unless it's actually been used to perform
HTML (or Markdown) preview yet.
Ref-counted objects must not be stack allocated. Make DOM::Document's
constructor private to avoid this issue. (I wish we could mark classes
as heap-only..)
When a document reaches ref_count==0, we will now remove all of the
descendant nodes from the document, and also break all the explicit
links (such as the currently hovered element.)
Basically, DOM nodes will keep the document alive even after the
document reaches ref_count==0. This allows JS wrappers to stay alive
and keep the document alive as well. This matches the behavior of
at least some other browsers.
This patch also adds a bunch of sanity checking assertions around
DOM teardown, to help catch mistakes in the future.
Fixes#3771.
DOM::Node now points to its LayoutNode with a WeakPtr.
LayoutNode points to its DOM::Node and DOM::Document with RefPtrs.
Layout trees come and go in response to various events, so the DOM tree
already has to deal with that. The DOM should always live at least as
long as the layout tree, so this patch enforces that assumption by
making layout nodes keep their corresponding DOM objects alive.
This may not be optimal, but it removes a lot of ambiguous raw pointer
action which is not worth accomodating.
Oops, it seems like I implemented all of the "nodes keep the document
alive" mechanism except the part where the functions are actually
called. :^)
Fixes#3811.
This reverts my previous commit in WebServer and fixes the whole issue
in a much better way. Instead of having the MIME type guesser take a
URL (which we don't actually have in the WebServer at that point),
just take a path as a StringView.
Also, make use of the case-insensitive StringView::ends_with() :^)
Instead of just ripping out the root of the layout tree from its RefPtr
in Document, actually go through the DOM and gather up all the layout
nodes. Then destroy them all in one swoop.
Also, make sure to do this when detaching Document from Frame,
to enforce the invariant that layout only occurs in framed documents.
These happen right after "DOMContentLoaded" for now, which is incorrect
since they should really wait until subresources have loaded.
However, this makes a bunch of things work already so let's do it.
We were never wrapping and using the actual DOM::Event but instead
wrapped the *target* twice and passed it to the event listener callback,
as this value and as argument.
This unbreaks "fun demo" and "canvas path quadratic curve test" - and
event dispatching in general, of course :^)
Fixes#3721.
In addition to being reference-counted, all nodes that are part of a
document must also keep the document alive.
This is achieved by adding a second ref-count to the Document object
and incrementing/decrementing it whenever a node is created/destroyed
in that document.
This brings us much closer to a proper DOM lifetime model, although
the JS bindings still need more work.
This allows layout nodes to do some setup before their children paint,
and cleanup after their children paint. This will be used for SVG
components, where their attributes (like stroke width, fill color, etc)
need to be correctly propogated to layout nodes down the line.
This is a hack to stop chewing CPU on sites that use a font we don't
have and have a lot of text or changes text often.
Examples are the Serenity 2nd birthday page and the JS specification.
Previously we'd only pick up background-image when it was part of the
background shorthand.
CSS property application remains hackish, lots of room for improvement
in this area. :^)
When the user right-clicks on an image, you might want to show a
special context menu, separate from the regular link context menu.
This patch only implements enough of the functionality to get this
working in a single-process context.
This is the origin timestamp of the same monotonic clock used for the
performance.now() timestamp.
I got a little confused while implementing this, since the numbers are
very low. That's because it uses the CLOCK_MONOTONIC system clock,
which we start counting from 0 at boot. :^)
This patch introduces the HighResolutionTime namespace which is home to
the Performance object (exposed via window.performance)
performance.now() is currently the only function, and it returns the
number of milliseconds since the window object was constructed. :^)
More work on decoupling the general runtime from Interpreter. The goal
is becoming clearer. Interpreter should be one possible way to execute
code inside a VM. In the future we might have other ways :^)
This patch moves the exception state, call stack and scope stack from
Interpreter to VM. I'm doing this to help myself discover what the
split between Interpreter and VM should be, by shuffling things around
and seeing what falls where.
With these changes, we no longer have a persistent lexical environment
for the current global object on the Interpreter's call stack. Instead,
we push/pop that environment on Interpreter::run() enter/exit.
Since it should only be used to find the global "this", and not for
variable storage (that goes directly into the global object instead!),
I had to insert some short-circuiting when walking the environment
parent chain during variable lookup.
Note that this is a "stepping stone" commit, not a final design.
With this patch, we now enforce basic same-origin policy for this one
<iframe> attribute.
To make it easier to add more attributes like this, I've added an
extended IDL attribute ("[ReturnNullIfCrossOrigin]") that does exactly
what it sounds like. :^)
Getting ready for some extremely basic same-origin policy stuff,
this initial implementation simply checks that two origins have
identical protocol, host and port.
Taking a big step towards a world of multiple global object, this patch
adds a new JS::VM object that houses the JS::Heap.
This means that the Heap moves out of Interpreter, and the same Heap
can now be used by multiple Interpreters, and can also outlive them.
The VM keeps a stack of Interpreter pointers. We push/pop on this
stack when entering/exiting execution with a given Interpreter.
This allows us to make this change without disturbing too much of
the existing code.
There is still a 1-to-1 relationship between Interpreter and the
global object. This will change in the future.
Ultimately, the goal here is to make Interpreter a transient object
that only needs to exist while you execute some code. Getting there
will take a lot more work though. :^)
Note that in LibWeb, the global JS::VM is called main_thread_vm(),
to distinguish it from future worker VM's.
This will be inherited by documents and workers, to provide a common
abstraction for script execution. (We don't have workers yet, but we
might as well make this little space for them now to simplify things
down the road.)
Instead of everyone overriding save_to() and set_property() and doing
a pretty asymmetric job of implementing the various properties, let's
add a bit of structure here.
Object properties are now represented by a Core::Property. Properties
are registered with a getter and setter (optional) in constructors.
I've added some convenience macros for creating and registering
properties, but this does still feel a bit bulky. We'll have to
iterate on this and see where it goes.
Following in the footsteps of <input type=checkbox>, this patch adds
LayoutButton which implements a basic push button using LibGfx styling
primitives.
- After letting a LayoutNode handle a mouseup, re-do the hit test
since things may have changed.
- Make sure we always update the document's hovered node.
After dispatching a "change" event due to the checked state being
modified, we may have been removed from the layout tree.
Make LayoutCheckBox protect itself to prevent this from crashing.
Also, add a little test page for checkboxes. :^)
This is implemented entirely inside LibWeb, there is no GUI::CheckBox
widget instantiated, unlike other input types. All input types should
be moved to this new style of implementation.
To implement form controls internally in LibWeb (necessary for multi
process forms), we'll need the ability to handle events since we can't
rely on LibGUI widgets anymore.
A LayoutNode can now override wants_mouse_events() and if it returns
true, it will now receive mousedown, mousemove and mouseup events. :^)
For now, the new DOM::EventDispatcher is very simple, it just iterates
over the set of listeners on an EventTarget and invokes the callbacks
as it goes.
This simplifies EventTarget subclasses since they no longer have to
implement the callback mechanism themselves.
"self" is a way to refer to the global object that will work in both
a window context and a web worker context.
"frames" apparently used to return a list of frame objects according
to MDN, but it now just returns the window object.
Now that LibJS's .prettierrc has been moved to the repository root (as
we start having .js files in /res), we don't need to keep a second,
identical copy for the LibWeb tests.
The fact that a `MarkedValueList` had to be created was just annoying,
so here's an alternative.
This patchset also removes some (now) unneeded MarkedValueList.h includes.