diff --git a/chrome/browser/ui/libgtkui/gtk_ui.cc b/chrome/browser/ui/libgtkui/gtk_ui.cc
index fc25e5c..4cd4711 100644
--- a/chrome/browser/ui/libgtkui/gtk_ui.cc
+++ b/chrome/browser/ui/libgtkui/gtk_ui.cc
@@ -169,12 +169,11 @@
     g_object_unref(pixbuf);
     g_object_unref(pixmap);
 #else
-    gtk_widget_draw(button, cr);
-
-    // There's probably a better way to do this
+    GtkStyleContext* context = gtk_widget_get_style_context(button);
+    gtk_render_background(context, cr, 0, 0, width, height);
+    gtk_render_frame(context, cr, 0, 0, width, height);
     if (focus_)
-      gtk_render_focus(gtk_widget_get_style_context(button), cr, 0, 0, width,
-                       height);
+      gtk_render_focus(context, cr, 0, 0, width, height);
 #endif
 
     cairo_destroy(cr);
diff --git a/chrome/browser/ui/libgtkui/gtk_util.cc b/chrome/browser/ui/libgtkui/gtk_util.cc
index 55bd85f..d8c09f1 100644
--- a/chrome/browser/ui/libgtkui/gtk_util.cc
+++ b/chrome/browser/ui/libgtkui/gtk_util.cc
@@ -207,6 +207,60 @@
 }
 
 #if GTK_MAJOR_VERSION > 2
+CairoSurface::CairoSurface(SkBitmap& bitmap)
+    : surface_(cairo_image_surface_create_for_data(
+          static_cast<unsigned char*>(bitmap.getAddr(0, 0)),
+          CAIRO_FORMAT_ARGB32,
+          bitmap.width(),
+          bitmap.height(),
+          cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, bitmap.width()))),
+      cairo_(cairo_create(surface_)) {}
+
+CairoSurface::CairoSurface(const gfx::Size& size)
+    : surface_(cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
+                                          size.width(),
+                                          size.height())),
+      cairo_(cairo_create(surface_)) {}
+
+CairoSurface::~CairoSurface() {
+  cairo_destroy(cairo_);
+  cairo_surface_destroy(surface_);
+}
+
+SkColor CairoSurface::GetAveragePixelValue(bool only_frame_pixels) {
+  int num_samples = 0;
+  long a = 0, r = 0, g = 0, b = 0;
+  SkColor* data =
+      reinterpret_cast<SkColor*>(cairo_image_surface_get_data(surface_));
+  int width = cairo_image_surface_get_width(surface_);
+  int height = cairo_image_surface_get_height(surface_);
+  auto accumulate = [&](int x, int y) mutable {
+    SkColor color = data[x * width + y];
+    int alpha = SkColorGetA(color);
+    a += alpha;
+    r += alpha * SkColorGetR(color);
+    g += alpha * SkColorGetG(color);
+    b += alpha * SkColorGetB(color);
+    num_samples++;
+  };
+  if (width == 1 || height == 1 || !only_frame_pixels) {
+    // Count every pixel in the surface.
+    for (int x = 0; x < width; x++)
+      for (int y = 0; y < height; y++)
+        accumulate(x, y);
+  } else {
+    // Count the pixels in the top and bottom rows.
+    for (int x = 0; x < width; x++)
+      for (int y : {0, height - 1})
+        accumulate(x, y);
+    // Count the pixels in the left and right columns.
+    for (int x : {0, width - 1})
+      for (int y = 1; y < height - 1; y++)
+        accumulate(x, y);
+  }
+  return SkColorSetARGB(a / num_samples, r / a, g / a, b / a);
+}
+
 bool GtkVersionCheck(int major, int minor, int micro) {
   static int actual_major = gtk_get_major_version();
   if (actual_major > major)
@@ -227,8 +281,8 @@
     return false;
 }
 
-ScopedStyleContext AppendNode(GtkStyleContext* context,
-                              const std::string& css_node) {
+ScopedStyleContext AppendCssNodeToStyleContext(GtkStyleContext* context,
+                                               const std::string& css_node) {
   GtkWidgetPath* path =
       context ? gtk_widget_path_copy(gtk_style_context_get_path(context))
               : gtk_widget_path_new();
@@ -332,12 +386,12 @@
 ScopedStyleContext GetStyleContextFromCss(const char* css_selector) {
   // Prepend "GtkWindow.background" to the selector since all widgets must live
   // in a window, but we don't want to specify that every time.
-  auto context = AppendNode(nullptr, "GtkWindow.background");
+  auto context = AppendCssNodeToStyleContext(nullptr, "GtkWindow.background");
 
   for (const auto& widget_type :
        base::SplitString(css_selector, base::kWhitespaceASCII,
                          base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY)) {
-    context = AppendNode(context, widget_type);
+    context = AppendCssNodeToStyleContext(context, widget_type);
   }
   return context;
 }
@@ -405,31 +459,6 @@
   ApplyCssToContext(context, provider);
 }
 
-// A 1x1 cairo surface that GTK can render into.
-class PixelSurface {
- public:
-  PixelSurface()
-      : surface_(cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 1, 1)),
-        cairo_(cairo_create(surface_)) {}
-
-  ~PixelSurface() {
-    cairo_destroy(cairo_);
-    cairo_surface_destroy(surface_);
-  }
-
-  // Get the drawing context for GTK to use.
-  cairo_t* cairo() { return cairo_; }
-
-  // Get the color value of the single pixel.
-  SkColor GetPixelValue() {
-    return *reinterpret_cast<SkColor*>(cairo_image_surface_get_data(surface_));
-  }
-
- private:
-  cairo_surface_t* surface_;
-  cairo_t* cairo_;
-};
-
 void RenderBackground(const gfx::Size& size,
                       cairo_t* cr,
                       GtkStyleContext* context) {
@@ -439,6 +468,16 @@
   gtk_render_background(context, cr, 0, 0, size.width(), size.height());
 }
 
+gfx::Size GetMinimumWidgetSize(GtkStyleContext* context) {
+  if (!GtkVersionCheck(3, 20))
+    return gfx::Size(1, 1);
+  int w = 1, h = 1;
+  gtk_style_context_get(context, gtk_style_context_get_state(context),
+                        "min-width", &w, "min-height", &h, NULL);
+  DCHECK(w >= 0 && h >= 0);
+  return gfx::Size(w ? w : 1, h ? h : 1);
+}
+
 SkColor GetBgColor(const char* css_selector) {
   // Backgrounds are more general than solid colors (eg. gradients),
   // but chromium requires us to boil this down to one color.  We
@@ -448,9 +487,10 @@
   // removing any borders, and hope that we get a good color.
   auto context = GetStyleContextFromCss(css_selector);
   RemoveBorders(context);
-  PixelSurface surface;
-  RenderBackground(gfx::Size(1, 1), surface.cairo(), context);
-  return surface.GetPixelValue();
+  gfx::Size size = GetMinimumWidgetSize(context);
+  CairoSurface surface(size);
+  RenderBackground(size, surface.cairo(), context);
+  return surface.GetAveragePixelValue(false);
 }
 
 SkColor GetBorderColor(const char* css_selector) {
@@ -470,10 +510,11 @@
   }
 
   AddBorders(context);
-  PixelSurface surface;
-  RenderBackground(gfx::Size(1, 1), surface.cairo(), context);
+  gfx::Size size = GetMinimumWidgetSize(context);
+  CairoSurface surface(size);
+  RenderBackground(size, surface.cairo(), context);
   gtk_render_frame(context, surface.cairo(), 0, 0, 1, 1);
