]> Pileus Git - ~andy/gtk/blobdiff - gtk/gtkcssprovider.c
Change FSF Address
[~andy/gtk] / gtk / gtkcssprovider.c
index e1b9b33bcdd799af68b2c3572728f422df956e74..32bdd3a5650f64132b78e86f1746e41f6c9d6ca9 100644 (file)
@@ -12,9 +12,7 @@
  * Lesser General Public License for more details.
  *
  * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
  */
 
 #include "config.h"
 
 #include "gtkcssproviderprivate.h"
 
+#include "gtkbitmaskprivate.h"
+#include "gtkcssstylefuncsprivate.h"
 #include "gtkcssparserprivate.h"
+#include "gtkcsssectionprivate.h"
 #include "gtkcssselectorprivate.h"
+#include "gtkcssshorthandpropertyprivate.h"
 #include "gtksymboliccolor.h"
 #include "gtkstyleprovider.h"
 #include "gtkstylecontextprivate.h"
 #include "gtkstylepropertiesprivate.h"
 #include "gtkstylepropertyprivate.h"
+#include "gtkstyleproviderprivate.h"
 #include "gtkbindings.h"
 #include "gtkmarshalers.h"
 #include "gtkprivate.h"
  *
  * /&ast; Theme any widget within a GtkBin &ast;/
  * GtkBin * {
- *     font-name: Sans 20
+ *     font: Sans 20
  * }
  *
  * /&ast; Theme a label named title-label &ast;/
  * GtkLabel&num;title-label {
- *     font-name: Sans 15
+ *     font: Sans 15
  * }
  *
  * /&ast; Theme any widget named main-entry &ast;/
  *         <entry>darker(@color)</entry>
  *         <entry>A darker variant of @color</entry>
  *       </row>
+ *       <row>
+ *         <entry>alpha(@color, @f)</entry>
+ *         <entry>Modifies passed color's alpha by a factor @f. @f is a
+ *                floating point number. @f < 1.0 results in a more transparent
+ *                color while @f > 1.0 results in a more opaque color.
+ *         </entry>
+ *         <entry>alhpa(blue, 0.5)</entry>
+ *       </row>
  *     </tbody>
  *   </tgroup>
  * </informaltable>
  * which is always rendered on top of the shadow layer.
  * </para>
  * </refsect2>
- * <refsect2 id="gtkcssprovider-slices">
+ * <refsect2>
+ * <title>Box shadow</title>
+ * <para>
+ * Themes can apply shadows on framed elements using the CSS3 box-shadow syntax,
+ * as defined in 
+ * <ulink url="http://www.w3.org/TR/css3-background/#the-box-shadow">the CSS3 specification</ulink>.
+ * </para>
+ * <para>
+ * A box shadow is specified using the syntax
+ * <literallayout>box-shadow: [ @inset ] @horizontal_offset @vertical_offset [ @blur_radius ] [ @spread ] @color</literallayout>
+ * A positive offset will draw a shadow that is offset to the right (down) of the box,
+ * a negative offset to the left (top). The optional spread parameter defines an additional
+ * distance to expand the shadow shape in all directions, by the specified radius.
+ * The optional blur radius parameter is parsed, but it is currently not rendered by
+ * the GTK+ theming engine.
+ * The inset parameter defines whether the drop shadow should be rendered inside or outside
+ * the box canvas. Only inset box-shadows are currently supported by the GTK+ theming engine,
+ * non-inset elements are currently ignored.
+ * </para>
+ * <para>
+ * To set multiple box-shadows on an element, you can specify a comma-separated list
+ * of shadow elements in the box-shadow property. Shadows are always rendered
+ * front-back, i.e. the first shadow specified is on top of the others, so they may
+ * overlap other boxes or other shadows.
+ * </para>
+ * </refsect2>
+ * <refsect2 id="gtkcssprovider-border-image">
  * <title>Border images</title>
  * <para>
- * Images can be used in 'slices' for the purpose of creating scalable
- * borders.
+ * Images and gradients can also be used in slices for the purpose of creating
+ * scalable borders.
+ * For more information, see the CSS3 documentation for the border-image property,
+ * which can be found <ulink url="http://www.w3.org/TR/css3-background/#border-images">here</ulink>.
  * </para>
  * <inlinegraphic fileref="slices.png" format="PNG"/>
  * <para>
- * The syntax for specifying border images of this kind is:
- * <literallayout>url(@path) @top @right @bottom @left [repeat|stretch]? [repeat|stretch]?</literallayout>
- * The sizes of the 'cut off' portions are specified
- * with the @top, @right, @bottom and @left parameters.
- * The 'middle' sections can be repeated or stretched to create
- * the desired effect, by adding the 'repeat' or 'stretch' options after
- * the dimensions. If two options are specified, the first one affects
+ * The parameters of the slicing process are controlled by
+ * four separate properties. Note that you can use the
+ * <literallayout>border-image</literallayout> shorthand property
+ * to set values for the three properties at the same time.
+ * </para>
+ * <para>
+ * <literallayout>border-image-source: url(@path)
+ * (or border-image-source: -gtk-gradient(...))</literallayout>:
+ * Specifies the source of the border image, and it can either
+ * be an URL or a gradient (see above).
+ * </para>
+ * <para>
+ * <literallayout>border-image-slice: @top @right @bottom @left</literallayout>
+ * The sizes specified by the @top, @right, @bottom and @left parameters
+ * are the offsets, in pixels, from the relevant edge where the image
+ * should be "cut off" to build the slices used for the rendering
+ * of the border.
+ * </para>
+ * <para>
+ * <literallayout>border-image-width: @top @right @bottom @left</literallayout>
+ * The sizes specified by the @top, @right, @bottom and @left parameters
+ * are inward distances from the border box edge, used to specify the
+ * rendered size of each slice determined by border-image-slice.
+ * If this property is not specified, the values of border-width will
+ * be used as a fallback.
+ * </para>
+ * <para>
+ * <literallayout>border-image-repeat: [stretch|repeat|round|space] ? 
+ * [stretch|repeat|round|space]</literallayout>
+ * Specifies how the image slices should be rendered in the area
+ * outlined by border-width.
+ * The default (stretch) is to resize the slice to fill in the whole 
+ * allocated area.
+ * If the value of this property is 'repeat', the image slice
+ * will be tiled to fill the area.
+ * If the value of this property is 'round', the image slice will
+ * be tiled to fill the area, and scaled to fit it exactly
+ * a whole number of times.
+ * If the value of this property is 'space', the image slice will
+ * be tiled to fill the area, and if it doesn't fit it exactly a whole
+ * number of times, the extra space is distributed as padding around 
+ * the slices.
+ * If two options are specified, the first one affects
  * the horizontal behaviour and the second one the vertical behaviour.
  * If only one option is specified, it affects both.
  * </para>
  * map intuitively in a widget based environment).
  * </para>
  * <para>
- * There is also a difference in shorthand properties, for
- * example in common CSS it is fine to define a font through
- * the different @font-family, @font-style, @font-size
- * properties, meanwhile in GTK+'s CSS only the canonical
- * @font property is supported.
- * </para>
- * <para>
  * The currently supported properties are:
  * </para>
  * <informaltable>
  *       <row>
  *         <entry>background-color</entry>
  *         <entry morerows="2">color (see above)</entry>
- *         <entry morerows="2">#GdkRGBA</entry>
- *         <entry morerows="2"><literallayout>background-color: &num;fff;
+ *         <entry morerows="7">#GdkRGBA</entry>
+ *         <entry morerows="7"><literallayout>background-color: &num;fff;
  * color: &amp;color1;
  * background-color: shade (&amp;color1, 0.5);
  * color: mix (&amp;color1, &num;f0f, 0.8);</literallayout>
  *         <entry>color</entry>
  *       </row>
  *       <row>
+ *         <entry>border-top-color</entry>
+ *         <entry morerows="4">transparent|color (see above)</entry>
+ *       </row>
+ *       <row>
+ *         <entry>border-right-color</entry>
+ *       </row>
+ *       <row>
+ *         <entry>border-bottom-color</entry>
+ *       </row>
+ *       <row>
+ *         <entry>border-left-color</entry>
+ *       </row>
+ *       <row>
  *         <entry>border-color</entry>
+ *         <entry>[transparent|color]{1,4}</entry>
+ *       </row>
+ *       <row>
+ *         <entry>font-family</entry>
+ *         <entry>@family [, @family]*</entry>
+ *         <entry>#gchararray</entry>
+ *         <entry>font-family: Sans, Arial;</entry>
+ *       </row>
+ *       <row>
+ *         <entry>font-style</entry>
+ *         <entry>[normal|oblique|italic]</entry>
+ *         <entry>#PANGO_TYPE_STYLE</entry>
+ *         <entry>font-style: italic;</entry>
+ *       </row>
+ *       <row>
+ *         <entry>font-variant</entry>
+ *         <entry>[normal|small-caps]</entry>
+ *         <entry>#PANGO_TYPE_VARIANT</entry>
+ *         <entry>font-variant: normal;</entry>
+ *       </row>
+ *       <row>
+ *         <entry>font-weight</entry>
+ *         <entry>[normal|bold|bolder|lighter|100|200|300|400|500|600|700|800|900]</entry>
+ *         <entry>#PANGO_TYPE_WEIGHT</entry>
+ *         <entry>font-weight: bold;</entry>
+ *       </row>
+ *       <row>
+ *         <entry>font-size</entry>
+ *         <entry>Font size in point</entry>
+ *         <entry>#gint</entry>
+ *         <entry>font-size: 13;</entry>
  *       </row>
  *       <row>
  *         <entry>font</entry>
  *         </entry>
  *       </row>
  *       <row>
+ *         <entry>background-repeat</entry>
+ *         <entry>[repeat|no-repeat]</entry>
+ *         <entry>internal</entry>
+ *         <entry><literallayout>background-repeat: no-repeat;</literallayout>
+ *                If not specified, the style doesn't respect the CSS3
+ *                specification, since the background will be
+ *                stretched to fill the area.
+ *         </entry>
+ *       </row>
+ *       <row>
  *         <entry>border-top-width</entry>
  *         <entry>integer</entry>
  *         <entry>#gint</entry>
