Class: CSS::Native::Cascade

Inherits:
Cascade
  • Object
show all
Defined in:
lib/css/native.rb

Overview

Subclass of CSS::Cascade that uses the native matcher for the inner rule-matching loop. Selectors are pre-compiled at construction —those that can’t be compiled (pseudo-classes etc.) fall through to the pure-Ruby matcher, so behavior is identical to CSS::Cascade.

Requires a Nokogiri document at construction; the snapshot is built once and reused for every resolve(). Mutate the DOM and you must construct a fresh CSS::Native::Cascade.

Constant Summary

Constants inherited from Cascade

Cascade::TRANSPARENT_AT_RULES

Instance Method Summary collapse

Constructor Details

#initialize(stylesheet, document, context: CSS::MediaQueries::Context.default) ⇒ Cascade

Returns a new instance of Cascade.



83
84
85
86
87
88
89
90
91
92
93
94
# File 'lib/css/native.rb', line 83

def initialize(stylesheet, document, context: CSS::MediaQueries::Context.default)
  super(stylesheet, context: context)

  @snapshot        = Snapshot.from_document(document)
  @compiled_by_ast = {}.compare_by_identity

  @entries.each do |entry|
    entry.selector_pairs.each {|ast, _spec|
      @compiled_by_ast[ast] = Native.compile_or_nil(ast)
    }
  end
end

Instance Method Details

#resolve(element, inline_style: nil, state: nil) ⇒ Object

Override: batch every candidate’s compiled selectors into one FFI hop per resolve (GVL released), then merge in any Ruby-fallback matches. Cuts per-resolve FFI cost from O(candidates) to O(1).



99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
# File 'lib/css/native.rb', line 99

def resolve(element, inline_style: nil, state: nil)
  cache        = {}
  candidates   = collect_candidate_indexes(element, cache)
  order        = 0
  matches      = []
  native_state = state && @snapshot.compile_state(state)

  best_by_entry = native_pass(element, candidates, native_state)
  ruby_fallback_pass(element, candidates, best_by_entry, cache, state)

  candidates.each do |entry_idx|
    spec = best_by_entry[entry_idx] or next

    @entries[entry_idx].declarations.each {|decl|
      order += 1
      matches << CSS::Cascade::Match.new(declaration: decl, specificity: spec, inline: false, order: order)
    }
  end

  if inline_style
    inline_declarations(inline_style).each {|decl|
      order += 1
      matches << CSS::Cascade::Match.new(
        declaration: decl,
        specificity: CSS::Selectors::Specificity::ZERO,
        inline:      true,
        order:       order
      )
    }
  end

  pick_winners(matches)
end