]> Pileus Git - ~andy/gtk/blobdiff - gtk/gtkcssprovider.c
Mention gvfs in the gtk_show_uri() docs
[~andy/gtk] / gtk / gtkcssprovider.c
index bd97a89aa2732c3c65217f4eaf534a01b1e23819..6f72071a6885b344179e123bf4ea9f37ce04b767 100644 (file)
@@ -29,6 +29,7 @@
 #include "gtkanimationdescription.h"
 #include "gtk9slice.h"
 #include "gtkcssprovider.h"
+#include "gtkprivate.h"
 
 /**
  * SECTION:gtkcssprovider
@@ -36,9 +37,9 @@
  * @Title: GtkCssProvider
  * @See_also: #GtkStyleContext, #GtkStyleProvider
  *
- * #GtkCssProvider is an object implementing #GtkStyleProvider, it is able
- * to parse <ulink url="http://www.w3.org/TR/CSS2">CSS</ulink>-like input
- * in order to style widgets.
+ * GtkCssProvider is an object implementing the #GtkStyleProvider interface.
+ * It is able to parse <ulink url="http://www.w3.org/TR/CSS2">CSS</ulink>-like
+ * input in order to style widgets.
  *
  * <refsect2 id="gtkcssprovider-files">
  * <title>Default files</title>
  * calling gtk_css_provider_load_from_file() and adding the provider with
  * gtk_style_context_add_provider() or gtk_style_context_add_provider_for_screen().
  * In addition, certain files will be read when GTK+ is initialized. First,
- * the file <filename><replaceable>HOME</replaceable>/.gtk-3.0.css</filename>
+ * the file <filename><envar>$XDG_CONFIG_HOME</envar>/gtk-3.0/gtk.css</filename>
  * is loaded if it exists. Then, GTK+ tries to load
- * <filename><replaceable>HOME</replaceable>/.themes/<replaceable>theme-name</replaceable>/gtk-3.0/gtk.css</filename>,
+ * <filename><envar>$HOME</envar>/.themes/<replaceable>theme-name</replaceable>/gtk-3.0/gtk.css</filename>,
  * falling back to
- * <filename><replaceable>GTK_DATA_PREFIX</replaceable>/share/themes/<replaceable>theme-name</replaceable>/gtk-3.0/gtk.css</filename>,
+ * <filename><replaceable>datadir</replaceable>/share/themes/<replaceable>theme-name</replaceable>/gtk-3.0/gtk.css</filename>,
  * where <replaceable>theme-name</replaceable> is the name of the current theme
- * (see the #GtkSettings:gtk-theme-name setting) and <replaceable>GTK_DATA_PREFIX</replaceable>
+ * (see the #GtkSettings:gtk-theme-name setting) and <replaceable>datadir</replaceable>
  * is the prefix configured when GTK+ was compiled, unless overridden by the
  * <envar>GTK_DATA_PREFIX</envar> environment variable.
  * </para>
  * </para>
  * <para>
  * Refer to the documentation of individual widgets to learn which
- * style classes they define.
+ * style classes they define and see <xref linkend="gtkstylecontext-classes"/>
+ * for a list of all style classes used by GTK+ widgets.
  * </para>
  * <example>
  * <title>Style classes in selectors</title>
  * </para>
  * <para>
  * Refer to the documentation of individual widgets to learn which
- * regions and pseudo-classes they define.
+ * regions and pseudo-classes they define and see
+ * <xref linkend="gtkstylecontext-classes"/> for a list of all regions
+ * used by GTK+ widgets.
  * </para>
  * <example>
  * <title>Regions in selectors</title>
  *     <tbody>
  *       <row>
  *         <entry>rgb(@r, @g, @b)</entry>
- *         <entry>An opaque color; @r, @g, @b can be either integers between 0 and 255 or percentages</entry>
- *         <entry>rgb(128, 10, 54)
- *                rgb(20%, 30%, 0%)</entry>
+ *         <entry>An opaque color; @r, @g, @b can be either integers between
+ *                0 and 255 or percentages</entry>
+ *         <entry><literallayout>rgb(128, 10, 54)
+ * rgb(20%, 30%, 0%)</literallayout></entry>
  *       </row>
  *       <row>
  *         <entry>rgba(@r, @g, @b, @a)</entry>
- *         <entry>A translucent color; @r, @g, @b are as in the previous row, @a is a floating point number between 0 and 1</entry>
- *         <entry>rgba(255, 255, 0, 0.5)</entry>
+ *         <entry>A translucent color; @r, @g, @b are as in the previous row,
+ *                @a is a floating point number between 0 and 1</entry>
+ *         <entry><literallayout>rgba(255, 255, 0, 0.5)</literallayout></entry>
  *       </row>
  *       <row>
  *         <entry>&num;@xxyyzz</entry>
- *         <entry>An opaque color; @xx, @yy, @zz are hexadecimal numbers specifying @r, @g, @b
- *                variants with between 1 and 4 hexadecimal digits per component are allowed</entry>
- *         <entry>&num;ff12ab
- *                &num;f0c</entry>
+ *         <entry>An opaque color; @xx, @yy, @zz are hexadecimal numbers
+ *                specifying @r, @g, @b variants with between 1 and 4
+ *                hexadecimal digits per component are allowed</entry>
+ *         <entry><literallayout>&num;ff12ab
+ * &num;f0c</literallayout></entry>
  *       </row>
  *       <row>
  *         <entry>&commat;name</entry>
- *         <entry>Reference to a color that has been defined with &commat;define-color</entry>
+ *         <entry>Reference to a color that has been defined with
+ *                &commat;define-color
+ *         </entry>
  *         <entry>&commat;bg_color</entry>
  *       </row>
  *       <row>
  *         <entry>mix(@color1, @color2, @f)</entry>
- *         <entry>A linear combination of @color1 and @color2. @f is a floating point number between 0 and 1.</entry>
- *         <entry>mix(&num;ff1e0a, &commat;bg_color, 0.8)</entry>
+ *         <entry>A linear combination of @color1 and @color2. @f is a
+ *                floating point number between 0 and 1.</entry>
+ *         <entry><literallayout>mix(&num;ff1e0a, &commat;bg_color, 0.8)</literallayout></entry>
  *       </row>
  *       <row>
  *         <entry>shade(@color, @f)</entry>
- *         <entry>A lighter or darker variant of @color. @f is a floating point number.</entry>
+ *         <entry>A lighter or darker variant of @color. @f is a
+ *                floating point number.
+ *         </entry>
  *         <entry>shade(&commat;fg_color, 0.5)</entry>
  *       </row>
  *       <row>
  *   </tgroup>
  * </informaltable>
  * </refsect2>
+ * <refsect2 id="gtkcssprovider-gradients">
+ * <title>Gradients</title>
+ * <para>
+ * Linear or radial Gradients can be used as background images.
+ * </para>
+ * <para>
+ * A linear gradient along the line from (@start_x, @start_y) to
+ * (@end_x, @end_y) is specified using the syntax
+ * <literallayout>-gtk-gradient (linear,
+ *               @start_x @start_y, @end_x @end_y,
+ *               color-stop (@position, @color),
+ *               ...)</literallayout>
+ * where @start_x and @end_x can be either a floating point number between
+ * 0 and 1 or one of the special values 'left', 'right' or 'center', @start_y
+ * and @end_y can be either a floating point number between 0 and 1 or one
+ * of the special values 'top', 'bottom' or 'center', @position is a floating
+ * point number between 0 and 1 and @color is a color expression (see above).
+ * The color-stop can be repeated multiple times to add more than one color
+ * stop. 'from (@color)' and 'to (@color)' can be used as abbreviations for
+ * color stops with position 0 and 1, respectively.
+ * </para>
+ * <example>
+ * <title>A linear gradient</title>
+ * <inlinegraphic fileref="gradient1.png" format="PNG"/>
+ * <para>This gradient was specified with
+ * <literallayout>-gtk-gradient (linear,
+ *                left top, right bottom,
+ *                from(&commat;yellow), to(&commat;blue))</literallayout></para>
+ * </example>
+ * <example>
+ * <title>Another linear gradient</title>
+ * <inlinegraphic fileref="gradient2.png" format="PNG"/>
+ * <para>This gradient was specified with
+ * <literallayout>-gtk-gradient (linear,
+ *                0 0, 0 1,
+ *                color-stop(0, &commat;yellow),
+ *                color-stop(0.2, &commat;blue),
+ *                color-stop(1, &num;0f0))</literallayout></para>
+ * </example>
+ * <para>
+ * A radial gradient along the two circles defined by (@start_x, @start_y,
+ * @start_radius) and (@end_x, @end_y, @end_radius) is specified using the
+ * syntax
+ * <literallayout>-gtk-gradient (radial,
+ *                @start_x @start_y, @start_radius,
+ *                @end_x @end_y, @end_radius,
+ *                color-stop (@position, @color),
+ *                ...)</literallayout>
+ * where @start_radius and @end_radius are floating point numbers and
+ * the other parameters are as before.
+ * </para>
+ * <example>
+ * <title>A radial gradient</title>
+ * <inlinegraphic fileref="gradient3.png" format="PNG"/>
+ * <para>This gradient was specified with
+ * <literallayout>-gtk-gradient (radial,
+ *                center center, 0,
+ *                center center, 1,
+ *                from(&commat;yellow), to(&commat;green))</literallayout></para>
+ * </example>
+ * <example>
+ * <title>Another radial gradient</title>
+ * <inlinegraphic fileref="gradient4.png" format="PNG"/>
+ * <para>This gradient was specified with
+ * <literallayout>-gtk-gradient (radial,
+ *                0.4 0.4, 0.1,
+ *                0.6 0.6, 0.7,
+ *                color-stop (0, &num;f00),
+ *                color-stop (0.1, &num;a0f),
+ *                color-stop (0.2, &commat;yellow),
+ *                color-stop (1, &commat;green))</literallayout></para>
+ * </example>
+ * </refsect2>
+ * <refsect2 id="gtkcssprovider-slices">
+ * <title>Border images</title>
+ * <para>
+ * Images can be used in 'slices' for the purpose of creating scalable
+ * borders.
+ * </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 horizontal behaviour and the second one the vertical behaviour.
+ * If only one option is specified, it affects both.
+ * </para>
+ * <example>
+ * <title>A border image</title>
+ * <inlinegraphic fileref="border1.png" format="PNG"/>
+ * <para>This border image was specified with
+ * <literallayout>url("gradient1.png") 10 10 10 10</literallayout>
+ * </para>
+ * </example>
+ * <example>
+ * <title>A repeating border image</title>
+ * <inlinegraphic fileref="border2.png" format="PNG"/>
+ * <para>This border image was specified with
+ * <literallayout>url("gradient1.png") 10 10 10 10 repeat</literallayout>
+ * </para>
+ * </example>
+ * <example>
+ * <title>A stretched border image</title>
+ * <inlinegraphic fileref="border3.png" format="PNG"/>
+ * <para>This border image was specified with
+ * <literallayout>url("gradient1.png") 10 10 10 10 stretch</literallayout>
+ * </para>
+ * </example>
+ * </refsect2>
+ * <refsect2 id="gtkcssprovider-transitions">
+ * <para>Styles can specify transitions that will be used to create a gradual
+ * change in the appearance when a widget state changes. The following
+ * syntax is used to specify transitions:
+ * <literallayout>@duration [s|ms] [linear|ease|ease-in|ease-out|ease-in-out] [loop]?</literallayout>
+ * The @duration is the amount of time that the animation will take for
+ * a complete cycle from start to end. If the loop option is given, the
+ * animation will be repated until the state changes again.
+ * The option after the duration determines the transition function from a
+ * small set of predefined functions.
+ * <figure><title>Linear transition</title>
+ * <graphic fileref="linear.png" format="PNG"/>
+ * </figure>
+ * <figure><title>Ease transition</title>
+ * <graphic fileref="ease.png" format="PNG"/>
+ * </figure>
+ * <figure><title>Ease-in-out transition</title>
+ * <graphic fileref="ease-in-out.png" format="PNG"/>
+ * </figure>
+ * <figure><title>Ease-in transition</title>
+ * <graphic fileref="ease-in.png" format="PNG"/>
+ * </figure>
+ * <figure><title>Ease-out transition</title>
+ * <graphic fileref="ease-out.png" format="PNG"/>
+ * </figure>
+ * </para>
+ * </refsect2>
  * <refsect2 id="gtkcssprovider-properties">
  * <title>Supported properties</title>
  * <para>
  *     <tbody>
  *       <row>
  *         <entry>engine</entry>
- *         <entry><programlisting>engine-name</programlisting></entry>
+ *         <entry>engine-name</entry>
  *         <entry>#GtkThemingEngine</entry>
- *         <entry><programlisting>engine: clearlooks;</programlisting></entry>
+ *         <entry>engine: clearlooks;
+ *  engine: none; /&ast; use the default (i.e. builtin) engine) &ast;/ </entry>
  *       </row>
  *       <row>
  *         <entry>background-color</entry>
- *         <entry morerows="1"><programlisting>color</programlisting></entry>
- *         <entry morerows="1">#GdkRGBA</entry>
- *         <entry morerows="1">
- *           <programlisting>
- * background-color: &num;fff;
- * color: @color-name;
- * background-color: shade (@color-name, 0.5);
- * color: mix (@color-name, &num;f0f, 0.8);</programlisting>
+ *         <entry morerows="2">color (see above)</entry>
+ *         <entry morerows="2">#GdkRGBA</entry>
+ *         <entry morerows="2"><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>
  *       </row>
  *       <row>
  *         <entry>color</entry>
  *       </row>
  *       <row>
+ *         <entry>border-color</entry>
+ *       </row>
+ *       <row>
  *         <entry>font</entry>
- *         <entry><programlisting>family [style] [size]</programlisting></entry>
+ *         <entry>@family [@style] [@size]</entry>
  *         <entry>#PangoFontDescription</entry>
- *         <entry><programlisting>font: Sans 15;</programlisting></entry>
+ *         <entry>font: Sans 15;</entry>
  *       </row>
  *       <row>
  *         <entry>margin</entry>
- *         <entry morerows="1">
- *           <programlisting>
- * width
- * vertical-width horizontal-width
- * top-width horizontal-width bottom-width
- * top-width right-width bottom-width left-width
- *           </programlisting>
+ *         <entry morerows="1"><literallayout>@width
+ * @vertical_width @horizontal_width
+ * @top_width @horizontal_width @bottom_width
+ * @top_width @right_width @bottom_width @left_width</literallayout>
  *         </entry>
  *         <entry morerows="1">#GtkBorder</entry>
- *         <entry morerows="1">
- *           <programlisting>
- * margin: 5;
+ *         <entry morerows="1"><literallayout>margin: 5;
  * margin: 5 10;
  * margin: 5 10 3;
- * margin: 5 10 3 5;</programlisting>
+ * margin: 5 10 3 5;</literallayout>
  *         </entry>
  *       </row>
  *       <row>
  *       </row>
  *       <row>
  *         <entry>background-image</entry>
- *         <entry>
- *           <programlisting>
- * -gtk-gradient (linear,
- *                starting-x-position starting-y-position,
- *                ending-x-position ending-y-position,
- *                [ [from|to] (color) |
- *                  color-stop (percentage, color) ] )
- *
- * -gtk-gradient (radial,
- *                starting-x-position starting-y-position, starting-radius,
- *                ending-x-position ending-y-position, ending-radius,
- *                [ [from|to] (color) |
- *                  color-stop (percentage, color) ]* )</programlisting>
- *         </entry>
+ *         <entry><literallayout>gradient (see above) or
+ * url(@path)</literallayout></entry>
  *         <entry>#cairo_pattern_t</entry>
- *         <entry>
- *           <programlisting>
- * -gtk-gradient (linear,
+ *         <entry><literallayout>-gtk-gradient (linear,
  *                left top, right top,
  *                from (&num;fff), to (&num;000));
  * -gtk-gradient (linear, 0.0 0.5, 0.5 1.0,
  *                center center, 0.2,
  *                center center, 0.8,
  *                color-stop (0.0, &num;fff),
- *                color-stop (1.0, &num;000));</programlisting>
+ *                color-stop (1.0, &num;000));
+ * url ('background.png');</literallayout>
  *         </entry>
  *       </row>
  *       <row>
+ *         <entry>border-width</entry>
+ *         <entry>integer</entry>
+ *         <entry>#gint</entry>
+ *         <entry>border-width: 5;</entry>
+ *       </row>
+ *       <row>
+ *         <entry>border-radius</entry>
+ *         <entry>integer</entry>
+ *         <entry>#gint</entry>
+ *         <entry>border-radius: 5;</entry>
+ *       </row>
+ *       <row>
+ *         <entry>border-style</entry>
+ *         <entry>[none|solid|inset|outset]</entry>
+ *         <entry>#GtkBorderStyle</entry>
+ *         <entry>border-style: solid;</entry>
+ *       </row>
+ *       <row>
  *         <entry>border-image</entry>
- *         <entry><programlisting>url([path]) top-distance right-distance bottom-distance left-distance horizontal-option vertical-option</programlisting></entry>
- *         <entry></entry>
- *         <entry>
- *           <programlisting>
- * border-image: url ("/path/to/image.png") 3 4 3 4 stretch;
- * border-image: url ("/path/to/image.png") 3 4 4 3 repeat stretch;</programlisting>
+ *         <entry><literallayout>border image (see above)</literallayout></entry>
+ *         <entry>internal use only</entry>
+ *         <entry><literallayout>border-image: url("/path/to/image.png") 3 4 3 4 stretch;
+ * border-image: url("/path/to/image.png") 3 4 4 3 repeat stretch;</literallayout>
  *         </entry>
  *       </row>
  *       <row>
  *         <entry>transition</entry>
- *         <entry><programlisting>duration [s|ms] [linear|ease|ease-in|ease-out|ease-in-out] [loop]?</programlisting></entry>
- *         <entry></entry>
- *         <entry>
- *           <programlisting>
- * transition: 150ms ease-in-out;
- * transition: 1s linear loop;</programlisting>
+ *         <entry>transition (see above)</entry>
+ *         <entry>internal use only</entry>
+ *         <entry><literallayout>transition: 150ms ease-in-out;
+ * transition: 1s linear loop;</literallayout>
  *         </entry>
  *       </row>
  *     </tbody>
  *   </tgroup>
  * </informaltable>
+ * <para>
+ * GtkThemingEngines can register their own, engine-specific style properties
+ * with the function gtk_theming_engine_register_property(). These properties
+ * can be set in CSS like other properties, using a name of the form
+ * <literallayout>-<replaceable>namespace</replaceable>-<replaceable>name</replaceable></literallayout>, where <replaceable>namespace</replaceable> is typically
+ * the name of the theming engine, and <replaceable>name</replaceable> is the
+ * name of the property. Style properties that have been registered by widgets
+ * using gtk_widget_class_install_style_property() can also be set in this
+ * way, using the widget class name for <replaceable>namespace</replaceable>.
+ * </para>
+ * <example>
+ * <title>Using engine-specific style properties</title>
+ * <programlisting>
+ * * {
+ *     engine: clearlooks;
+ *     border-radius: 4;
+ *     -GtkPaned-handle-size: 6;
+ *     -clearlooks-colorize-scrollbar: false;
+ * }
+ * </programlisting>
+ * </example>
  * </refsect2>
  */
 
