Coverage Report

Created: 2026-01-09 06:35

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libsass/src/util.cpp
Line
Count
Source
1
#include "sass.hpp"
2
#include "sass.h"
3
#include "ast.hpp"
4
#include "util.hpp"
5
#include "util_string.hpp"
6
#include "lexer.hpp"
7
#include "prelexer.hpp"
8
#include "constants.hpp"
9
#include "utf8/checked.h"
10
11
#include <cmath>
12
#include <stdint.h>
13
#if defined(_MSC_VER) && _MSC_VER >= 1800 && _MSC_VER < 1900 && defined(_M_X64)
14
#include <mutex>
15
#endif
16
17
namespace Sass {
18
19
  double round(double val, size_t precision)
20
0
  {
21
    // Disable FMA3-optimized implementation when compiling with VS2013 for x64 targets
22
    // See https://github.com/sass/node-sass/issues/1854 for details
23
    // FIXME: Remove this workaround when we switch to VS2015+
24
    #if defined(_MSC_VER) && _MSC_VER >= 1800 && _MSC_VER < 1900 && defined(_M_X64)
25
      static std::once_flag flag;
26
      std::call_once(flag, []() { _set_FMA3_enable(0); });
27
    #endif
28
29
    // https://github.com/sass/sass/commit/4e3e1d5684cc29073a507578fc977434ff488c93
30
0
    if (std::fmod(val, 1) - 0.5 > - std::pow(0.1, precision + 1)) return std::ceil(val);
31
0
    else if (std::fmod(val, 1) - 0.5 > std::pow(0.1, precision)) return std::floor(val);
32
    // work around some compiler issue
33
    // cygwin has it not defined in std
34
0
    using namespace std;
35
0
    return ::round(val);
36
0
  }
37
38
  /* Locale unspecific atof function. */
39
  double sass_strtod(const char *str)
40
0
  {
41
0
    char separator = *(localeconv()->decimal_point);
42
0
    if(separator != '.'){
43
      // The current locale specifies another
44
      // separator. convert the separator to the
45
      // one understood by the locale if needed
46
0
      const char *found = strchr(str, '.');
47
0
      if(found != NULL){
48
        // substitution is required. perform the substitution on a copy
49
        // of the string. This is slower but it is thread safe.
50
0
        char *copy = sass_copy_c_string(str);
51
0
        *(copy + (found - str)) = separator;
52
0
        double res = strtod(copy, NULL);
53
0
        free(copy);
54
0
        return res;
55
0
      }
56
0
    }
57
58
0
    return strtod(str, NULL);
59
0
  }
60
61
  // helper for safe access to c_ctx
62
0
  const char* safe_str (const char* str, const char* alt) {
63
0
    return str == NULL ? alt : str;
64
0
  }
65
66
0
  void free_string_array(char ** arr) {
67
0
    if(!arr)
68
0
        return;
69
70
0
    char **it = arr;
71
0
    while (it && (*it)) {
72
0
      free(*it);
73
0
      ++it;
74
0
    }
75
76
0
    free(arr);
77
0
  }
78
79
0
  char **copy_strings(const sass::vector<sass::string>& strings, char*** array, int skip) {
80
0
    int num = static_cast<int>(strings.size()) - skip;
81
0
    char** arr = (char**) calloc(num + 1, sizeof(char*));
82
0
    if (arr == 0)
83
0
      return *array = (char **)NULL;
84
85
0
    for(int i = 0; i < num; i++) {
86
0
      arr[i] = (char*) malloc(sizeof(char) * (strings[i + skip].size() + 1));
87
0
      if (arr[i] == 0) {
88
0
        free_string_array(arr);
89
0
        return *array = (char **)NULL;
90
0
      }
91
0
      std::copy(strings[i + skip].begin(), strings[i + skip].end(), arr[i]);
92
0
      arr[i][strings[i + skip].size()] = '\0';
93
0
    }
94
95
0
    arr[num] = 0;
96
0
    return *array = arr;
97
0
  }
98
99
  // read css string (handle multiline DELIM)
100
  sass::string read_css_string(const sass::string& str, bool css)
101
0
  {
102
0
    if (!css) return str;
103
0
    sass::string out("");
104
0
    bool esc = false;
105
0
    for (auto i : str) {
106
0
      if (i == '\\') {
107
0
        esc = ! esc;
108
0
      } else if (esc && i == '\r') {
109
0
        continue;
110
0
      } else if (esc && i == '\n') {
111
0
        out.resize (out.size () - 1);
112
0
        esc = false;
113
0
        continue;
114
0
      } else {
115
0
        esc = false;
116
0
      }
117
0
      out.push_back(i);
118
0
    }
119
    // happens when parsing does not correctly skip
120
    // over escaped sequences for ie. interpolations
121
    // one example: foo\#{interpolate}
122
    // if (esc) out += '\\';
123
0
    return out;
124
0
  }
125
126
  // double escape all escape sequences
127
  // keep unescaped quotes and backslashes
128
  sass::string evacuate_escapes(const sass::string& str)
129
0
  {
130
0
    sass::string out("");
131
0
    bool esc = false;
132
0
    for (auto i : str) {
133
0
      if (i == '\\' && !esc) {
134
0
        out += '\\';
135
0
        out += '\\';
136
0
        esc = true;
137
0
      } else if (esc && i == '"') {
138
0
        out += '\\';
139
0
        out += i;
140
0
        esc = false;
141
0
      } else if (esc && i == '\'') {
142
0
        out += '\\';
143
0
        out += i;
144
0
        esc = false;
145
0
      } else if (esc && i == '\\') {
146
0
        out += '\\';
147
0
        out += i;
148
0
        esc = false;
149
0
      } else {
150
0
        esc = false;
151
0
        out += i;
152
0
      }
153
0
    }
154
    // happens when parsing does not correctly skip
155
    // over escaped sequences for ie. interpolations
156
    // one example: foo\#{interpolate}
157
    // if (esc) out += '\\';
158
0
    return out;
159
0
  }
160
161
  // bell characters are replaced with spaces
162
  void newline_to_space(sass::string& str)
163
0
  {
164
0
    std::replace(str.begin(), str.end(), '\n', ' ');
165
0
  }
166
167
  // 1. Removes whitespace after newlines.
168
  // 2. Replaces newlines with spaces.
169
  //
170
  // This method only considers LF and CRLF as newlines.
171
  sass::string string_to_output(const sass::string& str)
172
0
  {
173
0
    sass::string result;
174
0
    result.reserve(str.size());
175
0
    std::size_t pos = 0;
176
0
    while (true) {
177
0
      const std::size_t newline = str.find_first_of("\n\r", pos);
178
0
      if (newline == sass::string::npos) break;
179
0
      result.append(str, pos, newline - pos);
180
0
      if (str[newline] == '\r') {
181
0
        if (str[newline + 1] == '\n') {
182
0
          pos = newline + 2;
183
0
        } else {
184
          // CR without LF: append as-is and continue.
185
0
          result += '\r';
186
0
          pos = newline + 1;
187
0
          continue;
188
0
        }
189
0
      } else {
190
0
        pos = newline + 1;
191
0
      }
192
0
      result += ' ';
193
0
      const std::size_t non_space = str.find_first_not_of(" \f\n\r\t\v", pos);
194
0
      if (non_space != sass::string::npos) {
195
0
        pos = non_space;
196
0
      }
197
0
    }
198
0
    result.append(str, pos, sass::string::npos);
199
0
    return result;
200
0
  }
201
202
  sass::string escape_string(const sass::string& str)
203
0
  {
204
0
    sass::string out;
205
0
    out.reserve(str.size());
206
0
    for (char c : str) {
207
0
      switch (c) {
208
0
        case '\n':
209
0
          out.append("\\n");
210
0
          break;
211
0
        case '\r':
212
0
          out.append("\\r");
213
0
          break;
214
0
        case '\f':
215
0
          out.append("\\f");
216
0
          break;
217
0
        default:
218
0
          out += c;
219
0
      }
220
0
    }
221
0
    return out;
222
0
  }
223
224
  sass::string comment_to_compact_string(const sass::string& text)
225
0
  {
226
0
    sass::string str = "";
227
0
    size_t has = 0;
228
0
    char prev = 0;
229
0
    bool clean = false;
230
0
    for (auto i : text) {
231
0
      if (clean) {
232
0
        if (i == '\n') { has = 0; }
233
0
        else if (i == '\t') { ++ has; }
234
0
        else if (i == ' ') { ++ has; }
235
0
        else if (i == '*') {}
236
0
        else {
237
0
          clean = false;
238
0
          str += ' ';
239
0
          if (prev == '*' && i == '/') str += "*/";
240
0
          else str += i;
241
0
        }
242
0
      } else if (i == '\n') {
243
0
        clean = true;
244
0
      } else {
245
0
        str += i;
246
0
      }
247
0
      prev = i;
248
0
    }
249
0
    if (has) return str;
250
0
    else return text;
251
0
  }
252
253
  // find best quote_mark by detecting if the string contains any single
254
  // or double quotes. When a single quote is found, we not we want a double
255
  // quote as quote_mark. Otherwise we check if the string cotains any double
256
  // quotes, which will trigger the use of single quotes as best quote_mark.
257
  char detect_best_quotemark(const char* s, char qm)
258
0
  {
259
    // ensure valid fallback quote_mark
260
0
    char quote_mark = qm && qm != '*' ? qm : '"';
261
0
    while (*s) {
262
      // force double quotes as soon
263
      // as one single quote is found
264
0
      if (*s == '\'') { return '"'; }
265
      // a single does not force quote_mark
266
      // maybe we see a double quote later
267
0
      else if (*s == '"') { quote_mark = '\''; }
268
0
      ++ s;
269
0
    }
270
0
    return quote_mark;
271
0
  }
272
273
  sass::string read_hex_escapes(const sass::string& s)
274
0
  {
275
276
0
    sass::string result;
277
0
    bool skipped = false;
278
279
0
    for (size_t i = 0, L = s.length(); i < L; ++i) {
280
281
      // implement the same strange ruby sass behavior
282
      // an escape sequence can also mean a unicode char
283
0
      if (s[i] == '\\' && !skipped) {
284
285
        // remember
286
0
        skipped = true;
287
288
        // escape length
289
0
        size_t len = 1;
290
291
        // parse as many sequence chars as possible
292
        // ToDo: Check if ruby aborts after possible max
293
0
        while (i + len < L && s[i + len] && Util::ascii_isxdigit(static_cast<unsigned char>(s[i + len]))) ++ len;
294
295
0
        if (len > 1) {
296
297
          // convert the extracted hex string to code point value
298
          // ToDo: Maybe we could do this without creating a substring
299
0
          uint32_t cp = strtol(s.substr (i + 1, len - 1).c_str(), NULL, 16);
300
301
0
          if (s[i + len] == ' ') ++ len;
302
303
          // assert invalid code points
304
0
          if (cp == 0) cp = 0xFFFD;
305
          // replace bell character
306
          // if (cp == '\n') cp = 32;
307
308
          // use a very simple approach to convert via utf8 lib
309
          // maybe there is a more elegant way; maybe we should
310
          // convert the whole output from string to a stream!?
311
          // allocate memory for utf8 char and convert to utf8
312
0
          unsigned char u[5] = {0,0,0,0,0}; utf8::append(cp, u);
313
0
          for(size_t m = 0; m < 5 && u[m]; m++) result.push_back(u[m]);
314
315
          // skip some more chars?
316
0
          i += len - 1; skipped = false;
317
318
0
        }
319
320
0
        else {
321
322
0
          skipped = false;
323
324
0
          result.push_back(s[i]);
325
326
0
        }
327
328
0
      }
329
330
0
      else {
331
332
0
        result.push_back(s[i]);
333
334
0
      }
335
336
0
    }
337
338
0
    return result;
339
340
0
  }
341
342
  sass::string unquote(const sass::string& s, char* qd, bool keep_utf8_sequences, bool strict)
343
0
  {
344
345
    // not enough room for quotes
346
    // no possibility to unquote
347
0
    if (s.length() < 2) return s;
348
349
0
    char q;
350
0
    bool skipped = false;
351
352
    // this is no guarantee that the unquoting will work
353
    // what about whitespace before/after the quote_mark?
354
0
    if      (*s.begin() == '"'  && *s.rbegin() == '"')  q = '"';
355
0
    else if (*s.begin() == '\'' && *s.rbegin() == '\'') q = '\'';
356
0
    else                                                return s;
357
358
0
    sass::string unq;
359
0
    unq.reserve(s.length()-2);
360
361
0
    for (size_t i = 1, L = s.length() - 1; i < L; ++i) {
362
363
      // implement the same strange ruby sass behavior
364
      // an escape sequence can also mean a unicode char
365
0
      if (s[i] == '\\' && !skipped) {
366
        // remember
367
0
        skipped = true;
368
369
        // skip it
370
        // ++ i;
371
372
        // if (i == L) break;
373
374
        // escape length
375
0
        size_t len = 1;
376
377
        // parse as many sequence chars as possible
378
        // ToDo: Check if ruby aborts after possible max
379
0
        while (i + len < L && s[i + len] && Util::ascii_isxdigit(static_cast<unsigned char>(s[i + len]))) ++ len;
380
381
        // hex string?
382
0
        if (keep_utf8_sequences) {
383
0
          unq.push_back(s[i]);
384
0
        } else if (len > 1) {
385
386
          // convert the extracted hex string to code point value
387
          // ToDo: Maybe we could do this without creating a substring
388
0
          uint32_t cp = strtol(s.substr (i + 1, len - 1).c_str(), NULL, 16);
389
390
0
          if (s[i + len] == ' ') ++ len;
391
392
          // assert invalid code points
393
0
          if (cp == 0) cp = 0xFFFD;
394
          // replace bell character
395
          // if (cp == '\n') cp = 32;
396
397
          // use a very simple approach to convert via utf8 lib
398
          // maybe there is a more elegant way; maybe we should
399
          // convert the whole output from string to a stream!?
400
          // allocate memory for utf8 char and convert to utf8
401
0
          unsigned char u[5] = {0,0,0,0,0}; utf8::append(cp, u);
402
0
          for(size_t m = 0; m < 5 && u[m]; m++) unq.push_back(u[m]);
403
404
          // skip some more chars?
405
0
          i += len - 1; skipped = false;
406
407
0
        }
408
409
410
0
      }
411
      // check for unexpected delimiter
412
      // be strict and throw error back
413
      // else if (!skipped && q == s[i]) {
414
      //   // don't be that strict
415
      //   return s;
416
      //   // this basically always means an internal error and not users fault
417
      //   error("Unescaped delimiter in string to unquote found. [" + s + "]", SourceSpan("[UNQUOTE]"));
418
      // }
419
0
      else {
420
0
        if (strict && !skipped) {
421
0
          if (s[i] == q) return s;
422
0
        }
423
0
        skipped = false;
424
0
        unq.push_back(s[i]);
425
0
      }
426
427
0
    }
428
0
    if (skipped) { return s; }
429
0
    if (qd) *qd = q;
430
0
    return unq;
431
432
0
  }
433
434
  sass::string quote(const sass::string& s, char q)
435
0
  {
436
437
    // autodetect with fallback to given quote
438
0
    q = detect_best_quotemark(s.c_str(), q);
439
440
    // return an empty quoted string
441
0
    if (s.empty()) return sass::string(2, q ? q : '"');
442
443
0
    sass::string quoted;
444
0
    quoted.reserve(s.length()+2);
445
0
    quoted.push_back(q);
446
447
0
    const char* it = s.c_str();
448
0
    const char* end = it + strlen(it) + 1;
449
0
    while (*it && it < end) {
450
0
      const char* now = it;
451
452
0
      if (*it == q) {
453
0
        quoted.push_back('\\');
454
0
      } else if (*it == '\\') {
455
0
        quoted.push_back('\\');
456
0
      }
457
458
0
      int cp = utf8::next(it, end);
459
460
      // in case of \r, check if the next in sequence
461
      // is \n and then advance the iterator and skip \r
462
0
      if (cp == '\r' && it < end && utf8::peek_next(it, end) == '\n') {
463
0
        cp = utf8::next(it, end);
464
0
      }
465
466
0
      if (cp == '\n') {
467
0
        quoted.push_back('\\');
468
0
        quoted.push_back('a');
469
        // we hope we can remove this flag once we figure out
470
        // why ruby sass has these different output behaviors
471
        // gsub(/\n(?![a-fA-F0-9\s])/, "\\a").gsub("\n", "\\a ")
472
0
        using namespace Prelexer;
473
0
        if (alternatives <
474
0
          Prelexer::char_range<'a', 'f'>,
475
0
          Prelexer::char_range<'A', 'F'>,
476
0
          Prelexer::char_range<'0', '9'>,
477
0
          space
478
0
        >(it) != NULL) {
479
0
          quoted.push_back(' ');
480
0
        }
481
0
      } else if (cp < 127) {
482
0
        quoted.push_back((char) cp);
483
0
      } else {
484
0
        while (now < it) {
485
0
          quoted.push_back(*now);
486
0
          ++ now;
487
0
        }
488
0
      }
489
0
    }
490
491
0
    quoted.push_back(q);
492
0
    return quoted;
493
0
  }
494
495
  bool is_hex_doublet(double n)
496
0
  {
497
0
    return n == 0x00 || n == 0x11 || n == 0x22 || n == 0x33 ||
498
0
           n == 0x44 || n == 0x55 || n == 0x66 || n == 0x77 ||
499
0
           n == 0x88 || n == 0x99 || n == 0xAA || n == 0xBB ||
500
0
           n == 0xCC || n == 0xDD || n == 0xEE || n == 0xFF ;
501
0
  }
502
503
  bool is_color_doublet(double r, double g, double b)
504
0
  {
505
0
    return is_hex_doublet(r) && is_hex_doublet(g) && is_hex_doublet(b);
506
0
  }
507
508
  bool peek_linefeed(const char* start)
509
0
  {
510
0
    using namespace Prelexer;
511
0
    using namespace Constants;
512
0
    return sequence <
513
0
             zero_plus <
514
0
               alternatives <
515
0
                 exactly <' '>,
516
0
                 exactly <'\t'>,
517
0
                 line_comment,
518
0
                 block_comment,
519
0
                 delimited_by <
520
0
                   slash_star,
521
0
                   star_slash,
522
0
                   false
523
0
                 >
524
0
               >
525
0
             >,
526
0
             re_linebreak
527
0
           >(start) != 0;
528
0
  }
529
530
  namespace Util {
531
532
0
    bool isPrintable(StyleRule* r, Sass_Output_Style style) {
533
0
      if (r == NULL) {
534
0
        return false;
535
0
      }
536
537
0
      Block_Obj b = r->block();
538
539
0
      SelectorList* sl = r->selector();
540
0
      bool hasSelectors = sl ? sl->length() > 0 : false;
541
542
0
      if (!hasSelectors) {
543
0
        return false;
544
0
      }
545
546
0
      bool hasDeclarations = false;
547
0
      bool hasPrintableChildBlocks = false;
548
0
      for (size_t i = 0, L = b->length(); i < L; ++i) {
549
0
        Statement_Obj stm = b->at(i);
550
0
        if (Cast<AtRule>(stm)) {
551
0
          return true;
552
0
        } else if (Declaration* d = Cast<Declaration>(stm)) {
553
0
          return isPrintable(d, style);
554
0
        } else if (ParentStatement* p = Cast<ParentStatement>(stm)) {
555
0
          Block_Obj pChildBlock = p->block();
556
0
          if (isPrintable(pChildBlock, style)) {
557
0
            hasPrintableChildBlocks = true;
558
0
          }
559
0
        } else if (Comment* c = Cast<Comment>(stm)) {
560
          // keep for uncompressed
561
0
          if (style != COMPRESSED) {
562
0
            hasDeclarations = true;
563
0
          }
564
          // output style compressed
565
0
          if (c->is_important()) {
566
0
            hasDeclarations = c->is_important();
567
0
          }
568
0
        } else {
569
0
          hasDeclarations = true;
570
0
        }
571
572
0
        if (hasDeclarations || hasPrintableChildBlocks) {
573
0
          return true;
574
0
        }
575
0
      }
576
577
0
      return false;
578
0
    }
579
580
    bool isPrintable(String_Constant* s, Sass_Output_Style style)
581
0
    {
582
0
      return ! s->value().empty();
583
0
    }
584
585
    bool isPrintable(String_Quoted* s, Sass_Output_Style style)
586
0
    {
587
0
      return true;
588
0
    }
589
590
    bool isPrintable(Declaration* d, Sass_Output_Style style)
591
0
    {
592
0
      ExpressionObj val = d->value();
593
0
      if (String_Quoted_Obj sq = Cast<String_Quoted>(val)) return isPrintable(sq.ptr(), style);
594
0
      if (String_Constant_Obj sc = Cast<String_Constant>(val)) return isPrintable(sc.ptr(), style);
595
0
      return true;
596
0
    }
597
598
0
    bool isPrintable(SupportsRule* f, Sass_Output_Style style) {
599
0
      if (f == NULL) {
600
0
        return false;
601
0
      }
602
603
0
      Block_Obj b = f->block();
604
605
0
      bool hasDeclarations = false;
606
0
      bool hasPrintableChildBlocks = false;
607
0
      for (size_t i = 0, L = b->length(); i < L; ++i) {
608
0
        Statement_Obj stm = b->at(i);
609
0
        if (Cast<Declaration>(stm) || Cast<AtRule>(stm)) {
610
0
          hasDeclarations = true;
611
0
        }
612
0
        else if (ParentStatement* b = Cast<ParentStatement>(stm)) {
613
0
          Block_Obj pChildBlock = b->block();
614
0
          if (!b->is_invisible()) {
615
0
            if (isPrintable(pChildBlock, style)) {
616
0
              hasPrintableChildBlocks = true;
617
0
            }
618
0
          }
619
0
        }
620
621
0
        if (hasDeclarations || hasPrintableChildBlocks) {
622
0
          return true;
623
0
        }
624
0
      }
625
626
0
      return false;
627
0
    }
628
629
    bool isPrintable(CssMediaRule* m, Sass_Output_Style style)
630
0
    {
631
0
      if (m == nullptr) return false;
632
0
      Block_Obj b = m->block();
633
0
      if (b == nullptr) return false;
634
0
      if (m->empty()) return false;
635
0
      for (size_t i = 0, L = b->length(); i < L; ++i) {
636
0
        Statement_Obj stm = b->at(i);
637
0
        if (Cast<AtRule>(stm)) return true;
638
0
        else if (Cast<Declaration>(stm)) return true;
639
0
        else if (Comment* c = Cast<Comment>(stm)) {
640
0
          if (isPrintable(c, style)) {
641
0
            return true;
642
0
          }
643
0
        }
644
0
        else if (StyleRule* r = Cast<StyleRule>(stm)) {
645
0
          if (isPrintable(r, style)) {
646
0
            return true;
647
0
          }
648
0
        }
649
0
        else if (SupportsRule* f = Cast<SupportsRule>(stm)) {
650
0
          if (isPrintable(f, style)) {
651
0
            return true;
652
0
          }
653
0
        }
654
0
        else if (CssMediaRule* mb = Cast<CssMediaRule>(stm)) {
655
0
          if (isPrintable(mb, style)) {
656
0
            return true;
657
0
          }
658
0
        }
659
0
        else if (ParentStatement* b = Cast<ParentStatement>(stm)) {
660
0
          if (isPrintable(b->block(), style)) {
661
0
            return true;
662
0
          }
663
0
        }
664
0
      }
665
0
      return false;
666
0
    }
667
668
    bool isPrintable(Comment* c, Sass_Output_Style style)
669
0
    {
670
      // keep for uncompressed
671
0
      if (style != COMPRESSED) {
672
0
        return true;
673
0
      }
674
      // output style compressed
675
0
      if (c->is_important()) {
676
0
        return true;
677
0
      }
678
      // not printable
679
0
      return false;
680
0
    };
681
682
0
    bool isPrintable(Block_Obj b, Sass_Output_Style style) {
683
0
      if (!b) {
684
0
        return false;
685
0
      }
686
687
0
      for (size_t i = 0, L = b->length(); i < L; ++i) {
688
0
        Statement_Obj stm = b->at(i);
689
0
        if (Cast<Declaration>(stm) || Cast<AtRule>(stm)) {
690
0
          return true;
691
0
        }
692
0
        else if (Comment* c = Cast<Comment>(stm)) {
693
0
          if (isPrintable(c, style)) {
694
0
            return true;
695
0
          }
696
0
        }
697
0
        else if (StyleRule* r = Cast<StyleRule>(stm)) {
698
0
          if (isPrintable(r, style)) {
699
0
            return true;
700
0
          }
701
0
        }
702
0
        else if (SupportsRule* f = Cast<SupportsRule>(stm)) {
703
0
          if (isPrintable(f, style)) {
704
0
            return true;
705
0
          }
706
0
        }
707
0
        else if (CssMediaRule * m = Cast<CssMediaRule>(stm)) {
708
0
          if (isPrintable(m, style)) {
709
0
            return true;
710
0
          }
711
0
        }
712
0
        else if (ParentStatement* b = Cast<ParentStatement>(stm)) {
713
0
          if (isPrintable(b->block(), style)) {
714
0
            return true;
715
0
          }
716
0
        }
717
0
      }
718
719
0
      return false;
720
0
    }
721
722
  }
723
}