@@ -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
@@ -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);
@@ -588,7 +582,7 @@ static void _buildTableChildrenWhileWrappingIntoAnonymousBox(BuilderContext bc,
588582 if (bc.parentStyle ->display != Display::Internal::TABLE_ROW)
589583 anonTableWrapper.createRowIfNone (style);
590584 anonTableWrapper.createCellIfNone (style);
591- _buildText (anonTableWrapper.cellBuilderContext (), * text, style);
585+ _buildText (anonTableWrapper.cellBuilderContext (), text-> data () , style);
592586 }
593587 }
594588
@@ -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,6 +676,10 @@ 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) {
@@ -742,9 +744,18 @@ static void _innerDisplayDispatchCreationOfInlineLevelBox(BuilderContext bc, Gc:
742744// MARK: Dispatching from Node to builder based on outside role ------------------------------------------------------
743745
744746static void _buildChildren (BuilderContext bc, Gc::Ref<Dom::Node> parent) {
747+ auto el = parent->is <Dom::Element>();
748+ if (auto before = el ? el->getPseudoElement (Dom::PseudoElement::BEFORE) : NONE) {
749+ _buildPseudoElement (bc, before.unwrap ());
750+ }
751+
745752 for (auto child = parent->firstChild (); child; child = child->nextSibling ()) {
746753 _buildNode (bc, *child);
747754 }
755+
756+ if (auto after = el ? el->getPseudoElement (Dom::PseudoElement::AFTER) : NONE) {
757+ _buildPseudoElement (bc, after.unwrap ());
758+ }
748759}
749760
750761static void _buildChildBoxDisplay (BuilderContext bc, Gc::Ref<Dom::Node> node, Display display) {
@@ -797,7 +808,36 @@ static void _buildNode(BuilderContext bc, Gc::Ref<Dom::Node> node) {
797808 _buildChildDefaultDisplay (bc, *el, childStyle, display);
798809 }
799810 } else if (auto text = node->is <Dom::Text>()) {
800- _buildText (bc, *text, bc.parentStyle );
811+ _buildText (bc, text->data (), bc.parentStyle );
812+ }
813+ }
814+
815+ export Box _buildBlockPseudoElement (Dom::PseudoElement& el) {
816+ auto proseStyle = _proseStyleFromStyle (*el.specifiedValues (), el.specifiedValues ()->fontFace );
817+
818+ if (el.specifiedValues ()->content .is <String>()) {
819+ auto prose = makeRc<Gfx::Prose>(proseStyle);
820+ prose->append (el.specifiedValues ()->content .unwrap <String>().str ());
821+ return {el.specifiedValues (), InlineBox{prose}, nullptr };
822+ }
823+
824+ return {el.specifiedValues (), nullptr };
825+ }
826+
827+ static void _buildPseudoElement (BuilderContext bc, Rc<Dom::PseudoElement> pseudoElement) {
828+ auto style = pseudoElement->specifiedValues ();
829+ auto display = style->display ;
830+ if (display == Display::NONE)
831+ return ;
832+
833+ if (display == Display::INLINE) {
834+ bc.startInlineBox (_proseStyleFromStyle (*style, style->fontFace ));
835+ if (auto maybeStr = style->content .is <String>())
836+ _buildText (bc, maybeStr->str (), style);
837+ bc.endInlineBox ();
838+ } else {
839+ bc.flushRootInlineBoxIntoAnonymousBox ();
840+ bc.addToParentBox (_buildBlockPseudoElement (*pseudoElement));
801841 }
802842}
803843
@@ -852,7 +892,6 @@ export Box buildForPseudoElement(Dom::PseudoElement& el, usize currentPage, Runn
852892
853893 if (el.specifiedValues ()->content .is <String>()) {
854894 auto prose = makeRc<Gfx::Prose>(proseStyle);
855-
856895 prose->append (el.specifiedValues ()->content .unwrap <String>().str ());
857896 return {el.specifiedValues (), InlineBox{prose}, nullptr };
858897 } else if (el.specifiedValues ()->content .is <ElementContent>()) {
0 commit comments