diff --git a/Base/home/anon/www/position-absolute-top-left.html b/Base/home/anon/www/position-absolute-top-left.html
new file mode 100644
index 0000000000..2c3601f8d1
--- /dev/null
+++ b/Base/home/anon/www/position-absolute-top-left.html
@@ -0,0 +1,67 @@
+
+
+
+
+ absolute
+
+
+
+
+
+
+
+
diff --git a/Base/home/anon/www/welcome.html b/Base/home/anon/www/welcome.html
index d4d99a920a..719d910ff4 100644
--- a/Base/home/anon/www/welcome.html
+++ b/Base/home/anon/www/welcome.html
@@ -23,6 +23,7 @@ h1 {
This is a very simple browser built on the LibWeb engine.
Some small test pages:
+ - position: absolute; for top and left
- fun demo
- requestAnimationFrame test
- canvas 2D test
diff --git a/Libraries/LibWeb/CSS/StyleProperties.cpp b/Libraries/LibWeb/CSS/StyleProperties.cpp
index 8fb316fa26..154a823cf4 100644
--- a/Libraries/LibWeb/CSS/StyleProperties.cpp
+++ b/Libraries/LibWeb/CSS/StyleProperties.cpp
@@ -162,6 +162,22 @@ float StyleProperties::line_height() const
return (float)font().glyph_height() * 1.4f;
}
+CSS::Position StyleProperties::position() const
+{
+ if (property(CSS::PropertyID::Position).has_value()) {
+ String position_string = string_or_fallback(CSS::PropertyID::Position, "static");
+ if (position_string == "relative")
+ return CSS::Position::Relative;
+ if (position_string == "absolute")
+ return CSS::Position::Absolute;
+ if (position_string == "sticky")
+ return CSS::Position::Sticky;
+ if (position_string == "fixed")
+ return CSS::Position::Fixed;
+ }
+ return CSS::Position::Static;
+}
+
bool StyleProperties::operator==(const StyleProperties& other) const
{
if (m_property_values.size() != other.m_property_values.size())
diff --git a/Libraries/LibWeb/CSS/StyleProperties.h b/Libraries/LibWeb/CSS/StyleProperties.h
index 8835ff0380..908e20c641 100644
--- a/Libraries/LibWeb/CSS/StyleProperties.h
+++ b/Libraries/LibWeb/CSS/StyleProperties.h
@@ -70,6 +70,8 @@ public:
bool operator==(const StyleProperties&) const;
bool operator!=(const StyleProperties& other) const { return !(*this == other); }
+ CSS::Position position() const;
+
private:
HashMap> m_property_values;
diff --git a/Libraries/LibWeb/CSS/StyleValue.h b/Libraries/LibWeb/CSS/StyleValue.h
index c1214a0bca..49d9098f9d 100644
--- a/Libraries/LibWeb/CSS/StyleValue.h
+++ b/Libraries/LibWeb/CSS/StyleValue.h
@@ -50,6 +50,14 @@ enum class ValueID {
Right,
Justify,
};
+
+enum class Position {
+ Static,
+ Relative,
+ Absolute,
+ Fixed,
+ Sticky,
+};
}
class StyleValue : public RefCounted {
@@ -65,6 +73,7 @@ public:
Color,
Identifier,
Image,
+ Position,
};
Type type() const { return m_type; }
@@ -76,6 +85,7 @@ public:
bool is_image() const { return type() == Type::Image; }
bool is_string() const { return type() == Type::String; }
bool is_length() const { return type() == Type::Length; }
+ bool is_position() const { return type() == Type::Position; }
virtual String to_string() const = 0;
virtual Length to_length() const { return {}; }
diff --git a/Libraries/LibWeb/Layout/BoxModelMetrics.h b/Libraries/LibWeb/Layout/BoxModelMetrics.h
index 18bdb4c574..f32fbff9d7 100644
--- a/Libraries/LibWeb/Layout/BoxModelMetrics.h
+++ b/Libraries/LibWeb/Layout/BoxModelMetrics.h
@@ -39,10 +39,12 @@ public:
LengthBox& margin() { return m_margin; }
LengthBox& padding() { return m_padding; }
LengthBox& border() { return m_border; }
+ LengthBox& offset() { return m_offset; }
const LengthBox& margin() const { return m_margin; }
const LengthBox& padding() const { return m_padding; }
const LengthBox& border() const { return m_border; }
+ const LengthBox& offset() const { return m_offset; }
struct PixelBox {
float top;
@@ -57,6 +59,7 @@ private:
LengthBox m_margin;
LengthBox m_padding;
LengthBox m_border;
+ LengthBox m_offset;
};
}
diff --git a/Libraries/LibWeb/Layout/LayoutBlock.cpp b/Libraries/LibWeb/Layout/LayoutBlock.cpp
index 013f9f3d1b..95b78887ed 100644
--- a/Libraries/LibWeb/Layout/LayoutBlock.cpp
+++ b/Libraries/LibWeb/Layout/LayoutBlock.cpp
@@ -296,24 +296,52 @@ void LayoutBlock::compute_position()
auto width = style.length_or_fallback(CSS::PropertyID::Width, auto_value);
+ if (style.position() == CSS::Position::Absolute) {
+ box_model().offset().top = style.length_or_fallback(CSS::PropertyID::Top, zero_value);
+ box_model().offset().right = style.length_or_fallback(CSS::PropertyID::Right, zero_value);
+ box_model().offset().bottom = style.length_or_fallback(CSS::PropertyID::Bottom, zero_value);
+ box_model().offset().left = style.length_or_fallback(CSS::PropertyID::Left, zero_value);
+ }
+
box_model().margin().top = style.length_or_fallback(CSS::PropertyID::MarginTop, zero_value);
box_model().margin().bottom = style.length_or_fallback(CSS::PropertyID::MarginBottom, zero_value);
box_model().border().top = style.length_or_fallback(CSS::PropertyID::BorderTopWidth, zero_value);
box_model().border().bottom = style.length_or_fallback(CSS::PropertyID::BorderBottomWidth, zero_value);
box_model().padding().top = style.length_or_fallback(CSS::PropertyID::PaddingTop, zero_value);
box_model().padding().bottom = style.length_or_fallback(CSS::PropertyID::PaddingBottom, zero_value);
- rect().set_x(containing_block()->x() + box_model().margin().left.to_px() + box_model().border().left.to_px() + box_model().padding().left.to_px());
- float top_border = -1;
- if (previous_sibling() != nullptr) {
- auto& previous_sibling_rect = previous_sibling()->rect();
- auto& previous_sibling_style = previous_sibling()->box_model();
- top_border = previous_sibling_rect.y() + previous_sibling_rect.height();
- top_border += previous_sibling_style.full_margin().bottom;
- } else {
- top_border = containing_block()->y();
+ float position_x = box_model().margin().left.to_px()
+ + box_model().border().left.to_px()
+ + box_model().padding().left.to_px()
+ + box_model().offset().left.to_px();
+
+ if (style.position() != CSS::Position::Absolute || containing_block()->style().position() == CSS::Position::Absolute)
+ position_x += containing_block()->x();
+
+ rect().set_x(position_x);
+
+ float position_y = box_model().full_margin().top
+ + box_model().offset().top.to_px();
+
+ if (style.position() != CSS::Position::Absolute || containing_block()->style().position() == CSS::Position::Absolute) {
+ LayoutBlock* relevant_sibling = previous_sibling();
+ while (relevant_sibling != nullptr) {
+ if (relevant_sibling->style().position() != CSS::Position::Absolute)
+ break;
+ relevant_sibling = relevant_sibling->previous_sibling();
+ }
+
+ if (relevant_sibling == nullptr) {
+ position_y += containing_block()->y();
+ } else {
+ auto& previous_sibling_rect = relevant_sibling->rect();
+ auto& previous_sibling_style = relevant_sibling->box_model();
+ position_y += previous_sibling_rect.y() + previous_sibling_rect.height();
+ position_y += previous_sibling_style.full_margin().bottom;
+ }
}
- rect().set_y(top_border + box_model().full_margin().top);
+
+ rect().set_y(position_y);
}
void LayoutBlock::compute_height()