-  return surface.GetPixelValue();
+  return surface.GetAveragePixelValue(true);
 }
 
 SkColor GetSeparatorColor(const char* css_selector) {
diff --git a/chrome/browser/ui/libgtkui/gtk_util.h b/chrome/browser/ui/libgtkui/gtk_util.h
index 91f9520..d71e09a 100644
--- a/chrome/browser/ui/libgtkui/gtk_util.h
+++ b/chrome/browser/ui/libgtkui/gtk_util.h
@@ -83,6 +83,31 @@
 #define GTK_STATE_FLAG_VISITED static_cast<GtkStateFlags>(1 << 10)
 #define GTK_STATE_FLAG_CHECKED static_cast<GtkStateFlags>(1 << 11)
 
+class CairoSurface {
+ public:
+  // Attaches a cairo surface to an SkBitmap so that GTK can render
+  // into it.  |bitmap| must outlive this CairoSurface.
+  explicit CairoSurface(SkBitmap& bitmap);
+
+  // Creates a new cairo surface with the given size.  The memory for
+  // this surface is deallocated when this CairoSurface is destroyed.
+  explicit CairoSurface(const gfx::Size& size);
+
+  ~CairoSurface();
+
+  // Get the drawing context for GTK to use.
+  cairo_t* cairo() { return cairo_; }
+
+  // If |only_frame_pixels| is false, returns the average of all
+  // pixels in the surface, otherwise returns the average of only the
+  // edge pixels.
+  SkColor GetAveragePixelValue(bool only_frame_pixels);
+
+ private:
+  cairo_surface_t* surface_;
+  cairo_t* cairo_;
+};
+
 // Returns true iff the runtime version of Gtk used meets
 // |major|.|minor|.|micro|.
 bool GtkVersionCheck(int major, int minor = 0, int micro = 0);
@@ -131,6 +156,12 @@
 
 typedef ScopedGObject<GtkStyleContext> ScopedStyleContext;
 
+// If |context| is NULL, creates a new top-level style context
+// specified by parsing |css_node|.  Otherwise, creates the child
+// context with |context| as the parent.
+ScopedStyleContext AppendCssNodeToStyleContext(GtkStyleContext* context,
+                                               const std::string& css_node);
+
 // Parses |css_selector| into a GtkStyleContext.  The format is a
 // sequence of whitespace-separated objects.  Each object may have at
 // most one object name at the beginning of the string, and any number
diff --git a/chrome/browser/ui/libgtkui/native_theme_gtk3.cc b/chrome/browser/ui/libgtkui/native_theme_gtk3.cc
index a593b14..d274241 100644
--- a/chrome/browser/ui/libgtkui/native_theme_gtk3.cc
+++ b/chrome/browser/ui/libgtkui/native_theme_gtk3.cc
@@ -35,11 +35,8 @@
   bitmap.allocN32Pixels(size.width(), size.height());
   bitmap.eraseColor(0);
 
-  cairo_surface_t* surface = cairo_image_surface_create_for_data(
-      static_cast<unsigned char*>(bitmap.getAddr(0, 0)), CAIRO_FORMAT_ARGB32,
-      size.width(), size.height(),
-      cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, size.width()));
-  cairo_t* cr = cairo_create(surface);
+  CairoSurface surface(bitmap);
+  cairo_t* cr = surface.cairo();
 
   switch (bg_mode) {
     case BG_RENDER_NORMAL:
@@ -53,8 +50,6 @@
   }
   if (render_frame)
     gtk_render_frame(context, cr, 0, 0, size.width(), size.height());
-  cairo_destroy(cr);
-  cairo_surface_destroy(surface);
   return bitmap;
 }
 
@@ -365,6 +360,9 @@
   g_type_class_unref(g_type_class_ref(gtk_text_view_get_type()));
   g_type_class_unref(g_type_class_ref(gtk_separator_get_type()));
   g_type_class_unref(g_type_class_ref(gtk_menu_bar_get_type()));
+  g_type_class_unref(g_type_class_ref(gtk_arrow_get_type()));
+  g_type_class_unref(g_type_class_ref(gtk_scrolled_window_get_type()));
+  g_type_class_unref(g_type_class_ref(gtk_range_get_type()));
 
   g_signal_connect_after(gtk_settings_get_default(), "notify::gtk-theme-name",
                          G_CALLBACK(OnThemeChanged), this);
@@ -387,6 +385,92 @@
   return color;
 }
 
+void NativeThemeGtk3::PaintArrowButton(SkCanvas* canvas,
+                                       const gfx::Rect& rect,
+                                       Part direction,
+                                       State state) const {
+  auto context = GetStyleContextFromCss(
+      GtkVersionCheck(3, 20)
+          ? "GtkScrollbar#scrollbar #contents GtkButton#button"
+          : "GtkRange.scrollbar.button");
+  GtkStateFlags state_flags = StateToStateFlags(state);
+  gtk_style_context_set_state(context, state_flags);
+  SkBitmap bitmap =
+      GetWidgetBitmap(rect.size(), context, BG_RENDER_NORMAL, true);
+
+  gdouble angle = 0;
+  switch (direction) {
+    case kScrollbarUpArrow:
+      angle = 0;
+      gtk_style_context_add_class(context, GTK_STYLE_CLASS_TOP);
+      break;
+    case kScrollbarRightArrow:
+      angle = G_PI / 2;
+      gtk_style_context_add_class(context, GTK_STYLE_CLASS_RIGHT);
+      break;
+    case kScrollbarDownArrow:
+      angle = G_PI;
+      gtk_style_context_add_class(context, GTK_STYLE_CLASS_BOTTOM);
+      break;
+    case kScrollbarLeftArrow:
+      angle = 3 * G_PI / 2;
+      gtk_style_context_add_class(context, GTK_STYLE_CLASS_LEFT);
+      break;
+    default:
+      NOTREACHED();
+  }
+
+  CairoSurface surface(bitmap);
+
+  auto arrow_context = AppendCssNodeToStyleContext(context, "GtkArrow#arrow");
+  gfloat arrow_scaling = 0.7;  // Default scaling for a GtkArrow
+  gtk_style_context_get_style(arrow_context, "arrow-scaling", &arrow_scaling,
+                              nullptr);
+  const double w = rect.width();
+  const double h = rect.height();
+  const double arrow_size = std::min(w, h) * arrow_scaling;
+  gtk_render_arrow(arrow_context, surface.cairo(), angle, (w - arrow_size) / 2,
+                   (h - arrow_size) / 2, arrow_size);
+  canvas->drawBitmap(bitmap, rect.x(), rect.y());
+}
+
+void NativeThemeGtk3::PaintScrollbarTrack(
+    SkCanvas* canvas,
+    Part part,
+    State state,
+    const ScrollbarTrackExtraParams& extra_params,
+    const gfx::Rect& rect) const {
+  PaintWidget(canvas, rect,
+              GetStyleContextFromCss(GtkVersionCheck(3, 20)
+                  ? "GtkScrollbar#scrollbar #contents #trough"
+                  : "GtkScrollbar.scrollbar.trough"),
+                  BG_RENDER_NORMAL,true);
+}
+
+void NativeThemeGtk3::PaintScrollbarThumb(
+    SkCanvas* canvas,
+    Part part,
+    State state,
+    const gfx::Rect& rect,
+    NativeTheme::ScrollbarOverlayColorTheme theme) const {
+  auto context = GetStyleContextFromCss(
+      GtkVersionCheck(3, 20)
+          ? "GtkScrollbar#scrollbar #contents #trough #slider"
+          : "GtkScrollbar.scrollbar.slider");
+  gtk_style_context_set_state(context, StateToStateFlags(state));
+  PaintWidget(canvas, rect, context, BG_RENDER_NORMAL, true);
+}
+
+void NativeThemeGtk3::PaintScrollbarCorner(SkCanvas* canvas,
+                                           State state,
+                                           const gfx::Rect& rect) const {
+  auto context = GetStyleContextFromCss(
+      GtkVersionCheck(3, 19, 2)
+          ? "GtkScrolledWindow#scrolledwindow #junction"
+          : "GtkScrolledWindow.scrolledwindow.scrollbars-junction");
+  PaintWidget(canvas, rect, context, BG_RENDER_NORMAL, true);
+}
+
 void NativeThemeGtk3::PaintMenuPopupBackground(
     SkCanvas* canvas,
     const gfx::Size& size,
diff --git a/chrome/browser/ui/libgtkui/native_theme_gtk3.h b/chrome/browser/ui/libgtkui/native_theme_gtk3.h
index 77de310..206481cd 100644
--- a/chrome/browser/ui/libgtkui/native_theme_gtk3.h
+++ b/chrome/browser/ui/libgtkui/native_theme_gtk3.h
@@ -21,6 +21,24 @@
 
   // Overridden from ui::NativeThemeBase:
   SkColor GetSystemColor(ColorId color_id) const override;
+  void PaintArrowButton(SkCanvas* canvas,
+                        const gfx::Rect& rect,
+                        Part direction,
+                        State state) const override;
+  void PaintScrollbarTrack(SkCanvas* canvas,
+                           Part part,
+                           State state,
+                           const ScrollbarTrackExtraParams& extra_params,
+                           const gfx::Rect& rect) const override;
+  void PaintScrollbarThumb(
+      SkCanvas* canvas,
+      Part part,
+      State state,
+      const gfx::Rect& rect,
+      NativeTheme::ScrollbarOverlayColorTheme theme) const override;
+  void PaintScrollbarCorner(SkCanvas* canvas,
+                            State state,
+                            const gfx::Rect& rect) const override;
   void PaintMenuPopupBackground(
       SkCanvas* canvas,
       const gfx::Size& size,
diff --git a/chromeos/CHROMEOS_LKGM b/chromeos/CHROMEOS_LKGM
index 19f980b..1561b67 100644
--- a/chromeos/CHROMEOS_LKGM
+++ b/chromeos/CHROMEOS_LKGM
@@ -1 +1 @@
-9224.0.0
\ No newline at end of file
+9227.0.0
\ No newline at end of file
diff --git a/components/search_engines/template_url.cc b/components/search_engines/template_url.cc
index dcaafd378..0a457d1 100644
--- a/components/search_engines/template_url.cc
+++ b/components/search_engines/template_url.cc
@@ -67,9 +67,6 @@
 // Used if the output encoding parameter is required.
 const char kOutputEncodingType[] = "UTF-8";
 
-constexpr char kGoogleInstantExtendedEnabledKey[] =
-    "{google:instantExtendedEnabledKey}";
-
 // Attempts to encode |terms| and |original_query| in |encoding| and escape
 // them.  |terms| may be escaped as path or query depending on |is_in_query|;
 // |original_query| is always escaped as query.  Returns whether the encoding
@@ -163,22 +160,6 @@
       (*(param.rbegin()) == kEndParameter);
 }
 
-// Special case for search_terms_replacement_key comparison, because of
-// its special initialization in TemplateUrl constructor.
-bool SearchTermsReplacementKeysMatch(
-    const std::string& search_terms_replacement_key1,
-    const std::string& search_terms_replacement_key2) {
-  if (search_terms_replacement_key1 == search_terms_replacement_key2)
-    return true;
-  if (search_terms_replacement_key1 == google_util::kInstantExtendedAPIParam &&
-      search_terms_replacement_key2 == kGoogleInstantExtendedEnabledKey)
-    return true;
-  if (search_terms_replacement_key2 == google_util::kInstantExtendedAPIParam &&
-      search_terms_replacement_key1 == kGoogleInstantExtendedEnabledKey)
-    return true;
-  return false;
-}
-
 }  // namespace
 
 
