about summary refs log tree commit diff stats
path: root/src
diff options
context:
space:
mode:
authorbptato <nincsnevem662@gmail.com>2022-12-27 23:36:13 +0100
committerbptato <nincsnevem662@gmail.com>2022-12-27 23:42:28 +0100
commitb5cffb3d805714767f2568b09fe86bb3a83d10b7 (patch)
treed9106729545b5b3915bfd3a97cf08f8da0d82619 /src
parent65b0b48f445b6c56016a3c842089ef117a9298bc (diff)
downloadchawan-b5cffb3d805714767f2568b09fe86bb3a83d10b7.tar.gz
layout/engine: get rid of dom dependency
Layout should only depend on cascading.
Diffstat (limited to 'src')
-rw-r--r--src/css/cascade.nim57
-rw-r--r--src/css/selectorparser.nim2
-rw-r--r--src/css/values.nim37
-rw-r--r--src/layout/box.nim1
-rw-r--r--src/layout/engine.nim53
-rw-r--r--src/render/renderdocument.nim2
6 files changed, 96 insertions, 56 deletions
diff --git a/src/css/cascade.nim b/src/css/cascade.nim
index 1207444d..633bf0f5 100644
--- a/src/css/cascade.nim
+++ b/src/css/cascade.nim
@@ -74,49 +74,61 @@ func calcRules(styledNode: StyledNode, sheet: CSSStylesheet): DeclarationList =
           dl
 
 func calcPresentationalHints(element: Element): CSSComputedValues =
-  template set_cv(a, b, c: untyped) =
+  template set_cv(a, b: untyped) =
     if result == nil:
       new(result)
-    result[a] = CSSComputedValue(t: a, v: ValueTypes[a], b: c)
+    result{a} = b
   template map_width =
     let s = parseDimensionValues(element.attr("width"))
     if s.isSome:
-      set_cv(PROPERTY_WIDTH, length, s.get)
+      set_cv "width", s.get
   template map_height =
     let s = parseDimensionValues(element.attr("height"))
     if s.isSome:
-      set_cv(PROPERTY_HEIGHT, length, s.get)
+      set_cv "height", s.get
   template map_width_nozero =
     let s = parseDimensionValues(element.attr("width"))
     if s.isSome and s.get.num != 0:
-      set_cv(PROPERTY_WIDTH, length, s.get)
+      set_cv "width", s.get
   template map_height_nozero =
     let s = parseDimensionValues(element.attr("height"))
     if s.isSome and s.get.num != 0:
-      set_cv(PROPERTY_HEIGHT, length, s.get)
+      set_cv "height", s.get
   template map_bgcolor =
     let c = parseLegacyColor(element.attr("bgcolor"))
     if c.isSome:
-      set_cv(PROPERTY_BACKGROUND_COLOR, color, c.get)
+      set_cv "background-color", c.get
   template map_valign =
     case element.attr("valign").toLowerAscii()
-    of "top": set_cv(PROPERTY_VERTICAL_ALIGN, verticalalign, CSSVerticalAlign(keyword: VERTICAL_ALIGN_TOP))
-    of "middle": set_cv(PROPERTY_VERTICAL_ALIGN, verticalalign, CSSVerticalAlign(keyword: VERTICAL_ALIGN_MIDDLE))
-    of "bottom": set_cv(PROPERTY_VERTICAL_ALIGN, verticalalign, CSSVerticalAlign(keyword: VERTICAL_ALIGN_BOTTOM))
-    of "baseline": set_cv(PROPERTY_VERTICAL_ALIGN, verticalalign, CSSVerticalAlign(keyword: VERTICAL_ALIGN_BASELINE))
+    of "top": set_cv "vertical-align", CSSVerticalAlign(keyword: VERTICAL_ALIGN_TOP)
+    of "middle": set_cv "vertical-align", CSSVerticalAlign(keyword: VERTICAL_ALIGN_MIDDLE)
+    of "bottom": set_cv "vertical-align", CSSVerticalAlign(keyword: VERTICAL_ALIGN_BOTTOM)
+    of "baseline": set_cv "vertical-align", CSSVerticalAlign(keyword: VERTICAL_ALIGN_BASELINE)
   template map_align =
     case element.attr("align").toLowerAscii()
