about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorbptato <nincsnevem662@gmail.com>2021-12-18 13:49:52 +0100
committerbptato <nincsnevem662@gmail.com>2021-12-18 13:49:52 +0100
commita125464839927fd04f761c530e90888e340758ef (patch)
tree7c2939cbf9e4c4c461a27505883761d6b3d52fb3
parente4d4e1fada5fe734510d100d073e20b5343fe46e (diff)
downloadchawan-a125464839927fd04f761c530e90888e340758ef.tar.gz
Rework cascading algorithm
-rw-r--r--src/css/style.nim183
-rw-r--r--src/css/values.nim148
-rw-r--r--src/html/dom.nim7
-rw-r--r--src/layout/box.nim2
-rw-r--r--src/layout/engine.nim36
5 files changed, 166 insertions, 210 deletions
diff --git a/src/css/style.nim b/src/css/style.nim
index c8e759e7..63bc48e1 100644
--- a/src/css/style.nim
+++ b/src/css/style.nim
@@ -195,57 +195,31 @@ proc querySelector*(document: Document, q: string): seq[Element] =
   for sel in selectors:
     result.add(document.selectElems(sel))
 
-proc applyComputed(elem: Element, cval: CSSComputedValue, pseudo: PseudoElem) =
-  case pseudo
-  of PSEUDO_NONE:
-    elem.cssvalues[cval.t] = cval
-  of PSEUDO_BEFORE:
-    if elem.cssvalues_before == nil:
-      elem.cssvalues_before.rootProperties()
-    elem.cssvalues_before[cval.t] = cval
-  of PSEUDO_AFTER:
-    if elem.cssvalues_after == nil:
-      elem.cssvalues_after.rootProperties()
-    elem.cssvalues_after[cval.t] = cval
-  elem.cssapplied = true
-  elem.rendered = false
-
-proc applyShorthand(elem: Element, left, right, top, bottom: CSSComputedValue, pseudo: PseudoElem) =
-  elem.applyComputed(left, pseudo)
-  elem.applyComputed(right, pseudo)
-  elem.applyComputed(top, pseudo)
-  elem.applyComputed(bottom, pseudo)
-
-proc applyProperty(elem: Element, s: CSSSpecifiedValue, pseudo: PseudoElem) =
-  var parent: CSSComputedValues
+proc applyProperty(elem: Element, d: CSSDeclaration, pseudo: PseudoElem) =
+  var parent: CSSSpecifiedValues
   if elem.parentElement != nil:
-    parent = elem.parentElement.cssvalues
+    parent = elem.parentElement.css
   else:
     parent = rootProperties()
 
-  let cval = getComputedValue(s, elem.cssvalues, parent)
-  case cval.t
-  of PROPERTY_MARGIN:
-    let left = CSSComputedValue(t: PROPERTY_MARGIN_LEFT, v: VALUE_LENGTH, length: cval.length)
-    let right = CSSComputedValue(t: PROPERTY_MARGIN_RIGHT, v: VALUE_LENGTH, length: cval.length)
-    let top = CSSComputedValue(t: PROPERTY_MARGIN_TOP, v: VALUE_LENGTH, length: cval.length)
-    let bottom = CSSComputedValue(t: PROPERTY_MARGIN_BOTTOM, v: VALUE_LENGTH, length: cval.length)
-    elem.applyShorthand(left, right, top, bottom, pseudo)
-  of PROPERTY_PADDING:
-    let left = CSSComputedValue(t: PROPERTY_PADDING_LEFT, v: VALUE_LENGTH, length: cval.length)
-    let right = CSSComputedValue(t: PROPERTY_PADDING_RIGHT, v: VALUE_LENGTH, length: cval.length)
-    let top = CSSComputedValue(t: PROPERTY_PADDING_TOP, v: VALUE_LENGTH, length: cval.length)
-    let bottom = CSSComputedValue(t: PROPERTY_PADDING_BOTTOM, v: VALUE_LENGTH, length: cval.length)
-    elem.applyShorthand(left, right, top, bottom, pseudo)
-  else: elem.applyComputed(cval, pseudo)
+  case pseudo
+  of PSEUDO_NONE:
+    elem.css.applyValue(parent, d)
+  of PSEUDO_BEFORE, PSEUDO_AFTER:
+    if elem.pseudo[pseudo] == nil:
+      elem.pseudo[pseudo] = elem.css.inheritProperties()
+    elem.pseudo[pseudo].applyValue(parent, d)
 
