diff options
author | bptato <nincsnevem662@gmail.com> | 2024-06-02 18:54:55 +0200 |
---|---|---|
committer | bptato <nincsnevem662@gmail.com> | 2024-06-02 19:17:52 +0200 |
commit | 284df8430542a49c2affb8912e3fe9a077e9634e (patch) | |
tree | e51b88a8784dde60d1bea80877a5371a39a64c90 /src/css | |
parent | e1a03329f113a30c092d4f1dcd8d99f6ed4ec335 (diff) | |
download | chawan-284df8430542a49c2affb8912e3fe9a077e9634e.tar.gz |
css: slightly optimize cascade
Parse rule values in sheet addRule, not during cascade.
Diffstat (limited to 'src/css')
-rw-r--r-- | src/css/cascade.nim | 150 | ||||
-rw-r--r-- | src/css/cssparser.nim | 19 | ||||
-rw-r--r-- | src/css/cssvalues.nim | 76 | ||||
-rw-r--r-- | src/css/sheet.nim | 36 |
4 files changed, 145 insertions, 136 deletions
diff --git a/src/css/cascade.nim b/src/css/cascade.nim index cd80fb96..4fb69de8 100644 --- a/src/css/cascade.nim +++ b/src/css/cascade.nim @@ -20,12 +20,12 @@ import types/opt import chame/tags type - DeclarationList* = array[PseudoElem, seq[CSSDeclaration]] + RuleList* = array[PseudoElem, seq[CSSRuleDef]] - DeclarationListMap* = ref object - ua: DeclarationList # user agent - user: DeclarationList - author: seq[DeclarationList] + RuleListMap* = ref object + ua: RuleList # user agent + user: RuleList + author: seq[RuleList] func appliesLR(feature: MediaFeature; window: Window; n: LayoutUnit): bool = @@ -86,24 +86,24 @@ func applies*(mqlist: MediaQueryList; window: Window): bool = appliesFwdDecl = applies type - ToSorts = array[PseudoElem, seq[(int, seq[CSSDeclaration])]] + ToSorts = array[PseudoElem, seq[(int, CSSRuleDef)]] proc calcRule(tosorts: var ToSorts; styledNode: StyledNode; rule: CSSRuleDef) = for sel in rule.sels: if styledNode.selectorsMatch(sel): let spec = getSpecificity(sel) - tosorts[sel.pseudo].add((spec, rule.decls)) + tosorts[sel.pseudo].add((spec, rule)) -func calcRules(styledNode: StyledNode; sheet: CSSStylesheet): DeclarationList = +func calcRules(styledNode: StyledNode; sheet: CSSStylesheet): RuleList = var tosorts: ToSorts let elem = Element(styledNode.node) for rule in sheet.genRules(elem.localName, elem.id, elem.classList.toks): tosorts.calcRule(styledNode, rule) for i in PseudoElem: - tosorts[i].sort((proc(x, y: (int, seq[CSSDeclaration])): int = + tosorts[i].sort((proc(x, y: (int, CSSRuleDef)): int = cmp(x[0], y[0]) ), order = Ascending) - result[i] = newSeqOfCap[CSSDeclaration](tosorts[i].len) + result[i] = newSeqOfCap[CSSRuleDef](tosorts[i].len) for item in tosorts[i]: result[i].add(item[1]) @@ -257,58 +257,126 @@ func calcPresentationalHints(element: Element): CSSComputedValues = map_list_type_ul else: discard +type + CSSValueEntryObj = object + normal: seq[CSSComputedEntry] + important: seq[CSSComputedEntry] + + CSSValueEntryMap = array[CSSOrigin, CSSValueEntryObj] + +func buildComputedValues(rules: CSSValueEntryMap; presHints, parent: + CSSComputedValues): CSSComputedValues = + new(result) + var previousOrigins: array[CSSOrigin, CSSComputedValues] + for entry in rules[coUserAgent].normal: # user agent + result.applyValue(entry, parent, nil) + previousOrigins[coUserAgent] = result.copyProperties() + # Presentational hints override user agent style, but respect user/author + # style. + if presHints != nil: + for prop in CSSPropertyType: + if presHints[prop] != nil: + result[prop] = presHints[prop] + for entry in rules[coUser].normal: # user + result.applyValue(entry, parent, previousOrigins[coUserAgent]) + # save user origins so author can use them + previousOrigins[coUser] = result.copyProperties() + for entry in rules[coAuthor].normal: # author + result.applyValue(entry, parent, previousOrigins[coUser]) + # no need to save user origins + for entry in rules[coAuthor].important: # author important + result.applyValue(entry, parent, previousOrigins[coUser]) + # important, so no need to save origins + for entry in rules[coUser].important: # user important + result.applyValue(entry, parent, previousOrigins[coUserAgent]) + # important, so no need to save origins + for entry in rules[coUserAgent].important: # user agent important + result.applyValue(entry, parent, nil) + # important, so no need to save origins + # set defaults + for prop in CSSPropertyType: + if result[prop] == nil: + if prop.inherited and parent != nil and parent[prop] != nil: + result[prop] = parent[prop] + else: + result[prop] = getDefault(prop) + if result{"float"} != FloatNone: + #TODO it may be better to handle this in layout + let display = result{"display"}.blockify() + if display != result{"display"}: + result{"display"} = display + +proc add(map: var CSSValueEntryObj; rules: seq[CSSRuleDef]) = + for rule in rules: + map.normal.add(rule.normalVals) + map.important.add(rule.importantVals) + proc applyDeclarations(styledNode: StyledNode; parent: CSSComputedValues; - map: DeclarationListMap) = - let pseudo = peNone - var builder = CSSComputedValuesBuilder(parent: parent) - builder.addValues(map.ua[pseudo], coUserAgent) - builder.addValues(map.user[pseudo], coUser) + map: RuleListMap) = + var rules: CSSValueEntryMap + var presHints: CSSComputedValues = nil + rules[coUserAgent].add(map.ua[peNone]) + rules[coUser].add(map.user[peNone]) for rule in map.author: - builder.addValues(rule[pseudo], coAuthor) + rules[coAuthor].add(rule[peNone]) if styledNode.node != nil: let element = Element(styledNode.node) let style = element.style_cached if style != nil: - builder.addValues(style.decls, coAuthor) - builder.preshints = element.calcPresentationalHints() - styledNode.computed = builder.buildComputedValues() + for decl in style.decls: + let vals = parseComputedValues(decl.name, decl.value) + if decl.important: + rules[coAuthor].important.add(vals) + else: + rules[coAuthor].normal.add(vals) + presHints = element.calcPresentationalHints() + styledNode.computed = rules.buildComputedValues(presHints, parent) + +func hasValues(rules: CSSValueEntryMap): bool = + for origin in CSSOrigin: + if rules[origin].normal.len > 0 or rules[origin].important.len > 0: + return true + return false # Either returns a new styled node or nil. proc applyDeclarations(pseudo: PseudoElem; styledParent: StyledNode; - map: DeclarationListMap): StyledNode = - var builder = CSSComputedValuesBuilder(parent: styledParent.computed) - builder.addValues(map.ua[pseudo], coUserAgent) - builder.addValues(map.user[pseudo], coUser) + map: RuleListMap): StyledNode = + var rules: CSSValueEntryMap + rules[coUserAgent].add(map.ua[pseudo]) + rules[coUser].add(map.user[pseudo]) for rule in map.author: - builder.addValues(rule[pseudo], coAuthor) - if builder.hasValues(): - let cvals = builder.buildComputedValues() - result = styledParent.newStyledElement(pseudo, cvals) + rules[coAuthor].add(rule[pseudo]) + if rules.hasValues(): + let cvals = rules.buildComputedValues(nil, styledParent.computed) + return styledParent.newStyledElement(pseudo, cvals) + return nil func applyMediaQuery(ss: CSSStylesheet; window: Window): CSSStylesheet = - if ss == nil: return nil - new(result) - result[] = ss[] + if ss == nil: + return nil + var res = CSSStylesheet() + res[] = ss[] for mq in ss.mqList: if mq.query.applies(window): - result.add(mq.children.applyMediaQuery(window)) + res.add(mq.children.applyMediaQuery(window)) + return res func calcRules(styledNode: StyledNode; ua, user: CSSStylesheet; - author: seq[CSSStylesheet]): DeclarationListMap = + author: seq[CSSStylesheet]): RuleListMap = let uadecls = calcRules(styledNode, ua) - var userdecls: DeclarationList + var userdecls: RuleList if user != nil: userdecls = calcRules(styledNode, user) - var authordecls: seq[DeclarationList] + var authordecls: seq[RuleList] for rule in author: authordecls.add(calcRules(styledNode, rule)) - return DeclarationListMap( + return RuleListMap( ua: uadecls, user: userdecls, author: authordecls ) -proc applyStyle(parent, styledNode: StyledNode; map: DeclarationListMap) = +proc applyStyle(parent, styledNode: StyledNode; map: RuleListMap) = let parentComputed = if parent != nil: parent.computed else: @@ -320,7 +388,7 @@ type CascadeFrame = object child: Node pseudo: PseudoElem cachedChild: StyledNode - parentDeclMap: DeclarationListMap + parentDeclMap: RuleListMap proc getAuthorSheets(document: Document): seq[CSSStylesheet] = var author: seq[CSSStylesheet] @@ -350,7 +418,7 @@ proc applyRulesFrameValid(frame: CascadeFrame): StyledNode = return styledChild proc applyRulesFrameInvalid(frame: CascadeFrame; ua, user: CSSStylesheet; - author: seq[CSSStylesheet]; declmap: var DeclarationListMap): StyledNode = + author: seq[CSSStylesheet]; declmap: var RuleListMap): StyledNode = var styledChild: StyledNode let pseudo = frame.pseudo let styledParent = frame.styledParent @@ -460,7 +528,7 @@ proc stackAppend(styledStack: var seq[CascadeFrame]; frame: CascadeFrame; proc stackAppend(styledStack: var seq[CascadeFrame]; frame: CascadeFrame; styledParent: StyledNode; pseudo: PseudoElem; i: var int; - parentDeclMap: DeclarationListMap = nil) = + parentDeclMap: RuleListMap = nil) = if frame.cachedChild != nil: var cached: StyledNode let oldi = i @@ -494,7 +562,7 @@ proc stackAppend(styledStack: var seq[CascadeFrame]; frame: CascadeFrame; # Append children to styledChild. proc appendChildren(styledStack: var seq[CascadeFrame]; frame: CascadeFrame; - styledChild: StyledNode; parentDeclMap: DeclarationListMap) = + styledChild: StyledNode; parentDeclMap: RuleListMap) = # i points to the child currently being inspected. var idx = if frame.cachedChild != nil: frame.cachedChild.children.len - 1 @@ -532,7 +600,7 @@ proc applyRules(document: Document; ua, user: CSSStylesheet; var root: StyledNode while styledStack.len > 0: var frame = styledStack.pop() - var declmap: DeclarationListMap + var declmap: RuleListMap let styledParent = frame.styledParent let valid = frame.cachedChild != nil and frame.cachedChild.isValid() let styledChild = if valid: diff --git a/src/css/cssparser.nim b/src/css/cssparser.nim index 121175c9..0d71e6d0 100644 --- a/src/css/cssparser.nim +++ b/src/css/cssparser.nim @@ -253,9 +253,8 @@ proc consumeEscape(state: var CSSTokenizerState): string = return $c #NOTE this assumes the caller doesn't care about non-ascii proc consumeString(state: var CSSTokenizerState): CSSToken = - var s: string + var s = "" let ending = state.curr - while state.has(): let c = state.consume() case c @@ -276,32 +275,31 @@ proc consumeString(state: var CSSTokenizerState): CSSToken = return CSSToken(tokenType: cttString, value: s) proc consumeIdentSequence(state: var CSSTokenizerState): string = + var s = "" while state.has(): let c = state.consume() if state.isValidEscape(): - result &= state.consumeEscape() + s &= state.consumeEscape() elif c in Ident: - result &= c + s &= c else: state.reconsume() - return result + return s + return s proc consumeNumber(state: var CSSTokenizerState): (tflagb, float64) = var t = tflagbInteger - var repr: string + var repr = "" if state.has() and state.peek() in {'+', '-'}: repr &= state.consume() - while state.has() and state.peek() in AsciiDigit: repr &= state.consume() - if state.has(1) and state.peek() == '.' and state.peek(1) in AsciiDigit: repr &= state.consume() repr &= state.consume() t = tflagbNumber while state.has() and state.peek() in AsciiDigit: repr &= state.consume() - if state.has(1) and state.peek() in {'E', 'e'} and state.peek(1) in AsciiDigit or state.has(2) and state.peek() in {'E', 'e'} and @@ -315,8 +313,7 @@ proc consumeNumber(state: var CSSTokenizerState): (tflagb, float64) = t = tflagbNumber while state.has() and state.peek() in AsciiDigit: repr &= state.consume() - - let val = parseFloat64($repr) + let val = parseFloat64(repr) return (t, val) proc consumeNumericToken(state: var CSSTokenizerState): CSSToken = diff --git a/src/css/cssvalues.nim b/src/css/cssvalues.nim index 010634be..e3a09a39 100644 --- a/src/css/cssvalues.nim +++ b/src/css/cssvalues.nim @@ -404,14 +404,6 @@ type val: CSSComputedValue global: CSSGlobalType - CSSComputedEntries = seq[CSSComputedEntry] - - CSSComputedValuesBuilder* = object - parent*: CSSComputedValues - normalProperties: array[CSSOrigin, CSSComputedEntries] - importantProperties: array[CSSOrigin, CSSComputedEntries] - preshints*: CSSComputedValues - const ShorthandNames = block: var tab = initTable[string, CSSShorthandType]() for t in CSSShorthandType: @@ -575,7 +567,7 @@ macro `{}=`*(vals: CSSComputedValues; s: static string, val: typed) = `vs`: `val` ) -func inherited(t: CSSPropertyType): bool = +func inherited*(t: CSSPropertyType): bool = return t in InheritedProperties func em_to_px(em: float64; window: WindowAttributes): LayoutUnit = @@ -1350,7 +1342,7 @@ func getInitialTable(): array[CSSPropertyType, CSSComputedValue] = let defaultTable = getInitialTable() -template getDefault(t: CSSPropertyType): CSSComputedValue = +template getDefault*(t: CSSPropertyType): CSSComputedValue = {.cast(noSideEffect).}: defaultTable[t] @@ -1538,17 +1530,8 @@ proc parseComputedValues*(name: string; value: seq[CSSComponentValue]): return res return @[] -proc addValues*(builder: var CSSComputedValuesBuilder; - decls: seq[CSSDeclaration]; origin: CSSOrigin) = - for decl in decls: - let vals = parseComputedValues(decl.name, decl.value) - if decl.important: - builder.importantProperties[origin].add(vals) - else: - builder.normalProperties[origin].add(vals) - -proc applyValue(vals: CSSComputedValues; entry: CSSComputedEntry; - parent: CSSComputedValues; previousOrigin: CSSComputedValues) = +proc applyValue*(vals: CSSComputedValues; entry: CSSComputedEntry; + parent, previousOrigin: CSSComputedValues) = let parentVal = if parent != nil: parent[entry.t] else: @@ -1621,54 +1604,3 @@ func splitTable*(computed: CSSComputedValues): innerComputed[prop] = computed[prop] outerComputed{"display"} = computed{"display"} return (outerComputed, innerComputed) - -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 buildComputedValues*(builder: CSSComputedValuesBuilder): - CSSComputedValues = - new(result) - var previousOrigins: array[CSSOrigin, CSSComputedValues] - for entry in builder.normalProperties[coUserAgent]: # user agent - result.applyValue(entry, builder.parent, nil) - previousOrigins[coUserAgent] = result.copyProperties() - # Presentational hints override user agent style, but respect user/author - # style. - if builder.preshints != nil: - for prop in CSSPropertyType: - if builder.preshints[prop] != nil: - result[prop] = builder.preshints[prop] - for entry in builder.normalProperties[coUser]: # user - result.applyValue(entry, builder.parent, previousOrigins[coUserAgent]) - # save user origins so author can use them - previousOrigins[coUser] = result.copyProperties() - for entry in builder.normalProperties[coAuthor]: # author - result.applyValue(entry, builder.parent, previousOrigins[coUser]) - # no need to save user origins - for entry in builder.importantProperties[coAuthor]: # author important - result.applyValue(entry, builder.parent, previousOrigins[coUser]) - # important, so no need to save origins - for entry in builder.importantProperties[coUser]: # user important - result.applyValue(entry, builder.parent, previousOrigins[coUserAgent]) - # important, so no need to save origins - for entry in builder.importantProperties[coUserAgent]: # user agent important - result.applyValue(entry, builder.parent, nil) - # important, so no need to save origins - # set defaults - for prop in CSSPropertyType: - 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) - if result{"float"} != FloatNone: - #TODO it may be better to handle this in layout - let display = result{"display"}.blockify() - if display != result{"display"}: - result{"display"} = display diff --git a/src/css/sheet.nim b/src/css/sheet.nim index 11c5cb36..eb11854e 100644 --- a/src/css/sheet.nim +++ b/src/css/sheet.nim @@ -2,6 +2,7 @@ import std/algorithm import std/tables import css/cssparser +import css/cssvalues import css/mediaquery import css/selectorparser import html/catom @@ -11,7 +12,8 @@ type CSSRuleDef* = ref object of CSSRuleBase sels*: SelectorList - decls*: seq[CSSDeclaration] + normalVals*: seq[CSSComputedEntry] + importantVals*: seq[CSSComputedEntry] # Absolute position in the stylesheet; used for sorting rules after # retrieval from the cache. idx: int @@ -137,7 +139,7 @@ iterator genRules*(sheet: CSSStylesheet; tag, id: CAtom; classes: seq[CAtom]): for rule in rules: yield rule -proc add(sheet: var CSSStylesheet; rule: CSSRuleDef) = +proc add(sheet: CSSStylesheet; rule: CSSRuleDef) = var hashes: SelectorHashes for cxsel in rule.sels: hashes.getSelectorIds(cxsel) @@ -159,7 +161,7 @@ proc add(sheet: var CSSStylesheet; rule: CSSRuleDef) = else: sheet.generalList.add(rule) -proc add*(sheet: var CSSStylesheet; sheet2: CSSStylesheet) = +proc add*(sheet, sheet2: CSSStylesheet) = sheet.generalList.add(sheet2.generalList) for key, value in sheet2.tagTable.pairs: sheet.tagTable.withValue(key, p): @@ -177,18 +179,27 @@ proc add*(sheet: var CSSStylesheet; sheet2: CSSStylesheet) = do: sheet.classTable[key] = value -proc addRule(stylesheet: var CSSStylesheet; rule: CSSQualifiedRule) = +proc addRule(stylesheet: CSSStylesheet; rule: CSSQualifiedRule) = let sels = parseSelectors(rule.prelude, stylesheet.factory) if sels.len > 0: - let r = CSSRuleDef( + var normalVals: seq[CSSComputedEntry] = @[] + var importantVals: seq[CSSComputedEntry] = @[] + let decls = rule.oblock.value.parseDeclarations2() + for decl in decls: + let vals = parseComputedValues(decl.name, decl.value) + if decl.important: + importantVals.add(vals) + else: + normalVals.add(vals) + stylesheet.add(CSSRuleDef( sels: sels, - decls: rule.oblock.value.parseDeclarations2(), + normalVals: normalVals, + importantVals: importantVals, idx: stylesheet.len - ) - stylesheet.add(r) + )) inc stylesheet.len -proc addAtRule(stylesheet: var CSSStylesheet; atrule: CSSAtRule) = +proc addAtRule(stylesheet: CSSStylesheet; atrule: CSSAtRule) = case atrule.name of "media": if atrule.oblock == nil: @@ -212,9 +223,10 @@ proc addAtRule(stylesheet: var CSSStylesheet; atrule: CSSAtRule) = proc parseStylesheet*(ibuf: string; factory: CAtomFactory): CSSStylesheet = let raw = parseStylesheet(ibuf) - result = newStylesheet(raw.value.len, factory) + let sheet = newStylesheet(raw.value.len, factory) for v in raw.value: if v of CSSAtRule: - result.addAtRule(CSSAtRule(v)) + sheet.addAtRule(CSSAtRule(v)) else: - result.addRule(CSSQualifiedRule(v)) + sheet.addRule(CSSQualifiedRule(v)) + return sheet |