habaki 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +3 -0
  3. data/ext/katana/extconf.rb +20 -0
  4. data/ext/katana/rb_katana.c +280 -0
  5. data/ext/katana/rb_katana.h +102 -0
  6. data/ext/katana/rb_katana_array.c +144 -0
  7. data/ext/katana/rb_katana_declaration.c +389 -0
  8. data/ext/katana/rb_katana_rule.c +461 -0
  9. data/ext/katana/rb_katana_selector.c +559 -0
  10. data/ext/katana/src/foundation.c +237 -0
  11. data/ext/katana/src/foundation.h +120 -0
  12. data/ext/katana/src/katana.h +590 -0
  13. data/ext/katana/src/katana.lex.c +4104 -0
  14. data/ext/katana/src/katana.lex.h +592 -0
  15. data/ext/katana/src/katana.tab.c +4422 -0
  16. data/ext/katana/src/katana.tab.h +262 -0
  17. data/ext/katana/src/parser.c +1563 -0
  18. data/ext/katana/src/parser.h +237 -0
  19. data/ext/katana/src/selector.c +659 -0
  20. data/ext/katana/src/selector.h +54 -0
  21. data/ext/katana/src/tokenizer.c +300 -0
  22. data/ext/katana/src/tokenizer.h +41 -0
  23. data/lib/habaki/charset_rule.rb +25 -0
  24. data/lib/habaki/declaration.rb +53 -0
  25. data/lib/habaki/declarations.rb +346 -0
  26. data/lib/habaki/error.rb +43 -0
  27. data/lib/habaki/font_face_rule.rb +24 -0
  28. data/lib/habaki/formal_syntax.rb +464 -0
  29. data/lib/habaki/formatter.rb +99 -0
  30. data/lib/habaki/import_rule.rb +34 -0
  31. data/lib/habaki/media_rule.rb +173 -0
  32. data/lib/habaki/namespace_rule.rb +31 -0
  33. data/lib/habaki/node.rb +52 -0
  34. data/lib/habaki/page_rule.rb +24 -0
  35. data/lib/habaki/qualified_name.rb +29 -0
  36. data/lib/habaki/rule.rb +48 -0
  37. data/lib/habaki/rules.rb +225 -0
  38. data/lib/habaki/selector.rb +98 -0
  39. data/lib/habaki/selectors.rb +49 -0
  40. data/lib/habaki/style_rule.rb +35 -0
  41. data/lib/habaki/stylesheet.rb +158 -0
  42. data/lib/habaki/sub_selector.rb +234 -0
  43. data/lib/habaki/sub_selectors.rb +42 -0
  44. data/lib/habaki/supports_rule.rb +65 -0
  45. data/lib/habaki/value.rb +321 -0
  46. data/lib/habaki/values.rb +86 -0
  47. data/lib/habaki/visitor/element.rb +50 -0
  48. data/lib/habaki/visitor/media.rb +22 -0
  49. data/lib/habaki/visitor/nokogiri_element.rb +56 -0
  50. data/lib/habaki.rb +39 -0
  51. metadata +190 -0
