diff --git a/Tests/LibWeb/Text/data/blank.html b/Tests/LibWeb/Text/data/blank.html
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/Tests/LibWeb/Text/expected/HTML/HTMLFrameElement-load.txt b/Tests/LibWeb/Text/expected/HTML/HTMLFrameElement-load.txt
new file mode 100644
index 0000000000..fce6688448
--- /dev/null
+++ b/Tests/LibWeb/Text/expected/HTML/HTMLFrameElement-load.txt
@@ -0,0 +1 @@
+frame load: blank.html
diff --git a/Tests/LibWeb/Text/input/HTML/HTMLFrameElement-load.html b/Tests/LibWeb/Text/input/HTML/HTMLFrameElement-load.html
new file mode 100644
index 0000000000..1384059978
--- /dev/null
+++ b/Tests/LibWeb/Text/input/HTML/HTMLFrameElement-load.html
@@ -0,0 +1,18 @@
+
+
+
+
+
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLFrameElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLFrameElement.cpp
index 66bbde2972..2330700723 100644
--- a/Userland/Libraries/LibWeb/HTML/HTMLFrameElement.cpp
+++ b/Userland/Libraries/LibWeb/HTML/HTMLFrameElement.cpp
@@ -6,15 +6,23 @@
#include
#include
+#include
+#include
+#include
+#include
#include
+#include
namespace Web::HTML {
JS_DEFINE_ALLOCATOR(HTMLFrameElement);
HTMLFrameElement::HTMLFrameElement(DOM::Document& document, DOM::QualifiedName qualified_name)
- : HTMLElement(document, move(qualified_name))
+ : NavigableContainer(document, move(qualified_name))
{
+ // https://html.spec.whatwg.org/multipage/obsolete.html#frames:potentially-delays-the-load-event
+ // The frame element potentially delays the load event.
+ set_potentially_delays_the_load_event(true);
}
HTMLFrameElement::~HTMLFrameElement() = default;
@@ -25,6 +33,47 @@ void HTMLFrameElement::initialize(JS::Realm& realm)
WEB_SET_PROTOTYPE_FOR_INTERFACE(HTMLFrameElement);
}
+// https://html.spec.whatwg.org/multipage/obsolete.html#frames:html-element-insertion-steps
+void HTMLFrameElement::inserted()
+{
+ Base::inserted();
+
+ // 1. If insertedNode is not in a document tree, then return.
+ if (!in_a_document_tree())
+ return;
+
+ // 2. If insertedNode's root's browsing context is null, then return.
+ if (root().document().browsing_context() == nullptr)
+ return;
+
+ // 3. Create a new child navigable for insertedNode.
+ MUST(create_new_child_navigable(JS::create_heap_function(realm().heap(), [this] {
+ // 4. Process the frame attributes for insertedNode, with initialInsertion set to true.
+ process_the_frame_attributes(true);
+ set_content_navigable_initialized();
+ })));
+}
+
+// https://html.spec.whatwg.org/multipage/obsolete.html#frames:html-element-removing-steps
+void HTMLFrameElement::removed_from(DOM::Node* node)
+{
+ Base::removed_from(node);
+
+ // The frame HTML element removing steps, given removedNode, are to destroy a child navigable given removedNode.
+ destroy_the_child_navigable();
+}
+
+// https://html.spec.whatwg.org/multipage/obsolete.html#frames:frame-3
+void HTMLFrameElement::attribute_changed(FlyString const& name, Optional const& old_value, Optional const& value)
+{
+ Base::attribute_changed(name, old_value, value);
+
+ // Whenever a frame element with a non-null content navigable has its src attribute set, changed, or removed, the
+ // user agent must process the frame attributes.
+ if (content_navigable() && name == HTML::AttributeNames::src)
+ process_the_frame_attributes();
+}
+
// https://html.spec.whatwg.org/multipage/interaction.html#dom-tabindex
i32 HTMLFrameElement::default_tab_index_value() const
{
@@ -32,4 +81,28 @@ i32 HTMLFrameElement::default_tab_index_value() const
return 0;
}
+// https://html.spec.whatwg.org/multipage/obsolete.html#process-the-frame-attributes
+void HTMLFrameElement::process_the_frame_attributes(bool initial_insertion)
+{
+ // 1. Let url be the result of running the shared attribute processing steps for iframe and frame elements given
+ // element and initialInsertion.
+ auto url = shared_attribute_processing_steps_for_iframe_and_frame(initial_insertion);
+
+ // 2. If url is null, then return.
+ if (!url.has_value())
+ return;
+
+ // 3. If url matches about:blank and initialInsertion is true, then:
+ if (url_matches_about_blank(*url) && initial_insertion) {
+ // 1. Fire an event named load at element.
+ dispatch_event(DOM::Event::create(realm(), HTML::EventNames::load));
+
+ // 2. Return.
+ return;
+ }
+
+ // 3. Navigate an iframe or frame given element, url, and the empty string.
+ navigate_an_iframe_or_frame(*url, ReferrerPolicy::ReferrerPolicy::EmptyString);
+}
+
}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLFrameElement.h b/Userland/Libraries/LibWeb/HTML/HTMLFrameElement.h
index 5e236c8dbc..ea326b972c 100644
--- a/Userland/Libraries/LibWeb/HTML/HTMLFrameElement.h
+++ b/Userland/Libraries/LibWeb/HTML/HTMLFrameElement.h
@@ -6,13 +6,13 @@
#pragma once
-#include
+#include
namespace Web::HTML {
// NOTE: This element is marked as obsolete, but is still listed as required by the specification.
-class HTMLFrameElement final : public HTMLElement {
- WEB_PLATFORM_OBJECT(HTMLFrameElement, HTMLElement);
+class HTMLFrameElement final : public NavigableContainer {
+ WEB_PLATFORM_OBJECT(HTMLFrameElement, NavigableContainer);
JS_DECLARE_ALLOCATOR(HTMLFrameElement);
public:
@@ -24,7 +24,12 @@ private:
virtual void initialize(JS::Realm&) override;
// ^DOM::Element
+ virtual void inserted() override;
+ virtual void removed_from(Node*) override;
+ virtual void attribute_changed(FlyString const& name, Optional const& old_value, Optional const& value) override;
virtual i32 default_tab_index_value() const override;
+
+ void process_the_frame_attributes(bool initial_insertion = false);
};
}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLFrameElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLFrameElement.idl
index ec8ad5f190..6199b14456 100644
--- a/Userland/Libraries/LibWeb/HTML/HTMLFrameElement.idl
+++ b/Userland/Libraries/LibWeb/HTML/HTMLFrameElement.idl
@@ -12,8 +12,8 @@ interface HTMLFrameElement : HTMLElement {
[CEReactions, Reflect=frameborder] attribute DOMString frameBorder;
[CEReactions, Reflect=longdesc, URL] attribute USVString longDesc;
[CEReactions, Reflect=noresize] attribute boolean noResize;
- [FIXME] readonly attribute Document? contentDocument;
- [FIXME] readonly attribute WindowProxy? contentWindow;
+ readonly attribute Document? contentDocument;
+ readonly attribute WindowProxy? contentWindow;
[CEReactions, LegacyNullToEmptyString, Reflect=marginheight] attribute DOMString marginHeight;
[CEReactions, LegacyNullToEmptyString, Reflect=marginwidth] attribute DOMString marginWidth;