diff --git a/Base/home/anon/www/form.html b/Base/home/anon/www/form.html
new file mode 100644
index 0000000000..3df9bed419
--- /dev/null
+++ b/Base/home/anon/www/form.html
@@ -0,0 +1,10 @@
+
+
+
Form
+
+
+
+
diff --git a/Base/home/anon/www/welcome.html b/Base/home/anon/www/welcome.html
index a839738b7d..d11541fc1c 100644
--- a/Base/home/anon/www/welcome.html
+++ b/Base/home/anon/www/welcome.html
@@ -24,6 +24,7 @@ h1 {
Some small test pages:
- small
+ - form
- borders
- css
- acid1
diff --git a/Libraries/LibHTML/DOM/ElementFactory.cpp b/Libraries/LibHTML/DOM/ElementFactory.cpp
index 922f25283d..9edfa84e9d 100644
--- a/Libraries/LibHTML/DOM/ElementFactory.cpp
+++ b/Libraries/LibHTML/DOM/ElementFactory.cpp
@@ -4,11 +4,13 @@
#include
#include
#include
+#include
#include
#include
#include
#include
#include
+#include
#include
#include
#include
@@ -38,6 +40,10 @@ NonnullRefPtr create_element(Document& document, const String& tag_name
return adopt(*new HTMLImageElement(document, lowercase_tag_name));
if (lowercase_tag_name == "blink")
return adopt(*new HTMLBlinkElement(document, lowercase_tag_name));
+ if (lowercase_tag_name == "form")
+ return adopt(*new HTMLFormElement(document, lowercase_tag_name));
+ if (lowercase_tag_name == "input")
+ return adopt(*new HTMLInputElement(document, lowercase_tag_name));
if (lowercase_tag_name == "br")
return adopt(*new HTMLBRElement(document, lowercase_tag_name));
if (lowercase_tag_name == "h1"
diff --git a/Libraries/LibHTML/DOM/HTMLFormElement.cpp b/Libraries/LibHTML/DOM/HTMLFormElement.cpp
new file mode 100644
index 0000000000..1e0f047533
--- /dev/null
+++ b/Libraries/LibHTML/DOM/HTMLFormElement.cpp
@@ -0,0 +1,58 @@
+#include
+#include
+#include
+#include
+#include
+
+HTMLFormElement::HTMLFormElement(Document& document, const String& tag_name)
+ : HTMLElement(document, tag_name)
+{
+}
+
+HTMLFormElement::~HTMLFormElement()
+{
+}
+
+void HTMLFormElement::submit()
+{
+ if (action().is_null()) {
+ dbg() << "Unsupported form action ''";
+ return;
+ }
+
+ if (method().to_lowercase() != "get") {
+ dbg() << "Unsupported form method '" << method() << "'";
+ return;
+ }
+
+ URL url(document().complete_url(action()));
+
+ struct NameAndValue {
+ String name;
+ String value;
+ };
+
+ Vector parameters;
+
+ for_each_in_subtree([&](auto& node) {
+ if (is(node)) {
+ auto& input = to(node);
+ if (!input.name().is_null())
+ parameters.append({ input.name(), input.value() });
+ }
+ return IterationDecision::Continue;
+ });
+
+ StringBuilder builder;
+ for (int i = 0; i < parameters.size(); ++i) {
+ builder.append(parameters[i].name);
+ builder.append('=');
+ builder.append(parameters[i].value);
+ if (i != parameters.size() - 1)
+ builder.append('&');
+ }
+ url.set_query(builder.to_string());
+
+ // FIXME: We shouldn't let the form just do this willy-nilly.
+ document().frame()->html_view()->load(url);
+}
diff --git a/Libraries/LibHTML/DOM/HTMLFormElement.h b/Libraries/LibHTML/DOM/HTMLFormElement.h
new file mode 100644
index 0000000000..b46b5788e0
--- /dev/null
+++ b/Libraries/LibHTML/DOM/HTMLFormElement.h
@@ -0,0 +1,20 @@
+#pragma once
+
+#include
+
+class HTMLFormElement : public HTMLElement {
+public:
+ HTMLFormElement(Document&, const String& tag_name);
+ virtual ~HTMLFormElement() override;
+
+ String action() const { return attribute("action"); }
+ String method() const { return attribute("method"); }
+
+ void submit();
+};
+
+template<>
+inline bool is(const Node& node)
+{
+ return is(node) && to(node).tag_name().to_lowercase() == "form";
+}
diff --git a/Libraries/LibHTML/DOM/HTMLInputElement.cpp b/Libraries/LibHTML/DOM/HTMLInputElement.cpp
new file mode 100644
index 0000000000..46b98d1a4f
--- /dev/null
+++ b/Libraries/LibHTML/DOM/HTMLInputElement.cpp
@@ -0,0 +1,51 @@
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+HTMLInputElement::HTMLInputElement(Document& document, const String& tag_name)
+ : HTMLElement(document, tag_name)
+{
+}
+
+HTMLInputElement::~HTMLInputElement()
+{
+}
+
+RefPtr HTMLInputElement::create_layout_node(const StyleProperties*) const
+{
+ ASSERT(document().frame());
+ auto& frame = *document().frame();
+ ASSERT(frame.html_view());
+ auto& html_view = const_cast(*frame.html_view());
+
+ RefPtr widget;
+ if (type() == "submit") {
+ auto button = GButton::construct(value(), &html_view);
+ int text_width = Font::default_font().width(value());
+ button->set_relative_rect(0, 0, text_width + 20, 20);
+ button->on_click = [this](auto&) {
+ if (auto* form = first_ancestor_of_type()) {
+ // FIXME: Remove this const_cast once we have a non-const first_ancestor_of_type.
+ const_cast(form)->submit();
+ }
+ };
+ widget = button;
+ } else {
+ auto text_box = GTextBox::construct(&html_view);
+ text_box->set_text(value());
+ text_box->on_change = [this] {
+ auto& widget = to(layout_node())->widget();
+ const_cast(this)->set_attribute("value", static_cast(widget).text());
+ };
+ int text_width = Font::default_font().width(value());
+ text_box->set_relative_rect(0, 0, text_width + 20, 20);
+ widget = text_box;
+ }
+
+ return adopt(*new LayoutWidget(*this, *widget));
+}
diff --git a/Libraries/LibHTML/DOM/HTMLInputElement.h b/Libraries/LibHTML/DOM/HTMLInputElement.h
new file mode 100644
index 0000000000..15fbdc334a
--- /dev/null
+++ b/Libraries/LibHTML/DOM/HTMLInputElement.h
@@ -0,0 +1,21 @@
+#pragma once
+
+#include
+
+class HTMLInputElement : public HTMLElement {
+public:
+ HTMLInputElement(Document&, const String& tag_name);
+ virtual ~HTMLInputElement() override;
+
+ virtual RefPtr create_layout_node(const StyleProperties* parent_style) const override;
+
+ String type() const { return attribute("type"); }
+ String value() const { return attribute("value"); }
+ String name() const { return attribute("name"); }
+};
+
+template<>
+inline bool is(const Node& node)
+{
+ return is(node) && to(node).tag_name().to_lowercase() == "input";
+}
diff --git a/Libraries/LibHTML/Layout/LayoutNode.h b/Libraries/LibHTML/Layout/LayoutNode.h
index 3602405a45..7f80b73e91 100644
--- a/Libraries/LibHTML/Layout/LayoutNode.h
+++ b/Libraries/LibHTML/Layout/LayoutNode.h
@@ -57,6 +57,7 @@ public:
virtual bool is_text() const { return false; }
virtual bool is_block() const { return false; }
virtual bool is_replaced() const { return false; }
+ virtual bool is_widget() const { return false; }
virtual bool is_box() const { return false; }
virtual bool is_table() const { return false; }
virtual bool is_table_row() const { return false; }
diff --git a/Libraries/LibHTML/Layout/LayoutWidget.cpp b/Libraries/LibHTML/Layout/LayoutWidget.cpp
new file mode 100644
index 0000000000..cfbd492dd5
--- /dev/null
+++ b/Libraries/LibHTML/Layout/LayoutWidget.cpp
@@ -0,0 +1,28 @@
+#include
+#include
+#include
+#include
+#include
+
+LayoutWidget::LayoutWidget(const Element& element, GWidget& widget)
+ : LayoutReplaced(element, StyleProperties::create())
+ , m_widget(widget)
+{
+}
+
+LayoutWidget::~LayoutWidget()
+{
+ widget().remove_from_parent();
+}
+
+void LayoutWidget::layout()
+{
+ rect().set_size(FloatSize(widget().width(), widget().height()));
+ LayoutReplaced::layout();
+ widget().move_to(rect().x(), rect().y());
+}
+
+void LayoutWidget::render(RenderingContext& context)
+{
+ LayoutReplaced::render(context);
+}
diff --git a/Libraries/LibHTML/Layout/LayoutWidget.h b/Libraries/LibHTML/Layout/LayoutWidget.h
new file mode 100644
index 0000000000..f33c0f3541
--- /dev/null
+++ b/Libraries/LibHTML/Layout/LayoutWidget.h
@@ -0,0 +1,30 @@
+#pragma once
+
+#include
+
+class GWidget;
+
+class LayoutWidget : public LayoutReplaced {
+public:
+ LayoutWidget(const Element&, GWidget&);
+ virtual ~LayoutWidget() override;
+
+ virtual void layout() override;
+ virtual void render(RenderingContext&) override;
+
+ GWidget& widget() { return m_widget; }
+ const GWidget& widget() const { return m_widget; }
+
+ virtual bool is_widget() const final { return true; }
+
+private:
+ virtual const char* class_name() const override { return "LayoutWidget"; }
+
+ NonnullRefPtr m_widget;
+};
+
+template<>
+inline bool is(const LayoutNode& node)
+{
+ return node.is_widget();
+}
diff --git a/Libraries/LibHTML/Makefile b/Libraries/LibHTML/Makefile
index cb38b174e8..4090c7f9ac 100644
--- a/Libraries/LibHTML/Makefile
+++ b/Libraries/LibHTML/Makefile
@@ -27,11 +27,13 @@ LIBHTML_OBJS = \
DOM/HTMLBodyElement.o \
DOM/HTMLElement.o \
DOM/HTMLFontElement.o \
+ DOM/HTMLFormElement.o \
DOM/HTMLHRElement.o \
DOM/HTMLHeadElement.o \
DOM/HTMLHeadingElement.o \
DOM/HTMLHtmlElement.o \
DOM/HTMLImageElement.o \
+ DOM/HTMLInputElement.o \
DOM/HTMLLinkElement.o \
DOM/HTMLStyleElement.o \
DOM/HTMLTitleElement.o \
@@ -59,6 +61,7 @@ LIBHTML_OBJS = \
Layout/LayoutTableRow.o \
Layout/LayoutText.o \
Layout/LayoutTreeBuilder.o \
+ Layout/LayoutWidget.o \
Layout/LineBox.o \
Layout/LineBoxFragment.o \
Parser/CSSParser.o \