François Cartegnie pushed to branch master at VideoLAN / VLC


Commits:
096eecd2 by François Cartegnie at 2026-01-06T16:17:59+01:00
codec: webvtt: fix leak on failed chained selectors

refs #29451

- - - - -
3270c3cf by François Cartegnie at 2026-01-06T16:17:59+01:00
codec: webvtt: fix bogus check

- - - - -
89e0fe52 by François Cartegnie at 2026-01-06T16:17:59+01:00
codec: webvtt: use NULL instead of 0

- - - - -
96f5580b by François Cartegnie at 2026-01-06T16:17:59+01:00
codec: webvtt: check function strings

- - - - -
43801e41 by François Cartegnie at 2026-01-06T16:17:59+01:00
codec: webvtt: fix potential double free

- - - - -
924b4285 by François Cartegnie at 2026-01-06T16:17:59+01:00
codec: webvtt: fix selector leak on error

refs #29451

- - - - -
3c6279e1 by François Cartegnie at 2026-01-06T16:17:59+01:00
codec: webvtt: add YYNOMEM fallback

- - - - -
a1267c86 by François Cartegnie at 2026-01-06T16:17:59+01:00
codec: webvtt: fix useless check before assignment

- - - - -
842511f0 by François Cartegnie at 2026-01-06T16:17:59+01:00
codec: webvtt: fix grammar error handling and cleanups

- - - - -
61ca5329 by François Cartegnie at 2026-01-06T16:17:59+01:00
test: webvtt: split CSS tests

- - - - -
949502c5 by François Cartegnie at 2026-01-06T16:17:59+01:00
test: webvtt: add ::cue pseudo element test

- - - - -
01f7fc0a by François Cartegnie at 2026-01-06T16:17:59+01:00
test: webvtt: add CSS parser error recovery test

- - - - -
ace499ba by François Cartegnie at 2026-01-06T16:17:59+01:00
codec: webvtt: update CSS parser grammar

Fixes unwanted reduction of IDENT as operator

ex:  el3{color:red !important fail;}
due to operator grammar and declaration error propagation.
  | /* empty */     { $$ = 0; }
and declaration error propagation
  property ':' maybe_space error expr prio
Error handling then leaks the mis-reduced IDENT token.

- - - - -


2 changed files:

- modules/codec/webvtt/CSSGrammar.y
- modules/codec/webvtt/css_test.c


Changes:

=====================================
modules/codec/webvtt/CSSGrammar.y
=====================================
@@ -44,6 +44,10 @@
 #define YY_TYPEDEF_YY_SCANNER_T
 typedef void* yyscan_t;
 #endif
+
+#if YYBISON < 30800
+# define YYNOMEM YYABORT
+#endif
 %}
 
 %union {
@@ -74,7 +78,7 @@ static void yyerror(yyscan_t scanner, vlc_css_parser_t *p, 
const char *msg)
 
 %}
 
-%expect 10
+%expect 7
 
 %nonassoc LOWEST_PREC
 
@@ -189,9 +193,7 @@ maybe_sgml:
 
 maybe_charset:
    /* empty */
-  | charset {
-    vlc_css_rules_Delete($1);
-  }
+  | charset
   ;
 
 closing_brace:
@@ -202,13 +204,13 @@ closing_brace:
 charset:
   CHARSET_SYM maybe_space STRING maybe_space ';' {
       free( $3 );
-      $$ = 0;
+      $$ = NULL;
   }
   | CHARSET_SYM error invalid_block {
-      $$ = 0;
+      $$ = NULL;
   }
   | CHARSET_SYM error ';' {
-      $$ = 0;
+      $$ = NULL;
   }
 ;
 
@@ -216,10 +218,10 @@ ignored_charset:
     CHARSET_SYM maybe_space STRING maybe_space ';' {
         // Ignore any @charset rule not at the beginning of the style sheet.
         free( $3 );
-        $$ = 0;
+        $$ = NULL;
     }
     | CHARSET_SYM maybe_space ';' {
-        $$ = 0;
+        $$ = NULL;
     }
 ;
 
