1 | // sass.hpp must go before all system headers to get the |
2 | // __EXTENSIONS__ fix on Solaris. |
3 | #include "sass.hpp" |
4 | #include "ast.hpp" |
5 | |
6 | #include "util_string.hpp" |
7 | |
8 | namespace Sass { |
9 | |
10 | // ########################################################################## |
11 | // To compare/debug against libsass you can use debugger.hpp: |
12 | // c++: std::cerr << "result " << debug_vec(compound) << "\n"; |
13 | // dart: stderr.writeln("result " + compound.toString()); |
14 | // ########################################################################## |
15 | |
16 | // ########################################################################## |
17 | // Returns whether [list1] is a superselector of [list2]. |
18 | // That is, whether [list1] matches every element that |
19 | // [list2] matches, as well as possibly additional elements. |
20 | // ########################################################################## |
21 | bool listIsSuperslector( |
22 | const sass::vector<ComplexSelectorObj>& list1, |
23 | const sass::vector<ComplexSelectorObj>& list2); |
24 | |
25 | // ########################################################################## |
26 | // Returns whether [complex1] is a superselector of [complex2]. |
27 | // That is, whether [complex1] matches every element that |
28 | // [complex2] matches, as well as possibly additional elements. |
29 | // ########################################################################## |
30 | bool complexIsSuperselector( |
31 | const sass::vector<SelectorComponentObj>& complex1, |
32 | const sass::vector<SelectorComponentObj>& complex2); |
33 | |
34 | // ########################################################################## |
35 | // Returns all pseudo selectors in [compound] that have |
36 | // a selector argument, and that have the given [name]. |
37 | // ########################################################################## |
38 | sass::vector<PseudoSelectorObj> selectorPseudoNamed( |
39 | CompoundSelectorObj compound, sass::string name) |
40 | { |
41 | sass::vector<PseudoSelectorObj> rv; |
42 | for (SimpleSelectorObj sel : compound->elements()) { |
43 | if (PseudoSelectorObj pseudo = Cast<PseudoSelector>(ptr: sel)) { |
44 | if (pseudo->isClass() && pseudo->selector()) { |
45 | if (sel->name() == name) { |
46 | rv.push_back(x: sel); |
47 | } |
48 | } |
49 | } |
50 | } |
51 | return rv; |
52 | } |
53 | // EO selectorPseudoNamed |
54 | |
55 | // ########################################################################## |
56 | // Returns whether [simple1] is a superselector of [simple2]. |
57 | // That is, whether [simple1] matches every element that |
58 | // [simple2] matches, as well as possibly additional elements. |
59 | // ########################################################################## |
60 | bool simpleIsSuperselector( |
61 | const SimpleSelectorObj& simple1, |
62 | const SimpleSelectorObj& simple2) |
63 | { |
64 | // If they are equal they are superselectors |
65 | if (ObjEqualityFn(lhs: simple1, rhs: simple2)) { |
66 | return true; |
67 | } |
68 | // Some selector pseudoclasses can match normal selectors. |
69 | if (const PseudoSelector* pseudo = Cast<PseudoSelector>(ptr: simple2)) { |
70 | if (pseudo->selector() && isSubselectorPseudo(norm: pseudo->normalized())) { |
71 | for (auto complex : pseudo->selector()->elements()) { |
72 | // Make sure we have exacly one items |
73 | if (complex->length() != 1) { |
74 | return false; |
75 | } |
76 | // That items must be a compound selector |
77 | if (auto compound = Cast<CompoundSelector>(ptr: complex->at(i: 0))) { |
78 | // It must contain the lhs simple selector |
79 | if (!compound->contains(el: simple1)) { |
80 | return false; |
81 | } |
82 | } |
83 | } |
84 | return true; |
85 | } |
86 | } |
87 | return false; |
88 | } |
89 | // EO simpleIsSuperselector |
90 | |
91 | // ########################################################################## |
92 | // Returns whether [simple] is a superselector of [compound]. |
93 | // That is, whether [simple] matches every element that |
94 | // [compound] matches, as well as possibly additional elements. |
95 | // ########################################################################## |
96 | bool simpleIsSuperselectorOfCompound( |
97 | const SimpleSelectorObj& simple, |
98 | const CompoundSelectorObj& compound) |
99 | { |
100 | for (SimpleSelectorObj simple2 : compound->elements()) { |
101 | if (simpleIsSuperselector(simple1: simple, simple2)) { |
102 | return true; |
103 | } |
104 | } |
105 | return false; |
106 | } |
107 | // EO simpleIsSuperselectorOfCompound |
108 | |
109 | // ########################################################################## |
110 | // ########################################################################## |
111 | bool typeIsSuperselectorOfCompound( |
112 | const TypeSelectorObj& type, |
113 | const CompoundSelectorObj& compound) |
114 | { |
115 | for (const SimpleSelectorObj& simple : compound->elements()) { |
116 | if (const TypeSelectorObj& rhs = Cast<TypeSelector>(ptr: simple)) { |
117 | if (*type != *rhs) return true; |
118 | } |
119 | } |
120 | return false; |
121 | } |
122 | // EO typeIsSuperselectorOfCompound |
123 | |
124 | // ########################################################################## |
125 | // ########################################################################## |
126 | bool idIsSuperselectorOfCompound( |
127 | const IDSelectorObj& id, |
128 | const CompoundSelectorObj& compound) |
129 | { |
130 | for (const SimpleSelectorObj& simple : compound->elements()) { |
131 | if (const IDSelectorObj& rhs = Cast<IDSelector>(ptr: simple)) { |
132 | if (*id != *rhs) return true; |
133 | } |
134 | } |
135 | return false; |
136 | } |
137 | // EO idIsSuperselectorOfCompound |
138 | |
139 | // ########################################################################## |
140 | // ########################################################################## |
141 | bool pseudoIsSuperselectorOfPseudo( |
142 | const PseudoSelectorObj& pseudo1, |
143 | const PseudoSelectorObj& pseudo2, |
144 | const ComplexSelectorObj& parent |
145 | ) |
146 | { |
147 | if (!pseudo2->selector()) return false; |
148 | if (pseudo1->name() == pseudo2->name()) { |
149 | SelectorListObj list = pseudo2->selector(); |
150 | return listIsSuperslector(list1: list->elements(), list2: { parent }); |
151 | } |
152 | return false; |
153 | } |
154 | // EO pseudoIsSuperselectorOfPseudo |
155 | |
156 | // ########################################################################## |
157 | // ########################################################################## |
158 | bool pseudoNotIsSuperselectorOfCompound( |
159 | const PseudoSelectorObj& pseudo1, |
160 | const CompoundSelectorObj& compound2, |
161 | const ComplexSelectorObj& parent) |
162 | { |
163 | for (const SimpleSelectorObj& simple2 : compound2->elements()) { |
164 | if (const TypeSelectorObj& type2 = Cast<TypeSelector>(ptr: simple2)) { |
165 | if (const CompoundSelectorObj& compound1 = Cast<CompoundSelector>(ptr: parent->last())) { |
166 | if (typeIsSuperselectorOfCompound(type: type2, compound: compound1)) return true; |
167 | } |
168 | } |
169 | else if (const IDSelectorObj& id2 = Cast<IDSelector>(ptr: simple2)) { |
170 | if (const CompoundSelectorObj& compound1 = Cast<CompoundSelector>(ptr: parent->last())) { |
171 | if (idIsSuperselectorOfCompound(id: id2, compound: compound1)) return true; |
172 | } |
173 | } |
174 | else if (const PseudoSelectorObj& pseudo2 = Cast<PseudoSelector>(ptr: simple2)) { |
175 | if (pseudoIsSuperselectorOfPseudo(pseudo1, pseudo2, parent)) return true; |
176 | } |
177 | } |
178 | return false; |
179 | } |
180 | // pseudoNotIsSuperselectorOfCompound |
181 | |
182 | // ########################################################################## |
183 | // Returns whether [pseudo1] is a superselector of [compound2]. |
184 | // That is, whether [pseudo1] matches every element that [compound2] |
185 | // matches, as well as possibly additional elements. This assumes that |
186 | // [pseudo1]'s `selector` argument is not `null`. If [parents] is passed, |
187 | // it represents the parents of [compound2]. This is relevant for pseudo |
188 | // selectors with selector arguments, where we may need to know if the |
189 | // parent selectors in the selector argument match [parents]. |
190 | // ########################################################################## |
191 | bool selectorPseudoIsSuperselector( |
192 | const PseudoSelectorObj& pseudo1, |
193 | const CompoundSelectorObj& compound2, |
194 | // ToDo: is this really the most convenient way to do this? |
195 | sass::vector<SelectorComponentObj>::const_iterator parents_from, |
196 | sass::vector<SelectorComponentObj>::const_iterator parents_to) |
197 | { |
198 | |
199 | // ToDo: move normalization function |
200 | sass::string name(Util::unvendor(name: pseudo1->name())); |
201 | |
202 | if (name == "matches" || name == "any" ) { |
203 | sass::vector<PseudoSelectorObj> pseudos = |
204 | selectorPseudoNamed(compound: compound2, name: pseudo1->name()); |
205 | SelectorListObj selector1 = pseudo1->selector(); |
206 | for (PseudoSelectorObj pseudo2 : pseudos) { |
207 | SelectorListObj selector = pseudo2->selector(); |
208 | if (selector1->isSuperselectorOf(sub: selector)) { |
209 | return true; |
210 | } |
211 | } |
212 | |
213 | for (ComplexSelectorObj complex1 : selector1->elements()) { |
214 | sass::vector<SelectorComponentObj> parents; |
215 | for (auto cur = parents_from; cur != parents_to; cur++) { |
216 | parents.push_back(x: *cur); |
217 | } |
218 | parents.push_back(x: compound2); |
219 | if (complexIsSuperselector(complex1: complex1->elements(), complex2: parents)) { |
220 | return true; |
221 | } |
222 | } |
223 | |
224 | } |
225 | else if (name == "has" || name == "host" || name == "host-context" || name == "slotted" ) { |
226 | sass::vector<PseudoSelectorObj> pseudos = |
227 | selectorPseudoNamed(compound: compound2, name: pseudo1->name()); |
228 | SelectorListObj selector1 = pseudo1->selector(); |
229 | for (PseudoSelectorObj pseudo2 : pseudos) { |
230 | SelectorListObj selector = pseudo2->selector(); |
231 | if (selector1->isSuperselectorOf(sub: selector)) { |
232 | return true; |
233 | } |
234 | } |
235 | |
236 | } |
237 | else if (name == "not" ) { |
238 | for (ComplexSelectorObj complex : pseudo1->selector()->elements()) { |
239 | if (!pseudoNotIsSuperselectorOfCompound(pseudo1, compound2, parent: complex)) return false; |
240 | } |
241 | return true; |
242 | } |
243 | else if (name == "current" ) { |
244 | sass::vector<PseudoSelectorObj> pseudos = |
245 | selectorPseudoNamed(compound: compound2, name: "current" ); |
246 | for (PseudoSelectorObj pseudo2 : pseudos) { |
247 | if (ObjEqualityFn(lhs: pseudo1, rhs: pseudo2)) return true; |
248 | } |
249 | |
250 | } |
251 | else if (name == "nth-child" || name == "nth-last-child" ) { |
252 | for (auto simple2 : compound2->elements()) { |
253 | if (PseudoSelectorObj pseudo2 = simple2->getPseudoSelector()) { |
254 | if (pseudo1->name() != pseudo2->name()) continue; |
255 | if (!ObjEqualityFn(lhs: pseudo1->argument(), rhs: pseudo2->argument())) continue; |
256 | if (pseudo1->selector()->isSuperselectorOf(sub: pseudo2->selector())) return true; |
257 | } |
258 | } |
259 | return false; |
260 | } |
261 | |
262 | return false; |
263 | |
264 | } |
265 | // EO selectorPseudoIsSuperselector |
266 | |
267 | // ########################################################################## |
268 | // Returns whether [compound1] is a superselector of [compound2]. |
269 | // That is, whether [compound1] matches every element that [compound2] |
270 | // matches, as well as possibly additional elements. If [parents] is |
271 | // passed, it represents the parents of [compound2]. This is relevant |
272 | // for pseudo selectors with selector arguments, where we may need to |
273 | // know if the parent selectors in the selector argument match [parents]. |
274 | // ########################################################################## |
275 | bool compoundIsSuperselector( |
276 | const CompoundSelectorObj& compound1, |
277 | const CompoundSelectorObj& compound2, |
278 | // ToDo: is this really the most convenient way to do this? |
279 | const sass::vector<SelectorComponentObj>::const_iterator parents_from, |
280 | const sass::vector<SelectorComponentObj>::const_iterator parents_to) |
281 | { |
282 | // Every selector in [compound1.components] must have |
283 | // a matching selector in [compound2.components]. |
284 | for (SimpleSelectorObj simple1 : compound1->elements()) { |
285 | PseudoSelectorObj pseudo1 = Cast<PseudoSelector>(ptr: simple1); |
286 | if (pseudo1 && pseudo1->selector()) { |
287 | if (!selectorPseudoIsSuperselector(pseudo1, compound2, parents_from, parents_to)) { |
288 | return false; |
289 | } |
290 | } |
291 | else if (!simpleIsSuperselectorOfCompound(simple: simple1, compound: compound2)) { |
292 | return false; |
293 | } |
294 | } |
295 | // [compound1] can't be a superselector of a selector |
296 | // with pseudo-elements that [compound2] doesn't share. |
297 | for (SimpleSelectorObj simple2 : compound2->elements()) { |
298 | PseudoSelectorObj pseudo2 = Cast<PseudoSelector>(ptr: simple2); |
299 | if (pseudo2 && pseudo2->isElement()) { |
300 | if (!simpleIsSuperselectorOfCompound(simple: pseudo2, compound: compound1)) { |
301 | return false; |
302 | } |
303 | } |
304 | } |
305 | return true; |
306 | } |
307 | // EO compoundIsSuperselector |
308 | |
309 | // ########################################################################## |
310 | // Returns whether [compound1] is a superselector of [compound2]. |
311 | // That is, whether [compound1] matches every element that [compound2] |
312 | // matches, as well as possibly additional elements. If [parents] is |
313 | // passed, it represents the parents of [compound2]. This is relevant |
314 | // for pseudo selectors with selector arguments, where we may need to |
315 | // know if the parent selectors in the selector argument match [parents]. |
316 | // ########################################################################## |
317 | bool compoundIsSuperselector( |
318 | const CompoundSelectorObj& compound1, |
319 | const CompoundSelectorObj& compound2, |
320 | const sass::vector<SelectorComponentObj>& parents) |
321 | { |
322 | return compoundIsSuperselector( |
323 | compound1, compound2, |
324 | parents_from: parents.begin(), parents_to: parents.end() |
325 | ); |
326 | } |
327 | // EO compoundIsSuperselector |
328 | |
329 | // ########################################################################## |
330 | // Returns whether [complex1] is a superselector of [complex2]. |
331 | // That is, whether [complex1] matches every element that |
332 | // [complex2] matches, as well as possibly additional elements. |
333 | // ########################################################################## |
334 | bool complexIsSuperselector( |
335 | const sass::vector<SelectorComponentObj>& complex1, |
336 | const sass::vector<SelectorComponentObj>& complex2) |
337 | { |
338 | |
339 | // Selectors with trailing operators are neither superselectors nor subselectors. |
340 | if (!complex1.empty() && Cast<SelectorCombinator>(ptr: complex1.back())) return false; |
341 | if (!complex2.empty() && Cast<SelectorCombinator>(ptr: complex2.back())) return false; |
342 | |
343 | size_t i1 = 0, i2 = 0; |
344 | while (true) { |
345 | |
346 | size_t remaining1 = complex1.size() - i1; |
347 | size_t remaining2 = complex2.size() - i2; |
348 | |
349 | if (remaining1 == 0 || remaining2 == 0) { |
350 | return false; |
351 | } |
352 | // More complex selectors are never |
353 | // superselectors of less complex ones. |
354 | if (remaining1 > remaining2) { |
355 | return false; |
356 | } |
357 | |
358 | // Selectors with leading operators are |
359 | // neither superselectors nor subselectors. |
360 | if (Cast<SelectorCombinator>(ptr: complex1[i1])) { |
361 | return false; |
362 | } |
363 | if (Cast<SelectorCombinator>(ptr: complex2[i2])) { |
364 | return false; |
365 | } |
366 | |
367 | CompoundSelectorObj compound1 = Cast<CompoundSelector>(ptr: complex1[i1]); |
368 | CompoundSelectorObj compound2 = Cast<CompoundSelector>(ptr: complex2.back()); |
369 | |
370 | if (remaining1 == 1) { |
371 | sass::vector<SelectorComponentObj>::const_iterator parents_to = complex2.end(); |
372 | sass::vector<SelectorComponentObj>::const_iterator parents_from = complex2.begin(); |
373 | std::advance(i&: parents_from, n: i2 + 1); // equivalent to dart `.skip(i2 + 1)` |
374 | bool rv = compoundIsSuperselector(compound1, compound2, parents_from, parents_to); |
375 | sass::vector<SelectorComponentObj> pp; |
376 | |
377 | sass::vector<SelectorComponentObj>::const_iterator end = parents_to; |
378 | sass::vector<SelectorComponentObj>::const_iterator beg = parents_from; |
379 | while (beg != end) { |
380 | pp.push_back(x: *beg); |
381 | beg++; |
382 | } |
383 | |
384 | return rv; |
385 | } |
386 | |
387 | // Find the first index where `complex2.sublist(i2, afterSuperselector)` |
388 | // is a subselector of [compound1]. We stop before the superselector |
389 | // would encompass all of [complex2] because we know [complex1] has |
390 | // more than one element, and consuming all of [complex2] wouldn't |
391 | // leave anything for the rest of [complex1] to match. |
392 | size_t afterSuperselector = i2 + 1; |
393 | for (; afterSuperselector < complex2.size(); afterSuperselector++) { |
394 | SelectorComponentObj component2 = complex2[afterSuperselector - 1]; |
395 | if (CompoundSelectorObj compound2 = Cast<CompoundSelector>(ptr: component2)) { |
396 | sass::vector<SelectorComponentObj>::const_iterator parents_to = complex2.begin(); |
397 | sass::vector<SelectorComponentObj>::const_iterator parents_from = complex2.begin(); |
398 | // complex2.take(afterSuperselector - 1).skip(i2 + 1) |
399 | std::advance(i&: parents_from, n: i2 + 1); // equivalent to dart `.skip` |
400 | std::advance(i&: parents_to, n: afterSuperselector); // equivalent to dart `.take` |
401 | if (compoundIsSuperselector(compound1, compound2, parents_from, parents_to)) { |
402 | break; |
403 | } |
404 | } |
405 | } |
406 | if (afterSuperselector == complex2.size()) { |
407 | return false; |
408 | } |
409 | |
410 | SelectorComponentObj component1 = complex1[i1 + 1], |
411 | component2 = complex2[afterSuperselector]; |
412 | |
413 | SelectorCombinatorObj combinator1 = Cast<SelectorCombinator>(ptr: component1); |
414 | SelectorCombinatorObj combinator2 = Cast<SelectorCombinator>(ptr: component2); |
415 | |
416 | if (!combinator1.isNull()) { |
417 | |
418 | if (combinator2.isNull()) { |
419 | return false; |
420 | } |
421 | // `.a ~ .b` is a superselector of `.a + .b`, |
422 | // but otherwise the combinators must match. |
423 | if (combinator1->isGeneralCombinator()) { |
424 | if (combinator2->isChildCombinator()) { |
425 | return false; |
426 | } |
427 | } |
428 | else if (*combinator1 != *combinator2) { |
429 | return false; |
430 | } |
431 | |
432 | // `.foo > .baz` is not a superselector of `.foo > .bar > .baz` or |
433 | // `.foo > .bar .baz`, despite the fact that `.baz` is a superselector of |
434 | // `.bar > .baz` and `.bar .baz`. Same goes for `+` and `~`. |
435 | if (remaining1 == 3 && remaining2 > 3) { |
436 | return false; |
437 | } |
438 | |
439 | i1 += 2; i2 = afterSuperselector + 1; |
440 | |
441 | } |
442 | else if (!combinator2.isNull()) { |
443 | if (!combinator2->isChildCombinator()) { |
444 | return false; |
445 | } |
446 | i1 += 1; i2 = afterSuperselector + 1; |
447 | } |
448 | else { |
449 | i1 += 1; i2 = afterSuperselector; |
450 | } |
451 | } |
452 | |
453 | return false; |
454 | |
455 | } |
456 | // EO complexIsSuperselector |
457 | |
458 | // ########################################################################## |
459 | // Like [complexIsSuperselector], but compares [complex1] |
460 | // and [complex2] as though they shared an implicit base |
461 | // [SimpleSelector]. For example, `B` is not normally a |
462 | // superselector of `B A`, since it doesn't match elements |
463 | // that match `A`. However, it *is* a parent superselector, |
464 | // since `B X` is a superselector of `B A X`. |
465 | // ########################################################################## |
466 | bool complexIsParentSuperselector( |
467 | const sass::vector<SelectorComponentObj>& complex1, |
468 | const sass::vector<SelectorComponentObj>& complex2) |
469 | { |
470 | // Try some simple heuristics to see if we can avoid allocations. |
471 | if (complex1.empty() && complex2.empty()) return false; |
472 | if (Cast<SelectorCombinator>(ptr: complex1.front())) return false; |
473 | if (Cast<SelectorCombinator>(ptr: complex2.front())) return false; |
474 | if (complex1.size() > complex2.size()) return false; |
475 | // TODO(nweiz): There's got to be a way to do this without a bunch of extra allocations... |
476 | sass::vector<SelectorComponentObj> cplx1(complex1); |
477 | sass::vector<SelectorComponentObj> cplx2(complex2); |
478 | CompoundSelectorObj base = SASS_MEMORY_NEW(CompoundSelector, "[tmp]" ); |
479 | cplx1.push_back(x: base); cplx2.push_back(x: base); |
480 | return complexIsSuperselector(complex1: cplx1, complex2: cplx2); |
481 | } |
482 | // EO complexIsParentSuperselector |
483 | |
484 | // ########################################################################## |
485 | // Returns whether [list] has a superselector for [complex]. |
486 | // That is, whether an item in [list] matches every element that |
487 | // [complex] matches, as well as possibly additional elements. |
488 | // ########################################################################## |
489 | bool listHasSuperslectorForComplex( |
490 | sass::vector<ComplexSelectorObj> list, |
491 | ComplexSelectorObj complex) |
492 | { |
493 | // Return true if every [complex] selector on [list2] |
494 | // is a super selector of the full selector [list1]. |
495 | for (ComplexSelectorObj lhs : list) { |
496 | if (complexIsSuperselector(complex1: lhs->elements(), complex2: complex->elements())) { |
497 | return true; |
498 | } |
499 | } |
500 | return false; |
501 | } |
502 | // listIsSuperslectorOfComplex |
503 | |
504 | // ########################################################################## |
505 | // Returns whether [list1] is a superselector of [list2]. |
506 | // That is, whether [list1] matches every element that |
507 | // [list2] matches, as well as possibly additional elements. |
508 | // ########################################################################## |
509 | bool listIsSuperslector( |
510 | const sass::vector<ComplexSelectorObj>& list1, |
511 | const sass::vector<ComplexSelectorObj>& list2) |
512 | { |
513 | // Return true if every [complex] selector on [list2] |
514 | // is a super selector of the full selector [list1]. |
515 | for (ComplexSelectorObj complex : list2) { |
516 | if (!listHasSuperslectorForComplex(list: list1, complex)) { |
517 | return false; |
518 | } |
519 | } |
520 | return true; |
521 | } |
522 | // EO listIsSuperslector |
523 | |
524 | // ########################################################################## |
525 | // Implement selector methods (dispatch to functions) |
526 | // ########################################################################## |
527 | bool SelectorList::isSuperselectorOf(const SelectorList* sub) const |
528 | { |
529 | return listIsSuperslector(list1: elements(), list2: sub->elements()); |
530 | } |
531 | bool ComplexSelector::isSuperselectorOf(const ComplexSelector* sub) const |
532 | { |
533 | return complexIsSuperselector(complex1: elements(), complex2: sub->elements()); |
534 | } |
535 | |
536 | // ########################################################################## |
537 | // ########################################################################## |
538 | |
539 | } |
540 | |