@@ -848,20 +970,18 @@ struct GtkCssRuleset
   GtkCssSelector *selector;
   GHashTable *widget_style;
   GHashTable *style;
-
-  guint has_inherit :1;
+  GtkBitmask *set_styles;
 };
 
 struct _GtkCssScanner
 {
   GtkCssProvider *provider;
   GtkCssParser *parser;
+  GtkCssSection *section;
   GtkCssScanner *parent;
   GFile *file;
   GFile *base;
   GSList *state;
-  GSList *cur_selectors;
-  GHashTable *cur_properties;
 };
 
 struct _GtkCssProviderPrivate
@@ -871,32 +991,7 @@ struct _GtkCssProviderPrivate
   GHashTable *symbolic_colors;
 
   GArray *rulesets;
-};
-
-enum ParserScope {
-  SCOPE_SELECTOR,
-  SCOPE_PSEUDO_CLASS,
-  SCOPE_NTH_CHILD,
-  SCOPE_DECLARATION,
-  SCOPE_VALUE,
-  SCOPE_BINDING_SET
-};
-
-/* Extend GtkStateType, since these
- * values are also used as symbols
- */
-enum ParserSymbol {
-  /* Scope: pseudo-class */
-  SYMBOL_NTH_CHILD = GTK_STATE_FOCUSED + 1,
-  SYMBOL_FIRST_CHILD,
-  SYMBOL_LAST_CHILD,
-  SYMBOL_SORTED_CHILD,
-
-  /* Scope: nth-child */
-  SYMBOL_NTH_CHILD_EVEN,
-  SYMBOL_NTH_CHILD_ODD,
-  SYMBOL_NTH_CHILD_FIRST,
-  SYMBOL_NTH_CHILD_LAST
+  GResource *resource;
 };
 
 enum {
@@ -908,13 +1003,13 @@ static guint css_provider_signals[LAST_SIGNAL] = { 0 };
 
 static void gtk_css_provider_finalize (GObject *object);
 static void gtk_css_style_provider_iface_init (GtkStyleProviderIface *iface);
+static void gtk_css_style_provider_private_iface_init (GtkStyleProviderPrivateInterface *iface);
 
 static gboolean
 gtk_css_provider_load_internal (GtkCssProvider *css_provider,
                                 GtkCssScanner  *scanner,
                                 GFile          *file,
                                 const char     *data,
-                                gsize           length,
                                 GError        **error);
 
 GQuark
@@ -925,14 +1020,14 @@ gtk_css_provider_error_quark (void)
 
 G_DEFINE_TYPE_EXTENDED (GtkCssProvider, gtk_css_provider, G_TYPE_OBJECT, 0,
                         G_IMPLEMENT_INTERFACE (GTK_TYPE_STYLE_PROVIDER,
-                                               gtk_css_style_provider_iface_init));
+                                               gtk_css_style_provider_iface_init)
+                        G_IMPLEMENT_INTERFACE (GTK_TYPE_STYLE_PROVIDER_PRIVATE,
+                                               gtk_css_style_provider_private_iface_init));
 
 static void
 gtk_css_provider_parsing_error (GtkCssProvider  *provider,
-                                const gchar     *path,
-                                guint            line,
-                                guint            position,
-                                const GError *   error)
+                                GtkCssSection   *section,
+                                const GError    *error)
 {
   /* Only emit a warning when we have no error handlers. This is our
    * default handlers. And in this case erroneous CSS files are a bug
@@ -945,7 +1040,34 @@ gtk_css_provider_parsing_error (GtkCssProvider  *provider,
                                      0,
                                      TRUE))
     {
-      g_warning ("Theme parsing error: %s:%u:%u: %s", path ? path : "<unknown>", line, position, error->message);
+      GFileInfo *info;
+      GFile *file;
+      const char *path;
+
+      file = gtk_css_section_get_file (section);
+      if (file)
+        {
+          info = g_file_query_info (file, G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME, 0, NULL, NULL);
+
+          if (info)
+            path = g_file_info_get_display_name (info);
+          else
+            path = "<broken file>";
+        }
+      else
+        {
+          info = NULL;
+          path = "<data>";
+        }
+
+      g_warning ("Theme parsing error: %s:%u:%u: %s",
+                 path,
+                 gtk_css_section_get_end_line (section) + 1,
+                 gtk_css_section_get_end_position (section),
+                 error->message);
+
+      if (info)
+        g_object_unref (info);
     }
 }
 
@@ -957,11 +1079,7 @@ gtk_css_provider_class_init (GtkCssProviderClass *klass)
   /**
    * GtkCssProvider::parsing-error:
    * @provider: the provider that had a parsing error
-   * @path: path to the parsed file or %NULL if the file cannot be
-   *   identified or the data was not loaded from a file
-   * @line: line in the file or data or 0 if unknown
-   * @position: offset into the current line or 0 if unknown or the
-   *   whole line is affected
+   * @section: section the error happened in
    * @error: The parsing error
    *
    * Signals that a parsing error occured. the @path, @line and @position
@@ -982,9 +1100,8 @@ gtk_css_provider_class_init (GtkCssProviderClass *klass)
                   G_SIGNAL_RUN_LAST,
                   G_STRUCT_OFFSET (GtkCssProviderClass, parsing_error),
                   NULL, NULL,
-                  _gtk_marshal_VOID__STRING_UINT_UINT_BOXED,
-                  G_TYPE_NONE, 4,
-                  G_TYPE_STRING, G_TYPE_UINT, G_TYPE_UINT, G_TYPE_ERROR);
+                  _gtk_marshal_VOID__BOXED_BOXED,
+                  G_TYPE_NONE, 2, GTK_TYPE_CSS_SECTION, G_TYPE_ERROR);
 
   object_class->finalize = gtk_css_provider_finalize;
 
@@ -993,27 +1110,6 @@ gtk_css_provider_class_init (GtkCssProviderClass *klass)
   g_type_class_add_private (object_class, sizeof (GtkCssProviderPrivate));
 }
 
-static void
-gtk_css_provider_take_error_full (GtkCssProvider *provider,
-                                  GFile          *file,
-                                  guint           line,
-                                  guint           position,
-                                  GError         *error)
-{
-  char *filename;
-
-  if (file)
-    filename = g_file_get_path (file);
-  else
-    filename = NULL;
-
-  g_signal_emit (provider, css_provider_signals[PARSING_ERROR], 0,
-                 filename, line, position, error);
-
-  g_free (filename);
-  g_error_free (error);
-}
-
 static void
 gtk_css_ruleset_init_copy (GtkCssRuleset       *new,
                            const GtkCssRuleset *ruleset,
@@ -1026,6 +1122,8 @@ gtk_css_ruleset_init_copy (GtkCssRuleset       *new,
     g_hash_table_ref (new->widget_style);
   if (new->style)
     g_hash_table_ref (new->style);
+  if (new->set_styles)
+    new->set_styles = _gtk_bitmask_copy (new->set_styles);
 }
 
 static void
@@ -1033,6 +1131,8 @@ gtk_css_ruleset_clear (GtkCssRuleset *ruleset)
 {
   if (ruleset->style)
     g_hash_table_unref (ruleset->style);
+  if (ruleset->set_styles)
+    _gtk_bitmask_free (ruleset->set_styles);
   if (ruleset->widget_style)
     g_hash_table_unref (ruleset->widget_style);
   if (ruleset->selector)
@@ -1041,19 +1141,39 @@ gtk_css_ruleset_clear (GtkCssRuleset *ruleset)
   memset (ruleset, 0, sizeof (GtkCssRuleset));
 }
 
+typedef struct _PropertyValue PropertyValue;
+struct _PropertyValue {
+  GtkCssSection *section;
+  GValue         value;
+};
+
+static PropertyValue *
+property_value_new (GtkCssSection *section)
+{
+  PropertyValue *value;
+
+  value = g_slice_new0 (PropertyValue);
+
+  value->section = gtk_css_section_ref (section);
+
+  return value;
+}
+
 static void
-property_value_free (GValue *value)
+property_value_free (PropertyValue *value)
 {
-  if (G_IS_VALUE (value))
-    g_value_unset (value);
+  if (G_IS_VALUE (&value->value))
+    g_value_unset (&value->value);
+
+  gtk_css_section_unref (value->section);
 
-  g_slice_free (GValue, value);
+  g_slice_free (PropertyValue, value);
 }
 
 static void
 gtk_css_ruleset_add_style (GtkCssRuleset *ruleset,
                            char          *name,
-                           GValue        *value)
+                           PropertyValue *value)
 {
   if (ruleset->widget_style == NULL)
     ruleset->widget_style = g_hash_table_new_full (g_str_hash,
@@ -1065,81 +1185,85 @@ gtk_css_ruleset_add_style (GtkCssRuleset *ruleset,
 }
 
 static void
-gtk_css_ruleset_add (GtkCssRuleset          *ruleset,
-                     const GtkStyleProperty *prop,
-                     GValue                 *value)
+gtk_css_ruleset_add (GtkCssRuleset    *ruleset,
+                     GtkStyleProperty *prop,
+                     PropertyValue    *value)
 {
   if (ruleset->style == NULL)
-    ruleset->style = g_hash_table_new_full (g_direct_hash,
-                                            g_direct_equal,
-                                            NULL,
-                                            (GDestroyNotify) property_value_free);
-
-  if (_gtk_style_property_is_shorthand (prop))
     {
-      GParameter *parameters;
-      guint i, n_parameters;
+      ruleset->style = g_hash_table_new_full (g_direct_hash,
+                                              g_direct_equal,
+                                              NULL,
+                                              (GDestroyNotify) property_value_free);
+      ruleset->set_styles = _gtk_bitmask_new ();
+    }
 
-      parameters = _gtk_style_property_unpack (prop, value, &n_parameters);
+  if (GTK_IS_CSS_SHORTHAND_PROPERTY (prop))
+    {
+      GtkCssShorthandProperty *shorthand = GTK_CSS_SHORTHAND_PROPERTY (prop);
+      GArray *array = g_value_get_boxed (&value->value);
+      guint i;
 
-      for (i = 0; i < n_parameters; i++)
+      for (i = 0; i < _gtk_css_shorthand_property_get_n_subproperties (shorthand); i++)
         {
-          const GtkStyleProperty *child;
-          GValue *value;
-
-          child = _gtk_style_property_lookup (parameters[i].name);
-          value = g_slice_dup (GValue, &parameters[i].value);
-          gtk_css_ruleset_add (ruleset, child, value);
+          GtkCssStyleProperty *child = _gtk_css_shorthand_property_get_subproperty (shorthand, i);
+          const GValue *sub = &g_array_index (array, GValue, i);
+          PropertyValue *val;
+          
+          val = property_value_new (value->section);
+          g_value_init (&val->value, G_VALUE_TYPE (sub));
+          g_value_copy (sub, &val->value);
+          gtk_css_ruleset_add (ruleset, GTK_STYLE_PROPERTY (child), val);
         }
-      g_free (parameters);
-      return;
+      property_value_free (value);
     }
+  else if (GTK_IS_CSS_STYLE_PROPERTY (prop))
+    {
+      g_return_if_fail (_gtk_css_style_property_is_specified_type (GTK_CSS_STYLE_PROPERTY (prop),
+                                                                   G_VALUE_TYPE (&value->value)));
 
-  ruleset->has_inherit |= gtk_style_param_get_inherit (prop->pspec);
-  g_hash_table_insert (ruleset->style, (gpointer) prop, value);
+      _gtk_bitmask_set (ruleset->set_styles,
+                        _gtk_css_style_property_get_id (GTK_CSS_STYLE_PROPERTY (prop)),
+                        TRUE);
+      g_hash_table_insert (ruleset->style, prop, value);
+    }
+  else
+    {
+      g_assert_not_reached ();
+    }
 }
 
 static gboolean
 gtk_css_ruleset_matches (GtkCssRuleset *ruleset,
                          GtkWidgetPath *path,
-                         guint          length)
+                         GtkStateFlags  state)
 {
-  return _gtk_css_selector_matches (ruleset->selector, path, length);
-}
-
-static void
-gtk_css_scanner_reset (GtkCssScanner *scanner)
-{
-  g_slist_free (scanner->state);
-  scanner->state = NULL;
-
-  g_slist_free_full (scanner->cur_selectors, (GDestroyNotify) _gtk_css_selector_free);
-  scanner->cur_selectors = NULL;
-
-  if (scanner->cur_properties)
-    g_hash_table_unref (scanner->cur_properties);
-
-  scanner ->cur_properties = g_hash_table_new_full (g_str_hash,
-                                                    g_str_equal,
-                                                    (GDestroyNotify) g_free,
-                                                    (GDestroyNotify) property_value_free);
+  return _gtk_css_selector_matches (ruleset->selector, path, state);
 }
 
 static void
 gtk_css_scanner_destroy (GtkCssScanner *scanner)
 {
-  gtk_css_scanner_reset (scanner);
-
+  if (scanner->section)
+    gtk_css_section_unref (scanner->section);
   g_object_unref (scanner->provider);
   if (scanner->file)
     g_object_unref (scanner->file);
   g_object_unref (scanner->base);
-  g_hash_table_destroy (scanner->cur_properties);
   _gtk_css_parser_free (scanner->parser);
 
   g_slice_free (GtkCssScanner, scanner);
 }
 
+static void
+gtk_css_provider_emit_error (GtkCssProvider *provider,
+                             GtkCssScanner  *scanner,
+                             const GError   *error)
+{
+  g_signal_emit (provider, css_provider_signals[PARSING_ERROR], 0,
+                 scanner != NULL ? scanner->section : NULL, error);
+}
+
 static void
 gtk_css_scanner_parser_error (GtkCssParser *parser,
                               const GError *error,
@@ -1147,29 +1271,27 @@ gtk_css_scanner_parser_error (GtkCssParser *parser,
 {
   GtkCssScanner *scanner = user_data;
 
-  gtk_css_provider_take_error_full (scanner->provider,
-                                    scanner->file,
-                                    _gtk_css_parser_get_line (scanner->parser),
-                                    _gtk_css_parser_get_position (scanner->parser),
-                                    g_error_copy (error));
+  gtk_css_provider_emit_error (scanner->provider,
+                               scanner,
+                               error);
 }
 
 static GtkCssScanner *
 gtk_css_scanner_new (GtkCssProvider *provider,
                      GtkCssScanner  *parent,
+                     GtkCssSection  *section,
                      GFile          *file,
-                     const gchar    *data,
-                     gsize           length)
+                     const gchar    *text)
 {
   GtkCssScanner *scanner;
 
-  g_assert (data[length] == 0);
-
   scanner = g_slice_new0 (GtkCssScanner);
 
   g_object_ref (provider);
   scanner->provider = provider;
   scanner->parent = parent;
+  if (section)
+    scanner->section = gtk_css_section_ref (section);
 
   if (file)
     {
@@ -1183,12 +1305,7 @@ gtk_css_scanner_new (GtkCssProvider *provider,
       g_free (dir);
     }
 
-  scanner->cur_properties = g_hash_table_new_full (g_str_hash,
-                                                   g_str_equal,
-                                                   (GDestroyNotify) g_free,
-                                                   (GDestroyNotify) property_value_free);
-
-  scanner->parser = _gtk_css_parser_new (data,
+  scanner->parser = _gtk_css_parser_new (text,
                                          gtk_css_scanner_parser_error,
                                          scanner);
 
@@ -1216,6 +1333,40 @@ gtk_css_scanner_would_recurse (GtkCssScanner *scanner,
   return FALSE;
 }
 
+static void
+gtk_css_scanner_push_section (GtkCssScanner     *scanner,
+                              GtkCssSectionType  section_type)
+{
+  GtkCssSection *section;
+
+  section = _gtk_css_section_new (scanner->section,
+                                  section_type,
+                                  scanner->parser,
+                                  scanner->file);
+
+  if (scanner->section)
+    gtk_css_section_unref (scanner->section);
+  scanner->section = section;
+}
+
+static void
+gtk_css_scanner_pop_section (GtkCssScanner *scanner,
+                             GtkCssSectionType check_type)
+{
+  GtkCssSection *parent;
+  
+  g_assert (gtk_css_section_get_section_type (scanner->section) == check_type);
+
+  parent = gtk_css_section_get_parent (scanner->section);
+  if (parent)
+    gtk_css_section_ref (parent);
+
+  _gtk_css_section_end (scanner->section);
+  gtk_css_section_unref (scanner->section);
+
+  scanner->section = parent;
+}
+
 static void
 gtk_css_provider_init (GtkCssProvider *css_provider)
 {
@@ -1262,68 +1413,45 @@ gtk_css_provider_get_style (GtkStyleProvider *provider,
   GtkCssProvider *css_provider;
   GtkCssProviderPrivate *priv;
   GtkStyleProperties *props;
-  guint i, l, length;
+  guint i;
 
   css_provider = GTK_CSS_PROVIDER (provider);
   priv = css_provider->priv;
-  length = gtk_widget_path_length (path);
   props = gtk_style_properties_new ();
 
   css_provider_dump_symbolic_colors (css_provider, props);
 
-  for (l = 1; l <= length; l++)
+  for (i = 0; i < priv->rulesets->len; i++)
     {
-      for (i = 0; i < priv->rulesets->len; i++)
-        {
-          GtkCssRuleset *ruleset;
-          GHashTableIter iter;
-          gpointer key, value;
-
-          ruleset = &g_array_index (priv->rulesets, GtkCssRuleset, i);
-
-          if (ruleset->style == NULL)
-            continue;
+      GtkCssRuleset *ruleset;
+      GHashTableIter iter;
+      gpointer key, val;
 
-          if (l < length && (!ruleset->has_inherit || _gtk_css_selector_get_state_flags (ruleset->selector)))
-            continue;
+      ruleset = &g_array_index (priv->rulesets, GtkCssRuleset, i);
 
-          if (!gtk_css_ruleset_matches (ruleset, path, l))
-            continue;
+      if (ruleset->style == NULL)
+        continue;
 
-          g_hash_table_iter_init (&iter, ruleset->style);
+      if (!gtk_css_ruleset_matches (ruleset, path, 0))
+        continue;
 
-          while (g_hash_table_iter_next (&iter, &key, &value))
-            {
-              GtkStyleProperty *prop = key;
+      g_hash_table_iter_init (&iter, ruleset->style);
 
-              if (l != length && !gtk_style_param_get_inherit (prop->pspec))
-                continue;
+      while (g_hash_table_iter_next (&iter, &key, &val))
+        {
+          GtkCssStyleProperty *prop = key;
+          PropertyValue *value = val;
 
-              _gtk_style_properties_set_property_by_property (props,
-                                                              prop,
-                                                              _gtk_css_selector_get_state_flags (ruleset->selector),
-                                                              value);
-            }
+          _gtk_style_properties_set_property_by_property (props,
+                                                          prop,
+                                                          _gtk_css_selector_get_state_flags (ruleset->selector),
+                                                          &value->value);
         }
     }
 
   return props;
 }
 
-static void
-gtk_css_provider_parser_error (GtkCssParser *parser,
-                               const GError *error,
-                               gpointer      user_data)
-{
-  GtkCssProvider *provider = user_data;
-
-  gtk_css_provider_take_error_full (provider,
-                                    NULL,
-                                    _gtk_css_parser_get_line (parser),
-                                    _gtk_css_parser_get_position (parser),
-                                    g_error_copy (error));
-}
-
 static gboolean
 gtk_css_provider_get_style_property (GtkStyleProvider *provider,
                                      GtkWidgetPath    *path,
@@ -1333,7 +1461,7 @@ gtk_css_provider_get_style_property (GtkStyleProvider *provider,
 {
   GtkCssProvider *css_provider = GTK_CSS_PROVIDER (provider);
   GtkCssProviderPrivate *priv = css_provider->priv;
-  const GValue *val;
+  PropertyValue *val;
   gboolean found = FALSE;
   gchar *prop_name;
   gint i;
@@ -1345,36 +1473,32 @@ gtk_css_provider_get_style_property (GtkStyleProvider *provider,
   for (i = priv->rulesets->len - 1; i >= 0; i--)
     {
       GtkCssRuleset *ruleset;
-      GtkStateFlags selector_state;
 
       ruleset = &g_array_index (priv->rulesets, GtkCssRuleset, i);
 
       if (ruleset->widget_style == NULL)
         continue;
 
-      if (!gtk_css_ruleset_matches (ruleset, path, gtk_widget_path_length (path)))
+      if (!gtk_css_ruleset_matches (ruleset, path, state))
         continue;
 
-      selector_state = _gtk_css_selector_get_state_flags (ruleset->selector);
       val = g_hash_table_lookup (ruleset->widget_style, prop_name);
 
-      if (val &&
-          (selector_state == 0 ||
-           selector_state == state ||
-           ((selector_state & state) != 0 &&
-            (selector_state & ~(state)) == 0)))
+      if (val)
         {
-          GtkCssParser *parser;
+          GtkCssScanner *scanner;
 
-          parser = _gtk_css_parser_new (g_value_get_string (val),
-                                        gtk_css_provider_parser_error,
-                                        provider);
+          scanner = gtk_css_scanner_new (css_provider,
+                                         NULL,
+                                         val->section,
+                                         gtk_css_section_get_file (val->section),
+                                         g_value_get_string (&val->value));
 
-          found = _gtk_css_value_parse (value,
-                                        parser,
-                                        NULL);
+          found = _gtk_css_style_parse_value (value,
+                                              scanner->parser,
+                                              NULL);
 
-          _gtk_css_parser_free (parser);
+          gtk_css_scanner_destroy (scanner);
 
           if (found)
             break;
@@ -1393,6 +1517,69 @@ gtk_css_style_provider_iface_init (GtkStyleProviderIface *iface)
   iface->get_style_property = gtk_css_provider_get_style_property;
 }
 
+static GtkSymbolicColor *
+gtk_css_style_provider_get_color (GtkStyleProviderPrivate *provider,
+                                  const char              *name)
+{
+  GtkCssProvider *css_provider = GTK_CSS_PROVIDER (provider);
+
+  return g_hash_table_lookup (css_provider->priv->symbolic_colors, name);
+}
+
+static void
+gtk_css_style_provider_lookup (GtkStyleProviderPrivate *provider,
+                               GtkWidgetPath           *path,
+                               GtkStateFlags            state,
+                               GtkCssLookup            *lookup)
+{
+  GtkCssProvider *css_provider;
+  GtkCssProviderPrivate *priv;
+  int i;
+
+  css_provider = GTK_CSS_PROVIDER (provider);
+  priv = css_provider->priv;
+
+  for (i = priv->rulesets->len - 1; i >= 0; i--)
+    {
+      GtkCssRuleset *ruleset;
+      GHashTableIter iter;
+      gpointer key, val;
+
+      ruleset = &g_array_index (priv->rulesets, GtkCssRuleset, i);
+
+      if (ruleset->style == NULL)
+        continue;
+
+      if (!_gtk_bitmask_intersects (_gtk_css_lookup_get_missing (lookup),
+                                    ruleset->set_styles))
+        continue;
+
+      if (!gtk_css_ruleset_matches (ruleset, path, state))
+        continue;
+
+      g_hash_table_iter_init (&iter, ruleset->style);
+
+      while (g_hash_table_iter_next (&iter, &key, &val))
+        {
+          GtkCssStyleProperty *prop = key;
+          PropertyValue *value = val;
+          guint id = _gtk_css_style_property_get_id (prop);
+
+          if (!_gtk_css_lookup_is_missing (lookup, id))
+            continue;
+
+          _gtk_css_lookup_set (lookup, id, value->section, &value->value);
+        }
+    }
+}
+
+static void
+gtk_css_style_provider_private_iface_init (GtkStyleProviderPrivateInterface *iface)
+{
+  iface->get_color = gtk_css_style_provider_get_color;
+  iface->lookup = gtk_css_style_provider_lookup;
+}
+
 static void
 gtk_css_provider_finalize (GObject *object)
 {
@@ -1411,6 +1598,13 @@ gtk_css_provider_finalize (GObject *object)
   if (priv->symbolic_colors)
     g_hash_table_destroy (priv->symbolic_colors);
 
+  if (priv->resource)
+    {
+      g_resources_unregister (priv->resource);
+      g_resource_unref (priv->resource);
+      priv->resource = NULL;
+    }
+
   G_OBJECT_CLASS (gtk_css_provider_parent_class)->finalize (object);
 }
 
@@ -1432,11 +1626,11 @@ gtk_css_provider_take_error (GtkCssProvider *provider,
                              GtkCssScanner  *scanner,
                              GError         *error)
 {
-  gtk_css_provider_take_error_full (provider,
-                                    scanner->file,
-                                    _gtk_css_parser_get_line (scanner->parser),
-                                    _gtk_css_parser_get_position (scanner->parser),
-                                    error);
+  gtk_css_provider_emit_error (provider,
+                               scanner,
+                               error);
+
+  g_error_free (error);
 }
 
 static void
@@ -1524,6 +1718,15 @@ gtk_css_provider_reset (GtkCssProvider *css_provider)
 
   priv = css_provider->priv;
 
+  if (priv->resource)
+    {
+      g_resources_unregister (priv->resource);
+      g_resource_unref (priv->resource);
+      priv->resource = NULL;
+    }
+
+  g_hash_table_remove_all (priv->symbolic_colors);
+
   for (i = 0; i < priv->rulesets->len; i++)
     gtk_css_ruleset_clear (&g_array_index (priv->rulesets, GtkCssRuleset, i));
   g_array_set_size (priv->rulesets, 0);
@@ -1531,16 +1734,37 @@ gtk_css_provider_reset (GtkCssProvider *css_provider)
 
 static void
 gtk_css_provider_propagate_error (GtkCssProvider  *provider,
-                                  const gchar     *path,
-                                  guint            line,
-                                  guint            position,
+                                  GtkCssSection   *section,
                                   const GError    *error,
                                   GError         **propagate_to)
 {
+
+  GFileInfo *info;
+  GFile *file;
+  const char *path;
+
+  file = gtk_css_section_get_file (section);
+  if (file)
+    {
+      info = g_file_query_info (file,G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME, 0, NULL, NULL);
+
+      if (info)
+        path = g_file_info_get_display_name (info);
+      else
+        path = "<broken file>";
+    }
+  else
+    {
+      info = NULL;
+      path = "<unknown>";
+    }
+
   /* don't fail for deprecations */
   if (g_error_matches (error, GTK_CSS_PROVIDER_ERROR, GTK_CSS_PROVIDER_ERROR_DEPRECATED))
     {
-      g_warning ("Theme parsing error: %s:%u:%u: %s", path ? path : "<unknown>", line, position, error->message);
+      g_warning ("Theme parsing error: %s:%u:%u: %s", path,
+                 gtk_css_section_get_end_line (section) + 1,
+                 gtk_css_section_get_end_position (section), error->message);
       return;
     }
 
@@ -1549,26 +1773,54 @@ gtk_css_provider_propagate_error (GtkCssProvider  *provider,
     return;
 
   *propagate_to = g_error_copy (error);
-  g_prefix_error (propagate_to, "%s:%u:%u: ", path ? path : "<unknown>", line, position);
+  g_prefix_error (propagate_to, "%s:%u:%u: ", path,
+                  gtk_css_section_get_end_line (section) + 1,
+                  gtk_css_section_get_end_position (section));
+
+  if (info)
+    g_object_unref (info);
 }
 
-static void
+static gboolean
 parse_import (GtkCssScanner *scanner)
 {
   GFile *file;
-  char *uri;
 
-  uri = _gtk_css_parser_read_uri (scanner->parser);
-  if (uri == NULL)
+  gtk_css_scanner_push_section (scanner, GTK_CSS_SECTION_IMPORT);
+
+  if (!_gtk_css_parser_try (scanner->parser, "@import", TRUE))
     {
-      _gtk_css_parser_resync (scanner->parser, TRUE, 0);
-      return;
+      gtk_css_scanner_pop_section (scanner, GTK_CSS_SECTION_IMPORT);
+      return FALSE;
     }
 
-  file = g_file_resolve_relative_path (gtk_css_scanner_get_base_url (scanner), uri);
-  g_free (uri);
+  if (_gtk_css_parser_is_string (scanner->parser))
+    {
+      char *uri;
+
+      uri = _gtk_css_parser_read_string (scanner->parser);
+      file = g_file_resolve_relative_path (gtk_css_scanner_get_base_url (scanner), uri);
+      g_free (uri);
+    }
+  else
+    {
+      file = _gtk_css_parser_read_url (scanner->parser,
+                                       gtk_css_scanner_get_base_url (scanner));
+    }
 
-  if (gtk_css_scanner_would_recurse (scanner, file))
+  if (file == NULL)
+    {
+      _gtk_css_parser_resync (scanner->parser, TRUE, 0);
+      gtk_css_scanner_pop_section (scanner, GTK_CSS_SECTION_IMPORT);
+      return TRUE;
+    }
+
+  if (!_gtk_css_parser_try (scanner->parser, ";", FALSE))
+    {
+      gtk_css_provider_invalid_token (scanner->provider, scanner, "semicolon");
+      _gtk_css_parser_resync (scanner->parser, TRUE, 0);
+    }
+  else if (gtk_css_scanner_would_recurse (scanner, file))
     {
        char *path = g_file_get_path (file);
        gtk_css_provider_error (scanner->provider,
@@ -1584,27 +1836,32 @@ parse_import (GtkCssScanner *scanner)
       gtk_css_provider_load_internal (scanner->provider,
                                       scanner,
                                       file,
-                                      NULL, 0,
+                                      NULL,
                                       NULL);
     }
 
-  if (!_gtk_css_parser_try (scanner->parser, ";", TRUE))
-    {
-      g_object_unref (file);
-      gtk_css_provider_invalid_token (scanner->provider, scanner, "semicolon");
-      _gtk_css_parser_resync (scanner->parser, TRUE, 0);
-      return;
-    }
-
   g_object_unref (file);
+
+  gtk_css_scanner_pop_section (scanner, GTK_CSS_SECTION_IMPORT);
+  _gtk_css_parser_skip_whitespace (scanner->parser);
+
+  return TRUE;
 }
 
-static void
+static gboolean
 parse_color_definition (GtkCssScanner *scanner)
 {
   GtkSymbolicColor *symbolic;
   char *name;
 
+  gtk_css_scanner_push_section (scanner, GTK_CSS_SECTION_COLOR_DEFINITION);
+
+  if (!_gtk_css_parser_try (scanner->parser, "@define-color", TRUE))
+    {
+      gtk_css_scanner_pop_section (scanner, GTK_CSS_SECTION_COLOR_DEFINITION);
+      return FALSE;
+    }
+
   name = _gtk_css_parser_try_name (scanner->parser, TRUE);
   if (name == NULL)
     {
@@ -1614,7 +1871,8 @@ parse_color_definition (GtkCssScanner *scanner)
                                       GTK_CSS_PROVIDER_ERROR_SYNTAX,
                                       "Not a valid color name");
       _gtk_css_parser_resync (scanner->parser, TRUE, 0);
-      return;
+      gtk_css_scanner_pop_section (scanner, GTK_CSS_SECTION_COLOR_DEFINITION);
+      return TRUE;
     }
 
   symbolic = _gtk_css_parser_read_symbolic_color (scanner->parser);
@@ -1622,7 +1880,8 @@ parse_color_definition (GtkCssScanner *scanner)
     {
       g_free (name);
       _gtk_css_parser_resync (scanner->parser, TRUE, 0);
-      return;
+      gtk_css_scanner_pop_section (scanner, GTK_CSS_SECTION_COLOR_DEFINITION);
+      return TRUE;
     }
 
   if (!_gtk_css_parser_try (scanner->parser, ";", TRUE))
@@ -1635,18 +1894,31 @@ parse_color_definition (GtkCssScanner *scanner)
                                       GTK_CSS_PROVIDER_ERROR_SYNTAX,
                                       "Missing semicolon at end of color definition");
       _gtk_css_parser_resync (scanner->parser, TRUE, 0);
-      return;
+
+      gtk_css_scanner_pop_section (scanner, GTK_CSS_SECTION_COLOR_DEFINITION);
+      return TRUE;
     }
 
   g_hash_table_insert (scanner->provider->priv->symbolic_colors, name, symbolic);
