You can now cycle through focusable elements (currently only hyperlinks
are focusable) with the Tab key.
The focus outline is rendered in a new FocusOutline paint phase.
Decorated Interpreter::call() with [[nodiscard]] to provoke thinking
about the returned value at each call site. This is definitely not
perfect and we should really start thinking about slimming down the
public-facing LibJS interpreter API.
Fixes#3136.
This enables a nice warning in case a function becomes dead code. Also, in the
case of {Event,Node}WrapperFactory.cpp, the corresponding header was forgotten.
This would cause an issue later when we enable -Wmissing-declarations.
Is my clang-format misconfigured? Why is the diff for NodeWrapperFactory.cpp
so large?
This patch makes images have an implicit zero intrinsic size before
they have either loaded or failed to load. This is tracked by the
ImageLoader object.
This fixes a long-standing issue with images occupying empty 150x150
rectangles of space.
We don't want to carry over exceptions across multiple
Document::run_javascript() calls as Interpreter::run() and every of its
exception checks will get confused - in this case there would be an
exception, but not because a certain action failed.
Real-life example:
<script>var a = {}; a.test()</script>
<script>alert("It worked!")</script>
The above HTML will invoke Document::run_javascript() twice, the first
call will result in a TypeError, which is still stored during the second
call. The interpreter will eventually call the following functions (in
order) for the alert() invocation:
- Identifier::execute()
- Interpreter::get_variable()
- Object::get() (on the global object)
That last Object::get() call has an exception check which is triggered
as we still carry around the exception from earlier - and eventually
returns an empty value.
Long story short, the second script will wrongly fail with
"ReferenceError, 'alert' is not defined".
Fixes#3091.
This is mostly to get the grunt work of the way. This is split up into
multiple commits to hopefully make it more manageable to review.
Note that these are not full implementations, and the bindings mostly
get the low hanging fruit.
Also implements some attributes that I kept out because they had
dashes in them. Therefore, this closes#2905.
These are pretty rare, but they do come up in some places and it's not
hard to support. The Gfx::Font information is approximate (and bad)
but we can fix that separately.
This function did a const_cast internally which made the call side look
"safe". This method is removed completely and call sites are replaced
with ByteBuffer::wrap(const_cast<void*>(data), size) which makes the
behaviour obvious.
The text cursor follows slightly different "intuitive" rules than the
regular hit testing. Clicking past the right edge of a text box should
still "hit" the text box, and place the cursor at its end, for example.
We solve this by adding a HitTestType enum that is passed to hit_test()
and determines whether past-the-edge candidates are considered.
After running a build command, make by default stat()s the command's
output, and if it wasn't touched, then it cancels all build steps
that were scheduled only because this command was expected to change
the output.
Ninja has the same feature, but it's opt-in behind the per-command
"restat = 1" setting. However, CMake enables it by default for all
custom commands.
Use Meta/write-only-on-difference.sh to write the output to a temporary
file, and then copy the temporary file only to the final location if the
contents of the output have changed since last time.
write-only-on-difference.sh automatically creates the output's parent
directory, so stop doing that in CMake.
Reduces the number of build steps that run after touching a file
in LibCore from 522 to 312.
Since we now no longer trigger the CMake special case "If COMMAND
specifies an executable target name (created by the add_executable()
command), it will automatically be replaced by the location of the
executable created at build time", we now need to use qualified paths to
the generators.
Somewhat related to #2877.
Now that document element returns a generic DOM element, we need to
make sure head and body get a html element.
The spec just says to check if the document element is a html element,
so let's do that.
Also change DOM::Document::document_element() to return an Element*
and not an HTML::HTMLHtmlElement since that's not the only kind of
documentElement we might encounter.
HTMLElement is the only interface that includes ElementContentEditable
in the HTML specification. This makes sense, as Element is also a base
class for elements in other specifications such as SVG,
which definitely shouldn't be editable.
Also adds a test for the attribute based on what Andreas did in the
video that added it.
- parse_flag now only parses one digit instead of consuming an entirely
valid number
- match_number => match_coordinate
- match_coordinate now returns true if `ch()` is '.'
- parse_number no longer matches a +/-
- Don't crash when encountering one of the three unsupported path
commands. Instead, just skip them. No reason to crash the browser over a
silly SVG element :)
This works everywhere right now, but it's obviously not going to stay
that way forever. :^)
Note that this does not advance the cursor correctly for whitespace
since the cursor is DOM-based and doesn't take whitespace collapsing
into account yet.
Each Web::Frame now has a cursor that sits at a DOM::Position. It will
blink and look like a nice regular text cursor.
It doesn't really do anything yet, but it will eventually.
Const pointers into the DOM was a nice idea, but in practice, there are
too many situations where the layout tree wants to some non-const thing
to the DOM.
Note that these aren't full implementations of the bindings. This
mostly implements the low hanging fruit (namely, basic reflections)
There are some attributes that should be USVString instead of
DOMString. However, USVString is a slightly different definition
of DOMString, so it should suffice for now.
LibWeb keeps growing and the Web namespace is filling up fast.
Let's put DOM stuff into Web::DOM, just like we already started doing
with SVG stuff in Web::SVG.
This commit starts adding a basic SVG element. Currently, svg elements
have support for the width and height properties, as well as the stroke,
stroke-width, and fill properties. The only child element supported
is the path element, as most other graphical elements are just shorthand
for paths.
This allows you to not have to write a separate test file
for the same thing but in a different situation.
This doesn't handle when you change the page with location.href
however.
Changes the name of the page load handlers to prevent confusion
with this.
Sometimes the IDL attribute and the DOM attribute don't have the same
exact name. In those cases, we can now do this:
[Reflect=foobar] attribute DOMString fooBar;
You can now tag reflecting attributes with [Reflect] to generate code
that does basic DOM element attribute get/set.
(This patch also makes it easy to add more extended attributes like
that going forward.)
From the HTML spec:
"Some IDL attributes are defined to reflect a particular content
attribute. This means that on getting, the IDL attribute returns
the current value of the content attribute, and on setting,
the IDL attribute changes the value of the content attribute
to the given value."
To prepare for fully qualified tag names, let's call this local_name.
Note that we still keep an Element::tag_name() around since that's what
the JS bindings end up calling into for the Element.tagName property.
LibWeb currently has no test suite or program. Let's change that :^)
test-web is mostly a copy of test-js, but modified for LibWeb.
test-web imports both LibJS/Tests/test-common.js and
LibWeb/Test/test-common.js
LibWeb's suite provides the ability to specify the page to load,
what to do before the page is loaded, and what to do after it's
loaded.
This also provides a test of document.doctype and its close sibling
document.compatMode.
Currently, this isn't added to Lagom because of CodeGenerators.
btoa() takes a byte string, so it must decode the UTF-8 argument into
a Vector<u8> before calling encode_base64.
Likewise, in atob() decode_base64 returns a byte string, so that needs
to be converted to UTF-8.
With this, `btoa(String.fromCharCode(255))` is '/w==' as it should
be, and `atob(btoa(String.fromCharCode(255))) == String.fromCharCode(255)`
remains true.
If we know the width, but not the height, we have to *divide* with the
intrinsic ratio to get the height (not multiply.) :^)
This makes things like <img width=300 src=image.png> work right.
Images were added before replaced element layout knew about intrinsic
sizes, so this was a bit backwards. We now instead transfer the known
intrinsic sizes from the ImageLoader to the LayoutImage.
Presentation attribute lengths (width, height, etc.) can always be
unit-less (e.g "400") so going via the normal CSS parsing path only
works when the document is in quirks mode.
Add a separate parse_html_length() that always allows unit-less values.
The specification says that parts labelled as a "fragment case" will
only occur when parsing a fragment. It says that if it occurs when
not parsing a fragment, then it is a specification error.
We should probably assume at this point that it's an implementation
error. This fixes a few little mistakes that were caught out by this.
Also moves the context element outside insertion mode reset,
as other (unimplemented) parts refer to it, such as
"adjusted current node".
Also cleans up insertion mode reset.
This allows us to determine which mode to render the page in.
Exposes "doctype" and "compatMode" on Document.
Exposes "name", "publicId" and "systemId" on DocumentType.
Since the vast majority of message boxes should be modal, require
the parent window to be passed in, which can be nullptr for the
rare case that they don't. By it being the first argument, the
default arguments also don't need to be explicitly stated in most
cases, and it encourages passing in a parent window handle.
Fix up several message boxes that should have been modal.
Fixes https://github.com/SerenityOS/serenity/issues/2649
Loading a page with iframes could lead to a scenario, where the iframe
document finished layout prior to the main frame beeing laid out
initially. This caused a crash/assertion of the browser.
This should enable to destinguish between IFrame, Reload and Navigation
motivated loads in order to call the appropriate hooks.
This change is motivated as loading the IFrame test page causes the
IFrame url to be added to the history and shows up as the current
browser location bar.
Change: on_link_hover(String) -> on_link_hover(URL)
Also, we now fire the hook when a link is unhovered as well, allowing
the embedder to react to nothing being hovered anymore.
Activating a "#foo" fragment link will now be handled internally by
the Frame instead of involving the widget layer.
If the viewport needs to be scrolled as a result, we will simply ask
the PageClient to scroll a new rect into view.
During app teardown, the Application object may be destroyed before
something else, and so having Application::the() return a reference was
obscuring the truth about its lifetime.
This patch makes the API more honest by returning a pointer. While
this makes call sites look a bit more sketchy, do note that the global
Application pointer only becomes null during app teardown.
To make the plain text we copy out from LibWeb look at least somewhat
like its original form, let's insert newlines at <br> elements and when
we exit a block-level element.
This is far from perfect, but seems to work pretty okay.
This works by finding the very first and very last LayoutText nodes
in the layout tree and then setting the selection bounds to those two
nodes. For some reason it gets glitchy if we set the very first and
very last *LayoutNode* as the selection bounds, but I didn't feel like
investigating that too closely right now.
We now remember the last candidate fragment when hit testing past the
right end of text and use that as the fallback result if nothing else
matches. This makes it possible to drag-select outside the line boxes
in a way that feels mostly natural. :^)
We use this to ensure that we're always working with a selection where
the start() is before the end() in document order. That simplifies all
the logic around this.
Text selection currently works at the LayoutNode level. The root of the
layout tree has a LayoutRange selection() which in turn has two
LayoutPosition objects: start() and end().
A LayoutPosition is a LayoutNode + a text offset into that node.
We handle the selection painting in LayoutText::paint_fragment(), after
the normal painting is finished. The basic mechanism is that each
LayoutFragment is queried for its selection_rect(), and if a non-empty
rect is returned, we clip to it and paint the text once more.
Sometimes people make tables with a specific width. In those cases,
we can't just use the auto-sizing algorithm, but instead have to
respect whatever width the content specifies.
This is a bit rickety right now, since we don't implement generation
of anonymous table boxes.
The basic mechanism here is that block layout (which table-cell uses)
now has a virtual way of asking for the width of the logical containing
block. This is necessary as table-row does not produce a block-level
element and so was previously unable to provide a containing block
width for table-cell layout.
If the table has a non-auto specified width, we now interpret that as
a request to use fixed table layout. This will evolve over time. :^)
"width: 500" is not a valid CSS property in standards mode and should
be ignored.
To plumb the quirks-mode flag into CSS parsing, this patch adds a new
CSS::ParsingContext object that must be passed to the CSS parser.
Currently it only allows you to check the quirks-mode flag. In the
future it will be a good place to put additional information needed
for things like relative URL resolution, etc.
This narrows <div class=parser> on ACID2 to the correct width. :^)
Margin collapsing is a bit confusing, but if I understand correctly,
when collapsing a box's top margin with the vertically adjacent
sibling box above, we should "skip over" empty (0-height) boxes and
collapse their margin with *their* vertically adjacent sibling box
above, etc. Iterating until we hit the first non-empty in-flow block.
This pulls up the bottom part of the face on ACID2. :^)
This patch adds a Web::Timer object that represents a single timer
registration made with window.setTimeout() or window.setInterval().
All live timers are owned by the DOM Window object.
The timers can be stopped via clearTimeout() or clearInterval().
Note that those API's are actually interchangeable, but we have to
support both.
This patch implements most of the HTML fragment parsing algorithm and
ports Element::set_inner_html() to it. This was the last remaining user
of the old HTML parser. :^)
Instead of storing the three-part specificy for every selector,
just mash them together into a 32-bit value instead.
This saves both space and time, and matches the behavior of other
browser engines.
We could previously place a box next to a preceding sibling with
position:fixed, which is wrong since fixed-position elements are taken
out of the normal flow.
When highlighting a node in the inspector, we now paint three overlays:
- The content box (magenta)
- The padding box (cyan)
- The margin box (yellow)
This makes it a lot easier to debug layout issues.
Sometimes we end up with an empty line box at the bottom of a block.
Instead of worrying about this in all the places we split into lines,
just remove the trailing box (if any) after splitting is finshed.
To make this possible, I also had to give each LayoutNode a Document&
so it can resolve document-specific colors correctly. There's probably
ways to avoid having this extra member by resolving colors later, but
this works for now.
This patch also adds the ability for Length to contain percentage
values. This is a little off-spec, but will make storing and dealing
with lengths a lot easier.
To resolve a Length to a px-or-auto Length, there are now helpers
for that. After calling them, you no longer have to think about
em, rem, %, and such things.
StyleProperties is really only the specified "input" to what eventually
becomes the used/computed style we use for layout and painting.
Unlike StyleProperties, LayoutStyle will have strongly typed values for
everything it contains (i.e no CSS::ValueID or strings, etc.)
This first patch moves z-index into LayoutStyle.
This avoids having to query the StyleProperties hash map whenever we
need to know if an element is absolutely positioned. This was extremely
hot in interactive window resize profiles.
In this case, we need to undo the right-side offsetting, since the
width computation algorithm will already have stretched the width to
accomodate both the side constraints.
Previously we would always just use the combined content width as the
shrunken width in shrink-to-fit width calculations, but if the element
has a non-auto specified width, we should just let that take over.
This is far from perfect and doesn't take stuff like min/max-width
into account. Will need more work, this just covers the basic case.
Divide the Object constructor into three variants:
- The regular one (takes an Object& prototype)
- One for use by GlobalObject
- One for use by objects without a prototype (e.g ObjectPrototype)
Instead of taking the JS::Heap&. This allows us to get rid of some
calls to JS::Interpreter::global_object(). We're getting closer and
closer to multiple global objects. :^)
Now that we're using the new HTML parser, we don't have to do the weird
"run the script when inserted into the document, uhh, or when the text
content of the script element changes" dance.
Instead, we just follow the spec, and scripts run the way they should.
<a href="/foo&=bar"> was being tokenized into <a href="/foo&=bar">.
The spec mentions this but I had overlooked it. The bug happens because
we interpreted the "&" as a named character reference.
We still use a Gfx::ImageDecoder for GIF images, but there's no need
for the ImageLoader object to have its own pointer to it. Just grab
the ImageDecoder from the ImageResource when needed.