@@ -140,15 +140,15 @@ void _appendTextToInlineBox(Io::SScan scan, Rc<Style::SpecifiedValues> parentSty
140140 }
141141}
142142
143- bool _buildText (Gc::Ref<Dom::Text> node , Rc<Style::SpecifiedValues> parentStyle, InlineBox& rootInlineBox, bool skipIfWhitespace) {
143+ bool _buildText (Str text , Rc<Style::SpecifiedValues> parentStyle, InlineBox& rootInlineBox, bool skipIfWhitespace) {
144144 if (skipIfWhitespace) {
145- Io::SScan scan{node-> data () };
145+ Io::SScan scan{text };
146146 scan.eat (Re::space ());
147147 if (scan.ended ())
148148 return false ;
149149 }
150150
151- _appendTextToInlineBox (node-> data () , parentStyle, rootInlineBox);
151+ _appendTextToInlineBox (text , parentStyle, rootInlineBox);
152152 return true ;
153153}
154154
@@ -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,11 +289,12 @@ 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
295296// https://developer.mozilla.org/en-US/docs/Web/API/Document_Object_Model/Whitespace#how_does_css_process_whitespace/
296- static void _buildText (BuilderContext bc, Gc::Ref<Dom::Text> node , Rc<Style::SpecifiedValues> parentStyle) {
297+ static void _buildText (BuilderContext bc, Str text , Rc<Style::SpecifiedValues> parentStyle) {
297298 // https://www.w3.org/TR/css-tables-3/#fixup-algorithm
298299 // TODO: For tables, the default case is to skip whitespace text, but there are some extra checks to be done
299300
@@ -307,7 +308,7 @@ static void _buildText(BuilderContext bc, Gc::Ref<Dom::Text> node, Rc<Style::Spe
307308 bc.from == BuilderContext::From::FLEX or
308309 bc.from == BuilderContext::From::BLOCK;
309310
310- bool addedNonWhitespace = _buildText (node , parentStyle, bc.rootInlineBox (), shouldSkipWhitespace);
311+ bool addedNonWhitespace = _buildText (text , parentStyle, bc.rootInlineBox (), shouldSkipWhitespace);
311312
312313 // https://www.w3.org/TR/css-flexbox-1/#algo-anon-box
313314 // https://www.w3.org/TR/css-flexbox-1/#flex-items
@@ -428,7 +429,7 @@ static void createAndBuildInlineFlowfromElement(BuilderContext bc, Rc<Style::Spe
428429 bc.flushRootInlineBoxIntoAnonymousBox ();
429430 return ;
430431 }
431-
432+
432433 if (isVoidElement (el)) {
433434 _buildVoidElement (bc, el);
434435 return ;
@@ -588,7 +589,7 @@ static void _buildTableChildrenWhileWrappingIntoAnonymousBox(BuilderContext bc,
588589 if (bc.parentStyle ->display != Display::Internal::TABLE_ROW)
589590 anonTableWrapper.createRowIfNone (style);
590591 anonTableWrapper.createCellIfNone (style);
591- _buildText (anonTableWrapper.cellBuilderContext (), * text, style);
592+ _buildText (anonTableWrapper.cellBuilderContext (), text-> data () , style);
592593 }
593594 }
594595
@@ -668,6 +669,10 @@ static void _buildTableBox(BuilderContext tableWrapperBc, Gc::Ref<Dom::Element>
668669 searchAndBuildCaption ();
669670 }
670671
672+ if (auto before = el->getPseudoElement (Dom::PseudoElement::BEFORE)) {
673+ _buildPseudoElement (tableWrapperBc, before.unwrap ());
674+ }
675+
671676 // An anonymous table-row box must be generated around each sequence of consecutive children of a table-root
672677 // box which are not proper table child boxes.
673678 _buildTableChildrenWhileWrappingIntoAnonymousBox (tableWrapperBc.toTableContent (tableBox), *el, tableBoxStyle, true , [](Display const & display) {
@@ -678,6 +683,10 @@ static void _buildTableBox(BuilderContext tableWrapperBc, Gc::Ref<Dom::Element>
678683 if (not captionsOnTop) {
679684 searchAndBuildCaption ();
680685 }
686+
687+ if (auto before = el->getPseudoElement (Dom::PseudoElement::AFTER)) {
688+ _buildPseudoElement (tableWrapperBc, before.unwrap ());
689+ }
681690}
682691
683692static Box _createTableWrapperAndBuildTable (BuilderContext bc, Rc<Style::SpecifiedValues> tableStyle, Gc::Ref<Dom::Element> tableBoxEl) {
@@ -742,9 +751,18 @@ static void _innerDisplayDispatchCreationOfInlineLevelBox(BuilderContext bc, Gc:
742751// MARK: Dispatching from Node to builder based on outside role ------------------------------------------------------
743752
744753static void _buildChildren (BuilderContext bc, Gc::Ref<Dom::Node> parent) {
754+ auto el = parent->is <Dom::Element>();
755+ if (auto before = el ? el->getPseudoElement (Dom::PseudoElement::BEFORE) : NONE) {
756+ _buildPseudoElement (bc, before.unwrap ());
757+ }
758+
745759 for (auto child = parent->firstChild (); child; child = child->nextSibling ()) {
746760 _buildNode (bc, *child);
747761 }
762+
763+ if (auto after = el ? el->getPseudoElement (Dom::PseudoElement::AFTER) : NONE) {
764+ _buildPseudoElement (bc, after.unwrap ());
765+ }
748766}
749767
750768static void _buildChildBoxDisplay (BuilderContext bc, Gc::Ref<Dom::Node> node, Display display) {
@@ -797,7 +815,36 @@ static void _buildNode(BuilderContext bc, Gc::Ref<Dom::Node> node) {
797815 _buildChildDefaultDisplay (bc, *el, childStyle, display);
798816 }
799817 } else if (auto text = node->is <Dom::Text>()) {
800- _buildText (bc, *text, bc.parentStyle );
818+ _buildText (bc, text->data (), bc.parentStyle );
819+ }
820+ }
821+
822+ export Box _buildBlockPseudoElement (Dom::PseudoElement& el) {
823+ auto proseStyle = _proseStyleFromStyle (*el.specifiedValues (), el.specifiedValues ()->fontFace );
824+
825+ if (el.specifiedValues ()->content .is <String>()) {
826+ auto prose = makeRc<Gfx::Prose>(proseStyle);
827+ prose->append (el.specifiedValues ()->content .unwrap <String>().str ());
828+ return {el.specifiedValues (), InlineBox{prose}, nullptr };
829+ }
830+
831+ return {el.specifiedValues (), nullptr };
832+ }
833+
834+ static void _buildPseudoElement (BuilderContext bc, Rc<Dom::PseudoElement> pseudoElement) {
835+ auto style = pseudoElement->specifiedValues ();
836+ auto display = style->display ;
837+ if (display == Display::NONE)
838+ return ;
839+
840+ if (display == Display::INLINE) {
841+ bc.startInlineBox (_proseStyleFromStyle (*style, style->fontFace ));
842+ if (auto maybeStr = style->content .is <String>())
843+ _buildText (bc, maybeStr->str (), style);
844+ bc.endInlineBox ();
845+ } else {
846+ bc.flushRootInlineBoxIntoAnonymousBox ();
847+ bc.addToParentBox (_buildBlockPseudoElement (*pseudoElement));
801848 }
802849}
803850
@@ -852,7 +899,6 @@ export Box buildForPseudoElement(Dom::PseudoElement& el, usize currentPage, Runn
852899
853900 if (el.specifiedValues ()->content .is <String>()) {
854901 auto prose = makeRc<Gfx::Prose>(proseStyle);
855-
856902 prose->append (el.specifiedValues ()->content .unwrap <String>().str ());
857903 return {el.specifiedValues (), InlineBox{prose}, nullptr };
858904 } else if (el.specifiedValues ()->content .is <ElementContent>()) {
0 commit comments