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))