about summary refs log tree commit diff stats
path: root/src/css
diff options
context:
space:
mode:
Diffstat (limited to 'src/css')
-rw-r--r--src/css/style.nim183
-rw-r--r--src/css/values.nim148
2 files changed, 144 insertions, 187 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)