@@ -624,7 +605,7 @@
   } else if (parameter == "google:instantExtendedEnabledParameter") {
     replacements->push_back(Replacement(GOOGLE_INSTANT_EXTENDED_ENABLED,
                                         start));
-  } else if (parameter == kGoogleInstantExtendedEnabledKey) {
+  } else if (parameter == "google:instantExtendedEnabledKey") {
     url->insert(start, google_util::kInstantExtendedAPIParam);
   } else if (parameter == "google:iOSSearchLanguage") {
     replacements->push_back(Replacement(GOOGLE_IOS_SEARCH_LANGUAGE, start));
@@ -1177,8 +1158,10 @@
   ResizeURLRefVector();
   SetPrepopulateId(data_.prepopulate_id);
 
-  if (data_.search_terms_replacement_key == kGoogleInstantExtendedEnabledKey)
+  if (data_.search_terms_replacement_key ==
+      "{google:instantExtendedEnabledKey}") {
     data_.search_terms_replacement_key = google_util::kInstantExtendedAPIParam;
+  }
 }
 
 TemplateURL::~TemplateURL() {
@@ -1223,23 +1206,23 @@
     return !t_url && !data;
 
   return (t_url->short_name() == data->short_name()) &&
-         t_url->HasSameKeywordAs(*data, search_terms_data) &&
-         (t_url->url() == data->url()) &&
-         (t_url->suggestions_url() == data->suggestions_url) &&
-         (t_url->instant_url() == data->instant_url) &&
-         (t_url->image_url() == data->image_url) &&
-         (t_url->new_tab_url() == data->new_tab_url) &&
-         (t_url->search_url_post_params() == data->search_url_post_params) &&
-         (t_url->suggestions_url_post_params() ==
+      t_url->HasSameKeywordAs(*data, search_terms_data) &&
+      (t_url->url() == data->url()) &&
+      (t_url->suggestions_url() == data->suggestions_url) &&
+      (t_url->instant_url() == data->instant_url) &&
+      (t_url->image_url() == data->image_url) &&
+      (t_url->new_tab_url() == data->new_tab_url) &&
+      (t_url->search_url_post_params() == data->search_url_post_params) &&
+      (t_url->suggestions_url_post_params() ==
           data->suggestions_url_post_params) &&
-         (t_url->instant_url_post_params() == data->instant_url_post_params) &&
-         (t_url->image_url_post_params() == data->image_url_post_params) &&
-         (t_url->favicon_url() == data->favicon_url) &&
-         (t_url->safe_for_autoreplace() == data->safe_for_autoreplace) &&
-         (t_url->input_encodings() == data->input_encodings) &&
-         (t_url->alternate_urls() == data->alternate_urls) &&
-         SearchTermsReplacementKeysMatch(t_url->search_terms_replacement_key(),
-                                         data->search_terms_replacement_key);
+      (t_url->instant_url_post_params() == data->instant_url_post_params) &&
+      (t_url->image_url_post_params() == data->image_url_post_params) &&
+      (t_url->favicon_url() == data->favicon_url) &&
+      (t_url->safe_for_autoreplace() == data->safe_for_autoreplace) &&
+      (t_url->input_encodings() == data->input_encodings) &&
+      (t_url->alternate_urls() == data->alternate_urls) &&
+      (t_url->search_terms_replacement_key() ==
+          data->search_terms_replacement_key);
 }
 
 base::string16 TemplateURL::AdjustedShortNameForLocaleDirection() const {
diff --git a/components/search_engines/template_url_unittest.cc b/components/search_engines/template_url_unittest.cc
index 123e78b..47630f3 100644
--- a/components/search_engines/template_url_unittest.cc
+++ b/components/search_engines/template_url_unittest.cc
@@ -1868,25 +1868,3 @@
 
   search_terms_data_.set_google_base_url("http://www.google.com/");
 }
-
-// Test that TemplateURL object created with settings for google engine
-// matches its TemplateURLData.
-TEST_F(TemplateURLTest, MatchesData) {
-  TemplateURLData data;
-  data.SetURL("{google:baseURL}search?q={searchTerms}");
-  data.SetShortName(ASCIIToUTF16("Google"));
-  data.SetKeyword(ASCIIToUTF16("google.com"));
-  data.suggestions_url = "{google:baseSuggestURL}search?q={searchTerms}";
-  data.instant_url = "{google:baseURL}webhp";
-  data.image_url = "{google:baseURL}searchbyimage/upload";
-  data.new_tab_url = "{google:baseURL}_/chrome/newtab";
-  data.contextual_search_url = "{google:baseURL}_/contextualsearch";
-  data.image_url_post_params = "encoded_image={google:imageThumbnail}";
-  data.alternate_urls.push_back("{google:baseURL}s#q={searchTerms}");
-  // search_terms_replacement_key with value of
-  // "{google:instantExtendedEnabledKey}" is replaced inside TemplateUrl
-  // constructor so must be handled specially inside MatchesData.
-  data.search_terms_replacement_key = "{google:instantExtendedEnabledKey}";
-  TemplateURL url(data);
-  EXPECT_TRUE(TemplateURL::MatchesData(&url, &data, search_terms_data_));
-}
diff --git a/third_party/WebKit/LayoutTests/animations/custom-properties/color-type-interpolation-expected.txt b/third_party/WebKit/LayoutTests/animations/custom-properties/color-type-interpolation-expected.txt
new file mode 100644
index 0000000..673a815
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/animations/custom-properties/color-type-interpolation-expected.txt
@@ -0,0 +1,148 @@
+This is a testharness.js-based test.
+PASS This test uses interpolation-test.js. 
+FAIL CSS Transitions: property <--color> from neutral to [green] at (-0.3) is [green] assert_equals: expected "rgb ( 255 , 255 , 0 ) " but got "green "
+FAIL CSS Transitions: property <--color> from neutral to [green] at (0) is [green] assert_equals: expected "rgb ( 255 , 255 , 0 ) " but got "green "
+FAIL CSS Transitions: property <--color> from neutral to [green] at (0.3) is [green] assert_equals: expected "rgb ( 179 , 217 , 0 ) " but got "green "
+FAIL CSS Transitions: property <--color> from neutral to [green] at (0.6) is [green] assert_equals: expected "rgb ( 102 , 179 , 0 ) " but got "green "
+FAIL CSS Transitions: property <--color> from neutral to [green] at (1) is [green] assert_equals: expected "rgb ( 0 , 128 , 0 ) " but got "green "
+FAIL CSS Transitions: property <--color> from neutral to [green] at (1.5) is [green] assert_equals: expected "rgb ( 0 , 65 , 0 ) " but got "green "
+FAIL CSS Transitions: property <--color> from [initial] to [green] at (-0.3) is [green] assert_equals: expected "rgb ( 0 , 0 , 0 ) " but got "green "
+FAIL CSS Transitions: property <--color> from [initial] to [green] at (0) is [green] assert_equals: expected "rgb ( 0 , 0 , 0 ) " but got "green "
+FAIL CSS Transitions: property <--color> from [initial] to [green] at (0.3) is [green] assert_equals: expected "rgb ( 0 , 38 , 0 ) " but got "green "
+FAIL CSS Transitions: property <--color> from [initial] to [green] at (0.6) is [green] assert_equals: expected "rgb ( 0 , 77 , 0 ) " but got "green "
+FAIL CSS Transitions: property <--color> from [initial] to [green] at (1) is [green] assert_equals: expected "rgb ( 0 , 128 , 0 ) " but got "green "
+FAIL CSS Transitions: property <--color> from [initial] to [green] at (1.5) is [green] assert_equals: expected "rgb ( 0 , 192 , 0 ) " but got "green "
+FAIL CSS Transitions: property <--color> from [inherit] to [green] at (-0.3) is [green] assert_equals: expected "rgb ( 0 , 0 , 255 ) " but got "green "
+FAIL CSS Transitions: property <--color> from [inherit] to [green] at (0) is [green] assert_equals: expected "rgb ( 0 , 0 , 255 ) " but got "green "
+FAIL CSS Transitions: property <--color> from [inherit] to [green] at (0.3) is [green] assert_equals: expected "rgb ( 0 , 38 , 179 ) " but got "green "
+FAIL CSS Transitions: property <--color> from [inherit] to [green] at (0.6) is [green] assert_equals: expected "rgb ( 0 , 77 , 102 ) " but got "green "
+FAIL CSS Transitions: property <--color> from [inherit] to [green] at (1) is [green] assert_equals: expected "rgb ( 0 , 128 , 0 ) " but got "green "
+FAIL CSS Transitions: property <--color> from [inherit] to [green] at (1.5) is [green] assert_equals: expected "rgb ( 0 , 192 , 0 ) " but got "green "
+FAIL CSS Transitions: property <--color> from [unset] to [green] at (-0.3) is [green] assert_equals: expected "rgb ( 0 , 0 , 0 ) " but got "green "
+FAIL CSS Transitions: property <--color> from [unset] to [green] at (0) is [green] assert_equals: expected "rgb ( 0 , 0 , 0 ) " but got "green "
+FAIL CSS Transitions: property <--color> from [unset] to [green] at (0.3) is [green] assert_equals: expected "rgb ( 0 , 38 , 0 ) " but got "green "
+FAIL CSS Transitions: property <--color> from [unset] to [green] at (0.6) is [green] assert_equals: expected "rgb ( 0 , 77 , 0 ) " but got "green "
+FAIL CSS Transitions: property <--color> from [unset] to [green] at (1) is [green] assert_equals: expected "rgb ( 0 , 128 , 0 ) " but got "green "
+FAIL CSS Transitions: property <--color> from [unset] to [green] at (1.5) is [green] assert_equals: expected "rgb ( 0 , 192 , 0 ) " but got "green "
+FAIL CSS Transitions: property <--color> from [black] to [orange] at (-0.3) is [orange] assert_equals: expected "rgb ( 0 , 0 , 0 ) " but got "orange "
+FAIL CSS Transitions: property <--color> from [black] to [orange] at (0) is [orange] assert_equals: expected "rgb ( 0 , 0 , 0 ) " but got "orange "
+FAIL CSS Transitions: property <--color> from [black] to [orange] at (0.3) is [orange] assert_equals: expected "rgb ( 77 , 50 , 0 ) " but got "orange "
+FAIL CSS Transitions: property <--color> from [black] to [orange] at (0.6) is [orange] assert_equals: expected "rgb ( 153 , 99 , 0 ) " but got "orange "
+FAIL CSS Transitions: property <--color> from [black] to [orange] at (1) is [orange] assert_equals: expected "rgb ( 255 , 165 , 0 ) " but got "orange "
+FAIL CSS Transitions: property <--color> from [black] to [orange] at (1.5) is [orange] assert_equals: expected "rgb ( 255 , 248 , 0 ) " but got "orange "
+FAIL CSS Transitions: property <--color> from [black] to [currentcolor] at (-0.3) is [currentcolor] assert_equals: expected "rgb ( 0 , 0 , 0 ) " but got "currentcolor "
+FAIL CSS Transitions: property <--color> from [black] to [currentcolor] at (0) is [currentcolor] assert_equals: expected "rgb ( 0 , 0 , 0 ) " but got "currentcolor "
+FAIL CSS Transitions: property <--color> from [black] to [currentcolor] at (0.3) is [currentcolor] assert_equals: expected "rgb ( 0 , 77 , 0 ) " but got "currentcolor "
+FAIL CSS Transitions: property <--color> from [black] to [currentcolor] at (0.6) is [currentcolor] assert_equals: expected "rgb ( 0 , 153 , 0 ) " but got "currentcolor "
+FAIL CSS Transitions: property <--color> from [black] to [currentcolor] at (1) is [currentcolor] assert_equals: expected "rgb ( 0 , 255 , 0 ) " but got "currentcolor "
+FAIL CSS Transitions: property <--color> from [black] to [currentcolor] at (1.5) is [currentcolor] assert_equals: expected "rgb ( 0 , 255 , 0 ) " but got "currentcolor "
+FAIL CSS Transitions: property <--color> from [-webkit-activelink] to [green] at (-0.3) is [green] assert_equals: expected "rgb ( 255 , 0 , 0 ) " but got "green "
+FAIL CSS Transitions: property <--color> from [-webkit-activelink] to [green] at (0) is [green] assert_equals: expected "rgb ( 255 , 0 , 0 ) " but got "green "
+FAIL CSS Transitions: property <--color> from [-webkit-activelink] to [green] at (0.3) is [green] assert_equals: expected "rgb ( 179 , 38 , 0 ) " but got "green "
+FAIL CSS Transitions: property <--color> from [-webkit-activelink] to [green] at (0.6) is [green] assert_equals: expected "rgb ( 102 , 77 , 0 ) " but got "green "
+FAIL CSS Transitions: property <--color> from [-webkit-activelink] to [green] at (1) is [green] assert_equals: expected "rgb ( 0 , 128 , 0 ) " but got "green "
+FAIL CSS Transitions: property <--color> from [-webkit-activelink] to [green] at (1.5) is [green] assert_equals: expected "rgb ( 0 , 192 , 0 ) " but got "green "
+FAIL CSS Transitions: property <--color> from [-webkit-link] to [green] at (-0.3) is [green] assert_equals: expected "rgb ( 0 , 0 , 255 ) " but got "green "
+FAIL CSS Transitions: property <--color> from [-webkit-link] to [green] at (0) is [green] assert_equals: expected "rgb ( 0 , 0 , 255 ) " but got "green "
+FAIL CSS Transitions: property <--color> from [-webkit-link] to [green] at (0.3) is [green] assert_equals: expected "rgb ( 0 , 38 , 179 ) " but got "green "
+FAIL CSS Transitions: property <--color> from [-webkit-link] to [green] at (0.6) is [green] assert_equals: expected "rgb ( 0 , 77 , 102 ) " but got "green "
+FAIL CSS Transitions: property <--color> from [-webkit-link] to [green] at (1) is [green] assert_equals: expected "rgb ( 0 , 128 , 0 ) " but got "green "
+FAIL CSS Transitions: property <--color> from [-webkit-link] to [green] at (1.5) is [green] assert_equals: expected "rgb ( 0 , 192 , 0 ) " but got "green "
+PASS CSS Animations: property <--color> from neutral to [green] at (-0.3) is [rgb(255, 255, 0)] 
+PASS CSS Animations: property <--color> from neutral to [green] at (0) is [rgb(255, 255, 0)] 
+PASS CSS Animations: property <--color> from neutral to [green] at (0.3) is [rgb(179, 217, 0)] 
+PASS CSS Animations: property <--color> from neutral to [green] at (0.6) is [rgb(102, 179, 0)] 
+PASS CSS Animations: property <--color> from neutral to [green] at (1) is [rgb(0, 128, 0)] 
+PASS CSS Animations: property <--color> from neutral to [green] at (1.5) is [rgb(0, 65, 0)] 
+PASS CSS Animations: property <--color> from [initial] to [green] at (-0.3) is [rgb(0, 0, 0)] 
+PASS CSS Animations: property <--color> from [initial] to [green] at (0) is [rgb(0, 0, 0)] 
+PASS CSS Animations: property <--color> from [initial] to [green] at (0.3) is [rgb(0, 38, 0)] 
+PASS CSS Animations: property <--color> from [initial] to [green] at (0.6) is [rgb(0, 77, 0)] 
+PASS CSS Animations: property <--color> from [initial] to [green] at (1) is [rgb(0, 128, 0)] 
+PASS CSS Animations: property <--color> from [initial] to [green] at (1.5) is [rgb(0, 192, 0)] 
+PASS CSS Animations: property <--color> from [inherit] to [green] at (-0.3) is [rgb(0, 0, 255)] 
+PASS CSS Animations: property <--color> from [inherit] to [green] at (0) is [rgb(0, 0, 255)] 
+PASS CSS Animations: property <--color> from [inherit] to [green] at (0.3) is [rgb(0, 38, 179)] 
+PASS CSS Animations: property <--color> from [inherit] to [green] at (0.6) is [rgb(0, 77, 102)] 
+PASS CSS Animations: property <--color> from [inherit] to [green] at (1) is [rgb(0, 128, 0)] 
+PASS CSS Animations: property <--color> from [inherit] to [green] at (1.5) is [rgb(0, 192, 0)] 
+PASS CSS Animations: property <--color> from [unset] to [green] at (-0.3) is [rgb(0, 0, 0)] 
+PASS CSS Animations: property <--color> from [unset] to [green] at (0) is [rgb(0, 0, 0)] 
+PASS CSS Animations: property <--color> from [unset] to [green] at (0.3) is [rgb(0, 38, 0)] 
+PASS CSS Animations: property <--color> from [unset] to [green] at (0.6) is [rgb(0, 77, 0)] 
+PASS CSS Animations: property <--color> from [unset] to [green] at (1) is [rgb(0, 128, 0)] 
+PASS CSS Animations: property <--color> from [unset] to [green] at (1.5) is [rgb(0, 192, 0)] 
+PASS CSS Animations: property <--color> from [black] to [orange] at (-0.3) is [rgb(0, 0, 0)] 
+PASS CSS Animations: property <--color> from [black] to [orange] at (0) is [rgb(0, 0, 0)] 
+PASS CSS Animations: property <--color> from [black] to [orange] at (0.3) is [rgb(77, 50, 0)] 
+PASS CSS Animations: property <--color> from [black] to [orange] at (0.6) is [rgb(153, 99, 0)] 
+PASS CSS Animations: property <--color> from [black] to [orange] at (1) is [rgb(255, 165, 0)] 
+PASS CSS Animations: property <--color> from [black] to [orange] at (1.5) is [rgb(255, 248, 0)] 
+PASS CSS Animations: property <--color> from [black] to [currentcolor] at (-0.3) is [rgb(0, 0, 0)] 
+PASS CSS Animations: property <--color> from [black] to [currentcolor] at (0) is [rgb(0, 0, 0)] 
+FAIL CSS Animations: property <--color> from [black] to [currentcolor] at (0.3) is [rgb(0, 0, 0)] assert_equals: expected "rgb ( 0 , 77 , 0 ) " but got "rgb ( 0 , 0 , 0 ) "
+FAIL CSS Animations: property <--color> from [black] to [currentcolor] at (0.6) is [rgb(0, 0, 0)] assert_equals: expected "rgb ( 0 , 153 , 0 ) " but got "rgb ( 0 , 0 , 0 ) "
+FAIL CSS Animations: property <--color> from [black] to [currentcolor] at (1) is [rgb(0, 0, 0)] assert_equals: expected "rgb ( 0 , 255 , 0 ) " but got "rgb ( 0 , 0 , 0 ) "
+FAIL CSS Animations: property <--color> from [black] to [currentcolor] at (1.5) is [rgb(0, 0, 0)] assert_equals: expected "rgb ( 0 , 255 , 0 ) " but got "rgb ( 0 , 0 , 0 ) "
+PASS CSS Animations: property <--color> from [-webkit-activelink] to [green] at (-0.3) is [rgb(255, 0, 0)] 
+PASS CSS Animations: property <--color> from [-webkit-activelink] to [green] at (0) is [rgb(255, 0, 0)] 
+PASS CSS Animations: property <--color> from [-webkit-activelink] to [green] at (0.3) is [rgb(179, 38, 0)] 
+PASS CSS Animations: property <--color> from [-webkit-activelink] to [green] at (0.6) is [rgb(102, 77, 0)] 
+PASS CSS Animations: property <--color> from [-webkit-activelink] to [green] at (1) is [rgb(0, 128, 0)] 
+PASS CSS Animations: property <--color> from [-webkit-activelink] to [green] at (1.5) is [rgb(0, 192, 0)] 
+PASS CSS Animations: property <--color> from [-webkit-link] to [green] at (-0.3) is [rgb(0, 0, 255)] 
+PASS CSS Animations: property <--color> from [-webkit-link] to [green] at (0) is [rgb(0, 0, 255)] 
+PASS CSS Animations: property <--color> from [-webkit-link] to [green] at (0.3) is [rgb(0, 38, 179)] 
+PASS CSS Animations: property <--color> from [-webkit-link] to [green] at (0.6) is [rgb(0, 77, 102)] 
+PASS CSS Animations: property <--color> from [-webkit-link] to [green] at (1) is [rgb(0, 128, 0)] 
+PASS CSS Animations: property <--color> from [-webkit-link] to [green] at (1.5) is [rgb(0, 192, 0)] 
+PASS Web Animations: property <--color> from neutral to [green] at (-0.3) is [rgb(255, 255, 0)] 
+PASS Web Animations: property <--color> from neutral to [green] at (0) is [rgb(255, 255, 0)] 
+PASS Web Animations: property <--color> from neutral to [green] at (0.3) is [rgb(179, 217, 0)] 
+PASS Web Animations: property <--color> from neutral to [green] at (0.6) is [rgb(102, 179, 0)] 
+PASS Web Animations: property <--color> from neutral to [green] at (1) is [rgb(0, 128, 0)] 
+PASS Web Animations: property <--color> from neutral to [green] at (1.5) is [rgb(0, 65, 0)] 
+PASS Web Animations: property <--color> from [initial] to [green] at (-0.3) is [rgb(0, 0, 0)] 
+PASS Web Animations: property <--color> from [initial] to [green] at (0) is [rgb(0, 0, 0)] 
+PASS Web Animations: property <--color> from [initial] to [green] at (0.3) is [rgb(0, 38, 0)] 
+PASS Web Animations: property <--color> from [initial] to [green] at (0.6) is [rgb(0, 77, 0)] 
+PASS Web Animations: property <--color> from [initial] to [green] at (1) is [rgb(0, 128, 0)] 
+PASS Web Animations: property <--color> from [initial] to [green] at (1.5) is [rgb(0, 192, 0)] 
+PASS Web Animations: property <--color> from [inherit] to [green] at (-0.3) is [rgb(0, 0, 255)] 
+PASS Web Animations: property <--color> from [inherit] to [green] at (0) is [rgb(0, 0, 255)] 
+PASS Web Animations: property <--color> from [inherit] to [green] at (0.3) is [rgb(0, 38, 179)] 
+PASS Web Animations: property <--color> from [inherit] to [green] at (0.6) is [rgb(0, 77, 102)] 
+PASS Web Animations: property <--color> from [inherit] to [green] at (1) is [rgb(0, 128, 0)] 
+PASS Web Animations: property <--color> from [inherit] to [green] at (1.5) is [rgb(0, 192, 0)] 
+PASS Web Animations: property <--color> from [unset] to [green] at (-0.3) is [rgb(0, 0, 0)] 
+PASS Web Animations: property <--color> from [unset] to [green] at (0) is [rgb(0, 0, 0)] 
+PASS Web Animations: property <--color> from [unset] to [green] at (0.3) is [rgb(0, 38, 0)] 
+PASS Web Animations: property <--color> from [unset] to [green] at (0.6) is [rgb(0, 77, 0)] 
+PASS Web Animations: property <--color> from [unset] to [green] at (1) is [rgb(0, 128, 0)] 
+PASS Web Animations: property <--color> from [unset] to [green] at (1.5) is [rgb(0, 192, 0)] 
+PASS Web Animations: property <--color> from [black] to [orange] at (-0.3) is [rgb(0, 0, 0)] 
+PASS Web Animations: property <--color> from [black] to [orange] at (0) is [rgb(0, 0, 0)] 
+PASS Web Animations: property <--color> from [black] to [orange] at (0.3) is [rgb(77, 50, 0)] 
+PASS Web Animations: property <--color> from [black] to [orange] at (0.6) is [rgb(153, 99, 0)] 
+PASS Web Animations: property <--color> from [black] to [orange] at (1) is [rgb(255, 165, 0)] 
+PASS Web Animations: property <--color> from [black] to [orange] at (1.5) is [rgb(255, 248, 0)] 
+PASS Web Animations: property <--color> from [black] to [currentcolor] at (-0.3) is [rgb(0, 0, 0)] 
+PASS Web Animations: property <--color> from [black] to [currentcolor] at (0) is [rgb(0, 0, 0)] 
+PASS Web Animations: property <--color> from [black] to [currentcolor] at (0.3) is [rgb(0, 77, 0)] 
+PASS Web Animations: property <--color> from [black] to [currentcolor] at (0.6) is [rgb(0, 153, 0)] 
+PASS Web Animations: property <--color> from [black] to [currentcolor] at (1) is [rgb(0, 255, 0)] 
+PASS Web Animations: property <--color> from [black] to [currentcolor] at (1.5) is [rgb(0, 255, 0)] 
+PASS Web Animations: property <--color> from [-webkit-activelink] to [green] at (-0.3) is [rgb(255, 0, 0)] 
+PASS Web Animations: property <--color> from [-webkit-activelink] to [green] at (0) is [rgb(255, 0, 0)] 
+PASS Web Animations: property <--color> from [-webkit-activelink] to [green] at (0.3) is [rgb(179, 38, 0)] 
+PASS Web Animations: property <--color> from [-webkit-activelink] to [green] at (0.6) is [rgb(102, 77, 0)] 
+PASS Web Animations: property <--color> from [-webkit-activelink] to [green] at (1) is [rgb(0, 128, 0)] 
+PASS Web Animations: property <--color> from [-webkit-activelink] to [green] at (1.5) is [rgb(0, 192, 0)] 
+PASS Web Animations: property <--color> from [-webkit-link] to [green] at (-0.3) is [rgb(0, 0, 255)] 
+PASS Web Animations: property <--color> from [-webkit-link] to [green] at (0) is [rgb(0, 0, 255)] 
+PASS Web Animations: property <--color> from [-webkit-link] to [green] at (0.3) is [rgb(0, 38, 179)] 
+PASS Web Animations: property <--color> from [-webkit-link] to [green] at (0.6) is [rgb(0, 77, 102)] 
+PASS Web Animations: property <--color> from [-webkit-link] to [green] at (1) is [rgb(0, 128, 0)] 
+PASS Web Animations: property <--color> from [-webkit-link] to [green] at (1.5) is [rgb(0, 192, 0)] 
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/animations/custom-properties/color-type-interpolation.html b/third_party/WebKit/LayoutTests/animations/custom-properties/color-type-interpolation.html
new file mode 100644
index 0000000..5d60922
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/animations/custom-properties/color-type-interpolation.html
@@ -0,0 +1,125 @@
+<!DOCTYPE html>
+<meta charset="UTF-8">
+<style>
+.parent {
+  --color: blue;
+}
+.target {
+  --color: yellow;
+  color: lime;
+}
+</style>
+<body alink="red" link="blue">
+<script src="../interpolation/resources/interpolation-test.js"></script>
+<script>
+CSS.registerProperty({
+  name: '--color',
+  syntax: '<color>',
+  initialValue: 'black',
+});
+
+assertInterpolation({
+  property: '--color',
+  from: neutralKeyframe,
+  to: 'green',
+}, [
+  {at: -0.3, is: 'rgb(255, 255, 0)'},
+  {at: 0, is: 'rgb(255, 255, 0)'},
+  {at: 0.3, is: 'rgb(179, 217, 0)'},
+  {at: 0.6, is: 'rgb(102, 179, 0)'},
+  {at: 1, is: 'rgb(0, 128, 0)'},
+  {at: 1.5, is: 'rgb(0, 65, 0)'},
+]);
+
+assertInterpolation({
+  property: '--color',
+  from: 'initial',
+  to: 'green',
+}, [
+  {at: -0.3, is: 'rgb(0, 0, 0)'},
+  {at: 0, is: 'rgb(0, 0, 0)'},
+  {at: 0.3, is: 'rgb(0, 38, 0)'},
+  {at: 0.6, is: 'rgb(0, 77, 0)'},
+  {at: 1, is: 'rgb(0, 128, 0)'},
+  {at: 1.5, is: 'rgb(0, 192, 0)'},
+]);
+
+assertInterpolation({
+  property: '--color',
+  from: 'inherit',
+  to: 'green',
+}, [
+  {at: -0.3, is: 'rgb(0, 0, 255)'},
+  {at: 0, is: 'rgb(0, 0, 255)'},
+  {at: 0.3, is: 'rgb(0, 38, 179)'},
+  {at: 0.6, is: 'rgb(0, 77, 102)'},
+  {at: 1, is: 'rgb(0, 128, 0)'},
+  {at: 1.5, is: 'rgb(0, 192, 0)'},
+]);
+
+assertInterpolation({
+  property: '--color',
+  from: 'unset',
+  to: 'green',
+}, [
+  {at: -0.3, is: 'rgb(0, 0, 0)'},
+  {at: 0, is: 'rgb(0, 0, 0)'},
+  {at: 0.3, is: 'rgb(0, 38, 0)'},
+  {at: 0.6, is: 'rgb(0, 77, 0)'},
+  {at: 1, is: 'rgb(0, 128, 0)'},
+  {at: 1.5, is: 'rgb(0, 192, 0)'},
+]);
+
+assertInterpolation({
+  property: '--color',
+  from: 'black',
+  to: 'orange',
+}, [
+  {at: -0.3, is: 'rgb(0, 0, 0)'},
+  {at: 0, is: 'rgb(0, 0, 0)'},
+  {at: 0.3, is: 'rgb(77, 50, 0)'},
+  {at: 0.6, is: 'rgb(153, 99, 0)'},
+  {at: 1, is: 'rgb(255, 165, 0)'},
+  {at: 1.5, is: 'rgb(255, 248, 0)'},
+]);
+
+assertInterpolation({
+  property: '--color',
+  from: 'black',
+  to: 'currentcolor',
+}, [
+  {at: -0.3, is: 'rgb(0, 0, 0)'},
+  {at: 0, is: 'rgb(0, 0, 0)'},
+  {at: 0.3, is: 'rgb(0, 77, 0)'},
+  {at: 0.6, is: 'rgb(0, 153, 0)'},
+  {at: 1, is: 'rgb(0, 255, 0)'},
+  {at: 1.5, is: 'rgb(0, 255, 0)'},
+]);
+
+assertInterpolation({
+  property: '--color',
+  from: '-webkit-activelink',
+  to: 'green',
+}, [
+  {at: -0.3, is: 'rgb(255, 0, 0)'},
+  {at: 0, is: 'rgb(255, 0, 0)'},
+  {at: 0.3, is: 'rgb(179, 38, 0)'},
+  {at: 0.6, is: 'rgb(102, 77, 0)'},
+  {at: 1, is: 'rgb(0, 128, 0)'},
+  {at: 1.5, is: 'rgb(0, 192, 0)'},
+]);
+
+assertInterpolation({
+  property: '--color',
+  from: '-webkit-link',
+  to: 'green',
+}, [
+  {at: -0.3, is: 'rgb(0, 0, 255)'},
+  {at: 0, is: 'rgb(0, 0, 255)'},
+  {at: 0.3, is: 'rgb(0, 38, 179)'},
+  {at: 0.6, is: 'rgb(0, 77, 102)'},
+  {at: 1, is: 'rgb(0, 128, 0)'},
+  {at: 1.5, is: 'rgb(0, 192, 0)'},
+]);
+</script>
+</body>
diff --git a/third_party/WebKit/Source/core/animation/CSSColorInterpolationType.cpp b/third_party/WebKit/Source/core/animation/CSSColorInterpolationType.cpp
index 0a3d410..ef834b99 100644
--- a/third_party/WebKit/Source/core/animation/CSSColorInterpolationType.cpp
+++ b/third_party/WebKit/Source/core/animation/CSSColorInterpolationType.cpp
@@ -265,4 +265,13 @@
           cssProperty() == CSSPropertyTextDecorationColor));
 }
 