+
+  gtk_css_scanner_pop_section (scanner, GTK_CSS_SECTION_COLOR_DEFINITION);
+  return TRUE;
 }
 
-static void
+static gboolean
 parse_binding_set (GtkCssScanner *scanner)
 {
   GtkBindingSet *binding_set;
   char *name;
 
+  gtk_css_scanner_push_section (scanner, GTK_CSS_SECTION_BINDING_SET);
+
+  if (!_gtk_css_parser_try (scanner->parser, "@binding-set", TRUE))
+    {
+      gtk_css_scanner_pop_section (scanner, GTK_CSS_SECTION_BINDING_SET);
+      return FALSE;
+    }
+
   name = _gtk_css_parser_try_ident (scanner->parser, TRUE);
   if (name == NULL)
     {
@@ -1688,7 +1960,15 @@ parse_binding_set (GtkCssScanner *scanner)
           continue;
         }
 
-      gtk_binding_entry_add_signal_from_string (binding_set, name);
+      if (gtk_binding_entry_add_signal_from_string (binding_set, name) != G_TOKEN_NONE)
+        {
+          gtk_css_provider_error_literal (scanner->provider,
+                                          scanner,
+                                          GTK_CSS_PROVIDER_ERROR,
+                                          GTK_CSS_PROVIDER_ERROR_SYNTAX,
+                                          "Failed to parse binding set.");
+        }
+
       g_free (name);
 
       if (!_gtk_css_parser_try (scanner->parser, ";", TRUE))
@@ -1718,23 +1998,31 @@ parse_binding_set (GtkCssScanner *scanner)
     }
 
 skip_semicolon:
