diff options
Diffstat (limited to 'src/css')
-rw-r--r-- | src/css/style.nim | 183 | ||||
-rw-r--r-- | src/css/values.nim | 148 |
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) |