+const CSSValue* CSSColorInterpolationType::createCSSValue(
+    const InterpolableValue& interpolableValue,
+    const NonInterpolableValue*,
+    const StyleResolverState& state) const {
+  const InterpolableList& colorPair = toInterpolableList(interpolableValue);
+  Color color = resolveInterpolableColor(*colorPair.get(Unvisited), state);
+  return CSSColorValue::create(color.rgb());
+}
+
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/animation/CSSColorInterpolationType.h b/third_party/WebKit/Source/core/animation/CSSColorInterpolationType.h
index 2d5c0eeb..505e201 100644
--- a/third_party/WebKit/Source/core/animation/CSSColorInterpolationType.h
+++ b/third_party/WebKit/Source/core/animation/CSSColorInterpolationType.h
@@ -50,6 +50,10 @@
                                        ConversionCheckers&) const final;
   InterpolationValue convertStyleColorPair(const StyleColor&,
                                            const StyleColor&) const;
+
+  const CSSValue* createCSSValue(const InterpolableValue&,
+                                 const NonInterpolableValue*,
+                                 const StyleResolverState&) const final;
 };
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/css/CSSPrimitiveValueMappings.h b/third_party/WebKit/Source/core/css/CSSPrimitiveValueMappings.h
index 0577d7a..cef699b 100644
--- a/third_party/WebKit/Source/core/css/CSSPrimitiveValueMappings.h
+++ b/third_party/WebKit/Source/core/css/CSSPrimitiveValueMappings.h
@@ -2796,13 +2796,13 @@
 inline CSSIdentifierValue::CSSIdentifierValue(EOverflowAnchor e)
     : CSSValue(IdentifierClass) {
   switch (e) {
-    case EOverflowAnchor::Visible:
+    case EOverflowAnchor::kVisible:
       m_valueID = CSSValueVisible;
       break;
-    case EOverflowAnchor::None:
+    case EOverflowAnchor::kNone:
       m_valueID = CSSValueNone;
       break;
-    case EOverflowAnchor::Auto:
+    case EOverflowAnchor::kAuto:
       m_valueID = CSSValueAuto;
       break;
   }
@@ -2812,17 +2812,17 @@
 inline EOverflowAnchor CSSIdentifierValue::convertTo() const {
   switch (m_valueID) {
     case CSSValueVisible:
-      return EOverflowAnchor::Visible;
+      return EOverflowAnchor::kVisible;
     case CSSValueNone:
-      return EOverflowAnchor::None;
+      return EOverflowAnchor::kNone;
     case CSSValueAuto:
-      return EOverflowAnchor::Auto;
+      return EOverflowAnchor::kAuto;
     default:
       break;
   }
 
   NOTREACHED();
-  return EOverflowAnchor::None;
+  return EOverflowAnchor::kNone;
 }
 
 template <>
diff --git a/third_party/WebKit/Source/core/css/CSSSyntaxDescriptor.cpp b/third_party/WebKit/Source/core/css/CSSSyntaxDescriptor.cpp
index 492c2ca..01fefa3 100644
--- a/third_party/WebKit/Source/core/css/CSSSyntaxDescriptor.cpp
+++ b/third_party/WebKit/Source/core/css/CSSSyntaxDescriptor.cpp
@@ -4,6 +4,8 @@
 
 #include "core/css/CSSSyntaxDescriptor.h"
 
+#include "core/animation/CSSColorInterpolationType.h"
+#include "core/animation/CSSValueInterpolationType.h"
 #include "core/css/CSSCustomPropertyDeclaration.h"
 #include "core/css/CSSURIValue.h"
 #include "core/css/CSSValueList.h"
@@ -211,4 +213,47 @@
                                                          isAnimationTainted);
 }
 
