]> Pileus Git - ~andy/gtk/blobdiff - gtk/gtkstylecontext.c
stylecontext: Update computed values when dependencies change
[~andy/gtk] / gtk / gtkstylecontext.c
index 5c739230fccdde9a381ddc8f3a528f98f961d875..7163b81d8ff3a59640889e27732f5a48d9ac555e 100644 (file)
 #include <gobject/gvaluecollector.h>
 
 #include "gtkstylecontextprivate.h"
+#include "gtkcontainerprivate.h"
+#include "gtkcssanimatedvaluesprivate.h"
+#include "gtkcssenginevalueprivate.h"
+#include "gtkcssnumbervalueprivate.h"
 #include "gtkcssrgbavalueprivate.h"
+#include "gtkdebug.h"
 #include "gtkstylepropertiesprivate.h"
 #include "gtktypebuiltins.h"
 #include "gtkthemingengineprivate.h"
 #include "gtkwindow.h"
 #include "gtkprivate.h"
 #include "gtksymboliccolorprivate.h"
-#include "gtkanimationdescription.h"
-#include "gtkcssnumbervalueprivate.h"
-#include "gtktimeline.h"
 #include "gtkiconfactory.h"
 #include "gtkwidgetpath.h"
 #include "gtkwidgetprivate.h"
 #include "gtkstylecascadeprivate.h"
 #include "gtkstyleproviderprivate.h"
 #include "gtksettings.h"
+#include "gtksettingsprivate.h"
 
 /**
  * SECTION:gtkstylecontext
 
 /* When these change we do a full restyling. Otherwise we try to figure out
  * if we need to change things. */
-#define GTK_STYLE_CONTEXT_RADICAL_CHANGE (GTK_CSS_CHANGE_NAME | GTK_CSS_CHANGE_CLASS)
+#define GTK_STYLE_CONTEXT_RADICAL_CHANGE (GTK_CSS_CHANGE_NAME | GTK_CSS_CHANGE_CLASS | GTK_CSS_CHANGE_SOURCE)
 /* When these change we don't clear the cache. This takes more memory but makes
  * things go faster. */
 #define GTK_STYLE_CONTEXT_CACHED_CHANGE (GTK_CSS_CHANGE_STATE)
 typedef struct GtkStyleInfo GtkStyleInfo;
 typedef struct GtkRegion GtkRegion;
 typedef struct PropertyValue PropertyValue;
-typedef struct AnimationInfo AnimationInfo;
 typedef struct StyleData StyleData;
 
 struct GtkRegion
@@ -332,6 +334,7 @@ struct PropertyValue
 
 struct GtkStyleInfo
 {
+  GtkStyleInfo *next;
   GArray *style_classes;
   GArray *regions;
   GtkJunctionSides junction_sides;
@@ -343,25 +346,7 @@ struct StyleData
 {
   GtkCssComputedValues *store;
   GArray *property_cache;
-};
-
-struct AnimationInfo
-{
-  GtkTimeline *timeline;
-
-  gpointer region_id;
-
-  /* Region stack (until region_id) at the time of
-   * rendering, this is used for nested cancellation.
-   */
-  GSList *parent_regions;
-
-  GdkWindow *window;
-  GtkStateType state;
-  gboolean target_value;
-
-  cairo_region_t *invalidation_region;
-  GArray *rectangles;
+  guint ref_count;
 };
 
 struct _GtkStyleContextPrivate
@@ -370,24 +355,21 @@ struct _GtkStyleContextPrivate
 
   GtkStyleCascade *cascade;
 
+  GtkStyleContext *animation_list_prev;
+  GtkStyleContext *animation_list_next;
+
   GtkStyleContext *parent;
   GSList *children;
   GtkWidget *widget;            
   GtkWidgetPath *widget_path;
   GHashTable *style_data;
-  GSList *info_stack;
-
-  GSList *animation_regions;
-  GSList *animations;
-
-  GtkThemingEngine *theming_engine;
+  GtkStyleInfo *info;
 
   GtkTextDirection direction;
 
   GtkCssChange relevant_changes;
   GtkCssChange pending_changes;
 
-  guint animations_invalidated : 1;
   guint invalidating_context : 1;
   guint invalid : 1;
 };
@@ -405,6 +387,8 @@ enum {
 };
 
 static guint signals[LAST_SIGNAL] = { 0 };
+static GtkStyleContext *_running_animations = NULL;
+guint _running_animations_timer_id = 0;
 
 static void gtk_style_context_finalize (GObject *object);
 
@@ -486,6 +470,65 @@ gtk_style_context_class_init (GtkStyleContextClass *klass)
   g_type_class_add_private (object_class, sizeof (GtkStyleContextPrivate));
 }
 
+static StyleData *
+style_data_new (void)
+{
+  StyleData *data;
+
+  data = g_slice_new0 (StyleData);
+  data->ref_count = 1;
+
+  return data;
+}
+
+static void
+clear_property_cache (StyleData *data)
+{
+  guint i;
+
+  if (!data->property_cache)
+    return;
+
+  for (i = 0; i < data->property_cache->len; i++)
+    {
+      PropertyValue *node = &g_array_index (data->property_cache, PropertyValue, i);
+
+      g_param_spec_unref (node->pspec);
+      g_value_unset (&node->value);
+    }
+
+  g_array_free (data->property_cache, TRUE);
+  data->property_cache = NULL;
+}
+
+static StyleData *
+style_data_ref (StyleData *style_data)
+{
+  style_data->ref_count++;
+
+  return style_data;
+}
+
+static void
+style_data_unref (StyleData *data)
+{
+  data->ref_count--;
+
+  if (data->ref_count > 0)
+    return;
+
+  g_object_unref (data->store);
+  clear_property_cache (data);
+
+  g_slice_free (StyleData, data);
+}
+
+static gboolean
+style_data_is_animating (StyleData *style_data)
+{
+  return GTK_IS_CSS_ANIMATED_VALUES (style_data->store);
+}
+
 static GtkStyleInfo *
 style_info_new (void)
 {
@@ -498,16 +541,43 @@ style_info_new (void)
   return info;
 }
 
+static void
+style_info_set_data (GtkStyleInfo *info,
+                     StyleData    *data)
+{
+  if (info->data == data)
+    return;
+
+  if (data)
+    style_data_ref (data);
+
+  if (info->data)
+    style_data_unref (info->data);
+
+  info->data = data;
+}
+
 static void
 style_info_free (GtkStyleInfo *info)
 {
+  style_info_set_data (info, NULL);
   g_array_free (info->style_classes, TRUE);
   g_array_free (info->regions, TRUE);
   g_slice_free (GtkStyleInfo, info);
 }
 
 static GtkStyleInfo *
-style_info_copy (const GtkStyleInfo *info)
+style_info_pop (GtkStyleInfo *info)
+{
+  GtkStyleInfo *next = info->next;
+
+  style_info_free (info);
+
+  return next;
+}
+
+static GtkStyleInfo *
+style_info_copy (GtkStyleInfo *info)
 {
   GtkStyleInfo *copy;
 
@@ -520,9 +590,10 @@ style_info_copy (const GtkStyleInfo *info)
                        info->regions->data,
                        info->regions->len);
 
+  copy->next = info;
   copy->junction_sides = info->junction_sides;
   copy->state_flags = info->state_flags;
-  copy->data = info->data;
+  style_info_set_data (copy, info->data);
 
   return copy;
 }
@@ -588,50 +659,51 @@ style_info_equal (gconstpointer elem1,
   return TRUE;
 }
 
-static StyleData *
-style_data_new (void)
+static void
+gtk_style_context_cascade_changed (GtkStyleCascade *cascade,
+                                   GtkStyleContext *context)
 {
-  StyleData *data;
-
-  data = g_slice_new0 (StyleData);
-
-  return data;
+  _gtk_style_context_queue_invalidate (context, GTK_CSS_CHANGE_SOURCE);
 }
 
 static void
-clear_property_cache (StyleData *data)
+gtk_style_context_set_cascade (GtkStyleContext *context,
+                               GtkStyleCascade *cascade)
 {
-  guint i;
+  GtkStyleContextPrivate *priv;
 
-  if (!data->property_cache)
+  priv = context->priv;
+
+  if (priv->cascade == cascade)
     return;
 
-  for (i = 0; i < data->property_cache->len; i++)
+  if (cascade)
     {
-      PropertyValue *node = &g_array_index (data->property_cache, PropertyValue, i);
-
-      g_param_spec_unref (node->pspec);
-      g_value_unset (&node->value);
+      g_object_ref (cascade);
+      g_signal_connect (cascade,
+                        "-gtk-private-changed",
+                        G_CALLBACK (gtk_style_context_cascade_changed),
+                        context);
     }
 
-  g_array_free (data->property_cache, TRUE);
-  data->property_cache = NULL;
-}
+  if (priv->cascade)
+    {
+      g_signal_handlers_disconnect_by_func (priv->cascade, 
+                                            gtk_style_context_cascade_changed,
+                                            context);
+      g_object_unref (priv->cascade);
+    }
 
-static void
-style_data_free (StyleData *data)
-{
-  g_object_unref (data->store);
-  clear_property_cache (data);
+  priv->cascade = cascade;
 
-  g_slice_free (StyleData, data);
+  if (cascade)
+    gtk_style_context_cascade_changed (cascade, context);
 }
 
 static void
 gtk_style_context_init (GtkStyleContext *style_context)
 {
   GtkStyleContextPrivate *priv;
-  GtkStyleInfo *info;
 
   priv = style_context->priv = G_TYPE_INSTANCE_GET_PRIVATE (style_context,
                                                             GTK_TYPE_STYLE_CONTEXT,
@@ -640,175 +712,94 @@ gtk_style_context_init (GtkStyleContext *style_context)
   priv->style_data = g_hash_table_new_full (style_info_hash,
                                             style_info_equal,
                                             (GDestroyNotify) style_info_free,
-                                            (GDestroyNotify) style_data_free);
-  priv->theming_engine = g_object_ref ((gpointer) gtk_theming_engine_load (NULL));
+                                            (GDestroyNotify) style_data_unref);
 
   priv->direction = GTK_TEXT_DIR_LTR;
 
   priv->screen = gdk_screen_get_default ();
-  priv->cascade = _gtk_style_cascade_get_for_screen (priv->screen);
-  g_object_ref (priv->cascade);
   priv->relevant_changes = GTK_CSS_CHANGE_ANY;
 
   /* Create default info store */
-  info = style_info_new ();
-  priv->info_stack = g_slist_prepend (priv->info_stack, info);
-}
-
-static void
-animation_info_free (AnimationInfo *info)
-{
-  g_object_unref (info->timeline);
-  g_object_unref (info->window);
-
-  if (info->invalidation_region)
-    cairo_region_destroy (info->invalidation_region);
+  priv->info = style_info_new ();
 
-  g_array_free (info->rectangles, TRUE);
-  g_slist_free (info->parent_regions);
-  g_slice_free (AnimationInfo, info);
+  gtk_style_context_set_cascade (style_context,
+                                 _gtk_style_cascade_get_for_screen (priv->screen));
 }
 
