@@ -170,7 +170,7 @@ struct BuilderContext {
170170
171171 // https://www.w3.org/TR/css-inline-3/#model
172172 void flushRootInlineBoxIntoAnonymousBox () {
173- if (not rootInlineBox ().active ())
173+ if (not _rootInlineBox or not rootInlineBox ().active ())
174174 return ;
175175
176176 // The root inline box inherits from its parent block container, but is otherwise unstyleable.
@@ -289,6 +289,7 @@ struct BuilderContext {
289289};
290290
291291static void _buildNode (BuilderContext bc, Gc::Ref<Dom::Node> node);
292+ static void _buildPseudoElement (BuilderContext bc, Rc<Dom::PseudoElement> pseudoElement);
292293
293294// MARK: Build void/leaves ---------------------------------------------------------
294295
@@ -424,11 +425,6 @@ bool _alwaysInlineBlock(Gc::Ref<Dom::Element> el) {
424425static void _buildChildren (BuilderContext bc, Gc::Ref<Dom::Node> parent);
425426
426427static void createAndBuildInlineFlowfromElement (BuilderContext bc, Rc<Style::SpecifiedValues> style, Gc::Ref<Dom::Element> el) {
427- if (el->qualifiedName == Html::BR_TAG) {
428- bc.flushRootInlineBoxIntoAnonymousBox ();
429- return ;
430- }
431-
432428 if (isVoidElement (el)) {
433429 _buildVoidElement (bc, el);
434430 return ;
@@ -442,9 +438,7 @@ static void createAndBuildInlineFlowfromElement(BuilderContext bc, Rc<Style::Spe
442438}
443439
444440static void buildBlockFlowFromElement (BuilderContext bc, Gc::Ref<Dom::Element> el) {
445- if (el->qualifiedName == Html::BR_TAG) {
446- // do nothing
447- } else if (el->qualifiedName == Svg::SVG_TAG) {
441+ if (el->qualifiedName == Svg::SVG_TAG) {
448442 bc.content () = _buildSVG (el);
449443 } else if (isVoidElement (el)) {
450444 _buildVoidElement (bc, el);
@@ -668,6 +662,10 @@ static void _buildTableBox(BuilderContext tableWrapperBc, Gc::Ref<Dom::Element>
668662 searchAndBuildCaption ();
669663 }
670664
665+ if (auto before = el->getPseudoElement (Dom::PseudoElement::BEFORE)) {
666+ _buildPseudoElement (tableWrapperBc, before.unwrap ());
667+ }
668+
671669 // An anonymous table-row box must be generated around each sequence of consecutive children of a table-root
672670 // box which are not proper table child boxes.
673671 _buildTableChildrenWhileWrappingIntoAnonymousBox (tableWrapperBc.toTableContent (tableBox), *el, tableBoxStyle, true , [](Display const & display) {
@@ -678,18 +676,27 @@ static void _buildTableBox(BuilderContext tableWrapperBc, Gc::Ref<Dom::Element>
678676 if (not captionsOnTop) {
679677 searchAndBuildCaption ();
680678 }
679+
680+ if (auto before = el->getPseudoElement (Dom::PseudoElement::AFTER)) {
681+ _buildPseudoElement (tableWrapperBc, before.unwrap ());
682+ }
681683}
682684
683685static Box _createTableWrapperAndBuildTable (BuilderContext bc, Rc<Style::SpecifiedValues> tableStyle, Gc::Ref<Dom::Element> tableBoxEl) {
684686 auto wrapperStyle = makeRc<Style::SpecifiedValues>(Style::SpecifiedValues::initial ());
685687 wrapperStyle->display = tableStyle->display ;
686688 wrapperStyle->margin = tableStyle->margin ;
689+ wrapperStyle->position = tableStyle->position ;
687690
688691 Box wrapper = {wrapperStyle, tableBoxEl};
689692 InlineBox rootInlineBox{_proseStyleFromStyle (*wrapperStyle, tableBoxEl->specifiedValues ()->fontFace )};
690693
691694 // SPEC: The table wrapper box establishes a block formatting context.
692- _buildTableBox (bc.toBlockContextWithoutRootInline (wrapper), tableBoxEl, tableStyle);
695+ auto innerStyle = makeRc<Style::SpecifiedValues>(*tableStyle);
696+ wrapperStyle->margin = Style::SpecifiedValues::initial ().margin ;
697+ innerStyle->position = Keywords::STATIC;
698+
699+ _buildTableBox (bc.toBlockContextWithoutRootInline (wrapper), tableBoxEl, innerStyle);
693700
694701 return wrapper;
695702}
@@ -723,9 +730,18 @@ static void _innerDisplayDispatchCreationOfInlineLevelBox(BuilderContext bc, Gc:
723730// MARK: Dispatching from Node to builder based on outside role ------------------------------------------------------
724731
725732static void _buildChildren (BuilderContext bc, Gc::Ref<Dom::Node> parent) {
733+ auto el = parent->is <Dom::Element>();
734+ if (auto before = el ? el->getPseudoElement (Dom::PseudoElement::BEFORE) : NONE) {
735+ _buildPseudoElement (bc, before.unwrap ());
736+ }
737+
726738 for (auto child = parent->firstChild (); child; child = child->nextSibling ()) {
727739 _buildNode (bc, *child);
728740 }
741+
742+ if (auto after = el ? el->getPseudoElement (Dom::PseudoElement::AFTER) : NONE) {
743+ _buildPseudoElement (bc, after.unwrap ());
744+ }
729745}
730746
731747static void _buildChildBoxDisplay (BuilderContext bc, Gc::Ref<Dom::Node> node, Display display) {
@@ -782,6 +798,29 @@ static void _buildNode(BuilderContext bc, Gc::Ref<Dom::Node> node) {
782798 }
783799}
784800
801+ export Box _buildBlockPseudoElement (Dom::PseudoElement& el) {
802+ auto proseStyle = _proseStyleFromStyle (*el.specifiedValues (), el.specifiedValues ()->fontFace );
803+
804+ if (el.specifiedValues ()->content .is <String>()) {
805+ auto prose = makeRc<Gfx::Prose>(proseStyle);
806+ prose->append (el.specifiedValues ()->content .unwrap <String>().str ());
807+ return {el.specifiedValues (), InlineBox{prose}, nullptr };
808+ }
809+
810+ return {el.specifiedValues (), nullptr };
811+ }
812+
813+ static void _buildPseudoElement (BuilderContext bc, Rc<Dom::PseudoElement> pseudoElement) {
814+ auto style = pseudoElement->specifiedValues ();
815+ auto display = style->display ;
816+ if (display == Display::NONE)
817+ return ;
818+
819+ // FIXME: Treat all pseudo element as block
820+ bc.flushRootInlineBoxIntoAnonymousBox ();
821+ bc.addToParentBox (_buildBlockPseudoElement (*pseudoElement));
822+ }
823+
785824// MARK: Entry points -----------------------------------------------------------------
786825
787826static auto dumpBoxes = Debug::Flag::debug(" web-boxes" s, " Dump the constructed boxes" s);
0 commit comments