+InterpolationTypes CSSSyntaxDescriptor::createInterpolationTypes(
+    const AtomicString& propertyName) const {
+  PropertyHandle property(propertyName);
+  InterpolationTypes interpolationTypes;
+  for (const CSSSyntaxComponent& component : m_syntaxComponents) {
+    if (component.m_repeatable) {
+      // TODO(alancutter): Support animation of repeatable types.
+      continue;
+    }
+
+    switch (component.m_type) {
+      case CSSSyntaxType::Color:
+        interpolationTypes.push_back(
+            WTF::makeUnique<CSSColorInterpolationType>(property));
+        break;
+      case CSSSyntaxType::Length:
+      case CSSSyntaxType::Number:
+      case CSSSyntaxType::Percentage:
+      case CSSSyntaxType::LengthPercentage:
+      case CSSSyntaxType::Image:
+      case CSSSyntaxType::Url:
+      case CSSSyntaxType::Integer:
+      case CSSSyntaxType::Angle:
+      case CSSSyntaxType::Time:
+      case CSSSyntaxType::Resolution:
+      case CSSSyntaxType::TransformFunction:
+        // TODO(alancutter): Support smooth interpolation of these types.
+        break;
+      case CSSSyntaxType::TokenStream:
+      case CSSSyntaxType::Ident:
+      case CSSSyntaxType::CustomIdent:
+        // Uses the CSSValueInterpolationType added below.
+        break;
+      default:
+        NOTREACHED();
+        break;
+    }
+  }
+  interpolationTypes.push_back(
+      WTF::makeUnique<CSSValueInterpolationType>(property));
+  return interpolationTypes;
+}
+
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/css/CSSSyntaxDescriptor.h b/third_party/WebKit/Source/core/css/CSSSyntaxDescriptor.h
index 94eb432..ff4dace 100644
--- a/third_party/WebKit/Source/core/css/CSSSyntaxDescriptor.h
+++ b/third_party/WebKit/Source/core/css/CSSSyntaxDescriptor.h
@@ -5,6 +5,7 @@
 #ifndef CSSSyntaxDescriptor_h
 #define CSSSyntaxDescriptor_h
 