-static AnimationInfo *
-animation_info_lookup_by_timeline (GtkStyleContext *context,
-                                   GtkTimeline     *timeline)
+static gboolean
+gtk_style_context_do_animations (gpointer unused)
 {
-  GtkStyleContextPrivate *priv;
-  AnimationInfo *info;
-  GSList *l;
-
-  priv = context->priv;
+  GtkStyleContext *context;
 
-  for (l = priv->animations; l; l = l->next)
+  for (context = _running_animations;
+       context != NULL;
+       context = context->priv->animation_list_next)
     {
-      info = l->data;
-
-      if (info->timeline == timeline)
-        return info;
+      _gtk_style_context_queue_invalidate (context, GTK_CSS_CHANGE_ANIMATE);
     }
 
-  return NULL;
+  return TRUE;
 }
 
-static void
-timeline_frame_cb (GtkTimeline *timeline,
-                   gdouble      progress,
-                   gpointer     user_data)
+static gboolean
+gtk_style_context_is_animating (GtkStyleContext *context)
 {
-  GtkStyleContextPrivate *priv;
-  GtkStyleContext *context;
-  AnimationInfo *info;
-
-  context = user_data;
-  priv = context->priv;
-  info = animation_info_lookup_by_timeline (context, timeline);
-
-  g_assert (info != NULL);
-
-  /* Cancel transition if window is gone */
-  if (gdk_window_is_destroyed (info->window) ||
-      !gdk_window_is_visible (info->window))
-    {
-      priv->animations = g_slist_remove (priv->animations, info);
-      animation_info_free (info);
-      return;
-    }
+  GtkStyleContextPrivate *priv = context->priv;
 
-  if (info->invalidation_region &&
-      !cairo_region_is_empty (info->invalidation_region))
-    gdk_window_invalidate_region (info->window, info->invalidation_region, TRUE);
-  else
-    gdk_window_invalidate_rect (info->window, NULL, TRUE);
+  return priv->animation_list_prev != NULL
+      || _running_animations == context;
 }
 
 static void
-timeline_finished_cb (GtkTimeline *timeline,
-                      gpointer     user_data)
+gtk_style_context_stop_animating (GtkStyleContext *context)
 {
-  GtkStyleContextPrivate *priv;
-  GtkStyleContext *context;
-  AnimationInfo *info;
-
-  context = user_data;
-  priv = context->priv;
-  info = animation_info_lookup_by_timeline (context, timeline);
+  GtkStyleContextPrivate *priv = context->priv;
 
-  g_assert (info != NULL);
+  if (!gtk_style_context_is_animating (context))
+    return;
 
-  priv->animations = g_slist_remove (priv->animations, info);
+  if (priv->animation_list_prev == NULL)
+    {
+      _running_animations = priv->animation_list_next;
 
-  /* Invalidate one last time the area, so the final content is painted */
-  if (info->invalidation_region &&
-      !cairo_region_is_empty (info->invalidation_region))
-    gdk_window_invalidate_region (info->window, info->invalidation_region, TRUE);
+      if (_running_animations == NULL)
+        {
+          /* we were the last animation */
+          g_source_remove (_running_animations_timer_id);
+          _running_animations_timer_id = 0;
+        }
+    }
   else
-    gdk_window_invalidate_rect (info->window, NULL, TRUE);
+    priv->animation_list_prev->priv->animation_list_next = priv->animation_list_next;
 
-  animation_info_free (info);
+  if (priv->animation_list_next)
+    priv->animation_list_next->priv->animation_list_prev = priv->animation_list_prev;
+
+  priv->animation_list_next = NULL;
+  priv->animation_list_prev = NULL;
 }
 
-static AnimationInfo *
-animation_info_new (GtkStyleContext         *context,
-                    gpointer                 region_id,
-                    guint                    duration,
-                    GtkTimelineProgressType  progress_type,
-                    gboolean                 loop,
-                    GtkStateType             state,
-                    gboolean                 target_value,
-                    GdkWindow               *window)
+static void
+gtk_style_context_start_animating (GtkStyleContext *context)
 {
-  AnimationInfo *info;
-
-  info = g_slice_new0 (AnimationInfo);
-
-  info->rectangles = g_array_new (FALSE, FALSE, sizeof (cairo_rectangle_int_t));
-  info->timeline = _gtk_timeline_new (duration);
-  info->window = g_object_ref (window);
-  info->state = state;
-  info->target_value = target_value;
-  info->region_id = region_id;
+  GtkStyleContextPrivate *priv = context->priv;
 
-  _gtk_timeline_set_progress_type (info->timeline, progress_type);
-  _gtk_timeline_set_loop (info->timeline, loop);
+  if (gtk_style_context_is_animating (context))
+    return;
 
-  if (!loop && !target_value)
+  if (_running_animations == NULL)
     {
-      _gtk_timeline_set_direction (info->timeline, GTK_TIMELINE_DIRECTION_BACKWARD);
-      _gtk_timeline_rewind (info->timeline);
+      _running_animations_timer_id = gdk_threads_add_timeout (25,
+                                                              gtk_style_context_do_animations,
+                                                              NULL);
+      _running_animations = context;
     }
-
-  g_signal_connect (info->timeline, "frame",
-                    G_CALLBACK (timeline_frame_cb), context);
-  g_signal_connect (info->timeline, "finished",
-                    G_CALLBACK (timeline_finished_cb), context);
-
-  _gtk_timeline_start (info->timeline);
-
-  return info;
-}
-
-static AnimationInfo *
-animation_info_lookup (GtkStyleContext *context,
-                       gpointer         region_id,
-                       GtkStateType     state)
-{
-  GtkStyleContextPrivate *priv;
-  GSList *l;
-
-  priv = context->priv;
-
-  for (l = priv->animations; l; l = l->next)
+  else
     {
-      AnimationInfo *info;
-
-      info = l->data;
-
-      if (info->state == state &&
-          info->region_id == region_id)
-        return info;
+      priv->animation_list_next = _running_animations;
+      _running_animations->priv->animation_list_prev = context;
+      _running_animations = context;
     }
-
-  return NULL;
 }
 
 static void
@@ -816,34 +807,26 @@ gtk_style_context_finalize (GObject *object)
 {
   GtkStyleContextPrivate *priv;
   GtkStyleContext *style_context;
-  GSList *l;
 
   style_context = GTK_STYLE_CONTEXT (object);
   priv = style_context->priv;
 
+  _gtk_style_context_stop_animations (style_context);
+
   /* children hold a reference to us */
   g_assert (priv->children == NULL);
 
   gtk_style_context_set_parent (style_context, NULL);
 
+  gtk_style_context_set_cascade (style_context, NULL);
+
   if (priv->widget_path)
     gtk_widget_path_free (priv->widget_path);
 
   g_hash_table_destroy (priv->style_data);
 
-  g_object_unref (priv->cascade);
-
-  g_slist_free_full (priv->info_stack, (GDestroyNotify) style_info_free);
-
-  g_slist_free (priv->animation_regions);
-
-  for (l = priv->animations; l; l = l->next)
-    animation_info_free ((AnimationInfo *) l->data);
-
-  g_slist_free (priv->animations);
-
-  if (priv->theming_engine)
-    g_object_unref (priv->theming_engine);
+  while (priv->info)
+    priv->info = style_info_pop (priv->info);
 
   G_OBJECT_CLASS (gtk_style_context_parent_class)->finalize (object);
 }
@@ -907,30 +890,6 @@ gtk_style_context_impl_get_property (GObject    *object,
     }
 }
 
-static void
-build_properties (GtkStyleContext *context,
-                  StyleData       *style_data,
-                  GtkWidgetPath   *path,
-                  GtkStateFlags    state)
-{
-  GtkStyleContextPrivate *priv;
-  GtkCssMatcher matcher;
-  GtkCssLookup *lookup;
-
-  priv = context->priv;
-
-  _gtk_css_matcher_init (&matcher, path, state);
-  lookup = _gtk_css_lookup_new ();
-
-  _gtk_style_provider_private_lookup (GTK_STYLE_PROVIDER_PRIVATE (priv->cascade),
-                                      &matcher,
-                                      lookup);
-
-  style_data->store = _gtk_css_computed_values_new ();
-  _gtk_css_lookup_resolve (lookup, context, style_data->store);
-  _gtk_css_lookup_free (lookup);
-}
-
 static GtkWidgetPath *
 create_query_path (GtkStyleContext *context)
 {
@@ -940,10 +899,10 @@ create_query_path (GtkStyleContext *context)
   guint i, pos;
 
   priv = context->priv;
-  path = gtk_widget_path_copy (priv->widget ? gtk_widget_get_path (priv->widget) : priv->widget_path);
+  path = priv->widget ? _gtk_widget_create_path (priv->widget) : gtk_widget_path_copy (priv->widget_path);
   pos = gtk_widget_path_length (path) - 1;
 
-  info = priv->info_stack->data;
+  info = priv->info;
 
   /* Set widget regions */
   for (i = 0; i < info->regions->len; i++)
@@ -969,15 +928,42 @@ create_query_path (GtkStyleContext *context)
   return path;
 }
 
