wrap() is now basically a no-op so we should stop using it everywhere
and eventually remove it. This patch removes uses of wrap() in
non-generated code.
Unlike ensure_web_prototype<T>(), the cached version doesn't require the
prototype type to be fully formed, so we can use it without including
the FooPrototype.h header. It's also a bit less verbose. :^)
This is a monster patch that turns all EventTargets into GC-allocated
PlatformObjects. Their C++ wrapper classes are removed, and the LibJS
garbage collector is now responsible for their lifetimes.
There's a fair amount of hacks and band-aids in this patch, and we'll
have a lot of cleanup to do after this.
This patch moves the following things to being GC-allocated:
- Bindings::CallbackType
- HTML::EventHandler
- DOM::IDLEventListener
- DOM::DOMEventListener
- DOM::NodeFilter
Note that we only use PlatformObject for things that might be exposed
to web content. Anything that is only used internally inherits directly
from JS::Cell instead, making them a bit more lightweight.
Removing the FIXME'd code in b99cc7d was a bit too eager, and relying on
the main thread VM's current realm only works when JS is being executed.
Restore a simplified version of the old code to determine the realm this
time instead of the global object, following the assumptions already
made in get_current_value_of_event_handler() regarding what kind of
event target 'this' can be.
Similar to create() in LibJS, wrap() et al. are on a low enough level to
warrant passing a Realm directly instead of relying on the current realm
from the VM, as a wrapper may need to be allocated while no JS is being
executed.
This is a continuation of the previous three commits.
Now that create() receives the allocating realm, we can simply forward
that to allocate(), which accounts for the majority of these changes.
Additionally, we can get rid of the realm_from_global_object() in one
place, with one more remaining in VM::throw_completion().
This is a continuation of the previous two commits.
As allocating a JS cell already primarily involves a realm instead of a
global object, and we'll need to pass one to the allocate() function
itself eventually (it's bridged via the global object right now), the
create() functions need to receive a realm as well.
The plan is for this to be the highest-level function that actually
receives a realm and passes it around, AOs on an even higher level will
use the "current realm" concept via VM::current_realm() as that's what
the spec assumes; passing around realms (or global objects, for that
matter) on higher AO levels is pointless and unlike for allocating
individual objects, which may happen outside of regular JS execution, we
don't need control over the specific realm that is being used there.
This patch implements the "create a new browsing context" function from
the HTML spec and replaces our existing logic with it.
The big difference is that browsing contexts now initially navigate to
"about:blank" instead of starting out in a strange "empty" state.
This makes it possible for websites to create a new iframe and start
scripting inside it right away, without having to load an URL into it.
The way we've been creating DOM::Document has been pretty far from what
the spec tells us to do, and this is a first big step towards getting us
closer to spec.
The new Document::create_and_initialize() is called by FrameLoader after
loading a "text/html" resource.
We create the JS Realm and the Window object when creating the Document
(previously, we'd do it on first access to Document::interpreter().)
The realm execution context is owned by the Environment Settings Object.
This state is less static than we originally assumed, and there are
special formatting context-specific rules that say certain sizes are
definite in special circumstances.
To be able to support this, we move the has-definite-size flags from
the layout node to the UsedValues struct instead.
Absolutely positioned boxes are handled by the BFC destructor, so we
need to make sure the ICB BFC is destroyed if we want these boxes
to get laid out.
Step 19 of node removal was missing, which allows the mutations of the
descendants of the removed node to still be observed by the parent.
Step 20 of node removal queued the mutation record for the removed
node instead of it's parent. Since queuing takes place after the node
is removed from the tree, the mutation record would be lost as the only
inclusive ancestor of the node at this point is only the node itself.
This takes care of two FIXMEs and fixes an issue on Google Docs where
we'd mix boxes from different documents in the same layout tree.
(This happened because shadow trees remained attached to their old
document when their host was adopted.)
Each of these strings would previously rely on StringView's char const*
constructor overload, which would call __builtin_strlen on the string.
Since we now have operator ""sv, we can replace these with much simpler
versions. This opens the door to being able to remove
StringView(char const*).
No functional changes.
Instead, put them in a Vector<OwnPtr<NodeState>>. Each layout node
has a unique index into the vector. It's a simple serial ID assigned
during layout tree construction. Every new layout restarts the sequence
at 0 for the next ICB.
This is a huge layout speed improvement on all content.
Previously we forwarded all event handler attributes to Window from
these two elements, however, we are only supposed to forward blur,
error, focus, load, resize and scroll.
Used by Google seemingly almost all around account sign in and
management. The modern sign in page has this near the beginning:
```html
<base href="https://accounts.google.com">
```
All of the XHRs performed by sign in are relative URLs to this
base URL. Previously we ignored this and did it relative to the
current URL, causing the XHRs to 404 and sign in to fall apart.
I presume they do this because you can access the sign in page
from multiple endpoints, such as `/ServiceLogin` and
`/o/oauth2/auth/identifier`
- Don't add multiple numbers to nested steps, just the innermost one
(as rendered in the HTML document)
- "Otherwise" comments go before the else, not after it
- "FIXME:" goes before step number, not between it and the comment text
- Always add a period between number and comment text
The majority of these were introduced in #13756, but some unrelated ones
have been updated as well.
The goal here is to move the parser-internal classes into this namespace
so they can have more convenient names without causing collisions. The
Parser itself won't collide, and would be more convenient to just
remain `CSS::Parser`, but having a namespace and a class with the same
name makes C++ unhappy.
This element doesn't actually support anything at the moment, but it
still massively speeds up painting performance on Wikipedia! :^)
How? Because we no longer paint SVG <path> elements found inside
<clipPath> elements. SVGClipPathElement::create_layout_node() returns
nullptr which stops the layout tree builder from recursing further into
the subtree, and so the <path> element never gets a layout or paint box.
Mousing over Wikipedia now barely break 50% CPU usage on my machine :^)
There were two main issues with these functions:
1. They were not updating layout before inspecting metrics.
2. They were not returning viewport metrics for the root and body
elements when appropriate.
Block the replacement of the favicon by the default favicon loader
when a favicon that is loaded through a link tag is already active.
This way, the favicon in the link tags will be prioritized against
the default favicons from `/favicon.ico` or the seranity default icon.
When a favicon has been loaded, trigger a favicon update on
document level. Of all the link tags in the header, the last
favicon that is load should be shown.
When the favicon could not be loaded, load the next icon in reverse tree
order.
If the font resource finishes loading we need to make sure the element
using it gets a chance to re-layout, even if the font-family property
didn't change.
This is now the source of truth for 'user enabled/disabled scripting',
but it has to ask the window's page, which actually stores the setting.
Also use this new functionality in two places where it was previously
marked as a FIXME.
There's no need to have a custom is_scripting_enabled() for the
Document class, as it (indirectly) inherits from Node.
Also, let's not hardcode false here :^)
When parsing the "style" attribute on elements, we'd previously ask the
CSS parser for a PropertyOwningCSSStyleDeclaration. Then we'd create a
new ElementCSSInlineStyleDeclaration and transfer the properties from
the first object to the second object.
This patch teaches the parser to make ElementCSSInlineStyleDeclaration
objects directly.
HTMLObjectElement will need to be both a FormAssociatedElement and a
BrowsingContextContainer. Currently, both of these classes inherit from
HTMLElement. This can work in C++, but is generally frowned upon, and
doesn't play particularly well with the rest of LibWeb.
Instead, we can essentially revert commit 3bb5c62 to remove HTMLElement
from FormAssociatedElement's hierarchy. This means that objects such as
HTMLObjectElement individually inherit from FormAssociatedElement and
HTMLElement now.
Some caveats are:
* FormAssociatedElement still needs to know when the HTMLElement is
inserted into and removed from the DOM. This hook is automatically
injected via a macro now, while still allowing classes like
HTMLInputElement to also know when the element is inserted.
* Casting from a DOM::Element to a FormAssociatedElement is now a
sideways cast, rather than directly following an inheritance chain.
This means static_cast cannot be used here; but we can safely use
dynamic_cast since the only 2 instances of this already use RTTI to
verify the cast.
If invoking a NodeFilter ends up deleting a node from the DOM, it's not
enough to only adjust the NodeIterator reference nodes in the
pre-removing steps. We must also adjust the current traversal pointer.
This is not in the spec, but it's how other engines behave, so let's do
the same.
I've encapsulated the Node + before-or-after-flag in a struct called
NodePointer so that we can use the same pre-removing steps for both the
traversal pointer and for the NodeIterator's reference node.
Note that when invoking the NodeFilter, we have to remember the node we
passed to the filter function, so that we can return it if accepted by
the filter.
This gets us another point on Acid3. :^)
This will help reduce the quite repetitive pattern of:
auto result_or_error = dom_node->do_something();
if (result_or_error.is_exception())
return result_or_error.exception();
auto result = result_or_error.release_value();
Similar to LibJS completions, this adds an alias to the error accessors.
This also removes the requirement on release_value() for ValueType to
not be Empty, which we also had to do for TRY compatibility in LibJS.
We were passing the wrong length argument to substring() when
stringifying a range where start and end are the same text node.
Also, make sure we visit all the contained text nodes when appending
them to the output.
This was caught by an Acid3 subtest.
I came across some websites that change an elements CSS "opacity" in
their :hover selectors. That caused us to relayout on hover, which we'd
like to avoid.
With this patch, we now check if a property only affects the stacking
context tree, and if nothing layout-affecting has changed, we only
invalidate the stacking context tree, causing it to be rebuilt on next
paint or hit test.
This makes :hover { opacity: ... } rules much faster. :^)
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.
When updating layout inside a nested browsing context, try first to
perform layout in the parent document (the nested browsing context's
container's document).
This ensures that nested browsing contexts have the right viewport
dimensions in case the parent layout changes them somehow.
We were hanging on to element inline style, even after the style
attribute was removed. This made inline style sticky and impossible to
remove. This patch fixes that. :^)
Add a flag to DOM::Document that means the whole document needs a style
update. This saves us the trouble of traversing the entire DOM to mark
all nodes as needing a style update.
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.
It makes no sense to require passing a global object and doing a stack
space check in some cases where running out of stack is highly unlikely,
we can't recover from errors, and currently ignore the result anyway.
This is most commonly in constructors and when setting things up, rather
than regular function calls.
This was causing us to miss layout invalidations. With this fixed, we
can remove the invalidation from Element::recompute_style() along with
the associated FIXME.
Thanks to Idan for spotting this! :^)
Instead of invalidating style for the entire document, we now locate the
nearest common ancestor between the old and new innermost hovered node,
and only invalidate that ancestor and its descendants.
This drastically reduces the amount of style update work when mousing
around on GitHub (and any other pages, really.) It's actually really
really snappy now. Very cool! :^)
Use the new CSS::property_affects_layout() helper to figure out if we
actually need to perform a full relayout after recomputing style.
There are three tiers of required invalidation after an element receives
new style: none, repaint only, or full relayout.
This avoids the need to rebuild the layout tree (and perform layout on
it) when trivial properties like "color" etc are changed.
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.
We were not actually walking past the first ancestor when setting
child-needs-update bit upwards.
Also, let's walk all the way to the root, even if there's a
child-needs-update bit already set. This ensures that we always leave
this function with the ancestor chain in a sane state.
- Access members directly instead of going through accessors,
avoiding a lot of redundant traversal done by accessors.
- Cross shadow boundaries and make sure that shadow trees get their
dirty bits updated as well.
- Do the minimum amount of traversal needed when setting the "child
needs style update" bit upwards through ancestors.
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.
The style update mechanism was happily ignoring shadow subtrees.
Fix this by checking if an element has a shadow root, and recursing into
it if needed.
Before this change, style invalidation didn't propagate upwards across
shadow boundaries, so our shadow trees were sitting there with invalid
style, never actually getting updated.
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.
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.)
Let's get this right before trying to make it fast. This patch removes
the code that tried to do less work when an element's style changes,
and instead simply invalidates the entire document.
Note that invalidations are still coalesced, and will not be
synchronized until update_style() and/or update_layout() is used.
If the style is dirty, update_style() may cause layout to become dirty.
Therefore we must always update style when updating layout, to ensure
up-to-date results.
This forces us to recompute style everywhere, since all kinds of
selectors may produce different results now.
In the future, we should look at narrowing down the invalidation that
occurs here, but for now let's just invalidate everything and make the
results correct before worrying about performance.
These steps run when a node is about to be removed from its parent,
and adjust the position of any live NodeIterators so that they don't
point at a now-removed node.
Note that while this commit implements what's in the DOM specification,
the specification doesn't fully match what other browsers do.
Spec bug: https://github.com/whatwg/dom/issues/907
This patch adds NodeIterator (created via Document.createNodeIterator())
which allows you to iterate through all the nodes in a subtree while
filtering with a provided NodeFilter callback along the way.
This first cut implements the full API, but does not yet handle nodes
being removed from the document while referenced by the iterator. That
will be done in a subsequent patch.