+#include "core/animation/InterpolationTypesMap.h"
 #include "core/css/parser/CSSParserTokenRange.h"
 
 namespace blink {
@@ -52,6 +53,9 @@
            m_syntaxComponents[0].m_type == CSSSyntaxType::TokenStream;
   }
 
+  InterpolationTypes createInterpolationTypes(
+      const AtomicString& propertyName) const;
+
  private:
   Vector<CSSSyntaxComponent> m_syntaxComponents;
 };
diff --git a/third_party/WebKit/Source/core/css/PropertyRegistration.cpp b/third_party/WebKit/Source/core/css/PropertyRegistration.cpp
index 71bbc5d70..fc59149 100644
--- a/third_party/WebKit/Source/core/css/PropertyRegistration.cpp
+++ b/third_party/WebKit/Source/core/css/PropertyRegistration.cpp
@@ -4,7 +4,6 @@
 
 #include "core/css/PropertyRegistration.h"
 
-#include "core/animation/CSSValueInterpolationType.h"
 #include "core/css/CSSStyleSheet.h"
 #include "core/css/CSSSyntaxDescriptor.h"
 #include "core/css/CSSValueList.h"
@@ -61,17 +60,6 @@
   return true;
 }
 
-InterpolationTypes interpolationTypesForSyntax(const AtomicString& propertyName,
-                                               const CSSSyntaxDescriptor&) {
-  PropertyHandle property(propertyName);
-  InterpolationTypes interpolationTypes;
-  // TODO(alancutter): Read the syntax descriptor and add the appropriate
-  // CSSInterpolationType subclasses.
-  interpolationTypes.push_back(
-      WTF::makeUnique<CSSValueInterpolationType>(property));
-  return interpolationTypes;
-}
-
 void PropertyRegistration::registerProperty(
     ExecutionContext* executionContext,
     const PropertyDescriptor& descriptor,
@@ -106,7 +94,7 @@
   }
 
   InterpolationTypes interpolationTypes =