+static void
+build_properties (GtkStyleContext      *context,
+                  GtkCssComputedValues *values,
+                  GtkStateFlags         state,
+                  const GtkBitmask     *relevant_changes)
+{
+  GtkStyleContextPrivate *priv;
+  GtkCssMatcher matcher;
+  GtkWidgetPath *path;
+  GtkCssLookup *lookup;
+
+  priv = context->priv;
+
+  path = create_query_path (context);
+  lookup = _gtk_css_lookup_new (relevant_changes);
+
+  if (_gtk_css_matcher_init (&matcher, path, state))
+    _gtk_style_provider_private_lookup (GTK_STYLE_PROVIDER_PRIVATE (priv->cascade),
+                                        &matcher,
+                                        lookup);
+
+  _gtk_css_lookup_resolve (lookup, context, values);
+
+  _gtk_css_lookup_free (lookup);
+  gtk_widget_path_free (path);
+}
+
 static StyleData *
 style_data_lookup (GtkStyleContext *context)
 {
   GtkStyleContextPrivate *priv;
   GtkStyleInfo *info;
-  GtkCssValue *v;
+  StyleData *data;
 
   priv = context->priv;
-  info = priv->info_stack->data;
+  info = priv->info;
 
   /* Current data in use is cached, just return it */
   if (info->data)
@@ -985,34 +971,40 @@ style_data_lookup (GtkStyleContext *context)
 
   g_assert (priv->widget != NULL || priv->widget_path != NULL);
 
-  info->data = g_hash_table_lookup (priv->style_data, info);
-
-  if (!info->data)
+  data = g_hash_table_lookup (priv->style_data, info);
+  if (data)
     {
-      GtkWidgetPath *path;
+      style_info_set_data (info, data);
+      return data;
+    }
 
-      path = create_query_path (context);
+  data = style_data_new ();
+  data->store = _gtk_css_computed_values_new ();
+  style_info_set_data (info, data);
+  g_hash_table_insert (priv->style_data,
+                       style_info_copy (info),
+                       data);
 
-      info->data = style_data_new ();
-      g_hash_table_insert (priv->style_data,
-                           style_info_copy (info),
-                           info->data);
+  build_properties (context, data->store, info->state_flags, NULL);
 
-      build_properties (context, info->data, path, info->state_flags);
+  return data;
+}
 
-      gtk_widget_path_free (path);
-    }
+static StyleData *
+style_data_lookup_for_state (GtkStyleContext *context,
+                             GtkStateFlags    state)
+{
+  StyleData *data;
 
-  if (priv->theming_engine)
-    g_object_unref (priv->theming_engine);
+  if (context->priv->info->state_flags == state)
+    return style_data_lookup (context);
 
-  v = _gtk_css_computed_values_get_value (info->data->store, GTK_CSS_PROPERTY_ENGINE);
-  if (v)
-    priv->theming_engine = _gtk_css_value_dup_object (v);
-  else
-    priv->theming_engine = g_object_ref (gtk_theming_engine_load (NULL));
+  gtk_style_context_save (context);
+  gtk_style_context_set_state (context, state);
+  data = style_data_lookup (context);
+  gtk_style_context_restore (context);
 
-  return info->data;
+  return data;
 }
 
 static void
@@ -1032,8 +1024,8 @@ gtk_style_context_set_invalid (GtkStyleContext *context,
     {
       if (priv->parent)
         gtk_style_context_set_invalid (priv->parent, TRUE);
-      else if (priv->widget)
-        gtk_widget_queue_resize (priv->widget);
+      else if (GTK_IS_RESIZE_CONTAINER (priv->widget))
+        _gtk_container_queue_restyle (GTK_CONTAINER (priv->widget));
     }
 }
 
@@ -1045,7 +1037,7 @@ gtk_style_context_set_invalid (GtkStyleContext *context,
 static gboolean
 gtk_style_context_is_saved (GtkStyleContext *context)
 {
-  return context->priv->info_stack->next != NULL;
+  return context->priv->info->next != NULL;
 }
 
 static void
@@ -1053,15 +1045,15 @@ gtk_style_context_queue_invalidate_internal (GtkStyleContext *context,
                                              GtkCssChange     change)
 {
   GtkStyleContextPrivate *priv = context->priv;
-  GtkStyleInfo *info = priv->info_stack->data;
+  GtkStyleInfo *info = priv->info;
 
   if (gtk_style_context_is_saved (context))
     {
-      info->data = NULL;
+      style_info_set_data (info, NULL);
     }
   else
     {
-      _gtk_style_context_queue_invalidate (context, GTK_CSS_CHANGE_STATE);
+      _gtk_style_context_queue_invalidate (context, change);
       /* XXX: We need to invalidate siblings here somehow */
     }
 }
@@ -1097,6 +1089,8 @@ _gtk_style_context_set_widget (GtkStyleContext *context,
 
   context->priv->widget = widget;
 
+  _gtk_style_context_stop_animations (context);
+
   _gtk_style_context_queue_invalidate (context, GTK_CSS_CHANGE_ANY_SELF);
 }
 
@@ -1111,6 +1105,10 @@ _gtk_style_context_set_widget (GtkStyleContext *context,
  *            %GTK_STYLE_PROVIDER_PRIORITY_USER
  *
  * Adds a style provider to @context, to be used in style construction.
+ * Note that a style provider added by this function only affects
+ * the style of the widget to which @context belongs. If you want
+ * to affect the style of all widgets, use
+ * gtk_style_context_add_provider_for_screen().
  *
  * <note><para>If both priorities are the same, A #GtkStyleProvider
  * added through this function takes precedence over another added
@@ -1136,13 +1134,14 @@ gtk_style_context_add_provider (GtkStyleContext  *context,
       
       new_cascade = _gtk_style_cascade_new ();
       _gtk_style_cascade_set_parent (new_cascade, priv->cascade);
-      g_object_unref (priv->cascade);
-      priv->cascade = new_cascade;
+      _gtk_style_cascade_add_provider (new_cascade, provider, priority);
+      gtk_style_context_set_cascade (context, new_cascade);
+      g_object_unref (new_cascade);
+    }
+  else
+    {
+      _gtk_style_cascade_add_provider (priv->cascade, provider, priority);
     }
-
-  _gtk_style_cascade_add_provider (priv->cascade, provider, priority);
-
-  gtk_style_context_invalidate (context);
 }
 
 /**
@@ -1216,8 +1215,7 @@ gtk_style_context_reset_widgets (GdkScreen *screen)
  *            %GTK_STYLE_PROVIDER_PRIORITY_USER
  *
  * Adds a global style provider to @screen, which will be used
- * in style construction for all #GtkStyleContext<!-- -->s under
- * @screen.
+ * in style construction for all #GtkStyleContexts under @screen.
  *
  * GTK+ uses this to make styling information from #GtkSettings
  * available.
@@ -1237,6 +1235,7 @@ gtk_style_context_add_provider_for_screen (GdkScreen        *screen,
 
   g_return_if_fail (GDK_IS_SCREEN (screen));
   g_return_if_fail (GTK_IS_STYLE_PROVIDER (provider));
+  g_return_if_fail (!GTK_IS_SETTINGS (provider) || _gtk_settings_get_screen (GTK_SETTINGS (provider)) == screen);
 
   cascade = _gtk_style_cascade_get_for_screen (screen);
   _gtk_style_cascade_add_provider (cascade, provider, priority);
@@ -1259,6 +1258,7 @@ gtk_style_context_remove_provider_for_screen (GdkScreen        *screen,
 
   g_return_if_fail (GDK_IS_SCREEN (screen));
   g_return_if_fail (GTK_IS_STYLE_PROVIDER (provider));
+  g_return_if_fail (!GTK_IS_SETTINGS (provider));
 
   cascade = _gtk_style_cascade_get_for_screen (screen);
   _gtk_style_cascade_remove_provider (cascade, provider);
@@ -1356,11 +1356,8 @@ gtk_style_context_get_property (GtkStyleContext *context,
       return;
     }
 
-  gtk_style_context_save (context);
-  gtk_style_context_set_state (context, state);
-  data = style_data_lookup (context);
+  data = style_data_lookup_for_state (context, state);
   _gtk_style_property_query (prop, value, gtk_style_context_query_func, data->store);
-  gtk_style_context_restore (context);
 }
 
 /**
@@ -1447,14 +1444,9 @@ void
 gtk_style_context_set_state (GtkStyleContext *context,
                              GtkStateFlags    flags)
 {
-  GtkStyleContextPrivate *priv;
-  GtkStyleInfo *info;
-
   g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
 
-  priv = context->priv;
-  info = priv->info_stack->data;
-  info->state_flags = flags;
+  context->priv->info->state_flags = flags;
   
   gtk_style_context_queue_invalidate_internal (context, GTK_CSS_CHANGE_STATE);
 }
@@ -1472,31 +1464,9 @@ gtk_style_context_set_state (GtkStyleContext *context,
 GtkStateFlags
 gtk_style_context_get_state (GtkStyleContext *context)
 {
-  GtkStyleContextPrivate *priv;
-  GtkStyleInfo *info;
-
   g_return_val_if_fail (GTK_IS_STYLE_CONTEXT (context), 0);
 
-  priv = context->priv;
-  info = priv->info_stack->data;
-
-  return info->state_flags;
-}
-
-static gboolean
-context_has_animatable_region (GtkStyleContext *context,
-                               gpointer         region_id)
-{
-  GtkStyleContextPrivate *priv;
-
-  /* NULL region_id means everything
-   * rendered through the style context
-   */
-  if (!region_id)
-    return TRUE;
-
-  priv = context->priv;
-  return g_slist_find (priv->animation_regions, region_id) != NULL;
+  return context->priv->info->state_flags;
 }
 
 /**
@@ -1517,34 +1487,16 @@ context_has_animatable_region (GtkStyleContext *context,
  * Returns: %TRUE if there is a running transition animation for @state.
  *
  * Since: 3.0
+ *
+ * Deprecated: 3.6: This function always returns %FALSE
  **/
 gboolean
 gtk_style_context_state_is_running (GtkStyleContext *context,
                                     GtkStateType     state,
                                     gdouble         *progress)
 {
-  GtkStyleContextPrivate *priv;
-  AnimationInfo *info;
-  GSList *l;
-
   g_return_val_if_fail (GTK_IS_STYLE_CONTEXT (context), FALSE);
 
-  priv = context->priv;
-
-  for (l = priv->animations; l; l = l->next)
-    {
-      info = l->data;
-
-      if (info->state == state &&
-          context_has_animatable_region (context, info->region_id))
-        {
-          if (progress)
-            *progress = _gtk_timeline_get_progress (info->timeline);
-
-          return TRUE;
-        }
-    }
-
   return FALSE;
 }
 
@@ -1693,16 +1645,17 @@ void
 gtk_style_context_save (GtkStyleContext *context)
 {
   GtkStyleContextPrivate *priv;
-  GtkStyleInfo *info;
 
   g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
 
   priv = context->priv;
 
-  g_assert (priv->info_stack != NULL);
-
-  info = style_info_copy (priv->info_stack->data);
-  priv->info_stack = g_slist_prepend (priv->info_stack, info);
+  priv->info = style_info_copy (priv->info);
+  /* Need to unset animations here because we can not know what style
+   * class potential transitions came from once we save().
+   */
+  if (priv->info->data && style_data_is_animating (priv->info->data))
+    style_info_set_data (priv->info, NULL);
 }
 
 /**
@@ -1718,26 +1671,19 @@ void
 gtk_style_context_restore (GtkStyleContext *context)
 {
   GtkStyleContextPrivate *priv;
-  GtkStyleInfo *info;
 
   g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
 
   priv = context->priv;
 
-  if (priv->info_stack)
-    {
-      info = priv->info_stack->data;
-      priv->info_stack = g_slist_remove (priv->info_stack, info);
-      style_info_free (info);
-    }
+  priv->info = style_info_pop (priv->info);
 
-  if (!priv->info_stack)
+  if (!priv->info)
     {
       g_warning ("Unpaired gtk_style_context_restore() call");
 
       /* Create default region */