+  elem.cssapplied = true
+  elem.rendered = false
 
 type
   ParsedRule* = tuple[sels: seq[SelectorList], oblock: CSSSimpleBlock]
   ParsedStylesheet* = seq[ParsedRule]
   ApplyResult = object
-    normal: seq[tuple[e:Element,d:CSSSpecifiedValue,p:PseudoElem]]
-    important: seq[tuple[e:Element,d:CSSSpecifiedValue,p:PseudoElem]]
+    normal: seq[CSSDeclaration]
+    important: seq[CSSDeclaration]
+  RuleList = array[low(PseudoElem)..high(PseudoElem), seq[CSSSimpleBlock]]
 
 proc parseStylesheet*(s: Stream): ParsedStylesheet =
   for v in parseCSS(s).value:
@@ -253,8 +227,7 @@ proc parseStylesheet*(s: Stream): ParsedStylesheet =
     if sels.len > 1 or sels[^1].len > 0:
       result.add((sels: sels, oblock: v.oblock))
 
-func calcRules(elem: Element, rules: ParsedStylesheet):
-    array[low(PseudoElem)..high(PseudoElem), seq[CSSSimpleBlock]] =
+func calcRules(elem: Element, rules: ParsedStylesheet): RuleList =
   var tosorts: array[low(PseudoElem)..high(PseudoElem), seq[tuple[s:int,b:CSSSimpleBlock]]]
   for rule in rules:
     for sel in rule.sels:
@@ -267,43 +240,48 @@ func calcRules(elem: Element, rules: ParsedStylesheet):
     tosorts[i].sort((x, y) => cmp(x.s,y.s))
     result[i] = tosorts[i].map((x) => x.b)
 
-proc applyItems*(ares: var ApplyResult, elem: Element, decls: seq[CSSParsedItem], pseudo: PseudoElem) =
+proc applyItems(ares: var ApplyResult, decls: seq[CSSParsedItem]) =
   for item in decls:
     if item of CSSDeclaration:
       let decl = CSSDeclaration(item)
       if decl.important:
-        ares.important.add((elem, getSpecifiedValue(decl), pseudo))
+        ares.important.add(decl)
       else:
-        ares.normal.add((elem, getSpecifiedValue(decl), pseudo))
+        ares.normal.add(decl)
 
-proc applyRules*(document: Document, pss: ParsedStylesheet, reset: bool = false): ApplyResult =
-  var stack: seq[Element]
+proc applyRules(element: Element, ua, user, author: RuleList, pseudo: PseudoElem) =
+  var ares: ApplyResult
 
-  document.root.cssvalues.rootProperties()
-  stack.add(document.root)
+  let rules_user_agent = ua[pseudo]
+  for rule in rules_user_agent:
+    let decls = parseCSSListOfDeclarations(rule.value)
+    ares.applyItems(decls)
 
-  while stack.len > 0:
-    let elem = stack.pop()
-    if not elem.cssapplied:
-      if reset:
-        elem.cssvalues.rootProperties()
-        elem.cssvalues_before = nil
-        elem.cssvalues_after = nil
-      let rules_pseudo = calcRules(elem, pss)
-      for pseudo in low(PseudoElem)..high(PseudoElem):
-        let rules = rules_pseudo[pseudo]
-        for rule in rules:
-          let decls = parseCSSListOfDeclarations(rule.value)
-          result.applyItems(elem, decls, pseudo)
+  let rules_user = user[pseudo]
+  for rule in rules_user:
+    let decls = parseCSSListOfDeclarations(rule.value)
+    ares.applyItems(decls)
 
-    var i = elem.children.len - 1
-    while i >= 0:
-      let child = elem.children[i]
-      stack.add(child)
-      dec i
+  let rules_author = author[pseudo]
+  for rule in rules_author:
+    let decls = parseCSSListOfDeclarations(rule.value)
+    ares.applyItems(decls)
+
+  if pseudo == PSEUDO_NONE:
+    let style = element.attr("style")
+    if style.len > 0:
+      let inline_rules = newStringStream(style).parseCSSListOfDeclarations()
+      ares.applyItems(inline_rules)
+
+  for rule in ares.normal:
+    element.applyProperty(rule, pseudo)
 