-      interpolationTypesForSyntax(atomicName, syntaxDescriptor);
+      syntaxDescriptor.createInterpolationTypes(atomicName);
 
   if (descriptor.hasInitialValue()) {
     CSSTokenizer tokenizer(descriptor.initialValue());
diff --git a/third_party/WebKit/Source/core/dom/Document.cpp b/third_party/WebKit/Source/core/dom/Document.cpp
index 3d8a203..d0f3223 100644
--- a/third_party/WebKit/Source/core/dom/Document.cpp
+++ b/third_party/WebKit/Source/core/dom/Document.cpp
@@ -1811,7 +1811,7 @@
         StyleChangeReasonForTracing::create(StyleChangeReason::FontSizeChange));
   }
 
-  EOverflowAnchor overflowAnchor = EOverflowAnchor::Auto;
+  EOverflowAnchor overflowAnchor = EOverflowAnchor::kAuto;
   EOverflow overflowX = EOverflow::Auto;
   EOverflow overflowY = EOverflow::Auto;
   float columnGap = 0;
@@ -1825,8 +1825,8 @@
       overflowX = EOverflow::Auto;
     if (overflowY == EOverflow::Visible)
       overflowY = EOverflow::Auto;
-    if (overflowAnchor == EOverflowAnchor::Visible)
-      overflowAnchor = EOverflowAnchor::Auto;
+    if (overflowAnchor == EOverflowAnchor::kVisible)
+      overflowAnchor = EOverflowAnchor::kAuto;
     // Column-gap is (ab)used by the current paged overflow implementation (in
     // lack of other ways to specify gaps between pages), so we have to
     // propagate it too.
diff --git a/third_party/WebKit/Source/core/frame/FrameView.cpp b/third_party/WebKit/Source/core/frame/FrameView.cpp
index 492fabd..1267adb0 100644
--- a/third_party/WebKit/Source/core/frame/FrameView.cpp
+++ b/third_party/WebKit/Source/core/frame/FrameView.cpp
@@ -994,7 +994,7 @@
   return RuntimeEnabledFeatures::scrollAnchoringEnabled() &&
          !RuntimeEnabledFeatures::rootLayerScrollingEnabled() &&
          m_scrollAnchor.hasScroller() &&
-         layoutBox()->style()->overflowAnchor() != EOverflowAnchor::None &&
+         layoutBox()->style()->overflowAnchor() != EOverflowAnchor::kNone &&
          !m_frame->document()->finishingOrIsPrinting();
 }
 
diff --git a/third_party/WebKit/Source/core/layout/ScrollAnchor.cpp b/third_party/WebKit/Source/core/layout/ScrollAnchor.cpp
index 68b2bb22..a261571 100644
--- a/third_party/WebKit/Source/core/layout/ScrollAnchor.cpp
+++ b/third_party/WebKit/Source/core/layout/ScrollAnchor.cpp
@@ -139,7 +139,7 @@
   if (!candidateMayMoveWithScroller(candidate, m_scroller))
     return ExamineResult(Skip);
 
-  if (candidate->style()->overflowAnchor() == EOverflowAnchor::None)
+  if (candidate->style()->overflowAnchor() == EOverflowAnchor::kNone)
     return ExamineResult(Skip);
 
   LayoutRect candidateRect = relativeBounds(candidate, m_scroller);