-      info = style_info_new ();
-      priv->info_stack = g_slist_prepend (priv->info_stack, info);
+      priv->info = style_info_new ();
     }
 }
 
@@ -1872,8 +1818,7 @@ gtk_style_context_add_class (GtkStyleContext *context,
   priv = context->priv;
   class_quark = g_quark_from_string (class_name);
 
-  g_assert (priv->info_stack != NULL);
-  info = priv->info_stack->data;
+  info = priv->info;
 
   if (!style_class_find (info->style_classes, class_quark, &position))
     {
@@ -1911,8 +1856,7 @@ gtk_style_context_remove_class (GtkStyleContext *context,
 
   priv = context->priv;
 
-  g_assert (priv->info_stack != NULL);
-  info = priv->info_stack->data;
+  info = priv->info;
 
   if (style_class_find (info->style_classes, class_quark, &position))
     {
@@ -1952,8 +1896,7 @@ gtk_style_context_has_class (GtkStyleContext *context,
 
   priv = context->priv;
 
-  g_assert (priv->info_stack != NULL);
-  info = priv->info_stack->data;
+  info = priv->info;
 
   if (style_class_find (info->style_classes, class_quark, NULL))
     return TRUE;
@@ -1986,8 +1929,7 @@ gtk_style_context_list_classes (GtkStyleContext *context)
 
   priv = context->priv;
 
-  g_assert (priv->info_stack != NULL);
-  info = priv->info_stack->data;
+  info = priv->info;
 
   for (i = 0; i < info->style_classes->len; i++)
     {
@@ -2025,8 +1967,7 @@ gtk_style_context_list_regions (GtkStyleContext *context)
 
   priv = context->priv;
 
-  g_assert (priv->info_stack != NULL);
-  info = priv->info_stack->data;
+  info = priv->info;
 
   for (i = 0; i < info->regions->len; i++)
     {
@@ -2110,8 +2051,7 @@ gtk_style_context_add_region (GtkStyleContext *context,
   priv = context->priv;
   region_quark = g_quark_from_string (region_name);
 
-  g_assert (priv->info_stack != NULL);
-  info = priv->info_stack->data;
+  info = priv->info;
 
   if (!region_find (info->regions, region_quark, &position))
     {
@@ -2154,8 +2094,7 @@ gtk_style_context_remove_region (GtkStyleContext *context,
 
   priv = context->priv;
 
-  g_assert (priv->info_stack != NULL);
-  info = priv->info_stack->data;
+  info = priv->info;
 
   if (region_find (info->regions, region_quark, &position))
     {
@@ -2202,8 +2141,7 @@ gtk_style_context_has_region (GtkStyleContext *context,
 
   priv = context->priv;
 
-  g_assert (priv->info_stack != NULL);
-  info = priv->info_stack->data;
+  info = priv->info;
 
   if (region_find (info->regions, region_quark, &position))
     {
@@ -2272,10 +2210,7 @@ _gtk_style_context_peek_style_property (GtkStyleContext *context,
 
   priv = context->priv;
 
-  gtk_style_context_save (context);
-  gtk_style_context_set_state (context, state);
-  data = style_data_lookup (context);
-  gtk_style_context_restore (context);
+  data = style_data_lookup_for_state (context, state);
 
   key.widget_type = widget_type;
   key.state = state;
@@ -2307,9 +2242,10 @@ _gtk_style_context_peek_style_property (GtkStyleContext *context,
 
   if (priv->widget || priv->widget_path)
     {
+      GtkWidgetPath *widget_path = priv->widget ? _gtk_widget_create_path (priv->widget) : priv->widget_path;
+
       if (gtk_style_provider_get_style_property (GTK_STYLE_PROVIDER (priv->cascade),
-                                                 priv->widget ? gtk_widget_get_path (priv->widget)
-                                                              : priv->widget_path,
+                                                 widget_path,
                                                  state, pspec, &pcache->value))
         {
           /* Resolve symbolic colors to GdkColor/GdkRGBA */
@@ -2348,8 +2284,14 @@ _gtk_style_context_peek_style_property (GtkStyleContext *context,
               gtk_symbolic_color_unref (color);
             }
 
+          if (priv->widget)
+            gtk_widget_path_free (widget_path);
+
           return &pcache->value;
         }
+
+      if (priv->widget)
+        gtk_widget_path_free (widget_path);
     }
 
   /* not supplied by any provider, revert to default */
@@ -2601,9 +2543,7 @@ gtk_style_context_set_screen (GtkStyleContext *context,
 
   if (priv->cascade == _gtk_style_cascade_get_for_screen (priv->screen))
     {
-      g_object_unref (priv->cascade);
-      priv->cascade = _gtk_style_cascade_get_for_screen (screen);
-      g_object_ref (priv->cascade);
+      gtk_style_context_set_cascade (context, _gtk_style_cascade_get_for_screen (screen));
     }
   else
     {
@@ -2613,8 +2553,6 @@ gtk_style_context_set_screen (GtkStyleContext *context,
   priv->screen = screen;
 
   g_object_notify (G_OBJECT (context), "screen");
-
-  gtk_style_context_invalidate (context);
 }
 
 /**
@@ -2706,14 +2644,9 @@ void
 gtk_style_context_set_junction_sides (GtkStyleContext  *context,
                                       GtkJunctionSides  sides)
 {
-  GtkStyleContextPrivate *priv;
-  GtkStyleInfo *info;
-
   g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
 
-  priv = context->priv;
-  info = priv->info_stack->data;
-  info->junction_sides = sides;
+  context->priv->info->junction_sides = sides;
 }
 
 /**
@@ -2729,14 +2662,9 @@ gtk_style_context_set_junction_sides (GtkStyleContext  *context,
 GtkJunctionSides
 gtk_style_context_get_junction_sides (GtkStyleContext *context)
 {
-  GtkStyleContextPrivate *priv;
-  GtkStyleInfo *info;
-
   g_return_val_if_fail (GTK_IS_STYLE_CONTEXT (context), 0);
 
-  priv = context->priv;
-  info = priv->info_stack->data;
-  return info->junction_sides;
+  return context->priv->info->junction_sides;
 }
 
 static GtkSymbolicColor *
@@ -2750,12 +2678,15 @@ gtk_style_context_color_lookup_func (gpointer    contextp,
 
 GtkCssValue *
 _gtk_style_context_resolve_color_value (GtkStyleContext  *context,
-                                       GtkSymbolicColor *color)
+                                        GtkCssValue      *current,
+                                       GtkCssValue      *color)
 {
   g_return_val_if_fail (GTK_IS_STYLE_CONTEXT (context), FALSE);
+  g_return_val_if_fail (current != NULL, FALSE);
   g_return_val_if_fail (color != NULL, FALSE);
 
-  return _gtk_symbolic_color_resolve_full (color,
+  return _gtk_symbolic_color_resolve_full ((GtkSymbolicColor *) color,
+                                           current,
                                            gtk_style_context_color_lookup_func,
                                            context);
 }
@@ -2773,6 +2704,7 @@ _gtk_style_context_resolve_color (GtkStyleContext  *context,
   g_return_val_if_fail (result != NULL, FALSE);
 
   val = _gtk_symbolic_color_resolve_full (color,
+                                          _gtk_style_context_peek_property (context, GTK_CSS_PROPERTY_COLOR),
                                          gtk_style_context_color_lookup_func,
                                          context);
   if (val == NULL)
@@ -2847,124 +2779,36 @@ gtk_style_context_lookup_color (GtkStyleContext *context,
  * <programlisting>
  * GtkButton {
  *     background-color: &num;f00
- * }
- *
- * GtkButton:hover {
- *     background-color: &num;fff;
- *     transition: 200ms linear
- * }
- * </programlisting>
- *
- * This combination will animate the button background from red to white
- * if a pointer enters the button, and back to red if the pointer leaves
- * the button.
- *
- * Note that @state is used when finding the transition parameters, which
- * is why the style places the transition under the :hover pseudo-class.
- *
- * Since: 3.0
- **/
-void
-gtk_style_context_notify_state_change (GtkStyleContext *context,
-                                       GdkWindow       *window,
-                                       gpointer         region_id,
-                                       GtkStateType     state,
-                                       gboolean         state_value)
-{
-  GtkStyleContextPrivate *priv;
-  GtkAnimationDescription *desc;
-  AnimationInfo *info;
-  GtkStateFlags flags;
-  GtkCssValue *v;
-  StyleData *data;
-
-  g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
-  g_return_if_fail (GDK_IS_WINDOW (window));
-  g_return_if_fail (state > GTK_STATE_NORMAL && state <= GTK_STATE_FOCUSED);
-
-  priv = context->priv;
-  g_return_if_fail (priv->widget != NULL || priv->widget_path != NULL);
-
-  state_value = (state_value == TRUE);
-
-  switch (state)
-    {
-    case GTK_STATE_ACTIVE:
-      flags = GTK_STATE_FLAG_ACTIVE;
-      break;
-    case GTK_STATE_PRELIGHT:
-      flags = GTK_STATE_FLAG_PRELIGHT;
-      break;
-    case GTK_STATE_SELECTED:
-      flags = GTK_STATE_FLAG_SELECTED;
-      break;
-    case GTK_STATE_INSENSITIVE:
-      flags = GTK_STATE_FLAG_INSENSITIVE;
-      break;
-    case GTK_STATE_INCONSISTENT:
-      flags = GTK_STATE_FLAG_INCONSISTENT;
-      break;
-    case GTK_STATE_FOCUSED:
-      flags = GTK_STATE_FLAG_FOCUSED;
-      break;
-    case GTK_STATE_NORMAL:
-    default:
-      flags = 0;
-      break;
-    }
-
-  /* Find out if there is any animation description for the given
-   * state, it will fallback to the normal state as well if necessary.
-   */
-  gtk_style_context_save (context);
-  gtk_style_context_set_state (context, flags);
-  data = style_data_lookup (context);
-  gtk_style_context_restore (context);
-  v = _gtk_css_computed_values_get_value (data->store, GTK_CSS_PROPERTY_TRANSITION);
-  if (!v)
-    return;
-  desc = _gtk_css_value_get_boxed (v);
-  if (!desc)
-    return;
-
-  if (_gtk_animation_description_get_duration (desc) == 0)
-    return;
-
-  info = animation_info_lookup (context, region_id, state);
-
-  if (info &&
-      info->target_value != state_value)
-    {
-      /* Target values are the opposite */
-      if (!_gtk_timeline_get_loop (info->timeline))
-        {
-          /* Reverse the animation */
-          if (_gtk_timeline_get_direction (info->timeline) == GTK_TIMELINE_DIRECTION_FORWARD)
-            _gtk_timeline_set_direction (info->timeline, GTK_TIMELINE_DIRECTION_BACKWARD);
-          else
-            _gtk_timeline_set_direction (info->timeline, GTK_TIMELINE_DIRECTION_FORWARD);
-
-          info->target_value = state_value;
-        }
-      else
-        {
-          /* Take it out of its looping state */
-          _gtk_timeline_set_loop (info->timeline, FALSE);
-        }
-    }
-  else if (!info &&
-           (!_gtk_animation_description_get_loop (desc) ||
-            state_value))
-    {
-      info = animation_info_new (context, region_id,
-                                 _gtk_animation_description_get_duration (desc),
-                                 _gtk_animation_description_get_progress_type (desc),
-                                 _gtk_animation_description_get_loop (desc),
-                                 state, state_value, window);
-
-      priv->animations = g_slist_prepend (priv->animations, info);
-      priv->animations_invalidated = TRUE;
-    }
+ * }
+ *
+ * GtkButton:hover {
+ *     background-color: &num;fff;
+ *     transition: 200ms linear
+ * }
+ * </programlisting>
+ *
+ * This combination will animate the button background from red to white
+ * if a pointer enters the button, and back to red if the pointer leaves
+ * the button.
+ *
+ * Note that @state is used when finding the transition parameters, which
+ * is why the style places the transition under the :hover pseudo-class.
+ *
+ * Since: 3.0
+ *
+ * Deprecated: 3.6: This function does nothing.
+ **/
+void
+gtk_style_context_notify_state_change (GtkStyleContext *context,
+                                       GdkWindow       *window,
+                                       gpointer         region_id,
+                                       GtkStateType     state,
+                                       gboolean         state_value)
+{
+  g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
+  g_return_if_fail (GDK_IS_WINDOW (window));
+  g_return_if_fail (state > GTK_STATE_NORMAL && state <= GTK_STATE_FOCUSED);
+  g_return_if_fail (context->priv->widget != NULL || context->priv->widget_path != NULL);
 }
 
 /**
@@ -2984,60 +2828,14 @@ gtk_style_context_notify_state_change (GtkStyleContext *context,
  * animatable regions.
  *
  * Since: 3.0
+ *
+ * Deprecated: 3.6: This function does nothing.
  **/
 void
 gtk_style_context_cancel_animations (GtkStyleContext *context,
                                      gpointer         region_id)
 {
-  GtkStyleContextPrivate *priv;
-  AnimationInfo *info;
-  GSList *l;
-
   g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
-
-  priv = context->priv;
-  l = priv->animations;
-
-  while (l)
-    {
-      info = l->data;
-      l = l->next;
-
-      if (!region_id ||
-          info->region_id == region_id ||
-          g_slist_find (info->parent_regions, region_id))
-        {
-          priv->animations = g_slist_remove (priv->animations, info);
-          animation_info_free (info);
-        }
-    }
-}
-
-static gboolean
-is_parent_of (GdkWindow *parent,
-              GdkWindow *child)
-{
-  GtkWidget *child_widget, *parent_widget;
-  GdkWindow *window;
-
-  gdk_window_get_user_data (child, (gpointer *) &child_widget);
-  gdk_window_get_user_data (parent, (gpointer *) &parent_widget);
-
-  if (child_widget != parent_widget &&
-      !gtk_widget_is_ancestor (child_widget, parent_widget))
-    return FALSE;
-
-  window = child;
-
-  while (window)
-    {
-      if (window == parent)
-        return TRUE;
-
-      window = gdk_window_get_parent (window);
-    }
-
-  return FALSE;
 }
 
 /**
@@ -3054,6 +2852,8 @@ is_parent_of (GdkWindow *parent,
  * with it.
  *
  * Since: 3.0
+ *
+ * Deprecated: 3.6: This function does nothing.
  **/
 void
 gtk_style_context_scroll_animations (GtkStyleContext *context,
@@ -3061,26 +2861,8 @@ gtk_style_context_scroll_animations (GtkStyleContext *context,
                                      gint             dx,
                                      gint             dy)
 {
-  GtkStyleContextPrivate *priv;
-  AnimationInfo *info;
-  GSList *l;
-
   g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
   g_return_if_fail (GDK_IS_WINDOW (window));
-
-  priv = context->priv;
-  l = priv->animations;
-
-  while (l)
-    {
-      info = l->data;
-      l = l->next;
-
-      if (info->invalidation_region &&
-          (window == info->window ||
-           is_parent_of (window, info->window)))
-        cairo_region_translate (info->invalidation_region, dx, dy);
-    }
 }
 
 /**
@@ -3099,18 +2881,15 @@ gtk_style_context_scroll_animations (GtkStyleContext *context,
  * can uniquely identify rendered elements subject to a state transition.
  *
  * Since: 3.0
+ *
+ * Deprecated: 3.6: This function does nothing.
  **/
 void
 gtk_style_context_push_animatable_region (GtkStyleContext *context,
                                           gpointer         region_id)
 {
-  GtkStyleContextPrivate *priv;
-
   g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
   g_return_if_fail (region_id != NULL);
-
-  priv = context->priv;
-  priv->animation_regions = g_slist_prepend (priv->animation_regions, region_id);
 }
 
 /**
@@ -3121,190 +2900,209 @@ gtk_style_context_push_animatable_region (GtkStyleContext *context,
  * See gtk_style_context_push_animatable_region().
  *
  * Since: 3.0
+ *
+ * Deprecated: 3.6: This function does nothing.
  **/
 void
 gtk_style_context_pop_animatable_region (GtkStyleContext *context)
 {
-  GtkStyleContextPrivate *priv;
-
   g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
-
-  priv = context->priv;
-  priv->animation_regions = g_slist_delete_link (priv->animation_regions,
-                                                 priv->animation_regions);
 }
 
-void
-_gtk_style_context_invalidate_animation_areas (GtkStyleContext *context)
+static void
+gtk_style_context_clear_cache (GtkStyleContext *context)
 {
   GtkStyleContextPrivate *priv;
-  GSList *l;
+  GtkStyleInfo *info;
 
   priv = context->priv;
 
-  for (l = priv->animations; l; l = l->next)
+  for (info = priv->info; info; info = info->next)
     {
-      AnimationInfo *info;
+      style_info_set_data (info, NULL);
+    }
+  g_hash_table_remove_all (priv->style_data);
+}
+
+static void
+gtk_style_context_update_cache (GtkStyleContext  *context,
+                                const GtkBitmask *parent_changes)
+{
+  GtkStyleContextPrivate *priv;
+  GHashTableIter iter;
+  gpointer key, value;
 
-      info = l->data;
+  priv = context->priv;
 
-      /* A NULL invalidation region means it has to be recreated on
-       * the next expose event, this happens usually after a widget
-       * allocation change, so the next expose after it will update
-       * the invalidation region.
-       */
-      if (info->invalidation_region)
-        {
-          cairo_region_destroy (info->invalidation_region);
-          info->invalidation_region = NULL;
-        }
-    }
+  g_hash_table_remove_all (priv->style_data);
 
-  priv->animations_invalidated = TRUE;
+  g_hash_table_iter_init (&iter, priv->style_data);
+  while (g_hash_table_iter_next (&iter, &key, &value))
+    {
+      GtkStyleInfo *info = key;
+      StyleData *data = value;
+      GtkBitmask *changes;
+
+      changes = _gtk_bitmask_copy (parent_changes);
+      changes = _gtk_bitmask_intersect (changes, data->store->depends_on_parent);
+      if (_gtk_bitmask_get (changes, GTK_CSS_PROPERTY_COLOR))
+        changes = _gtk_bitmask_union (changes, data->store->depends_on_color);
+      if (_gtk_bitmask_get (changes, GTK_CSS_PROPERTY_FONT_SIZE))
+        changes = _gtk_bitmask_union (changes, data->store->depends_on_font_size);
+
+      build_properties (context, data->store, info->state_flags, changes);
+    }
 }
 
-void
-_gtk_style_context_coalesce_animation_areas (GtkStyleContext *context,
-                                             GtkWidget       *widget)
+static void
+gtk_style_context_do_invalidate (GtkStyleContext *context)
 {
   GtkStyleContextPrivate *priv;
-  GSList *l;
+
+  g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
 
   priv = context->priv;
 
-  if (!priv->animations_invalidated)
+  /* Avoid reentrancy */
+  if (priv->invalidating_context)
     return;
 
-  l = priv->animations;
-
-  while (l)
-    {
-      AnimationInfo *info;
-      gint rel_x, rel_y;
-      GSList *cur;
-      guint i;
+  priv->invalidating_context = TRUE;
 
-      cur = l;
-      info = cur->data;
-      l = l->next;
+  g_signal_emit (context, signals[CHANGED], 0);
 
-      if (info->invalidation_region)
-        continue;
+  priv->invalidating_context = FALSE;
+}
 
-      if (info->rectangles->len == 0)
-        continue;
+void
+_gtk_style_context_stop_animations (GtkStyleContext *context)
+{
+  g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
 
-      info->invalidation_region = cairo_region_create ();
-      _gtk_widget_get_translation_to_window (widget, info->window, &rel_x, &rel_y);
+  if (!gtk_style_context_is_animating (context))
+    return;
 
-      for (i = 0; i < info->rectangles->len; i++)
-        {
-          cairo_rectangle_int_t *rect;
+  style_info_set_data (context->priv->info, NULL);
 
-          rect = &g_array_index (info->rectangles, cairo_rectangle_int_t, i);
+  gtk_style_context_stop_animating (context);
+}
 
-          /* These are widget relative coordinates,
-           * so have them inverted to be window relative
-           */
-          rect->x -= rel_x;
-          rect->y -= rel_y;
+static GtkBitmask *
+gtk_style_context_update_animations (GtkStyleContext *context,
+                                     gint64           timestamp)
+{
+  GtkBitmask *differences;
+  StyleData *style_data;
+  
+  style_data = style_data_lookup (context);
 
-          cairo_region_union_rectangle (info->invalidation_region, rect);
-        }
+  differences = _gtk_css_animated_values_advance (GTK_CSS_ANIMATED_VALUES (style_data->store),
+                                                  timestamp);
 
-      g_array_remove_range (info->rectangles, 0, info->rectangles->len);
-    }
+  if (_gtk_css_animated_values_is_finished (GTK_CSS_ANIMATED_VALUES (style_data->store)))
+    _gtk_style_context_stop_animations (context);
 
-  priv->animations_invalidated = FALSE;
+  return differences;
 }
 
-static void
-store_animation_region (GtkStyleContext *context,
-                        gdouble          x,
-                        gdouble          y,
-                        gdouble          width,
-                        gdouble          height)
+static gboolean
+gtk_style_context_should_animate (GtkStyleContext *context)
 {
   GtkStyleContextPrivate *priv;
-  GSList *l;
+  gboolean animate;
 
   priv = context->priv;
 
-  if (!priv->animations_invalidated)
-    return;
-
-  for (l = priv->animations; l; l = l->next)
-    {
-      AnimationInfo *info;
+  if (priv->widget == NULL)
+    return FALSE;
 
-      info = l->data;
+  if (!gtk_widget_get_mapped (priv->widget))
+    return FALSE;
 
-      /* The animation doesn't need updating
-       * the invalidation area, bail out.
-       */
-      if (info->invalidation_region)
-        continue;
+  g_object_get (gtk_widget_get_settings (context->priv->widget),
+                "gtk-enable-animations", &animate,
+                NULL);
 
-      if (context_has_animatable_region (context, info->region_id))
-        {
-          cairo_rectangle_int_t rect;
+  return animate;
+}
 
-          rect.x = (gint) x;
-          rect.y = (gint) y;
-          rect.width = (gint) width;
-          rect.height = (gint) height;
+static void
+gtk_style_context_start_animations (GtkStyleContext      *context,
+                                    GtkCssComputedValues *previous,
+                                    gint64                timestamp)
+{
+  StyleData *animated;
 
-          g_array_append_val (info->rectangles, rect);
+  if (!gtk_style_context_should_animate (context))
+    {
+      gtk_style_context_stop_animating (context);
+      return;
+    }
 
-          if (!info->parent_regions)
-            {
-              GSList *parent_regions;
+  animated = style_data_new ();
+  animated->store = _gtk_css_animated_values_new (style_data_lookup (context)->store,
+                                                  previous,
+                                                  timestamp);
 
-              parent_regions = g_slist_find (priv->animation_regions, info->region_id);
-              info->parent_regions = g_slist_copy (parent_regions);
-            }
-        }
+  if (_gtk_css_animated_values_is_finished (GTK_CSS_ANIMATED_VALUES (animated->store)))
+    {
+      style_data_unref (animated);
+      gtk_style_context_stop_animating (context);
+      return;
     }
+
+  style_info_set_data (context->priv->info, animated);
+  style_data_unref (animated);
+  gtk_style_context_start_animating (context);
 }
 
-static void
-gtk_style_context_do_invalidate (GtkStyleContext *context,
-                                 gboolean         clear_caches)
+static gboolean
+gtk_style_context_needs_full_revalidate (GtkStyleContext  *context,
+                                         GtkCssChange      change)
 {
-  GtkStyleContextPrivate *priv;
-
-  g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
-
-  priv = context->priv;
+  GtkStyleContextPrivate *priv = context->priv;
 
-  /* Avoid reentrancy */
-  if (priv->invalidating_context)
-    return;
+  /* Try to avoid invalidating if we can */
+  if (change & GTK_STYLE_CONTEXT_RADICAL_CHANGE)
+    {
+      priv->relevant_changes = GTK_CSS_CHANGE_ANY;
+    }
+  else
+    {
+      if (priv->relevant_changes == GTK_CSS_CHANGE_ANY)
+        {
+          GtkWidgetPath *path;
+          GtkCssMatcher matcher;
 
-  priv->invalidating_context = TRUE;
+          path = create_query_path (context);
+          if (_gtk_css_matcher_init (&matcher, path, priv->info->state_flags))
+            priv->relevant_changes = _gtk_style_provider_private_get_change (GTK_STYLE_PROVIDER_PRIVATE (priv->cascade),
+                                                                             &matcher);
+          else
+            priv->relevant_changes = 0;
 
-  if (clear_caches)
-    {
-      GSList *list;
+          priv->relevant_changes &= ~GTK_STYLE_CONTEXT_RADICAL_CHANGE;
 
-      for (list = priv->info_stack; list; list = list->next)
-        {
-          GtkStyleInfo *info = list->data;
-          info->data = NULL;
+          gtk_widget_path_unref (path);
         }
-      g_hash_table_remove_all (priv->style_data);
     }
 
-  g_signal_emit (context, signals[CHANGED], 0);
-
-  priv->invalidating_context = FALSE;
+  if (priv->relevant_changes & change)
+    return TRUE;
+  else
+    return FALSE;
 }
 
 void
-_gtk_style_context_validate (GtkStyleContext *context,
-                             GtkCssChange     change)
+_gtk_style_context_validate (GtkStyleContext  *context,
+                             gint64            timestamp,
+                             GtkCssChange      change,
+                             const GtkBitmask *parent_changes)
 {
   GtkStyleContextPrivate *priv;
+  GtkStyleInfo *info;
+  StyleData *current;
+  GtkBitmask *changes;
   GSList *list;
 
   g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
@@ -3312,50 +3110,99 @@ _gtk_style_context_validate (GtkStyleContext *context,
   priv = context->priv;
 
   change |= priv->pending_changes;
+  
+  /* If you run your application with
+   *   GTK_DEBUG=no-css-cache
+   * every invalidation will purge the cache and completely query
+   * everything anew form the cache. This is slow (in particular
+   * when animating), but useful for figuring out bugs.
+   *
+   * We achieve that by pretending that everything that could have
+   * changed has and so we of course totally need to redo everything.
+   *
+   * Note that this also completely revalidates child widgets all
+   * the time.
+   */
+  if (G_UNLIKELY (gtk_get_debug_flags () & GTK_DEBUG_NO_CSS_CACHE))
+    change = GTK_CSS_CHANGE_ANY;
 
-  if (!priv->invalid && change == 0)
+  if (!priv->invalid && change == 0 && _gtk_bitmask_is_empty (parent_changes))
     return;
 
   priv->pending_changes = 0;
   gtk_style_context_set_invalid (context, FALSE);
 
-  /* Try to avoid invalidating if we can */
-  if (change & GTK_STYLE_CONTEXT_RADICAL_CHANGE)
-    {
-      priv->relevant_changes = GTK_CSS_CHANGE_ANY;
-    }
+  info = priv->info;
+  if (info->data)
+    current = style_data_ref (info->data);
   else
+    current = NULL;
+
+  /* Try to avoid invalidating if we can */
+  if (current == NULL ||
+      gtk_style_context_needs_full_revalidate (context, change))
     {
-      if (priv->relevant_changes == GTK_CSS_CHANGE_ANY)
+      if ((priv->relevant_changes & change) & ~GTK_STYLE_CONTEXT_CACHED_CHANGE)
         {
-          GtkWidgetPath *path;
-          GtkCssMatcher matcher;
+          gtk_style_context_clear_cache (context);
+        }
+      else
+        {
+          gtk_style_context_update_cache (context, parent_changes);
+          style_info_set_data (info, NULL);
+        }
 
-          path = create_query_path (context);
-          _gtk_css_matcher_init (&matcher, path, ((GtkStyleInfo *) priv->info_stack->data)->state_flags);
+      if (current)
+        {
+          StyleData *data;
 
-          priv->relevant_changes = _gtk_style_provider_private_get_change (GTK_STYLE_PROVIDER_PRIVATE (priv->cascade),
-                                                                           &matcher);
-          priv->relevant_changes &= ~GTK_STYLE_CONTEXT_RADICAL_CHANGE;
+          gtk_style_context_start_animations (context, current->store, timestamp);
+          change &= ~GTK_CSS_CHANGE_ANIMATE;
 
-          gtk_widget_path_unref (path);
+          data = style_data_lookup (context);
+
+          changes = _gtk_css_computed_values_get_difference (data->store, current->store);
+
+          style_data_unref (current);
         }
+      else
+        {
+          changes = _gtk_bitmask_new ();
+          changes = _gtk_bitmask_invert_range (changes, 0, _gtk_css_style_property_get_n_properties ());
+        }
+    }
+  else
+    {
+      changes = _gtk_bitmask_copy (parent_changes);
+      changes = _gtk_bitmask_intersect (changes, current->store->depends_on_parent);
+      if (_gtk_bitmask_get (changes, GTK_CSS_PROPERTY_COLOR))
+        changes = _gtk_bitmask_union (changes, current->store->depends_on_color);
+      if (_gtk_bitmask_get (changes, GTK_CSS_PROPERTY_FONT_SIZE))
+        changes = _gtk_bitmask_union (changes, current->store->depends_on_font_size);
+
+      gtk_style_context_update_cache (context, parent_changes);
     }
 
-  if (priv->relevant_changes & change)
+  if (change & GTK_CSS_CHANGE_ANIMATE &&
+      gtk_style_context_is_animating (context))
     {
-      GtkStyleInfo *info = priv->info_stack->data;
-      gboolean clear_cache = ((priv->relevant_changes & change) & ~GTK_STYLE_CONTEXT_CACHED_CHANGE) != 0;
+      GtkBitmask *animation_changes;
 
-      info->data = NULL;
-      gtk_style_context_do_invalidate (context, clear_cache);
+      animation_changes = gtk_style_context_update_animations (context, timestamp);
+      changes = _gtk_bitmask_union (changes, animation_changes);
+      _gtk_bitmask_free (animation_changes);
     }
 
+  if (!_gtk_bitmask_is_empty (changes))
+    gtk_style_context_do_invalidate (context);
+
   change = _gtk_css_change_for_child (change);
   for (list = priv->children; list; list = list->next)
     {
-      _gtk_style_context_validate (list->data, change);
+      _gtk_style_context_validate (list->data, timestamp, change, changes);
     }
+
+  _gtk_bitmask_free (changes);
 }
 
 void
@@ -3369,11 +3216,15 @@ _gtk_style_context_queue_invalidate (GtkStyleContext *context,
 
   priv = context->priv;
 
-  if (priv->widget == NULL && priv->widget_path == NULL)
-    return;
-
-  priv->pending_changes |= change;
-  gtk_style_context_set_invalid (context, TRUE);
+  if (priv->widget != NULL)
+    {
+      priv->pending_changes |= change;
+      gtk_style_context_set_invalid (context, TRUE);
+    }
+  else if (priv->widget_path == NULL)
+    {
+      gtk_style_context_invalidate (context);
+    }
 }
 
 /**
@@ -3394,7 +3245,8 @@ gtk_style_context_invalidate (GtkStyleContext *context)
 {
   g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
 
-  gtk_style_context_do_invalidate (context, TRUE);
+  gtk_style_context_clear_cache (context);
+  gtk_style_context_do_invalidate (context);
 }
 
 /**
@@ -3659,10 +3511,7 @@ gtk_style_context_get_font (GtkStyleContext *context,
   priv = context->priv;
   g_return_val_if_fail (priv->widget != NULL || priv->widget_path != NULL, NULL);
 
-  gtk_style_context_save (context);
-  gtk_style_context_set_state (context, state);
-  data = style_data_lookup (context);
-  gtk_style_context_restore (context);
+  data = style_data_lookup_for_state (context, state);
 
   /* Yuck, fonts are created on-demand but we don't return a ref.
    * Do bad things to achieve this requirement */
@@ -3760,8 +3609,8 @@ gtk_render_check (GtkStyleContext *context,
                   gdouble          width,
                   gdouble          height)
 {
-  GtkStyleContextPrivate *priv;
   GtkThemingEngineClass *engine_class;
+  GtkThemingEngine *engine;
 
   g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
   g_return_if_fail (cr != NULL);
@@ -3769,15 +3618,14 @@ gtk_render_check (GtkStyleContext *context,
   if (width <= 0 || height <= 0)
     return;
 
-  priv = context->priv;
-  engine_class = GTK_THEMING_ENGINE_GET_CLASS (priv->theming_engine);
+  engine = _gtk_css_engine_value_get_engine (_gtk_style_context_peek_property (context, GTK_CSS_PROPERTY_ENGINE));
+  engine_class = GTK_THEMING_ENGINE_GET_CLASS (engine);
 
   cairo_save (cr);
 
-  store_animation_region (context, x, y, width, height);
 
-  _gtk_theming_engine_set_context (priv->theming_engine, context);
-  engine_class->render_check (priv->theming_engine, cr,
+  _gtk_theming_engine_set_context (engine, context);
+  engine_class->render_check (engine, cr,
                               x, y, width, height);
 
   cairo_restore (cr);
@@ -3811,8 +3659,8 @@ gtk_render_option (GtkStyleContext *context,
                    gdouble          width,
                    gdouble          height)
 {
-  GtkStyleContextPrivate *priv;
   GtkThemingEngineClass *engine_class;
+  GtkThemingEngine *engine;
 
   g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
   g_return_if_fail (cr != NULL);
@@ -3820,15 +3668,13 @@ gtk_render_option (GtkStyleContext *context,
   if (width <= 0 || height <= 0)
     return;
 
-  priv = context->priv;
-  engine_class = GTK_THEMING_ENGINE_GET_CLASS (priv->theming_engine);
+  engine = _gtk_css_engine_value_get_engine (_gtk_style_context_peek_property (context, GTK_CSS_PROPERTY_ENGINE));
+  engine_class = GTK_THEMING_ENGINE_GET_CLASS (engine);
 
   cairo_save (cr);
 
-  store_animation_region (context, x, y, width, height);
-
-  _gtk_theming_engine_set_context (priv->theming_engine, context);
-  engine_class->render_option (priv->theming_engine, cr,
+  _gtk_theming_engine_set_context (engine, context);
+  engine_class->render_option (engine, cr,
                                x, y, width, height);
 
   cairo_restore (cr);
@@ -3860,8 +3706,8 @@ gtk_render_arrow (GtkStyleContext *context,
                   gdouble          y,
                   gdouble          size)
 {
-  GtkStyleContextPrivate *priv;
   GtkThemingEngineClass *engine_class;
+  GtkThemingEngine *engine;
 
   g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
   g_return_if_fail (cr != NULL);
@@ -3869,18 +3715,16 @@ gtk_render_arrow (GtkStyleContext *context,
   if (size <= 0)
     return;
 
-  priv = context->priv;
-  engine_class = GTK_THEMING_ENGINE_GET_CLASS (priv->theming_engine);
+  engine = _gtk_css_engine_value_get_engine (_gtk_style_context_peek_property (context, GTK_CSS_PROPERTY_ENGINE));
+  engine_class = GTK_THEMING_ENGINE_GET_CLASS (engine);
 
   cairo_save (cr);
 
   gtk_style_context_save (context);
   gtk_style_context_add_class (context, GTK_STYLE_CLASS_ARROW);
 
-  store_animation_region (context, x, y, size, size);
-
-  _gtk_theming_engine_set_context (priv->theming_engine, context);
-  engine_class->render_arrow (priv->theming_engine, cr,
+  _gtk_theming_engine_set_context (engine, context);
+  engine_class->render_arrow (engine, cr,
                               angle, x, y, size);
 
   gtk_style_context_restore (context);
@@ -3916,8 +3760,8 @@ gtk_render_background (GtkStyleContext *context,
                        gdouble          width,
                        gdouble          height)
 {
-  GtkStyleContextPrivate *priv;
   GtkThemingEngineClass *engine_class;
+  GtkThemingEngine *engine;
 
   g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
   g_return_if_fail (cr != NULL);
@@ -3925,15 +3769,13 @@ gtk_render_background (GtkStyleContext *context,
   if (width <= 0 || height <= 0)
     return;
 
-  priv = context->priv;
-  engine_class = GTK_THEMING_ENGINE_GET_CLASS (priv->theming_engine);
+  engine = _gtk_css_engine_value_get_engine (_gtk_style_context_peek_property (context, GTK_CSS_PROPERTY_ENGINE));
+  engine_class = GTK_THEMING_ENGINE_GET_CLASS (engine);
 
   cairo_save (cr);
 
-  store_animation_region (context, x, y, width, height);
-
-  _gtk_theming_engine_set_context (priv->theming_engine, context);
-  engine_class->render_background (priv->theming_engine, cr, x, y, width, height);
+  _gtk_theming_engine_set_context (engine, context);
+  engine_class->render_background (engine, cr, x, y, width, height);
 
   cairo_restore (cr);
 }
@@ -3969,8 +3811,8 @@ gtk_render_frame (GtkStyleContext *context,
                   gdouble          width,
                   gdouble          height)
 {
-  GtkStyleContextPrivate *priv;
   GtkThemingEngineClass *engine_class;
+  GtkThemingEngine *engine;
 
   g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
   g_return_if_fail (cr != NULL);
@@ -3978,15 +3820,13 @@ gtk_render_frame (GtkStyleContext *context,
   if (width <= 0 || height <= 0)
     return;
 
-  priv = context->priv;
-  engine_class = GTK_THEMING_ENGINE_GET_CLASS (priv->theming_engine);
+  engine = _gtk_css_engine_value_get_engine (_gtk_style_context_peek_property (context, GTK_CSS_PROPERTY_ENGINE));
+  engine_class = GTK_THEMING_ENGINE_GET_CLASS (engine);
 
   cairo_save (cr);
 
-  store_animation_region (context, x, y, width, height);
-
-  _gtk_theming_engine_set_context (priv->theming_engine, context);
-  engine_class->render_frame (priv->theming_engine, cr, x, y, width, height);
+  _gtk_theming_engine_set_context (engine, context);
+  engine_class->render_frame (engine, cr, x, y, width, height);
 
   cairo_restore (cr);
 }
@@ -4019,8 +3859,8 @@ gtk_render_expander (GtkStyleContext *context,
                      gdouble          width,
                      gdouble          height)
 {
-  GtkStyleContextPrivate *priv;
   GtkThemingEngineClass *engine_class;
+  GtkThemingEngine *engine;
 
   g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
   g_return_if_fail (cr != NULL);
@@ -4028,15 +3868,13 @@ gtk_render_expander (GtkStyleContext *context,
   if (width <= 0 || height <= 0)
     return;
 
-  priv = context->priv;
-  engine_class = GTK_THEMING_ENGINE_GET_CLASS (priv->theming_engine);
+  engine = _gtk_css_engine_value_get_engine (_gtk_style_context_peek_property (context, GTK_CSS_PROPERTY_ENGINE));
+  engine_class = GTK_THEMING_ENGINE_GET_CLASS (engine);
 
   cairo_save (cr);
 
-  store_animation_region (context, x, y, width, height);
-
-  _gtk_theming_engine_set_context (priv->theming_engine, context);
-  engine_class->render_expander (priv->theming_engine, cr, x, y, width, height);
+  _gtk_theming_engine_set_context (engine, context);
+  engine_class->render_expander (engine, cr, x, y, width, height);
 
   cairo_restore (cr);
 }
@@ -4066,8 +3904,8 @@ gtk_render_focus (GtkStyleContext *context,
                   gdouble          width,
                   gdouble          height)
 {
-  GtkStyleContextPrivate *priv;
   GtkThemingEngineClass *engine_class;
+  GtkThemingEngine *engine;
 
   g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
   g_return_if_fail (cr != NULL);
@@ -4075,15 +3913,13 @@ gtk_render_focus (GtkStyleContext *context,
   if (width <= 0 || height <= 0)
     return;
 
-  priv = context->priv;
-  engine_class = GTK_THEMING_ENGINE_GET_CLASS (priv->theming_engine);
+  engine = _gtk_css_engine_value_get_engine (_gtk_style_context_peek_property (context, GTK_CSS_PROPERTY_ENGINE));
+  engine_class = GTK_THEMING_ENGINE_GET_CLASS (engine);
 
   cairo_save (cr);
 
-  store_animation_region (context, x, y, width, height);
-
-  _gtk_theming_engine_set_context (priv->theming_engine, context);
-  engine_class->render_focus (priv->theming_engine, cr, x, y, width, height);
+  _gtk_theming_engine_set_context (engine, context);
+  engine_class->render_focus (engine, cr, x, y, width, height);
 
   cairo_restore (cr);
 }
@@ -4107,29 +3943,23 @@ gtk_render_layout (GtkStyleContext *context,
                    gdouble          y,
                    PangoLayout     *layout)
 {
-  GtkStyleContextPrivate *priv;
   GtkThemingEngineClass *engine_class;
+  GtkThemingEngine *engine;
   PangoRectangle extents;
 
   g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
   g_return_if_fail (PANGO_IS_LAYOUT (layout));
   g_return_if_fail (cr != NULL);
 
-  priv = context->priv;
-  engine_class = GTK_THEMING_ENGINE_GET_CLASS (priv->theming_engine);
+  engine = _gtk_css_engine_value_get_engine (_gtk_style_context_peek_property (context, GTK_CSS_PROPERTY_ENGINE));
+  engine_class = GTK_THEMING_ENGINE_GET_CLASS (engine);
 
   cairo_save (cr);
 
   pango_layout_get_extents (layout, &extents, NULL);
 
-  store_animation_region (context,
-                          x + extents.x,
-                          y + extents.y,
-                          extents.width,
-                          extents.height);
-
-  _gtk_theming_engine_set_context (priv->theming_engine, context);
-  engine_class->render_layout (priv->theming_engine, cr, x, y, layout);
+  _gtk_theming_engine_set_context (engine, context);
+  engine_class->render_layout (engine, cr, x, y, layout);
 
   cairo_restore (cr);
 }
@@ -4155,19 +3985,19 @@ gtk_render_line (GtkStyleContext *context,
                  gdouble          x1,
                  gdouble          y1)
 {
-  GtkStyleContextPrivate *priv;
   GtkThemingEngineClass *engine_class;
+  GtkThemingEngine *engine;
 
   g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
   g_return_if_fail (cr != NULL);
 
-  priv = context->priv;
-  engine_class = GTK_THEMING_ENGINE_GET_CLASS (priv->theming_engine);
+  engine = _gtk_css_engine_value_get_engine (_gtk_style_context_peek_property (context, GTK_CSS_PROPERTY_ENGINE));
+  engine_class = GTK_THEMING_ENGINE_GET_CLASS (engine);
 
   cairo_save (cr);
 
-  _gtk_theming_engine_set_context (priv->theming_engine, context);
-  engine_class->render_line (priv->theming_engine, cr, x0, y0, x1, y1);
+  _gtk_theming_engine_set_context (engine, context);
+  engine_class->render_line (engine, cr, x0, y0, x1, y1);
 
   cairo_restore (cr);
 }
@@ -4202,8 +4032,8 @@ gtk_render_slider (GtkStyleContext *context,
                    gdouble          height,
                    GtkOrientation   orientation)
 {
-  GtkStyleContextPrivate *priv;
   GtkThemingEngineClass *engine_class;
+  GtkThemingEngine *engine;
 
   g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
   g_return_if_fail (cr != NULL);
@@ -4211,15 +4041,13 @@ gtk_render_slider (GtkStyleContext *context,
   if (width <= 0 || height <= 0)
     return;
 
-  priv = context->priv;
-  engine_class = GTK_THEMING_ENGINE_GET_CLASS (priv->theming_engine);
+  engine = _gtk_css_engine_value_get_engine (_gtk_style_context_peek_property (context, GTK_CSS_PROPERTY_ENGINE));
+  engine_class = GTK_THEMING_ENGINE_GET_CLASS (engine);
 
   cairo_save (cr);
 
-  store_animation_region (context, x, y, width, height);
-
-  _gtk_theming_engine_set_context (priv->theming_engine, context);
-  engine_class->render_slider (priv->theming_engine, cr, x, y, width, height, orientation);
+  _gtk_theming_engine_set_context (engine, context);
+  engine_class->render_slider (engine, cr, x, y, width, height, orientation);
 
   cairo_restore (cr);
 }
@@ -4259,8 +4087,8 @@ gtk_render_frame_gap (GtkStyleContext *context,
                       gdouble          xy0_gap,
                       gdouble          xy1_gap)
 {
-  GtkStyleContextPrivate *priv;
   GtkThemingEngineClass *engine_class;
+  GtkThemingEngine *engine;
 
   g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
   g_return_if_fail (cr != NULL);
@@ -4276,15 +4104,13 @@ gtk_render_frame_gap (GtkStyleContext *context,
   else
     g_return_if_fail (xy1_gap <= width);
 
-  priv = context->priv;
-  engine_class = GTK_THEMING_ENGINE_GET_CLASS (priv->theming_engine);
+  engine = _gtk_css_engine_value_get_engine (_gtk_style_context_peek_property (context, GTK_CSS_PROPERTY_ENGINE));
+  engine_class = GTK_THEMING_ENGINE_GET_CLASS (engine);
 
   cairo_save (cr);
 
-  store_animation_region (context, x, y, width, height);
-
-  _gtk_theming_engine_set_context (priv->theming_engine, context);
-  engine_class->render_frame_gap (priv->theming_engine, cr,
+  _gtk_theming_engine_set_context (engine, context);
+  engine_class->render_frame_gap (engine, cr,
                                   x, y, width, height, gap_side,
                                   xy0_gap, xy1_gap);
 
@@ -4321,8 +4147,8 @@ gtk_render_extension (GtkStyleContext *context,
                       gdouble          height,
                       GtkPositionType  gap_side)
 {
-  GtkStyleContextPrivate *priv;
   GtkThemingEngineClass *engine_class;
+  GtkThemingEngine *engine;
 
   g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
   g_return_if_fail (cr != NULL);
@@ -4330,15 +4156,13 @@ gtk_render_extension (GtkStyleContext *context,
   if (width <= 0 || height <= 0)
     return;
 
-  priv = context->priv;
-  engine_class = GTK_THEMING_ENGINE_GET_CLASS (priv->theming_engine);
+  engine = _gtk_css_engine_value_get_engine (_gtk_style_context_peek_property (context, GTK_CSS_PROPERTY_ENGINE));
+  engine_class = GTK_THEMING_ENGINE_GET_CLASS (engine);
 
   cairo_save (cr);
 
-  store_animation_region (context, x, y, width, height);
-
-  _gtk_theming_engine_set_context (priv->theming_engine, context);
-  engine_class->render_extension (priv->theming_engine, cr, x, y, width, height, gap_side);
+  _gtk_theming_engine_set_context (engine, context);
+  engine_class->render_extension (engine, cr, x, y, width, height, gap_side);
 
   cairo_restore (cr);
 }
@@ -4371,8 +4195,8 @@ gtk_render_handle (GtkStyleContext *context,
                    gdouble          width,
                    gdouble          height)
 {
-  GtkStyleContextPrivate *priv;
   GtkThemingEngineClass *engine_class;
+  GtkThemingEngine *engine;
 
   g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
   g_return_if_fail (cr != NULL);
@@ -4380,15 +4204,13 @@ gtk_render_handle (GtkStyleContext *context,
   if (width <= 0 || height <= 0)
     return;
 
-  priv = context->priv;
-  engine_class = GTK_THEMING_ENGINE_GET_CLASS (priv->theming_engine);
+  engine = _gtk_css_engine_value_get_engine (_gtk_style_context_peek_property (context, GTK_CSS_PROPERTY_ENGINE));
+  engine_class = GTK_THEMING_ENGINE_GET_CLASS (engine);
 
   cairo_save (cr);
 
-  store_animation_region (context, x, y, width, height);
-
-  _gtk_theming_engine_set_context (priv->theming_engine, context);
-  engine_class->render_handle (priv->theming_engine, cr, x, y, width, height);
+  _gtk_theming_engine_set_context (engine, context);
+  engine_class->render_handle (engine, cr, x, y, width, height);
 
   cairo_restore (cr);
 }
@@ -4416,8 +4238,8 @@ gtk_render_activity (GtkStyleContext *context,
                      gdouble          width,
                      gdouble          height)
 {
-  GtkStyleContextPrivate *priv;
   GtkThemingEngineClass *engine_class;
+  GtkThemingEngine *engine;
 
   g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
   g_return_if_fail (cr != NULL);
@@ -4425,15 +4247,13 @@ gtk_render_activity (GtkStyleContext *context,
   if (width <= 0 || height <= 0)
     return;
 
-  priv = context->priv;
-  engine_class = GTK_THEMING_ENGINE_GET_CLASS (priv->theming_engine);
+  engine = _gtk_css_engine_value_get_engine (_gtk_style_context_peek_property (context, GTK_CSS_PROPERTY_ENGINE));
+  engine_class = GTK_THEMING_ENGINE_GET_CLASS (engine);
 
   cairo_save (cr);
 
-  store_animation_region (context, x, y, width, height);
-
-  _gtk_theming_engine_set_context (priv->theming_engine, context);
-  engine_class->render_activity (priv->theming_engine, cr, x, y, width, height);
+  _gtk_theming_engine_set_context (engine, context);
+  engine_class->render_activity (engine, cr, x, y, width, height);
 
   cairo_restore (cr);
 }
@@ -4457,18 +4277,18 @@ gtk_render_icon_pixbuf (GtkStyleContext     *context,
                         const GtkIconSource *source,
                         GtkIconSize          size)
 {
-  GtkStyleContextPrivate *priv;
   GtkThemingEngineClass *engine_class;
+  GtkThemingEngine *engine;
 
   g_return_val_if_fail (GTK_IS_STYLE_CONTEXT (context), NULL);
   g_return_val_if_fail (size > GTK_ICON_SIZE_INVALID || size == -1, NULL);
   g_return_val_if_fail (source != NULL, NULL);
 
-  priv = context->priv;
-  engine_class = GTK_THEMING_ENGINE_GET_CLASS (priv->theming_engine);
+  engine = _gtk_css_engine_value_get_engine (_gtk_style_context_peek_property (context, GTK_CSS_PROPERTY_ENGINE));
+  engine_class = GTK_THEMING_ENGINE_GET_CLASS (engine);
 
-  _gtk_theming_engine_set_context (priv->theming_engine, context);
-  return engine_class->render_icon_pixbuf (priv->theming_engine, source, size);
+  _gtk_theming_engine_set_context (engine, context);
+  return engine_class->render_icon_pixbuf (engine, source, size);
 }
 
 /**
@@ -4490,24 +4310,19 @@ gtk_render_icon (GtkStyleContext *context,
                  gdouble          x,
                  gdouble          y)
 {
-  GtkStyleContextPrivate *priv;
   GtkThemingEngineClass *engine_class;
+  GtkThemingEngine *engine;
 
   g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
   g_return_if_fail (cr != NULL);
 
-  priv = context->priv;
-  engine_class = GTK_THEMING_ENGINE_GET_CLASS (priv->theming_engine);
+  engine = _gtk_css_engine_value_get_engine (_gtk_style_context_peek_property (context, GTK_CSS_PROPERTY_ENGINE));
+  engine_class = GTK_THEMING_ENGINE_GET_CLASS (engine);
 
   cairo_save (cr);
 
-  store_animation_region (context,
-                          x, y,
-                          gdk_pixbuf_get_width (pixbuf),
-                          gdk_pixbuf_get_height (pixbuf));
-
-  _gtk_theming_engine_set_context (priv->theming_engine, context);
-  engine_class->render_icon (priv->theming_engine, cr, pixbuf, x, y);
+  _gtk_theming_engine_set_context (engine, context);
+  engine_class->render_icon (engine, cr, pixbuf, x, y);
 
   cairo_restore (cr);
 }