-  if (_gtk_css_parser_try (scanner->parser, ";", TRUE))
-    gtk_css_provider_error_literal (scanner->provider,
-                                    scanner,
-                                    GTK_CSS_PROVIDER_ERROR,
-                                    GTK_CSS_PROVIDER_ERROR_DEPRECATED,
-                                    "Nonstandard semicolon at end of binding set");
+  if (_gtk_css_parser_begins_with (scanner->parser, ';'))
+    {
+      gtk_css_provider_error_literal (scanner->provider,
+                                      scanner,
+                                      GTK_CSS_PROVIDER_ERROR,
+                                      GTK_CSS_PROVIDER_ERROR_DEPRECATED,
+                                      "Nonstandard semicolon at end of binding set");
+      _gtk_css_parser_try (scanner->parser, ";", TRUE);
+    }
+
+  gtk_css_scanner_pop_section (scanner, GTK_CSS_SECTION_BINDING_SET);
+
+  return TRUE;
 }
 
 static void
 parse_at_keyword (GtkCssScanner *scanner)
 {
-  if (_gtk_css_parser_try (scanner->parser, "@import", TRUE))
-    parse_import (scanner);
-  else if (_gtk_css_parser_try (scanner->parser, "@define-color", TRUE))
-    parse_color_definition (scanner);
-  else if (_gtk_css_parser_try (scanner->parser, "@binding-set", TRUE))
-    parse_binding_set (scanner);
+  if (parse_import (scanner))
+    return;
+  if (parse_color_definition (scanner))
+    return;
+  if (parse_binding_set (scanner))
+    return;
+
   else
     {
       gtk_css_provider_error_literal (scanner->provider,
@@ -1804,8 +2092,9 @@ parse_selector_pseudo_class (GtkCssScanner  *scanner,
     GtkRegionFlags region_flag;
     GtkStateFlags state_flag;
   } pseudo_classes[] = {
-    { "first",        GTK_REGION_FIRST, 0 },
-    { "last",         GTK_REGION_LAST, 0 },
+    { "first-child",  GTK_REGION_FIRST, 0 },
+    { "last-child",   GTK_REGION_LAST, 0 },
+    { "only-child",   GTK_REGION_ONLY, 0 },
     { "sorted",       GTK_REGION_SORTED, 0 },
     { "active",       0, GTK_STATE_FLAG_ACTIVE },
     { "prelight",     0, GTK_STATE_FLAG_PRELIGHT },
@@ -1815,6 +2104,7 @@ parse_selector_pseudo_class (GtkCssScanner  *scanner,
     { "inconsistent", 0, GTK_STATE_FLAG_INCONSISTENT },
     { "focused",      0, GTK_STATE_FLAG_FOCUSED },
     { "focus",        0, GTK_STATE_FLAG_FOCUSED },
+    { "backdrop",     0, GTK_STATE_FLAG_BACKDROP },
     { NULL, }
   }, nth_child_classes[] = {
     { "first",        GTK_REGION_FIRST, 0 },
@@ -2026,6 +2316,8 @@ parse_selector_list (GtkCssScanner *scanner)
 {
   GSList *selectors = NULL;
 
+  gtk_css_scanner_push_section (scanner, GTK_CSS_SECTION_SELECTOR);
+
   do {
       GtkCssSelector *select = parse_selector (scanner);
 
@@ -2033,6 +2325,7 @@ parse_selector_list (GtkCssScanner *scanner)
         {
           g_slist_free_full (selectors, (GDestroyNotify) _gtk_css_selector_free);
           _gtk_css_parser_resync (scanner->parser, FALSE, 0);
+          gtk_css_scanner_pop_section (scanner, GTK_CSS_SECTION_SELECTOR);
           return NULL;
         }
 
@@ -2040,6 +2333,8 @@ parse_selector_list (GtkCssScanner *scanner)
     }
   while (_gtk_css_parser_try (scanner->parser, ",", TRUE));
 
+  gtk_css_scanner_pop_section (scanner, GTK_CSS_SECTION_SELECTOR);
+
   return selectors;
 }
 
@@ -2047,9 +2342,11 @@ static void
 parse_declaration (GtkCssScanner *scanner,
                    GtkCssRuleset *ruleset)
 {
-  const GtkStyleProperty *property;
+  GtkStyleProperty *property;
   char *name;
 
+  gtk_css_scanner_push_section (scanner, GTK_CSS_SECTION_DECLARATION);
+
   name = _gtk_css_parser_try_ident (scanner->parser, TRUE);
   if (name == NULL)
     goto check_for_semicolon;
@@ -2065,6 +2362,7 @@ parse_declaration (GtkCssScanner *scanner,
                               name);
       _gtk_css_parser_resync (scanner->parser, TRUE, '}');
       g_free (name);
+      gtk_css_scanner_pop_section (scanner, GTK_CSS_SECTION_DECLARATION);
       return;
     }
 
@@ -2073,109 +2371,88 @@ parse_declaration (GtkCssScanner *scanner,
       gtk_css_provider_invalid_token (scanner->provider, scanner, "':'");
       _gtk_css_parser_resync (scanner->parser, TRUE, '}');
       g_free (name);
+      gtk_css_scanner_pop_section (scanner, GTK_CSS_SECTION_DECLARATION);
       return;
     }
 
   if (property)
     {
-      GValue *val;
+      PropertyValue *val;
 
       g_free (name);
 
-      val = g_slice_new0 (GValue);
-      g_value_init (val, property->pspec->value_type);
-
-      if (_gtk_css_parser_try (scanner->parser, "none", TRUE))
-        {
-          /* Insert the default value, so it has an opportunity
-           * to override other style providers when merged
-           */
-          g_param_value_set_default (property->pspec, val);
-          gtk_css_ruleset_add (ruleset, property, val);
-        }
-      else if (property->parse_func)
-        {
-          GError *error = NULL;
-          char *value_str;
+      gtk_css_scanner_push_section (scanner, GTK_CSS_SECTION_VALUE);
 
-          value_str = _gtk_css_parser_read_value (scanner->parser);
-          if (value_str == NULL)
-            {
-              _gtk_css_parser_resync (scanner->parser, TRUE, '}');
-              g_slice_free (GValue, val);
-              return;
-            }
+      val = property_value_new (scanner->section);
 
-          if ((*property->parse_func) (value_str, val, &error))
-            gtk_css_ruleset_add (ruleset, property, val);
-          else
-            {
-              gtk_css_provider_take_error (scanner->provider, scanner, error);
-              g_value_unset (val);
-              g_slice_free (GValue, val);
-            }
-
-          g_free (value_str);
-        }
-      else
+      if (_gtk_style_property_parse_value (property,
+                                           &val->value,
+                                           scanner->parser,
+                                           gtk_css_scanner_get_base_url (scanner)))
         {
-          if (_gtk_css_value_parse (val,
-                                    scanner->parser,
-                                    gtk_css_scanner_get_base_url (scanner)))
+          if (_gtk_css_parser_begins_with (scanner->parser, ';') ||
+              _gtk_css_parser_begins_with (scanner->parser, '}') ||
+              _gtk_css_parser_is_eof (scanner->parser))
             {
-              if (_gtk_css_parser_begins_with (scanner->parser, ';') ||
-                  _gtk_css_parser_begins_with (scanner->parser, '}') ||
-                  _gtk_css_parser_is_eof (scanner->parser))
-                {
-                  gtk_css_ruleset_add (ruleset, property, val);
-                }
-              else
-                {
-                  gtk_css_provider_error_literal (scanner->provider,
-                                                  scanner,
-                                                  GTK_CSS_PROVIDER_ERROR,
-                                                  GTK_CSS_PROVIDER_ERROR_SYNTAX,
-                                                  "Junk at end of value");
-                  _gtk_css_parser_resync (scanner->parser, TRUE, '}');
-                  g_value_unset (val);
-                  g_slice_free (GValue, val);
-                  return;
-                }
+              gtk_css_ruleset_add (ruleset, property, val);
             }
           else
             {
-              g_value_unset (val);
-              g_slice_free (GValue, val);
+              gtk_css_provider_error_literal (scanner->provider,
+                                              scanner,
+                                              GTK_CSS_PROVIDER_ERROR,
+                                              GTK_CSS_PROVIDER_ERROR_SYNTAX,
+                                              "Junk at end of value");
               _gtk_css_parser_resync (scanner->parser, TRUE, '}');
+              property_value_free (val);
+              gtk_css_scanner_pop_section (scanner, GTK_CSS_SECTION_VALUE);
+              gtk_css_scanner_pop_section (scanner, GTK_CSS_SECTION_DECLARATION);
               return;
             }
         }
+      else
+        {
+          property_value_free (val);
+          _gtk_css_parser_resync (scanner->parser, TRUE, '}');
+          gtk_css_scanner_pop_section (scanner, GTK_CSS_SECTION_VALUE);
+          gtk_css_scanner_pop_section (scanner, GTK_CSS_SECTION_DECLARATION);
+          return;
+        }
+      gtk_css_scanner_pop_section (scanner, GTK_CSS_SECTION_VALUE);
     }
   else if (name[0] == '-')
     {
       char *value_str;
 
+      gtk_css_scanner_push_section (scanner, GTK_CSS_SECTION_VALUE);
+
       value_str = _gtk_css_parser_read_value (scanner->parser);
       if (value_str)
         {
-          GValue *val;
+          PropertyValue *val;
 
-          val = g_slice_new0 (GValue);
-          g_value_init (val, G_TYPE_STRING);
-          g_value_take_string (val, value_str);
+          val = property_value_new (scanner->section);
+          g_value_init (&val->value, G_TYPE_STRING);
+          g_value_take_string (&val->value, value_str);
 
           gtk_css_ruleset_add_style (ruleset, name, val);
         }
       else
         {
           _gtk_css_parser_resync (scanner->parser, TRUE, '}');
+          gtk_css_scanner_pop_section (scanner, GTK_CSS_SECTION_VALUE);
+          gtk_css_scanner_pop_section (scanner, GTK_CSS_SECTION_DECLARATION);
           return;
         }
+
+      gtk_css_scanner_pop_section (scanner, GTK_CSS_SECTION_VALUE);
     }
   else
     g_free (name);
 
 check_for_semicolon:
+  gtk_css_scanner_pop_section (scanner, GTK_CSS_SECTION_DECLARATION);
+
   if (!_gtk_css_parser_try (scanner->parser, ";", TRUE))
     {
       if (!_gtk_css_parser_begins_with (scanner->parser, '}') &&
@@ -2208,9 +2485,14 @@ parse_ruleset (GtkCssScanner *scanner)
   GSList *selectors;
   GtkCssRuleset ruleset = { 0, };
 
+  gtk_css_scanner_push_section (scanner, GTK_CSS_SECTION_RULESET);
+
   selectors = parse_selector_list (scanner);
   if (selectors == NULL)
-    return;
+    {
+      gtk_css_scanner_pop_section (scanner, GTK_CSS_SECTION_RULESET);
+      return;
+    }
 
   if (!_gtk_css_parser_try (scanner->parser, "{", TRUE))
     {
@@ -2221,6 +2503,7 @@ parse_ruleset (GtkCssScanner *scanner)
                                       "expected '{' after selectors");
       _gtk_css_parser_resync (scanner->parser, FALSE, 0);
       g_slist_free_full (selectors, (GDestroyNotify) _gtk_css_selector_free);
+      gtk_css_scanner_pop_section (scanner, GTK_CSS_SECTION_RULESET);
       return;
     }
 
@@ -2238,12 +2521,13 @@ parse_ruleset (GtkCssScanner *scanner)
           _gtk_css_parser_resync (scanner->parser, FALSE, 0);
           g_slist_free_full (selectors, (GDestroyNotify) _gtk_css_selector_free);
           gtk_css_ruleset_clear (&ruleset);
-          return;
+          gtk_css_scanner_pop_section (scanner, GTK_CSS_SECTION_RULESET);
         }
     }
 
   css_provider_commit (scanner->provider, selectors, &ruleset);
   gtk_css_ruleset_clear (&ruleset);
+  gtk_css_scanner_pop_section (scanner, GTK_CSS_SECTION_RULESET);
 }
 
 static void
@@ -2258,6 +2542,8 @@ parse_statement (GtkCssScanner *scanner)
 static void
 parse_stylesheet (GtkCssScanner *scanner)
 {
+  gtk_css_scanner_push_section (scanner, GTK_CSS_SECTION_DOCUMENT);
+
   _gtk_css_parser_skip_whitespace (scanner->parser);
 
   while (!_gtk_css_parser_is_eof (scanner->parser))
@@ -2268,6 +2554,8 @@ parse_stylesheet (GtkCssScanner *scanner)
 
       parse_statement (scanner);
     }
+
+  gtk_css_scanner_pop_section (scanner, GTK_CSS_SECTION_DOCUMENT);
 }
 
 static int
@@ -2304,8 +2592,7 @@ static gboolean
 gtk_css_provider_load_internal (GtkCssProvider *css_provider,
                                 GtkCssScanner  *parent,
                                 GFile          *file,
-                                const char     *data,
-                                gsize           length,
+                                const char     *text,
                                 GError        **error)
 {
   GtkCssScanner *scanner;
@@ -2320,41 +2607,43 @@ gtk_css_provider_load_internal (GtkCssProvider *css_provider,
   else
     error_handler = 0; /* silence gcc */
 
-  if (data == NULL)
+  if (text == NULL)
     {
       GError *load_error = NULL;
 
       if (g_file_load_contents (file, NULL,
-                                &free_data, &length,
+                                &free_data, NULL,
                                 NULL, &load_error))
         {
-          data = free_data;
+          text = free_data;
         }
       else
         {
+          GtkCssSection *section;
+          
           if (parent)
-            {
-              gtk_css_provider_error (css_provider,
-                                      parent,
-                                      GTK_CSS_PROVIDER_ERROR,
-                                      GTK_CSS_PROVIDER_ERROR_IMPORT,
-                                      "Failed to import: %s",
-                                      load_error->message);
-              g_error_free (load_error);
-            }
+            section = gtk_css_section_ref (parent->section);
           else
-            {
-              gtk_css_provider_take_error_full (css_provider,
-                                                file,
-                                                0, 0,
-                                                load_error);
-            }
+            section = _gtk_css_section_new_for_file (GTK_CSS_SECTION_DOCUMENT, file);
+
+          gtk_css_provider_error (css_provider,
+                                  parent,
+                                  GTK_CSS_PROVIDER_ERROR,
+                                  GTK_CSS_PROVIDER_ERROR_IMPORT,
+                                  "Failed to import: %s",
+                                  load_error->message);
+
+          gtk_css_section_unref (section);
         }
     }
 
-  if (data)
+  if (text)
     {
-      scanner = gtk_css_scanner_new (css_provider, parent, file, data, length);
+      scanner = gtk_css_scanner_new (css_provider,
+                                     parent,
+                                     parent ? parent->section : NULL,
+                                     file,
+                                     text);
 
       parse_stylesheet (scanner);
 
@@ -2384,8 +2673,10 @@ gtk_css_provider_load_internal (GtkCssProvider *css_provider,
 /**
  * gtk_css_provider_load_from_data:
  * @css_provider: a #GtkCssProvider
- * @data: CSS data loaded in memory
- * @length: the length of @data in bytes, or -1 for NUL terminated strings
+ * @data: (array length=length) (element-type guint8): CSS data loaded in memory
+ * @length: the length of @data in bytes, or -1 for NUL terminated strings. If
+ *   @length is not -1, the code will assume it is not NUL terminated and will
+ *   potentially do a copy.
  * @error: (out) (allow-none): return location for a #GError, or %NULL
  *
  * Loads @data into @css_provider, making it clear any previously loaded
@@ -2399,15 +2690,30 @@ gtk_css_provider_load_from_data (GtkCssProvider  *css_provider,
                                  gssize           length,
                                  GError         **error)
 {
+  char *free_data;
+  gboolean ret;
+
   g_return_val_if_fail (GTK_IS_CSS_PROVIDER (css_provider), FALSE);
   g_return_val_if_fail (data != NULL, FALSE);
 
   if (length < 0)
-    length = strlen (data);
+    {
+      length = strlen (data);
+      free_data = NULL;
+    }
+  else
+    {
+      free_data = g_strndup (data, length);
+      data = free_data;
+    }
 
   gtk_css_provider_reset (css_provider);
 
-  return gtk_css_provider_load_internal (css_provider, NULL, NULL, data, length, error);
+  ret = gtk_css_provider_load_internal (css_provider, NULL, NULL, data, error);
+
+  g_free (free_data);
+
+  return ret;
 }
 
 /**
@@ -2431,7 +2737,7 @@ gtk_css_provider_load_from_file (GtkCssProvider  *css_provider,
 
   gtk_css_provider_reset (css_provider);
 
-  return gtk_css_provider_load_internal (css_provider, NULL, file, NULL, 0, error);
+  return gtk_css_provider_load_internal (css_provider, NULL, file, NULL, error);
 }
 
 /**
@@ -2465,6 +2771,32 @@ gtk_css_provider_load_from_path (GtkCssProvider  *css_provider,
   return result;
 }
 
+static gboolean
+_gtk_css_provider_load_from_resource (GtkCssProvider  *css_provider,
+                                     const gchar     *resource_path)
+{
+  GFile *file;
+  char *uri, *escaped;
+  gboolean result;
+
+  g_return_val_if_fail (GTK_IS_CSS_PROVIDER (css_provider), FALSE);
+  g_return_val_if_fail (resource_path != NULL, FALSE);
+
+  escaped = g_uri_escape_string (resource_path,
+                                G_URI_RESERVED_CHARS_ALLOWED_IN_PATH, FALSE);
+  uri = g_strconcat ("resource://", escaped, NULL);
+  g_free (escaped);
+
+  file = g_file_new_for_uri (uri);
+  g_free (uri);
+
+  result = gtk_css_provider_load_from_file (css_provider, file, NULL);
+
+  g_object_unref (file);
+
+  return result;
+}
+
 /**
  * gtk_css_provider_get_default:
  *
@@ -2481,375 +2813,7 @@ gtk_css_provider_get_default (void)
 
   if (G_UNLIKELY (!provider))
     {
-      const gchar *str =
-        "@define-color fg_color #000; \n"
-        "@define-color bg_color #dcdad5; \n"
-        "@define-color text_color #000; \n"
-        "@define-color base_color #fff; \n"
-        "@define-color selected_bg_color #4b6983; \n"
-        "@define-color selected_fg_color #fff; \n"
-        "@define-color tooltip_bg_color #eee1b3; \n"
-        "@define-color tooltip_fg_color #000; \n"
-        "@define-color placeholder_text_color #808080; \n"
-        "\n"
-        "@define-color info_fg_color rgb (181, 171, 156);\n"
-        "@define-color info_bg_color rgb (252, 252, 189);\n"
-        "@define-color warning_fg_color rgb (173, 120, 41);\n"
-        "@define-color warning_bg_color rgb (250, 173, 61);\n"
-        "@define-color question_fg_color rgb (97, 122, 214);\n"
-        "@define-color question_bg_color rgb (138, 173, 212);\n"
-        "@define-color error_fg_color rgb (166, 38, 38);\n"
-        "@define-color error_bg_color rgb (237, 54, 54);\n"
-        "\n"
-        "* {\n"
-        "  background-color: @bg_color;\n"
-        "  color: @fg_color;\n"
-        "  border-color: shade (@bg_color, 0.6);\n"
-        "  padding: 2;\n"
-        "  border-width: 0;\n"
-        "}\n"
-        "\n"
-        "*:prelight {\n"
-        "  background-color: shade (@bg_color, 1.05);\n"
-        "  color: shade (@fg_color, 1.3);\n"
-        "}\n"
-        "\n"
-        "*:selected {\n"
-        "  background-color: @selected_bg_color;\n"
-        "  color: @selected_fg_color;\n"
-        "}\n"
-        "\n"
-        ".expander, GtkTreeView.view.expander {\n"
-        "  color: #fff;\n"
-        "}\n"
-        "\n"
-        ".expander:prelight,\n"
-        "GtkTreeView.view.expander:selected:prelight {\n"
-        "  color: @text_color;\n"
-        "}\n"
-        "\n"
-        ".expander:active {\n"
-        "  transition: 200ms linear;\n"
-        "}\n"
-        "\n"
-        "*:insensitive {\n"
-        "  border-color: shade (@bg_color, 0.7);\n"
-        "  background-color: shade (@bg_color, 0.9);\n"
-        "  color: shade (@bg_color, 0.7);\n"
-        "}\n"
-        "\n"
-        ".view {\n"
-        "  border-width: 0;\n"
-        "  border-radius: 0;\n"
-        "  background-color: @base_color;\n"
-        "  color: @text_color;\n"
-        "}\n"
-        ".view:selected {\n"
-        "  background-color: shade (@bg_color, 0.9);\n"
-        "  color: @fg_color;\n"
-        "}\n"
-        "\n"
-        ".view:selected:focused {\n"
-        "  background-color: @selected_bg_color;\n"
-        "  color: @selected_fg_color;\n"
-        "}\n"
-        "\n"
-        ".view column:sorted row,\n"
-        ".view column:sorted row:prelight {\n"
-        "  background-color: shade (@bg_color, 0.85);\n"
-        "}\n"
-        "\n"
-        ".view column:sorted row:nth-child(odd),\n"
-        ".view column:sorted row:nth-child(odd):prelight {\n"
-        "  background-color: shade (@bg_color, 0.8);\n"
-        "}\n"
-        "\n"
-        ".view row,\n"
-        ".view row:prelight {\n"
-        "  background-color: @base_color;\n"
-        "  color: @text_color;\n"
-        "}\n"
-        "\n"
-        ".view row:nth-child(odd),\n"
-        ".view row:nth-child(odd):prelight {\n"
-        "  background-color: shade (@base_color, 0.93); \n"
-        "}\n"
-        "\n"
-        ".view row:selected:focused {\n"
-        "  background-color: @selected_bg_color;\n"
-        "}\n"
-        "\n"
-        ".view row:selected {\n"
-        "  background-color: darker (@bg_color);\n"
-        "  color: @selected_fg_color;\n"
-        "}\n"
-        "\n"
-        ".view.cell.trough,\n"
-        ".view.cell.trough:hover,\n"
-        ".view.cell.trough:selected,\n"
-        ".view.cell.trough:selected:focused {\n"
-        "  background-color: @bg_color;\n"
-        "  color: @fg_color;\n"
-        "}\n"
-        "\n"
-        ".view.cell.progressbar,\n"
-        ".view.cell.progressbar:hover,\n"
-        ".view.cell.progressbar:selected,\n"
-        ".view.cell.progressbar:selected:focused {\n"
-        "  background-color: @selected_bg_color;\n"
-        "  color: @selected_fg_color;\n"
-        "}\n"
-        "\n"
-        ".rubberband {\n"
-        "  background-color: alpha (@fg_color, 0.25);\n"
-        "  border-color: @fg_color;\n"
-        "  border-style: solid;\n"
-        "  border-width: 1;\n"
-        "}\n"
-        "\n"
-        ".tooltip {\n"
-        "  background-color: @tooltip_bg_color; \n"
-        "  color: @tooltip_fg_color; \n"
-        "  border-color: @tooltip_fg_color; \n"
-        "  border-width: 1;\n"
-        "  border-style: solid;\n"
-        "}\n"
-        "\n"
-        ".button,\n"
-        ".slider {\n"
-        "  border-style: outset; \n"
-        "  border-width: 2; \n"
-        "}\n"
-        "\n"
-        ".button:active {\n"
-        "  background-color: shade (@bg_color, 0.7);\n"
-        "  border-style: inset; \n"
-        "}\n"
-        "\n"
-        ".button:prelight,\n"
-        ".slider:prelight {\n"
-        "  background-color: @selected_bg_color;\n"
-        "  color: @selected_fg_color;\n"
-        "  border-color: shade (@selected_bg_color, 0.7);\n"
-        "}\n"
-        "\n"
-        ".trough {\n"
-        "  background-color: darker (@bg_color);\n"
-        "  border-style: inset;\n"
-        "  border-width: 1;\n"
-        "  padding: 0;\n"
-        "}\n"
-        "\n"
-        ".entry {\n"
-        "  border-style: inset;\n"
-        "  border-width: 2;\n"
-        "  background-color: @base_color;\n"
-        "  color: @text_color;\n"
-        "}\n"
-        "\n"
-        ".entry:insensitive {\n"
-        "  background-color: shade (@base_color, 0.9);\n"
-        "  color: shade (@base_color, 0.7);\n"
-        "}\n"
-        ".entry:active {\n"
-        "  background-color: #c4c2bd;\n"
-        "  color: #000;\n"
-        "}\n"
-        "\n"
-        ".progressbar,\n"
-        ".entry.progressbar, \n"
-        ".cell.progressbar {\n"
-        "  background-color: @selected_bg_color;\n"
-        "  border-color: shade (@selected_bg_color, 0.7);\n"
-        "  color: @selected_fg_color;\n"
-        "  border-style: outset;\n"
-        "  border-width: 1;\n"
-        "}\n"
-        "\n"
-        "GtkCheckButton:hover,\n"
-        "GtkCheckButton:selected,\n"
-        "GtkRadioButton:hover,\n"
-        "GtkRadioButton:selected {\n"
-        "  background-color: shade (@bg_color, 1.05);\n"
-        "}\n"
-        "\n"
-        ".check, .radio,"
-        ".cell.check, .cell.radio,\n"
-        ".cell.check:hover, .cell.radio:hover {\n"
-        "  border-style: solid;\n"
-        "  border-width: 1;\n"
-        "  background-color: @base_color;\n"
-        "  border-color: @fg_color;\n"
-        "}\n"
-        "\n"
-        ".check:active, .radio:active,\n"
-        ".check:hover, .radio:hover {\n"
-        "  background-color: @base_color;\n"
-        "  border-color: @fg_color;\n"
-        "  color: @text_color;\n"
-        "}\n"
-        "\n"
-        ".check:selected, .radio:selected {\n"
-        "  background-color: darker (@bg_color);\n"
-        "  color: @selected_fg_color;\n"
-        "  border-color: @selected_fg_color;\n"
-        "}\n"
-        "\n"
-        ".check:selected:focused, .radio:selected:focused {\n"
-        "  background-color: @selected_bg_color;\n"
-        "}\n"
-        "\n"
-        ".menu.check, .menu.radio {\n"
-        "  color: @fg_color;\n"
-        "  border-style: none;\n"
-        "  border-width: 0;\n"
-        "}\n"
-        "\n"
-        ".popup {\n"
-        "  border-style: outset;\n"
-        "  border-width: 1;\n"
-        "}\n"
-        "\n"
-        ".viewport {\n"
-        "  border-style: inset;\n"
-        "  border-width: 2;\n"
-        "}\n"
-        "\n"
-        ".notebook {\n"
-        "  border-style: outset;\n"
-        "  border-width: 1;\n"
-        "}\n"
-        "\n"
-        ".frame {\n"
-        "  border-style: inset;\n"
-        "  border-width: 1;\n"
-        "}\n"
-        "\n"
-        "GtkScrolledWindow.frame {\n"
-        "  padding: 0;\n"
-        "}\n"
-        "\n"
-        ".menu,\n"
-        ".menubar,\n"
-        ".toolbar {\n"
-        "  border-style: outset;\n"
-        "  border-width: 1;\n"
-        "}\n"
-        "\n"
-        ".menu:hover,\n"
-        ".menubar:hover,\n"
-        ".menu.check:hover,\n"
-        ".menu.radio:hover {\n"
-        "  background-color: @selected_bg_color;\n"
-        "  color: @selected_fg_color;\n"
-        "}\n"
-        "\n"
-        "GtkSpinButton.button {\n"
-        "  border-width: 1;\n"
-        "}\n"
-        "\n"
-        ".scale.slider:hover,\n"
-        "GtkSpinButton.button:hover {\n"
-        "  background-color: shade (@bg_color, 1.05);\n"
-        "  border-color: shade (@bg_color, 0.8);\n"
-        "}\n"
-        "\n"
-        "GtkSwitch.trough:active {\n"
-        "  background-color: @selected_bg_color;\n"
-        "  color: @selected_fg_color;\n"
-        "}\n"
-        "\n"
-        "GtkToggleButton.button:inconsistent {\n"
-        "  border-style: outset;\n"
-        "  border-width: 1px;\n"
-        "  background-color: shade (@bg_color, 0.9);\n"
-        "  border-color: shade (@bg_color, 0.7);\n"
-        "}\n"
-        "\n"
-        "GtkLabel:selected {\n"
-        "  background-color: shade (@bg_color, 0.9);\n"
-        "}\n"
-        "\n"
-        "GtkLabel:selected:focused {\n"
-        "  background-color: @selected_bg_color;\n"
-        "}\n"
-        "\n"
-        ".spinner:active {\n"
-        "  transition: 750ms linear loop;\n"
-        "}\n"
-        "\n"
-        ".info {\n"
-        "  background-color: @info_bg_color;\n"
-        "  color: @info_fg_color;\n"
-        "}\n"
-        "\n"
-        ".warning {\n"
-        "  background-color: @warning_bg_color;\n"
-        "  color: @warning_fg_color;\n"
-        "}\n"
-        "\n"
-        ".question {\n"
-        "  background-color: @question_bg_color;\n"
-        "  color: @question_fg_color;\n"
-        "}\n"
-        "\n"
-        ".error {\n"
-        "  background-color: @error_bg_color;\n"
-        "  color: @error_fg_color;\n"
-        "}\n"
-        "\n"
-        ".highlight {\n"
-        "  background-color: @selected_bg_color;\n"
-        "  color: @selected_fg_color;\n"
-        "}\n"
-        "\n"
-        ".light-area-focus {\n"
-        "  color: #000;\n"
-        "}\n"
-        "\n"
-        ".dark-area-focus {\n"
-        "  color: #fff;\n"
-        "}\n"
-        "GtkCalendar.view {\n"
-        "  border-width: 1;\n"
-        "  border-style: inset;\n"
-        "  padding: 1;\n"
-        "}\n"
-        "\n"
-        "GtkCalendar.view:inconsistent {\n"
-        "  color: darker (@bg_color);\n"
-        "}\n"
-        "\n"
-        "GtkCalendar.header {\n"
-        "  background-color: @bg_color;\n"
-        "  border-style: outset;\n"
-        "  border-width: 2;\n"
-        "}\n"
-        "\n"
-        "GtkCalendar.highlight {\n"
-        "  border-width: 0;\n"
-        "}\n"
-        "\n"
-        "GtkCalendar.button {\n"
-        "  background-color: @bg_color;\n"
-        "}\n"
-        "\n"
-        "GtkCalendar.button:hover {\n"
-        "  background-color: lighter (@bg_color);\n"
-        "  color: @fg_color;\n"
-        "}\n"
-        "\n"
-        ".menu * {\n"
-        "  border-width: 0;\n"
-        "  padding: 2;\n"
-        "}\n"
-        "\n";
-
       provider = gtk_css_provider_new ();
-      if (!gtk_css_provider_load_from_data (provider, str, -1, NULL))
-        {
-          g_error ("Failed to load the internal default CSS.");
-        }
     }
 
   return provider;
@@ -2866,7 +2830,7 @@ _gtk_css_provider_get_theme_dir (void)
   if (var)
     path = g_build_filename (var, "share", "themes", NULL);
   else
-    path = g_build_filename (GTK_DATA_PREFIX, "share", "themes", NULL);
+    path = g_build_filename (_gtk_get_data_prefix (), "share", "themes", NULL);
 
   return path;
 }
@@ -2890,16 +2854,37 @@ gtk_css_provider_get_named (const gchar *name,
   GtkCssProvider *provider;
   gchar *key;
 
-  if (G_UNLIKELY (!themes))
-    themes = g_hash_table_new (g_str_hash, g_str_equal);
-
   if (variant == NULL)
     key = (gchar *)name;
   else
     key = g_strconcat (name, "-", variant, NULL);
 
+  if (G_UNLIKELY (!themes))
+    themes = g_hash_table_new (g_str_hash, g_str_equal);
+
   provider = g_hash_table_lookup (themes, key);
 
+  if (!provider)
+    {
+      gchar *resource_path = NULL;
+
+      if (variant)
+        resource_path = g_strdup_printf ("/org/gtk/libgtk/%s-%s.css", name, variant);
+      else
+        resource_path = g_strdup_printf ("/org/gtk/libgtk/%s.css", name);
+
+      if (g_resources_get_info (resource_path, 0, NULL, NULL, NULL))
+       {
+         provider = gtk_css_provider_new ();
+         if (!_gtk_css_provider_load_from_resource (provider, resource_path))
+           {
+             g_object_unref (provider);
+             provider = NULL;
+           }
+       }
+      g_free (resource_path);
+    }
+
   if (!provider)
     {
       const gchar *home_dir;
@@ -2943,17 +2928,36 @@ gtk_css_provider_get_named (const gchar *name,
 
       if (path)
         {
+         char *dir, *resource_file;
+         GResource *resource;
+
           provider = gtk_css_provider_new ();
 
+         dir = g_path_get_dirname (path);
+         resource_file = g_build_filename (dir, "gtk.gresource", NULL);
+         resource = g_resource_load (resource_file, NULL);
+         if (resource != NULL)
+           g_resources_register (resource);
+
           if (!gtk_css_provider_load_from_path (provider, path, NULL))
             {
+             if (resource != NULL)
+               {
+                 g_resources_unregister (resource);
+                 g_resource_unref (resource);
+               }
               g_object_unref (provider);
               provider = NULL;
             }
           else
-            g_hash_table_insert (themes, g_strdup (key), provider);
+           {
+             /* Only set this after load success, as load_from_path will clear it */
+             provider->priv->resource = resource;
+             g_hash_table_insert (themes, g_strdup (key), provider);
+           }
 
           g_free (path);
+          g_free (dir);
         }
     }
 
@@ -2966,8 +2970,8 @@ gtk_css_provider_get_named (const gchar *name,
 static int
 compare_properties (gconstpointer a, gconstpointer b)
 {
-  return strcmp (((const GtkStyleProperty *) a)->pspec->name,
-                 ((const GtkStyleProperty *) b)->pspec->name);
+  return strcmp (_gtk_style_property_get_name ((GtkStyleProperty *) a),
+                 _gtk_style_property_get_name ((GtkStyleProperty *) b));
 }
 
 static void
@@ -2975,7 +2979,6 @@ gtk_css_ruleset_print (const GtkCssRuleset *ruleset,
                        GString             *str)
 {
   GList *keys, *walk;
-  char *s;
 
   _gtk_css_selector_print (ruleset->selector, str);
 
@@ -2989,15 +2992,13 @@ gtk_css_ruleset_print (const GtkCssRuleset *ruleset,
 
       for (walk = keys; walk; walk = walk->next)
         {
-          GtkStyleProperty *prop = walk->data;
-          const GValue *value = g_hash_table_lookup (ruleset->style, prop);
+          GtkCssStyleProperty *prop = walk->data;
+          const PropertyValue *value = g_hash_table_lookup (ruleset->style, prop);
 
           g_string_append (str, "  ");
-          g_string_append (str, prop->pspec->name);
+          g_string_append (str, _gtk_style_property_get_name (GTK_STYLE_PROPERTY (prop)));
           g_string_append (str, ": ");
-          s = _gtk_css_value_to_string (value);
-          g_string_append (str, s);
-          g_free (s);
+          _gtk_css_style_property_print_value (prop, &value->value, str);
           g_string_append (str, ";\n");
         }
 
@@ -3013,12 +3014,12 @@ gtk_css_ruleset_print (const GtkCssRuleset *ruleset,
       for (walk = keys; walk; walk = walk->next)
         {
           const char *name = walk->data;
-          const GValue *value = g_hash_table_lookup (ruleset->widget_style, (gpointer) name);
+          const PropertyValue *value = g_hash_table_lookup (ruleset->widget_style, (gpointer) name);
 
           g_string_append (str, "  ");
           g_string_append (str, name);
           g_string_append (str, ": ");
-          g_string_append (str, g_value_get_string (value));
+          g_string_append (str, g_value_get_string (&value->value));
           g_string_append (str, ";\n");
         }
 
@@ -3069,6 +3070,8 @@ gtk_css_provider_print_colors (GHashTable *colors,
  * this @provider.
  *
  * Returns: a new string representing the @provider.
+ *
+ * Since: 3.2
  **/
 char *
 gtk_css_provider_to_string (GtkCssProvider *provider)