-    of "center", "middle": set_cv(PROPERTY_TEXT_ALIGN, textalign, TEXT_ALIGN_CHA_CENTER)
-    of "left": set_cv(PROPERTY_TEXT_ALIGN, textalign, TEXT_ALIGN_CHA_LEFT)
-    of "right": set_cv(PROPERTY_TEXT_ALIGN, textalign, TEXT_ALIGN_CHA_RIGHT)
+    of "center", "middle": set_cv "text-align", TEXT_ALIGN_CHA_CENTER
+    of "left": set_cv "text-align", TEXT_ALIGN_CHA_LEFT
+    of "right": set_cv "text-align", TEXT_ALIGN_CHA_RIGHT
   template map_text =
     let c = parseLegacyColor(element.attr("text"))
     if c.isSome:
-      set_cv(PROPERTY_COLOR, color, c.get)
+      set_cv "color", c.get
   template map_color =
     let c = parseLegacyColor(element.attr("color"))
     if c.isSome:
-      set_cv(PROPERTY_COLOR, color, c.get)
+      set_cv "color", c.get
+  template map_colspan =
+    let colspan = element.attrigz("colspan")
+    if colspan.isSome:
+      let i = colspan.get
+      if i <= 1000:
+        set_cv "-cha-colspan", i
+  template map_rowspan =
+    let rowspan = element.attrigez("rowspan")
+    if rowspan.isSome:
+      let i = rowspan.get
+      if i <= 65534:
+        set_cv "-cha-rowspan", i
 
   case element.tagType
   of TAG_DIV:
@@ -131,6 +143,8 @@ func calcPresentationalHints(element: Element): CSSComputedValues =
     map_bgcolor
     map_valign
     map_align
+    map_colspan
+    map_rowspan
   of TAG_THEAD, TAG_TBODY, TAG_TFOOT, TAG_TR:
     map_height
     map_bgcolor
@@ -146,8 +160,8 @@ func calcPresentationalHints(element: Element): CSSComputedValues =
     map_text
   of TAG_TEXTAREA:
     let textarea = HTMLTextAreaElement(element)
-    set_cv(PROPERTY_WIDTH, length, CSSLength(unit: UNIT_CH, num: float64(textarea.cols)))
-    set_cv(PROPERTY_HEIGHT, length, CSSLength(unit: UNIT_EM, num: float64(textarea.rows)))
+    set_cv "width", CSSLength(unit: UNIT_CH, num: float64(textarea.cols))
+    set_cv "height", CSSLength(unit: UNIT_EM, num: float64(textarea.rows))
   of TAG_FONT:
     map_color
   else: discard