@@ -0,0 +1,659 @@
1
+ /**
2
+ * Copyright (c) 2015 QFish <im@qfi.sh>
3
+ *
4
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ * of this software and associated documentation files (the "Software"), to deal
6
+ * in the Software without restriction, including without limitation the rights
7
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ * copies of the Software, and to permit persons to whom the Software is
9
+ * furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in
12
+ * all copies or substantial portions of the Software.
13
+ *
14
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20
+ * THE SOFTWARE.
21
+ */
22
+
23
+ #include "selector.h"
24
+ #include <string.h>
25
+ #include <assert.h>
26
+ #include <strings.h>
27
+
28
+ #undef assert
29
+ #define assert(x)
30
+
31
+ // Refs:
32
+ // http://www.w3.org/TR/css3-selectors/
33
+ //
34
+
35
+ static KatanaPseudoType name_to_pseudo_type(const char* name, bool hasArguments);
36
+
37
+ bool katana_selector_crosses_tree_scopes(const KatanaSelector* selector)
38
+ {
39
+ // TODO: To be supported
40
+ return false;
41
+ }
42
+
43
+ // bool katana_is_attribute_selector(const KatanaSelector* selector)
44
+ // {
45
+ // return selector->match == KatanaSelectorMatchAttributeExact
46
+ // || selector->match == KatanaSelectorMatchAttributeSet
47
+ // || selector->match == KatanaSelectorMatchAttributeList
48
+ // || selector->match == KatanaSelectorMatchAttributeHyphen
49
+ // || selector->match == KatanaSelectorMatchAttributeContain
50
+ // || selector->match == KatanaSelectorMatchAttributeBegin
51
+ // || selector->match == KatanaSelectorMatchAttributeEnd;
52
+ // }
53
+
54
+ KatanaPseudoType katana_parse_pseudo_type(const char* name, bool hasArguments)
55
+ {
56
+ KatanaPseudoType pseudoType = name_to_pseudo_type(name, hasArguments);
57
+ if (pseudoType != KatanaPseudoUnknown)
58
+ return pseudoType;
59
+
60
+ if (katana_string_has_prefix(name, "-webkit-"))
61
+ return KatanaPseudoWebKitCustomElement;
62
+
63
+ return KatanaPseudoUnknown;
64
+ }
65
+
66
+ void katana_selector_extract_pseudo_type(KatanaSelector* selector)
67
+ {
68
+ if (selector->pseudo == KatanaPseudoNotParsed)
69
+ selector->pseudo = KatanaPseudoUnknown;
70
+
71
+ if (selector->match != KatanaSelectorMatchPseudoClass && selector->match != KatanaSelectorMatchPseudoElement && selector->match != KatanaSelectorMatchPagePseudoClass)
72
+ return;
73
+ bool hasArguments = (NULL != selector->data->argument) || (NULL != selector->data->selectors);
74
+ selector->pseudo = katana_parse_pseudo_type(selector->data->value, hasArguments);
75
+
76
+ bool element = false; // pseudo-element
77
+ bool compat = false; // single colon compatbility mode
78
+ bool isPagePseudoClass = false; // Page pseudo-class
79
+
80
+ switch (selector->pseudo) {
81
+ case KatanaPseudoAfter:
82
+ case KatanaPseudoBefore:
83
+ case KatanaPseudoFirstLetter:
84
+ case KatanaPseudoFirstLine:
85
+ compat = true;
86
+ case KatanaPseudoBackdrop:
87
+ case KatanaPseudoCue:
88
+ case KatanaPseudoResizer:
89
+ case KatanaPseudoScrollbar:
90
+ case KatanaPseudoScrollbarCorner:
91
+ case KatanaPseudoScrollbarButton:
92
+ case KatanaPseudoScrollbarThumb:
93
+ case KatanaPseudoScrollbarTrack:
94
+ case KatanaPseudoScrollbarTrackPiece:
95
+ case KatanaPseudoSelection:
96
+ case KatanaPseudoWebKitCustomElement:
97
+ case KatanaPseudoContent:
98
+ case KatanaPseudoShadow:
99
+ element = true;
100
+ break;
101
+ case KatanaPseudoUnknown:
102
+ case KatanaPseudoEmpty:
103
+ case KatanaPseudoFirstChild:
104
+ case KatanaPseudoFirstOfType:
105
+ case KatanaPseudoLastChild:
106
+ case KatanaPseudoLastOfType:
107
+ case KatanaPseudoOnlyChild:
108
+ case KatanaPseudoOnlyOfType:
109
+ case KatanaPseudoNthChild:
110
+ case KatanaPseudoNthOfType:
111
+ case KatanaPseudoNthLastChild:
112
+ case KatanaPseudoNthLastOfType:
113
+ case KatanaPseudoLink:
114
+ case KatanaPseudoVisited:
115
+ case KatanaPseudoAny:
116
+ case KatanaPseudoAnyLink:
117
+ case KatanaPseudoAutofill:
118
+ case KatanaPseudoHover:
119
+ case KatanaPseudoDrag:
120
+ case KatanaPseudoFocus:
121
+ case KatanaPseudoActive:
122
+ case KatanaPseudoChecked:
123
+ case KatanaPseudoEnabled:
124
+ case KatanaPseudoFullPageMedia:
125
+ case KatanaPseudoDefault:
126
+ case KatanaPseudoDisabled:
127
+ case KatanaPseudoOptional:
128
+ case KatanaPseudoRequired:
129
+ case KatanaPseudoReadOnly:
130
+ case KatanaPseudoReadWrite:
131
+ case KatanaPseudoScope:
132
+ case KatanaPseudoValid:
133
+ case KatanaPseudoInvalid:
134
+ case KatanaPseudoIndeterminate:
135
+ case KatanaPseudoTarget:
136
+ case KatanaPseudoLang:
137
+ case KatanaPseudoNot:
138
+ case KatanaPseudoRoot:
139
+ case KatanaPseudoWindowInactive:
140
+ case KatanaPseudoCornerPresent:
141
+ case KatanaPseudoDecrement:
142
+ case KatanaPseudoIncrement:
143
+ case KatanaPseudoHorizontal:
144
+ case KatanaPseudoVertical:
145
+ case KatanaPseudoStart:
146
+ case KatanaPseudoEnd:
147
+ case KatanaPseudoDoubleButton:
148
+ case KatanaPseudoSingleButton:
149
+ case KatanaPseudoNoButton:
150
+ case KatanaPseudoNotParsed:
151
+ case KatanaPseudoFullScreen:
152
+ case KatanaPseudoFullScreenDocument:
153
+ case KatanaPseudoFullScreenAncestor:
154
+ case KatanaPseudoInRange:
155
+ case KatanaPseudoOutOfRange:
156
+ case KatanaPseudoFutureCue:
157
+ case KatanaPseudoPastCue:
158
+ case KatanaPseudoHost:
159
+ case KatanaPseudoHostContext:
160
+ case KatanaPseudoUnresolved:
161
+ case KatanaPseudoSpatialNavigationFocus:
162
+ case KatanaPseudoListBox:
163
+ break;
164
+ case KatanaPseudoFirstPage:
165
+ case KatanaPseudoLeftPage:
166
+ case KatanaPseudoRightPage:
167
+ isPagePseudoClass = true;
168
+ break;
169
+ }
170
+
171
+ bool matchPagePseudoClass = (selector->match == KatanaSelectorMatchPagePseudoClass);
172
+ if (matchPagePseudoClass != isPagePseudoClass)
173
+ selector->pseudo = KatanaPseudoUnknown;
174
+ else if (selector->match == KatanaSelectorMatchPseudoClass && element) {
175
+ if (!compat)
176
+ selector->pseudo = KatanaPseudoUnknown;
177
+ else
178
+ selector->match = KatanaSelectorMatchPseudoElement;
179
+ } else if (selector->match == KatanaSelectorMatchPseudoElement && !element)
180
+ selector->pseudo = KatanaPseudoUnknown;
181
+ }
182
+
183
+ bool katana_selector_matches_pseudo_element(KatanaSelector* selector)
184
+ {
185
+ if (selector->pseudo == KatanaPseudoUnknown)
186
+ katana_selector_extract_pseudo_type(selector);
187
+ return selector->match == KatanaSelectorMatchPseudoElement;
188
+ }
189
+
190
+ bool katana_selector_is_custom_pseudo_element(KatanaSelector* selector)
191
+ {
192
+ return selector->match == KatanaSelectorMatchPseudoElement && selector->pseudo == KatanaPseudoWebKitCustomElement;
193
+ }
194
+
195
+ bool katana_selector_is_direct_adjacent(KatanaSelector* selector)
196
+ {
197
+ return selector->relation == KatanaSelectorRelationDirectAdjacent || selector->relation == KatanaSelectorRelationIndirectAdjacent;
198
+ }
199
+
200
+ bool katana_selector_is_adjacent(KatanaSelector* selector)
201
+ {
202
+ return selector->relation == KatanaSelectorRelationDirectAdjacent;
203
+ }
204
+
205
+ bool katana_selector_is_shadow(KatanaSelector* selector)
206
+ {
207
+ return selector->relation == KatanaSelectorRelationShadowPseudo || selector->relation == KatanaSelectorRelationShadowDeep;
208
+ }
209
+
210
+ bool katana_selector_is_sibling(KatanaSelector* selector)
211
+ {
212
+ katana_selector_extract_pseudo_type(selector);
213
+
214
+ KatanaPseudoType type = selector->pseudo;
215
+ return selector->relation == KatanaSelectorRelationDirectAdjacent
216
+ || selector->relation == KatanaSelectorRelationIndirectAdjacent
217
+ || type == KatanaPseudoEmpty
218
+ || type == KatanaPseudoFirstChild
219
+ || type == KatanaPseudoFirstOfType
220
+ || type == KatanaPseudoLastChild
221
+ || type == KatanaPseudoLastOfType
222
+ || type == KatanaPseudoOnlyChild
223
+ || type == KatanaPseudoOnlyOfType
224
+ || type == KatanaPseudoNthChild
225
+ || type == KatanaPseudoNthOfType
226
+ || type == KatanaPseudoNthLastChild
227
+ || type == KatanaPseudoNthLastOfType;
228
+ }
229
+
230
+ bool katana_selector_is_attribute(const KatanaSelector* selector)
231
+ {
232
+ return selector->match >= KatanaSelectorMatchFirstAttribute;
233
+ }
234
+
235
+ bool katana_selector_is_content_pseudo_element(KatanaSelector* selector)
236
+ {
237
+ katana_selector_extract_pseudo_type(selector);
238
+ return selector->match == KatanaSelectorMatchPseudoElement && selector->pseudo == KatanaPseudoContent;
239
+ }
240
+
241
+ bool katana_selector_is_shadow_pseudo_element(KatanaSelector* selector)
242
+ {
243
+ return selector->match == KatanaSelectorMatchPseudoElement
244
+ && selector->pseudo == KatanaPseudoShadow;
245
+ }
246
+
247
+ bool katana_selector_is_host_pseudo_class(KatanaSelector* selector)
248
+ {
249
+ return selector->match == KatanaSelectorMatchPseudoClass && (selector->pseudo == KatanaPseudoHost || selector->pseudo == KatanaPseudoHostContext);
250
+ }
251
+
252
+ bool katana_selector_is_tree_boundary_crossing(KatanaSelector* selector)
253
+ {
254
+ katana_selector_extract_pseudo_type(selector);
255
+ return selector->match == KatanaSelectorMatchPseudoClass && (selector->pseudo == KatanaPseudoHost || selector->pseudo == KatanaPseudoHostContext);
256
+ }
257
+
258
+ bool katana_selector_is_insertion_point_crossing(KatanaSelector* selector)
259
+ {
260
+ katana_selector_extract_pseudo_type(selector);
261
+ return (selector->match == KatanaSelectorMatchPseudoClass && selector->pseudo == KatanaPseudoHostContext)
262
+ || (selector->match == KatanaSelectorMatchPseudoElement && selector->pseudo == KatanaPseudoContent);
263
+ }
264
+
265
+ KatanaParserString* katana_build_relation_selector_string(KatanaParser* parser, const char* relation, KatanaParserString* string, KatanaParserString* next, KatanaSelector* tagHistory)
266
+ {
267
+ if ( NULL != relation ) {
268
+ katana_string_prepend_characters(parser, relation, string);
269
+ }
270
+
271
+ if ( NULL != next ) {
272
+ katana_string_append_string(parser, next, string);
273
+ }
274
+
275
+ KatanaParserString * result = katana_selector_to_string(parser, tagHistory, (KatanaParserString*)string);
276
+ katana_parser_deallocate(parser, (void*) string->data);
277
+ katana_parser_deallocate(parser, (void*) string);
278
+ return result;
279
+ }
280
+
281
+ KatanaParserString* katana_selector_to_string(KatanaParser* parser, KatanaSelector* selector, KatanaParserString* next)
282
+ {
283
+ KatanaParserString* string = katana_parser_allocate(parser, sizeof(KatanaParserString));
284
+ katana_string_init(parser, string);
285
+
286
+ bool tag_is_implicit = true;
287
+
288
+ if (selector->match == KatanaSelectorMatchTag && tag_is_implicit)
289
+ {
290
+ if ( NULL == selector->tag->prefix )
291
+ katana_string_append_characters(parser, selector->tag->local, string);
292
+ else {
293
+ katana_string_append_characters(parser, selector->tag->prefix, string);
294
+ katana_string_append_characters(parser, "|", string);
295
+ katana_string_append_characters(parser, selector->tag->local, string);
296
+ }
297
+ }
298
+
299
+ const KatanaSelector* cs = selector;
300
+
301
+ while (true) {
302
+ if (cs->match == KatanaSelectorMatchId) {
303
+ katana_string_append_characters(parser, "#", string);
304
+ katana_string_append_characters(parser, cs->data->value, string);
305
+ } else if (cs->match == KatanaSelectorMatchClass) {
306
+ katana_string_append_characters(parser, ".", string);
307
+ katana_string_append_characters(parser, cs->data->value, string);
308
+ } else if (cs->match == KatanaSelectorMatchPseudoClass || cs->match == KatanaSelectorMatchPagePseudoClass) {
309
+ katana_string_append_characters(parser, ":", string);
310
+ katana_string_append_characters(parser, cs->data->value, string);
311
+
312
+ switch (cs->pseudo) {
313
+ case KatanaPseudoAny:
314
+ case KatanaPseudoNot:
315
+ case KatanaPseudoHost:
316
+ case KatanaPseudoHostContext: {
317
+ if ( cs->data->selectors ) {
318
+ KatanaArray* sels = cs->data->selectors;
319
+ for (size_t i=0; i<sels->length; i++) {
320
+ KatanaParserString* str = katana_selector_to_string(parser, sels->data[i], NULL);
321
+ katana_string_append_string(parser, str, string);
322
+ katana_parser_deallocate(parser, (void*) str->data);
323
+ katana_parser_deallocate(parser, (void*) str);
324
+ if ( i != sels->length -1 ) {
325
+ katana_string_append_characters(parser, ", ", string);
326
+ }
327
+ }
328
+ katana_string_append_characters(parser, ")", string);
329
+ }
330
+ }
331
+ break;
332
+ case KatanaPseudoLang:
333
+ case KatanaPseudoNthChild:
334
+ case KatanaPseudoNthLastChild:
335
+ case KatanaPseudoNthOfType:
336
+ case KatanaPseudoNthLastOfType: {
337
+ katana_string_append_characters(parser, cs->data->argument, string);
338
+ katana_string_append_characters(parser, ")", string);
339
+ }
340
+ break;
341
+ default:
342
+ break;
343
+ }
344
+ } else if (cs->match == KatanaSelectorMatchPseudoElement) {
345
+ katana_string_append_characters(parser, "::", string);
346
+ katana_string_append_characters(parser, cs->data->value, string);
347
+ } else if (katana_selector_is_attribute(cs)) {
348
+ katana_string_append_characters(parser, "[", string);
349
+ if (NULL != cs->data->attribute->prefix) {
350
+ katana_string_append_characters(parser, cs->data->attribute->prefix, string);
351
+ katana_string_append_characters(parser, "|", string);
352
+ }
353
+ katana_string_append_characters(parser, cs->data->attribute->local, string);
354
+ switch (cs->match) {
355
+ case KatanaSelectorMatchAttributeExact:
356
+ katana_string_append_characters(parser, "=", string);
357
+ break;
358
+ case KatanaSelectorMatchAttributeSet:
359
+ katana_string_append_characters(parser, "]", string);
360
+ break;
361
+ case KatanaSelectorMatchAttributeList:
362
+ katana_string_append_characters(parser, "~=", string);
363
+ break;
364
+ case KatanaSelectorMatchAttributeHyphen:
365
+ katana_string_append_characters(parser, "|=", string);
366
+ break;
367
+ case KatanaSelectorMatchAttributeBegin:
368
+ katana_string_append_characters(parser, "^=", string);
369
+ break;
370
+ case KatanaSelectorMatchAttributeEnd:
371
+ katana_string_append_characters(parser, "$=", string);
372
+ break;
373
+ case KatanaSelectorMatchAttributeContain:
374
+ katana_string_append_characters(parser, "*=", string);
375
+ break;
376
+ default:
377
+ break;
378
+ }
379
+ if (cs->match != KatanaSelectorMatchAttributeSet) {
380
+ katana_string_append_characters(parser, "\"", string);
381
+ katana_string_append_characters(parser, cs->data->value, string);
382
+ katana_string_append_characters(parser, "\"", string);
383
+ if (cs->data->bits.attributeMatchType == KatanaAttributeMatchTypeCaseInsensitive)
384
+ katana_string_append_characters(parser, " i", string);
385
+ katana_string_append_characters(parser, "]", string);
386
+ }
387
+ }
388
+ if (cs->relation != KatanaSelectorRelationSubSelector || !cs->tagHistory)
389
+ break;
390
+ cs = cs->tagHistory;
391
+ }
392
+
393
+ KatanaSelector* tagHistory = cs->tagHistory;
394
+
395
+ if ( NULL != tagHistory ) {
396
+ switch (cs->relation) {
397
+ case KatanaSelectorRelationDescendant:
398
+ {
399
+ return katana_build_relation_selector_string(parser, " ", string, next, tagHistory);
400
+ }
401
+ case KatanaSelectorRelationChild:
402
+ {
403
+ return katana_build_relation_selector_string(parser, " > ", string, next, tagHistory);
404
+ }
405
+ case KatanaSelectorRelationShadowDeep:
406
+ {
407
+ return katana_build_relation_selector_string(parser, " /deep/ ", string, next, tagHistory);
408
+ }
409
+ case KatanaSelectorRelationDirectAdjacent:
410
+ {
411
+ return katana_build_relation_selector_string(parser, " + ", string, next, tagHistory);
412
+ }
413
+ case KatanaSelectorRelationIndirectAdjacent:
414
+ {
415
+ return katana_build_relation_selector_string(parser, " ~ ", string, next, tagHistory);
416
+ }
417
+ case KatanaSelectorRelationSubSelector:
418
+ {
419
+ return NULL;
420
+ }
421
+ case KatanaSelectorRelationShadowPseudo:
422
+ {
423
+ return katana_build_relation_selector_string(parser, NULL, string, next, tagHistory);
424
+ }
425
+ }
426
+ }
427
+
428
+ if ( NULL != next ) {
429
+ katana_string_append_string(parser, (KatanaParserString*)next, string);
430
+ }
431
+
432
+ return (KatanaParserString*)string;
433
+ }
434
+
435
+ unsigned calc_specificity_for_one_selector(const KatanaSelector* selector)
436
+ {
437
+ switch ( selector->match ) {
438
+ case KatanaSelectorMatchId:
439
+ return 0x10000;
440
+
441
+ case KatanaSelectorMatchPseudoClass:
442
+ case KatanaSelectorMatchAttributeExact:
443
+ case KatanaSelectorMatchClass:
444
+ case KatanaSelectorMatchAttributeSet:
445
+ case KatanaSelectorMatchAttributeList:
446
+ case KatanaSelectorMatchAttributeHyphen:
447
+ case KatanaSelectorMatchPseudoElement:
448
+ case KatanaSelectorMatchAttributeContain:
449
+ case KatanaSelectorMatchAttributeBegin:
450
+ case KatanaSelectorMatchAttributeEnd:
451
+ return 0x100;
452
+
453
+ case KatanaSelectorMatchTag:
454
+ return !strcasecmp(selector->tag->local, "*") ? 0 : 1;
455
+ case KatanaSelectorMatchUnknown:
456
+ case KatanaSelectorMatchPagePseudoClass:
457
+ return 0;
458
+ }
459
+
460
+ return 0;
461
+ }
462
+
463
+ unsigned katana_calc_specificity_for_selector(KatanaSelector* selector)
464
+ {
465
+ if ( NULL == selector ) {
466
+ return 0;
467
+ }
468
+
469
+ static const unsigned idMask = 0xff0000;
470
+ static const unsigned classMask = 0xff00;
471
+ static const unsigned elementMask = 0xff;
472
+
473
+ unsigned total = 0;
474
+ unsigned temp = 0;
475
+
476
+ for (const KatanaSelector * next = selector; next; next = next->tagHistory)
477
+ {
478
+ temp = total + calc_specificity_for_one_selector(next);
479
+
480
+ if ((temp & idMask) < (total & idMask))
481
+ total |= idMask;
482
+ else if ((temp & classMask) < (total & classMask))
483
+ total |= classMask;
484
+ else if ((temp & elementMask) < (total & elementMask))
485
+ total |= elementMask;
486
+ else
487
+ total = temp;
488
+ }
489
+
490
+ return total;
491
+ }
492
+
493
+ // Could be made smaller and faster by replacing pointer with an
494
+ // offset into a string buffer and making the bit fields smaller but
495
+ // that could not be maintained by hand.
496
+ typedef struct {
497
+ const char* string;
498
+ unsigned type:8;
499
+ } KatanaNameToPseudoStruct;
500
+
501
+ // These tables should be kept sorted.
502
+ const static KatanaNameToPseudoStruct kPseudoTypeWithoutArgumentsMap[] = {
503
+ {"-internal-list-box", KatanaPseudoListBox},
504
+ {"-internal-media-controls-cast-button", KatanaPseudoWebKitCustomElement},
505
+ {"-internal-media-controls-overlay-cast-button", KatanaPseudoWebKitCustomElement},
506
+ {"-internal-spatial-navigation-focus", KatanaPseudoSpatialNavigationFocus},
507
+ {"-webkit-any-link", KatanaPseudoAnyLink},
508
+ {"-webkit-autofill", KatanaPseudoAutofill},
509
+ {"-webkit-drag", KatanaPseudoDrag},
510
+ {"-webkit-full-page-media", KatanaPseudoFullPageMedia},
511
+ {"-webkit-full-screen", KatanaPseudoFullScreen},
512
+ {"-webkit-full-screen-ancestor", KatanaPseudoFullScreenAncestor},
513
+ {"-webkit-full-screen-document", KatanaPseudoFullScreenDocument},
514
+ {"-webkit-resizer", KatanaPseudoResizer},
515
+ {"-webkit-scrollbar", KatanaPseudoScrollbar},
516
+ {"-webkit-scrollbar-button", KatanaPseudoScrollbarButton},
517
+ {"-webkit-scrollbar-corner", KatanaPseudoScrollbarCorner},
518
+ {"-webkit-scrollbar-thumb", KatanaPseudoScrollbarThumb},
519
+ {"-webkit-scrollbar-track", KatanaPseudoScrollbarTrack},
520
+ {"-webkit-scrollbar-track-piece", KatanaPseudoScrollbarTrackPiece},
521
+ {"active", KatanaPseudoActive},
522
+ {"after", KatanaPseudoAfter},
523
+ {"backdrop", KatanaPseudoBackdrop},
524
+ {"before", KatanaPseudoBefore},
525
+ {"checked", KatanaPseudoChecked},
526
+ {"content", KatanaPseudoContent},
527
+ {"corner-present", KatanaPseudoCornerPresent},
528
+ {"cue", KatanaPseudoWebKitCustomElement},
529
+ {"decrement", KatanaPseudoDecrement},
530
+ {"default", KatanaPseudoDefault},
531
+ {"disabled", KatanaPseudoDisabled},
532
+ {"double-button", KatanaPseudoDoubleButton},
533
+ {"empty", KatanaPseudoEmpty},
534
+ {"enabled", KatanaPseudoEnabled},
535
+ {"end", KatanaPseudoEnd},
536
+ {"first", KatanaPseudoFirstPage},
537
+ {"first-child", KatanaPseudoFirstChild},
538
+ {"first-letter", KatanaPseudoFirstLetter},
539
+ {"first-line", KatanaPseudoFirstLine},
540
+ {"first-of-type", KatanaPseudoFirstOfType},
541
+ {"focus", KatanaPseudoFocus},
542
+ {"future", KatanaPseudoFutureCue},
543
+ {"horizontal", KatanaPseudoHorizontal},
544
+ {"host", KatanaPseudoHost},
545
+ {"hover", KatanaPseudoHover},
546
+ {"in-range", KatanaPseudoInRange},
547
+ {"increment", KatanaPseudoIncrement},
548
+ {"indeterminate", KatanaPseudoIndeterminate},
549
+ {"invalid", KatanaPseudoInvalid},
550
+ {"last-child", KatanaPseudoLastChild},
551
+ {"last-of-type", KatanaPseudoLastOfType},
552
+ {"left", KatanaPseudoLeftPage},
553
+ {"link", KatanaPseudoLink},
554
+ {"no-button", KatanaPseudoNoButton},
555
+ {"only-child", KatanaPseudoOnlyChild},
556
+ {"only-of-type", KatanaPseudoOnlyOfType},
557
+ {"optional", KatanaPseudoOptional},
558
+ {"out-of-range", KatanaPseudoOutOfRange},
559
+ {"past", KatanaPseudoPastCue},
560
+ {"read-only", KatanaPseudoReadOnly},
561
+ {"read-write", KatanaPseudoReadWrite},
562
+ {"required", KatanaPseudoRequired},
563
+ {"right", KatanaPseudoRightPage},
564
+ {"root", KatanaPseudoRoot},
565
+ {"scope", KatanaPseudoScope},
566
+ {"selection", KatanaPseudoSelection},
567
+ {"shadow", KatanaPseudoShadow},
568
+ {"single-button", KatanaPseudoSingleButton},
569
+ {"start", KatanaPseudoStart},
570
+ {"target", KatanaPseudoTarget},
571
+ {"unresolved", KatanaPseudoUnresolved},
572
+ {"valid", KatanaPseudoValid},
573
+ {"vertical", KatanaPseudoVertical},
574
+ {"visited", KatanaPseudoVisited},
575
+ {"window-inactive", KatanaPseudoWindowInactive},
576
+ };
577
+
578
+ const static KatanaNameToPseudoStruct kPseudoTypeWithArgumentsMap[] = {
579
+ {"-webkit-any(", KatanaPseudoAny},
580
+ {"cue(", KatanaPseudoCue},
581
+ {"host(", KatanaPseudoHost},
582
+ {"host-context(", KatanaPseudoHostContext},
583
+ {"lang(", KatanaPseudoLang},
584
+ {"not(", KatanaPseudoNot},
585
+ {"nth-child(", KatanaPseudoNthChild},
586
+ {"nth-last-child(", KatanaPseudoNthLastChild},
587
+ {"nth-last-of-type(", KatanaPseudoNthLastOfType},
588
+ {"nth-of-type(", KatanaPseudoNthOfType},
589
+ };
590
+
591
+ static const KatanaNameToPseudoStruct* lower_bound(const KatanaNameToPseudoStruct *map,
592
+ size_t count, const char* key);
593
+
594
+ static KatanaPseudoType name_to_pseudo_type(const char* name, bool hasArguments)
595
+ {
596
+ if (NULL == name)
597
+ return KatanaPseudoUnknown;
598
+
599
+ const KatanaNameToPseudoStruct* pseudoTypeMap;
600
+ size_t count;
601
+
602
+ if (hasArguments) {
603
+ pseudoTypeMap = kPseudoTypeWithArgumentsMap;
604
+ count = sizeof(kPseudoTypeWithArgumentsMap) / sizeof(KatanaNameToPseudoStruct);
605
+ } else {
606
+ pseudoTypeMap = kPseudoTypeWithoutArgumentsMap;
607
+ count = sizeof(kPseudoTypeWithoutArgumentsMap) / sizeof(KatanaNameToPseudoStruct);
608
+ }
609
+
610
+ const KatanaNameToPseudoStruct* match = lower_bound(pseudoTypeMap, count, name);
611
+ if ( match == (pseudoTypeMap + count)
612
+ || 0 != strcasecmp(match->string, name) )
613
+ return KatanaPseudoUnknown;
614
+
615
+ return match->type;
616
+ }
617
+
618
+ static const KatanaNameToPseudoStruct* lower_bound(const KatanaNameToPseudoStruct *array,
619
+ size_t size, const char* key) {
620
+ const KatanaNameToPseudoStruct* it;
621
+ const KatanaNameToPseudoStruct* first = array;
622
+ size_t count = size, step;
623
+ while (count > 0) {
624
+ it = first;
625
+ step = count / 2;
626
+ it += step;
627
+ if (strncmp(it->string, key, strlen(key)) < 0) {
628
+ first = ++it;
629
+ count -= step + 1;
630
+ } else count = step;
631
+ }
632
+ return first;
633
+ }
634
+
635
+ #if KATANA_PARSER_DEBUG
636
+
637
+ void test_lower_bound()
638
+ {
639
+ const KatanaNameToPseudoStruct* pseudoTypeMap;
640
+ size_t count;
641
+
642
+ pseudoTypeMap = kPseudoTypeWithArgumentsMap;
643
+ count = sizeof(kPseudoTypeWithArgumentsMap) / sizeof(KatanaNameToPseudoStruct);
644
+
645
+ for ( size_t i = 0; i < count; i++ ) {
646
+ const KatanaNameToPseudoStruct* res = lower_bound(pseudoTypeMap, count, pseudoTypeMap[i].string);
647
+ assert(pseudoTypeMap[i].type == res->type);
648
+ }
649
+
650
+ pseudoTypeMap = kPseudoTypeWithoutArgumentsMap;
651
+ count = sizeof(kPseudoTypeWithoutArgumentsMap) / sizeof(KatanaNameToPseudoStruct);
652
+
653
+ for ( size_t i = 0; i < count; i++ ) {
654
+ const KatanaNameToPseudoStruct* res = lower_bound(pseudoTypeMap, count, pseudoTypeMap[i].string);
655
+ assert(pseudoTypeMap[i].type == res->type);
656
+ }
657
+ }
658
+
659
+ #endif // #if KATANA_PARSER_DEBUG