/var/www/html_nl/wp-content/plugins/woocommerce/vendor/pelago/emogrifier/src/Css/CssDocument.php


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
<?php

declare(strict_types=1);

namespace 
Pelago\Emogrifier\Css;

use 
Sabberworm\CSS\CSSList\AtRuleBlockList as CssAtRuleBlockList;
use 
Sabberworm\CSS\CSSList\Document as SabberwormCssDocument;
use 
Sabberworm\CSS\Parser as CssParser;
use 
Sabberworm\CSS\Property\AtRule as CssAtRule;
use 
Sabberworm\CSS\Property\Charset as CssCharset;
use 
Sabberworm\CSS\Property\Import as CssImport;
use 
Sabberworm\CSS\Renderable as CssRenderable;
use 
Sabberworm\CSS\RuleSet\DeclarationBlock as CssDeclarationBlock;
use 
Sabberworm\CSS\RuleSet\RuleSet as CssRuleSet;

/**
 * Parses and stores a CSS document from a string of CSS, and provides methods to obtain the CSS in parts or as data
 * structures.
 *
 * @internal
 */
class CssDocument
{
    
/**
     * @var SabberwormCssDocument
     */
    
private $sabberwormCssDocument;

    
/**
     * `@import` rules must precede all other types of rules, except `@charset` rules.  This property is used while
     * rendering at-rules to enforce that.
     *
     * @var bool
     */
    
private $isImportRuleAllowed true;

    
/**
     * @param string $css
     */
    
public function __construct(string $css)
    {
        
$cssParser = new CssParser($css);
        
/** @var SabberwormCssDocument $sabberwormCssDocument */
        
$sabberwormCssDocument $cssParser->parse();
        
$this->sabberwormCssDocument $sabberwormCssDocument;
    }

    
/**
     * Collates the media query, selectors and declarations for individual rules from the parsed CSS, in order.
     *
     * @param array<array-key, string> $allowedMediaTypes
     *
     * @return array<int, StyleRule>
     */
    
public function getStyleRulesData(array $allowedMediaTypes): array
    {
        
$ruleMatches = [];
        
/** @var CssRenderable $rule */
        
foreach ($this->sabberwormCssDocument->getContents() as $rule) {
            if (
$rule instanceof CssAtRuleBlockList) {
                
$containingAtRule $this->getFilteredAtIdentifierAndRule($rule$allowedMediaTypes);
                if (
\is_string($containingAtRule)) {
                    
/** @var CssRenderable $nestedRule */
                    
foreach ($rule->getContents() as $nestedRule) {
                        if (
$nestedRule instanceof CssDeclarationBlock) {
                            
$ruleMatches[] = new StyleRule($nestedRule$containingAtRule);
                        }
                    }
                }
            } elseif (
$rule instanceof CssDeclarationBlock) {
                
$ruleMatches[] = new StyleRule($rule);
            }
        }

        return 
$ruleMatches;
    }

    
/**
     * Renders at-rules from the parsed CSS that are valid and not conditional group rules (i.e. not rules such as
     * `@media` which contain style rules whose data is returned by {@see getStyleRulesData}).  Also does not render
     * `@charset` rules; these are discarded (only UTF-8 is supported).
     *
     * @return string
     */
    
public function renderNonConditionalAtRules(): string
    
{
        
$this->isImportRuleAllowed true;
        
/** @var array<int, CssRenderable> $cssContents */
        
$cssContents $this->sabberwormCssDocument->getContents();
        
$atRules \array_filter($cssContents, [$this'isValidAtRuleToRender']);

        if (
$atRules === []) {
            return 
'';
        }

        
$atRulesDocument = new SabberwormCssDocument();
        
$atRulesDocument->setContents($atRules);

        
/** @var string $renderedRules */
        
$renderedRules $atRulesDocument->render();
        return 
$renderedRules;
    }

    
/**
     * @param CssAtRuleBlockList $rule
     * @param array<array-key, string> $allowedMediaTypes
     *
     * @return ?string
     *         If the nested at-rule is supported, it's opening declaration (e.g. "@media (max-width: 768px)") is
     *         returned; otherwise the return value is null.
     */
    
private function getFilteredAtIdentifierAndRule(CssAtRuleBlockList $rule, array $allowedMediaTypes): ?string
    
{
        
$result null;

        if (
$rule->atRuleName() === 'media') {
            
/** @var string $mediaQueryList */
            
$mediaQueryList $rule->atRuleArgs();
            [
$mediaType] = \explode('('$mediaQueryList2);
            if (
\trim($mediaType) !== '') {
                
$escapedAllowedMediaTypes \array_map(
                    static function (
string $allowedMediaType): string {
                        return 
\preg_quote($allowedMediaType'/');
                    },
                    
$allowedMediaTypes
                
);
                
$mediaTypesMatcher \implode('|'$escapedAllowedMediaTypes);
                
$isAllowed \preg_match('/^\\s*+(?:only\\s++)?+(?:' $mediaTypesMatcher ')/i'$mediaType) > 0;
            } else {
                
$isAllowed true;
            }

            if (
$isAllowed) {
                
$result '@media ' $mediaQueryList;
            }
        }

        return 
$result;
    }

    
/**
     * Tests if a CSS rule is an at-rule that should be passed though and copied to a `<style>` element unmodified:
     * - `@charset` rules are discarded - only UTF-8 is supported - `false` is returned;
     * - `@import` rules are passed through only if they satisfy the specification ("user agents must ignore any
     *   '@import' rule that occurs inside a block or after any non-ignored statement other than an '@charset' or an
     *   '@import' rule");
     * - `@media` rules are processed separately to see if their nested rules apply - `false` is returned;
     * - `@font-face` rules are checked for validity - they must contain both a `src` and `font-family` property;
     * - other at-rules are assumed to be valid and treated as a black box - `true` is returned.
     *
     * @param CssRenderable $rule
     *
     * @return bool
     */
    
private function isValidAtRuleToRender(CssRenderable $rule): bool
    
{
        if (
$rule instanceof CssCharset) {
            return 
false;
        }

        if (
$rule instanceof CssImport) {
            return 
$this->isImportRuleAllowed;
        }

        
$this->isImportRuleAllowed false;

        if (!
$rule instanceof CssAtRule) {
            return 
false;
        }

        switch (
$rule->atRuleName()) {
            case 
'media':
                
$result false;
                break;
            case 
'font-face':
                
$result $rule instanceof CssRuleSet
                    
&& $rule->getRules('font-family') !== []
                    && 
$rule->getRules('src') !== [];
                break;
            default:
                
$result true;
        }

        return 
$result;
    }
}