diff --git a/third_party/WebKit/Source/core/layout/ng/ng_layout_inline_items_builder.cc b/third_party/WebKit/Source/core/layout/ng/ng_layout_inline_items_builder.cc
index 57adefd0..0a1ea0cc 100644
--- a/third_party/WebKit/Source/core/layout/ng/ng_layout_inline_items_builder.cc
+++ b/third_party/WebKit/Source/core/layout/ng/ng_layout_inline_items_builder.cc
@@ -95,14 +95,21 @@
                                  after_style);
 }
 
+static void AppendItem(Vector<NGLayoutInlineItem>* items,
+                       unsigned start,
+                       unsigned end,
+                       const ComputedStyle* style = nullptr,
+                       LayoutObject* layout_object = nullptr) {
+  DCHECK(items->isEmpty() || items->back().EndOffset() == start);
+  DCHECK_LT(start, end);
+  items->push_back(NGLayoutInlineItem(start, end, style, layout_object));
+}
+
 void NGLayoutInlineItemsBuilder::Append(const String& string,
                                         const ComputedStyle* style,
                                         LayoutObject* layout_object) {
-  if (string.isEmpty()) {
-    unsigned offset = text_.length();
-    items_->push_back(NGLayoutInlineItem(offset, offset, style, layout_object));
+  if (string.isEmpty())
     return;
-  }
 
   if (has_pending_newline_)
     ProcessPendingNewline(string, style);
@@ -150,8 +157,8 @@
     }
   }
 
-  items_->push_back(
-      NGLayoutInlineItem(start_offset, text_.length(), style, layout_object));
+  if (text_.length() > start_offset)
+    AppendItem(items_, start_offset, text_.length(), style, layout_object);
 }
 
 void NGLayoutInlineItemsBuilder::Append(UChar character,
@@ -164,8 +171,7 @@
 
   text_.append(character);
   unsigned end_offset = text_.length();
-  items_->push_back(
-      NGLayoutInlineItem(end_offset - 1, end_offset, style, layout_object));
+  AppendItem(items_, end_offset - 1, end_offset, style, layout_object);
   is_last_collapsible_space_ = false;
 }
 
@@ -176,7 +182,7 @@
 
   text_.append(character);
   unsigned end_offset = text_.length();
-  items_->push_back(NGLayoutInlineItem(end_offset - 1, end_offset, nullptr));
+  AppendItem(items_, end_offset - 1, end_offset, nullptr);
 }
 
 void NGLayoutInlineItemsBuilder::ProcessPendingNewline(
@@ -194,13 +200,25 @@
 }
 
 void NGLayoutInlineItemsBuilder::RemoveTrailingCollapsibleSpace(
-    unsigned* offset) {
-  if (!text_.isEmpty() && is_last_collapsible_space_) {
-    DCHECK_EQ(spaceCharacter, text_[text_.length() - 1]);
-    text_.resize(text_.length() - 1);
-    is_last_collapsible_space_ = false;
-    if (*offset > text_.length())
-      *offset = text_.length();
+    unsigned* next_start_offset) {
+  if (!is_last_collapsible_space_ || text_.isEmpty())
+    return;
+  DCHECK_EQ(spaceCharacter, text_[text_.length() - 1]);
+  unsigned new_size = text_.length() - 1;
+  text_.resize(new_size);
+  is_last_collapsible_space_ = false;
+
+  // Adjust the last item if the removed space is already appended.
+  if (*next_start_offset > new_size) {
+    *next_start_offset = new_size;
+    if (!items_->isEmpty()) {
+      NGLayoutInlineItem& last_item = items_->back();
+      DCHECK_EQ(last_item.EndOffset(), new_size + 1);
+      if (last_item.StartOffset() == new_size)
+        items_->pop_back();
+      else
+        last_item.SetEndOffset(new_size);
+    }
   }
 }
 
diff --git a/third_party/WebKit/Source/core/layout/ng/ng_layout_inline_items_builder_test.cc b/third_party/WebKit/Source/core/layout/ng/ng_layout_inline_items_builder_test.cc
index eef2866b..b9a01db 100644
--- a/third_party/WebKit/Source/core/layout/ng/ng_layout_inline_items_builder_test.cc
+++ b/third_party/WebKit/Source/core/layout/ng/ng_layout_inline_items_builder_test.cc
@@ -33,6 +33,7 @@
     for (int i = 0; i < size; i++)
       builder.Append(inputs[i], style_.get());
     text_ = builder.ToString();
+    ValidateItems();
     return text_;
   }
 
@@ -46,6 +47,24 @@
     return TestAppend(inputs, 2);
   }
 
+  const String& TestAppend(const String& input1,
+                           const String& input2,
+                           const String& input3) {
+    String inputs[] = {input1, input2, input3};
+    return TestAppend(inputs, 3);
+  }
+
+  void ValidateItems() {
+    unsigned current_offset = 0;
+    for (unsigned i = 0; i < items_.size(); i++) {
+      const NGLayoutInlineItem& item = items_[i];
+      EXPECT_EQ(current_offset, item.StartOffset());
+      EXPECT_LT(item.StartOffset(), item.EndOffset());
+      current_offset = item.EndOffset();
+    }
+    EXPECT_EQ(current_offset, text_.length());
+  }
+
   Vector<NGLayoutInlineItem> items_;
   String text_;
   RefPtr<ComputedStyle> style_;
@@ -96,6 +115,11 @@
   EXPECT_EQ("text", TestAppend("  text")) << "Leading spaces are removed.";
 }
 
+TEST_F(NGLayoutInlineItemsBuilderTest, CollapseBeforeNewlineAcrossElements) {
+  EXPECT_EQ("text text", TestAppend("text ", "\ntext"));
+  EXPECT_EQ("text text", TestAppend("text", " ", "\ntext"));
+}
+
 TEST_F(NGLayoutInlineItemsBuilderTest, CollapseBeforeAndAfterNewline) {
   SetWhiteSpace(EWhiteSpace::kPreLine);
   EXPECT_EQ("text\ntext", TestAppend("text  \n  text"))
@@ -173,6 +197,11 @@
   EXPECT_EQ(String(u"Hello \u2068World"), builder.ToString());
 }
 
+TEST_F(NGLayoutInlineItemsBuilderTest, AppendEmptyString) {
+  EXPECT_EQ("", TestAppend(""));
+  EXPECT_EQ(0u, items_.size());
+}
+
 TEST_F(NGLayoutInlineItemsBuilderTest, Empty) {
   Vector<NGLayoutInlineItem> items;
   NGLayoutInlineItemsBuilder builder(&items);
diff --git a/third_party/WebKit/Source/core/paint/PaintLayerScrollableArea.cpp b/third_party/WebKit/Source/core/paint/PaintLayerScrollableArea.cpp
index 4b9c6d9..a07dab91 100644
--- a/third_party/WebKit/Source/core/paint/PaintLayerScrollableArea.cpp
+++ b/third_party/WebKit/Source/core/paint/PaintLayerScrollableArea.cpp
@@ -867,7 +867,7 @@
 bool PaintLayerScrollableArea::shouldPerformScrollAnchoring() const {
   return RuntimeEnabledFeatures::scrollAnchoringEnabled() &&
          m_scrollAnchor.hasScroller() &&
-         layoutBox()->style()->overflowAnchor() != EOverflowAnchor::None &&
+         layoutBox()->style()->overflowAnchor() != EOverflowAnchor::kNone &&
          !box().document().finishingOrIsPrinting();
 }
 
diff --git a/third_party/WebKit/Source/core/style/ComputedStyle.h b/third_party/WebKit/Source/core/style/ComputedStyle.h
index 395478dc..f8b43e8 100644
--- a/third_party/WebKit/Source/core/style/ComputedStyle.h
+++ b/third_party/WebKit/Source/core/style/ComputedStyle.h
@@ -1479,7 +1479,7 @@
   // Overflow properties.
   // overflow-anchor
   static EOverflowAnchor initialOverflowAnchor() {
-    return EOverflowAnchor::Auto;
+    return EOverflowAnchor::kAuto;
   }
   EOverflowAnchor overflowAnchor() const {
     return static_cast<EOverflowAnchor>(m_nonInheritedData.m_overflowAnchor);
diff --git a/third_party/WebKit/Source/core/style/ComputedStyleConstants.h b/third_party/WebKit/Source/core/style/ComputedStyleConstants.h
index 8693c5e..d5f4a78 100644
--- a/third_party/WebKit/Source/core/style/ComputedStyleConstants.h
+++ b/third_party/WebKit/Source/core/style/ComputedStyleConstants.h
@@ -144,7 +144,7 @@
 
 // Random visual rendering model attributes. Not inherited.
 
-enum class EOverflowAnchor : unsigned { Visible, None, Auto };
+enum class EOverflowAnchor : unsigned { kVisible, kNone, kAuto };
 
 enum class EOverflow : unsigned {
   Visible,