@@ -286,6 +300,11 @@ proc applyRules(document: Document, ua, user: CSSStylesheet, cachedTree: StyledN
           let styledText = styledParent.newStyledReplacement(content)
           styledText.pseudo = pseudo
           styledParent.children.add(styledText)
+        of PSEUDO_NEWLINE:
+          let content = CSSContent(t: CONTENT_NEWLINE)
+          let styledText = styledParent.newStyledReplacement(content)
+          styledText.pseudo = pseudo
+          styledParent.children.add(styledText)
         of PSEUDO_NONE: discard
       else:
         assert child != nil
@@ -364,6 +383,8 @@ proc applyRules(document: Document, ua, user: CSSStylesheet, cachedTree: StyledN
         stack_append styledChild, PSEUDO_TEXTAREA_TEXT
       elif elem.tagType == TAG_IMG or elem.tagType == TAG_IMAGE:
         stack_append styledChild, PSEUDO_IMAGE
+      elif elem.tagType == TAG_BR:
+        stack_append styledChild, PSEUDO_NEWLINE
       else:
         for i in countdown(elem.childList.high, 0):
           if elem.childList[i].nodeType in {ELEMENT_NODE, TEXT_NODE}:
diff --git a/src/css/selectorparser.nim b/src/css/selectorparser.nim
index 8a38292b..2c6ab659 100644
--- a/src/css/selectorparser.nim
+++ b/src/css/selectorparser.nim
@@ -19,7 +19,7 @@ type
   PseudoElem* = enum
     PSEUDO_NONE, PSEUDO_BEFORE, PSEUDO_AFTER,
     # internal
-    PSEUDO_INPUT_TEXT, PSEUDO_TEXTAREA_TEXT, PSEUDO_IMAGE
+    PSEUDO_INPUT_TEXT, PSEUDO_TEXTAREA_TEXT, PSEUDO_IMAGE, PSEUDO_NEWLINE
 
   PseudoClass* = enum
     PSEUDO_FIRST_CHILD, PSEUDO_LAST_CHILD, PSEUDO_ONLY_CHILD, PSEUDO_HOVER,
diff --git a/src/css/values.nim b/src/css/values.nim
index 874d82a0..f847b72b 100644
--- a/src/css/values.nim
+++ b/src/css/values.nim
@@ -35,7 +35,8 @@ type
     PROPERTY_RIGHT, PROPERTY_TOP, PROPERTY_BOTTOM, PROPERTY_CAPTION_SIDE,
     PROPERTY_BORDER_SPACING, PROPERTY_BORDER_COLLAPSE, PROPERTY_QUOTES,
     PROPERTY_COUNTER_RESET, PROPERTY_MAX_WIDTH, PROPERTY_MAX_HEIGHT,
-    PROPERTY_MIN_WIDTH, PROPERTY_MIN_HEIGHT, PROPERTY_BACKGROUND_IMAGE
+    PROPERTY_MIN_WIDTH, PROPERTY_MIN_HEIGHT, PROPERTY_BACKGROUND_IMAGE,
+    PROPERTY_CHA_COLSPAN, PROPERTY_CHA_ROWSPAN
 
   CSSValueType* = enum
     VALUE_NONE, VALUE_LENGTH, VALUE_COLOR, VALUE_CONTENT, VALUE_DISPLAY,
@@ -102,7 +103,8 @@ type
 
   CSSContentType* = enum
     CONTENT_STRING, CONTENT_OPEN_QUOTE, CONTENT_CLOSE_QUOTE,
-    CONTENT_NO_OPEN_QUOTE, CONTENT_NO_CLOSE_QUOTE, CONTENT_IMAGE
+    CONTENT_NO_OPEN_QUOTE, CONTENT_NO_CLOSE_QUOTE, CONTENT_IMAGE,
+    CONTENT_NEWLINE
 
 const RowGroupBox* = {DISPLAY_TABLE_ROW_GROUP, DISPLAY_TABLE_HEADER_GROUP,
                       DISPLAY_TABLE_FOOTER_GROUP}
@@ -250,7 +252,9 @@ const PropertyNames = {
   "max-height": PROPERTY_MAX_HEIGHT,
   "min-width": PROPERTY_MIN_WIDTH,
   "min-height": PROPERTY_MIN_HEIGHT,
-  "background-image": PROPERTY_BACKGROUND_IMAGE
+  "background-image": PROPERTY_BACKGROUND_IMAGE,
+  "-cha-colspan": PROPERTY_CHA_COLSPAN,
+  "-cha-rowspan": PROPERTY_CHA_ROWSPAN
 }.toTable()
 
 const ValueTypes* = [
@@ -294,7 +298,9 @@ const ValueTypes* = [
   PROPERTY_MAX_HEIGHT: VALUE_LENGTH,
   PROPERTY_MIN_WIDTH: VALUE_LENGTH,
   PROPERTY_MIN_HEIGHT: VALUE_LENGTH,
-  PROPERTY_BACKGROUND_IMAGE: VALUE_IMAGE
+  PROPERTY_BACKGROUND_IMAGE: VALUE_IMAGE,
+  PROPERTY_CHA_COLSPAN: VALUE_INTEGER,
+  PROPERTY_CHA_ROWSPAN: VALUE_INTEGER
 ]
 
 const InheritedProperties = {
@@ -856,6 +862,15 @@ func cssImage(cval: CSSComponentValue): Option[CSSContent] =
     if tok.tokenType == CSS_URL_TOKEN or tok.tokenType == CSS_BAD_URL_TOKEN:
       return some(CSSContent(t: CONTENT_IMAGE, s: "[img]"))
 
+func cssInteger(cval: CSSComponentValue, range: Slice[int]): int =
+  if isToken(cval):
+    let tok = getToken(cval)
+    if tok.tokenType == CSS_NUMBER_TOKEN:
+      let i = int(tok.nvalue)
+      if float64(i) == tok.nvalue and i in range:
+        return i
+  raise newException(CSSValueError, "Invalid integer")
+
 proc getValueFromDecl(val: CSSComputedValue, d: CSSDeclaration, vtype: CSSValueType, ptype: CSSPropertyType) =
   var i = 0
   d.value.skipWhitespace(i)
@@ -891,6 +906,10 @@ proc getValueFromDecl(val: CSSComputedValue, d: CSSDeclaration, vtype: CSSValueT
   of VALUE_INTEGER:
     if ptype == PROPERTY_FONT_WEIGHT:
       val.integer = cssFontWeight(cval)
+    elif ptype == PROPERTY_CHA_COLSPAN:
+      val.integer = cssInteger(cval, 1 .. 1000)
+    elif ptype == PROPERTY_CHA_ROWSPAN:
+      val.integer = cssInteger(cval, 0 .. 65534)
   of VALUE_TEXT_DECORATION: val.textdecoration = cssTextDecoration(d)
   of VALUE_WORD_BREAK: val.wordbreak = cssWordBreak(cval)
   of VALUE_LIST_STYLE_TYPE: val.liststyletype = cssListStyleType(cval)
@@ -947,6 +966,14 @@ func getInitialLength(t: CSSPropertyType): CSSLength =
   else:
     return CSSLength(auto: false, unit: UNIT_PX, num: 0)
 
+func getInitialInteger(t: CSSPropertyType): int =
+  case t
+  of PROPERTY_CHA_COLSPAN, PROPERTY_CHA_ROWSPAN:
+    return 1
+  of PROPERTY_FONT_WEIGHT:
+    return 400 # normal
+  else: discard
+
 func calcInitial(t: CSSPropertyType): CSSComputedValue =
   let v = valueType(t)
   var nv: CSSComputedValue
@@ -959,6 +986,8 @@ func calcInitial(t: CSSPropertyType): CSSComputedValue =
     nv = CSSComputedValue(t: t, v: v, wordbreak: WORD_BREAK_NORMAL)
   of VALUE_LENGTH:
     nv = CSSComputedValue(t: t, v: v, length: getInitialLength(t))
+  of VALUE_INTEGER:
+    nv = CSSComputedValue(t: t, v: v, integer: getInitialInteger(t))
   of VALUE_QUOTES:
     nv = CSSComputedValue(t: t, v: v, quotes: CSSQuotes(auto: true))
   else:
diff --git a/src/layout/box.nim b/src/layout/box.nim
index a436cfae..05df1ae6 100644
--- a/src/layout/box.nim
+++ b/src/layout/box.nim
@@ -50,7 +50,6 @@ type
   TableRowBoxBuilder* = ref object of BlockBoxBuilder
 
   TableCellBoxBuilder* = ref object of BlockBoxBuilder
-    colspan*: int
 
   TableBoxBuilder* = ref object of BlockBoxBuilder
     rowgroups*: seq[TableRowGroupBoxBuilder]
diff --git a/src/layout/engine.nim b/src/layout/engine.nim
index b26e0ebe..97b19f2b 100644
--- a/src/layout/engine.nim
+++ b/src/layout/engine.nim
@@ -6,8 +6,6 @@ import unicode
 
 import css/stylednode
 import css/values
-import html/tags
-import html/dom
 import io/window
 import layout/box
 import utils/twtstr
@@ -844,15 +842,16 @@ proc preBuildTableRow(pctx: var TableContext, box: TableRowBoxBuilder, parent: B
   for child in box.children:
     assert child.computed{"display"} == DISPLAY_TABLE_CELL
     let cellbuilder = TableCellBoxBuilder(child)
+    let colspan = cellbuilder.computed{"-cha-colspan"}
     let cell = parent.viewport.buildTableCell(cellbuilder, parent.contentWidth, parent.contentHeight)
-    ctx.cells[i] = CellWrapper(box: cell, builder: cellbuilder, colspan: cellbuilder.colspan)
-    if pctx.cols.len < n + cellbuilder.colspan:
-      pctx.cols.setLen(n + cellbuilder.colspan)
-    if ctx.reflow.len < n + cellbuilder.colspan:
-      ctx.reflow.setLen(n + cellbuilder.colspan)
-    let minw = cell.xminwidth div cellbuilder.colspan
-    let w = cell.width div cellbuilder.colspan
-    for i in n ..< n + cellbuilder.colspan:
+    ctx.cells[i] = CellWrapper(box: cell, builder: cellbuilder, colspan: colspan)
+    if pctx.cols.len < n + colspan:
+      pctx.cols.setLen(n + colspan)
+    if ctx.reflow.len < n + colspan:
+      ctx.reflow.setLen(n + colspan)
+    let minw = cell.xminwidth div colspan
+    let w = cell.width div colspan
+    for i in n ..< n + colspan:
       ctx.width += pctx.inlinespacing
       pctx.cols[i].maxwidth = w
       if pctx.cols[i].width < w:
@@ -878,7 +877,7 @@ proc preBuildTableRow(pctx: var TableContext, box: TableRowBoxBuilder, parent: B
           ctx.reflow[i] = true
       ctx.width += pctx.cols[i].width
       ctx.width += pctx.inlinespacing
-    n += cellbuilder.colspan
+    n += colspan
     inc i
   ctx.ncols = n
   return ctx
@@ -1122,13 +1121,7 @@ proc buildRootBlock(viewport: Viewport, builder: BlockBoxBuilder) =
 # Returns a block box, disregarding the computed value of display
 proc getBlockBox(computed: CSSComputedValues): BlockBoxBuilder =
   new(result)
-  result.computed = computed.copyProperties()
-  result.computed{"display"} = DISPLAY_BLOCK
-
-proc getTextBox(box: BoxBuilder): InlineBoxBuilder =
-  new(result)
-  result.inlinelayout = true
-  result.computed = box.computed.inheritProperties()
+  result.computed = computed
 
 proc getTextBox(computed: CSSComputedValues): InlineBoxBuilder =
   new(result)
@@ -1164,7 +1157,6 @@ proc getTableRowBox(computed: CSSComputedValues): TableRowBoxBuilder =
 proc getTableCellBox(computed: CSSComputedValues): TableCellBoxBuilder =
   new(result)
   result.computed = computed
-  result.colspan = 1
 
 proc getTableCaptionBox(computed: CSSComputedValues): TableCaptionBoxBuilder =
   new(result)
@@ -1193,7 +1185,9 @@ proc add(blockgroup: var BlockGroup, box: BoxBuilder) {.inline.} =
 proc flush(blockgroup: var BlockGroup) {.inline.} =
   if blockgroup.boxes.len > 0:
     assert blockgroup.parent.computed{"display"} != DISPLAY_INLINE
-    let bbox = getBlockBox(blockgroup.parent.computed.inheritProperties())
+    let computed = blockgroup.parent.computed.inheritProperties()
+    computed{"display"} = DISPLAY_BLOCK
+    let bbox = getBlockBox(computed)
     bbox.inlinelayout = true
     bbox.children = blockgroup.boxes
     blockgroup.parent.children.add(bbox)
@@ -1264,13 +1258,6 @@ proc flush(ctx: var InnerBlockContext) =
 
 proc generateFromElem(ctx: var InnerBlockContext, styledNode: StyledNode) =
   let box = ctx.blockgroup.parent
-  if styledNode.node != nil:
-    let elem = Element(styledNode.node)
-    if elem.tagType == TAG_BR:
-      ctx.iflush()
-      ctx.ibox = box.getTextBox()
-      ctx.ibox.newline = true
-      ctx.iflush()
 
   case styledNode.computed{"display"}
   of DISPLAY_BLOCK:
@@ -1287,6 +1274,8 @@ proc generateFromElem(ctx: var InnerBlockContext, styledNode: StyledNode) =
       childbox.marker = nil
     else:
       childbox.content = ctx.generateBlockBox(styledNode)
+    childbox.content.computed = childbox.content.computed.copyProperties()
+    childbox.content.computed{"display"} = DISPLAY_BLOCK
     box.children.add(childbox)
   of DISPLAY_INLINE:
     ctx.iflush()
@@ -1294,7 +1283,6 @@ proc generateFromElem(ctx: var InnerBlockContext, styledNode: StyledNode) =
   of DISPLAY_INLINE_BLOCK:
     ctx.iflush()
     let childbox = ctx.generateBlockBox(styledNode)
-    childbox.computed{"display"} = DISPLAY_INLINE_BLOCK
     ctx.blockgroup.add(childbox)
   of DISPLAY_TABLE:
     ctx.flush()
@@ -1396,6 +1384,11 @@ proc generateReplacement(ctx: var InnerBlockContext, child, parent: StyledNode)
   of CONTENT_IMAGE:
     #TODO idk
     ctx.generateInlineText(child.content.s, parent)
+  of CONTENT_NEWLINE:
+    ctx.iflush()
+    ctx.ibox = parent.computed.getTextBox()
+    ctx.ibox.newline = true
+    ctx.iflush()
 
 proc generateInlineBoxes(ctx: var InnerBlockContext, styledNode: StyledNode) =
   for child in styledNode.children:
@@ -1466,8 +1459,6 @@ proc generateBlockBox(styledNode: StyledNode, viewport: Viewport, marker = none(
 
 proc generateTableCellBox(styledNode: StyledNode, viewport: Viewport, parent: var InnerBlockContext): TableCellBoxBuilder =
   let box = getTableCellBox(styledNode.computed)
-  if styledNode.node != nil and styledNode.node.nodeType == ELEMENT_NODE:
-    box.colspan = Element(styledNode.node).attri("colspan").get(1)
   var ctx = newInnerBlockContext(styledNode, box, viewport, addr parent)
   ctx.generateInnerBlockBox()
   ctx.flush()
@@ -1545,7 +1536,7 @@ proc generateTableBox(styledNode: StyledNode, viewport: Viewport, parent: var In
   box.generateTableChildWrappers()
   return box
 
-proc renderLayout*(viewport: var Viewport, document: Document, root: StyledNode) =
+proc renderLayout*(viewport: var Viewport, root: StyledNode) =
   viewport.root.setLen(0)
   viewport.absolutes.setLen(0)
   let builder = root.generateBlockBox(viewport)
diff --git a/src/render/renderdocument.nim b/src/render/renderdocument.nim
index a988cca7..cd6d7a0e 100644
--- a/src/render/renderdocument.nim
+++ b/src/render/renderdocument.nim
@@ -335,7 +335,7 @@ proc renderDocument*(document: Document, window: WindowAttributes, userstyle: CS
     uastyle = quirkstyle
   let styledNode = document.applyStylesheets(uastyle, userstyle, previousStyled)
   result[1] = styledNode
-  layout.renderLayout(document, styledNode)
+  layout.renderLayout(styledNode)
   result[0].setLen(0)
   for root in layout.root:
     result[0].renderBlockContext(root, 0, 0, window)