Module: Dommy::Internal

Defined in:
lib/dommy/internal/idna.rb,
lib/dommy/internal/punycode.rb,
lib/dommy/internal/idna_data.rb,
lib/dommy/internal/cookie_jar.rb,
lib/dommy/internal/namespaces.rb,
lib/dommy/internal/url_parser.rb,
lib/dommy/internal/ipv4_parser.rb,
lib/dommy/internal/parent_node.rb,
lib/dommy/internal/dom_matching.rb,
lib/dommy/internal/node_equality.rb,
lib/dommy/internal/node_traversal.rb,
lib/dommy/internal/selector_parser.rb,
lib/dommy/internal/global_functions.rb,
lib/dommy/internal/observer_manager.rb,
lib/dommy/internal/observer_matcher.rb,
lib/dommy/internal/scope_resolution.rb,
lib/dommy/internal/node_wrapper_cache.rb,
lib/dommy/internal/css_pseudo_handlers.rb,
lib/dommy/internal/observable_callback.rb,
lib/dommy/internal/mutation_coordinator.rb,
lib/dommy/internal/reflected_attributes.rb,
lib/dommy/internal/shadow_root_registry.rb,
lib/dommy/internal/range_text_serializer.rb,
lib/dommy/internal/template_content_registry.rb

Defined Under Namespace

Modules: DomMatching, GlobalFunctions, IDNA, IDNAData, Ipv4Parser, Namespaces, NodeEquality, NodeTraversal, ObservableCallback, ParentNode, Punycode, ReflectedAttributes, ScopeResolution, SelectorParser, UrlParser Classes: CSSPseudoHandlers, CookieJar, MutationCoordinator, NodeWrapperCache, ObserverManager, ObserverMatcher, RangeTextSerializer, ScopedCSSPseudoHandlers, ShadowRootRegistry, TemplateContentRegistry

Constant Summary collapse

CSS_PSEUDO_HANDLERS =
CSSPseudoHandlers.new
KNOWN_PSEUDOS =

The complete set of CSS pseudo-classes (+ the four legacy single-colon pseudo-elements). A ‘:identifier` outside this set is an unknown selector token → SyntaxError, whereas a known-but-unimplemented one (`:hover`) is a valid selector that simply matches nothing.

%w[
  active any-link autofill blank checked current default defined disabled empty
  enabled first first-child first-of-type focus focus-visible focus-within
  fullscreen future has host hover in-range indeterminate invalid is lang
  last-child last-of-type left link local-link modal not nth-child nth-col
  nth-last-child nth-last-col nth-last-of-type nth-of-type only-child
  only-of-type optional out-of-range past placeholder-shown playing paused
  read-only read-write required right root scope target target-within
  user-invalid user-valid valid visited where dir
  before after first-line first-letter
].to_set.freeze
ATTR_ESCAPED_COLON =

Nokogiri’s CSS→XPath compiler chokes on an escaped colon INSIDE an attribute selector (‘[xlink:href]`, a namespaced/SVG attribute → “Invalid predicate”), though it handles escaped colons in class/id selectors fine (`.md:flex`, `#a:b` — Tailwind). Those attribute selectors target XML-namespaced attributes the HTML backend doesn’t model, so drop just the comma-clauses that use them; the rest of the selector list is preserved. (Real frameworks hit this constantly — Turbo’s click handler matches ‘a, a` on every click.) Returns a backend-safe selector; if every clause was unsupported, returns one that compiles but never matches.

/\[[^\]]*\\:[^\]]*\]/

Class Method Summary collapse

Class Method Details

.backend_safe_selector(selector) ⇒ Object



99
100
101
102
103
104
105
106
107
108
# File 'lib/dommy/internal/css_pseudo_handlers.rb', line 99

def self.backend_safe_selector(selector)
  # First drop clauses whose subject is a pseudo-element (`::before`,
  # `:first-line`) — they match no element, and the backend can't compile
  # `::`. Then drop the escaped-colon attribute clauses below.
  s = SelectorParser.matchable_selector(selector.to_s)
  return s unless s.include?('\\') && s.match?(ATTR_ESCAPED_COLON)

  kept = split_selector_list(s).reject { |clause| clause.match?(ATTR_ESCAPED_COLON) }
  kept.empty? ? ":not(*)" : kept.join(", ")
end

.css_query_arg!(args) ⇒ Object

Coerce the JS argument of a query method (querySelector/All) per WebIDL: the selector is a non-nullable DOMString, so JS ‘null` → “null” and `undefined` → “undefined” (which then match `<null>` / `<undefined>` typed elements rather than returning nothing), while a missing argument is a TypeError. Used at every JS dispatch site so the behaviour is uniform.



78
79
80
81
82
83
84
85
86
# File 'lib/dommy/internal/css_pseudo_handlers.rb', line 78

def self.css_query_arg!(args)
  raise ::Dommy::Bridge::TypeError, "1 argument required, but only 0 present" if args.empty?

  value = args[0]
  return "null" if value.nil?
  return "undefined" if defined?(::Dommy::Bridge::UNDEFINED) && value.equal?(::Dommy::Bridge::UNDEFINED)

  value
end

.scoped_pseudo_handlers(scope_node) ⇒ Object



43
44
45
# File 'lib/dommy/internal/css_pseudo_handlers.rb', line 43

def self.scoped_pseudo_handlers(scope_node)
  ScopedCSSPseudoHandlers.new(scope_node)
end

.split_selector_list(selector) ⇒ Object

Split a selector list on top-level commas only (commas inside […], (…), or quotes are part of a single complex selector and must not split it).



112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
# File 'lib/dommy/internal/css_pseudo_handlers.rb', line 112

def self.split_selector_list(selector)
  clauses = []
  depth = 0
  quote = nil
  current = +""
  selector.each_char do |ch|
    if quote
      quote = nil if ch == quote
    elsif ch == '"' || ch == "'"
      quote = ch
    elsif ch == "[" || ch == "("
      depth += 1
    elsif ch == "]" || ch == ")"
      depth -= 1 if depth.positive?
    elsif ch == "," && depth.zero?
      clauses << current.strip
      current = +""
      next
    end
    current << ch
  end
  clauses << current.strip
  clauses.reject(&:empty?)
end

.validate_selector!(selector) ⇒ Object

Validate a non-null CSS selector for ‘querySelector`/`matches`/`closest`, raising SyntaxError for syntactically invalid selectors. Delegates to the full grammar parser (SelectorParser), which catches everything the old heuristic did (empty string, leading combinator, unknown pseudo-class) plus the rest of the Selectors grammar (`[*=v]`, `..x`, `div % p`, unknown pseudo-elements, undeclared namespaces, …) that Nokogiri silently accepts.



69
70
71
# File 'lib/dommy/internal/css_pseudo_handlers.rb', line 69

def self.validate_selector!(selector)
  SelectorParser.validate!(selector.to_s)
end