-proc applyAuthorRules*(document: Document): ApplyResult =
+  for rule in ares.important:
+    element.applyProperty(rule, pseudo)
+
+proc applyRules*(document: Document, ua, user: ParsedStylesheet) =
   var stack: seq[Element]
+
   var embedded_rules: seq[ParsedStylesheet]
 
   stack.add(document.head)
@@ -315,16 +293,19 @@ proc applyAuthorRules*(document: Document): ApplyResult =
         if ct.nodeType == TEXT_NODE:
           rules_head &= Text(ct).data
 
+  if rules_head.len > 0:
+    let parsed = newStringStream(rules_head).parseStylesheet()
+    embedded_rules.add(parsed)
+
   stack.setLen(0)
 
   stack.add(document.root)
 
-  if rules_head.len > 0:
-    let parsed = newStringStream(rules_head).parseStylesheet()
-    embedded_rules.add(parsed)
+  document.root.css = rootProperties()
 
   while stack.len > 0:
     let elem = stack.pop()
+
     var rules_local = ""
     for child in elem.children:
       if child.tagType == TAG_STYLE:
@@ -337,19 +318,18 @@ proc applyAuthorRules*(document: Document): ApplyResult =
       embedded_rules.add(parsed)
 
     if not elem.cssapplied:
+      if elem.parentElement != nil:
+        elem.css = elem.parentElement.css.inheritProperties()
+      else:
+        elem.css = rootProperties()
+
+      let uarules = calcRules(elem, ua)
+      let userrules = calcRules(elem, user)
       let this_rules = embedded_rules.concat()
-      let rules_pseudo = calcRules(elem, this_rules)
+      let authorrules = calcRules(elem, this_rules)
 
       for pseudo in low(PseudoElem)..high(PseudoElem):
-        let rules = rules_pseudo[pseudo]
-        for rule in rules:
-          let decls = parseCSSListOfDeclarations(rule.value)
-          result.applyItems(elem, decls, pseudo)
-
-      let style = elem.attr("style")
-      if style.len > 0:
-        let inline_rules = newStringStream(style).parseCSSListOfDeclarations()
-        result.applyItems(elem, inline_rules, PSEUDO_NONE)
+        elem.applyRules(uarules, userrules, authorrules, pseudo)
 
     var i = elem.children.len - 1
     while i >= 0:
@@ -361,44 +341,7 @@ proc applyAuthorRules*(document: Document): ApplyResult =
       discard embedded_rules.pop()
 
 proc applyStylesheets*(document: Document, uass, userss: ParsedStylesheet) =
-  let ua = document.applyRules(uass, true)
-  let user = document.applyRules(userss)
-  let author = document.applyAuthorRules()
-  var elems: seq[Element]
-
-  for rule in ua.normal:
-    if not rule.e.cssapplied:
-      elems.add(rule.e)
-    rule.e.applyProperty(rule.d, rule.p)
-  for rule in user.normal:
-    if not rule.e.cssapplied:
-      elems.add(rule.e)
-    rule.e.applyProperty(rule.d, rule.p)
-  for rule in author.normal:
-    if not rule.e.cssapplied:
-      elems.add(rule.e)
-    rule.e.applyProperty(rule.d, rule.p)
-
-  for rule in author.important:
-    if not rule.e.cssapplied:
-      elems.add(rule.e)
-    rule.e.applyProperty(rule.d, rule.p)
-  for rule in user.important:
-    if not rule.e.cssapplied:
-      elems.add(rule.e)
-    rule.e.applyProperty(rule.d, rule.p)
-  for rule in ua.important:
-    if not rule.e.cssapplied:
-      elems.add(rule.e)
-    rule.e.applyProperty(rule.d, rule.p)
-
-  for elem in elems:
-    if elem.parentElement != nil:
-      elem.cssvalues.inheritProperties(elem.parentElement.cssvalues)
-      if elem.cssvalues_before != nil:
-        elem.cssvalues_before.inheritProperties(elem.cssvalues)
-      if elem.cssvalues_after != nil:
-        elem.cssvalues_after.inheritProperties(elem.cssvalues)
+  document.applyRules(uass, userss)
 
 proc refreshStyle*(elem: Element) =
   elem.cssapplied = false
