diff --git a/Base/home/anon/www/90s-bg.png b/Base/home/anon/www/90s-bg.png
new file mode 100644
index 0000000000..b7a0b97cd0
Binary files /dev/null and b/Base/home/anon/www/90s-bg.png differ
diff --git a/Base/home/anon/www/welcome.html b/Base/home/anon/www/welcome.html
index 1f60a17eb9..7b5ddf0239 100644
--- a/Base/home/anon/www/welcome.html
+++ b/Base/home/anon/www/welcome.html
@@ -6,19 +6,19 @@
-
+
Welcome to the Serenity Browser!
This is a very simple browser built on the LibHTML engine.
Some small test pages:
diff --git a/Libraries/LibHTML/CSS/PropertyID.h b/Libraries/LibHTML/CSS/PropertyID.h
index 6e2be9e091..3702da99b6 100644
--- a/Libraries/LibHTML/CSS/PropertyID.h
+++ b/Libraries/LibHTML/CSS/PropertyID.h
@@ -7,6 +7,7 @@ enum class PropertyID {
Invalid,
BackgroundColor,
+ BackgroundImage,
BorderBottomColor,
BorderBottomStyle,
BorderBottomWidth,
diff --git a/Libraries/LibHTML/CSS/StyleValue.cpp b/Libraries/LibHTML/CSS/StyleValue.cpp
index 77ee2f9c58..6fa6d17fa4 100644
--- a/Libraries/LibHTML/CSS/StyleValue.cpp
+++ b/Libraries/LibHTML/CSS/StyleValue.cpp
@@ -1,5 +1,9 @@
+#include
+#include
#include
#include
+#include
+#include
StyleValue::StyleValue(Type type)
: m_type(type)
@@ -28,3 +32,20 @@ Color IdentifierStyleValue::to_color(const Document& document) const
return document.link_color();
return {};
}
+
+ImageStyleValue::ImageStyleValue(const URL& url, Document& document)
+ : StyleValue(Type::Image)
+ , m_url(url)
+ , m_document(document.make_weak_ptr())
+{
+ NonnullRefPtr protector(*this);
+ ResourceLoader::the().load(url, [this, protector](auto& data) {
+ if (!m_document)
+ return;
+ m_bitmap = load_png_from_memory(data.data(), data.size());
+ if (!m_bitmap)
+ return;
+ // FIXME: Do less than a full repaint if possible?
+ m_document->frame()->set_needs_display({});
+ });
+}
diff --git a/Libraries/LibHTML/CSS/StyleValue.h b/Libraries/LibHTML/CSS/StyleValue.h
index bd4e9b4000..53420123a3 100644
--- a/Libraries/LibHTML/CSS/StyleValue.h
+++ b/Libraries/LibHTML/CSS/StyleValue.h
@@ -4,7 +4,10 @@
#include
#include
#include
+#include
+#include
#include
+#include
#include
#include
@@ -32,6 +35,7 @@ public:
Length,
Color,
Identifier,
+ Image,
};
Type type() const { return m_type; }
@@ -40,6 +44,7 @@ public:
bool is_initial() const { return type() == Type::Initial; }
bool is_color() const { return type() == Type::Color; }
bool is_identifier() const { return type() == Type::Identifier; }
+ bool is_image() const { return type() == Type::Image; }
virtual String to_string() const = 0;
virtual Length to_length() const { return {}; }
@@ -171,3 +176,20 @@ private:
CSS::ValueID m_id { CSS::ValueID::Invalid };
};
+
+class ImageStyleValue final : public StyleValue {
+public:
+ static NonnullRefPtr create(const URL& url, Document& document) { return adopt(*new ImageStyleValue(url, document)); }
+ virtual ~ImageStyleValue() override {}
+
+ String to_string() const override { return String::format("Image{%s}", m_url.to_string().characters()); }
+
+ const GraphicsBitmap* bitmap() const { return m_bitmap; }
+
+private:
+ ImageStyleValue(const URL&, Document&);
+
+ URL m_url;
+ WeakPtr m_document;
+ RefPtr m_bitmap;
+};
diff --git a/Libraries/LibHTML/DOM/Document.cpp b/Libraries/LibHTML/DOM/Document.cpp
index 9be8bc28a8..9a6c991160 100644
--- a/Libraries/LibHTML/DOM/Document.cpp
+++ b/Libraries/LibHTML/DOM/Document.cpp
@@ -119,6 +119,27 @@ Color Document::background_color() const
return background_color.value()->to_color(*this);
}
+RefPtr Document::background_image() const
+{
+ auto* body_element = body();
+ if (!body_element)
+ return {};
+
+ auto* body_layout_node = body_element->layout_node();
+ if (!body_layout_node)
+ return {};
+
+ auto background_image = body_layout_node->style().property(CSS::PropertyID::BackgroundImage);
+ if (!background_image.has_value() || !background_image.value()->is_image())
+ return {};
+
+ auto& image_value = static_cast(*background_image.value());
+ if (!image_value.bitmap())
+ return {};
+
+ return *image_value.bitmap();
+}
+
URL Document::complete_url(const String& string) const
{
URL url(string);
diff --git a/Libraries/LibHTML/DOM/Document.h b/Libraries/LibHTML/DOM/Document.h
index 032522b694..94fb6ff49d 100644
--- a/Libraries/LibHTML/DOM/Document.h
+++ b/Libraries/LibHTML/DOM/Document.h
@@ -56,6 +56,7 @@ public:
const Frame* frame() const { return m_frame.ptr(); }
Color background_color() const;
+ RefPtr background_image() const;
Color link_color() const { return m_link_color; }
void set_link_color(Color);
diff --git a/Libraries/LibHTML/DOM/HTMLBodyElement.cpp b/Libraries/LibHTML/DOM/HTMLBodyElement.cpp
index 724bf31459..03c25c7757 100644
--- a/Libraries/LibHTML/DOM/HTMLBodyElement.cpp
+++ b/Libraries/LibHTML/DOM/HTMLBodyElement.cpp
@@ -23,6 +23,8 @@ void HTMLBodyElement::apply_presentational_hints(StyleProperties& style) const
auto color = Color::from_string(value);
if (color.has_value())
style.set_property(CSS::PropertyID::Color, ColorStyleValue::create(color.value()));
+ } else if (name == "background") {
+ style.set_property(CSS::PropertyID::BackgroundImage, ImageStyleValue::create(document().complete_url(value), const_cast(document())));
}
});
}
diff --git a/Libraries/LibHTML/HtmlView.cpp b/Libraries/LibHTML/HtmlView.cpp
index ef99039562..311223dc6b 100644
--- a/Libraries/LibHTML/HtmlView.cpp
+++ b/Libraries/LibHTML/HtmlView.cpp
@@ -25,6 +25,10 @@ HtmlView::HtmlView(GWidget* parent)
, m_main_frame(Frame::create())
{
main_frame().on_set_needs_display = [this](auto& content_rect) {
+ if (content_rect.is_empty()) {
+ update();
+ return;
+ }
Rect adjusted_rect = content_rect;
adjusted_rect.set_location(to_widget_position(content_rect.location()));
update(adjusted_rect);
@@ -118,6 +122,10 @@ void HtmlView::paint_event(GPaintEvent& event)
painter.fill_rect(event.rect(), m_document->background_color());
+ if (auto background_bitmap = m_document->background_image()) {
+ painter.draw_tiled_bitmap(event.rect(), *background_bitmap);
+ }
+
painter.translate(frame_thickness(), frame_thickness());
painter.translate(-horizontal_scrollbar().value(), -vertical_scrollbar().value());
diff --git a/Libraries/LibHTML/Layout/LayoutBox.cpp b/Libraries/LibHTML/Layout/LayoutBox.cpp
index 39d7c494e8..44ab81fe03 100644
--- a/Libraries/LibHTML/Layout/LayoutBox.cpp
+++ b/Libraries/LibHTML/Layout/LayoutBox.cpp
@@ -1,5 +1,6 @@
#include
#include
+#include
#include
#include
#include
@@ -26,9 +27,19 @@ void LayoutBox::render(RenderingContext& context)
padded_rect.set_y(y() - box_model().padding().top.to_px());
padded_rect.set_height(height() + box_model().padding().top.to_px() + box_model().padding().bottom.to_px());
- auto bgcolor = style().property(CSS::PropertyID::BackgroundColor);
- if (bgcolor.has_value() && bgcolor.value()->is_color()) {
- context.painter().fill_rect(padded_rect, bgcolor.value()->to_color(document()));
+ if (!is_body()) {
+ auto bgcolor = style().property(CSS::PropertyID::BackgroundColor);
+ if (bgcolor.has_value() && bgcolor.value()->is_color()) {
+ context.painter().fill_rect(padded_rect, bgcolor.value()->to_color(document()));
+ }
+
+ auto bgimage = style().property(CSS::PropertyID::BackgroundImage);
+ if (bgimage.has_value() && bgimage.value()->is_image()) {
+ auto& image_value = static_cast(*bgimage.value());
+ if (image_value.bitmap()) {
+ context.painter().draw_tiled_bitmap(padded_rect, *image_value.bitmap());
+ }
+ }
}
// FIXME: Respect all individual border sides
@@ -93,3 +104,8 @@ void LayoutBox::set_needs_display()
LayoutNode::set_needs_display();
}
+
+bool LayoutBox::is_body() const
+{
+ return node() && node() == document().body();
+}
diff --git a/Libraries/LibHTML/Layout/LayoutBox.h b/Libraries/LibHTML/Layout/LayoutBox.h
index f66be67571..3fa0c294bf 100644
--- a/Libraries/LibHTML/Layout/LayoutBox.h
+++ b/Libraries/LibHTML/Layout/LayoutBox.h
@@ -18,6 +18,8 @@ public:
virtual HitTestResult hit_test(const Point& position) const override;
virtual void set_needs_display() override;
+ bool is_body() const;
+
protected:
LayoutBox(const Node* node, NonnullRefPtr style)
: LayoutNodeWithStyleAndBoxModelMetrics(node, move(style))