@@ -279,19 +281,16 @@ unary_operator:
 ruleset:
     selector_list '{' maybe_space declaration_list closing_brace {
         $$ = vlc_css_rule_New();
-        if($$)
-        {
-            $$->p_selectors = $1;
-            $$->p_declarations = $4;
-        }
+        if( !$$ )
+            YYNOMEM;
+        $$->p_selectors = $1;
+        $$->p_declarations = $4;
     }
   ;
 
 selector_list:
     selector %prec UNIMPORTANT_TOK {
-        if ($1) {
-            $$ = $1;
-        }
+        $$ = $1;
     }
     | selector_list ',' maybe_space selector %prec UNIMPORTANT_TOK {
         if ($1 && $4 )
@@ -328,22 +327,32 @@ selector:
     }
     | selector_with_trailing_whitespace simple_selector
     {
-        $$ = $1;
-        if ($$ && $2)
+        if( $1 && $2 )
         {
-            vlc_css_selector_AddSpecifier( $$, $2 );
+            vlc_css_selector_AddSpecifier( $1, $2 );
             $2->combinator = RELATION_DESCENDENT;
+            $$ = $1;
+        }
+        else
+        {
+            vlc_css_selectors_Delete( $1 );
+            vlc_css_selectors_Delete( $2 );
+            $$ = NULL;
         }
-        else $$ = $2;
     }
     | selector combinator simple_selector {
-        $$ = $1;
-        if ($$ && $3)
+        if( $1 && $3 )
         {
-            vlc_css_selector_AddSpecifier( $$, $3 );
+            vlc_css_selector_AddSpecifier( $1, $3 );
             $3->combinator = $2;
+            $$ = $1;
+        }
+        else
+        {
+            vlc_css_selectors_Delete( $1 );
+            vlc_css_selectors_Delete( $3 );
+            $$ = NULL;
         }
-        else $$ = $3;
     }
     | selector error {
         vlc_css_selectors_Delete( $1 );
@@ -353,19 +362,27 @@ selector:
 
 simple_selector:
     element_name {
+        if( !$1 )
+        {
+            $$ = NULL;
+            YYERROR;
+        }
         $$ = vlc_css_selector_New( SELECTOR_SIMPLE, $1 );
+        if( !$$ )
+            YYNOMEM; // destructors called
         free( $1 );
     }
     | element_name specifier_list {
-        $$ = vlc_css_selector_New( SELECTOR_SIMPLE, $1 );
-        if( $$ && $2 )
+        if( !$1 )
         {
-            vlc_css_selector_AddSpecifier( $$, $2 );
-        }
-        else
-        {
-            vlc_css_selectors_Delete( $2 );
+            $$ = NULL;
+            YYERROR;
         }
+        $$ = vlc_css_selector_New( SELECTOR_SIMPLE, $1 );
+        if( !$$ )
+            YYNOMEM; // destructors called
+        if( $2 )
+            vlc_css_selector_AddSpecifier( $$, $2 );
         free( $1 );
     }
     | specifier_list {
@@ -388,7 +405,8 @@ specifier_list:
             $$ = $1;
             while( $1->specifiers.p_first )
                 $1 = $1->specifiers.p_first;
-            vlc_css_selector_AddSpecifier( $1, $2 );
+            if( $2 )
+                vlc_css_selector_AddSpecifier( $1, $2 );
         }
         else $$ = $2;
     }
