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
8namespace 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

source code of gtk/subprojects/libsass/src/ast_sel_super.cpp