about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorbptato <nincsnevem662@gmail.com>2022-07-30 11:45:32 +0200
committerbptato <nincsnevem662@gmail.com>2022-07-30 11:45:32 +0200
commitca81bbcecd3777e8b20e12332ef813b874dd7fa5 (patch)
treedf1f6e6e7b9ff3822d26063a36bdda1eeff9f04a
parent0dfe2a310db3fb7492ed448235e0757042f0f5ca (diff)
downloadchawan-ca81bbcecd3777e8b20e12332ef813b874dd7fa5.tar.gz
Implement revert value
-rw-r--r--src/css/cascade.nim79
-rw-r--r--src/css/stylednode.nim13
-rw-r--r--src/css/values.nim333
-rw-r--r--src/types/color.nim156
4 files changed, 327 insertions, 254 deletions
diff --git a/src/css/cascade.nim b/src/css/cascade.nim
index 9d2e9d53..c9acfe4c 100644
--- a/src/css/cascade.nim
+++ b/src/css/cascade.nim
@@ -13,14 +13,8 @@ import html/dom
 import html/tags
 
 type
-  ApplyResult = object
-    normal: seq[CSSDeclaration]
-    important: seq[CSSDeclaration]
   DeclarationList* = array[PseudoElem, seq[CSSDeclaration]]
 
-proc applyProperty(styledNode: StyledNode, parent: CSSComputedValues, d: CSSDeclaration) =
-  styledNode.computed.applyValue(parent, d)
-
 func applies(mq: MediaQuery): bool =
   case mq.t
   of CONDITION_MEDIA:
@@ -74,69 +68,34 @@ func calcRules(styledNode: StyledNode, sheet: CSSStylesheet): DeclarationList =
       for item in tosorts[i]:
         for dl in item[1]:
           dl
-
  
-proc applyNormal(ares: var ApplyResult, decls: seq[CSSDeclaration]) =
-  for decl in decls:
-    if not decl.important:
-      ares.normal.add(decl)
-
-proc applyImportant(ares: var ApplyResult, decls: seq[CSSDeclaration]) =
-  for decl in decls:
-    if decl.important:
-      ares.important.add(decl)
-
 proc applyDeclarations(styledNode: StyledNode, parent: CSSComputedValues, ua, user: DeclarationList, author: seq[DeclarationList]) =
   let pseudo = PSEUDO_NONE
-  var ares: ApplyResult
+  var builder = newComputedValueBuilder(parent)
 
-  ares.applyNormal(ua[pseudo])
-  ares.applyNormal(user[pseudo])
+  builder.addValues(ua[pseudo], ORIGIN_USER_AGENT)
+  builder.addValues(user[pseudo], ORIGIN_USER)
   for rule in author:
-    ares.applyNormal(rule[pseudo])
-
-  for rule in author:
-    ares.applyImportant(rule[pseudo])
-
+    builder.addValues(rule[pseudo], ORIGIN_AUTHOR)
   if styledNode.node != nil:
     let style = Element(styledNode.node).attr("style")
     if style.len > 0:
       let inline_rules = newStringStream(style).parseListOfDeclarations2()
-      ares.applyNormal(inline_rules)
-      ares.applyImportant(inline_rules)
-
-  ares.applyImportant(user[pseudo])
-  ares.applyImportant(ua[pseudo])
+      builder.addValues(inline_rules, ORIGIN_AUTHOR)
 
-  styledNode.computed = parent.inheritProperties()
-  for rule in ares.normal:
-    styledNode.applyProperty(parent, rule)
-
-  for rule in ares.important:
-    styledNode.applyProperty(parent, rule)
+  styledNode.computed = builder.buildComputedValues()
 
 # Either returns a new styled node or nil.
-proc applyDeclarations(pseudo: PseudoElem, parent: CSSComputedValues, ua, user: DeclarationList, author: seq[DeclarationList]): StyledNode =
-  var ares: ApplyResult
-
-  ares.applyNormal(ua[pseudo])
-  ares.applyNormal(user[pseudo])
-  for rule in author:
-    ares.applyNormal(rule[pseudo])
+proc applyDeclarations(pseudo: PseudoElem, styledParent: StyledNode, ua, user: DeclarationList, author: seq[DeclarationList]): StyledNode =
+  var builder = newComputedValueBuilder(styledParent.computed)
 
