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 // ########################################################################## selectorPseudoNamed(CompoundSelectorObj compound,sass::string name)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>(sel)) { 44 if (pseudo->isClass() && pseudo->selector()) { 45 if (sel->name() == name) { 46 rv.push_back(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 // ########################################################################## simpleIsSuperselector(const SimpleSelectorObj & simple1,const SimpleSelectorObj & simple2)60 bool simpleIsSuperselector( 61 const SimpleSelectorObj& simple1, 62 const SimpleSelectorObj& simple2) 63 { 64 // If they are equal they are superselectors 65 if (ObjEqualityFn(simple1, simple2)) { 66 return true; 67 } 68 // Some selector pseudoclasses can match normal selectors. 69 if (const PseudoSelector* pseudo = Cast<PseudoSelector>(simple2)) { 70 if (pseudo->selector() && isSubselectorPseudo(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>(complex->at(0))) { 78 // It must contain the lhs simple selector 79 if (!compound->contains(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 // ########################################################################## simpleIsSuperselectorOfCompound(const SimpleSelectorObj & simple,const CompoundSelectorObj & compound)96 bool simpleIsSuperselectorOfCompound( 97 const SimpleSelectorObj& simple, 98 const CompoundSelectorObj& compound) 99 { 100 for (SimpleSelectorObj simple2 : compound->elements()) { 101 if (simpleIsSuperselector(simple, simple2)) { 102 return true; 103 } 104 } 105 return false; 106 } 107 // EO simpleIsSuperselectorOfCompound 108 109 // ########################################################################## 110 // ########################################################################## typeIsSuperselectorOfCompound(const TypeSelectorObj & type,const CompoundSelectorObj & compound)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>(simple)) { 117 if (*type != *rhs) return true; 118 } 119 } 120 return false; 121 } 122 // EO typeIsSuperselectorOfCompound 123 124 // ########################################################################## 125 // ########################################################################## idIsSuperselectorOfCompound(const IDSelectorObj & id,const CompoundSelectorObj & compound)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>(simple)) { 132 if (*id != *rhs) return true; 133 } 134 } 135 return false; 136 } 137 // EO idIsSuperselectorOfCompound 138 139 // ########################################################################## 140 // ########################################################################## pseudoIsSuperselectorOfPseudo(const PseudoSelectorObj & pseudo1,const PseudoSelectorObj & pseudo2,const ComplexSelectorObj & parent)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(list->elements(), { parent }); 151 } 152 return false; 153 } 154 // EO pseudoIsSuperselectorOfPseudo 155 156 // ########################################################################## 157 // ########################################################################## pseudoNotIsSuperselectorOfCompound(const PseudoSelectorObj & pseudo1,const CompoundSelectorObj & compound2,const ComplexSelectorObj & parent)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>(simple2)) { 165 if (const CompoundSelectorObj& compound1 = Cast<CompoundSelector>(parent->last())) { 166 if (typeIsSuperselectorOfCompound(type2, compound1)) return true; 167 } 168 } 169 else if (const IDSelectorObj& id2 = Cast<IDSelector>(simple2)) { 170 if (const CompoundSelectorObj& compound1 = Cast<CompoundSelector>(parent->last())) { 171 if (idIsSuperselectorOfCompound(id2, compound1)) return true; 172 } 173 } 174 else if (const PseudoSelectorObj& pseudo2 = Cast<PseudoSelector>(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 // ########################################################################## selectorPseudoIsSuperselector(const PseudoSelectorObj & pseudo1,const CompoundSelectorObj & compound2,sass::vector<SelectorComponentObj>::const_iterator parents_from,sass::vector<SelectorComponentObj>::const_iterator parents_to)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(pseudo1->name())); 201 202 if (name == "matches" || name == "any") { 203 sass::vector<PseudoSelectorObj> pseudos = 204 selectorPseudoNamed(compound2, pseudo1->name()); 205 SelectorListObj selector1 = pseudo1->selector(); 206 for (PseudoSelectorObj pseudo2 : pseudos) { 207 SelectorListObj selector = pseudo2->selector(); 208 if (selector1->isSuperselectorOf(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(*cur); 217 } 218 parents.push_back(compound2); 219 if (complexIsSuperselector(complex1->elements(), 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(compound2, pseudo1->name()); 228 SelectorListObj selector1 = pseudo1->selector(); 229 for (PseudoSelectorObj pseudo2 : pseudos) { 230 SelectorListObj selector = pseudo2->selector(); 231 if (selector1->isSuperselectorOf(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, complex)) return false; 240 } 241 return true; 242 } 243 else if (name == "current") { 244 sass::vector<PseudoSelectorObj> pseudos = 245 selectorPseudoNamed(compound2, "current"); 246 for (PseudoSelectorObj pseudo2 : pseudos) { 247 if (ObjEqualityFn(pseudo1, 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(pseudo1->argument(), pseudo2->argument())) continue; 256 if (pseudo1->selector()->isSuperselectorOf(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 // ########################################################################## compoundIsSuperselector(const CompoundSelectorObj & compound1,const CompoundSelectorObj & compound2,const sass::vector<SelectorComponentObj>::const_iterator parents_from,const sass::vector<SelectorComponentObj>::const_iterator parents_to)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>(simple1); 286 if (pseudo1 && pseudo1->selector()) { 287 if (!selectorPseudoIsSuperselector(pseudo1, compound2, parents_from, parents_to)) { 288 return false; 289 } 290 } 291 else if (!simpleIsSuperselectorOfCompound(simple1, 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>(simple2); 299 if (pseudo2 && pseudo2->isElement()) { 300 if (!simpleIsSuperselectorOfCompound(pseudo2, 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 // ########################################################################## compoundIsSuperselector(const CompoundSelectorObj & compound1,const CompoundSelectorObj & compound2,const sass::vector<SelectorComponentObj> & parents)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.begin(), 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 // ########################################################################## complexIsSuperselector(const sass::vector<SelectorComponentObj> & complex1,const sass::vector<SelectorComponentObj> & complex2)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>(complex1.back())) return false; 341 if (!complex2.empty() && Cast<SelectorCombinator>(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>(complex1[i1])) { 361 return false; 362 } 363 if (Cast<SelectorCombinator>(complex2[i2])) { 364 return false; 365 } 366 367 CompoundSelectorObj compound1 = Cast<CompoundSelector>(complex1[i1]); 368 CompoundSelectorObj compound2 = Cast<CompoundSelector>(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(parents_from, 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(*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>(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(parents_from, i2 + 1); // equivalent to dart `.skip` 400 std::advance(parents_to, 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>(component1); 414 SelectorCombinatorObj combinator2 = Cast<SelectorCombinator>(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 // ########################################################################## complexIsParentSuperselector(const sass::vector<SelectorComponentObj> & complex1,const sass::vector<SelectorComponentObj> & complex2)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>(complex1.front())) return false; 473 if (Cast<SelectorCombinator>(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(base); cplx2.push_back(base); 480 return complexIsSuperselector(cplx1, 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 // ########################################################################## listHasSuperslectorForComplex(sass::vector<ComplexSelectorObj> list,ComplexSelectorObj complex)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(lhs->elements(), 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 // ########################################################################## listIsSuperslector(const sass::vector<ComplexSelectorObj> & list1,const sass::vector<ComplexSelectorObj> & list2)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(list1, complex)) { 517 return false; 518 } 519 } 520 return true; 521 } 522 // EO listIsSuperslector 523 524 // ########################################################################## 525 // Implement selector methods (dispatch to functions) 526 // ########################################################################## isSuperselectorOf(const SelectorList * sub) const527 bool SelectorList::isSuperselectorOf(const SelectorList* sub) const 528 { 529 return listIsSuperslector(elements(), sub->elements()); 530 } isSuperselectorOf(const ComplexSelector * sub) const531 bool ComplexSelector::isSuperselectorOf(const ComplexSelector* sub) const 532 { 533 return complexIsSuperselector(elements(), sub->elements()); 534 } 535 536 // ########################################################################## 537 // ########################################################################## 538 539 } 540