diff --git a/src/css/values.nim b/src/css/values.nim
index 6bf49dc2..4771dfc5 100644
--- a/src/css/values.nim
+++ b/src/css/values.nim
@@ -8,8 +8,11 @@ import strutils
 
 import utils/twtstr
 import css/parser
+import css/selector
 import types/color
 
+export selector.PseudoElem
+
 type
   CSSUnit* = enum
     UNIT_CM, UNIT_MM, UNIT_IN, UNIT_PX, UNIT_PT, UNIT_PC,
@@ -75,7 +78,7 @@ type
     rgba: RGBAColor
     termcolor: int
   
-  CSSComputedValue* = ref object
+  CSSSpecifiedValue* = ref object
     t*: CSSPropertyType
     case v*: CSSValueType
     of VALUE_COLOR:
@@ -100,11 +103,9 @@ type
       liststyletype*: CSSListStyleType
     of VALUE_NONE: discard
 
-  CSSComputedValues* = ref array[low(CSSPropertyType)..high(CSSPropertyType), CSSComputedValue]
+  CSSSpecifiedValues* = ref array[low(CSSPropertyType)..high(CSSPropertyType), CSSSpecifiedValue]
 
-  CSSSpecifiedValue* = object
-    global*: CSSGlobalValueType
-    computed*: CSSComputedValue
+  CSSValues* = array[low(PseudoElem)..high(PseudoElem), CSSSpecifiedValues]
 
   CSSValueError* = object of ValueError
 