+  builder.addValues(ua[pseudo], ORIGIN_USER_AGENT)
+  builder.addValues(user[pseudo], ORIGIN_USER)
   for rule in author:
-    ares.applyImportant(rule[pseudo])
-
-  ares.applyImportant(user[pseudo])
-  ares.applyImportant(ua[pseudo])
+    builder.addValues(rule[pseudo], ORIGIN_AUTHOR)
 
-  if ares.normal.len > 0 or ares.important.len > 0:
-    result = StyledNode(t: STYLED_ELEMENT, node: nil, computed: parent.inheritProperties(), pseudo: pseudo)
-    for rule in ares.normal:
-      result.applyProperty(parent, rule)
-
-    for rule in ares.important:
-      result.applyProperty(parent, rule)
+  if builder.hasValues():
+    result = styledParent.newStyledElement(pseudo, builder.buildComputedValues())
 
 func applyMediaQuery(ss: CSSStylesheet): CSSStylesheet =
   result = ss
@@ -193,6 +152,7 @@ proc applyRules(document: Document, ua, user: CSSStylesheet, cachedTree: StyledN
       else:
         # Text
         styledChild = styledParent.newStyledText(cachedChild.text)
+        styledChild.pseudo = cachedChild.pseudo
         styledChild.node = cachedChild.node
       if styledParent == nil:
         # Root element
@@ -205,7 +165,7 @@ proc applyRules(document: Document, ua, user: CSSStylesheet, cachedTree: StyledN
         let (ua, user, authordecls) = styledParent.calcRules(ua, user, author)
         case pseudo
         of PSEUDO_BEFORE, PSEUDO_AFTER:
-          let styledPseudo = pseudo.applyDeclarations(styledParent.computed, ua, user, authordecls)
+          let styledPseudo = pseudo.applyDeclarations(styledParent, ua, user, authordecls)
           if styledPseudo != nil:
             styledParent.children.add(styledPseudo)
             let content = styledPseudo.computed{"content"}
@@ -214,17 +174,18 @@ proc applyRules(document: Document, ua, user: CSSStylesheet, cachedTree: StyledN
         of PSEUDO_INPUT_TEXT:
           let content = HTMLInputElement(styledParent.node).inputString()
           if content.len > 0:
-            styledChild = styledParent.newStyledText(content)
-            styledParent.children.add(styledChild)
+            let styledText = styledParent.newStyledText(content)
+            styledText.pseudo = pseudo
+            styledParent.children.add(styledText)
         of PSEUDO_NONE: discard
       else:
         assert child != nil
         if styledParent != nil:
           if child.nodeType == ELEMENT_NODE:
             styledChild = styledParent.newStyledElement(Element(child))
+            styledParent.children.add(styledChild)
             let (ua, user, authordecls) = styledChild.calcRules(ua, user, author)
             applyStyle(styledParent, styledChild, ua, user, authordecls)
-            styledParent.children.add(styledChild)
           elif child.nodeType == TEXT_NODE:
             let text = Text(child)
             styledChild = styledParent.newStyledText(text)
@@ -253,7 +214,7 @@ proc applyRules(document: Document, ua, user: CSSStylesheet, cachedTree: StyledN
         if cachedChild != nil:
           var cached: StyledNode
           for it in cachedChild.children:
-            if it.t == STYLED_ELEMENT and it.pseudo == ps:
+            if it.pseudo == ps:
               cached = it
               break
           # When calculating pseudo-element rules, their dependencies are added
diff --git a/src/css/stylednode.nim b/src/css/stylednode.nim
index f575d99a..52a13771 100644
--- a/src/css/stylednode.nim
+++ b/src/css/stylednode.nim
@@ -41,23 +41,21 @@ type
   DependencyType* = enum
     DEPEND_HOVER, DEPEND_CHECKED
 
-  InvalidationRegistry* = set[DependencyType]
-
   DependencyInfo* = object
     # All nodes we depend on, for each dependency type d.
     nodes*: array[DependencyType, seq[StyledNode]]
-    # Previous value. Node is marked invalid when one of these no longer
-    # matches the DOM value.
+    # Previous value. The owner StyledNode is marked as invalid when one of
+    # these no longer matches the DOM value.
     prev: array[DependencyType, bool]
 
   StyledNode* = ref object
     parent*: StyledNode
     node*: Node
+    pseudo*: PseudoElem #TODO this should be in element only
     case t*: StyledType
     of STYLED_TEXT:
       text*: string
     of STYLED_ELEMENT:
-      pseudo*: PseudoElem
       computed*: CSSComputedValues
       children*: seq[StyledNode]
       depends*: DependencyInfo
@@ -121,6 +119,11 @@ func newStyledElement*(element: Element): StyledNode =
 func newStyledElement*(parent: StyledNode, pseudo: PseudoElem, computed: CSSComputedValues, reg: sink DependencyInfo): StyledNode =
   result = StyledNode(t: STYLED_ELEMENT, computed: computed, pseudo: pseudo, parent: parent)
   result.depends = reg
+  result.parent = parent
+
+func newStyledElement*(parent: StyledNode, pseudo: PseudoElem, computed: CSSComputedValues): StyledNode =
+  result = StyledNode(t: STYLED_ELEMENT, computed: computed, pseudo: pseudo, parent: parent)
+  result.parent = parent
 
 func newStyledText*(parent: StyledNode, text: string): StyledNode =
   result = StyledNode(t: STYLED_TEXT, text: text, parent: parent)
diff --git a/src/css/values.nim b/src/css/values.nim
index 3fd8e38b..fa7bac8a 100644
--- a/src/css/values.nim
+++ b/src/css/values.nim
@@ -1,7 +1,4 @@
 import tables
-import sugar
-import sequtils
-import options
 import macros
 import strutils
 
@@ -130,6 +127,22 @@ type
 
   CSSComputedValues* = ref array[CSSPropertyType, CSSComputedValue]
 
+  CSSOrigin* = enum
+    ORIGIN_USER_AGENT
+    ORIGIN_USER
+    ORIGIN_AUTHOR
+
+  CSSComputedValueBuilder = object
+    global: CSSGlobalValueType
+    val: CSSComputedValue
+
+  CSSComputedValueBuilders = seq[CSSComputedValueBuilder]
+
+  CSSComputedValuesBuilder* = object
+    parent: CSSComputedValues
+    normalProperties: array[CSSOrigin, CSSComputedValueBuilders]
+    importantProperties: array[CSSOrigin, CSSComputedValueBuilders]
+
   CSSValueError* = object of ValueError
 
 const PropertyNames = {
@@ -277,157 +290,6 @@ func listMarker*(t: CSSListStyleType, i: int): string =
   of LIST_STYLE_TYPE_LOWER_ROMAN: return romanNumber_lower(i) & ". "
   of LIST_STYLE_TYPE_JAPANESE_INFORMAL: return japaneseNumber(i) & "、"
 
-const ColorsRGB = {
-  "aliceblue": 0xf0f8ff,
-  "antiquewhite": 0xfaebd7,
-  "aqua": 0x00ffff,
-  "aquamarine": 0x7fffd4,
-  "azure": 0xf0ffff,
-  "beige": 0xf5f5dc,
-  "bisque": 0xffe4c4,
-  "black": 0x000000,
-  "blanchedalmond": 0xffebcd,
-  "blue": 0x0000ff,
-  "blueviolet": 0x8a2be2,
-  "brown": 0xa52a2a,
-  "burlywood": 0xdeb887,
-  "cadetblue": 0x5f9ea0,
-  "chartreuse": 0x7fff00,
-  "chocolate": 0xd2691e,
-  "coral": 0xff7f50,
-  "cornflowerblue": 0x6495ed,
-  "cornsilk": 0xfff8dc,
-  "crimson": 0xdc143c,
-  "cyan": 0x00ffff,
-  "darkblue": 0x00008b,
-  "darkcyan": 0x008b8b,
-  "darkgoldenrod": 0xb8860b,
-  "darkgray": 0xa9a9a9,
-  "darkgreen": 0x006400,
-  "darkgrey": 0xa9a9a9,
-  "darkkhaki": 0xbdb76b,
-  "darkmagenta": 0x8b008b,
-  "darkolivegreen": 0x556b2f,
-  "darkorange": 0xff8c00,
-  "darkorchid": 0x9932cc,
-  "darkred": 0x8b0000,
-  "darksalmon": 0xe9967a,
-  "darkseagreen": 0x8fbc8f,
-  "darkslateblue": 0x483d8b,
-  "darkslategray": 0x2f4f4f,
-  "darkslategrey": 0x2f4f4f,
-  "darkturquoise": 0x00ced1,
-  "darkviolet": 0x9400d3,
-  "deeppink": 0xff1493,
-  "deepskyblue": 0x00bfff,
-  "dimgray": 0x696969,
-  "dimgrey": 0x696969,
-  "dodgerblue": 0x1e90ff,
-  "firebrick": 0xb22222,
-  "floralwhite": 0xfffaf0,
-  "forestgreen": 0x228b22,
-  "fuchsia": 0xff00ff,
-  "gainsboro": 0xdcdcdc,
-  "ghostwhite": 0xf8f8ff,
-  "gold": 0xffd700,
-  "goldenrod": 0xdaa520,
-  "gray": 0x808080,
-  "green": 0x008000,
-  "greenyellow": 0xadff2f,
-  "grey": 0x808080,
-  "honeydew": 0xf0fff0,
-  "hotpink": 0xff69b4,
-  "indianred": 0xcd5c5c,
-  "indigo": 0x4b0082,
-  "ivory": 0xfffff0,
-  "khaki": 0xf0e68c,
-  "lavender": 0xe6e6fa,
-  "lavenderblush": 0xfff0f5,
-  "lawngreen": 0x7cfc00,
-  "lemonchiffon": 0xfffacd,
-  "lightblue": 0xadd8e6,
-  "lightcoral": 0xf08080,
-  "lightcyan": 0xe0ffff,
-  "lightgoldenrodyellow": 0xfafad2,
-  "lightgray": 0xd3d3d3,
-  "lightgreen": 0x90ee90,
-  "lightgrey": 0xd3d3d3,
-  "lightpink": 0xffb6c1,
-  "lightsalmon": 0xffa07a,
-  "lightseagreen": 0x20b2aa,
-  "lightskyblue": 0x87cefa,
-  "lightslategray": 0x778899,
-  "lightslategrey": 0x778899,
-  "lightsteelblue": 0xb0c4de,
-  "lightyellow": 0xffffe0,
-  "lime": 0x00ff00,
-  "limegreen": 0x32cd32,
-  "linen": 0xfaf0e6,
-  "magenta": 0xff00ff,
-  "maroon": 0x800000,
-  "mediumaquamarine": 0x66cdaa,
-  "mediumblue": 0x0000cd,
-  "mediumorchid": 0xba55d3,
-  "mediumpurple": 0x9370db,
-  "mediumseagreen": 0x3cb371,
-  "mediumslateblue": 0x7b68ee,
-  "mediumspringgreen": 0x00fa9a,
-  "mediumturquoise": 0x48d1cc,
-  "mediumvioletred": 0xc71585,
-  "midnightblue": 0x191970,
-  "mintcream": 0xf5fffa,
-  "mistyrose": 0xffe4e1,
-  "moccasin": 0xffe4b5,
-  "navajowhite": 0xffdead,
-  "navy": 0x000080,
-  "oldlace": 0xfdf5e6,
-  "olive": 0x808000,
-  "olivedrab": 0x6b8e23,
-  "orange": 0xffa500,
-  "orangered": 0xff4500,
-  "orchid": 0xda70d6,
-  "palegoldenrod": 0xeee8aa,
-  "palegreen": 0x98fb98,
-  "paleturquoise": 0xafeeee,
-  "palevioletred": 0xdb7093,
-  "papayawhip": 0xffefd5,
-  "peachpuff": 0xffdab9,
-  "peru": 0xcd853f,
-  "pink": 0xffc0cb,
-  "plum": 0xdda0dd,
-  "powderblue": 0xb0e0e6,
-  "purple": 0x800080,
-  "red": 0xff0000,
-  "rosybrown": 0xbc8f8f,
-  "royalblue": 0x4169e1,
-  "saddlebrown": 0x8b4513,
-  "salmon": 0xfa8072,
-  "sandybrown": 0xf4a460,
-  "seagreen": 0x2e8b57,
-  "seashell": 0xfff5ee,
-  "sienna": 0xa0522d,
-  "silver": 0xc0c0c0,
-  "skyblue": 0x87ceeb,
-  "slateblue": 0x6a5acd,
-  "slategray": 0x708090,
-  "slategrey": 0x708090,
-  "snow": 0xfffafa,
-  "springgreen": 0x00ff7f,
-  "steelblue": 0x4682b4,
-  "tan": 0xd2b48c,
-  "teal": 0x008080,
-  "thistle": 0xd8bfd8,
-  "tomato": 0xff6347,
-  "turquoise": 0x40e0d0,
-  "violet": 0xee82ee,
-  "wheat": 0xf5deb3,
-  "white": 0xffffff,
-  "whitesmoke": 0xf5f5f5,
-  "yellow": 0xffff00,
-  "yellowgreen": 0x9acd32,
-  "rebeccapurple": 0x663399,
-}.map((a) => (a[0], RGBColor(a[1]))).toTable()
-
 const Colors: Table[string, CSSColor] = ((func (): Table[string, CSSColor] =
   for name, rgb in ColorsRGB:
     result[name] = CSSColor(rgba: rgb)
@@ -800,11 +662,10 @@ func getInitialTable(): array[CSSPropertyType, CSSComputedValue] =
 
 let defaultTable = getInitialTable()
 
-func getDefault(t: CSSPropertyType): CSSComputedValue = {.cast(noSideEffect).}:
-  assert defaultTable[t] != nil
-  return defaultTable[t]
+template getDefault(t: CSSPropertyType): CSSComputedValue = {.cast(noSideEffect).}:
+  defaultTable[t]
 
-func getComputedValue(d: CSSDeclaration, parent: CSSComputedValues): tuple[a:CSSComputedValue,b:CSSGlobalValueType] =
+func getComputedValue(d: CSSDeclaration): (CSSComputedValue, CSSGlobalValueType) =
   let name = d.name
   let ptype = propertyType(name)
   let vtype = valueType(ptype)
@@ -839,60 +700,152 @@ func equals*(a, b: CSSComputedValue): bool =
   of VALUE_NONE: return true
   return false
 
-proc applyValue(vals, parent: CSSComputedValues, t: CSSPropertyType, val: CSSComputedValue, global: CSSGlobalValueType) =
+proc newComputedValueBuilder*(parent: CSSComputedValues): CSSComputedValuesBuilder =
+  result.parent = parent
+
+proc addValuesImportant*(builder: var CSSComputedValuesBuilder, decls: seq[CSSDeclaration], origin: CSSOrigin) =
+  for decl in decls:
+    if decl.important:
+      let (val, global) = getComputedValue(decl)
+      builder.importantProperties[origin].add(CSSComputedValueBuilder(val: val, global: global))
+
+proc addValuesNormal*(builder: var CSSComputedValuesBuilder, decls: seq[CSSDeclaration], origin: CSSOrigin) =
+  for decl in decls:
+    if not decl.important:
+      let (val, global) = getComputedValue(decl)
+      builder.normalProperties[origin].add(CSSComputedValueBuilder(val: val, global: global))
+
+proc addValues*(builder: var CSSComputedValuesBuilder, decls: seq[CSSDeclaration], origin: CSSOrigin) =
+  for decl in decls:
+    let (val, global) = getComputedValue(decl)
+    if decl.important:
+      builder.importantProperties[origin].add(CSSComputedValueBuilder(val: val, global: global))
+    else:
+      builder.normalProperties[origin].add(CSSComputedValueBuilder(val: val, global: global))
+
+proc applyValue(vals: CSSComputedValues, prop: CSSPropertyType, val: CSSComputedValue, global: CSSGlobalValueType, parent: CSSComputedValues, previousOrigin: CSSComputedValues) =
+  let parentVal = if parent != nil:
+    parent[prop]
+  else:
+    nil
   case global
-  of VALUE_INHERIT, VALUE_UNSET:
-    if inherited(t):
-      if parent[t] != nil:
-        vals[t] = parent[t]
-    vals[t] = getDefault(t)
+  of VALUE_INHERIT:
+    if parentVal != nil:
+      vals[prop] = parentVal
+    else:
+      vals[prop] = getDefault(prop)
   of VALUE_INITIAL:
-    vals[t] = getDefault(t)
+    vals[prop] = getDefault(prop)
+  of VALUE_UNSET:
+    if inherited(prop):
+      # inherit
+      if parentVal != nil:
+        vals[prop] = parentVal
+      else:
+        vals[prop] = getDefault(prop)
+    else:
+      # initial
+      vals[prop] = getDefault(prop)
   of VALUE_REVERT:
-    vals[t] = getDefault(t) #TODO
+    if previousOrigin != nil:
+      vals[prop] = previousOrigin[prop]
+    else:
+      vals[prop] = getDefault(prop)
   of VALUE_NOGLOBAL:
-    vals[t] = val
+    vals[prop] = val
 
-proc applyValue*(vals, parent: CSSComputedValues, d: CSSDeclaration) =
-  let vv = getComputedValue(d, parent)
-  let val = vv.a
+func inheritProperties*(parent: CSSComputedValues): CSSComputedValues =
+  new(result)
+  for prop in CSSPropertyType:
+    if inherited(prop) and parent[prop] != nil:
+      result[prop] = parent[prop]
+    else:
+      result[prop] = getDefault(prop)
+
+func copyProperties*(parent: CSSComputedValues): CSSComputedValues =
+  new(result)
+  for prop in CSSPropertyType:
+    result[prop] = parent[prop]
+
+func rootProperties*(): CSSComputedValues =
+  new(result)
+  for prop in CSSPropertyType:
+    result[prop] = getDefault(prop)
+
+proc buildComputedValue(vals, parent, previousOrigin: CSSComputedValues, build: CSSComputedValueBuilder) =
+  let global = build.global
+  let val = build.val
   case val.t
   of PROPERTY_ALL:
-    let global = cssGlobal(d)
     if global != VALUE_NOGLOBAL:
       for t in CSSPropertyType:
-        vals.applyValue(parent, t, nil, global)
+        vals.applyValue(t, nil, global, parent, previousOrigin)
   of PROPERTY_MARGIN:
     let left = CSSComputedValue(t: PROPERTY_MARGIN_LEFT, v: VALUE_LENGTH, length: val.length)
     let right = CSSComputedValue(t: PROPERTY_MARGIN_RIGHT, v: VALUE_LENGTH, length: val.length)
     let top = CSSComputedValue(t: PROPERTY_MARGIN_TOP, v: VALUE_LENGTH, length: val.length)
     let bottom = CSSComputedValue(t: PROPERTY_MARGIN_BOTTOM, v: VALUE_LENGTH, length: val.length)
     for val in [left, right, top, bottom]:
-      vals.applyValue(parent, val.t, val, vv.b)
+      vals.applyValue(val.t, val, global, parent, previousOrigin)
   of PROPERTY_PADDING:
     let left = CSSComputedValue(t: PROPERTY_PADDING_LEFT, v: VALUE_LENGTH, length: val.length)
     let right = CSSComputedValue(t: PROPERTY_PADDING_RIGHT, v: VALUE_LENGTH, length: val.length)
     let top = CSSComputedValue(t: PROPERTY_PADDING_TOP, v: VALUE_LENGTH, length: val.length)
     let bottom = CSSComputedValue(t: PROPERTY_PADDING_BOTTOM, v: VALUE_LENGTH, length: val.length)
     for val in [left, right, top, bottom]:
-      vals.applyValue(parent, val.t, val, vv.b)
+      vals.applyValue(val.t, val, global, parent, previousOrigin)
   else:
-    vals.applyValue(parent, val.t, vv.a, vv.b)
-
-func inheritProperties*(parent: CSSComputedValues): CSSComputedValues =
-  new(result)
-  for prop in CSSPropertyType:
-    if inherited(prop) and parent[prop] != nil:
-      result[prop] = parent[prop]
-    else:
-      result[prop] = getDefault(prop)
-
-func copyProperties*(parent: CSSComputedValues): CSSComputedValues =
-  new(result)
-  for prop in CSSPropertyType:
-    result[prop] = parent[prop]
+    vals.applyValue(val.t, val, global, parent, previousOrigin)
+
+func hasValues*(builder: CSSComputedValuesBuilder): bool =
+  for origin in CSSOrigin:
+    if builder.normalProperties[origin].len > 0:
+      return true
+    if builder.importantProperties[origin].len > 0:
+      return true
+  return false
 
-func rootProperties*(): CSSComputedValues =
+func buildComputedValues*(builder: CSSComputedValuesBuilder): CSSComputedValues =
   new(result)
+  var previousOrigins: array[CSSOrigin, CSSComputedValues]
+  block:
+    let origin = ORIGIN_USER_AGENT
+    for build in builder.normalProperties[origin]:
+      result.buildComputedValue(builder.parent, nil, build)
+    previousOrigins[origin] = result.copyProperties()
+  block:
+    let origin = ORIGIN_USER
+    let prevOrigin = ORIGIN_USER_AGENT
+    for build in builder.normalProperties[origin]:
+      result.buildComputedValue(builder.parent, previousOrigins[prevOrigin], build)
+    previousOrigins[origin] = result.copyProperties() # save user origins so author can use them
+  block:
+    let origin = ORIGIN_AUTHOR
+    let prevOrigin = ORIGIN_USER
+    for build in builder.normalProperties[origin]:
+      result.buildComputedValue(builder.parent, previousOrigins[prevOrigin], build)
+    # no need to save user origins
+  block:
+    let origin = ORIGIN_AUTHOR
+    let prevOrigin = ORIGIN_USER
+    for build in builder.importantProperties[origin]:
+      result.buildComputedValue(builder.parent, previousOrigins[prevOrigin], build)
+    # important, so no need to save origins
+  block:
+    let origin = ORIGIN_USER
+    let prevOrigin = ORIGIN_USER_AGENT
+    for build in builder.importantProperties[origin]:
+      result.buildComputedValue(builder.parent, previousOrigins[prevOrigin], build)
+    # important, so no need to save origins
+  block:
+    let origin = ORIGIN_USER_AGENT
+    for build in builder.importantProperties[origin]:
+      result.buildComputedValue(builder.parent, nil, build)
+    # important, so no need to save origins
+  # set defaults
   for prop in CSSPropertyType:
-    result[prop] = getDefault(prop)
+    if result[prop] == nil:
+      if inherited(prop) and builder.parent != nil and builder.parent[prop] != nil:
+        result[prop] = builder.parent[prop]
+      else:
+        result[prop] = getDefault(prop)
diff --git a/src/types/color.nim b/src/types/color.nim
index cb3b2f3f..e5f72158 100644
--- a/src/types/color.nim
+++ b/src/types/color.nim
@@ -1,3 +1,7 @@
+import sequtils
+import sugar
+import tables
+
 type
   RGBColor* = distinct uint32
 
@@ -19,6 +23,158 @@ func `==`*(color1, color2: CellColor): bool =
 
 const defaultColor* = CellColor(rgb: false, color: 0)
 
+const ColorsRGB* = {
+  "aliceblue": 0xf0f8ff,
+  "antiquewhite": 0xfaebd7,
+  "aqua": 0x00ffff,
+  "aquamarine": 0x7fffd4,
+  "azure": 0xf0ffff,
+  "beige": 0xf5f5dc,
+  "bisque": 0xffe4c4,
+  "black": 0x000000,
+  "blanchedalmond": 0xffebcd,
+  "blue": 0x0000ff,
+  "blueviolet": 0x8a2be2,
+  "brown": 0xa52a2a,
+  "burlywood": 0xdeb887,
+  "cadetblue": 0x5f9ea0,
+  "chartreuse": 0x7fff00,
+  "chocolate": 0xd2691e,
+  "coral": 0xff7f50,
+  "cornflowerblue": 0x6495ed,
+  "cornsilk": 0xfff8dc,
+  "crimson": 0xdc143c,
+  "cyan": 0x00ffff,
+  "darkblue": 0x00008b,
+  "darkcyan": 0x008b8b,
+  "darkgoldenrod": 0xb8860b,
+  "darkgray": 0xa9a9a9,
+  "darkgreen": 0x006400,
+  "darkgrey": 0xa9a9a9,
+  "darkkhaki": 0xbdb76b,
+  "darkmagenta": 0x8b008b,
+  "darkolivegreen": 0x556b2f,
+  "darkorange": 0xff8c00,
+  "darkorchid": 0x9932cc,
+  "darkred": 0x8b0000,
+  "darksalmon": 0xe9967a,
+  "darkseagreen": 0x8fbc8f,
+  "darkslateblue": 0x483d8b,
+  "darkslategray": 0x2f4f4f,
+  "darkslategrey": 0x2f4f4f,
+  "darkturquoise": 0x00ced1,
+  "darkviolet": 0x9400d3,
+  "deeppink": 0xff1493,
+  "deepskyblue": 0x00bfff,
+  "dimgray": 0x696969,
+  "dimgrey": 0x696969,
+  "dodgerblue": 0x1e90ff,
+  "firebrick": 0xb22222,
+  "floralwhite": 0xfffaf0,
+  "forestgreen": 0x228b22,
+  "fuchsia": 0xff00ff,
+  "gainsboro": 0xdcdcdc,
+  "ghostwhite": 0xf8f8ff,
+  "gold": 0xffd700,
+  "goldenrod": 0xdaa520,
+  "gray": 0x808080,
+  "green": 0x008000,
+  "greenyellow": 0xadff2f,
+  "grey": 0x808080,
+  "honeydew": 0xf0fff0,
+  "hotpink": 0xff69b4,
+  "indianred": 0xcd5c5c,
+  "indigo": 0x4b0082,
+  "ivory": 0xfffff0,
+  "khaki": 0xf0e68c,
+  "lavender": 0xe6e6fa,
+  "lavenderblush": 0xfff0f5,
+  "lawngreen": 0x7cfc00,
+  "lemonchiffon": 0xfffacd,
+  "lightblue": 0xadd8e6,
+  "lightcoral": 0xf08080,
+  "lightcyan": 0xe0ffff,
+  "lightgoldenrodyellow": 0xfafad2,
+  "lightgray": 0xd3d3d3,
+  "lightgreen": 0x90ee90,
+  "lightgrey": 0xd3d3d3,
+  "lightpink": 0xffb6c1,
+  "lightsalmon": 0xffa07a,
+  "lightseagreen": 0x20b2aa,
+  "lightskyblue": 0x87cefa,
+  "lightslategray": 0x778899,
+  "lightslategrey": 0x778899,
+  "lightsteelblue": 0xb0c4de,
+  "lightyellow": 0xffffe0,
+  "lime": 0x00ff00,
+  "limegreen": 0x32cd32,
+  "linen": 0xfaf0e6,
+  "magenta": 0xff00ff,
+  "maroon": 0x800000,
+  "mediumaquamarine": 0x66cdaa,
+  "mediumblue": 0x0000cd,
+  "mediumorchid": 0xba55d3,
+  "mediumpurple": 0x9370db,
+  "mediumseagreen": 0x3cb371,
+  "mediumslateblue": 0x7b68ee,
+  "mediumspringgreen": 0x00fa9a,
+  "mediumturquoise": 0x48d1cc,
+  "mediumvioletred": 0xc71585,
+  "midnightblue": 0x191970,
+  "mintcream": 0xf5fffa,
+  "mistyrose": 0xffe4e1,
+  "moccasin": 0xffe4b5,
+  "navajowhite": 0xffdead,
+  "navy": 0x000080,
+  "oldlace": 0xfdf5e6,
+  "olive": 0x808000,
+  "olivedrab": 0x6b8e23,
+  "orange": 0xffa500,
+  "orangered": 0xff4500,
+  "orchid": 0xda70d6,
+  "palegoldenrod": 0xeee8aa,
+  "palegreen": 0x98fb98,
+  "paleturquoise": 0xafeeee,
+  "palevioletred": 0xdb7093,
+  "papayawhip": 0xffefd5,
+  "peachpuff": 0xffdab9,
+  "peru": 0xcd853f,
+  "pink": 0xffc0cb,
+  "plum": 0xdda0dd,
+  "powderblue": 0xb0e0e6,
+  "purple": 0x800080,
+  "red": 0xff0000,
+  "rosybrown": 0xbc8f8f,
+  "royalblue": 0x4169e1,
+  "saddlebrown": 0x8b4513,
+  "salmon": 0xfa8072,
+  "sandybrown": 0xf4a460,
+  "seagreen": 0x2e8b57,
+  "seashell": 0xfff5ee,
+  "sienna": 0xa0522d,
+  "silver": 0xc0c0c0,
+  "skyblue": 0x87ceeb,
+  "slateblue": 0x6a5acd,
+  "slategray": 0x708090,
+  "slategrey": 0x708090,
+  "snow": 0xfffafa,
+  "springgreen": 0x00ff7f,
+  "steelblue": 0x4682b4,
+  "tan": 0xd2b48c,
+  "teal": 0x008080,
+  "thistle": 0xd8bfd8,
+  "tomato": 0xff6347,
+  "turquoise": 0x40e0d0,
+  "violet": 0xee82ee,
+  "wheat": 0xf5deb3,
+  "white": 0xffffff,
+  "whitesmoke": 0xf5f5f5,
+  "yellow": 0xffff00,
+  "yellowgreen": 0x9acd32,
+  "rebeccapurple": 0x663399,
+}.map((a) => (a[0], RGBColor(a[1]))).toTable()
+
+
 func r*(c: RGBAColor): int =
   return int(c) shr 16 and 0xff