p_css 0.1.9 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +2 -5
- data/lib/css/cascade.rb +14 -3
- data/lib/css/parser.rb +8 -8
- data/lib/css/selectors/matcher.rb +17 -2
- data/lib/css/token.rb +2 -2
- data/lib/css/tokenizer.rb +55 -19
- data/lib/css/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 223962d85169a337ff087e759a5bce186ece7182cd15ead418c280228fdde208
|
|
4
|
+
data.tar.gz: 4ccd0ca9e312dec47fc8f41f4d0b02d6918f86c47279d463f1c6c3e2e75549b8
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 9a4e563505c86e0939c03410c1d75be48b5fc1e9b9ada13dc422ee0b290dba215a94d4cd5dced0c2c4984cd15770bd2797b667436b77709766e7c9d64b750280
|
|
7
|
+
data.tar.gz: dae2b3b18a78f04eb9dc9babd31e0b1f7e17102740f2fd4cfe34112b24095c8d2c1453e2e67ffb0cd91949d31c35ea4ef42d71c620d8cc0c86cfb67632592f18
|
data/README.md
CHANGED
|
@@ -25,11 +25,8 @@ and none of them currently:
|
|
|
25
25
|
- resolve the cascade so `display: none` in a `<style>` tag actually
|
|
26
26
|
influences a visibility judgement.
|
|
27
27
|
|
|
28
|
-
p CSS fills that gap. The
|
|
29
|
-
|
|
30
|
-
Nokogiri + QuickJS Capybara driver that needs to know whether an element
|
|
31
|
-
is hidden without a real browser), but the gem is intentionally general —
|
|
32
|
-
no DOM library is hardwired in.
|
|
28
|
+
p CSS fills that gap. The gem is intentionally general — no DOM library
|
|
29
|
+
is hardwired in.
|
|
33
30
|
|
|
34
31
|
## What's in the box
|
|
35
32
|
|
data/lib/css/cascade.rb
CHANGED
|
@@ -18,7 +18,10 @@ module CSS
|
|
|
18
18
|
# not modeled — `@layer`, `@supports`, `@container`, `@scope`, and
|
|
19
19
|
# `@starting-style` blocks are descended into unconditionally.
|
|
20
20
|
class Cascade
|
|
21
|
-
|
|
21
|
+
# Allocated once per matching declaration on every resolve; Struct
|
|
22
|
+
# constructs noticeably faster than Data, and Match never escapes
|
|
23
|
+
# `resolve`, so it doesn't need Data's immutability guarantees.
|
|
24
|
+
Match = Struct.new(:declaration, :specificity, :inline, :order)
|
|
22
25
|
|
|
23
26
|
RuleEntry = Data.define(:selector_pairs, :declarations)
|
|
24
27
|
|
|
@@ -56,14 +59,14 @@ module CSS
|
|
|
56
59
|
|
|
57
60
|
entry.declarations.each do |decl|
|
|
58
61
|
order += 1
|
|
59
|
-
matches << Match.new(
|
|
62
|
+
matches << Match.new(decl, spec, false, order)
|
|
60
63
|
end
|
|
61
64
|
end
|
|
62
65
|
|
|
63
66
|
if inline_style
|
|
64
67
|
inline_declarations(inline_style).each do |decl|
|
|
65
68
|
order += 1
|
|
66
|
-
matches << Match.new(
|
|
69
|
+
matches << Match.new(decl, Selectors::Specificity::ZERO, true, order)
|
|
67
70
|
end
|
|
68
71
|
end
|
|
69
72
|
|
|
@@ -216,6 +219,14 @@ module CSS
|
|
|
216
219
|
end
|
|
217
220
|
|
|
218
221
|
def best_matching_specificity(element, selector_pairs, cache, state)
|
|
222
|
+
# The single-selector case is the dominant shape in real stylesheets
|
|
223
|
+
# (`.foo { ... }`, `div > a { ... }`), so skip the each/block frame
|
|
224
|
+
# and the running-max compare for it.
|
|
225
|
+
if selector_pairs.size == 1
|
|
226
|
+
sel, spec = selector_pairs[0]
|
|
227
|
+
return Selectors::Matcher.matches?(element, sel, cache: cache, state: state) ? spec : nil
|
|
228
|
+
end
|
|
229
|
+
|
|
219
230
|
best = nil
|
|
220
231
|
|
|
221
232
|
selector_pairs.each do |sel, spec|
|
data/lib/css/parser.rb
CHANGED
|
@@ -105,7 +105,7 @@ module CSS
|
|
|
105
105
|
groups = []
|
|
106
106
|
current = []
|
|
107
107
|
|
|
108
|
-
|
|
108
|
+
while true
|
|
109
109
|
case peek.type
|
|
110
110
|
when :eof
|
|
111
111
|
groups << current
|
|
@@ -151,7 +151,7 @@ module CSS
|
|
|
151
151
|
def consume_rule_list(top_level:)
|
|
152
152
|
rules = []
|
|
153
153
|
|
|
154
|
-
|
|
154
|
+
while true
|
|
155
155
|
t = peek
|
|
156
156
|
|
|
157
157
|
case t.type
|
|
@@ -183,7 +183,7 @@ module CSS
|
|
|
183
183
|
prelude = []
|
|
184
184
|
block = nil
|
|
185
185
|
|
|
186
|
-
|
|
186
|
+
while true
|
|
187
187
|
t = peek
|
|
188
188
|
|
|
189
189
|
case t.type
|
|
@@ -214,7 +214,7 @@ module CSS
|
|
|
214
214
|
def consume_qualified_rule(nested:)
|
|
215
215
|
prelude = []
|
|
216
216
|
|
|
217
|
-
|
|
217
|
+
while true
|
|
218
218
|
t = peek
|
|
219
219
|
|
|
220
220
|
case t.type
|
|
@@ -254,7 +254,7 @@ module CSS
|
|
|
254
254
|
def collect_block_items(stop_at_close_brace:)
|
|
255
255
|
items = []
|
|
256
256
|
|
|
257
|
-
|
|
257
|
+
while true
|
|
258
258
|
t = peek
|
|
259
259
|
|
|
260
260
|
case t.type
|
|
@@ -306,7 +306,7 @@ module CSS
|
|
|
306
306
|
|
|
307
307
|
value = []
|
|
308
308
|
|
|
309
|
-
|
|
309
|
+
while true
|
|
310
310
|
t = peek
|
|
311
311
|
|
|
312
312
|
case t.type
|
|
@@ -350,7 +350,7 @@ module CSS
|
|
|
350
350
|
end_type = BRACKET_CLOSE_TYPE.fetch(open_type)
|
|
351
351
|
values = []
|
|
352
352
|
|
|
353
|
-
|
|
353
|
+
while true
|
|
354
354
|
t = peek
|
|
355
355
|
|
|
356
356
|
case t.type
|
|
@@ -371,7 +371,7 @@ module CSS
|
|
|
371
371
|
name = consume.value
|
|
372
372
|
values = []
|
|
373
373
|
|
|
374
|
-
|
|
374
|
+
while true
|
|
375
375
|
t = peek
|
|
376
376
|
|
|
377
377
|
case t.type
|
|
@@ -99,7 +99,17 @@ module CSS
|
|
|
99
99
|
end
|
|
100
100
|
|
|
101
101
|
def match_compound(element, compound, cache, state)
|
|
102
|
-
compound.components
|
|
102
|
+
components = compound.components
|
|
103
|
+
i = 0
|
|
104
|
+
n = components.size
|
|
105
|
+
|
|
106
|
+
while i < n
|
|
107
|
+
return false unless match_simple(element, components[i], cache, state)
|
|
108
|
+
|
|
109
|
+
i += 1
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
true
|
|
103
113
|
end
|
|
104
114
|
|
|
105
115
|
def match_simple(element, simple, cache, state)
|
|
@@ -285,7 +295,12 @@ module CSS
|
|
|
285
295
|
return nil if p.nil?
|
|
286
296
|
|
|
287
297
|
siblings = element_children(p)
|
|
288
|
-
|
|
298
|
+
|
|
299
|
+
if of_type
|
|
300
|
+
own_tag = tag(element)
|
|
301
|
+
siblings = siblings.select { tag(_1) == own_tag }
|
|
302
|
+
end
|
|
303
|
+
|
|
289
304
|
siblings = siblings.reverse if from_end
|
|
290
305
|
|
|
291
306
|
idx = siblings.index { same_node?(_1, element) }
|
data/lib/css/token.rb
CHANGED
|
@@ -15,12 +15,12 @@ module CSS
|
|
|
15
15
|
colon semicolon comma
|
|
16
16
|
lbracket rbracket lparen rparen lbrace rbrace
|
|
17
17
|
eof
|
|
18
|
-
].freeze
|
|
18
|
+
].to_h { [_1, true] }.freeze
|
|
19
19
|
|
|
20
20
|
attr_reader :type, :value, :flag, :unit
|
|
21
21
|
|
|
22
22
|
def initialize(type, value = nil, flag: nil, unit: nil, position: nil)
|
|
23
|
-
raise ArgumentError, "unknown token type: #{type.inspect}" unless TYPES
|
|
23
|
+
raise ArgumentError, "unknown token type: #{type.inspect}" unless TYPES[type]
|
|
24
24
|
|
|
25
25
|
@type = type
|
|
26
26
|
@value = value
|
data/lib/css/tokenizer.rb
CHANGED
|
@@ -34,7 +34,7 @@ module CSS
|
|
|
34
34
|
def tokenize
|
|
35
35
|
tokens = []
|
|
36
36
|
|
|
37
|
-
|
|
37
|
+
while true
|
|
38
38
|
token = next_token
|
|
39
39
|
break if token.type == :eof
|
|
40
40
|
|
|
@@ -45,7 +45,7 @@ module CSS
|
|
|
45
45
|
end
|
|
46
46
|
|
|
47
47
|
def next_token
|
|
48
|
-
consume_comments
|
|
48
|
+
consume_comments if !@preserve_comments && @chars[@pos] == '/'
|
|
49
49
|
|
|
50
50
|
return Token.new(:eof) if @pos >= @length
|
|
51
51
|
|
|
@@ -62,7 +62,7 @@ module CSS
|
|
|
62
62
|
|
|
63
63
|
c = consume
|
|
64
64
|
|
|
65
|
-
return consume_whitespace if
|
|
65
|
+
return consume_whitespace if c == ' ' || c == "\n" || c == "\t"
|
|
66
66
|
return consume_string_token(c) if c == '"' || c == "'"
|
|
67
67
|
|
|
68
68
|
if (c == '+' || c == '-' || c == '.') && number_starts?(c, peek, peek(1))
|
|
@@ -252,7 +252,9 @@ module CSS
|
|
|
252
252
|
end
|
|
253
253
|
|
|
254
254
|
def consume_whitespace
|
|
255
|
-
|
|
255
|
+
while (c = @chars[@pos]) && (c == ' ' || c == "\n" || c == "\t")
|
|
256
|
+
@pos += 1
|
|
257
|
+
end
|
|
256
258
|
|
|
257
259
|
Token.new(:whitespace)
|
|
258
260
|
end
|
|
@@ -261,7 +263,7 @@ module CSS
|
|
|
261
263
|
def consume_string_token(ending)
|
|
262
264
|
buf = +''
|
|
263
265
|
|
|
264
|
-
|
|
266
|
+
while true
|
|
265
267
|
c = consume
|
|
266
268
|
|
|
267
269
|
case c
|
|
@@ -306,22 +308,41 @@ module CSS
|
|
|
306
308
|
end
|
|
307
309
|
end
|
|
308
310
|
|
|
309
|
-
# §4.3.11.
|
|
311
|
+
# §4.3.11. Fast path: walk @chars while the run is pure ident code points,
|
|
312
|
+
# then slice and return. Escapes (rare in real CSS) switch to a buf-based
|
|
313
|
+
# slow path that mirrors the spec's append-each-char semantics.
|
|
310
314
|
def consume_ident_sequence
|
|
311
|
-
|
|
315
|
+
start = @pos
|
|
316
|
+
c = nil
|
|
312
317
|
|
|
313
|
-
|
|
314
|
-
|
|
318
|
+
while (c = @chars[@pos])
|
|
319
|
+
o = c.ord
|
|
320
|
+
break unless o >= 128 || IDENT_CP_TABLE[o]
|
|
321
|
+
|
|
322
|
+
@pos += 1
|
|
323
|
+
end
|
|
324
|
+
|
|
325
|
+
return @chars[start, @pos - start].join unless c == '\\' && (n = @chars[@pos + 1]) && n != "\n"
|
|
326
|
+
|
|
327
|
+
buf = @chars[start, @pos - start].join.dup
|
|
328
|
+
@pos += 1
|
|
329
|
+
buf << consume_escaped_code_point
|
|
330
|
+
|
|
331
|
+
while (c = @chars[@pos])
|
|
332
|
+
o = c.ord
|
|
315
333
|
|
|
316
|
-
if
|
|
334
|
+
if o >= 128 || IDENT_CP_TABLE[o]
|
|
317
335
|
buf << c
|
|
318
|
-
|
|
336
|
+
@pos += 1
|
|
337
|
+
elsif c == '\\' && (n = @chars[@pos + 1]) && n != "\n"
|
|
338
|
+
@pos += 1
|
|
319
339
|
buf << consume_escaped_code_point
|
|
320
340
|
else
|
|
321
|
-
|
|
322
|
-
return buf
|
|
341
|
+
break
|
|
323
342
|
end
|
|
324
343
|
end
|
|
344
|
+
|
|
345
|
+
buf
|
|
325
346
|
end
|
|
326
347
|
|
|
327
348
|
# §4.3.4.
|
|
@@ -355,7 +376,7 @@ module CSS
|
|
|
355
376
|
|
|
356
377
|
consume while whitespace?(peek)
|
|
357
378
|
|
|
358
|
-
|
|
379
|
+
while true
|
|
359
380
|
c = consume
|
|
360
381
|
|
|
361
382
|
case c
|
|
@@ -396,7 +417,7 @@ module CSS
|
|
|
396
417
|
|
|
397
418
|
# §4.3.14.
|
|
398
419
|
def consume_bad_url_remnants
|
|
399
|
-
|
|
420
|
+
while true
|
|
400
421
|
c = consume
|
|
401
422
|
|
|
402
423
|
return if c.nil? || c == ')'
|
|
@@ -419,17 +440,27 @@ module CSS
|
|
|
419
440
|
end
|
|
420
441
|
end
|
|
421
442
|
|
|
422
|
-
# §4.3.12. Returns [numeric_value, :integer | :number].
|
|
443
|
+
# §4.3.12. Returns [numeric_value, :integer | :number]. The three digit
|
|
444
|
+
# runs inline DIGIT_TABLE so the loop bodies stay in the same frame.
|
|
423
445
|
def consume_number
|
|
424
446
|
repr = +''
|
|
425
447
|
flag = :integer
|
|
426
448
|
|
|
427
449
|
repr << consume if peek == '+' || peek == '-'
|
|
428
|
-
|
|
450
|
+
|
|
451
|
+
while (c = @chars[@pos]) && (o = c.ord) < 128 && DIGIT_TABLE[o]
|
|
452
|
+
repr << c
|
|
453
|
+
@pos += 1
|
|
454
|
+
end
|
|
429
455
|
|
|
430
456
|
if peek == '.' && digit?(peek(1))
|
|
431
457
|
repr << consume
|
|
432
|
-
|
|
458
|
+
|
|
459
|
+
while (c = @chars[@pos]) && (o = c.ord) < 128 && DIGIT_TABLE[o]
|
|
460
|
+
repr << c
|
|
461
|
+
@pos += 1
|
|
462
|
+
end
|
|
463
|
+
|
|
433
464
|
flag = :number
|
|
434
465
|
end
|
|
435
466
|
|
|
@@ -437,7 +468,12 @@ module CSS
|
|
|
437
468
|
(digit?(peek(1)) || ((peek(1) == '+' || peek(1) == '-') && digit?(peek(2))))
|
|
438
469
|
repr << consume
|
|
439
470
|
repr << consume if peek == '+' || peek == '-'
|
|
440
|
-
|
|
471
|
+
|
|
472
|
+
while (c = @chars[@pos]) && (o = c.ord) < 128 && DIGIT_TABLE[o]
|
|
473
|
+
repr << c
|
|
474
|
+
@pos += 1
|
|
475
|
+
end
|
|
476
|
+
|
|
441
477
|
flag = :number
|
|
442
478
|
end
|
|
443
479
|
|
data/lib/css/version.rb
CHANGED