The purpose of this new object will be to keep track of various states
during an ongoing layout.
Until now, we've been updating layout tree nodes as we go during layout,
which adds an invisible layer of implicit serialization to the whole
layout system.
My idea with FormattingState is that running layout will produce a
result entirely contained within the FormattingState object. At the end
of layout, it can then be applied to the layout tree, or simply queried
for some metrics we were trying to determine.
When doing subtree layouts to determine intrinsic sizes, we will
eventually be able to clone the current FormattingState, and run the
subtree layout in isolation, opening up opportunities for parallelism.
This first patch doesn't go very far though, it merely adds the object
as a skeleton class, and makes sure the root BFC has one. :^)
This implements basic support for dynamic markup insertion, adding
* Document::open()
* Document::write(Vector<String> const&)
* Document::writeln(Vector<String> const&)
* Document::close()
The HTMLParser is modified to make it possible to create a
script-created parser which initially only contains a HTMLTokenizer
without any data. Aditionally the HTMLParser::run method gains an
overload which does not modify the Document and does not run
HTMLParser::the_end() so that we can reenter the parser at a later time.
Furthermore all FIXMEs that consern the insertion point are implemented
wich is defined in the HTMLTokenizer. Additionally the following
member-variables of the HTMLParser are now exposed by getter funcions:
* m_tokenizer
* m_aborted
* m_script_nesting_level
The HTMLTokenizer is modified so that it contains an insertion
point which keeps track of where the next input from the Document::write
functions will be inserted. The insertion point is implemented as the
charakter offset into m_decoded_input and a boolean describing if the
insertion point is defined. Functions to update, check and {re}store the
insertion point are also added.
The function HTMLTokenizer::insert_eof is added to tell a script-created
parser that document::close was called and HTMLParser::the_end() should
be called.
Lastly an explicit default constructor is added to HTMLTokenizer to
create a empty HTMLTokenizer into which data can be inserted.
ACID3 test page throws exception about document.write. Let's at least
get rid of it by defining these stubs.
I added document.writeln too because it is similar.
BFC currently has a number of architectural issues due to it being
responsible for setting the dimensions of the BFC root.
This patch moves the logic for setting up the ICB from BFC to Document.
This patch introduces the StyleComputer::RuleCache, which divides all of
our (author) CSS rules into buckets.
Currently, there are two buckets:
- Rules where a specific class must be present.
- All other rules.
This allows us to check a significantly smaller set of rules for each
element, since we can skip over any rule that requires a class attribute
not present on the element.
This takes the typical numer of rules tested per element on Discord from
~16000 to ~550. :^)
We can definitely improve the cache invalidation. It currently happens
too often due to media queries. And we also need to make sure we
invalidate when mutating style through CSSOM APIs.
This overrides the JS host hooks to follow the spec for queuing
promises, making/calling job callbacks, unhandled promise rejection
handling and FinalizationRegistry queuing.
This also allows us to drop the on_call_stack_emptied hook in
Document::interpreter().
The environment settings object is effectively the context a piece of
script is running under, for example, it contains the origin,
responsible document, realm, global object and event loop for the
current context. This effectively replaces ScriptExecutionContext, but
it cannot be removed in this commit as EventTarget still depends on it.
https://html.spec.whatwg.org/multipage/webappapis.html#environment-settings-object
This commit removes all exception related code:
Remove VM::exception(), VM::throw_exception() etc. Any leftover
throw_exception calls are moved to throw_completion.
The one method left is clear_exception() which is now a no-op. Most of
these calls are just to clear whatever exception might have been thrown
when handling a Completion. So to have a cleaner commit this will be
removed in a next commit.
It also removes the actual Exception and TemporaryClearException classes
since these are no longer used.
In any spot where the exception was actually used an attempt was made to
preserve that behavior. However since it is no longer tracked by the VM
we cannot access exceptions which were thrown in previous calls.
There are two such cases which might have different behavior:
- In Web::DOM::Document::interpreter() the on_call_stack_emptied hook
used to print any uncaught exception but this is now no longer
possible as the VM does not store uncaught exceptions.
- In js the code used to be interruptable by throwing an exception on
the VM. This is no longer possible but was already somewhat fragile
before as you could happen to throw an exception just before a VERIFY.
Instead of making each Layout::Node compute style for itself, we now
compute it in TreeBuilder before even calling create_layout_node().
For non-element DOM nodes, we create the style and layout tree node
in TreeBuilder. This allows us to move create_layout_node() from
DOM::Node to DOM::Element.
This also refactors interpreter creation to follow
InitializeHostDefinedRealm, but I couldn't fit it in the title :^)
This allows us to follow the spec much more closely rather than being
completely ad-hoc with just the parse node instead of having all the
surrounding data such as the realm of the parse node.
The interpreter creation refactor creates the global execution context
once and doesn't take it off the stack. This allows LibWeb to take the
global execution context and manually handle it, following the HTML
spec. The HTML spec calls this the "realm execution context" of the
environment settings object.
It also allows us to specify the globalThis type, as it can be
different from the global object type. For example, on the web, Window
global objects use a WindowProxy global this value to enforce the same
origin policy on operations like [[GetOwnProperty]].
Finally, it allows us to directly call Program::execute in perform_eval
and perform_shadow_realm_eval as this moves
global_declaration_instantiation into Interpreter::run
(ScriptEvaluation) as per the spec.
Note that this doesn't evalulate Source Text Modules yet or refactor
the bytecode interpreter, that's work for future us :^)
This patch was originally build by Luke for the environment settings
object change but was also needed for modules. So I (davidot) have
modified it with the new completion changes and setup for that.
Co-authored-by: davidot <davidot@serenityos.org>
Instead of making it a void function, checking for an exception, and
then receiving the relevant result via VM::last_value(), we can
consolidate all of this by using completions.
This allows us to remove more uses of VM::exception(), and all uses of
VM::last_value().
This allows document.implementation to keep the underlying document
alive for as long as we need it (for example, if someone holds on to a
DOMImplementation JS wrapper after the document is GC'd.)
These aren't needed now that we render using background_layers instead.
The one casualty is the resolved style for background-repeat, but that
was incorrect anyway.
Instead of storing these as individual `background-foo` properties, we
combine them together into layers, since that is how they will be
painted. It also makes it more convenient to pass them around.
While right now this doesn't save much complexity, it will do once we
care about multiple background layers per node. Then, having a single
repeat value per layer will simplify things.
It also means we can remove the pseudo-property concept entirely! :^)
If we had a scheduled update of either of these kind, make sure to
cancel it after performing an update. Otherwise we might do a redundant
second update with the same results.
This could happen if something schedules an async layout, and before it
can happen, something requires a sync layout, which we do right away.
We now evaluate the conditions of `@media` rules at the same point in
the HTML event loop as evaluation of `MediaQueryList`s. This is not
strictly to spec, but since the spec doesn't actually say when to do
this, it seems to make the most sense. In any case, it works! :^)
The recursive style update function was written a bit strangely and
would only mark descendants of the style update root as not needing a
style update.
With this patch, all nodes in the subtree now have clean style after a
style update finishes.
Layout depends on style (and not the other way around), so if the
document has dirty style when we enter update_layout(), make sure we
call update_style() before proceeding with the layout work.
This has the pleasant effect of coalescing some redundant layouts.
Instead of doing layout synchronously whenever something changes,
we now use a basic event loop timer to defer and coalesce relayouts.
If you did something that requires a relayout of the page, make sure
to call Document::set_needs_layout() and it will get coalesced with all
the other layout updates.
There's lots of room for improvement here, but this already makes many
web pages significantly snappier. :^)
Also, note that this exposes a number of layout bugs where we have been
relying on multiple relayouts to calculate the correct dimensions for
things. Now that we only do a single layout in many cases, these kind of
problems are much more noticeable. That should also make them easier to
figure out and fix. :^)
This is used surprisingly often. For example, it is used by a core
YouTube library called Structured Page Fragments.
It allows you to manually dispatch an event with arbitrary data
attached to it.
The only thing missing from this implementation is the constructor.
This is because WrapperGenerator is currently missing dictionary
capabilities.
The only difference from what we were already doing is that setting the
same ready state twice no longer fires a "readystatechange" event.
I don't think that could happen in practice though.
Resolved style is a spec concept that refers to the weird mix of
computed style and used style reflected by getComputedStyle().
The purpose of this class is to produce the *computed* style for a given
element, so let's call it StyleComputer.
This also moves getElementsByTagName to ParentNode to remove the code
duplication between Document and Element. This additionally fixes a bug
where getElementsByTagName did not check if the element was a
descendant, meaning it would also include the context element if the
condition matched.
Rather than destroying and rebuilding the entire document layout tree in
every call to `ComputedCSSStyleDeclaration::property()`, we now just
make sure that the layout tree exists.
This speeds up the DOM Inspector significantly, from taking several
seconds to select an element, to almost instant. :^)
This namespace will be used for all interfaces defined in the URL
specification, like URL and URLSearchParams.
This has the unfortunate side-effect of requiring us to use the fully
qualified AK::URL name whenever we want to refer to the AK class, so
this commit also fixes all such references.
The spec allows us to optionally return from these for any reason.
Our reason is that we don't have all the infrastructure in place yet to
implement them.
This will be used by the HTML parser to determine whether it's okay to
start running a script.
Note that we don't actually count the script-blocking style sheets yet.
This patch only adds a the checking mechanism for the parser.
These were an ad-hoc way to implement special behaviour when reading or
writing to specific object properties. Because these were effectively
replaced by the abillity to override the internal methods of Object,
they are no longer needed.
Previously it was not doing so, and some code relied on this not being
the case.
In particular, set_caption, set_t_head and set_t_foot in
HTMLTableElement relied on this. This commit is not here to fix this,
so I added an assertion to make it equivalent to a reference for now.
We must hook `on_call_stack_emptied` after the interpreter was created,
as the initialization of the WindowsObject can invoke some internal
calls, which will eventually lead to this hook being called without
`m_interpreter` being fully initialized yet.
This method builds a JSON object representing the full state of the
DOM tree.
The JSON that is built will be used for building the DOM Inspector
widget for the OutOfProcessWebView.
The WebIDL spec specifies a few "simple" exception types in addition to
the DOMException type, let's support all of those.
This allows functions returning ExceptionOr<T> to throw regular
javascript exceptions (as limited by the webidl spec) by returning a
`DOM::SimpleException { DOM::SimpleExceptionType::T, "error message" }`
which is pretty damn cool :^)
Adds support for the :active pseudo-class for hyperlinks (<a> tags
only).
Also, since it was very similar to :focus and an element having a
focused state was already implemented, I went ahead and implemented
that pseudo-class too, although I cannot come up with a working
example to validate it.
This counter is increased each time a synchronous execution sequence
completes, and will allow us to emulate the abstract operations
AddToKeptObjects & ClearKeptObjects efficiently.
Previously, AK::Function would accept _any_ callable type, and try to
call it when called, first with the given set of arguments, then with
zero arguments, and if all of those failed, it would simply not call the
function and **return a value-constructed Out type**.
This lead to many, many, many hard to debug situations when someone
forgot a `const` in their lambda argument types, and many cases of
people taking zero arguments in their lambdas to ignore them.
This commit reworks the Function interface to not include any such
surprising behaviour, if your function instance is not callable with
the declared argument set of the Function, it can simply not be
assigned to that Function instance, end of story.
This replaces ctype.h with CharacterType.h everywhere I could find
issues with narrowing conversions. While using it will probably make
sense almost everywhere in the future, the most critical places should
have been addressed.
Our "frame" concept very closely matches what the web specs call a
"browsing context", so let's rename it to that. :^)
The "main frame" becomes the "top-level browsing context",
and "sub-frames" are now "nested browsing contexts".
LibWeb is now responsible for logging unhandled exceptions itself,
which means set_should_log_exceptions() is no longer used and can be
removed. It turned out to be not the best option for web page exception
logging, as we would have no indication regarding whether the exception
was later handled of not.
Instead of having to run queued promise jobs in LibWeb in various
places, this allows us to consolidate that into one function - this is
very close to how the spec describes it as well ("at some future point
in time, when there is no running execution context and the execution
context stack is empty, the implementation must [...]").
Eventually this will also be used to log unhandled exceptions, and
possibly other actions that require JS execution to have ended.
HTMLCollection is an awkward legacy interface from the DOM spec.
It provides a live view of a DOM subtree, with some kind of filtering
that determines which elements are part of the collection.
We now return HTMLCollection objects from these APIs:
- getElementsByClassName()
- getElementsByName()
- getElementsByTagName()
This initial implementation does not do any kind of caching, since that
is quite a tricky problem, and there will be plenty of time for tricky
problems later on when the engine is more mature.
SPDX License Identifiers are a more compact / standardized
way of representing file license information.
See: https://spdx.dev/resources/use/#identifiers
This was done with the `ambr` search and replace tool.
ambr --no-parent-ignore --key-from-file --rep-from-file key.txt rep.txt *
To protect the main Browser process against nefarious cookies, parse the
cookies out-of-process and then send the parsed result over IPC to the
main process. This way, if the cookie parser blows up, only that tab
will be affected.
These provide the cursor coordinate within the viewport at which the
event occurred (as opposed to the page relative coordinates exposed via
offsetX, offsetY).
To implement the HttpOnly attribute, the CookieJar needs to know where a
request originated from. Namely, it needs to distinguish between HTTP /
non-HTTP (i.e. JavaScript) requests. When the HttpOnly attribute is set,
requests from JavaScript are to be blocked.
This is a legacy function providing a way of constructing events without
using their constructors exposed on the global object.
We don't have many of the events it supports yet, nor can we throw a
DOMException from it, so that's two FIXMEs for later.
The mutation algorithms now more closely follow the spec and
fixes some assertion failures in tests such as Acid3 and Dromaeo.
The main thing that is missing right now is passing exceptions to the
bindings layer. This is because of issue #6075. I spent a while trying
to work it out and got so frustrated I just left it as a FIXME. Besides
that, the algorithms bail at the appropriate points.
This also makes the adopting steps in the document more spec compliant
as it's needed by the insertion algorithm. While I was at it, I added
the adoptNode IDL binding.
This adds a bunch of ancestor/descendant checks to TreeNode as well.
I moved the "remove_all_children" function to Node as it needs to use
the full remove algorithm instead of simply removing it from
the child list.
This is because it includes the initial node that the function was
called on, which makes it "inclusive" as according to the spec.
This is important as there are non-inclusive variants, particularly
used in the node mutation algorithms.
The background-repeat value may be specified as either one- or two-value
identifiers (to be interpreted as horizontal and vertical repeat). This
adds two pseudo-properties, background-repeat-x and background-repeat-y,
to handle this. One-value identifiers are mapped to two-value in
accordance with the spec.
Update the painting of background images for both <body> nodes and other
non-initial nodes. Currently, only the following values are supported:
repeat, repeat-x, repeat-y, no-repeat
This also doesn't support the two-value syntax which allows for setting
horizontal and vertical repetition separately.
We now run queued promise jobs after calling event handler, timer, and
requestAnimationFrame() callbacks - this is a bit ad-hoc, but I don't
want to switch LibWeb to use an event loop right now - this works just
fine, too.
We might want to revisit this at a later point and do tasks and
microtasks properly.
With one small exception, this is how we've been using this API already,
and it makes sense: a Program is just a ScopeNode with any number of
statements, which are executed one by one. There's no explicit return
value at the end, only a completion value of the last value-producing
statement, which we then access using VM::last_value() if needed (e.g.
in the REPL).
(...and ASSERT_NOT_REACHED => VERIFY_NOT_REACHED)
Since all of these checks are done in release builds as well,
let's rename them to VERIFY to prevent confusion, as everyone is
used to assertions being compiled out in release.
We can introduce a new ASSERT macro that is specifically for debug
checks, but I'm doing this wholesale conversion first since we've
accumulated thousands of these already, and it's not immediately
obvious which ones are suitable for ASSERT.
We'll want to remove the LibGUI dependency from the WebContent process.
This is the first basic step of removing unnecessary LibGUI includes
and swapping out GUI::Painter for Gfx::Painter.
From https://dom.spec.whatwg.org/#concept-getelementsbytagname:
2. Otherwise, if root’s node document is an HTML document, return a
HTMLCollection rooted at root, whose filter matches the following
descendant elements:
* Whose namespace is the HTML namespace and whose qualified name
is qualifiedName, in ASCII lowercase.
* Whose namespace is not the HTML namespace and whose qualified
name is qualifiedName.
This patch adds an IPC call for debugging requests. It's stringly typed
and very simple, and allows us to easily implement all the features in
the Browser's Debug menu.