1 | // sass.hpp must go before all system headers to get the |
2 | // __EXTENSIONS__ fix on Solaris. |
3 | #include "sass.hpp" |
4 | |
5 | #include "ast.hpp" |
6 | #include "permutate.hpp" |
7 | #include "util_string.hpp" |
8 | |
9 | namespace Sass { |
10 | |
11 | ///////////////////////////////////////////////////////////////////////// |
12 | ///////////////////////////////////////////////////////////////////////// |
13 | |
14 | Selector::Selector(SourceSpan pstate) |
15 | : Expression(pstate), |
16 | hash_(0) |
17 | { concrete_type(concrete_type__: SELECTOR); } |
18 | |
19 | Selector::Selector(const Selector* ptr) |
20 | : Expression(ptr), |
21 | hash_(ptr->hash_) |
22 | { concrete_type(concrete_type__: SELECTOR); } |
23 | |
24 | |
25 | bool Selector::has_real_parent_ref() const |
26 | { |
27 | return false; |
28 | } |
29 | |
30 | ///////////////////////////////////////////////////////////////////////// |
31 | ///////////////////////////////////////////////////////////////////////// |
32 | |
33 | Selector_Schema::Selector_Schema(SourceSpan pstate, String_Obj c) |
34 | : AST_Node(pstate), |
35 | contents_(c), |
36 | connect_parent_(true), |
37 | hash_(0) |
38 | { } |
39 | Selector_Schema::Selector_Schema(const Selector_Schema* ptr) |
40 | : AST_Node(ptr), |
41 | contents_(ptr->contents_), |
42 | connect_parent_(ptr->connect_parent_), |
43 | hash_(ptr->hash_) |
44 | { } |
45 | |
46 | unsigned long Selector_Schema::specificity() const |
47 | { |
48 | return 0; |
49 | } |
50 | |
51 | size_t Selector_Schema::hash() const { |
52 | if (hash_ == 0) { |
53 | hash_combine(seed&: hash_, val: contents_->hash()); |
54 | } |
55 | return hash_; |
56 | } |
57 | |
58 | bool Selector_Schema::has_real_parent_ref() const |
59 | { |
60 | // Note: disabled since it does not seem to do anything? |
61 | // if (String_Schema_Obj schema = Cast<String_Schema>(contents())) { |
62 | // if (schema->empty()) return false; |
63 | // const auto first = schema->first(); |
64 | // return Cast<Parent_Reference>(first); |
65 | // } |
66 | return false; |
67 | } |
68 | |
69 | ///////////////////////////////////////////////////////////////////////// |
70 | ///////////////////////////////////////////////////////////////////////// |
71 | |
72 | SimpleSelector::SimpleSelector(SourceSpan pstate, sass::string n) |
73 | : Selector(pstate), ns_("" ), name_(n), has_ns_(false) |
74 | { |
75 | size_t pos = n.find(c: '|'); |
76 | // found some namespace |
77 | if (pos != sass::string::npos) { |
78 | has_ns_ = true; |
79 | ns_ = n.substr(pos: 0, n: pos); |
80 | name_ = n.substr(pos: pos + 1); |
81 | } |
82 | } |
83 | SimpleSelector::SimpleSelector(const SimpleSelector* ptr) |
84 | : Selector(ptr), |
85 | ns_(ptr->ns_), |
86 | name_(ptr->name_), |
87 | has_ns_(ptr->has_ns_) |
88 | { } |
89 | |
90 | sass::string SimpleSelector::ns_name() const |
91 | { |
92 | if (!has_ns_) return name_; |
93 | else return ns_ + "|" + name_; |
94 | } |
95 | |
96 | size_t SimpleSelector::hash() const |
97 | { |
98 | if (hash_ == 0) { |
99 | hash_combine(seed&: hash_, val: name()); |
100 | hash_combine(seed&: hash_, val: (int)SELECTOR); |
101 | hash_combine(seed&: hash_, val: (int)simple_type()); |
102 | if (has_ns_) hash_combine(seed&: hash_, val: ns()); |
103 | } |
104 | return hash_; |
105 | } |
106 | |
107 | bool SimpleSelector::empty() const { |
108 | return ns().empty() && name().empty(); |
109 | } |
110 | |
111 | // namespace compare functions |
112 | bool SimpleSelector::is_ns_eq(const SimpleSelector& r) const |
113 | { |
114 | return has_ns_ == r.has_ns_ && ns_ == r.ns_; |
115 | } |
116 | |
117 | // namespace query functions |
118 | bool SimpleSelector::is_universal_ns() const |
119 | { |
120 | return has_ns_ && ns_ == "*" ; |
121 | } |
122 | |
123 | bool SimpleSelector::is_empty_ns() const |
124 | { |
125 | return !has_ns_ || ns_ == "" ; |
126 | } |
127 | |
128 | bool SimpleSelector::has_empty_ns() const |
129 | { |
130 | return has_ns_ && ns_ == "" ; |
131 | } |
132 | |
133 | bool SimpleSelector::has_qualified_ns() const |
134 | { |
135 | return has_ns_ && ns_ != "" && ns_ != "*" ; |
136 | } |
137 | |
138 | // name query functions |
139 | bool SimpleSelector::is_universal() const |
140 | { |
141 | return name_ == "*" ; |
142 | } |
143 | |
144 | bool SimpleSelector::has_placeholder() |
145 | { |
146 | return false; |
147 | } |
148 | |
149 | bool SimpleSelector::has_real_parent_ref() const |
150 | { |
151 | return false; |
152 | }; |
153 | |
154 | bool SimpleSelector::is_pseudo_element() const |
155 | { |
156 | return false; |
157 | } |
158 | |
159 | CompoundSelectorObj SimpleSelector::wrapInCompound() |
160 | { |
161 | CompoundSelectorObj selector = |
162 | SASS_MEMORY_NEW(CompoundSelector, pstate()); |
163 | selector->append(element: this); |
164 | return selector; |
165 | } |
166 | ComplexSelectorObj SimpleSelector::wrapInComplex() |
167 | { |
168 | ComplexSelectorObj selector = |
169 | SASS_MEMORY_NEW(ComplexSelector, pstate()); |
170 | selector->append(element: wrapInCompound()); |
171 | return selector; |
172 | } |
173 | |
174 | ///////////////////////////////////////////////////////////////////////// |
175 | ///////////////////////////////////////////////////////////////////////// |
176 | |
177 | PlaceholderSelector::PlaceholderSelector(SourceSpan pstate, sass::string n) |
178 | : SimpleSelector(pstate, n) |
179 | { simple_type(simple_type__: PLACEHOLDER_SEL); } |
180 | PlaceholderSelector::PlaceholderSelector(const PlaceholderSelector* ptr) |
181 | : SimpleSelector(ptr) |
182 | { simple_type(simple_type__: PLACEHOLDER_SEL); } |
183 | unsigned long PlaceholderSelector::specificity() const |
184 | { |
185 | return Constants::Specificity_Base; |
186 | } |
187 | bool PlaceholderSelector::has_placeholder() { |
188 | return true; |
189 | } |
190 | |
191 | ///////////////////////////////////////////////////////////////////////// |
192 | ///////////////////////////////////////////////////////////////////////// |
193 | |
194 | TypeSelector::TypeSelector(SourceSpan pstate, sass::string n) |
195 | : SimpleSelector(pstate, n) |
196 | { simple_type(simple_type__: TYPE_SEL); } |
197 | TypeSelector::TypeSelector(const TypeSelector* ptr) |
198 | : SimpleSelector(ptr) |
199 | { simple_type(simple_type__: TYPE_SEL); } |
200 | |
201 | unsigned long TypeSelector::specificity() const |
202 | { |
203 | if (name() == "*" ) return 0; |
204 | else return Constants::Specificity_Element; |
205 | } |
206 | |
207 | ///////////////////////////////////////////////////////////////////////// |
208 | ///////////////////////////////////////////////////////////////////////// |
209 | |
210 | ClassSelector::ClassSelector(SourceSpan pstate, sass::string n) |
211 | : SimpleSelector(pstate, n) |
212 | { simple_type(simple_type__: CLASS_SEL); } |
213 | ClassSelector::ClassSelector(const ClassSelector* ptr) |
214 | : SimpleSelector(ptr) |
215 | { simple_type(simple_type__: CLASS_SEL); } |
216 | |
217 | unsigned long ClassSelector::specificity() const |
218 | { |
219 | return Constants::Specificity_Class; |
220 | } |
221 | |
222 | ///////////////////////////////////////////////////////////////////////// |
223 | ///////////////////////////////////////////////////////////////////////// |
224 | |
225 | IDSelector::IDSelector(SourceSpan pstate, sass::string n) |
226 | : SimpleSelector(pstate, n) |
227 | { simple_type(simple_type__: ID_SEL); } |
228 | IDSelector::IDSelector(const IDSelector* ptr) |
229 | : SimpleSelector(ptr) |
230 | { simple_type(simple_type__: ID_SEL); } |
231 | |
232 | unsigned long IDSelector::specificity() const |
233 | { |
234 | return Constants::Specificity_ID; |
235 | } |
236 | |
237 | ///////////////////////////////////////////////////////////////////////// |
238 | ///////////////////////////////////////////////////////////////////////// |
239 | |
240 | AttributeSelector::AttributeSelector(SourceSpan pstate, sass::string n, sass::string m, String_Obj v, char o) |
241 | : SimpleSelector(pstate, n), matcher_(m), value_(v), modifier_(o) |
242 | { simple_type(simple_type__: ATTRIBUTE_SEL); } |
243 | AttributeSelector::AttributeSelector(const AttributeSelector* ptr) |
244 | : SimpleSelector(ptr), |
245 | matcher_(ptr->matcher_), |
246 | value_(ptr->value_), |
247 | modifier_(ptr->modifier_) |
248 | { simple_type(simple_type__: ATTRIBUTE_SEL); } |
249 | |
250 | size_t AttributeSelector::hash() const |
251 | { |
252 | if (hash_ == 0) { |
253 | hash_combine(seed&: hash_, val: SimpleSelector::hash()); |
254 | hash_combine(seed&: hash_, val: std::hash<sass::string>()(matcher())); |
255 | if (value_) hash_combine(seed&: hash_, val: value_->hash()); |
256 | } |
257 | return hash_; |
258 | } |
259 | |
260 | unsigned long AttributeSelector::specificity() const |
261 | { |
262 | return Constants::Specificity_Attr; |
263 | } |
264 | |
265 | ///////////////////////////////////////////////////////////////////////// |
266 | ///////////////////////////////////////////////////////////////////////// |
267 | |
268 | PseudoSelector::PseudoSelector(SourceSpan pstate, sass::string name, bool element) |
269 | : SimpleSelector(pstate, name), |
270 | normalized_(Util::unvendor(name)), |
271 | argument_({}), |
272 | selector_({}), |
273 | isSyntacticClass_(!element), |
274 | isClass_(!element && !isFakePseudoElement(name: normalized_)) |
275 | { simple_type(simple_type__: PSEUDO_SEL); } |
276 | PseudoSelector::PseudoSelector(const PseudoSelector* ptr) |
277 | : SimpleSelector(ptr), |
278 | normalized_(ptr->normalized()), |
279 | argument_(ptr->argument()), |
280 | selector_(ptr->selector()), |
281 | isSyntacticClass_(ptr->isSyntacticClass()), |
282 | isClass_(ptr->isClass()) |
283 | { simple_type(simple_type__: PSEUDO_SEL); } |
284 | |
285 | // A pseudo-element is made of two colons (::) followed by the name. |
286 | // The `::` notation is introduced by the current document in order to |
287 | // establish a discrimination between pseudo-classes and pseudo-elements. |
288 | // For compatibility with existing style sheets, user agents must also |
289 | // accept the previous one-colon notation for pseudo-elements introduced |
290 | // in CSS levels 1 and 2 (namely, :first-line, :first-letter, :before and |
291 | // :after). This compatibility is not allowed for the new pseudo-elements |
292 | // introduced in this specification. |
293 | bool PseudoSelector::is_pseudo_element() const |
294 | { |
295 | return isElement(); |
296 | } |
297 | |
298 | size_t PseudoSelector::hash() const |
299 | { |
300 | if (hash_ == 0) { |
301 | hash_combine(seed&: hash_, val: SimpleSelector::hash()); |
302 | if (selector_) hash_combine(seed&: hash_, val: selector_->hash()); |
303 | if (argument_) hash_combine(seed&: hash_, val: argument_->hash()); |
304 | } |
305 | return hash_; |
306 | } |
307 | |
308 | unsigned long PseudoSelector::specificity() const |
309 | { |
310 | if (is_pseudo_element()) |
311 | return Constants::Specificity_Element; |
312 | return Constants::Specificity_Pseudo; |
313 | } |
314 | |
315 | PseudoSelectorObj PseudoSelector::withSelector(SelectorListObj selector) |
316 | { |
317 | PseudoSelectorObj pseudo = SASS_MEMORY_COPY(this); |
318 | pseudo->selector(selector__: selector); |
319 | return pseudo; |
320 | } |
321 | |
322 | bool PseudoSelector::empty() const |
323 | { |
324 | // Only considered empty if selector is |
325 | // available but has no items in it. |
326 | return selector() && selector()->empty(); |
327 | } |
328 | |
329 | void PseudoSelector::cloneChildren() |
330 | { |
331 | if (selector().isNull()) selector(selector__: {}); |
332 | else selector(SASS_MEMORY_CLONE(selector())); |
333 | } |
334 | |
335 | bool PseudoSelector::has_real_parent_ref() const { |
336 | if (!selector()) return false; |
337 | return selector()->has_real_parent_ref(); |
338 | } |
339 | |
340 | ///////////////////////////////////////////////////////////////////////// |
341 | ///////////////////////////////////////////////////////////////////////// |
342 | |
343 | SelectorList::SelectorList(SourceSpan pstate, size_t s) |
344 | : Selector(pstate), |
345 | Vectorized<ComplexSelectorObj>(s), |
346 | is_optional_(false) |
347 | { } |
348 | SelectorList::SelectorList(const SelectorList* ptr) |
349 | : Selector(ptr), |
350 | Vectorized<ComplexSelectorObj>(*ptr), |
351 | is_optional_(ptr->is_optional_) |
352 | { } |
353 | |
354 | size_t SelectorList::hash() const |
355 | { |
356 | if (Selector::hash_ == 0) { |
357 | hash_combine(seed&: Selector::hash_, val: Vectorized::hash()); |
358 | } |
359 | return Selector::hash_; |
360 | } |
361 | |
362 | bool SelectorList::has_real_parent_ref() const |
363 | { |
364 | for (ComplexSelectorObj s : elements()) { |
365 | if (s && s->has_real_parent_ref()) return true; |
366 | } |
367 | return false; |
368 | } |
369 | |
370 | void SelectorList::cloneChildren() |
371 | { |
372 | for (size_t i = 0, l = length(); i < l; i++) { |
373 | at(i) = SASS_MEMORY_CLONE(at(i)); |
374 | } |
375 | } |
376 | |
377 | unsigned long SelectorList::specificity() const |
378 | { |
379 | return 0; |
380 | } |
381 | |
382 | bool SelectorList::isInvisible() const |
383 | { |
384 | if (length() == 0) return true; |
385 | for (size_t i = 0; i < length(); i += 1) { |
386 | if (get(i)->isInvisible()) return true; |
387 | } |
388 | return false; |
389 | } |
390 | |
391 | ///////////////////////////////////////////////////////////////////////// |
392 | ///////////////////////////////////////////////////////////////////////// |
393 | |
394 | ComplexSelector::ComplexSelector(SourceSpan pstate) |
395 | : Selector(pstate), |
396 | Vectorized<SelectorComponentObj>(), |
397 | chroots_(false), |
398 | hasPreLineFeed_(false) |
399 | { |
400 | } |
401 | ComplexSelector::ComplexSelector(const ComplexSelector* ptr) |
402 | : Selector(ptr), |
403 | Vectorized<SelectorComponentObj>(ptr->elements()), |
404 | chroots_(ptr->chroots()), |
405 | hasPreLineFeed_(ptr->hasPreLineFeed()) |
406 | { |
407 | } |
408 | |
409 | void ComplexSelector::cloneChildren() |
410 | { |
411 | for (size_t i = 0, l = length(); i < l; i++) { |
412 | at(i) = SASS_MEMORY_CLONE(at(i)); |
413 | } |
414 | } |
415 | |
416 | unsigned long ComplexSelector::specificity() const |
417 | { |
418 | int sum = 0; |
419 | for (auto component : elements()) { |
420 | sum += component->specificity(); |
421 | } |
422 | return sum; |
423 | } |
424 | |
425 | bool ComplexSelector::isInvisible() const |
426 | { |
427 | if (length() == 0) return true; |
428 | for (size_t i = 0; i < length(); i += 1) { |
429 | if (CompoundSelectorObj compound = get(i)->getCompound()) { |
430 | if (compound->isInvisible()) return true; |
431 | } |
432 | } |
433 | return false; |
434 | } |
435 | |
436 | SelectorListObj ComplexSelector::wrapInList() |
437 | { |
438 | SelectorListObj selector = |
439 | SASS_MEMORY_NEW(SelectorList, pstate()); |
440 | selector->append(element: this); |
441 | return selector; |
442 | } |
443 | |
444 | size_t ComplexSelector::hash() const |
445 | { |
446 | if (Selector::hash_ == 0) { |
447 | hash_combine(seed&: Selector::hash_, val: Vectorized::hash()); |
448 | // ToDo: this breaks some extend lookup |
449 | // hash_combine(Selector::hash_, chroots_); |
450 | } |
451 | return Selector::hash_; |
452 | } |
453 | |
454 | bool ComplexSelector::has_placeholder() const { |
455 | for (size_t i = 0, L = length(); i < L; ++i) { |
456 | if (get(i)->has_placeholder()) return true; |
457 | } |
458 | return false; |
459 | } |
460 | |
461 | bool ComplexSelector::has_real_parent_ref() const |
462 | { |
463 | for (auto item : elements()) { |
464 | if (item->has_real_parent_ref()) return true; |
465 | } |
466 | return false; |
467 | } |
468 | |
469 | ///////////////////////////////////////////////////////////////////////// |
470 | ///////////////////////////////////////////////////////////////////////// |
471 | |
472 | SelectorComponent::SelectorComponent(SourceSpan pstate, bool postLineBreak) |
473 | : Selector(pstate), |
474 | hasPostLineBreak_(postLineBreak) |
475 | { |
476 | } |
477 | |
478 | SelectorComponent::SelectorComponent(const SelectorComponent* ptr) |
479 | : Selector(ptr), |
480 | hasPostLineBreak_(ptr->hasPostLineBreak()) |
481 | { } |
482 | |
483 | void SelectorComponent::cloneChildren() |
484 | { |
485 | } |
486 | |
487 | unsigned long SelectorComponent::specificity() const |
488 | { |
489 | return 0; |
490 | } |
491 | |
492 | // Wrap the compound selector with a complex selector |
493 | ComplexSelector* SelectorComponent::wrapInComplex() |
494 | { |
495 | auto complex = SASS_MEMORY_NEW(ComplexSelector, pstate()); |
496 | complex->append(element: this); |
497 | return complex; |
498 | } |
499 | |
500 | ///////////////////////////////////////////////////////////////////////// |
501 | ///////////////////////////////////////////////////////////////////////// |
502 | |
503 | SelectorCombinator::SelectorCombinator(SourceSpan pstate, SelectorCombinator::Combinator combinator, bool postLineBreak) |
504 | : SelectorComponent(pstate, postLineBreak), |
505 | combinator_(combinator) |
506 | { |
507 | } |
508 | SelectorCombinator::SelectorCombinator(const SelectorCombinator* ptr) |
509 | : SelectorComponent(ptr->pstate(), false), |
510 | combinator_(ptr->combinator()) |
511 | { } |
512 | |
513 | void SelectorCombinator::cloneChildren() |
514 | { |
515 | } |
516 | |
517 | unsigned long SelectorCombinator::specificity() const |
518 | { |
519 | return 0; |
520 | } |
521 | |
522 | ///////////////////////////////////////////////////////////////////////// |
523 | ///////////////////////////////////////////////////////////////////////// |
524 | |
525 | CompoundSelector::CompoundSelector(SourceSpan pstate, bool postLineBreak) |
526 | : SelectorComponent(pstate, postLineBreak), |
527 | Vectorized<SimpleSelectorObj>(), |
528 | hasRealParent_(false), |
529 | extended_(false) |
530 | { |
531 | } |
532 | CompoundSelector::CompoundSelector(const CompoundSelector* ptr) |
533 | : SelectorComponent(ptr), |
534 | Vectorized<SimpleSelectorObj>(*ptr), |
535 | hasRealParent_(ptr->hasRealParent()), |
536 | extended_(ptr->extended()) |
537 | { } |
538 | |
539 | size_t CompoundSelector::hash() const |
540 | { |
541 | if (Selector::hash_ == 0) { |
542 | hash_combine(seed&: Selector::hash_, val: Vectorized::hash()); |
543 | hash_combine(seed&: Selector::hash_, val: hasRealParent_); |
544 | } |
545 | return Selector::hash_; |
546 | } |
547 | |
548 | bool CompoundSelector::has_real_parent_ref() const |
549 | { |
550 | if (hasRealParent()) return true; |
551 | // ToDo: dart sass has another check? |
552 | // if (Cast<TypeSelector>(front)) { |
553 | // if (front->ns() != "") return false; |
554 | // } |
555 | for (const SimpleSelector* s : elements()) { |
556 | if (s && s->has_real_parent_ref()) return true; |
557 | } |
558 | return false; |
559 | } |
560 | |
561 | bool CompoundSelector::has_placeholder() const |
562 | { |
563 | if (length() == 0) return false; |
564 | for (SimpleSelectorObj ss : elements()) { |
565 | if (ss->has_placeholder()) return true; |
566 | } |
567 | return false; |
568 | } |
569 | |
570 | void CompoundSelector::cloneChildren() |
571 | { |
572 | for (size_t i = 0, l = length(); i < l; i++) { |
573 | at(i) = SASS_MEMORY_CLONE(at(i)); |
574 | } |
575 | } |
576 | |
577 | unsigned long CompoundSelector::specificity() const |
578 | { |
579 | int sum = 0; |
580 | for (size_t i = 0, L = length(); i < L; ++i) |
581 | { sum += get(i)->specificity(); } |
582 | return sum; |
583 | } |
584 | |
585 | bool CompoundSelector::isInvisible() const |
586 | { |
587 | for (size_t i = 0; i < length(); i += 1) { |
588 | if (!get(i)->isInvisible()) return false; |
589 | } |
590 | return true; |
591 | } |
592 | |
593 | bool CompoundSelector::isSuperselectorOf(const CompoundSelector* sub, sass::string wrapped) const |
594 | { |
595 | CompoundSelector* rhs2 = const_cast<CompoundSelector*>(sub); |
596 | CompoundSelector* lhs2 = const_cast<CompoundSelector*>(this); |
597 | return compoundIsSuperselector(compound1: lhs2, compound2: rhs2, parents: {}); |
598 | } |
599 | |
600 | ///////////////////////////////////////////////////////////////////////// |
601 | ///////////////////////////////////////////////////////////////////////// |
602 | |
603 | MediaRule::MediaRule(SourceSpan pstate, Block_Obj block) : |
604 | ParentStatement(pstate, block), |
605 | schema_({}) |
606 | { |
607 | statement_type(statement_type__: MEDIA); |
608 | } |
609 | |
610 | MediaRule::MediaRule(const MediaRule* ptr) : |
611 | ParentStatement(ptr), |
612 | schema_(ptr->schema_) |
613 | { |
614 | statement_type(statement_type__: MEDIA); |
615 | } |
616 | |
617 | ///////////////////////////////////////////////////////////////////////// |
618 | ///////////////////////////////////////////////////////////////////////// |
619 | |
620 | CssMediaRule::CssMediaRule(SourceSpan pstate, Block_Obj block) : |
621 | ParentStatement(pstate, block), |
622 | Vectorized() |
623 | { |
624 | statement_type(statement_type__: MEDIA); |
625 | } |
626 | |
627 | CssMediaRule::CssMediaRule(const CssMediaRule* ptr) : |
628 | ParentStatement(ptr), |
629 | Vectorized(*ptr) |
630 | { |
631 | statement_type(statement_type__: MEDIA); |
632 | } |
633 | |
634 | CssMediaQuery::CssMediaQuery(SourceSpan pstate) : |
635 | AST_Node(pstate), |
636 | modifier_("" ), |
637 | type_("" ), |
638 | features_() |
639 | { |
640 | } |
641 | |
642 | ///////////////////////////////////////////////////////////////////////// |
643 | ///////////////////////////////////////////////////////////////////////// |
644 | |
645 | bool CssMediaQuery::operator==(const CssMediaQuery& rhs) const |
646 | { |
647 | return type_ == rhs.type_ |
648 | && modifier_ == rhs.modifier_ |
649 | && features_ == rhs.features_; |
650 | } |
651 | |
652 | // Implemented after dart-sass (maybe move to other class?) |
653 | CssMediaQuery_Obj CssMediaQuery::merge(CssMediaQuery_Obj& other) |
654 | { |
655 | |
656 | sass::string ourType = this->type(); |
657 | Util::ascii_str_tolower(s: &ourType); |
658 | |
659 | sass::string theirType = other->type(); |
660 | Util::ascii_str_tolower(s: &theirType); |
661 | |
662 | sass::string ourModifier = this->modifier(); |
663 | Util::ascii_str_tolower(s: &ourModifier); |
664 | |
665 | sass::string theirModifier = other->modifier(); |
666 | Util::ascii_str_tolower(s: &theirModifier); |
667 | |
668 | sass::string type; |
669 | sass::string modifier; |
670 | sass::vector<sass::string> features; |
671 | |
672 | if (ourType.empty() && theirType.empty()) { |
673 | CssMediaQuery_Obj query = SASS_MEMORY_NEW(CssMediaQuery, pstate()); |
674 | sass::vector<sass::string> f1(this->features()); |
675 | sass::vector<sass::string> f2(other->features()); |
676 | features.insert(position: features.end(), first: f1.begin(), last: f1.end()); |
677 | features.insert(position: features.end(), first: f2.begin(), last: f2.end()); |
678 | query->features(features__: features); |
679 | return query; |
680 | } |
681 | |
682 | if ((ourModifier == "not" ) != (theirModifier == "not" )) { |
683 | if (ourType == theirType) { |
684 | sass::vector<sass::string> negativeFeatures = |
685 | ourModifier == "not" ? this->features() : other->features(); |
686 | sass::vector<sass::string> positiveFeatures = |
687 | ourModifier == "not" ? other->features() : this->features(); |
688 | |
689 | // If the negative features are a subset of the positive features, the |
690 | // query is empty. For example, `not screen and (color)` has no |
691 | // intersection with `screen and (color) and (grid)`. |
692 | // However, `not screen and (color)` *does* intersect with `screen and |
693 | // (grid)`, because it means `not (screen and (color))` and so it allows |
694 | // a screen with no color but with a grid. |
695 | if (listIsSubsetOrEqual(lhs: negativeFeatures, rhs: positiveFeatures)) { |
696 | return SASS_MEMORY_NEW(CssMediaQuery, pstate()); |
697 | } |
698 | else { |
699 | return {}; |
700 | } |
701 | } |
702 | else if (this->matchesAllTypes() || other->matchesAllTypes()) { |
703 | return {}; |
704 | } |
705 | |
706 | if (ourModifier == "not" ) { |
707 | modifier = theirModifier; |
708 | type = theirType; |
709 | features = other->features(); |
710 | } |
711 | else { |
712 | modifier = ourModifier; |
713 | type = ourType; |
714 | features = this->features(); |
715 | } |
716 | } |
717 | else if (ourModifier == "not" ) { |
718 | SASS_ASSERT(theirModifier == "not" , "modifiers not is sync" ); |
719 | |
720 | // CSS has no way of representing "neither screen nor print". |
721 | if (ourType != theirType) return {}; |
722 | |
723 | auto moreFeatures = this->features().size() > other->features().size() |
724 | ? this->features() |
725 | : other->features(); |
726 | auto fewerFeatures = this->features().size() > other->features().size() |
727 | ? other->features() |
728 | : this->features(); |
729 | |
730 | // If one set of features is a superset of the other, |
731 | // use those features because they're strictly narrower. |
732 | if (listIsSubsetOrEqual(lhs: fewerFeatures, rhs: moreFeatures)) { |
733 | modifier = ourModifier; // "not" |
734 | type = ourType; |
735 | features = moreFeatures; |
736 | } |
737 | else { |
738 | // Otherwise, there's no way to |
739 | // represent the intersection. |
740 | return {}; |
741 | } |
742 | |
743 | } |
744 | else { |
745 | if (this->matchesAllTypes()) { |
746 | modifier = theirModifier; |
747 | // Omit the type if either input query did, since that indicates that they |
748 | // aren't targeting a browser that requires "all and". |
749 | type = (other->matchesAllTypes() && ourType.empty()) ? "" : theirType; |
750 | sass::vector<sass::string> f1(this->features()); |
751 | sass::vector<sass::string> f2(other->features()); |
752 | features.insert(position: features.end(), first: f1.begin(), last: f1.end()); |
753 | features.insert(position: features.end(), first: f2.begin(), last: f2.end()); |
754 | } |
755 | else if (other->matchesAllTypes()) { |
756 | modifier = ourModifier; |
757 | type = ourType; |
758 | sass::vector<sass::string> f1(this->features()); |
759 | sass::vector<sass::string> f2(other->features()); |
760 | features.insert(position: features.end(), first: f1.begin(), last: f1.end()); |
761 | features.insert(position: features.end(), first: f2.begin(), last: f2.end()); |
762 | } |
763 | else if (ourType != theirType) { |
764 | return SASS_MEMORY_NEW(CssMediaQuery, pstate()); |
765 | } |
766 | else { |
767 | modifier = ourModifier.empty() ? theirModifier : ourModifier; |
768 | type = ourType; |
769 | sass::vector<sass::string> f1(this->features()); |
770 | sass::vector<sass::string> f2(other->features()); |
771 | features.insert(position: features.end(), first: f1.begin(), last: f1.end()); |
772 | features.insert(position: features.end(), first: f2.begin(), last: f2.end()); |
773 | } |
774 | } |
775 | |
776 | CssMediaQuery_Obj query = SASS_MEMORY_NEW(CssMediaQuery, pstate()); |
777 | query->modifier(modifier__: modifier == ourModifier ? this->modifier() : other->modifier()); |
778 | query->type(type__: ourType.empty() ? other->type() : this->type()); |
779 | query->features(features__: features); |
780 | return query; |
781 | } |
782 | |
783 | CssMediaQuery::CssMediaQuery(const CssMediaQuery* ptr) : |
784 | AST_Node(*ptr), |
785 | modifier_(ptr->modifier_), |
786 | type_(ptr->type_), |
787 | features_(ptr->features_) |
788 | { |
789 | } |
790 | |
791 | ///////////////////////////////////////////////////////////////////////// |
792 | // ToDo: finalize specificity implementation |
793 | ///////////////////////////////////////////////////////////////////////// |
794 | |
795 | size_t SelectorList::maxSpecificity() const |
796 | { |
797 | size_t specificity = 0; |
798 | for (auto complex : elements()) { |
799 | specificity = std::max(a: specificity, b: complex->maxSpecificity()); |
800 | } |
801 | return specificity; |
802 | } |
803 | |
804 | size_t SelectorList::minSpecificity() const |
805 | { |
806 | size_t specificity = 0; |
807 | for (auto complex : elements()) { |
808 | specificity = std::min(a: specificity, b: complex->minSpecificity()); |
809 | } |
810 | return specificity; |
811 | } |
812 | |
813 | size_t CompoundSelector::maxSpecificity() const |
814 | { |
815 | size_t specificity = 0; |
816 | for (auto simple : elements()) { |
817 | specificity += simple->maxSpecificity(); |
818 | } |
819 | return specificity; |
820 | } |
821 | |
822 | size_t CompoundSelector::minSpecificity() const |
823 | { |
824 | size_t specificity = 0; |
825 | for (auto simple : elements()) { |
826 | specificity += simple->minSpecificity(); |
827 | } |
828 | return specificity; |
829 | } |
830 | |
831 | size_t ComplexSelector::maxSpecificity() const |
832 | { |
833 | size_t specificity = 0; |
834 | for (auto component : elements()) { |
835 | specificity += component->maxSpecificity(); |
836 | } |
837 | return specificity; |
838 | } |
839 | |
840 | size_t ComplexSelector::minSpecificity() const |
841 | { |
842 | size_t specificity = 0; |
843 | for (auto component : elements()) { |
844 | specificity += component->minSpecificity(); |
845 | } |
846 | return specificity; |
847 | } |
848 | |
849 | ///////////////////////////////////////////////////////////////////////// |
850 | // ToDo: this might be done easier with new selector format |
851 | ///////////////////////////////////////////////////////////////////////// |
852 | |
853 | sass::vector<ComplexSelectorObj> |
854 | CompoundSelector::resolve_parent_refs(SelectorStack pstack, Backtraces& traces, bool implicit_parent) |
855 | { |
856 | |
857 | auto parent = pstack.back(); |
858 | sass::vector<ComplexSelectorObj> rv; |
859 | |
860 | for (SimpleSelectorObj simple : elements()) { |
861 | if (PseudoSelector * pseudo = Cast<PseudoSelector>(ptr: simple)) { |
862 | if (SelectorList* sel = Cast<SelectorList>(ptr: pseudo->selector())) { |
863 | if (parent) { |
864 | pseudo->selector(selector__: sel->resolve_parent_refs( |
865 | pstack, traces, implicit_parent)); |
866 | } |
867 | } |
868 | } |
869 | } |
870 | |
871 | // Mix with parents from stack |
872 | if (hasRealParent()) { |
873 | |
874 | if (parent.isNull()) { |
875 | return { wrapInComplex() }; |
876 | } |
877 | else { |
878 | for (auto complex : parent->elements()) { |
879 | // The parent complex selector has a compound selector |
880 | if (CompoundSelectorObj tail = Cast<CompoundSelector>(ptr: complex->last())) { |
881 | // Create a copy to alter it |
882 | complex = SASS_MEMORY_COPY(complex); |
883 | tail = SASS_MEMORY_COPY(tail); |
884 | |
885 | // Check if we can merge front with back |
886 | if (length() > 0 && tail->length() > 0) { |
887 | SimpleSelectorObj back = tail->last(); |
888 | SimpleSelectorObj front = first(); |
889 | auto simple_back = Cast<SimpleSelector>(ptr: back); |
890 | auto simple_front = Cast<TypeSelector>(ptr: front); |
891 | if (simple_front && simple_back) { |
892 | simple_back = SASS_MEMORY_COPY(simple_back); |
893 | auto name = simple_back->name(); |
894 | name += simple_front->name(); |
895 | simple_back->name(name__: name); |
896 | tail->elements().back() = simple_back; |
897 | tail->elements().insert(position: tail->end(), |
898 | first: begin() + 1, last: end()); |
899 | } |
900 | else { |
901 | tail->concat(v: this); |
902 | } |
903 | } |
904 | else { |
905 | tail->concat(v: this); |
906 | } |
907 | |
908 | complex->elements().back() = tail; |
909 | // Append to results |
910 | rv.push_back(x: complex); |
911 | } |
912 | else { |
913 | // Can't insert parent that ends with a combinator |
914 | // where the parent selector is followed by something |
915 | if (parent && length() > 0) { |
916 | throw Exception::InvalidParent(parent, traces, this); |
917 | } |
918 | // Create a copy to alter it |
919 | complex = SASS_MEMORY_COPY(complex); |
920 | // Just append ourself |
921 | complex->append(element: this); |
922 | // Append to results |
923 | rv.push_back(x: complex); |
924 | } |
925 | } |
926 | } |
927 | } |
928 | |
929 | // No parents |
930 | else { |
931 | // Create a new wrapper to wrap ourself |
932 | auto complex = SASS_MEMORY_NEW(ComplexSelector, pstate()); |
933 | // Just append ourself |
934 | complex->append(element: this); |
935 | // Append to results |
936 | rv.push_back(x: complex); |
937 | } |
938 | |
939 | return rv; |
940 | |
941 | } |
942 | |
943 | bool cmpSimpleSelectors(SimpleSelector* a, SimpleSelector* b) |
944 | { |
945 | return (a->getSortOrder() < b->getSortOrder()); |
946 | } |
947 | |
948 | void CompoundSelector::sortChildren() |
949 | { |
950 | std::sort(first: begin(), last: end(), comp: cmpSimpleSelectors); |
951 | } |
952 | |
953 | /* better return sass::vector? only - is empty container anyway? */ |
954 | SelectorList* ComplexSelector::resolve_parent_refs(SelectorStack pstack, Backtraces& traces, bool implicit_parent) |
955 | { |
956 | |
957 | sass::vector<sass::vector<ComplexSelectorObj>> vars; |
958 | |
959 | auto parent = pstack.back(); |
960 | |
961 | if (has_real_parent_ref() && !parent) { |
962 | throw Exception::TopLevelParent(traces, pstate()); |
963 | } |
964 | |
965 | if (!chroots() && parent) { |
966 | |
967 | if (!has_real_parent_ref() && !implicit_parent) { |
968 | SelectorList* retval = SASS_MEMORY_NEW(SelectorList, pstate(), 1); |
969 | retval->append(element: this); |
970 | return retval; |
971 | } |
972 | |
973 | vars.push_back(x: parent->elements()); |
974 | } |
975 | |
976 | for (auto sel : elements()) { |
977 | if (CompoundSelectorObj comp = Cast<CompoundSelector>(ptr: sel)) { |
978 | auto asd = comp->resolve_parent_refs(pstack, traces, implicit_parent); |
979 | if (asd.size() > 0) vars.push_back(x: asd); |
980 | } |
981 | else { |
982 | // ToDo: merge together sequences whenever possible |
983 | auto cont = SASS_MEMORY_NEW(ComplexSelector, pstate()); |
984 | cont->append(element: sel); |
985 | vars.push_back(x: { cont }); |
986 | } |
987 | } |
988 | |
989 | // Need complex selectors to preserve linefeeds |
990 | sass::vector<sass::vector<ComplexSelectorObj>> res = permutateAlt(in: vars); |
991 | |
992 | // std::reverse(std::begin(res), std::end(res)); |
993 | |
994 | auto lst = SASS_MEMORY_NEW(SelectorList, pstate()); |
995 | for (auto items : res) { |
996 | if (items.size() > 0) { |
997 | ComplexSelectorObj first = SASS_MEMORY_COPY(items[0]); |
998 | first->hasPreLineFeed(hasPreLineFeed__: first->hasPreLineFeed() || (!has_real_parent_ref() && hasPreLineFeed())); |
999 | // ToDo: remove once we know how to handle line feeds |
1000 | // ToDo: currently a mashup between ruby and dart sass |
1001 | // if (has_real_parent_ref()) first->has_line_feed(false); |
1002 | // first->has_line_break(first->has_line_break() || has_line_break()); |
1003 | first->chroots(chroots__: true); // has been resolved by now |
1004 | for (size_t i = 1; i < items.size(); i += 1) { |
1005 | first->concat(v: items[i]); |
1006 | } |
1007 | lst->append(element: first); |
1008 | } |
1009 | } |
1010 | |
1011 | return lst; |
1012 | |
1013 | } |
1014 | |
1015 | SelectorList* SelectorList::resolve_parent_refs(SelectorStack pstack, Backtraces& traces, bool implicit_parent) |
1016 | { |
1017 | SelectorList* rv = SASS_MEMORY_NEW(SelectorList, pstate()); |
1018 | for (auto sel : elements()) { |
1019 | // Note: this one is tricky as we get back a pointer from resolve parents ... |
1020 | SelectorListObj res = sel->resolve_parent_refs(pstack, traces, implicit_parent); |
1021 | // Note: ... and concat will only append the items in elements |
1022 | // Therefore by passing it directly, the container will leak! |
1023 | rv->concat(v: res); |
1024 | } |
1025 | return rv; |
1026 | } |
1027 | |
1028 | ///////////////////////////////////////////////////////////////////////// |
1029 | ///////////////////////////////////////////////////////////////////////// |
1030 | |
1031 | IMPLEMENT_AST_OPERATORS(Selector_Schema); |
1032 | IMPLEMENT_AST_OPERATORS(PlaceholderSelector); |
1033 | IMPLEMENT_AST_OPERATORS(AttributeSelector); |
1034 | IMPLEMENT_AST_OPERATORS(TypeSelector); |
1035 | IMPLEMENT_AST_OPERATORS(ClassSelector); |
1036 | IMPLEMENT_AST_OPERATORS(IDSelector); |
1037 | IMPLEMENT_AST_OPERATORS(PseudoSelector); |
1038 | IMPLEMENT_AST_OPERATORS(SelectorCombinator); |
1039 | IMPLEMENT_AST_OPERATORS(CompoundSelector); |
1040 | IMPLEMENT_AST_OPERATORS(ComplexSelector); |
1041 | IMPLEMENT_AST_OPERATORS(SelectorList); |
1042 | |
1043 | } |
1044 | |