@@ -174,16 +175,15 @@ func getPropInheritedArray(): array[low(CSSPropertyType)..high(CSSPropertyType),
 
 const InheritedArray = getPropInheritedArray()
 
-func propertyType*(s: string): CSSPropertyType =
+func propertyType(s: string): CSSPropertyType =
   return PropertyNames.getOrDefault(s, PROPERTY_NONE)
 
-func valueType*(prop: CSSPropertyType): CSSValueType =
+func valueType(prop: CSSPropertyType): CSSValueType =
   return ValueTypes[prop]
 
-macro `{}`*(vals: CSSComputedValues, s: typed): untyped =
+macro `{}`*(vals: CSSSpecifiedValues, s: typed): untyped =
   let t = propertyType($s)
-  let v = valueType(t)
-  let vs = $v
+  let vs = $valueType(t)
   let s = vs.split(Rune('_'))[1..^1].join("_").tolower()
   result = newDotExpr(newTree(nnkBracketExpr, vals, newLit(t)), newIdentNode(s))
 
@@ -412,7 +412,7 @@ func color(r, g, b: int): CSSColor =
 func color(r, g, b, a: int): CSSColor =
   return CSSColor(rgba: rgba(r, g, b, a))
 
-func cssColor*(d: CSSDeclaration): CSSColor =
+func cssColor(d: CSSDeclaration): CSSColor =
   if d.value.len > 0:
     if d.value[0] of CSSToken:
       let tok = CSSToken(d.value[0])
@@ -499,7 +499,7 @@ func isToken(d: CSSDeclaration): bool = d.value.len > 0 and d.value[0] of CSSTok
 
 func getToken(d: CSSDeclaration): CSSToken = (CSSToken)d.value[0]
 
-func cssGlobal(d: CSSDeclaration): CSSGlobalValueType =
+func cssGlobal*(d: CSSDeclaration): CSSGlobalValueType =
   if isToken(d):
     let tok = getToken(d)
     if tok.tokenType == CSS_IDENT_TOKEN:
@@ -616,7 +616,7 @@ func cssListStyleType(d: CSSDeclaration): CSSListStyleType =
       of "japanese-informal": return LIST_STYLE_TYPE_JAPANESE_INFORMAL
   raise newException(CSSValueError, "Invalid list style")
 
-proc getValueFromDecl(val: CSSComputedValue, d: CSSDeclaration, vtype: CSSValueType, ptype: CSSPropertyType) =
+proc getValueFromDecl(val: CSSSpecifiedValue, d: CSSDeclaration, vtype: CSSValueType, ptype: CSSPropertyType) =
   case vtype
   of VALUE_COLOR: val.color = cssColor(d)
   of VALUE_LENGTH: val.length = cssLength(d)
@@ -632,19 +632,6 @@ proc getValueFromDecl(val: CSSComputedValue, d: CSSDeclaration, vtype: CSSValueT
   of VALUE_LIST_STYLE_TYPE: val.liststyletype = cssListStyleType(d)
   of VALUE_NONE: discard
 
-func getSpecifiedValue*(d: CSSDeclaration): CSSSpecifiedValue =
-  let name = $d.name
-  let ptype = propertyType(name)
-  let vtype = valueType(ptype)
-  result.computed = CSSComputedValue(t: ptype, v: vtype)
-  try:
-    result.computed.getValueFromDecl(d, vtype, ptype)
-  except CSSValueError:
-    result.global = VALUE_UNSET
-
-  if result.global == VALUE_NOGLOBAL:
-    result.global = cssGlobal(d)
-
 func getInitialColor(t: CSSPropertyType): CSSColor =
   case t
   of PROPERTY_COLOR:
@@ -659,63 +646,90 @@ func getInitialLength(t: CSSPropertyType): CSSLength =
   else:
     return CSSLength()
 
-func calcDefault(t: CSSPropertyType): CSSComputedValue =
+func calcInitial(t: CSSPropertyType): CSSSpecifiedValue =
   let v = valueType(t)
-  var nv: CSSComputedValue
+  var nv: CSSSpecifiedValue
   case v
   of VALUE_COLOR:
-    nv = CSSComputedValue(t: t, v: v, color: getInitialColor(t))
+    nv = CSSSpecifiedValue(t: t, v: v, color: getInitialColor(t))
   of VALUE_DISPLAY:
-    nv = CSSComputedValue(t: t, v: v, display: DISPLAY_INLINE)
+    nv = CSSSpecifiedValue(t: t, v: v, display: DISPLAY_INLINE)
   of VALUE_WORD_BREAK:
-    nv = CSSComputedValue(t: t, v: v, wordbreak: WORD_BREAK_NORMAL)
+    nv = CSSSpecifiedValue(t: t, v: v, wordbreak: WORD_BREAK_NORMAL)
   of VALUE_LENGTH:
-    nv = CSSComputedValue(t: t, v: v, length: getInitialLength(t))
+    nv = CSSSpecifiedValue(t: t, v: v, length: getInitialLength(t))
   else:
-    nv = CSSComputedValue(t: t, v: v)
+    nv = CSSSpecifiedValue(t: t, v: v)
   return nv
 
-func getDefaultTable(): array[low(CSSPropertyType)..high(CSSPropertyType), CSSComputedValue] =
+func getInitialTable(): array[low(CSSPropertyType)..high(CSSPropertyType), CSSSpecifiedValue] =
   for i in low(result)..high(result):
-    result[i] = calcDefault(i)
+    result[i] = calcInitial(i)
 
-let defaultTable = getDefaultTable()
+let defaultTable = getInitialTable()
 
-func getDefault(t: CSSPropertyType): CSSComputedValue = {.cast(noSideEffect).}:
+func getDefault(t: CSSPropertyType): CSSSpecifiedValue = {.cast(noSideEffect).}:
   assert defaultTable[t] != nil
   return defaultTable[t]
 
-func getComputedValue*(prop: CSSSpecifiedValue, current, parent: CSSComputedValues): CSSComputedValue =
-  case prop.global
-  of VALUE_INHERIT:
-    if inherited(prop.computed.t):
-      return current[prop.computed.t]
-  of VALUE_INITIAL:
-    return getDefault(prop.computed.t)
-  of VALUE_UNSET:
-    if inherited(prop.computed.t):
-      return current[prop.computed.t]
-    return getDefault(prop.computed.t)
-  of VALUE_REVERT: discard #TODO
-  of VALUE_NOGLOBAL: discard
-
-  return prop.computed
-
-proc rootProperties*(vals: var CSSComputedValues) =
-  new(vals)
-  for prop in low(CSSPropertyType)..high(CSSPropertyType):
-    vals[prop] = getDefault(prop)
+func getSpecifiedValue(d: CSSDeclaration, parent: CSSSpecifiedValues): tuple[a:CSSSpecifiedValue,b:CSSGlobalValueType] =
+  let name = $d.name
+  let ptype = propertyType(name)
+  let vtype = valueType(ptype)
 
-proc inheritProperties*(vals: var CSSComputedValues, parent: CSSComputedValues) =
-  if vals == nil:
-    new(vals)
-  for prop in low(CSSPropertyType)..high(CSSPropertyType):
-    if vals[prop] == nil:
-      vals[prop] = getDefault(prop)
-    if inherited(prop) and parent[prop] != nil and vals[prop] == getDefault(prop):
-      vals[prop] = parent[prop]
+  var val = CSSSpecifiedValue(t: ptype, v: vtype)
+  try:
+    val.getValueFromDecl(d, vtype, ptype)
+  except CSSValueError:
+    val = getDefault(ptype)
+
+  return (val, cssGlobal(d))
+
+proc applyValue(vals, parent: CSSSpecifiedValues, t: CSSPropertyType, val: CSSSpecifiedValue, global: CSSGlobalValueType) =
+  case global
+  of VALUE_INHERIT, VALUE_UNSET:
+    if inherited(t):
+      if parent[t] != nil:
+        vals[t] = parent[t]
+    vals[t] = getDefault(t)
+  of VALUE_INITIAL:
+    vals[t] = getDefault(t)
+  of VALUE_REVERT:
+    vals[t] = getDefault(t) #TODO
+  of VALUE_NOGLOBAL:
+    vals[t] = val
+
+proc applyShorthand(vals, parent: CSSSpecifiedValues, left, right, top, bottom: CSSSpecifiedValue, global: CSSGlobalValueType) =
+  vals.applyValue(parent, left.t, left, global)
+  vals.applyValue(parent, left.t, right, global)
+  vals.applyValue(parent, left.t, top, global)
+  vals.applyValue(parent, left.t, bottom, global)
+
+proc applyValue*(vals, parent: CSSSpecifiedValues, d: CSSDeclaration) =
+  let vv = getSpecifiedValue(d, parent)
+  let val = vv.a
+  case val.t
+  of PROPERTY_ALL:
+    let global = cssGlobal(d)
+    if global != VALUE_NOGLOBAL:
+      for t in low(CSSPropertyType)..high(CSSPropertyType):
+        vals.applyValue(parent, t, nil, global)
+  of PROPERTY_MARGIN:
+    let left = CSSSpecifiedValue(t: PROPERTY_MARGIN_LEFT, v: VALUE_LENGTH, length: val.length)
+    let right = CSSSpecifiedValue(t: PROPERTY_MARGIN_RIGHT, v: VALUE_LENGTH, length: val.length)
+    let top = CSSSpecifiedValue(t: PROPERTY_MARGIN_TOP, v: VALUE_LENGTH, length: val.length)
+    let bottom = CSSSpecifiedValue(t: PROPERTY_MARGIN_BOTTOM, v: VALUE_LENGTH, length: val.length)
+    vals.applyShorthand(parent, left, right, top, bottom, vv.b)
+  of PROPERTY_PADDING:
+    let left = CSSSpecifiedValue(t: PROPERTY_PADDING_LEFT, v: VALUE_LENGTH, length: val.length)
+    let right = CSSSpecifiedValue(t: PROPERTY_PADDING_RIGHT, v: VALUE_LENGTH, length: val.length)
+    let top = CSSSpecifiedValue(t: PROPERTY_PADDING_TOP, v: VALUE_LENGTH, length: val.length)
+    let bottom = CSSSpecifiedValue(t: PROPERTY_PADDING_BOTTOM, v: VALUE_LENGTH, length: val.length)
+    vals.applyShorthand(parent, left, right, top, bottom, vv.b)
+  else:
+    vals.applyValue(parent, val.t, vv.a, vv.b)
 
-func inheritProperties*(parent: CSSComputedValues): CSSComputedValues =
+func inheritProperties*(parent: CSSSpecifiedValues): CSSSpecifiedValues =
   new(result)
   for prop in low(CSSPropertyType)..high(CSSPropertyType):
     if inherited(prop) and parent[prop] != nil:
@@ -723,7 +737,7 @@ func inheritProperties*(parent: CSSComputedValues): CSSComputedValues =
     else:
       result[prop] = getDefault(prop)
 
-func rootProperties*(): CSSComputedValues =
+func rootProperties*(): CSSSpecifiedValues =
   new(result)
   for prop in low(CSSPropertyType)..high(CSSPropertyType):
     result[prop] = getDefault(prop)
diff --git a/src/html/dom.nim b/src/html/dom.nim
index 3de1847c..02258296 100644
--- a/src/html/dom.nim
+++ b/src/html/dom.nim
@@ -65,9 +65,8 @@ type
     id*: string
     classList*: seq[string]
     attributes*: Table[string, string]
-    cssvalues*: CSSComputedValues
-    cssvalues_before*: CSSComputedValues
-    cssvalues_after*: CSSComputedValues
+    css*: CSSSpecifiedValues
+    pseudo*: array[low(PseudoElem)..high(PseudoElem), CSSSpecifiedValues]
     hover*: bool
     cssapplied*: bool
     rendered*: bool
@@ -281,7 +280,7 @@ func newHtmlElement*(tagType: TagType): HTMLElement =
 
   result.nodeType = ELEMENT_NODE
   result.tagType = tagType
-  result.cssvalues.rootProperties()
+  result.css = rootProperties()
 
 func newDocument*(): Document =
   new(result)
diff --git a/src/layout/box.nim b/src/layout/box.nim
index b3cb972a..7a380376 100644
--- a/src/layout/box.nim
+++ b/src/layout/box.nim
@@ -21,7 +21,7 @@ type
     children*: seq[CSSBox]
     icontext*: InlineContext
     bcontext*: BlockContext
-    cssvalues*: CSSComputedValues
+    cssvalues*: CSSSpecifiedValues
     node*: Node
 
   RowContainer* = object
diff --git a/src/layout/engine.nim b/src/layout/engine.nim
index 61472fe9..625b726d 100644
--- a/src/layout/engine.nim
+++ b/src/layout/engine.nim
@@ -74,7 +74,7 @@ proc flushMargins(box: CSSBox) =
   box.bcontext.margin_done += box.bcontext.margin_todo
   box.bcontext.margin_todo = 0
 
-proc applyBlockStart(state: LayoutState, box, parent: CSSBox, vals: CSSComputedValues) =
+proc applyBlockStart(state: LayoutState, box, parent: CSSBox, vals: CSSSpecifiedValues) =
   parent.flushMargins()
   box.bcontext = newBlockContext()
   box.x += vals{"margin-left"}.cells_w(state, parent.bcontext.width)
@@ -101,7 +101,7 @@ proc applyBlockStart(state: LayoutState, box, parent: CSSBox, vals: CSSComputedV
 
   box.cssvalues = vals
 
-func newBlockBox(state: var LayoutState, parent: CSSBox, vals: CSSComputedValues): CSSBlockBox =
+func newBlockBox(state: var LayoutState, parent: CSSBox, vals: CSSSpecifiedValues): CSSBlockBox =
   new(result)
   result.t = BLOCK
   if parent.icontext.conty:
@@ -113,7 +113,7 @@ func newBlockBox(state: var LayoutState, parent: CSSBox, vals: CSSComputedValues
   result.icontext.fromy = result.y
   result.icontext.fromx = result.x
 
-func newInlineBlockBox*(state: LayoutState, parent: CSSBox, vals: CSSComputedValues): CSSInlineBlockBox =
+func newInlineBlockBox*(state: LayoutState, parent: CSSBox, vals: CSSSpecifiedValues): CSSInlineBlockBox =
   new(result)
   result.t = INLINE_BLOCK
   result.x = parent.icontext.fromx
@@ -123,7 +123,7 @@ func newInlineBlockBox*(state: LayoutState, parent: CSSBox, vals: CSSComputedVal
   result.icontext.fromy = result.y
   result.icontext.fromx = result.x
 
-func newInlineBox*(state: LayoutState, parent: CSSBox, vals: CSSComputedValues): CSSInlineBox =
+func newInlineBox*(state: LayoutState, parent: CSSBox, vals: CSSSpecifiedValues): CSSInlineBox =
   new(result)
   result.t = INLINE
   result.x = parent.x
@@ -144,7 +144,7 @@ type InlineState = object
   ww: int
   skip: bool
   nodes: seq[Node]
-  cssvalues: CSSComputedValues
+  cssvalues: CSSSpecifiedValues
   x: int
 
 func fromx(state: InlineState): int = state.icontext.fromx
@@ -240,7 +240,7 @@ proc preWrap(state: var InlineState) =
   state.skip = true
 
 proc processInlineText(str: string, icontext: InlineContext,
-                       bcontext: BlockContext, cssvalues: CSSComputedValues,
+                       bcontext: BlockContext, cssvalues: CSSSpecifiedValues,
                        x: int, nodes: seq[Node]): seq[CSSRowBox] =
   var state: InlineState
   state.icontext = icontext
@@ -377,7 +377,7 @@ proc add(state: var LayoutState, parent: CSSBox, box: CSSBox) =
   of INLINE: state.add(parent, CSSInlineBox(box))
   of INLINE_BLOCK: state.add(parent, CSSInlineBlockBox(box))
 
-proc processComputedValueBox(state: var LayoutState, parent: CSSBox, values: CSSComputedValues): CSSBox =
+proc processComputedValueBox(state: var LayoutState, parent: CSSBox, values: CSSSpecifiedValues): CSSBox =
   case values{"display"}
   of DISPLAY_BLOCK:
     result = state.newBlockBox(parent, values)
@@ -392,7 +392,7 @@ proc processComputedValueBox(state: var LayoutState, parent: CSSBox, values: CSS
   else:
     return nil
 
-proc processBr(state: var LayoutState, parent: CSSBox, vals: CSSComputedValues) =
+proc processBr(state: var LayoutState, parent: CSSBox, vals: CSSSpecifiedValues) =
   if vals{"display"} == DISPLAY_INLINE:
     if parent.icontext.conty:
       parent.flushConty()
@@ -403,9 +403,9 @@ proc processBr(state: var LayoutState, parent: CSSBox, vals: CSSComputedValues)
 
 proc processElemBox(state: var LayoutState, parent: CSSBox, elem: Element): CSSBox =
   if elem.tagType == TAG_BR:
-    state.processBr(parent, elem.cssvalues)
+    state.processBr(parent, elem.css)
 
-  result = state.processComputedValueBox(parent, elem.cssvalues)
+  result = state.processComputedValueBox(parent, elem.css)
   if result != nil:
     result.node = elem
 
@@ -428,12 +428,12 @@ proc processNode(state: var LayoutState, parent: CSSBox, node: Node): CSSBox =
   else: discard
 
 proc processBeforePseudoElem(state: var LayoutState, parent: CSSBox, elem: Element) =
-  if elem.cssvalues_before != nil:
-    let box = state.processComputedValueBox(parent, elem.cssvalues_before)
+  if elem.pseudo[PSEUDO_BEFORE] != nil:
+    let box = state.processComputedValueBox(parent, elem.pseudo[PSEUDO_BEFORE])
     if box == nil: return
     box.node = elem
 
-    let text = elem.cssvalues_before{"content"}
+    let text = elem.pseudo[PSEUDO_BEFORE]{"content"}
     var inline = state.processInlineBox(box, $text)
     if inline != nil:
       inline.node = elem
@@ -442,12 +442,12 @@ proc processBeforePseudoElem(state: var LayoutState, parent: CSSBox, elem: Eleme
     state.add(parent, box)
 
 proc processAfterPseudoElem(state: var LayoutState, parent: CSSBox, elem: Element) =
-  if elem.cssvalues_after != nil:
-    let box = state.processComputedValueBox(parent, elem.cssvalues_after)
+  if elem.pseudo[PSEUDO_AFTER] != nil:
+    let box = state.processComputedValueBox(parent, elem.pseudo[PSEUDO_AFTER])
     if box == nil: return
     box.node = elem
 
-    let text = elem.cssvalues_after{"content"}
+    let text = elem.pseudo[PSEUDO_AFTER]{"content"}
     var inline = state.processInlineBox(box, $text)
     if inline != nil:
       inline.node = elem
@@ -456,12 +456,12 @@ proc processAfterPseudoElem(state: var LayoutState, parent: CSSBox, elem: Elemen
     state.add(parent, box)
 
 proc processMarker(state: var LayoutState, parent: CSSBox, elem: Element) =
-  if elem.cssvalues{"display"} == DISPLAY_LIST_ITEM:
+  if elem.css{"display"} == DISPLAY_LIST_ITEM:
     var ordinalvalue = 1
     if elem.tagType == TAG_LI:
       ordinalvalue = HTMLLIElement(elem).ordinalvalue
 
-    let text = elem.cssvalues{"list-style-type"}.listMarker(ordinalvalue)
+    let text = elem.css{"list-style-type"}.listMarker(ordinalvalue)
     let tlen = text.width()
     parent.icontext.fromx -= tlen
     let marker = state.processInlineBox(parent, text)