@@ -400,17 +418,28 @@ specifier_list:
 
 specifier:
     IDSEL {
+        if( !$1 )
+        {
+            $$ = NULL;
+            YYERROR;
+        }
         $$ = vlc_css_selector_New( SPECIFIER_ID, $1 );
+        if( !$$ )
+            YYNOMEM; // $1 destructor called
         free( $1 );
     }
     /* Case when #fffaaa like token is lexed as HEX instead of IDSEL */
   | HASH {
-        if ($1[0] >= '0' && $1[0] <= '9') {
+        if ( !$1 || ($1[0] >= '0' && $1[0] <= '9') )
+        {
             $$ = NULL;
+            YYERROR; // $1 destructor called
         } else {
             $$ = vlc_css_selector_New( SPECIFIER_ID, $1 );
+            if( !$$ )
+                YYNOMEM;  // $1 destructor called
+            free( $1 );
         }
-        free( $1 );
     }
   | class
   | attrib
@@ -419,7 +448,14 @@ specifier:
 
 class:
     '.' IDENT {
+        if( !$2 )
+        {
+            $$ = NULL;
+            YYERROR;
+        }
         $$ = vlc_css_selector_New( SPECIFIER_CLASS, $2 );
+        if( !$$ )
+            YYNOMEM; // $2 destructor called
         free( $2 );
     }
   ;
@@ -432,15 +468,29 @@ attr_name:
 
 attrib:
     '[' maybe_space attr_name ']' {
+        if( !$3 )
+        {
+            $$ = NULL;
+            YYERROR;
+        }
         $$ = vlc_css_selector_New( SPECIFIER_ATTRIB, $3 );
         free( $3 );
     }
     | '[' maybe_space attr_name match maybe_space ident_or_string maybe_space 
']' {
+        if( !$6 || !$3 )
+        {
+            $$ = NULL;
+            YYERROR;
+        }
         $$ = vlc_css_selector_New( SPECIFIER_ATTRIB, $3 );
-        if( $$ && $$ )
+        if( !$$ )
+            YYNOMEM;
+        $$->match = $4;
+        $$->p_matchsel = vlc_css_selector_New( SPECIFIER_ID, $6 );
+        if ( !$$->p_matchsel )
         {
-            $$->match = $4;
-            $$->p_matchsel = vlc_css_selector_New( SPECIFIER_ID, $6 );
+            vlc_css_selectors_Delete( $$ );
+            YYNOMEM;
         }
         free( $3 );
         free( $6 );
@@ -475,41 +525,81 @@ ident_or_string:
 
 pseudo:
     ':' IDENT {
+        if( !$2 )
+        {
+            $$ = NULL;
+            YYERROR;
+        }
         $$ = vlc_css_selector_New( SELECTOR_PSEUDOCLASS, $2 );
+        if( !$$ )
+            YYNOMEM;
         free( $2 );
     }
     | ':' ':' IDENT {
+        if( !$3 )
+        {
+            $$ = NULL;
+            YYERROR;
+        }
         $$ = vlc_css_selector_New( SELECTOR_PSEUDOELEMENT, $3 );
+        if( !$$ )
+            YYNOMEM;
         free( $3 );
     }
     // used by :nth-*
     | ':' FUNCTION maybe_space maybe_unary_operator NUMBER maybe_space ')' {
+        if( !$2 )
+        {
+            $$ = NULL;
+            YYERROR;
+        }
+
         if(*$2 != 0)
             $2[strlen($2) - 1] = 0;
         $$ = vlc_css_selector_New( SELECTOR_PSEUDOCLASS, $2 );
+        if( !$$ )
+            YYNOMEM;
         $5.val *= $4;
+
         free( $2 );
         vlc_css_term_Clean( $5 );
     }
     // required for WEBVTT weirdos cue::(::past)
     | ':' ':' FUNCTION maybe_space selector maybe_space ')' {
+        if( !$3 )
+        {
+            $$ = NULL;
+            YYERROR;
+        }
+
         if(*$3 != 0)
             $3[strlen($3) - 1] = 0;
         $$ = vlc_css_selector_New( SELECTOR_PSEUDOELEMENT, $3 );
-        free( $3 );
-        if( $$ && $5 )
+        if( !$$ )
+            YYNOMEM;
+
+        if( $5 )
         {
             vlc_css_selector_AddSpecifier( $$, $5 );
             $5->combinator = RELATION_SELF;
         }
-        else
-            vlc_css_selectors_Delete( $5 );
+
+        free( $3 );
     }
     // used by :nth-*(odd/even) and :lang
     | ':' FUNCTION maybe_space IDENT maybe_space ')' {
+        if( !$2 )
+        {
+            $$ = NULL;
+            YYERROR;
+        }
+
         if(*$2 != 0)
             $2[strlen($2) - 1] = 0;
         $$ = vlc_css_selector_New( SELECTOR_PSEUDOCLASS, $2 );
+        if( !$$ )
+            YYNOMEM;
+
         free( $2 );
         free( $4 );
     }
@@ -577,59 +667,44 @@ decl_list:
 
 declaration:
     property ':' maybe_space expr prio {
-        if( $4 )
+        if( !$1 || !$4 )
         {
-            $$ = vlc_css_declaration_New( $1 );
-            if( $$ )
-                $$->expr = $4;
-            else
-                vlc_css_expression_Delete( $4 );
+            $$ = NULL;
+            YYERROR;
         }
-        else $$ = NULL;
+        $$ = vlc_css_declaration_New( $1 );
+        if( !$$ )
+            YYNOMEM;
+        $$->expr = $4;
         free( $1 );
     }
-    |
-    property error {
+    | property ':' maybe_space expr error {
+        /* e.g. color: red !important fail;  or color: red; garbage */
         free( $1 );
+        vlc_css_expression_Delete( $4 );
         $$ = NULL;
     }
-    |
-    property ':' maybe_space error expr prio {
+    | property ':' maybe_space error {
+        /* color: garbage */
         free( $1 );
-        vlc_css_expression_Delete( $5 );
-        /* The default movable type template has letter-spacing: .none;  
Handle this by looking for
-        error tokens at the start of an expr, recover the expr and then treat 
as an error, cleaning
-        up and deleting the shifted expr.  */
         $$ = NULL;
     }
-    |
-    property ':' maybe_space expr prio error {
+    | property error {
+        /* color garbage */
         free( $1 );
-        vlc_css_expression_Delete( $4 );
-        /* When we encounter something like p {color: red !important fail;} we 
should drop the declaration */
         $$ = NULL;
     }
-    |
-    IMPORTANT_SYM maybe_space {
-        /* Handle this case: div { text-align: center; !important } Just 
reduce away the stray !important. */
-        $$ = NULL;
-    }
-    |
-    property ':' maybe_space {
+    | property ':' maybe_space {
+        /* color: ; */
         free( $1 );
-        /* div { font-family: } Just reduce away this property with no value. 
*/
         $$ = NULL;
     }
-    |
-    property ':' maybe_space error {
+    | property invalid_block {
         free( $1 );
-        /* if we come across rules with invalid values like this case: p { 
weight: *; }, just discard the rule */
         $$ = NULL;
     }
-    |
-    property invalid_block {
-        /* if we come across: div { color{;color:maroon} }, ignore everything 
within curly brackets */
-        free( $1 );
+    | IMPORTANT_SYM maybe_space {
+        /* stray !important */
         $$ = NULL;
     }
   ;
@@ -649,38 +724,38 @@ expr:
     term {
         $$ = vlc_css_expression_New( $1 );
         if( !$$ )
-            vlc_css_term_Clean( $1 );
+            YYNOMEM;
     }
-    | expr operator term {
+    | expr term {
+        if( !$1 )
+        {
+            $$ = NULL;
+            YYERROR;
+        }
         $$ = $1;
-        if( !$1 || !vlc_css_expression_AddTerm($1, $2, $3) )
-            vlc_css_term_Clean( $3 );
+        if( !vlc_css_expression_AddTerm( $1, ' ', $2 ) )
+            YYNOMEM;
     }
-    | expr invalid_block_list {
-        vlc_css_expression_Delete( $1 );
-        $$ = NULL;
-    }
-    | expr invalid_block_list error {
-        vlc_css_expression_Delete( $1 );
-        $$ = NULL;
-    }
-    | expr error {
-        vlc_css_expression_Delete( $1 );
-        $$ = NULL;
+    | expr operator maybe_space term {
+        if( !$1 )
+        {
+            $$ = NULL;
+            YYERROR;
+        }
+        $$ = $1;
+        if( !vlc_css_expression_AddTerm( $1, '/', $4 ) )
+            YYNOMEM;
     }
   ;
 
 operator:
-    '/' maybe_space {
-        $$ = '/';
-    }
-  | ',' maybe_space {
-        $$ = ',';
-    }
-  | /* empty */ {
-        $$ = 0;
-  }
-  ;
+      '/' maybe_space {
+          $$ = '/';
+      }
+    | ',' maybe_space {
+          $$ = ',';
+      }
+    ;
 
 term:
   unary_term { $$ = $1; }
@@ -720,22 +795,22 @@ function:
     FUNCTION maybe_space expr ')' maybe_space {
         $$.type = TYPE_FUNCTION; $$.function = $3;
         $$.psz = $1;
-        if(*$$.psz != 0)
+        if($1 && *$$.psz != 0)
             $$.psz[strlen($$.psz) - 1] = 0;
     } |
     FUNCTION maybe_space expr TOKEN_EOF {
         $$.type = TYPE_FUNCTION; $$.function = $3; $$.psz = $1;
-        if(*$$.psz != 0)
+        if($1 && *$$.psz != 0)
             $$.psz[strlen($$.psz) - 1] = 0;
     } |
     FUNCTION maybe_space ')' maybe_space {
         $$.type = TYPE_FUNCTION; $$.function = NULL; $$.psz = $1;
-        if(*$$.psz != 0)
+        if($1 && *$$.psz != 0)
             $$.psz[strlen($$.psz) - 1] = 0;
     } |
     FUNCTION maybe_space error {
         $$.type = TYPE_FUNCTION; $$.function = NULL; $$.psz = $1;
-        if(*$$.psz != 0)
+        if($1 && *$$.psz != 0)
             $$.psz[strlen($$.psz) - 1] = 0;
   }
   ;


=====================================
modules/codec/webvtt/css_test.c
=====================================
@@ -33,53 +33,25 @@
 #define CHECK(test) run = (test); fprintf(stderr, "* Running test %s\n", run);
 #define EXPECT(foo) if(!(foo)) BAILOUT(run)
 
-const char * css =
+#define PARSE_CSS(r) \
+    const char *run = r;\
+    vlc_css_parser_t p;\
+    vlc_css_parser_Init(&p);\
+    bool b = vlc_css_parser_ParseBytes(&p, (const uint8_t *)css, strlen(css));\
+    EXPECT(b);\
+    vlc_css_parser_Debug(&p);\
+    const vlc_css_rule_t *rule = p.rules.p_first
+
+
+static int test_element_selectors(void)
+{
+    const char * css =
         "el1 { float0: 1; }\n"
         ".class1 { hex1: #F0000f; }\n"
         "#id1 { text2: \"foo bar\"; }\n"
-        ":pseudo { text2: \"foobar\"; }\n"
-        "attrib[foo=\"bar\"] { text2: \"foobar\"; }\n"
-        "attrib2[foo] { text2: \"foobar\"; }\n"
-        "attribincludes[foo~=\"bar\"] { text2: \"foobar\"; }\n"
-        "attribdashmatch[foo|=\"bar\"] { text2: \"foobar\"; }\n"
-        "attribstarts[foo^=\"bar\"] { text2: \"foobar\"; }\n"
-        "attribends[foo$=\"bar\"] { text2: \"foobar\"; }\n"
-        "attribcontains[foo*=\"bar\"] { text2: \"foobar\"; }\n"
-        "parent1 child1 { float0: 1; }\n"
-        "el2,el3 { float0: 1; }\n"
-        "el4+el0 { float0: 1; }\n"
-        "el5~el0 { float0: 1; }\n"
-        "el6>el0 { float0: 1; }\n"
-        "values { "
-        "  neg: -1;"
-        "  ems: 100em;"
-        "  exs: 100ex;"
-        "  pixels: 100px;"
-        "  points: 100pt; "
-        "  mm: 100mm;"
-        "  percent: 100%;"
-        "  ms: 100ms;"
-        "  hz: 100Hz;"
-        "  degrees: 100deg;"
-        "  dimension: 100 -200em 300px;"
-        "  string: \"foobar\";"
-        "  function: foo(1);"
-        "  identifier: foobar;"
-        "  hexcolor: #ff00ff;"
-        "  unicoderange: U+00-FF;"
-        "  uri: url(http://crap/);"
-        "}\n"
-;
+    ;
 
-int main(void)
-{
-    const char *run ="parsing";
-    vlc_css_parser_t p;
-    vlc_css_parser_Init(&p);
-    bool b = vlc_css_parser_ParseBytes(&p, (const uint8_t *)css, strlen(css));
-    EXPECT(b);
-    vlc_css_parser_Debug(&p);
-    const vlc_css_rule_t *rule = p.rules.p_first;
+    PARSE_CSS("test_element_selectors");
 
     CHECK("element selector");
     EXPECT(rule && rule->b_valid);
@@ -113,14 +85,29 @@ int main(void)
     EXPECT(decl->expr->seq[0].term.type == TYPE_STRING);
     EXPECT(!strcmp(decl->expr->seq[0].term.psz,"foo bar"));
 
-    CHECK("pseudoclass selector");
-    rule = rule->p_next;
-    EXPECT(rule && rule->b_valid);
-    EXPECT(!strcmp(rule->p_selectors->psz_name,"pseudo"));
-    EXPECT(rule->p_selectors->type == SELECTOR_PSEUDOCLASS);
+    vlc_css_parser_Clean(&p);
+    return 0;
+
+error:
+    vlc_css_parser_Clean(&p);
+    return 1;
+}
+
+static int test_attributes_selectors(void)
+{
+    const char * css =
+        "attrib[foo=\"bar\"] { text2: \"foobar\"; }\n"
+        "attrib2[foo] { text2: \"foobar\"; }\n"
+        "attribincludes[foo~=\"bar\"] { text2: \"foobar\"; }\n"
+        "attribdashmatch[foo|=\"bar\"] { text2: \"foobar\"; }\n"
+        "attribstarts[foo^=\"bar\"] { text2: \"foobar\"; }\n"
+        "attribends[foo$=\"bar\"] { text2: \"foobar\"; }\n"
+        "attribcontains[foo*=\"bar\"] { text2: \"foobar\"; }\n"
+    ;
+
+    PARSE_CSS("test_attributes_selectors");
 
     CHECK("attribute selector equals");
-    rule = rule->p_next;
     EXPECT(rule && rule->b_valid);
     EXPECT(!strcmp(rule->p_selectors->psz_name,"attrib"));
     EXPECT(rule->p_selectors->type == SELECTOR_SIMPLE);
@@ -201,9 +188,72 @@ int main(void)
     EXPECT(rule->p_selectors->specifiers.p_first->p_matchsel);
     
EXPECT(!strcmp(rule->p_selectors->specifiers.p_first->p_matchsel->psz_name, 
"bar"));
 
-    CHECK("selectors combination parent child");
+    vlc_css_parser_Clean(&p);
+    return 0;
+
+error:
+    vlc_css_parser_Clean(&p);
+    return 1;
+}
+
+static int test_pseudo_selectors(void)
+{
+    const char * css =
+        ":pseudo { text2: \"foobar\"; }\n"
+        "::cue(b) { color: red; }\n" /* WebVTT special */
+        "::cue(v[voice=\"Tom\"]) { color: blue; }\n"
+    ;
+
+    PARSE_CSS("test_pseudo_selectors");
+
+    CHECK("pseudoclass selector");
+    EXPECT(rule && rule->b_valid);
+    EXPECT(!strcmp(rule->p_selectors->psz_name,"pseudo"));
+    EXPECT(rule->p_selectors->type == SELECTOR_PSEUDOCLASS);
+
+    CHECK("pseudoelement ::cue");
+    rule = rule->p_next;
+    EXPECT(rule && rule->b_valid);
+    EXPECT(!strcmp(rule->p_selectors->psz_name,"cue"));
+    EXPECT(rule->p_selectors->type == SELECTOR_PSEUDOELEMENT);
+
+    CHECK("pseudoelement ::cue(v[voice])");
     rule = rule->p_next;
     EXPECT(rule && rule->b_valid);
+    EXPECT(!strcmp(rule->p_selectors->psz_name,"cue"));
+    EXPECT(rule->p_selectors->type == SELECTOR_PSEUDOELEMENT);
+    EXPECT(rule->p_selectors->specifiers.p_first);
+    EXPECT(rule->p_selectors->specifiers.p_first->type == SELECTOR_SIMPLE);
+    EXPECT(!strcmp(rule->p_selectors->specifiers.p_first->psz_name, "v"));
+    EXPECT(rule->p_selectors->specifiers.p_first->specifiers.p_first);
+    EXPECT(rule->p_selectors->specifiers.p_first->specifiers.p_first->type == 
SPECIFIER_ATTRIB);
+    
EXPECT(!strcmp(rule->p_selectors->specifiers.p_first->specifiers.p_first->psz_name,
 "voice"));
+    
EXPECT(rule->p_selectors->specifiers.p_first->specifiers.p_first->p_matchsel);
+    
EXPECT(rule->p_selectors->specifiers.p_first->specifiers.p_first->p_matchsel->match
 == MATCH_EQUALS);
+    
EXPECT(!strcmp(rule->p_selectors->specifiers.p_first->specifiers.p_first->p_matchsel->psz_name,
 "Tom"));
+
+    vlc_css_parser_Clean(&p);
+    return 0;
+
+error:
+    vlc_css_parser_Clean(&p);
+    return 1;
+}
+
+static int test_combinators(void)
+{
+    const char * css =
+        "parent1 child1 { float0: 1; }\n"
+        "el2,el3 { float0: 1; }\n"
+        "el4+el0 { float0: 1; }\n"
+        "el5~el0 { float0: 1; }\n"
+        "el6>el0 { float0: 1; }\n"
+    ;
+
+    PARSE_CSS("test_combinators");
+
+    CHECK("selectors combination parent child");
+    EXPECT(rule && rule->b_valid);
     EXPECT(!strcmp(rule->p_selectors->psz_name,"parent1"));
     EXPECT(rule->p_selectors->specifiers.p_first);
     EXPECT(rule->p_selectors->specifiers.p_first->combinator == 
RELATION_DESCENDENT);
@@ -240,11 +290,44 @@ int main(void)
     EXPECT(rule->p_selectors->specifiers.p_first->combinator == 
RELATION_CHILD);
     EXPECT(!strcmp(rule->p_selectors->specifiers.p_first->psz_name, "el0"));
 
+    vlc_css_parser_Clean(&p);
+    return 0;
+
+error:
+    vlc_css_parser_Clean(&p);
+    return 1;
+}
+
+static int test_values(void)
+{
+    const char * css =
+        "values { "
+        "  neg: -1;"
+        "  ems: 100em;"
+        "  exs: 100ex;"
+        "  pixels: 100px;"
+        "  points: 100pt; "
+        "  mm: 100mm;"
+        "  percent: 100%;"
+        "  ms: 100ms;"
+        "  hz: 100Hz;"
+        "  degrees: 100deg;"
+        "  dimension: 100 -200em 300px;"
+        "  string: \"foobar\";"
+        "  function: foo(1);"
+        "  identifier: foobar;"
+        "  hexcolor: #ff00ff;"
+        "  unicoderange: U+00-FF;"
+        "  uri: url(http://crap/);"
+        "}\n"
+    ;
+
+    PARSE_CSS("test_values");
+
     CHECK("values");
-    rule = rule->p_next;
     EXPECT(rule && rule->b_valid);
     EXPECT(!strcmp(rule->p_selectors->psz_name,"values"));
-    decl = rule->p_declarations;
+    const vlc_css_declaration_t *decl = rule->p_declarations;
     EXPECT(decl && !strcmp(decl->psz_property, "neg"));
     EXPECT(decl->expr && decl->expr->i_count);
     EXPECT(decl->expr->seq[0].term.type == TYPE_NONE);
@@ -341,3 +424,57 @@ error:
     vlc_css_parser_Clean(&p);
     return 1;
 }
+
+static int test_error_cases(void)
+{
+    const char *css =
+        /* Invalid charset */
+        "@charset \"UTF-8\" extra;\n"
+        "@charset ;\n"
+        /* Invalid block */
+        "el1 { color { ; color: red } }\n"
+        "el2 { color: red; }\n"
+        "el3 { color: red !important fail; }\n"
+        "el4 { font-family: ; }\n"
+        "el5 { weight: *; }\n"
+        "el6 { color: red; !important }\n"
+        /* Invalid selectors */
+        "#123abc { color: red; }\n" /* HASH starting with digit triggers 
YYERROR */
+        "el7 + ;\n" /* combinators without right side */
+        "el8 ~ ;\n"
+        "el9 > ;\n"
+        "el10 , ;\n"
+        /* Invalid declarations */
+        "div { ; }\n"
+        "div { color: ; }\n"
+        "div { color: *; }\n"
+        /* Invalid function / expr */
+        "div { foo(): 1; }\n"
+        "div { bar( ; }\n"
+    ;
+
+    PARSE_CSS("test_error_cases");
+
+    /* We just want to make sure the parser
+     * does not crash on error handling and backtracking
+     * and frees memory */
+
+    VLC_UNUSED(rule);
+
+    vlc_css_parser_Clean(&p);
+    return 0;
+
+error:
+    vlc_css_parser_Clean(&p);
+    return 1;
+}
+
+int main(void)
+{
+    return test_element_selectors() ||
+           test_attributes_selectors() ||
+           test_pseudo_selectors() ||
+           test_combinators() ||
+           test_values() ||
+           test_error_cases();
+}



View it on GitLab: 
https://code.videolan.org/videolan/vlc/-/compare/15c9a6e8a3a4ec54ec85d75f415fb521f84867a1...ace499bad067b3aed23dd1a02e3b6c61b929526b

-- 
View it on GitLab: 
https://code.videolan.org/videolan/vlc/-/compare/15c9a6e8a3a4ec54ec85d75f415fb521f84867a1...ace499bad067b3aed23dd1a02e3b6c61b929526b
You're receiving this email because of your account on code.videolan.org.


VideoLAN code repository instance
_______________________________________________
vlc-commits mailing list
[email protected]
https://mailman.videolan.org/listinfo/vlc-commits

Reply via email to