1 /*
2  * Copyright © 2011 Red Hat Inc.
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library. If not, see <http://www.gnu.org/licenses/>.
16  *
17  * Authors: Benjamin Otte <otte@gnome.org>
18  */
19 
20 #include "config.h"
21 
22 #include "gtkcssshorthandpropertyprivate.h"
23 
24 #include <cairo-gobject.h>
25 #include <math.h>
26 
27 #include "gtkcssarrayvalueprivate.h"
28 #include "gtkcssbgsizevalueprivate.h"
29 #include "gtkcssbordervalueprivate.h"
30 #include "gtkcsscolorvalueprivate.h"
31 #include "gtkcsscornervalueprivate.h"
32 #include "gtkcsseasevalueprivate.h"
33 #include "gtkcssenumvalueprivate.h"
34 #include "gtkcssimageprivate.h"
35 #include "gtkcssimagevalueprivate.h"
36 #include "gtkcssnumbervalueprivate.h"
37 #include "gtkcsspositionvalueprivate.h"
38 #include "gtkcssrepeatvalueprivate.h"
39 #include "gtkcssstringvalueprivate.h"
40 #include "gtkcssstylefuncsprivate.h"
41 #include "gtkcssvalueprivate.h"
42 #include "deprecated/gtkstylepropertiesprivate.h"
43 #include "gtktypebuiltins.h"
44 
45 /* this is in case round() is not provided by the compiler,
46  * such as in the case of C89 compilers, like MSVC
47  */
48 #include "fallback-c89.c"
49 
50 /*** PARSING ***/
51 
52 static gboolean
value_is_done_parsing(GtkCssParser * parser)53 value_is_done_parsing (GtkCssParser *parser)
54 {
55   return _gtk_css_parser_is_eof (parser) ||
56          _gtk_css_parser_begins_with (parser, ',') ||
57          _gtk_css_parser_begins_with (parser, ';') ||
58          _gtk_css_parser_begins_with (parser, '}');
59 }
60 
61 static gboolean
parse_four_numbers(GtkCssShorthandProperty * shorthand,GtkCssValue ** values,GtkCssParser * parser,GtkCssNumberParseFlags flags)62 parse_four_numbers (GtkCssShorthandProperty  *shorthand,
63                     GtkCssValue             **values,
64                     GtkCssParser             *parser,
65                     GtkCssNumberParseFlags    flags)
66 {
67   guint i;
68 
69   for (i = 0; i < 4; i++)
70     {
71       if (!gtk_css_number_value_can_parse (parser))
72         break;
73 
74       values[i] = _gtk_css_number_value_parse (parser, flags);
75       if (values[i] == NULL)
76         return FALSE;
77     }
78 
79   if (i == 0)
80     {
81       _gtk_css_parser_error (parser, "Expected a length");
82       return FALSE;
83     }
84 
85   for (; i < 4; i++)
86     {
87       values[i] = _gtk_css_value_ref (values[(i - 1) >> 1]);
88     }
89 
90   return TRUE;
91 }
92 
93 static gboolean
parse_margin(GtkCssShorthandProperty * shorthand,GtkCssValue ** values,GtkCssParser * parser)94 parse_margin (GtkCssShorthandProperty  *shorthand,
95               GtkCssValue             **values,
96               GtkCssParser             *parser)
97 {
98   return parse_four_numbers (shorthand,
99                              values,
100                              parser,
101                              GTK_CSS_NUMBER_AS_PIXELS
102                              | GTK_CSS_PARSE_LENGTH);
103 }
104 
105 static gboolean
parse_padding(GtkCssShorthandProperty * shorthand,GtkCssValue ** values,GtkCssParser * parser)106 parse_padding (GtkCssShorthandProperty  *shorthand,
107                GtkCssValue             **values,
108                GtkCssParser             *parser)
109 {
110   return parse_four_numbers (shorthand,
111                              values,
112                              parser,
113                              GTK_CSS_POSITIVE_ONLY
114                              | GTK_CSS_NUMBER_AS_PIXELS
115                              | GTK_CSS_PARSE_LENGTH);
116 }
117 
118 static gboolean
parse_border_width(GtkCssShorthandProperty * shorthand,GtkCssValue ** values,GtkCssParser * parser)119 parse_border_width (GtkCssShorthandProperty  *shorthand,
120                     GtkCssValue             **values,
121                     GtkCssParser             *parser)
122 {
123   return parse_four_numbers (shorthand,
124                              values,
125                              parser,
126                              GTK_CSS_POSITIVE_ONLY
127                              | GTK_CSS_NUMBER_AS_PIXELS
128                              | GTK_CSS_PARSE_LENGTH);
129 }
130 
131 static gboolean
parse_border_radius(GtkCssShorthandProperty * shorthand,GtkCssValue ** values,GtkCssParser * parser)132 parse_border_radius (GtkCssShorthandProperty  *shorthand,
133                      GtkCssValue             **values,
134                      GtkCssParser             *parser)
135 {
136   GtkCssValue *x[4] = { NULL, }, *y[4] = { NULL, };
137   guint i;
138 
139   for (i = 0; i < 4; i++)
140     {
141       if (!gtk_css_number_value_can_parse (parser))
142         break;
143       x[i] = _gtk_css_number_value_parse (parser,
144                                           GTK_CSS_POSITIVE_ONLY
145                                           | GTK_CSS_PARSE_PERCENT
146                                           | GTK_CSS_NUMBER_AS_PIXELS
147                                           | GTK_CSS_PARSE_LENGTH);
148       if (x[i] == NULL)
149         goto fail;
150     }
151 
152   if (i == 0)
153     {
154       _gtk_css_parser_error (parser, "Expected a number");
155       goto fail;
156     }
157 
158   /* The magic (i - 1) >> 1 below makes it take the correct value
159    * according to spec. Feel free to check the 4 cases
160    */
161   for (; i < 4; i++)
162     x[i] = _gtk_css_value_ref (x[(i - 1) >> 1]);
163 
164   if (_gtk_css_parser_try (parser, "/", TRUE))
165     {
166       for (i = 0; i < 4; i++)
167         {
168           if (!gtk_css_number_value_can_parse (parser))
169             break;
170           y[i] = _gtk_css_number_value_parse (parser,
171                                               GTK_CSS_POSITIVE_ONLY
172                                               | GTK_CSS_PARSE_PERCENT
173                                               | GTK_CSS_NUMBER_AS_PIXELS
174                                               | GTK_CSS_PARSE_LENGTH);
175           if (y[i] == NULL)
176             goto fail;
177         }
178 
179       if (i == 0)
180         {
181           _gtk_css_parser_error (parser, "Expected a number");
182           goto fail;
183         }
184 
185       for (; i < 4; i++)
186         y[i] = _gtk_css_value_ref (y[(i - 1) >> 1]);
187     }
188   else
189     {
190       for (i = 0; i < 4; i++)
191         y[i] = _gtk_css_value_ref (x[i]);
192     }
193 
194   for (i = 0; i < 4; i++)
195     {
196       values[i] = _gtk_css_corner_value_new (x[i], y[i]);
197     }
198 
199   return TRUE;
200 
201 fail:
202   for (i = 0; i < 4; i++)
203     {
204       if (x[i])
205         _gtk_css_value_unref (x[i]);
206       if (y[i])
207         _gtk_css_value_unref (y[i]);
208     }
209   return FALSE;
210 }
211 
212 static gboolean
parse_border_color(GtkCssShorthandProperty * shorthand,GtkCssValue ** values,GtkCssParser * parser)213 parse_border_color (GtkCssShorthandProperty  *shorthand,
214                     GtkCssValue             **values,
215                     GtkCssParser             *parser)
216 {
217   guint i;
218 
219   for (i = 0; i < 4; i++)
220     {
221       values[i] = _gtk_css_color_value_parse (parser);
222       if (values[i] == NULL)
223         return FALSE;
224 
225       if (value_is_done_parsing (parser))
226         break;
227     }
228 
229   for (i++; i < 4; i++)
230     {
231       values[i] = _gtk_css_value_ref (values[(i - 1) >> 1]);
232     }
233 
234   return TRUE;
235 }
236 
237 static gboolean
parse_border_style(GtkCssShorthandProperty * shorthand,GtkCssValue ** values,GtkCssParser * parser)238 parse_border_style (GtkCssShorthandProperty  *shorthand,
239                     GtkCssValue             **values,
240                     GtkCssParser             *parser)
241 {
242   guint i;
243 
244   for (i = 0; i < 4; i++)
245     {
246       values[i] = _gtk_css_border_style_value_try_parse (parser);
247       if (values[i] == NULL)
248         break;
249     }
250 
251   if (i == 0)
252     {
253       _gtk_css_parser_error (parser, "Expected a border style");
254       return FALSE;
255     }
256 
257   for (; i < 4; i++)
258     values[i] = _gtk_css_value_ref (values[(i - 1) >> 1]);
259 
260   return TRUE;
261 }
262 
263 static gboolean
parse_border_image(GtkCssShorthandProperty * shorthand,GtkCssValue ** values,GtkCssParser * parser)264 parse_border_image (GtkCssShorthandProperty  *shorthand,
265                     GtkCssValue             **values,
266                     GtkCssParser             *parser)
267 {
268   do
269     {
270       if (values[0] == NULL &&
271           (_gtk_css_parser_has_prefix (parser, "none") ||
272            _gtk_css_image_can_parse (parser)))
273         {
274           GtkCssImage *image;
275 
276           if (_gtk_css_parser_try (parser, "none", TRUE))
277             image = NULL;
278           else
279             {
280               image = _gtk_css_image_new_parse (parser);
281               if (image == NULL)
282                 return FALSE;
283             }
284 
285           values[0] = _gtk_css_image_value_new (image);
286         }
287       else if (values[3] == NULL &&
288                (values[3] = _gtk_css_border_repeat_value_try_parse (parser)))
289         {
290           /* please move along */
291         }
292       else if (values[1] == NULL)
293         {
294           values[1] = _gtk_css_border_value_parse (parser,
295                                                    GTK_CSS_PARSE_PERCENT
296                                                    | GTK_CSS_PARSE_NUMBER
297                                                    | GTK_CSS_POSITIVE_ONLY,
298                                                    FALSE,
299                                                    TRUE);
300           if (values[1] == NULL)
301             return FALSE;
302 
303           if (_gtk_css_parser_try (parser, "/", TRUE))
304             {
305               values[2] = _gtk_css_border_value_parse (parser,
306                                                        GTK_CSS_PARSE_PERCENT
307                                                        | GTK_CSS_PARSE_LENGTH
308                                                        | GTK_CSS_PARSE_NUMBER
309                                                        | GTK_CSS_POSITIVE_ONLY,
310                                                        TRUE,
311                                                        FALSE);
312               if (values[2] == NULL)
313                 return FALSE;
314             }
315         }
316       else
317         {
318           /* We parsed everything and there's still stuff left?
319            * Pretend we didn't notice and let the normal code produce
320            * a 'junk at end of value' error
321            */
322           break;
323         }
324     }
325   while (!value_is_done_parsing (parser));
326 
327   return TRUE;
328 }
329 
330 static gboolean
parse_border_side(GtkCssShorthandProperty * shorthand,GtkCssValue ** values,GtkCssParser * parser)331 parse_border_side (GtkCssShorthandProperty  *shorthand,
332                    GtkCssValue             **values,
333                    GtkCssParser             *parser)
334 {
335   do
336   {
337     if (values[0] == NULL &&
338         gtk_css_number_value_can_parse (parser))
339       {
340         values[0] = _gtk_css_number_value_parse (parser,
341                                                  GTK_CSS_POSITIVE_ONLY
342                                                  | GTK_CSS_NUMBER_AS_PIXELS
343                                                  | GTK_CSS_PARSE_LENGTH);
344         if (values[0] == NULL)
345           return FALSE;
346       }
347     else if (values[1] == NULL &&
348              (values[1] = _gtk_css_border_style_value_try_parse (parser)))
349       {
350         /* Nothing to do */
351       }
352     else if (values[2] == NULL)
353       {
354         values[2] = _gtk_css_color_value_parse (parser);
355         if (values[2] == NULL)
356           return FALSE;
357       }
358     else
359       {
360         /* We parsed and there's still stuff left?
361          * Pretend we didn't notice and let the normal code produce
362          * a 'junk at end of value' error
363          */
364         break;
365       }
366   }
367   while (!value_is_done_parsing (parser));
368 
369   return TRUE;
370 }
371 
372 static gboolean
parse_border(GtkCssShorthandProperty * shorthand,GtkCssValue ** values,GtkCssParser * parser)373 parse_border (GtkCssShorthandProperty  *shorthand,
374               GtkCssValue             **values,
375               GtkCssParser             *parser)
376 {
377   do
378   {
379     if (values[0] == NULL &&
380         gtk_css_number_value_can_parse (parser))
381       {
382         values[0] = _gtk_css_number_value_parse (parser,
383                                                  GTK_CSS_POSITIVE_ONLY
384                                                  | GTK_CSS_NUMBER_AS_PIXELS
385                                                  | GTK_CSS_PARSE_LENGTH);
386         if (values[0] == NULL)
387           return FALSE;
388         values[1] = _gtk_css_value_ref (values[0]);
389         values[2] = _gtk_css_value_ref (values[0]);
390         values[3] = _gtk_css_value_ref (values[0]);
391       }
392     else if (values[4] == NULL &&
393              (values[4] = _gtk_css_border_style_value_try_parse (parser)))
394       {
395         values[5] = _gtk_css_value_ref (values[4]);
396         values[6] = _gtk_css_value_ref (values[4]);
397         values[7] = _gtk_css_value_ref (values[4]);
398       }
399     else if (values[8] == NULL)
400       {
401         values[8] = _gtk_css_color_value_parse (parser);
402         if (values[8] == NULL)
403           return FALSE;
404 
405         values[9] = _gtk_css_value_ref (values[8]);
406         values[10] = _gtk_css_value_ref (values[8]);
407         values[11] = _gtk_css_value_ref (values[8]);
408       }
409     else
410       {
411         /* We parsed everything and there's still stuff left?
412          * Pretend we didn't notice and let the normal code produce
413          * a 'junk at end of value' error
414          */
415         break;
416       }
417   }
418   while (!value_is_done_parsing (parser));
419 
420   /* Note that border-image values are not set: according to the spec
421    * they just need to be reset when using the border shorthand
422    */
423 
424   return TRUE;
425 }
426 
427 static gboolean
parse_font_with_pango(GtkCssShorthandProperty * shorthand,GtkCssValue ** values,GtkCssParser * parser)428 parse_font_with_pango (GtkCssShorthandProperty  *shorthand,
429                        GtkCssValue             **values,
430                        GtkCssParser             *parser)
431 {
432   PangoFontDescription *desc;
433   guint mask;
434   char *str;
435 
436   str = _gtk_css_parser_read_value (parser);
437   if (str == NULL)
438     return FALSE;
439 
440   desc = pango_font_description_from_string (str);
441   g_free (str);
442 
443   mask = pango_font_description_get_set_fields (desc);
444 
445   if (mask & PANGO_FONT_MASK_FAMILY)
446     {
447       values[0] = _gtk_css_array_value_new (_gtk_css_string_value_new (pango_font_description_get_family (desc)));
448     }
449   if (mask & PANGO_FONT_MASK_STYLE)
450     {
451       values[1] = _gtk_css_font_style_value_new (pango_font_description_get_style (desc));
452     }
453   if (mask & PANGO_FONT_MASK_VARIANT)
454     {
455       values[2] = _gtk_css_font_variant_value_new (pango_font_description_get_variant (desc));
456     }
457   if (mask & PANGO_FONT_MASK_WEIGHT)
458     {
459       values[3] = _gtk_css_font_weight_value_new (pango_font_description_get_weight (desc));
460     }
461   if (mask & PANGO_FONT_MASK_STRETCH)
462     {
463       values[4] = _gtk_css_font_stretch_value_new (pango_font_description_get_stretch (desc));
464     }
465   if (mask & PANGO_FONT_MASK_SIZE)
466     {
467       values[5] = _gtk_css_number_value_new ((double) pango_font_description_get_size (desc) / PANGO_SCALE, GTK_CSS_PX);
468     }
469 
470   pango_font_description_free (desc);
471 
472   return TRUE;
473 }
474 
475 static gboolean
parse_font(GtkCssShorthandProperty * shorthand,GtkCssValue ** values,GtkCssParser * parser)476 parse_font (GtkCssShorthandProperty  *shorthand,
477             GtkCssValue             **values,
478             GtkCssParser             *parser)
479 {
480   gboolean parsed_one;
481 
482   do
483     {
484       parsed_one = FALSE;
485 
486       if (values[1] == NULL)
487         {
488           values[1] = _gtk_css_font_style_value_try_parse (parser);
489           parsed_one = parsed_one || values[1] != NULL;
490         }
491 
492       if (values[2] == NULL)
493         {
494           values[2] = _gtk_css_font_variant_value_try_parse (parser);
495           parsed_one = parsed_one || values[2] != NULL;
496         }
497 
498       if (values[3] == NULL)
499         {
500           values[3] = _gtk_css_font_weight_value_try_parse (parser);
501           parsed_one = parsed_one || values[3] != NULL;
502         }
503 
504       if (values[4] == NULL)
505         {
506           values[4] = _gtk_css_font_stretch_value_try_parse (parser);
507           parsed_one = parsed_one || values[4] != NULL;
508         }
509     }
510   while (parsed_one && !value_is_done_parsing (parser));
511 
512   values[5] = gtk_css_font_size_value_parse (parser);
513 
514   if (values[1] == NULL && values[2] == NULL && values[3] == NULL &&
515       values[4] == NULL && values[5] == NULL)
516     {
517       if (parse_font_with_pango (shorthand, values, parser))
518         {
519           _gtk_css_parser_error_full (parser,
520                                       GTK_CSS_PROVIDER_ERROR_DEPRECATED,
521                                       "Using Pango syntax for the font: style property is deprecated; please use CSS syntax");
522           return TRUE;
523         }
524     }
525 
526   values[0] = gtk_css_font_family_value_parse (parser);
527 
528   return values[0] != NULL && values[5] != NULL;
529 }
530 
531 static gboolean
parse_one_background(GtkCssShorthandProperty * shorthand,GtkCssValue ** values,GtkCssParser * parser)532 parse_one_background (GtkCssShorthandProperty  *shorthand,
533                       GtkCssValue             **values,
534                       GtkCssParser             *parser)
535 {
536   GtkCssValue *value = NULL;
537 
538   do
539     {
540       /* the image part */
541       if (values[0] == NULL &&
542           (_gtk_css_parser_has_prefix (parser, "none") ||
543            _gtk_css_image_can_parse (parser)))
544         {
545           GtkCssImage *image;
546 
547           if (_gtk_css_parser_try (parser, "none", TRUE))
548             image = NULL;
549           else
550             {
551               image = _gtk_css_image_new_parse (parser);
552               if (image == NULL)
553                 return FALSE;
554             }
555 
556           values[0] = _gtk_css_image_value_new (image);
557         }
558       else if (values[1] == NULL &&
559                (value = _gtk_css_position_value_try_parse (parser)))
560         {
561           values[1] = value;
562           value = NULL;
563 
564           if (_gtk_css_parser_try (parser, "/", TRUE) &&
565               (value = _gtk_css_bg_size_value_parse (parser)))
566             {
567               values[2] = value;
568               value = NULL;
569             }
570         }
571       else if (values[3] == NULL &&
572                (value = _gtk_css_background_repeat_value_try_parse (parser)))
573         {
574           values[3] = value;
575           value = NULL;
576         }
577       else if ((values[4] == NULL || values[5] == NULL) &&
578                (value = _gtk_css_area_value_try_parse (parser)))
579         {
580           values[4] = value;
581 
582           if (values[5] == NULL)
583             {
584               values[5] = values[4];
585               values[4] = NULL;
586             }
587           value = NULL;
588         }
589       else if (values[6] == NULL)
590         {
591           value = _gtk_css_color_value_parse (parser);
592           if (value == NULL)
593             values[6] = _gtk_css_value_ref (_gtk_css_style_property_get_initial_value
594                                             (_gtk_css_shorthand_property_get_subproperty (shorthand, 6)));
595           else
596             values[6] = value;
597 
598           value = NULL;
599         }
600       else
601         {
602           /* We parsed everything and there's still stuff left?
603            * Pretend we didn't notice and let the normal code produce
604            * a 'junk at end of value' error
605            */
606           break;
607         }
608     }
609   while (!value_is_done_parsing (parser));
610 
611   if (values[5] != NULL && values[4] == NULL)
612     values[4] = _gtk_css_value_ref (values[5]);
613 
614   return TRUE;
615 }
616 
617 static gboolean
parse_background(GtkCssShorthandProperty * shorthand,GtkCssValue ** values,GtkCssParser * parser)618 parse_background (GtkCssShorthandProperty  *shorthand,
619                   GtkCssValue             **values,
620                   GtkCssParser             *parser)
621 {
622   GtkCssValue *step_values[7];
623   GPtrArray *arrays[6];
624   guint i;
625 
626   for (i = 0; i < 6; i++)
627     {
628       arrays[i] = g_ptr_array_new ();
629       step_values[i] = NULL;
630     }
631 
632   step_values[6] = NULL;
633 
634   do {
635     if (!parse_one_background (shorthand, step_values, parser))
636       {
637         for (i = 0; i < 6; i++)
638           {
639             g_ptr_array_set_free_func (arrays[i], (GDestroyNotify) _gtk_css_value_unref);
640             g_ptr_array_unref (arrays[i]);
641           }
642         return FALSE;
643       }
644 
645       for (i = 0; i < 6; i++)
646         {
647           if (step_values[i] == NULL)
648             {
649               GtkCssValue *initial = _gtk_css_style_property_get_initial_value (
650                                          _gtk_css_shorthand_property_get_subproperty (shorthand, i));
651               step_values[i] = _gtk_css_value_ref (_gtk_css_array_value_get_nth (initial, 0));
652             }
653 
654           g_ptr_array_add (arrays[i], step_values[i]);
655           step_values[i] = NULL;
656         }
657   } while (_gtk_css_parser_try (parser, ",", TRUE));
658 
659   for (i = 0; i < 6; i++)
660     {
661       values[i] = _gtk_css_array_value_new_from_array ((GtkCssValue **) arrays[i]->pdata, arrays[i]->len);
662       g_ptr_array_unref (arrays[i]);
663     }
664 
665   values[6] = step_values[6];
666 
667   return TRUE;
668 }
669 
670 static gboolean
parse_one_transition(GtkCssShorthandProperty * shorthand,GtkCssValue ** values,GtkCssParser * parser)671 parse_one_transition (GtkCssShorthandProperty  *shorthand,
672                       GtkCssValue             **values,
673                       GtkCssParser             *parser)
674 {
675   do
676     {
677       /* the image part */
678       if (values[2] == NULL &&
679           gtk_css_number_value_can_parse (parser) && !_gtk_css_parser_begins_with (parser, '-'))
680         {
681           GtkCssValue *number = _gtk_css_number_value_parse (parser, GTK_CSS_PARSE_TIME);
682 
683           if (number == NULL)
684             return FALSE;
685 
686           if (values[1] == NULL)
687             values[1] = number;
688           else
689             values[2] = number;
690         }
691       else if (values[3] == NULL &&
692                _gtk_css_ease_value_can_parse (parser))
693         {
694           values[3] = _gtk_css_ease_value_parse (parser);
695 
696           if (values[3] == NULL)
697             return FALSE;
698         }
699       else if (values[0] == NULL)
700         {
701           values[0] = _gtk_css_ident_value_try_parse (parser);
702           if (values[0] == NULL)
703             {
704               _gtk_css_parser_error (parser, "Unknown value for property");
705               return FALSE;
706             }
707 
708         }
709       else
710         {
711           /* We parsed everything and there's still stuff left?
712            * Pretend we didn't notice and let the normal code produce
713            * a 'junk at end of value' error
714            */
715           break;
716         }
717     }
718   while (!value_is_done_parsing (parser));
719 
720   return TRUE;
721 }
722 
723 static gboolean
parse_transition(GtkCssShorthandProperty * shorthand,GtkCssValue ** values,GtkCssParser * parser)724 parse_transition (GtkCssShorthandProperty  *shorthand,
725                   GtkCssValue             **values,
726                   GtkCssParser             *parser)
727 {
728   GtkCssValue *step_values[4];
729   GPtrArray *arrays[4];
730   guint i;
731 
732   for (i = 0; i < 4; i++)
733     {
734       arrays[i] = g_ptr_array_new ();
735       step_values[i] = NULL;
736     }
737 
738   do {
739     if (!parse_one_transition (shorthand, step_values, parser))
740       {
741         for (i = 0; i < 4; i++)
742           {
743             g_ptr_array_set_free_func (arrays[i], (GDestroyNotify) _gtk_css_value_unref);
744             g_ptr_array_unref (arrays[i]);
745           }
746         return FALSE;
747       }
748 
749       for (i = 0; i < 4; i++)
750         {
751           if (step_values[i] == NULL)
752             {
753               GtkCssValue *initial = _gtk_css_style_property_get_initial_value (
754                                          _gtk_css_shorthand_property_get_subproperty (shorthand, i));
755               step_values[i] = _gtk_css_value_ref (_gtk_css_array_value_get_nth (initial, 0));
756             }
757 
758           g_ptr_array_add (arrays[i], step_values[i]);
759           step_values[i] = NULL;
760         }
761   } while (_gtk_css_parser_try (parser, ",", TRUE));
762 
763   for (i = 0; i < 4; i++)
764     {
765       values[i] = _gtk_css_array_value_new_from_array ((GtkCssValue **) arrays[i]->pdata, arrays[i]->len);
766       g_ptr_array_unref (arrays[i]);
767     }
768 
769   return TRUE;
770 }
771 
772 static gboolean
parse_one_animation(GtkCssShorthandProperty * shorthand,GtkCssValue ** values,GtkCssParser * parser)773 parse_one_animation (GtkCssShorthandProperty  *shorthand,
774                      GtkCssValue             **values,
775                      GtkCssParser             *parser)
776 {
777   do
778     {
779       if (values[1] == NULL && _gtk_css_parser_try (parser, "infinite", TRUE))
780         {
781           values[1] = _gtk_css_number_value_new (HUGE_VAL, GTK_CSS_NUMBER);
782         }
783       else if ((values[1] == NULL || values[3] == NULL) &&
784                gtk_css_number_value_can_parse (parser))
785         {
786           GtkCssValue *value;
787 
788           value = _gtk_css_number_value_parse (parser,
789                                                GTK_CSS_POSITIVE_ONLY
790                                                | (values[1] == NULL ? GTK_CSS_PARSE_NUMBER : 0)
791                                                | (values[3] == NULL ? GTK_CSS_PARSE_TIME : 0));
792           if (value == NULL)
793             return FALSE;
794 
795           if (gtk_css_number_value_get_dimension (value) == GTK_CSS_DIMENSION_NUMBER)
796             values[1] = value;
797           else if (values[2] == NULL)
798             values[2] = value;
799           else
800             values[3] = value;
801         }
802       else if (values[4] == NULL &&
803                _gtk_css_ease_value_can_parse (parser))
804         {
805           values[4] = _gtk_css_ease_value_parse (parser);
806 
807           if (values[4] == NULL)
808             return FALSE;
809         }
810       else if (values[5] == NULL &&
811                (values[5] = _gtk_css_direction_value_try_parse (parser)))
812         {
813           /* nothing to do */
814         }
815       else if (values[6] == NULL &&
816                (values[6] = _gtk_css_fill_mode_value_try_parse (parser)))
817         {
818           /* nothing to do */
819         }
820       else if (values[0] == NULL &&
821                (values[0] = _gtk_css_ident_value_try_parse (parser)))
822         {
823           /* nothing to do */
824           /* keep in mind though that this needs to come last as fill modes, directions
825            * etc are valid idents */
826         }
827       else
828         {
829           /* We parsed everything and there's still stuff left?
830            * Pretend we didn't notice and let the normal code produce
831            * a 'junk at end of value' error */
832           break;
833         }
834     }
835   while (!value_is_done_parsing (parser));
836 
837   return TRUE;
838 }
839 
840 static gboolean
parse_animation(GtkCssShorthandProperty * shorthand,GtkCssValue ** values,GtkCssParser * parser)841 parse_animation (GtkCssShorthandProperty  *shorthand,
842                  GtkCssValue             **values,
843                  GtkCssParser             *parser)
844 {
845   GtkCssValue *step_values[7];
846   GPtrArray *arrays[7];
847   guint i;
848 
849   for (i = 0; i < 7; i++)
850     {
851       arrays[i] = g_ptr_array_new ();
852       step_values[i] = NULL;
853     }
854 
855   do {
856     if (!parse_one_animation (shorthand, step_values, parser))
857       {
858         for (i = 0; i < 7; i++)
859           {
860             g_ptr_array_set_free_func (arrays[i], (GDestroyNotify) _gtk_css_value_unref);
861             g_ptr_array_unref (arrays[i]);
862           }
863         return FALSE;
864       }
865 
866       for (i = 0; i < 7; i++)
867         {
868           if (step_values[i] == NULL)
869             {
870               GtkCssValue *initial = _gtk_css_style_property_get_initial_value (
871                                          _gtk_css_shorthand_property_get_subproperty (shorthand, i));
872               step_values[i] = _gtk_css_value_ref (_gtk_css_array_value_get_nth (initial, 0));
873             }
874 
875           g_ptr_array_add (arrays[i], step_values[i]);
876           step_values[i] = NULL;
877         }
878   } while (_gtk_css_parser_try (parser, ",", TRUE));
879 
880   for (i = 0; i < 7; i++)
881     {
882       values[i] = _gtk_css_array_value_new_from_array ((GtkCssValue **) arrays[i]->pdata, arrays[i]->len);
883       g_ptr_array_unref (arrays[i]);
884     }
885 
886   return TRUE;
887 }
888 
889 static gboolean
parse_text_decoration(GtkCssShorthandProperty * shorthand,GtkCssValue ** values,GtkCssParser * parser)890 parse_text_decoration (GtkCssShorthandProperty  *shorthand,
891                        GtkCssValue             **values,
892                        GtkCssParser             *parser)
893 {
894   do
895   {
896     if (values[0] == NULL &&
897         (values[0] = _gtk_css_text_decoration_line_value_try_parse (parser)))
898       {
899         if (values[0] == NULL)
900           return FALSE;
901       }
902     else if (values[1] == NULL &&
903         (values[1] = _gtk_css_text_decoration_style_value_try_parse (parser)))
904       {
905         if (values[1] == NULL)
906           return FALSE;
907       }
908     else if (values[2] == NULL)
909       {
910         values[2] = _gtk_css_color_value_parse (parser);
911         if (values[2] == NULL)
912           return FALSE;
913       }
914     else
915       {
916         /* We parsed and there's still stuff left?
917          * Pretend we didn't notice and let the normal code produce
918          * a 'junk at end of value' error */
919         break;
920       }
921   }
922   while (!value_is_done_parsing (parser));
923 
924   return TRUE;
925 }
926 
927 static gboolean
parse_all(GtkCssShorthandProperty * shorthand,GtkCssValue ** values,GtkCssParser * parser)928 parse_all (GtkCssShorthandProperty  *shorthand,
929            GtkCssValue             **values,
930            GtkCssParser             *parser)
931 {
932   _gtk_css_parser_error (parser, "The 'all' property can only be set to 'initial', 'inherit' or 'unset'");
933   return FALSE;
934 }
935 
936 /*** PACKING ***/
937 
938 static void
unpack_border(GtkCssShorthandProperty * shorthand,GtkStyleProperties * props,GtkStateFlags state,const GValue * value)939 unpack_border (GtkCssShorthandProperty *shorthand,
940                GtkStyleProperties      *props,
941                GtkStateFlags            state,
942                const GValue            *value)
943 {
944   GValue v = G_VALUE_INIT;
945   GtkBorder *border = g_value_get_boxed (value);
946 
947   g_value_init (&v, G_TYPE_INT);
948 
949   g_value_set_int (&v, border->top);
950   _gtk_style_property_assign (GTK_STYLE_PROPERTY (_gtk_css_shorthand_property_get_subproperty (shorthand, 0)), props, state, &v);
951   g_value_set_int (&v, border->right);
952   _gtk_style_property_assign (GTK_STYLE_PROPERTY (_gtk_css_shorthand_property_get_subproperty (shorthand, 1)), props, state, &v);
953   g_value_set_int (&v, border->bottom);
954   _gtk_style_property_assign (GTK_STYLE_PROPERTY (_gtk_css_shorthand_property_get_subproperty (shorthand, 2)), props, state, &v);
955   g_value_set_int (&v, border->left);
956   _gtk_style_property_assign (GTK_STYLE_PROPERTY (_gtk_css_shorthand_property_get_subproperty (shorthand, 3)), props, state, &v);
957 
958   g_value_unset (&v);
959 }
960 
961 static void
pack_border(GtkCssShorthandProperty * shorthand,GValue * value,GtkStyleQueryFunc query_func,gpointer query_data)962 pack_border (GtkCssShorthandProperty *shorthand,
963              GValue                  *value,
964              GtkStyleQueryFunc        query_func,
965              gpointer                 query_data)
966 {
967   GtkCssStyleProperty *prop;
968   GtkBorder border;
969   GValue v;
970 
971   prop = _gtk_css_shorthand_property_get_subproperty (shorthand, 0);
972   _gtk_style_property_query (GTK_STYLE_PROPERTY (prop), &v, query_func, query_data);
973   border.top = g_value_get_int (&v);
974   g_value_unset (&v);
975 
976   prop = _gtk_css_shorthand_property_get_subproperty (shorthand, 1);
977   _gtk_style_property_query (GTK_STYLE_PROPERTY (prop), &v, query_func, query_data);
978   border.right = g_value_get_int (&v);
979   g_value_unset (&v);
980 
981   prop = _gtk_css_shorthand_property_get_subproperty (shorthand, 2);
982   _gtk_style_property_query (GTK_STYLE_PROPERTY (prop), &v, query_func, query_data);
983   border.bottom = g_value_get_int (&v);
984   g_value_unset (&v);
985 
986   prop = _gtk_css_shorthand_property_get_subproperty (shorthand, 3);
987   _gtk_style_property_query (GTK_STYLE_PROPERTY (prop), &v, query_func, query_data);
988   border.left = g_value_get_int (&v);
989   g_value_unset (&v);
990 
991   g_value_init (value, GTK_TYPE_BORDER);
992   g_value_set_boxed (value, &border);
993 }
994 
995 static void
unpack_border_radius(GtkCssShorthandProperty * shorthand,GtkStyleProperties * props,GtkStateFlags state,const GValue * value)996 unpack_border_radius (GtkCssShorthandProperty *shorthand,
997                       GtkStyleProperties      *props,
998                       GtkStateFlags            state,
999                       const GValue            *value)
1000 {
1001   GtkCssValue *css_value;
1002   guint i;
1003 
1004   css_value = _gtk_css_corner_value_new (_gtk_css_number_value_new (g_value_get_int (value), GTK_CSS_PX),
1005                                          _gtk_css_number_value_new (g_value_get_int (value), GTK_CSS_PX));
1006 
1007   for (i = 0; i < 4; i++)
1008     _gtk_style_properties_set_property_by_property (props,
1009                                                     _gtk_css_shorthand_property_get_subproperty (shorthand, i),
1010                                                     state,
1011                                                     css_value);
1012 
1013   _gtk_css_value_unref (css_value);
1014 }
1015 
1016 static void
pack_border_radius(GtkCssShorthandProperty * shorthand,GValue * value,GtkStyleQueryFunc query_func,gpointer query_data)1017 pack_border_radius (GtkCssShorthandProperty *shorthand,
1018                     GValue                  *value,
1019                     GtkStyleQueryFunc        query_func,
1020                     gpointer                 query_data)
1021 {
1022   GtkCssStyleProperty *prop;
1023   GtkCssValue *v;
1024   int i = 0;
1025 
1026   prop = GTK_CSS_STYLE_PROPERTY (_gtk_style_property_lookup ("border-top-left-radius"));
1027   v = (* query_func) (_gtk_css_style_property_get_id (prop), query_data);
1028   if (v)
1029     i = _gtk_css_corner_value_get_x (v, 100);
1030 
1031   g_value_init (value, G_TYPE_INT);
1032   g_value_set_int (value, i);
1033 }
1034 
1035 static void
unpack_font_description(GtkCssShorthandProperty * shorthand,GtkStyleProperties * props,GtkStateFlags state,const GValue * value)1036 unpack_font_description (GtkCssShorthandProperty *shorthand,
1037                          GtkStyleProperties      *props,
1038                          GtkStateFlags            state,
1039                          const GValue            *value)
1040 {
1041   GtkStyleProperty *prop;
1042   PangoFontDescription *description;
1043   PangoFontMask mask;
1044   GValue v = G_VALUE_INIT;
1045 
1046   /* For backwards compat, we only unpack values that are indeed set.
1047    * For strict CSS conformance we need to unpack all of them.
1048    * Note that we do set all of them in the parse function, so it
1049    * will not have effects when parsing CSS files. It will though
1050    * for custom style providers.
1051    */
1052 
1053   description = g_value_get_boxed (value);
1054 
1055   if (description)
1056     mask = pango_font_description_get_set_fields (description);
1057   else
1058     mask = 0;
1059 
1060   if (mask & PANGO_FONT_MASK_FAMILY)
1061     {
1062       GPtrArray *strv = g_ptr_array_new ();
1063 
1064       g_ptr_array_add (strv, g_strdup (pango_font_description_get_family (description)));
1065       g_ptr_array_add (strv, NULL);
1066       g_value_init (&v, G_TYPE_STRV);
1067       g_value_take_boxed (&v, g_ptr_array_free (strv, FALSE));
1068 
1069       prop = _gtk_style_property_lookup ("font-family");
1070       _gtk_style_property_assign (prop, props, state, &v);
1071       g_value_unset (&v);
1072     }
1073 
1074   if (mask & PANGO_FONT_MASK_STYLE)
1075     {
1076       g_value_init (&v, PANGO_TYPE_STYLE);
1077       g_value_set_enum (&v, pango_font_description_get_style (description));
1078 
1079       prop = _gtk_style_property_lookup ("font-style");
1080       _gtk_style_property_assign (prop, props, state, &v);
1081       g_value_unset (&v);
1082     }
1083 
1084   if (mask & PANGO_FONT_MASK_VARIANT)
1085     {
1086       g_value_init (&v, PANGO_TYPE_VARIANT);
1087       g_value_set_enum (&v, pango_font_description_get_variant (description));
1088 
1089       prop = _gtk_style_property_lookup ("font-variant");
1090       _gtk_style_property_assign (prop, props, state, &v);
1091       g_value_unset (&v);
1092     }
1093 
1094   if (mask & PANGO_FONT_MASK_WEIGHT)
1095     {
1096       g_value_init (&v, PANGO_TYPE_WEIGHT);
1097       g_value_set_enum (&v, pango_font_description_get_weight (description));
1098 
1099       prop = _gtk_style_property_lookup ("font-weight");
1100       _gtk_style_property_assign (prop, props, state, &v);
1101       g_value_unset (&v);
1102     }
1103 
1104   if (mask & PANGO_FONT_MASK_STRETCH)
1105     {
1106       g_value_init (&v, PANGO_TYPE_STRETCH);
1107       g_value_set_enum (&v, pango_font_description_get_stretch (description));
1108 
1109       prop = _gtk_style_property_lookup ("font-stretch");
1110       _gtk_style_property_assign (prop, props, state, &v);
1111       g_value_unset (&v);
1112     }
1113 
1114   if (mask & PANGO_FONT_MASK_SIZE)
1115     {
1116       double size;
1117 
1118       g_value_init (&v, G_TYPE_DOUBLE);
1119       size = pango_font_description_get_size (description) / PANGO_SCALE;
1120       if (!pango_font_description_get_size_is_absolute (description))
1121         {
1122           double dpi = gdk_screen_get_resolution (gdk_screen_get_default ());
1123           if (dpi <= 0.0)
1124             dpi = 96.0;
1125           size = size * dpi / 72.0;
1126         }
1127       g_value_set_double (&v, size);
1128       prop = _gtk_style_property_lookup ("font-size");
1129       _gtk_style_property_assign (prop, props, state, &v);
1130       g_value_unset (&v);
1131     }
1132 }
1133 
1134 static void
pack_font_description(GtkCssShorthandProperty * shorthand,GValue * value,GtkStyleQueryFunc query_func,gpointer query_data)1135 pack_font_description (GtkCssShorthandProperty *shorthand,
1136                        GValue                  *value,
1137                        GtkStyleQueryFunc        query_func,
1138                        gpointer                 query_data)
1139 {
1140   PangoFontDescription *description;
1141   GtkCssValue *v;
1142   double dpi;
1143 
1144   description = pango_font_description_new ();
1145 
1146   v = (* query_func) (_gtk_css_style_property_get_id (GTK_CSS_STYLE_PROPERTY (_gtk_style_property_lookup ("font-family"))), query_data);
1147   if (v)
1148     {
1149       int i;
1150       GString *s = g_string_new ("");
1151 
1152       for (i = 0; i < _gtk_css_array_value_get_n_values (v); i++)
1153         {
1154           if (i > 0)
1155             g_string_append (s, ",");
1156           g_string_append (s, _gtk_css_string_value_get (_gtk_css_array_value_get_nth (v, i)));
1157         }
1158 
1159       pango_font_description_set_family (description, s->str);
1160       g_string_free (s, TRUE);
1161     }
1162 
1163   v = (* query_func) (_gtk_css_style_property_get_id (GTK_CSS_STYLE_PROPERTY (_gtk_style_property_lookup ("-gtk-dpi"))), query_data);
1164   dpi = _gtk_css_number_value_get (v, 96);
1165   v = (* query_func) (_gtk_css_style_property_get_id (GTK_CSS_STYLE_PROPERTY (_gtk_style_property_lookup ("font-size"))), query_data);
1166   if (v)
1167     pango_font_description_set_size (description, round (_gtk_css_number_value_get (v, 100) * PANGO_SCALE * 72 / dpi));
1168 
1169   v = (* query_func) (_gtk_css_style_property_get_id (GTK_CSS_STYLE_PROPERTY (_gtk_style_property_lookup ("font-style"))), query_data);
1170   if (v)
1171     pango_font_description_set_style (description, _gtk_css_font_style_value_get (v));
1172 
1173   v = (* query_func) (_gtk_css_style_property_get_id (GTK_CSS_STYLE_PROPERTY (_gtk_style_property_lookup ("font-variant"))), query_data);
1174   if (v)
1175     pango_font_description_set_variant (description, _gtk_css_font_variant_value_get (v));
1176 
1177   v = (* query_func) (_gtk_css_style_property_get_id (GTK_CSS_STYLE_PROPERTY (_gtk_style_property_lookup ("font-weight"))), query_data);
1178   if (v)
1179     pango_font_description_set_weight (description, _gtk_css_font_weight_value_get (v));
1180 
1181   v = (* query_func) (_gtk_css_style_property_get_id (GTK_CSS_STYLE_PROPERTY (_gtk_style_property_lookup ("font-stretch"))), query_data);
1182   if (v)
1183     pango_font_description_set_stretch (description, _gtk_css_font_stretch_value_get (v));
1184 
1185   g_value_init (value, PANGO_TYPE_FONT_DESCRIPTION);
1186   g_value_take_boxed (value, description);
1187 }
1188 
1189 static void
unpack_to_everything(GtkCssShorthandProperty * shorthand,GtkStyleProperties * props,GtkStateFlags state,const GValue * value)1190 unpack_to_everything (GtkCssShorthandProperty *shorthand,
1191                       GtkStyleProperties      *props,
1192                       GtkStateFlags            state,
1193                       const GValue            *value)
1194 {
1195   GtkCssStyleProperty *prop;
1196   guint i, n;
1197 
1198   n = _gtk_css_shorthand_property_get_n_subproperties (shorthand);
1199 
1200   for (i = 0; i < n; i++)
1201     {
1202       prop = _gtk_css_shorthand_property_get_subproperty (shorthand, i);
1203       _gtk_style_property_assign (GTK_STYLE_PROPERTY (prop), props, state, value);
1204     }
1205 }
1206 
1207 static void
pack_first_element(GtkCssShorthandProperty * shorthand,GValue * value,GtkStyleQueryFunc query_func,gpointer query_data)1208 pack_first_element (GtkCssShorthandProperty *shorthand,
1209                     GValue                  *value,
1210                     GtkStyleQueryFunc        query_func,
1211                     gpointer                 query_data)
1212 {
1213   GtkCssStyleProperty *prop;
1214 
1215   /* NB: This is a fallback for properties that originally were
1216    * not used as shorthand. We just pick the first subproperty
1217    * as a representative.
1218    * Lesson learned: Don't query the shorthand, query the
1219    * real properties instead. */
1220   prop = _gtk_css_shorthand_property_get_subproperty (shorthand, 0);
1221   _gtk_style_property_query (GTK_STYLE_PROPERTY (prop),
1222                              value,
1223                              query_func,
1224                              query_data);
1225 }
1226 
1227 static void
_gtk_css_shorthand_property_register(const char * name,GType value_type,const char ** subproperties,GtkCssShorthandPropertyParseFunc parse_func,GtkCssShorthandPropertyAssignFunc assign_func,GtkCssShorthandPropertyQueryFunc query_func)1228 _gtk_css_shorthand_property_register (const char                        *name,
1229                                       GType                              value_type,
1230                                       const char                       **subproperties,
1231                                       GtkCssShorthandPropertyParseFunc   parse_func,
1232                                       GtkCssShorthandPropertyAssignFunc  assign_func,
1233                                       GtkCssShorthandPropertyQueryFunc   query_func)
1234 {
1235   GtkCssShorthandProperty *node;
1236 
1237   node = g_object_new (GTK_TYPE_CSS_SHORTHAND_PROPERTY,
1238                        "name", name,
1239                        "value-type", value_type,
1240                        "subproperties", subproperties,
1241                        NULL);
1242 
1243   node->parse = parse_func;
1244   node->assign = assign_func;
1245   node->query = query_func;
1246 }
1247 
1248 /* NB: return value is transfer: container */
1249 static const char **
get_all_subproperties(void)1250 get_all_subproperties (void)
1251 {
1252   const char **properties;
1253   guint i, n;
1254 
1255   n = _gtk_css_style_property_get_n_properties ();
1256   properties = g_new (const char *, n + 1);
1257   properties[n] = NULL;
1258 
1259   for (i = 0; i < n; i++)
1260     {
1261       properties[i] = _gtk_style_property_get_name (GTK_STYLE_PROPERTY (_gtk_css_style_property_lookup_by_id (i)));
1262     }
1263 
1264   return properties;
1265 }
1266 
1267 void
_gtk_css_shorthand_property_init_properties(void)1268 _gtk_css_shorthand_property_init_properties (void)
1269 {
1270   /* The order is important here, be careful when changing it */
1271   const char *font_subproperties[] = { "font-family", "font-style", "font-variant", "font-weight", "font-stretch", "font-size", NULL };
1272   const char *margin_subproperties[] = { "margin-top", "margin-right", "margin-bottom", "margin-left", NULL };
1273   const char *padding_subproperties[] = { "padding-top", "padding-right", "padding-bottom", "padding-left", NULL };
1274   const char *border_width_subproperties[] = { "border-top-width", "border-right-width", "border-bottom-width", "border-left-width", NULL };
1275   const char *border_radius_subproperties[] = { "border-top-left-radius", "border-top-right-radius",
1276                                                 "border-bottom-right-radius", "border-bottom-left-radius", NULL };
1277   const char *border_color_subproperties[] = { "border-top-color", "border-right-color", "border-bottom-color", "border-left-color", NULL };
1278   const char *border_style_subproperties[] = { "border-top-style", "border-right-style", "border-bottom-style", "border-left-style", NULL };
1279   const char *border_image_subproperties[] = { "border-image-source", "border-image-slice", "border-image-width", "border-image-repeat", NULL };
1280   const char *border_top_subproperties[] = { "border-top-width", "border-top-style", "border-top-color", NULL };
1281   const char *border_right_subproperties[] = { "border-right-width", "border-right-style", "border-right-color", NULL };
1282   const char *border_bottom_subproperties[] = { "border-bottom-width", "border-bottom-style", "border-bottom-color", NULL };
1283   const char *border_left_subproperties[] = { "border-left-width", "border-left-style", "border-left-color", NULL };
1284   const char *border_subproperties[] = { "border-top-width", "border-right-width", "border-bottom-width", "border-left-width",
1285                                          "border-top-style", "border-right-style", "border-bottom-style", "border-left-style",
1286                                          "border-top-color", "border-right-color", "border-bottom-color", "border-left-color",
1287                                          "border-image-source", "border-image-slice", "border-image-width", "border-image-repeat", NULL };
1288   const char *outline_subproperties[] = { "outline-width", "outline-style", "outline-color", NULL };
1289   const char *outline_radius_subproperties[] = { "outline-top-left-radius", "outline-top-right-radius",
1290                                                  "outline-bottom-right-radius", "outline-bottom-left-radius", NULL };
1291   const char *background_subproperties[] = { "background-image", "background-position", "background-size", "background-repeat", "background-clip", "background-origin",
1292                                              "background-color", NULL };
1293   const char *transition_subproperties[] = { "transition-property", "transition-duration", "transition-delay", "transition-timing-function", NULL };
1294   const char *animation_subproperties[] = { "animation-name", "animation-iteration-count", "animation-duration", "animation-delay",
1295                                             "animation-timing-function", "animation-direction", "animation-fill-mode", NULL };
1296   const char *text_decoration_subproperties[] = { "text-decoration-line", "text-decoration-style", "text-decoration-color", NULL };
1297 
1298   const char **all_subproperties;
1299 
1300   _gtk_css_shorthand_property_register   ("font",
1301                                           PANGO_TYPE_FONT_DESCRIPTION,
1302                                           font_subproperties,
1303                                           parse_font,
1304                                           unpack_font_description,
1305                                           pack_font_description);
1306   _gtk_css_shorthand_property_register   ("margin",
1307                                           GTK_TYPE_BORDER,
1308                                           margin_subproperties,
1309                                           parse_margin,
1310                                           unpack_border,
1311                                           pack_border);
1312   _gtk_css_shorthand_property_register   ("padding",
1313                                           GTK_TYPE_BORDER,
1314                                           padding_subproperties,
1315                                           parse_padding,
1316                                           unpack_border,
1317                                           pack_border);
1318   _gtk_css_shorthand_property_register   ("border-width",
1319                                           GTK_TYPE_BORDER,
1320                                           border_width_subproperties,
1321                                           parse_border_width,
1322                                           unpack_border,
1323                                           pack_border);
1324   _gtk_css_shorthand_property_register   ("border-radius",
1325                                           G_TYPE_INT,
1326                                           border_radius_subproperties,
1327                                           parse_border_radius,
1328                                           unpack_border_radius,
1329                                           pack_border_radius);
1330   _gtk_css_shorthand_property_register   ("border-color",
1331                                           GDK_TYPE_RGBA,
1332                                           border_color_subproperties,
1333                                           parse_border_color,
1334                                           unpack_to_everything,
1335                                           pack_first_element);
1336   _gtk_css_shorthand_property_register   ("border-style",
1337                                           GTK_TYPE_BORDER_STYLE,
1338                                           border_style_subproperties,
1339                                           parse_border_style,
1340                                           unpack_to_everything,
1341                                           pack_first_element);
1342   _gtk_css_shorthand_property_register   ("border-image",
1343                                           G_TYPE_NONE,
1344                                           border_image_subproperties,
1345                                           parse_border_image,
1346                                           NULL,
1347                                           NULL);
1348   _gtk_css_shorthand_property_register   ("border-top",
1349                                           G_TYPE_NONE,
1350                                           border_top_subproperties,
1351                                           parse_border_side,
1352                                           NULL,
1353                                           NULL);
1354   _gtk_css_shorthand_property_register   ("border-right",
1355                                           G_TYPE_NONE,
1356                                           border_right_subproperties,
1357                                           parse_border_side,
1358                                           NULL,
1359                                           NULL);
1360   _gtk_css_shorthand_property_register   ("border-bottom",
1361                                           G_TYPE_NONE,
1362                                           border_bottom_subproperties,
1363                                           parse_border_side,
1364                                           NULL,
1365                                           NULL);
1366   _gtk_css_shorthand_property_register   ("border-left",
1367                                           G_TYPE_NONE,
1368                                           border_left_subproperties,
1369                                           parse_border_side,
1370                                           NULL,
1371                                           NULL);
1372   _gtk_css_shorthand_property_register   ("border",
1373                                           G_TYPE_NONE,
1374                                           border_subproperties,
1375                                           parse_border,
1376                                           NULL,
1377                                           NULL);
1378   _gtk_css_shorthand_property_register   ("-gtk-outline-radius",
1379                                           G_TYPE_INT,
1380                                           outline_radius_subproperties,
1381                                           parse_border_radius,
1382                                           unpack_border_radius,
1383                                           pack_border_radius);
1384   _gtk_style_property_add_alias ("-gtk-outline-radius", "outline-radius");
1385   _gtk_css_shorthand_property_register   ("outline",
1386                                           G_TYPE_NONE,
1387                                           outline_subproperties,
1388                                           parse_border_side,
1389                                           NULL,
1390                                           NULL);
1391   _gtk_css_shorthand_property_register   ("background",
1392                                           G_TYPE_NONE,
1393                                           background_subproperties,
1394                                           parse_background,
1395                                           NULL,
1396                                           NULL);
1397   _gtk_css_shorthand_property_register   ("transition",
1398                                           G_TYPE_NONE,
1399                                           transition_subproperties,
1400                                           parse_transition,
1401                                           NULL,
1402                                           NULL);
1403   _gtk_css_shorthand_property_register   ("animation",
1404                                           G_TYPE_NONE,
1405                                           animation_subproperties,
1406                                           parse_animation,
1407                                           NULL,
1408                                           NULL);
1409   _gtk_css_shorthand_property_register   ("text-decoration",
1410                                           G_TYPE_NONE,
1411                                           text_decoration_subproperties,
1412                                           parse_text_decoration,
1413                                           NULL,
1414                                           NULL);
1415 
1416   all_subproperties = get_all_subproperties ();
1417   _gtk_css_shorthand_property_register   ("all",
1418                                           G_TYPE_NONE,
1419                                           all_subproperties,
1420                                           parse_all,
1421                                           NULL,
1422                                           NULL);
1423   g_free (all_subproperties);
1424 }
1425