@@ -570,6 +741,9 @@ struct GtkCssProviderPrivate
   GScanner *scanner;
   gchar *filename;
 
+  const gchar *buffer;
+  const gchar *value_pos;
+
   GHashTable *symbolic_colors;
 
   GPtrArray *selectors_info;
@@ -610,15 +784,15 @@ static void gtk_css_style_provider_iface_init (GtkStyleProviderIface *iface);
 
 static void scanner_apply_scope (GScanner    *scanner,
                                  ParserScope  scope);
-static gboolean css_provider_parse_value (GtkCssProvider *css_provider,
-                                          const gchar    *value_str,
-                                          GValue         *value);
+static gboolean css_provider_parse_value (GtkCssProvider  *css_provider,
+                                          const gchar     *value_str,
+                                          GValue          *value,
+                                          GError         **error);
 static gboolean gtk_css_provider_load_from_path_internal (GtkCssProvider  *css_provider,
                                                           const gchar     *path,
                                                           gboolean         reset,
                                                           GError         **error);
 
-
 GQuark
 gtk_css_provider_error_quark (void)
 {
@@ -901,7 +1075,7 @@ compare_selector_element (GtkWidgetPath   *path,
     {
       GType type;
 
-      type = gtk_widget_path_iter_get_widget_type (path, index);
+      type = gtk_widget_path_iter_get_object_type (path, index);
 
       if (!g_type_is_a (type, elem->type))
         return FALSE;
@@ -978,7 +1152,7 @@ compare_selector (GtkWidgetPath *path,
                   SelectorPath  *selector)
 {
   GSList *elements = selector->elements;
-  gboolean match = TRUE;
+  gboolean match = TRUE, first = TRUE, first_match = FALSE;
   guint64 score = 0;
   gint i;
 
@@ -993,6 +1167,9 @@ compare_selector (GtkWidgetPath *path,
 
       match = compare_selector_element (path, i, elem, &elem_score);
 
+      if (match && first)
+        first_match = TRUE;
+
       /* Only move on to the next index if there is no match
        * with the current element (whether to continue or not
        * handled right after in the combinator check), or a
@@ -1025,6 +1202,8 @@ compare_selector (GtkWidgetPath *path,
           score <<= 4;
           score |= elem_score;
         }
+
+      first = FALSE;
     }
 
   /* If there are pending selector
@@ -1036,6 +1215,13 @@ compare_selector (GtkWidgetPath *path,
 
   if (!match)
     score = 0;
+  else if (first_match)
+    {
+      /* Assign more weight to these selectors
+       * that matched right from the first element.
+       */
+      score <<= 4;
+    }
 
   return score;
 }
@@ -1171,6 +1357,7 @@ gtk_css_provider_get_style (GtkStyleProvider *provider,
 static gboolean
 gtk_css_provider_get_style_property (GtkStyleProvider *provider,
                                      GtkWidgetPath    *path,
+                                     GtkStateFlags     state,
                                      GParamSpec       *pspec,
                                      GValue           *value)
 {
@@ -1193,14 +1380,18 @@ gtk_css_provider_get_style_property (GtkStyleProvider *provider,
       info = &g_array_index (priority_info, StylePriorityInfo, i);
       val = g_hash_table_lookup (info->style, prop_name);
 
-      if (val)
+      if (val &&
+          (info->state == 0 ||
+           info->state == state ||
+           ((info->state & state) != 0 &&
+            (info->state & ~(state)) == 0)))
         {
           const gchar *val_str;
 
           val_str = g_value_get_string (val);
           found = TRUE;
 
-          css_provider_parse_value (GTK_CSS_PROVIDER (provider), val_str, value);
+          css_provider_parse_value (GTK_CSS_PROVIDER (provider), val_str, value, NULL);
           break;
         }
     }
@@ -1273,22 +1464,22 @@ scanner_apply_scope (GScanner    *scanner,
 
   if (scope == SCOPE_VALUE)
     {
-      scanner->config->cset_identifier_first = G_CSET_a_2_z "@#-_0123456789" G_CSET_A_2_Z;
-      scanner->config->cset_identifier_nth = G_CSET_a_2_z "@#-_ 0123456789(),.%\t\n'\"" G_CSET_A_2_Z;
+      scanner->config->cset_identifier_first = G_CSET_a_2_z G_CSET_A_2_Z G_CSET_DIGITS "@#-_";
+      scanner->config->cset_identifier_nth = G_CSET_a_2_z G_CSET_A_2_Z G_CSET_DIGITS "@#-_ +(),.%\t\n'/\"";
       scanner->config->scan_identifier_1char = TRUE;
     }
   else if (scope == SCOPE_SELECTOR)
     {
       scanner->config->cset_identifier_first = G_CSET_a_2_z G_CSET_A_2_Z "*@";
-      scanner->config->cset_identifier_nth = G_CSET_a_2_z "-_#." G_CSET_A_2_Z;
+      scanner->config->cset_identifier_nth = G_CSET_a_2_z G_CSET_A_2_Z G_CSET_DIGITS "-_#.";
       scanner->config->scan_identifier_1char = TRUE;
     }
   else if (scope == SCOPE_PSEUDO_CLASS ||
            scope == SCOPE_NTH_CHILD ||
            scope == SCOPE_DECLARATION)
     {
-      scanner->config->cset_identifier_first = G_CSET_a_2_z "-" G_CSET_A_2_Z;
-      scanner->config->cset_identifier_nth = G_CSET_a_2_z "-" G_CSET_A_2_Z;
+      scanner->config->cset_identifier_first = G_CSET_a_2_z G_CSET_A_2_Z "-_";
+      scanner->config->cset_identifier_nth = G_CSET_a_2_z G_CSET_A_2_Z G_CSET_DIGITS "-_";
       scanner->config->scan_identifier_1char = FALSE;
     }
   else
@@ -1347,6 +1538,8 @@ css_provider_reset_parser (GtkCssProvider *css_provider)
   priv->state = NULL;
 
   scanner_apply_scope (priv->scanner, SCOPE_SELECTOR);
+  priv->scanner->user_data = NULL;
+  priv->value_pos = NULL;
 
   g_slist_foreach (priv->cur_selectors, (GFunc) selector_path_unref, NULL);
   g_slist_free (priv->cur_selectors);
@@ -1696,7 +1889,7 @@ parse_selector (GtkCssProvider  *css_provider,
 
 static GtkSymbolicColor *
 symbolic_color_parse_str (const gchar  *string,
-                         gchar       **end_ptr)
+                          gchar       **end_ptr)
 {
   GtkSymbolicColor *symbolic_color = NULL;
   gchar *str;
@@ -1889,7 +2082,7 @@ symbolic_color_parse_str (const gchar  *string,
       *end_ptr = (gchar *) str;
 
       if (str[0] != ')')
-       {
+        {
           gtk_symbolic_color_unref (color1);
           gtk_symbolic_color_unref (color2);
           return NULL;
@@ -1925,8 +2118,9 @@ symbolic_color_parse_str (const gchar  *string,
         }
       else
         {
-          /* color name? parse until first whitespace */
-          while (*end != ' ' && *end != '\0')
+          /* Color name */
+          while (*end != '\0' &&
+                 (g_ascii_isalnum (*end) || *end == ' '))
             end++;
         }
 
@@ -1947,7 +2141,8 @@ symbolic_color_parse_str (const gchar  *string,
 }
 
 static GtkSymbolicColor *
-symbolic_color_parse (const gchar *str)
+symbolic_color_parse (const gchar  *str,
+                      GError      **error)
 {
   GtkSymbolicColor *color;
   gchar *end;
@@ -1956,8 +2151,10 @@ symbolic_color_parse (const gchar *str)
 
   if (*end != '\0')
     {
-      g_message ("Error parsing symbolic color \"%s\", stopped at char %ld : '%c'",
-                 str, end - str, *end);
+      g_set_error_literal (error,
+                           GTK_CSS_PROVIDER_ERROR,
+                           GTK_CSS_PROVIDER_ERROR_FAILED,
+                           "Could not parse symbolic color");
 
       if (color)
         {
@@ -2043,6 +2240,13 @@ gradient_parse_str (const gchar  *str,
           else
             {
               coords[i * 3] = g_ascii_strtod (str, &end);
+
+              if (str == end)
+                {
+                  *end_ptr = (gchar *) str;
+                  return NULL;
+                }
+
               str = end;
             }
 
@@ -2066,6 +2270,13 @@ gradient_parse_str (const gchar  *str,
           else
             {
               coords[(i * 3) + 1] = g_ascii_strtod (str, &end);
+
+              if (str == end)
+                {
+                  *end_ptr = (gchar *) str;
+                  return NULL;
+                }
+
               str = end;
             }
 
@@ -2203,35 +2414,15 @@ gradient_parse_str (const gchar  *str,
   return gradient;
 }
 
-static GtkGradient *
-gradient_parse (const gchar *str)
-{
-  GtkGradient *gradient;
-  gchar *end;
-
-  gradient = gradient_parse_str (str, &end);
-
-  if (*end != '\0')
-    {
-      g_message ("Error parsing pattern \"%s\", stopped at char %ld : '%c'",
-                 str, end - str, *end);
-
-      if (gradient)
-        {
-          gtk_gradient_unref (gradient);
-          gradient = NULL;
-        }
-    }
-
-  return gradient;
-}
-
 static gchar *
 path_parse_str (GtkCssProvider  *css_provider,
-               const gchar     *str,
-               gchar          **end_ptr)
+                const gchar     *str,
+                gchar          **end_ptr,
+                GError         **error)
 {
   gchar *path, *chr;
+  const gchar *start, *end;
+  start = str;
 
   if (g_str_has_prefix (str, "url"))
     {
@@ -2244,20 +2435,21 @@ path_parse_str (GtkCssProvider  *css_provider,
           return NULL;
         }
 
-      chr = strrchr (str, ')');
-
+      chr = strchr (str, ')');
       if (!chr)
         {
           *end_ptr = (gchar *) str;
           return NULL;
         }
 
+      end = chr + 1;
+
       str++;
       SKIP_SPACES (str);
 
       if (*str == '"' || *str == '\'')
         {
-          gchar *p;
+          const gchar *p;
           p = str;
           str++;
 
@@ -2266,25 +2458,25 @@ path_parse_str (GtkCssProvider  *css_provider,
 
           if (*chr != *p || chr == p)
             {
-              *end_ptr = str;
+              *end_ptr = (gchar *)str;
               return NULL;
             }
         }
       else
         {
-          *end_ptr = str;
+          *end_ptr = (gchar *)str;
           return NULL;
         }
 
       path = g_strndup (str, chr - str);
       g_strstrip (path);
 
-      *end_ptr = str + strlen (str);
+      *end_ptr = (gchar *)end;
     }
   else
     {
       path = g_strdup (str);
-      *end_ptr = str + strlen (str);
+      *end_ptr = (gchar *)str + strlen (str);
     }
 
   /* Always return an absolute path */
@@ -2310,32 +2502,37 @@ path_parse_str (GtkCssProvider  *css_provider,
 
   if (!g_file_test (path, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_REGULAR))
     {
-      g_warning ("File doesn't exist: %s\n", path);
+      g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_EXIST,
+                   "File doesn't exist: %s", path);
       g_free (path);
       path = NULL;
+      *end_ptr = (gchar *)start;
     }
 
   return path;
 }
 
 static gchar *
-path_parse (GtkCssProvider *css_provider,
-            const gchar    *str)
+path_parse (GtkCssProvider  *css_provider,
+            const gchar     *str,
+            GError         **error)
 {
-  gchar *path, *end;
+  gchar *path;
+  gchar *end;
 
-  path = path_parse_str (css_provider, str, &end);
+  path = path_parse_str (css_provider, str, &end, error);
+
+  if (!path)
+    return NULL;
 
   if (*end != '\0')
     {
-      g_message ("Error parsing file path \"%s\", stopped at char %ld : '%c'",
-                 str, end - str, *end);
-
-      if (path)
-        {
-          g_free (path);
-          path = NULL;
-        }
+      g_set_error_literal (error,
+                           GTK_CSS_PROVIDER_ERROR,
+                           GTK_CSS_PROVIDER_ERROR_FAILED,
+                           "Error parsing path");
+      g_free (path);
+      path = NULL;
     }
 
   return path;
@@ -2344,12 +2541,12 @@ path_parse (GtkCssProvider *css_provider,
 static Gtk9Slice *
 slice_parse_str (GtkCssProvider  *css_provider,
                  const gchar     *str,
-                 gchar          **end_ptr)
+                 gchar          **end_ptr,
+                 GError         **error)
 {
   gdouble distance_top, distance_bottom;
   gdouble distance_left, distance_right;
   GtkSliceSideModifier mods[2];
-  GError *error = NULL;
   GdkPixbuf *pixbuf;
   Gtk9Slice *slice;
   gchar *path;
@@ -2358,7 +2555,7 @@ slice_parse_str (GtkCssProvider  *css_provider,
   SKIP_SPACES (str);
 
   /* Parse image url */
-  path = path_parse_str (css_provider, str, end_ptr);
+  path = path_parse_str (css_provider, str, end_ptr, error);
 
   if (!path)
       return NULL;
@@ -2367,22 +2564,22 @@ slice_parse_str (GtkCssProvider  *css_provider,
   SKIP_SPACES (str);
 
   /* Parse top/left/bottom/right distances */
-  distance_top = g_strtod (str, end_ptr);
+  distance_top = g_ascii_strtod (str, end_ptr);
 
   str = *end_ptr;
   SKIP_SPACES (str);
 
-  distance_right = g_strtod (str, end_ptr);
+  distance_right = g_ascii_strtod (str, end_ptr);
 
   str = *end_ptr;
   SKIP_SPACES (str);
 
-  distance_bottom = g_strtod (str, end_ptr);
+  distance_bottom = g_ascii_strtod (str, end_ptr);
 
   str = *end_ptr;
   SKIP_SPACES (str);
 
-  distance_left = g_strtod (str, end_ptr);
+  distance_left = g_ascii_strtod (str, end_ptr);
 
   str = *end_ptr;
   SKIP_SPACES (str);
@@ -2424,50 +2621,24 @@ slice_parse_str (GtkCssProvider  *css_provider,
       mods[1] = mods[0];
     }
 
-  pixbuf = gdk_pixbuf_new_from_file (path, &error);
+  pixbuf = gdk_pixbuf_new_from_file (path, error);
   g_free (path);
 
-  if (error)
+  if (!pixbuf)
     {
-      g_warning ("Pixbuf could not be loaded: %s\n", error->message);
-      g_error_free (error);
       *end_ptr = (gchar *) str;
       return NULL;
     }
 
-  slice = gtk_9slice_new (pixbuf,
-                          distance_top, distance_bottom,
-                          distance_left, distance_right,
-                          mods[0], mods[1]);
+  slice = _gtk_9slice_new (pixbuf,
+                           distance_top, distance_bottom,
+                           distance_left, distance_right,
+                           mods[0], mods[1]);
   g_object_unref (pixbuf);
 
   return slice;
 }
 
-static Gtk9Slice *
-slice_parse (GtkCssProvider *css_provider,
-             const gchar    *str)
-{
-  Gtk9Slice *slice;
-  gchar *end;
-
-  slice = slice_parse_str (css_provider, str, &end);
-
-  if (*end != '\0')
-    {
-      g_message ("Error parsing sliced image \"%s\", stopped at char %ld : '%c'",
-                 str, end - str, *end);
-
-      if (slice)
-        {
-          gtk_9slice_unref (slice);
-          slice = NULL;
-        }
-    }
-
-  return slice;
-}
-
 static gdouble
 unit_parse_str (const gchar     *str,
                 gchar          **end_str)
@@ -2475,7 +2646,7 @@ unit_parse_str (const gchar     *str,
   gdouble unit;
 
   SKIP_SPACES (str);
-  unit = g_strtod (str, end_str);
+  unit = g_ascii_strtod (str, end_str);
   str = *end_str;
 
   /* Now parse the unit type, if any. We
@@ -2560,62 +2731,46 @@ border_parse_str (const gchar  *str,
   return border;
 }
 
-static GtkBorder *
-border_parse (const gchar *str)
-{
-  GtkBorder *border;
-  gchar *end;
-
-  border = border_parse_str (str, &end);
-
-  if (*end != '\0')
-    {
-      g_message ("Error parsing border \"%s\", stopped at char %ld : '%c'",
-                 str, end - str, *end);
-
-      if (border)
-        gtk_border_free (border);
-
-      return NULL;
-    }
-
-  return border;
-}
-
 static gboolean
-css_provider_parse_value (GtkCssProvider *css_provider,
-                          const gchar    *value_str,
-                          GValue         *value)
+css_provider_parse_value (GtkCssProvider  *css_provider,
+                          const gchar     *value_str,
+                          GValue          *value,
+                          GError         **error)
 {
+  GtkCssProviderPrivate *priv;
   GType type;
   gboolean parsed = TRUE;
+  gchar *end = NULL;
 
+  priv = css_provider->priv;
   type = G_VALUE_TYPE (value);
 
   if (type == GDK_TYPE_RGBA ||
       type == GDK_TYPE_COLOR)
     {
-      GdkRGBA color;
-      GdkColor rgb;
+      GdkRGBA rgba;
+      GdkColor color;
 
       if (type == GDK_TYPE_RGBA &&
-          gdk_rgba_parse (&color, value_str))
-        g_value_set_boxed (value, &color);
+          gdk_rgba_parse (&rgba, value_str))
+        g_value_set_boxed (value, &rgba);
       else if (type == GDK_TYPE_COLOR &&
-               gdk_color_parse (value_str, &rgb))
-        g_value_set_boxed (value, &rgb);
+               gdk_color_parse (value_str, &color))
+        g_value_set_boxed (value, &color);
       else
         {
           GtkSymbolicColor *symbolic_color;
 
-          symbolic_color = symbolic_color_parse (value_str);
+          symbolic_color = symbolic_color_parse_str (value_str, &end);
 
-          if (!symbolic_color)
-            return FALSE;
-
-          g_value_unset (value);
-          g_value_init (value, GTK_TYPE_SYMBOLIC_COLOR);
-          g_value_take_boxed (value, symbolic_color);
+          if (symbolic_color)
+            {
+              g_value_unset (value);
+              g_value_init (value, GTK_TYPE_SYMBOLIC_COLOR);
+              g_value_take_boxed (value, symbolic_color);
+            }
+          else
+            parsed = FALSE;
         }
     }
   else if (type == PANGO_TYPE_FONT_DESCRIPTION)
@@ -2646,13 +2801,16 @@ css_provider_parse_value (GtkCssProvider *css_provider,
       GtkThemingEngine *engine;
 
       engine = gtk_theming_engine_load (value_str);
-      g_value_set_object (value, engine);
+      if (engine)
+        g_value_set_object (value, engine);
+      else
+        parsed = FALSE;
     }
   else if (type == GTK_TYPE_ANIMATION_DESCRIPTION)
     {
       GtkAnimationDescription *desc;
 
-      desc = gtk_animation_description_from_string (value_str);
+      desc = _gtk_animation_description_from_string (value_str);
 
       if (desc)
         g_value_take_boxed (value, desc);
@@ -2663,14 +2821,14 @@ css_provider_parse_value (GtkCssProvider *css_provider,
     {
       GtkBorder *border;
 
-      border = border_parse (value_str);
+      border = border_parse_str (value_str, &end);
       g_value_take_boxed (value, border);
     }
   else if (type == CAIRO_GOBJECT_TYPE_PATTERN)
     {
       GtkGradient *gradient;
 
-      gradient = gradient_parse (value_str);
+      gradient = gradient_parse_str (value_str, &end);
 
       if (gradient)
         {
@@ -2679,7 +2837,50 @@ css_provider_parse_value (GtkCssProvider *css_provider,
           g_value_take_boxed (value, gradient);
         }
       else
-        parsed = FALSE;
+        {
+          gchar *path;
+          GdkPixbuf *pixbuf;
+
+          g_clear_error (error);
+          path = path_parse_str (css_provider, value_str, &end, error);
+
+          if (path)
+            {
+              pixbuf = gdk_pixbuf_new_from_file (path, NULL);
+              g_free (path);
+
+              if (pixbuf)
+                {
+                  cairo_surface_t *surface;
+                  cairo_pattern_t *pattern;
+                  cairo_t *cr;
+                  cairo_matrix_t matrix;
+
+                  surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
+                                                        gdk_pixbuf_get_width (pixbuf),
+                                                        gdk_pixbuf_get_height (pixbuf));
+                  cr = cairo_create (surface);
+                  gdk_cairo_set_source_pixbuf (cr, pixbuf, 0, 0);
+                  cairo_paint (cr);
+                  pattern = cairo_pattern_create_for_surface (surface);
+
+                  cairo_matrix_init_scale (&matrix,
+                                           gdk_pixbuf_get_width (pixbuf),
+                                           gdk_pixbuf_get_height (pixbuf));
+                  cairo_pattern_set_matrix (pattern, &matrix);
+
+                  cairo_surface_destroy (surface);
+                  cairo_destroy (cr);
+                  g_object_unref (pixbuf);
+
+                  g_value_take_boxed (value, pattern);
+                }
+              else
+                parsed = FALSE;
+            }
+          else
+            parsed = FALSE;
+        }
     }
   else if (G_TYPE_IS_ENUM (type))
     {
@@ -2691,8 +2892,11 @@ css_provider_parse_value (GtkCssProvider *css_provider,
 
       if (!enum_value)
         {
-          g_warning ("Unknown value '%s' for enum type '%s'",
-                     value_str, g_type_name (type));
+          g_set_error (error,
+                       GTK_CSS_PROVIDER_ERROR,
+                       GTK_CSS_PROVIDER_ERROR_FAILED,
+                       "Unknown value '%s' for enum type '%s'",
+                       value_str, g_type_name (type));
           parsed = FALSE;
         }
       else
@@ -2725,8 +2929,11 @@ css_provider_parse_value (GtkCssProvider *css_provider,
 
           if (!flag_value)
             {
-              g_warning ("Unknown flag '%s' for type '%s'",
-                         value_str, g_type_name (type));
+              g_set_error (error,
+                           GTK_CSS_PROVIDER_ERROR,
+                           GTK_CSS_PROVIDER_ERROR_FAILED,
+                           "Unknown flag '%s' for type '%s'",
+                           value_str, g_type_name (type));
               parsed = FALSE;
             }
           else
@@ -2741,8 +2948,11 @@ css_provider_parse_value (GtkCssProvider *css_provider,
 
       if (!flag_value)
         {
-          g_warning ("Unknown flag '%s' for type '%s'",
-                     value_str, g_type_name (type));
+          g_set_error (error,
+                       GTK_CSS_PROVIDER_ERROR,
+                       GTK_CSS_PROVIDER_ERROR_FAILED,
+                       "Unknown flag '%s' for type '%s'",
+                       value_str, g_type_name (type));
           parsed = FALSE;
         }
       else
@@ -2757,7 +2967,7 @@ css_provider_parse_value (GtkCssProvider *css_provider,
     {
       Gtk9Slice *slice;
 
-      slice = slice_parse (css_provider, value_str);
+      slice = slice_parse_str (css_provider, value_str, &end, error);
 
       if (slice)
         g_value_take_boxed (value, slice);
@@ -2766,16 +2976,111 @@ css_provider_parse_value (GtkCssProvider *css_provider,
     }
   else
     {
-      g_warning ("Cannot parse string '%s' for type %s", value_str, g_type_name (type));
+      g_set_error (error,
+                   GTK_CSS_PROVIDER_ERROR,
+                   GTK_CSS_PROVIDER_ERROR_FAILED,
+                   "Cannot parse string '%s' for type %s",
+                   value_str, g_type_name (type));
       parsed = FALSE;
     }
 
+  if (end && *end)
+    {
+      /* Set error position in the scanner
+       * according to what we've parsed so far
+       */
+      priv->value_pos += (end - value_str);
+
+      if (error && !*error)
+        g_set_error_literal (error,
+                             GTK_CSS_PROVIDER_ERROR,
+                             GTK_CSS_PROVIDER_ERROR_FAILED,
+                             "Failed to parse value");
+    }
+
   return parsed;
 }
 
+static void
+scanner_report_warning (GtkCssProvider *css_provider,
+                        GTokenType      expected_token,
+                        GError         *error)
+{
+  GtkCssProviderPrivate *priv;
+  const gchar *line_end, *line_start;
+  const gchar *expected_str;
+  gchar buf[2], *line, *str;
+  guint pos;
+
+  priv = css_provider->priv;
+
+  if (error)
+    str = g_strdup (error->message);
+  else
+    {
+      if (priv->scanner->user_data)
+        expected_str = priv->scanner->user_data;
+      else
+        {
+          switch (expected_token)
+            {
+            case G_TOKEN_SYMBOL:
+              expected_str = "Symbol";
+            case G_TOKEN_IDENTIFIER:
+              expected_str = "Identifier";
+            default:
+              buf[0] = expected_token;
+              buf[1] = '\0';
+              expected_str = buf;
+            }
+        }
+
+      str = g_strdup_printf ("Parse error, expecting a %s '%s'",
+                             (expected_str != buf) ? "valid" : "",
+                             expected_str);
+    }
+
+  if (priv->value_pos)
+    line_start = priv->value_pos - 1;
+  else
+    line_start = priv->scanner->text - 1;
+
+  while (*line_start != '\n' &&
+         line_start != priv->buffer)
+    line_start--;
+
+  if (*line_start == '\n')
+    line_start++;
+
+  if (priv->value_pos)
+    pos = priv->value_pos - line_start + 1;
+  else
+    pos = priv->scanner->text - line_start - 1;
+
+  line_end = strchr (line_start, '\n');
+
+  if (line_end)
+    line = g_strndup (line_start, (line_end - line_start));
+  else
+    line = g_strdup (line_start);
+
+  g_message ("CSS: %s\n"
+             "%s, line %d, char %d:\n"
+             "%*c %s\n"
+             "%*c ^",
+             str, priv->scanner->input_name,
+             priv->scanner->line, priv->scanner->position,
+             3, ' ', line,
+             3 + pos, ' ');
+
+  g_free (line);
+  g_free (str);
+}
+
 static GTokenType
-parse_rule (GtkCssProvider *css_provider,
-            GScanner       *scanner)
+parse_rule (GtkCssProvider  *css_provider,
+            GScanner        *scanner,
+            GError         **error)
 {
   GtkCssProviderPrivate *priv;
   GTokenType expected_token;
@@ -2802,20 +3107,29 @@ parse_rule (GtkCssProvider *css_provider,
           g_scanner_get_next_token (scanner);
 
           if (scanner->token != G_TOKEN_IDENTIFIER)
-            return G_TOKEN_IDENTIFIER;
+            {
+              scanner->user_data = "Color name";
+              return G_TOKEN_IDENTIFIER;
+            }
 
           color_name = g_strdup (scanner->value.v_identifier);
           css_provider_push_scope (css_provider, SCOPE_VALUE);
           g_scanner_get_next_token (scanner);
 
           if (scanner->token != G_TOKEN_IDENTIFIER)
-            return G_TOKEN_IDENTIFIER;
+            {
+              scanner->user_data = "Color definition";
+              return G_TOKEN_IDENTIFIER;
+            }
 
           color_str = g_strstrip (scanner->value.v_identifier);
-          color = symbolic_color_parse (color_str);
+          color = symbolic_color_parse (color_str, error);
 
           if (!color)
-            return G_TOKEN_IDENTIFIER;
+            {
+              scanner->user_data = "Color definition";
+              return G_TOKEN_IDENTIFIER;
+            }
 
           g_hash_table_insert (priv->symbolic_colors, color_name, color);
 
@@ -2831,9 +3145,8 @@ parse_rule (GtkCssProvider *css_provider,
         {
           GScanner *scanner_backup;
           GSList *state_backup;
-          GError *error = NULL;
           gboolean loaded;
-          gchar *path;
+          gchar *path = NULL;
 
           css_provider_push_scope (css_provider, SCOPE_VALUE);
           g_scanner_get_next_token (scanner);
@@ -2841,15 +3154,18 @@ parse_rule (GtkCssProvider *css_provider,
           if (scanner->token == G_TOKEN_IDENTIFIER &&
               g_str_has_prefix (scanner->value.v_identifier, "url"))
             path = path_parse (css_provider,
-                               g_strstrip (scanner->value.v_identifier));
+                               g_strstrip (scanner->value.v_identifier),
+                               error);
           else if (scanner->token == G_TOKEN_STRING)
             path = path_parse (css_provider,
-                               g_strstrip (scanner->value.v_string));
-          else
-            return G_TOKEN_IDENTIFIER;
+                               g_strstrip (scanner->value.v_string),
+                               error);
 
           if (path == NULL)
-            return G_TOKEN_IDENTIFIER;
+            {
+              scanner->user_data = "File URL";
+              return G_TOKEN_IDENTIFIER;
+            }
 
           css_provider_pop_scope (css_provider);
           g_scanner_get_next_token (scanner);
@@ -2869,7 +3185,7 @@ parse_rule (GtkCssProvider *css_provider,
 
           /* FIXME: Avoid recursive importing */
           loaded = gtk_css_provider_load_from_path_internal (css_provider, path,
-                                                             FALSE, &error);
+                                                             FALSE, error);
 
           /* Restore previous state */
           css_provider_reset_parser (css_provider);
@@ -2881,16 +3197,17 @@ parse_rule (GtkCssProvider *css_provider,
 
           if (!loaded)
             {
-              g_warning ("Error loading imported file \"%s\": %s",
-                         path, (error) ? error->message : "");
-              g_error_free (error);
+              scanner->user_data = "File URL";
               return G_TOKEN_IDENTIFIER;
             }
           else
             return G_TOKEN_NONE;
         }
       else
-        return G_TOKEN_IDENTIFIER;
+        {
+          scanner->user_data = "Directive";
+          return G_TOKEN_IDENTIFIER;
+        }
     }
 
   expected_token = parse_selector (css_provider, scanner, &selector);
@@ -2898,6 +3215,7 @@ parse_rule (GtkCssProvider *css_provider,
   if (expected_token != G_TOKEN_NONE)
     {
       selector_path_unref (selector);
+      scanner->user_data = "Selector";
       return expected_token;
     }
 
@@ -2912,6 +3230,7 @@ parse_rule (GtkCssProvider *css_provider,
       if (expected_token != G_TOKEN_NONE)
         {
           selector_path_unref (selector);
+          scanner->user_data = "Selector";
           return expected_token;
         }
 
@@ -2929,10 +3248,9 @@ parse_rule (GtkCssProvider *css_provider,
 
   while (scanner->token == G_TOKEN_IDENTIFIER)
     {
-      const gchar *value_str = NULL;
+      gchar *value_str = NULL;
       GtkStylePropertyParser parse_func = NULL;
       GParamSpec *pspec;
-      GError *error = NULL;
       gchar *prop;
 
       prop = g_strdup (scanner->value.v_identifier);
@@ -2944,16 +3262,21 @@ parse_rule (GtkCssProvider *css_provider,
           return ':';
         }
 
+      priv->value_pos = priv->scanner->text;
+
       css_provider_push_scope (css_provider, SCOPE_VALUE);
       g_scanner_get_next_token (scanner);
 
       if (scanner->token != G_TOKEN_IDENTIFIER)
         {
           g_free (prop);
+          scanner->user_data = "Property value";
           return G_TOKEN_IDENTIFIER;
         }
 
-      value_str = g_strstrip (scanner->value.v_identifier);
+      value_str = scanner->value.v_identifier;
+      SKIP_SPACES (value_str);
+      g_strchomp (value_str);
 
       if (gtk_style_properties_lookup_property (prop, &parse_func, &pspec))
         {
@@ -2975,21 +3298,16 @@ parse_rule (GtkCssProvider *css_provider,
               g_value_set_string (val, value_str);
               g_hash_table_insert (priv->cur_properties, prop, val);
             }
-          else if ((parse_func && (parse_func) (value_str, val, &error)) ||
-                   (!parse_func && css_provider_parse_value (css_provider, value_str, val)))
+          else if ((parse_func && (parse_func) (value_str, val, error)) ||
+                   (!parse_func && css_provider_parse_value (css_provider, value_str, val, error)))
             g_hash_table_insert (priv->cur_properties, prop, val);
           else
             {
-              if (error)
-                {
-                  g_message ("Error parsing property value: %s\n", error->message);
-                  g_error_free (error);
-                }
-
               g_value_unset (val);
               g_slice_free (GValue, val);
               g_free (prop);
 
+              scanner->user_data = "Property value";
               return G_TOKEN_IDENTIFIER;
             }
         }
@@ -3024,24 +3342,14 @@ parse_rule (GtkCssProvider *css_provider,
   return G_TOKEN_NONE;
 }
 
-static void
-scanner_msg (GScanner *scanner,
-             gchar    *message,
-             gboolean  is_error)
-{
-  GError **error = scanner->user_data;
-
-  g_set_error_literal (error,
-                       GTK_CSS_PROVIDER_ERROR,
-                       GTK_CSS_PROVIDER_ERROR_FAILED,
-                       message);
-}
-
 static gboolean
 parse_stylesheet (GtkCssProvider  *css_provider,
                   GError         **error)
 {
   GtkCssProviderPrivate *priv;
+  gboolean result;
+
+  result = TRUE;
 
   priv = css_provider->priv;
   g_scanner_get_next_token (priv->scanner);
@@ -3049,28 +3357,32 @@ parse_stylesheet (GtkCssProvider  *css_provider,
   while (!g_scanner_eof (priv->scanner))
     {
       GTokenType expected_token;
+      GError *err = NULL;
 
       css_provider_reset_parser (css_provider);
-      expected_token = parse_rule (css_provider, priv->scanner);
+      expected_token = parse_rule (css_provider, priv->scanner, &err);
 
       if (expected_token != G_TOKEN_NONE)
         {
+          /* If a GError was passed in, propagate the error and bail out,
+           * else report a warning and keep going
+           */
           if (error != NULL)
             {
-              priv->scanner->msg_handler = scanner_msg;
-              priv->scanner->user_data = error;
+              result = FALSE;
+              if (err)
+                g_propagate_error (error, err);
+              else
+                g_set_error_literal (error,
+                                     GTK_CSS_PROVIDER_ERROR,
+                                     GTK_CSS_PROVIDER_ERROR_FAILED,
+                                     "Error parsing stylesheet");
+              break;
             }
-
-          g_scanner_unexp_token (priv->scanner, expected_token,
-                                 NULL, NULL, NULL,
-                                 "Error parsing style resource", FALSE);
-
-          if (error != NULL)
+          else
             {
-              priv->scanner->msg_handler = NULL;
-              priv->scanner->user_data = NULL;
-
-              return FALSE;
+              scanner_report_warning (css_provider, expected_token, err);
+              g_clear_error (&err);
             }
 
           while (!g_scanner_eof (priv->scanner) &&
@@ -3083,7 +3395,7 @@ parse_stylesheet (GtkCssProvider  *css_provider,
       g_scanner_get_next_token (priv->scanner);
     }
 
-  return TRUE;
+  return result;
 }
 
 /**
@@ -3118,10 +3430,12 @@ gtk_css_provider_load_from_data (GtkCssProvider  *css_provider,
     g_ptr_array_remove_range (priv->selectors_info, 0, priv->selectors_info->len);
 
   priv->scanner->input_name = "-";
+  priv->buffer = data;
   g_scanner_input_text (priv->scanner, data, (guint) length);
 
   g_free (priv->filename);
   priv->filename = NULL;
+  priv->buffer = NULL;
 
   return parse_stylesheet (css_provider, error);
 }
@@ -3168,10 +3482,12 @@ gtk_css_provider_load_from_file (GtkCssProvider  *css_provider,
   priv->filename = g_file_get_path (file);
 
   priv->scanner->input_name = priv->filename;
+  priv->buffer = data;
   g_scanner_input_text (priv->scanner, data, (guint) length);
 
   ret = parse_stylesheet (css_provider, error);
 
+  priv->buffer = NULL;
   g_free (data);
 
   return ret;
@@ -3216,10 +3532,12 @@ gtk_css_provider_load_from_path_internal (GtkCssProvider  *css_provider,
     }
 
   priv->scanner->input_name = priv->filename;
+  priv->buffer = data;
   g_scanner_input_text (priv->scanner, data, (guint) length);
 
   ret = parse_stylesheet (css_provider, error);
 
+  priv->buffer = NULL;
   g_mapped_file_unref (mapped_file);
 
   return ret;
@@ -3274,12 +3592,22 @@ gtk_css_provider_get_default (void)
         "@define-color tooltip_bg_color #eee1b3; \n"
         "@define-color tooltip_fg_color #000; \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"
         "GtkTreeView > GtkButton {\n"
         "  background-color: @bg_color;\n"
         "  color: @fg_color;\n"
         "  border-color: shade (@bg_color, 0.6);\n"
-        "  padding: 2 2; \n"
+        "  padding: 2;\n"
+        "  border-width: 0;\n"
         "}\n"
         "\n"
         "*:prelight {\n"
@@ -3292,7 +3620,20 @@ gtk_css_provider_get_default (void)
         "  color: @selected_fg_color;\n"
         "}\n"
         "\n"
+        ".expander {\n"
+        "  color: #fff;\n"
+        "}\n"
+        "\n"
+        ".expander:prelight {\n"
+        "  color: @text_color;\n"
+        "}\n"
+        "\n"
+        ".expander:active {\n"
+        "  transition: 300ms 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"
@@ -3314,6 +3655,9 @@ gtk_css_provider_get_default (void)
         ".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"
@@ -3350,8 +3694,12 @@ gtk_css_provider_get_default (void)
         "  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:prelight,\n"
+        ".progressbar,\n"
         ".entry.progressbar {\n"
         "  background-color: @selected_bg_color;\n"
         "  border-color: shade (@selected_bg_color, 0.7);\n"
@@ -3370,7 +3718,7 @@ gtk_css_provider_get_default (void)
         ".check:hover, .radio:hover {\n"
         "  background-color: @base_color;\n"
         "  border-color: @fg_color;\n"
-       "  color: @text_color;\n"
+        "  color: @text_color;\n"
         "  border-style: solid;\n"
         "  border-width: 1;\n"
         "}\n"
@@ -3408,10 +3756,89 @@ gtk_css_provider_get_default (void)
         "  border-style: inset;\n"
         "  border-width: 1;\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"
+        "  background-color: @selected_bg_color;\n"
+        "  color: @selected_fg_color;\n"
+        "}\n"
+        "\n"
+        ".menu .check,\n"
+        ".menu .radio,\n"
+        ".menu .check:active,\n"
+        ".menu .radio:active {\n"
+        "  border-style: none;\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"
+        "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"
+        "  color: @fg_color;\n"
+        "}\n"
+        "\n"
+        "GtkLabel:selected:focused {\n"
+        "  background-color: @selected_bg_color;\n"
+        "  color: @selected_fg_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";
 
       provider = gtk_css_provider_new ();
-      gtk_css_provider_load_from_data (provider, str, -1, NULL);
+      if (!gtk_css_provider_load_from_data (provider, str, -1, NULL))
+        {
+          g_error ("Failed to load the internal default CSS.");
+        }
     }
 
   return provider;
@@ -3492,6 +3919,8 @@ gtk_css_provider_get_named (const gchar *name,
             }
         }
 
+      g_free (subpath);
+
       if (path)
         {
           GError *error = NULL;