diff --git a/cc/BUILD.gn b/cc/BUILD.gn
index f7918544..c62ac3a 100644
--- a/cc/BUILD.gn
+++ b/cc/BUILD.gn
@@ -647,6 +647,8 @@
     "test/test_occlusion_tracker.h",
     "test/test_shared_bitmap_manager.cc",
     "test/test_shared_bitmap_manager.h",
+    "test/test_skcanvas.cc",
+    "test/test_skcanvas.h",
     "test/test_task_graph_runner.cc",
     "test/test_task_graph_runner.h",
     "test/test_texture.cc",
@@ -764,6 +766,7 @@
     "output/texture_mailbox_deleter_unittest.cc",
     "paint/discardable_image_map_unittest.cc",
     "paint/display_item_list_unittest.cc",
+    "paint/paint_op_buffer_unittest.cc",
     "quads/draw_polygon_unittest.cc",
     "quads/draw_quad_unittest.cc",
     "quads/nine_patch_generator_unittest.cc",
@@ -934,6 +937,7 @@
     "//cc/ipc",
     "//cc/ipc:interfaces",
     "//cc/paint",
+    "//cc/paint",
     "//cc/surfaces",
     "//cc/surfaces:surface_id",
     "//gpu",
diff --git a/cc/paint/BUILD.gn b/cc/paint/BUILD.gn
index 3af5dee..11f9fdac 100644
--- a/cc/paint/BUILD.gn
+++ b/cc/paint/BUILD.gn
@@ -32,13 +32,19 @@
     "paint_canvas.cc",
     "paint_canvas.h",
     "paint_export.h",
+    "paint_flags.cc",
     "paint_flags.h",
     "paint_image.cc",
     "paint_image.h",
+    "paint_op_buffer.cc",
+    "paint_op_buffer.h",
+    "paint_record.cc",
     "paint_record.h",
     "paint_recorder.cc",
     "paint_recorder.h",
     "paint_shader.h",
+    "record_paint_canvas.cc",
+    "record_paint_canvas.h",
     "skia_paint_canvas.cc",
     "skia_paint_canvas.h",
     "transform_display_item.cc",
diff --git a/cc/paint/display_item_list.cc b/cc/paint/display_item_list.cc
index 8359948..c1b069548 100644
--- a/cc/paint/display_item_list.cc
+++ b/cc/paint/display_item_list.cc
@@ -100,9 +100,13 @@
       if (canvas->quickReject(item.picture->cullRect()))
         break;
 
-      // SkPicture always does a wrapping save/restore on the canvas, so it is
-      // not necessary here.
+      // TODO(enne): Maybe the PaintRecord itself could know whether this
+      // was needed?  It's not clear whether these save/restore semantics
+      // that SkPicture handles during playback are things that should be
+      // kept around.
+      canvas->save();
       item.picture->playback(canvas, callback);
+      canvas->restore();
       break;
     }
     case DisplayItem::FLOAT_CLIP: {
@@ -176,6 +180,33 @@
   canvas->restore();
 }
 
+// Atttempts to merge a CompositingDisplayItem and DrawingDisplayItem
+// into a single "draw with alpha".  This function returns true if
+// it was successful.  If false, then the caller is responsible for
+// drawing these items.  This is a DisplayItemList version of the
+// SkRecord optimization SkRecordNoopSaveLayerDrawRestores.
+static bool MergeAndDrawIfPossible(const CompositingDisplayItem& save_item,
+                                   const DrawingDisplayItem& draw_item,
+                                   SkCanvas* canvas) {
+  if (save_item.color_filter)
+    return false;
+  if (save_item.xfermode != SkBlendMode::kSrcOver)
+    return false;
+  // TODO(enne): I believe that lcd_text_requires_opaque_layer is not
+  // relevant here and that lcd text is preserved post merge, but I haven't
+  // tested that.
+  const PaintRecord* record = draw_item.picture.get();
+  if (record->approximateOpCount() != 1)
+    return false;
+
+  const PaintOp* op = record->GetFirstOp();
+  if (!op->IsDrawOp())
+    return false;
+
+  op->RasterWithAlpha(canvas, save_item.alpha);
+  return true;
+}
+
 void DisplayItemList::Raster(SkCanvas* canvas,
                              SkPicture::AbortCallback* callback) const {
   gfx::Rect canvas_playback_rect;
@@ -184,14 +215,33 @@
 
   std::vector<size_t> indices;
   rtree_.Search(canvas_playback_rect, &indices);
-  for (size_t index : indices) {
-    RasterItem(items_[index], canvas, callback);
-
+  for (size_t i = 0; i < indices.size(); ++i) {
     // We use a callback during solid color analysis on the compositor thread to
     // break out early. Since we're handling a sequence of pictures via rtree
     // query results ourselves, we have to respect the callback and early out.
     if (callback && callback->abort())
       break;
+
+    const DisplayItem& item = items_[indices[i]];
+    // Optimize empty begin/end compositing and merge begin/draw/end compositing
+    // where possible.
+    // TODO(enne): remove empty clips here too?
+    // TODO(enne): does this happen recursively? Or is this good enough?
+    if (i < indices.size() - 2 && item.type == DisplayItem::COMPOSITING) {
+      const DisplayItem& second = items_[indices[i + 1]];
+      const DisplayItem& third = items_[indices[i + 2]];
+      if (second.type == DisplayItem::DRAWING &&
+          third.type == DisplayItem::END_COMPOSITING) {
+        if (MergeAndDrawIfPossible(
+                static_cast<const CompositingDisplayItem&>(item),
+                static_cast<const DrawingDisplayItem&>(second), canvas)) {
+          i += 2;
+          continue;
+        }
+      }
+    }
+
+    RasterItem(item, canvas, callback);
   }
 }
 
diff --git a/cc/paint/display_item_list_unittest.cc b/cc/paint/display_item_list_unittest.cc
index f1b9e75..b166229c6 100644
--- a/cc/paint/display_item_list_unittest.cc
+++ b/cc/paint/display_item_list_unittest.cc
@@ -17,16 +17,17 @@
 #include "cc/paint/compositing_display_item.h"
 #include "cc/paint/drawing_display_item.h"
 #include "cc/paint/filter_display_item.h"
-
 #include "cc/paint/float_clip_display_item.h"
 #include "cc/paint/paint_canvas.h"
 #include "cc/paint/paint_flags.h"
 #include "cc/paint/paint_record.h"
 #include "cc/paint/paint_recorder.h"
+#include "cc/paint/skia_paint_canvas.h"
 #include "cc/paint/transform_display_item.h"
 #include "cc/test/geometry_test_utils.h"
 #include "cc/test/pixel_test_utils.h"
 #include "cc/test/skia_common.h"
+#include "cc/test/test_skcanvas.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/skia/include/core/SkBitmap.h"
@@ -80,6 +81,19 @@
   return recorder.finishRecordingAsPicture();
 }
 
+sk_sp<const PaintRecord> CreateRectPictureWithAlpha(const gfx::Rect& bounds,
+                                                    uint8_t alpha) {
+  PaintRecorder recorder;
+  PaintCanvas* canvas =
+      recorder.beginRecording(bounds.width(), bounds.height());
+  PaintFlags flags;
+  flags.setAlpha(alpha);
+  canvas->drawRect(
+      SkRect::MakeXYWH(bounds.x(), bounds.y(), bounds.width(), bounds.height()),
+      flags);
+  return recorder.finishRecordingAsPicture();
+}
+
 void AppendFirstSerializationTestPicture(scoped_refptr<DisplayItemList> list,
                                          const gfx::Size& layer_size) {
   gfx::PointF offset(2.f, 3.f);
@@ -704,4 +718,110 @@
   EXPECT_RECT_EQ(filter_bounds, list->VisualRectForTesting(3));
 }
 
+// Verify that raster time optimizations for compositing item / draw single op /
+// end compositing item can be collapsed together into a single draw op
+// with the opacity from the compositing item folded in.
+TEST(DisplayItemListTest, SaveDrawRestore) {
+  auto list = make_scoped_refptr(new DisplayItemList);
+
+  list->CreateAndAppendPairedBeginItem<CompositingDisplayItem>(
+      80, SkBlendMode::kSrcOver, nullptr, nullptr, false);
+  list->CreateAndAppendDrawingItem<DrawingDisplayItem>(
+      kVisualRect, CreateRectPictureWithAlpha(kVisualRect, 40));
+  list->CreateAndAppendPairedEndItem<EndCompositingDisplayItem>();
+  list->Finalize();
+
+  SaveCountingCanvas canvas;
+  list->Raster(&canvas, nullptr);
+
+  EXPECT_EQ(0, canvas.save_count_);
+  EXPECT_EQ(0, canvas.restore_count_);
+  EXPECT_EQ(gfx::RectToSkRect(kVisualRect), canvas.draw_rect_);
+
+  float expected_alpha = 80 * 40 / 255.f;
+  EXPECT_LE(std::abs(expected_alpha - canvas.paint_.getAlpha()), 1.f);
+}
+
+// Verify that compositing item / end compositing item is a noop.
+// Here we're testing that Skia does an optimization that skips
+// save/restore with nothing in between.  If skia stops doing this
+// then we should reimplement this optimization in display list raster.
+TEST(DisplayItemListTest, SaveRestoreNoops) {
+  auto list = make_scoped_refptr(new DisplayItemList);
+
+  list->CreateAndAppendPairedBeginItem<CompositingDisplayItem>(
+      80, SkBlendMode::kSrcOver, nullptr, nullptr, false);
+  list->CreateAndAppendPairedEndItem<EndCompositingDisplayItem>();
+  list->CreateAndAppendPairedBeginItem<CompositingDisplayItem>(
+      255, SkBlendMode::kSrcOver, nullptr, nullptr, false);
+  list->CreateAndAppendPairedEndItem<EndCompositingDisplayItem>();
+  list->CreateAndAppendPairedBeginItem<CompositingDisplayItem>(
+      255, SkBlendMode::kSrc, nullptr, nullptr, false);
+  list->CreateAndAppendPairedEndItem<EndCompositingDisplayItem>();
+  list->Finalize();
+
+  SaveCountingCanvas canvas;
+  list->Raster(&canvas, nullptr);
+
+  EXPECT_EQ(0, canvas.save_count_);
+  EXPECT_EQ(0, canvas.restore_count_);
+}
+
+// The same as SaveDrawRestore, but with save flags that prevent the
+// optimization.
+TEST(DisplayItemListTest, SaveDrawRestoreFail_BadSaveFlags) {
+  auto list = make_scoped_refptr(new DisplayItemList);
+
+  // Use a blend mode that's not compatible with the SaveDrawRestore
+  // optimization.
+  list->CreateAndAppendPairedBeginItem<CompositingDisplayItem>(
+      80, SkBlendMode::kSrc, nullptr, nullptr, false);
+  list->CreateAndAppendDrawingItem<DrawingDisplayItem>(
+      kVisualRect, CreateRectPictureWithAlpha(kVisualRect, 40));
+  list->CreateAndAppendPairedEndItem<EndCompositingDisplayItem>();
+  list->Finalize();
+
+  SaveCountingCanvas canvas;
+  list->Raster(&canvas, nullptr);
+
+  EXPECT_EQ(1, canvas.save_count_);
+  EXPECT_EQ(1, canvas.restore_count_);
+  EXPECT_EQ(gfx::RectToSkRect(kVisualRect), canvas.draw_rect_);
+  EXPECT_LE(40, canvas.paint_.getAlpha());
+}
+
+// The same as SaveDrawRestore, but with too many ops in the PaintRecord.
+TEST(DisplayItemListTest, SaveDrawRestoreFail_TooManyOps) {
+  sk_sp<const PaintRecord> record;
+  {
+    PaintRecorder recorder;
+    PaintCanvas* canvas =
+        recorder.beginRecording(kVisualRect.width(), kVisualRect.height());
+    PaintFlags flags;
+    flags.setAlpha(40);
+    canvas->drawRect(gfx::RectToSkRect(kVisualRect), flags);
+    // Add an extra op here.
+    canvas->drawRect(gfx::RectToSkRect(kVisualRect), flags);
+    record = recorder.finishRecordingAsPicture();
+  }
+  EXPECT_GT(record->approximateOpCount(), 1);
+
+  auto list = make_scoped_refptr(new DisplayItemList);
+
+  list->CreateAndAppendPairedBeginItem<CompositingDisplayItem>(
+      80, SkBlendMode::kSrcOver, nullptr, nullptr, false);
+  list->CreateAndAppendDrawingItem<DrawingDisplayItem>(kVisualRect,
+                                                       std::move(record));
+  list->CreateAndAppendPairedEndItem<EndCompositingDisplayItem>();
+  list->Finalize();
+
+  SaveCountingCanvas canvas;
+  list->Raster(&canvas, nullptr);
+
+  EXPECT_EQ(1, canvas.save_count_);
+  EXPECT_EQ(1, canvas.restore_count_);
+  EXPECT_EQ(gfx::RectToSkRect(kVisualRect), canvas.draw_rect_);
+  EXPECT_LE(40, canvas.paint_.getAlpha());
+}
+
 }  // namespace cc
diff --git a/cc/paint/paint_canvas.h b/cc/paint/paint_canvas.h
index 6b9a0df..d45e84b 100644
--- a/cc/paint/paint_canvas.h
+++ b/cc/paint/paint_canvas.h
@@ -11,19 +11,24 @@
 #include "build/build_config.h"
 #include "cc/paint/paint_export.h"
 #include "cc/paint/paint_image.h"
-#include "cc/paint/paint_record.h"
 #include "third_party/skia/include/core/SkCanvas.h"
 
 namespace cc {
 
 class DisplayItemList;
 class PaintFlags;
+class PaintOpBuffer;
+
+using PaintRecord = PaintOpBuffer;
 
 class CC_PAINT_EXPORT PaintCanvas {
  public:
   virtual ~PaintCanvas() {}
 
   virtual SkMetaData& getMetaData() = 0;
+
+  // TODO(enne): this only appears to mostly be used to determine if this is
+  // recording or not, so could be simplified or removed.
   virtual SkImageInfo imageInfo() const = 0;
 
   // TODO(enne): It would be nice to get rid of flush() entirely, as it
@@ -42,7 +47,7 @@
                            int y) = 0;
   virtual int save() = 0;
   virtual int saveLayer(const SkRect* bounds, const PaintFlags* flags) = 0;
-  virtual int saveLayerAlpha(const SkRect* bounds, U8CPU alpha) = 0;
+  virtual int saveLayerAlpha(const SkRect* bounds, uint8_t alpha) = 0;
 
   virtual void restore() = 0;
   virtual int getSaveCount() const = 0;
@@ -93,6 +98,8 @@
   virtual bool getDeviceClipBounds(SkIRect* bounds) const = 0;
   virtual void drawColor(SkColor color, SkBlendMode mode) = 0;
   void drawColor(SkColor color) { drawColor(color, SkBlendMode::kSrcOver); }
+
+  // TODO(enne): This is a synonym for drawColor with kSrc.  Remove it.
   virtual void clear(SkColor color) = 0;
 
   virtual void drawLine(SkScalar x0,
diff --git a/cc/paint/paint_flags.cc b/cc/paint/paint_flags.cc
new file mode 100644
index 0000000..e16a8bb
--- /dev/null
+++ b/cc/paint/paint_flags.cc
@@ -0,0 +1,42 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "cc/paint/paint_flags.h"
+
+namespace cc {
+
+bool PaintFlags::IsSimpleOpacity() const {
+  uint32_t color = getColor();
+  if (SK_ColorTRANSPARENT != SkColorSetA(color, SK_AlphaTRANSPARENT))
+    return false;
+  if (!isSrcOver())
+    return false;
+  if (getLooper())
+    return false;
+  if (getPathEffect())
+    return false;
+  if (getShader())
+    return false;
+  if (getMaskFilter())
+    return false;
+  if (getColorFilter())
+    return false;
+  if (getImageFilter())
+    return false;
+  return true;
+}
+
+bool PaintFlags::SupportsFoldingAlpha() const {
+  if (!isSrcOver())
+    return false;
+  if (getColorFilter())
+    return false;
+  if (getImageFilter())
+    return false;
+  if (getLooper())
+    return false;
+  return true;
+}
+
+}  // namespace cc
diff --git a/cc/paint/paint_flags.h b/cc/paint/paint_flags.h
index b7e96c6..37b460d6 100644
--- a/cc/paint/paint_flags.h
+++ b/cc/paint/paint_flags.h
@@ -7,7 +7,6 @@
 
 #include "base/compiler_specific.h"
 #include "cc/paint/paint_export.h"
-#include "cc/paint/paint_shader.h"
 #include "third_party/skia/include/core/SkCanvas.h"
 #include "third_party/skia/include/core/SkColorFilter.h"
 #include "third_party/skia/include/core/SkDrawLooper.h"
@@ -19,6 +18,8 @@
 
 namespace cc {
 
+using PaintShader = SkShader;
+
 class CC_PAINT_EXPORT PaintFlags {
  public:
   enum Style {
@@ -198,6 +199,14 @@
     return paint_.computeFastBounds(orig, storage);
   }
 
+  bool operator==(const PaintFlags& flags) { return flags.paint_ == paint_; }
+  bool operator!=(const PaintFlags& flags) { return flags.paint_ != paint_; }
+
+  // Returns true if this just represents an opacity blend when
+  // used as saveLayer flags.
+  bool IsSimpleOpacity() const;
+  bool SupportsFoldingAlpha() const;
+
  private:
   friend const SkPaint& ToSkPaint(const PaintFlags& flags);
   friend const SkPaint* ToSkPaint(const PaintFlags* flags);
diff --git a/cc/paint/paint_image.cc b/cc/paint/paint_image.cc
index 70644ed..d34d05d 100644
--- a/cc/paint/paint_image.cc
+++ b/cc/paint/paint_image.cc
@@ -12,6 +12,9 @@
     : sk_image_(std::move(sk_image)),
       animation_type_(animation_type),
       completion_state_(completion_state) {}
+
+PaintImage::PaintImage(const PaintImage& image) = default;
+
 PaintImage::~PaintImage() = default;
 
 }  // namespace cc
diff --git a/cc/paint/paint_image.h b/cc/paint/paint_image.h
index 0ed1edd..c772316 100644
--- a/cc/paint/paint_image.h
+++ b/cc/paint/paint_image.h
@@ -23,6 +23,7 @@
   PaintImage(sk_sp<const SkImage> sk_image,
              AnimationType animation_type,
              CompletionState completion_state);
+  PaintImage(const PaintImage& image);
   ~PaintImage();
 
   const sk_sp<const SkImage>& sk_image() const { return sk_image_; }
diff --git a/cc/paint/paint_op_buffer.cc b/cc/paint/paint_op_buffer.cc
new file mode 100644
index 0000000..a98392a
--- /dev/null
+++ b/cc/paint/paint_op_buffer.cc
@@ -0,0 +1,556 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "cc/paint/paint_op_buffer.h"
+
+#include "cc/paint/display_item_list.h"
+#include "cc/paint/paint_record.h"
+#include "third_party/skia/include/core/SkAnnotation.h"
+
+namespace cc {
+
+#define TYPES(M)           \
+  M(AnnotateOp)            \
+  M(ClipPathOp)            \
+  M(ClipRectOp)            \
+  M(ClipRRectOp)           \
+  M(ConcatOp)              \
+  M(DrawArcOp)             \
+  M(DrawCircleOp)          \
+  M(DrawColorOp)           \
+  M(DrawDisplayItemListOp) \
+  M(DrawDRRectOp)          \
+  M(DrawImageOp)           \
+  M(DrawImageRectOp)       \
+  M(DrawIRectOp)           \
+  M(DrawLineOp)            \
+  M(DrawOvalOp)            \
+  M(DrawPathOp)            \
+  M(DrawPosTextOp)         \
+  M(DrawRecordOp)          \
+  M(DrawRectOp)            \
+  M(DrawRRectOp)           \
+  M(DrawTextOp)            \
+  M(DrawTextBlobOp)        \
+  M(NoopOp)                \
+  M(RestoreOp)             \
+  M(RotateOp)              \
+  M(SaveOp)                \
+  M(SaveLayerOp)           \
+  M(SaveLayerAlphaOp)      \
+  M(ScaleOp)               \
+  M(SetMatrixOp)           \
+  M(TranslateOp)
+
+// Helper template to share common code for RasterWithAlpha when paint ops
+// have or don't have PaintFlags.
+template <typename T, bool HasFlags>
+struct Rasterizer {
+  static void Raster(const T* op,
+                     SkCanvas* canvas,
+                     const SkMatrix& original_ctm) {
+    // Paint ops with kHasPaintFlags need to declare RasterWithPaintFlags
+    // otherwise, the paint op needs its own Raster function.  Without its
+    // own, this becomes an infinite loop as PaintOp::Raster calls itself.
+    static_assert(
+        !std::is_same<decltype(&PaintOp::Raster), decltype(&T::Raster)>::value,
+        "No Raster function");
+
+    op->Raster(canvas);
+  }
+  static void RasterWithAlpha(const T* op, SkCanvas* canvas, uint8_t alpha) {
+    DCHECK(T::kIsDrawOp);
+    // TODO(enne): is it ok to just drop the bounds here?
+    canvas->saveLayerAlpha(nullptr, alpha);
+    op->Raster(canvas);
+    canvas->restore();
+  }
+};
+
+template <typename T>
+struct Rasterizer<T, true> {
+  static void Raster(const T* op,
+                     SkCanvas* canvas,
+                     const SkMatrix& original_ctm) {
+    op->RasterWithFlags(canvas, op->flags);
+  }
+  static void RasterWithAlpha(const T* op, SkCanvas* canvas, uint8_t alpha) {
+    DCHECK(T::kIsDrawOp);
+    SkMatrix unused_matrix;
+    if (alpha == 255) {
+      Raster(op, canvas, unused_matrix);
+    } else if (op->flags.SupportsFoldingAlpha()) {
+      PaintFlags flags = op->flags;
+      flags.setAlpha(SkMulDiv255Round(flags.getAlpha(), alpha));
+      op->RasterWithFlags(canvas, flags);
+    } else {
+      canvas->saveLayerAlpha(nullptr, alpha);
+      op->RasterWithFlags(canvas, op->flags);
+      canvas->restore();
+    }
+  }
+};
+
+template <>
+struct Rasterizer<SetMatrixOp, false> {
+  static void Raster(const SetMatrixOp* op,
+                     SkCanvas* canvas,
+                     const SkMatrix& original_ctm) {
+    op->Raster(canvas, original_ctm);
+  }
+  static void RasterWithAlpha(const SetMatrixOp* op,
+                              SkCanvas* canvas,
+                              uint8_t alpha) {
+    NOTREACHED();
+  }
+};
+
+template <>
+struct Rasterizer<DrawRecordOp, false> {
+  static void Raster(const DrawRecordOp* op,
+                     SkCanvas* canvas,
+                     const SkMatrix& original_ctm) {
+    op->Raster(canvas);
+  }
+  static void RasterWithAlpha(const DrawRecordOp* op,
+                              SkCanvas* canvas,
+                              uint8_t alpha) {
+    // This "looking into records" optimization is done here instead of
+    // in the PaintOpBuffer::Raster function as DisplayItemList calls
+    // into RasterWithAlpha directly.
+    if (op->record->approximateOpCount() == 1) {
+      op->record->GetFirstOp()->RasterWithAlpha(canvas, alpha);
+      return;
+    }
+
+    canvas->saveLayerAlpha(nullptr, alpha);
+    op->Raster(canvas);
+    canvas->restore();
+  }
+};
+
+// TODO(enne): partially specialize RasterWithAlpha for draw color?
+
+static constexpr size_t kNumOpTypes =
+    static_cast<size_t>(PaintOpType::LastPaintOpType) + 1;
+
+// Verify that every op is in the TYPES macro.
+#define M(T) +1
+static_assert(kNumOpTypes == TYPES(M), "Missing op in list");
+#undef M
+
+using RasterFunction = void (*)(const PaintOp* op,
+                                SkCanvas* canvas,
+                                const SkMatrix& original_ctm);
+#define M(T)                                                              \
+  [](const PaintOp* op, SkCanvas* canvas, const SkMatrix& original_ctm) { \
+    Rasterizer<T, T::kHasPaintFlags>::Raster(static_cast<const T*>(op),   \
+                                             canvas, original_ctm);       \
+  },
+static const RasterFunction g_raster_functions[kNumOpTypes] = {TYPES(M)};
+#undef M
+
+using RasterAlphaFunction = void (*)(const PaintOp* op,
+                                     SkCanvas* canvas,
+                                     uint8_t alpha);
+#define M(T) \
+  T::kIsDrawOp ? \
+  [](const PaintOp* op, SkCanvas* canvas, uint8_t alpha) { \
+    Rasterizer<T, T::kHasPaintFlags>::RasterWithAlpha( \
+        static_cast<const T*>(op), canvas, alpha); \
+  } : static_cast<RasterAlphaFunction>(nullptr),
+static const RasterAlphaFunction g_raster_alpha_functions[kNumOpTypes] = {
+    TYPES(M)};
+#undef M
+
+// Most state ops (matrix, clip, save, restore) have a trivial destructor.
+// TODO(enne): evaluate if we need the nullptr optimization or if
+// we even need to differentiate trivial destructors here.
+using VoidFunction = void (*)(PaintOp* op);
+#define M(T)                                           \
+  !std::is_trivially_destructible<T>::value            \
+      ? [](PaintOp* op) { static_cast<T*>(op)->~T(); } \
+      : static_cast<VoidFunction>(nullptr),
+static const VoidFunction g_destructor_functions[kNumOpTypes] = {TYPES(M)};
+#undef M
+
+#define M(T) T::kIsDrawOp,
+static bool g_is_draw_op[kNumOpTypes] = {TYPES(M)};
+#undef M
+
+#define M(T)                                         \
+  static_assert(sizeof(T) <= sizeof(LargestPaintOp), \
+                #T " must be no bigger than LargestPaintOp");
+TYPES(M);
+#undef M
+
+#undef TYPES
+
+SkRect PaintOp::kUnsetRect = {SK_ScalarInfinity, 0, 0, 0};
+
+void AnnotateOp::Raster(SkCanvas* canvas) const {
+  switch (annotation_type) {
+    case PaintCanvas::AnnotationType::URL:
+      SkAnnotateRectWithURL(canvas, rect, data.get());
+      break;
+    case PaintCanvas::AnnotationType::LINK_TO_DESTINATION:
+      SkAnnotateLinkToDestination(canvas, rect, data.get());
+      break;
+    case PaintCanvas::AnnotationType::NAMED_DESTINATION: {
+      SkPoint point = SkPoint::Make(rect.x(), rect.y());
+      SkAnnotateNamedDestination(canvas, point, data.get());
+      break;
+    }
+  }
+}
+
+void ClipPathOp::Raster(SkCanvas* canvas) const {
+  canvas->clipPath(path, op, antialias);
+}
+
+void ClipRectOp::Raster(SkCanvas* canvas) const {
+  canvas->clipRect(rect, op, antialias);
+}
+
+void ClipRRectOp::Raster(SkCanvas* canvas) const {
+  canvas->clipRRect(rrect, op, antialias);
+}
+
+void ConcatOp::Raster(SkCanvas* canvas) const {
+  canvas->concat(matrix);
+}
+
+void DrawArcOp::RasterWithFlags(SkCanvas* canvas,
+                                const PaintFlags& flags) const {
+  canvas->drawArc(oval, start_angle, sweep_angle, use_center, ToSkPaint(flags));
+}
+
+void DrawCircleOp::RasterWithFlags(SkCanvas* canvas,
+                                   const PaintFlags& flags) const {
+  canvas->drawCircle(cx, cy, radius, ToSkPaint(flags));
+}
+
+void DrawColorOp::Raster(SkCanvas* canvas) const {
+  canvas->drawColor(color, mode);
+}
+
+void DrawDisplayItemListOp::Raster(SkCanvas* canvas) const {
+  list->Raster(canvas, nullptr);
+}
+
+void DrawDRRectOp::RasterWithFlags(SkCanvas* canvas,
+                                   const PaintFlags& flags) const {
+  canvas->drawDRRect(outer, inner, ToSkPaint(flags));
+}
+
+void DrawImageOp::RasterWithFlags(SkCanvas* canvas,
+                                  const PaintFlags& flags) const {
+  canvas->drawImage(image.sk_image().get(), left, top, ToSkPaint(&flags));
+}
+
+void DrawImageRectOp::RasterWithFlags(SkCanvas* canvas,
+                                      const PaintFlags& flags) const {
+  // TODO(enne): Probably PaintCanvas should just use the skia enum directly.
+  SkCanvas::SrcRectConstraint skconstraint =
+      static_cast<SkCanvas::SrcRectConstraint>(constraint);
+  canvas->drawImageRect(image.sk_image().get(), src, dst, ToSkPaint(&flags),
+                        skconstraint);
+}
+
+void DrawIRectOp::RasterWithFlags(SkCanvas* canvas,
+                                  const PaintFlags& flags) const {
+  canvas->drawIRect(rect, ToSkPaint(flags));
+}
+
+void DrawLineOp::RasterWithFlags(SkCanvas* canvas,
+                                 const PaintFlags& flags) const {
+  canvas->drawLine(x0, y0, x1, y1, ToSkPaint(flags));
+}
+
+void DrawOvalOp::RasterWithFlags(SkCanvas* canvas,
+                                 const PaintFlags& flags) const {
+  canvas->drawOval(oval, ToSkPaint(flags));
+}
+
+void DrawPathOp::RasterWithFlags(SkCanvas* canvas,
+                                 const PaintFlags& flags) const {
+  canvas->drawPath(path, ToSkPaint(flags));
+}
+
+void DrawPosTextOp::RasterWithFlags(SkCanvas* canvas,
+                                    const PaintFlags& flags) const {
+  canvas->drawPosText(paint_op_data(this), bytes, paint_op_array<SkPoint>(this),
+                      ToSkPaint(flags));
+}
+
+void DrawRecordOp::Raster(SkCanvas* canvas) const {
+  record->playback(canvas);
+}
+
+void DrawRectOp::RasterWithFlags(SkCanvas* canvas,
+                                 const PaintFlags& flags) const {
+  canvas->drawRect(rect, ToSkPaint(flags));
+}
+
+void DrawRRectOp::RasterWithFlags(SkCanvas* canvas,
+                                  const PaintFlags& flags) const {
+  canvas->drawRRect(rrect, ToSkPaint(flags));
+}
+
+void DrawTextOp::RasterWithFlags(SkCanvas* canvas,
+                                 const PaintFlags& flags) const {
+  canvas->drawText(paint_op_data(this), bytes, x, y, ToSkPaint(flags));
+}
+
+void DrawTextBlobOp::RasterWithFlags(SkCanvas* canvas,
+                                     const PaintFlags& flags) const {
+  canvas->drawTextBlob(blob.get(), x, y, ToSkPaint(flags));
+}
+
+void RestoreOp::Raster(SkCanvas* canvas) const {
+  canvas->restore();
+}
+
+void RotateOp::Raster(SkCanvas* canvas) const {
+  canvas->rotate(degrees);
+}
+
+void SaveOp::Raster(SkCanvas* canvas) const {
+  canvas->save();
+}
+
+void SaveLayerOp::RasterWithFlags(SkCanvas* canvas,
+                                  const PaintFlags& flags) const {
+  // See PaintOp::kUnsetRect
+  bool unset = bounds.left() == SK_ScalarInfinity;
+
+  canvas->saveLayer(unset ? nullptr : &bounds, ToSkPaint(&flags));
+}
+
+void SaveLayerAlphaOp::Raster(SkCanvas* canvas) const {
+  // See PaintOp::kUnsetRect
+  bool unset = bounds.left() == SK_ScalarInfinity;
+  canvas->saveLayerAlpha(unset ? nullptr : &bounds, alpha);
+}
+
+void ScaleOp::Raster(SkCanvas* canvas) const {
+  canvas->scale(sx, sy);
+}
+
+void SetMatrixOp::Raster(SkCanvas* canvas, const SkMatrix& original_ctm) const {
+  canvas->setMatrix(SkMatrix::Concat(original_ctm, matrix));
+}
+
+void TranslateOp::Raster(SkCanvas* canvas) const {
+  canvas->translate(dx, dy);
+}
+
+bool PaintOp::IsDrawOp() const {
+  return g_is_draw_op[type];
+}
+
+void PaintOp::Raster(SkCanvas* canvas, const SkMatrix& original_ctm) const {
+  g_raster_functions[type](this, canvas, original_ctm);
+}
+
+void PaintOp::RasterWithAlpha(SkCanvas* canvas, uint8_t alpha) const {
+  g_raster_alpha_functions[type](this, canvas, alpha);
+}
+
+DrawDisplayItemListOp::DrawDisplayItemListOp(
+    scoped_refptr<DisplayItemList> list)
+    : list(list) {}
+
+size_t DrawDisplayItemListOp::AdditionalBytesUsed() const {
+  return list->ApproximateMemoryUsage();
+}
+
+int ClipPathOp::CountSlowPaths() const {
+  return antialias && !path.isConvex() ? 1 : 0;
+}
+
+int DrawLineOp::CountSlowPaths() const {
+  if (const SkPathEffect* effect = flags.getPathEffect()) {
+    SkPathEffect::DashInfo info;
+    SkPathEffect::DashType dashType = effect->asADash(&info);
+    if (flags.getStrokeCap() != PaintFlags::kRound_Cap &&
+        dashType == SkPathEffect::kDash_DashType && info.fCount == 2) {
+      // The PaintFlags will count this as 1, so uncount that here as
+      // this kind of line is special cased and not slow.
+      return -1;
+    }
+  }
+  return 0;
+}
+
+int DrawPathOp::CountSlowPaths() const {
+  // This logic is copied from SkPathCounter instead of attempting to expose
+  // that from Skia.
+  if (!flags.isAntiAlias() || path.isConvex())
+    return 0;
+
+  PaintFlags::Style paintStyle = flags.getStyle();
+  const SkRect& pathBounds = path.getBounds();
+  if (paintStyle == PaintFlags::kStroke_Style && flags.getStrokeWidth() == 0) {
+    // AA hairline concave path is not slow.
+    return 0;
+  } else if (paintStyle == PaintFlags::kFill_Style &&
+             pathBounds.width() < 64.f && pathBounds.height() < 64.f &&
+             !path.isVolatile()) {
+    // AADF eligible concave path is not slow.
+    return 0;
+  } else {
+    return 1;
+  }
+}
+
+AnnotateOp::AnnotateOp(PaintCanvas::AnnotationType annotation_type,
+                       const SkRect& rect,
+                       sk_sp<SkData> data)
+    : annotation_type(annotation_type), rect(rect), data(std::move(data)) {}
+
+AnnotateOp::~AnnotateOp() = default;
+
+DrawDisplayItemListOp::~DrawDisplayItemListOp() = default;
+
+DrawImageOp::DrawImageOp(const PaintImage& image,
+                         SkScalar left,
+                         SkScalar top,
+                         const PaintFlags* flags)
+    : image(image),
+      left(left),
+      top(top),
+      flags(flags ? *flags : PaintFlags()) {}
+
+DrawImageOp::~DrawImageOp() = default;
+
+DrawImageRectOp::DrawImageRectOp(const PaintImage& image,
+                                 const SkRect& src,
+                                 const SkRect& dst,
+                                 const PaintFlags* flags,
+                                 PaintCanvas::SrcRectConstraint constraint)
+    : image(image),
+      flags(flags ? *flags : PaintFlags()),
+      src(src),
+      dst(dst),
+      constraint(constraint) {}
+
+DrawImageRectOp::~DrawImageRectOp() = default;
+
+DrawPosTextOp::DrawPosTextOp(size_t bytes,
+                             size_t count,
+                             const PaintFlags& flags)
+    : PaintOpWithDataArray(bytes, count), flags(flags) {}
+
+DrawPosTextOp::~DrawPosTextOp() = default;
+
+DrawRecordOp::DrawRecordOp(sk_sp<const PaintRecord> record)
+    : record(std::move(record)) {}
+
+DrawRecordOp::~DrawRecordOp() = default;
+
+size_t DrawRecordOp::AdditionalBytesUsed() const {
+  return record->approximateBytesUsed();
+}
+
+DrawTextBlobOp::DrawTextBlobOp(sk_sp<SkTextBlob> blob,
+                               SkScalar x,
+                               SkScalar y,
+                               const PaintFlags& flags)
+    : blob(std::move(blob)), x(x), y(y), flags(flags) {}
+
+DrawTextBlobOp::~DrawTextBlobOp() = default;
+
+PaintOpBuffer::PaintOpBuffer() : cull_rect_(SkRect::MakeEmpty()) {}
+
+PaintOpBuffer::PaintOpBuffer(const SkRect& cull_rect) : cull_rect_(cull_rect) {}
+
+PaintOpBuffer::~PaintOpBuffer() {
+  Reset();
+}
+
+void PaintOpBuffer::Reset() {
+  for (auto* op : Iterator(this)) {
+    auto func = g_destructor_functions[op->type];
+    if (func)
+      func(op);
+  }
+
+  // Leave data_ allocated, reserved_ unchanged.
+  used_ = 0;
+  op_count_ = 0;
+  num_slow_paths_ = 0;
+}
+
+void PaintOpBuffer::playback(SkCanvas* canvas) const {
+  // TODO(enne): a PaintRecord that contains a SetMatrix assumes that the
+  // SetMatrix is local to that PaintRecord itself.  Said differently, if you
+  // translate(x, y), then draw a paint record with a SetMatrix(identity),
+  // the translation should be preserved instead of clobbering the top level
+  // transform.  This could probably be done more efficiently.
+  SkMatrix original = canvas->getTotalMatrix();
+
+  for (Iterator iter(this); iter; ++iter) {
+    // Optimize out save/restores or save/draw/restore that can be a single
+    // draw.  See also: similar code in SkRecordOpts and cc's DisplayItemList.
+    // TODO(enne): consider making this recursive?
+    const PaintOp* op = *iter;
+    if (op->GetType() == PaintOpType::SaveLayerAlpha) {
+      const PaintOp* second = iter.peek1();
+      if (second) {
+        if (second->GetType() == PaintOpType::Restore) {
+          ++iter;
+          continue;
+        }
+        if (second->IsDrawOp()) {
+          const PaintOp* third = iter.peek2();
+          if (third && third->GetType() == PaintOpType::Restore) {
+            const SaveLayerAlphaOp* save_op =
+                static_cast<const SaveLayerAlphaOp*>(op);
+            second->RasterWithAlpha(canvas, save_op->alpha);
+            ++iter;
+            ++iter;
+            continue;
+          }
+        }
+      }
+    }
+    // TODO(enne): skip SaveLayer followed by restore with nothing in
+    // between, however SaveLayer with image filters on it (or maybe
+    // other PaintFlags options) are not a noop.  Figure out what these
+    // are so we can skip them correctly.
+
+    op->Raster(canvas, original);
+  }
+}
+
+void PaintOpBuffer::playback(SkCanvas* canvas,
+                             SkPicture::AbortCallback* callback) const {
+  // The abort callback is only used for analysis, in general, so
+  // this playback code can be more straightforward and not do the
+  // optimizations in the other function.
+  if (!callback) {
+    playback(canvas);
+    return;
+  }
+
+  SkMatrix original = canvas->getTotalMatrix();
+
+  // TODO(enne): ideally callers would just iterate themselves and we
+  // can remove the entire notion of an abort callback.
+  for (auto* op : Iterator(this)) {
+    op->Raster(canvas, original);
+    if (callback && callback->abort())
+      return;
+  }
+}
+
+void PaintOpBuffer::ShrinkToFit() {
+  if (!used_ || used_ == reserved_)
+    return;
+  data_.realloc(used_);
+  reserved_ = used_;
+}
+
+}  // namespace cc
diff --git a/cc/paint/paint_op_buffer.h b/cc/paint/paint_op_buffer.h
new file mode 100644
index 0000000..5075cf4
--- /dev/null
+++ b/cc/paint/paint_op_buffer.h
@@ -0,0 +1,782 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CC_PAINT_PAINT_OP_BUFFER_H_
+#define CC_PAINT_PAINT_OP_BUFFER_H_
+
+#include <stdint.h>
+
+#include "base/logging.h"
+#include "cc/paint/paint_canvas.h"
+#include "cc/paint/paint_export.h"
+#include "cc/paint/paint_flags.h"
+#include "third_party/skia/include/core/SkPicture.h"
+#include "third_party/skia/include/core/SkRect.h"
+#include "third_party/skia/include/core/SkTextBlob.h"
+
+// PaintOpBuffer is a reimplementation of SkLiteDL.
+// See: third_party/skia/src/core/SkLiteDL.h.
+
+namespace cc {
+
+class DisplayItemList;
+
+class ThreadsafeMatrix : public SkMatrix {
+ public:
+  explicit ThreadsafeMatrix(const SkMatrix& matrix) : SkMatrix(matrix) {
+    (void)getType();
+  }
+};
+
+class ThreadsafePath : public SkPath {
+ public:
+  explicit ThreadsafePath(const SkPath& path) : SkPath(path) {
+    updateBoundsCache();
+  }
+};
+
+enum class PaintOpType : uint8_t {
+  Annotate,
+  ClipPath,
+  ClipRect,
+  ClipRRect,
+  Concat,
+  DrawArc,
+  DrawCircle,
+  DrawColor,
+  DrawDisplayItemList,
+  DrawDRRect,
+  DrawImage,
+  DrawImageRect,
+  DrawIRect,
+  DrawLine,
+  DrawOval,
+  DrawPath,
+  DrawPosText,
+  DrawRecord,
+  DrawRect,
+  DrawRRect,
+  DrawText,
+  DrawTextBlob,
+  Noop,
+  Restore,
+  Rotate,
+  Save,
+  SaveLayer,
+  SaveLayerAlpha,
+  Scale,
+  SetMatrix,
+  Translate,
+  LastPaintOpType = Translate,
+};
+
+struct CC_PAINT_EXPORT PaintOp {
+  uint32_t type : 8;
+  uint32_t skip : 24;
+
+  PaintOpType GetType() const { return static_cast<PaintOpType>(type); }
+
+  void Raster(SkCanvas* canvas, const SkMatrix& original_ctm) const;
+  bool IsDrawOp() const;
+
+  // Only valid for draw ops.
+  void RasterWithAlpha(SkCanvas* canvas, uint8_t alpha) const;
+
+  int CountSlowPaths() const { return 0; }
+
+  // Returns the number of bytes used by this op in referenced sub records
+  // and display lists.  This doesn't count other objects like paths or blobs.
+  size_t AdditionalBytesUsed() const { return 0; }
+
+  static constexpr bool kIsDrawOp = false;
+  // If an op has |kHasPaintFlags| set to true, it must:
+  // (1) Provide a PaintFlags member called |flags|
+  // (2) Provide a RasterWithFlags function instead of a Raster function.
+  static constexpr bool kHasPaintFlags = false;
+  static SkRect kUnsetRect;
+};
+
+struct PaintOpWithData : PaintOp {
+  // Having data is just a helper for ops that have a varying amount of data and
+  // want a way to store that inline.  This is for ops that pass in a
+  // void* and a length.
+  explicit PaintOpWithData(size_t bytes) : bytes(bytes) {}
+
+  // Get data out by calling paint_op_data.  This can't be part of the class
+  // because it needs to know the size of the derived type.
+  size_t bytes;
+};
+
+template <typename T>
+const void* paint_op_data(const T* op) {
+  static_assert(std::is_convertible<T, PaintOpWithData>::value,
+                "T is not a PaintOpWithData");
+  // Arbitrary data for a PaintOp is stored after the PaintOp itself
+  // in the PaintOpBuffer.  Therefore, to access this data, it's
+  // pointer math to increment past the size of T.  Accessing the
+  // next op in the buffer is ((char*)op) + op->skip, with the data
+  // fitting between.
+  return op + 1;
+}
+
+template <typename T>
+void* paint_op_data(T* op) {
+  static_assert(std::is_convertible<T, PaintOpWithData>::value,
+                "T is not a PaintOpWithData");
+  return op + 1;
+}
+
+struct PaintOpWithDataArrayBase : PaintOpWithData {
+  // Helper class for static asserts in push functions.
+  using PaintOpWithData::PaintOpWithData;
+};
+
+template <typename T>
+struct PaintOpWithDataArray : PaintOpWithDataArrayBase {
+  // Paint op that has a T[count] and a char[bytes].
+  PaintOpWithDataArray(size_t bytes, size_t count)
+      : PaintOpWithDataArrayBase(bytes), count(count) {}
+  // Use paint_op_array to get array data.
+
+  size_t count;
+};
+
+template <typename M, typename T>
+const M* paint_op_array(const T* op) {
+  static_assert(std::is_convertible<T, PaintOpWithDataArrayBase>::value,
+                "T is not a PaintOpWithDataArray");
+  // See comment in paint_op_data.  Array data is stored after
+  // any void* data.  Memory layout here is: |op|data|array data|next op|
+  return SkTAddOffset<const M>(op + 1, op->bytes);
+}
+template <typename M, typename T>
+M* paint_op_array(T* op) {
+  static_assert(std::is_convertible<T, PaintOpWithDataArrayBase>::value,
+                "T is not a PaintOpWithDataArray");
+  return SkTAddOffset<M>(op + 1, op->bytes);
+}
+
+struct AnnotateOp final : PaintOp {
+  enum class AnnotationType {
+    URL,
+    LinkToDestination,
+    NamedDestination,
+  };
+
+  static constexpr PaintOpType kType = PaintOpType::Annotate;
+  AnnotateOp(PaintCanvas::AnnotationType annotation_type,
+             const SkRect& rect,
+             sk_sp<SkData> data);
+  ~AnnotateOp();
+  void Raster(SkCanvas* canvas) const;
+
+  PaintCanvas::AnnotationType annotation_type;
+  SkRect rect;
+  sk_sp<SkData> data;
+};
+
+struct ClipPathOp final : PaintOp {
+  static constexpr PaintOpType kType = PaintOpType::ClipPath;
+  ClipPathOp(SkPath path, SkClipOp op, bool antialias)
+      : path(path), op(op), antialias(antialias) {}
+  void Raster(SkCanvas* canvas) const;
+  int CountSlowPaths() const;
+
+  ThreadsafePath path;
+  SkClipOp op;
+  bool antialias;
+};
+
+struct ClipRectOp final : PaintOp {
+  static constexpr PaintOpType kType = PaintOpType::ClipRect;
+  ClipRectOp(const SkRect& rect, SkClipOp op, bool antialias)
+      : rect(rect), op(op), antialias(antialias) {}
+  void Raster(SkCanvas* canvas) const;
+
+  SkRect rect;
+  SkClipOp op;
+  bool antialias;
+};
+
+struct ClipRRectOp final : PaintOp {
+  static constexpr PaintOpType kType = PaintOpType::ClipRRect;
+  ClipRRectOp(const SkRRect& rrect, SkClipOp op, bool antialias)
+      : rrect(rrect), op(op), antialias(antialias) {}
+  void Raster(SkCanvas* canvas) const;
+
+  SkRRect rrect;
+  SkClipOp op;
+  bool antialias;
+};
+
+struct ConcatOp final : PaintOp {
+  static constexpr PaintOpType kType = PaintOpType::Concat;
+  explicit ConcatOp(const SkMatrix& matrix) : matrix(matrix) {}
+  void Raster(SkCanvas* canvas) const;
+
+  ThreadsafeMatrix matrix;
+};
+
+struct DrawArcOp final : PaintOp {
+  static constexpr PaintOpType kType = PaintOpType::DrawArc;
+  static constexpr bool kIsDrawOp = true;
+  static constexpr bool kHasPaintFlags = true;
+  DrawArcOp(const SkRect& oval,
+            SkScalar start_angle,
+            SkScalar sweep_angle,
+            bool use_center,
+            const PaintFlags& flags)
+      : oval(oval),
+        start_angle(start_angle),
+        sweep_angle(sweep_angle),
+        use_center(use_center),
+        flags(flags) {}
+  void RasterWithFlags(SkCanvas* canvas, const PaintFlags& flags) const;
+
+  SkRect oval;
+  SkScalar start_angle;
+  SkScalar sweep_angle;
+  bool use_center;
+  PaintFlags flags;
+};
+
+struct DrawCircleOp final : PaintOp {
+  static constexpr PaintOpType kType = PaintOpType::DrawCircle;
+  static constexpr bool kIsDrawOp = true;
+  static constexpr bool kHasPaintFlags = true;
+  DrawCircleOp(SkScalar cx,
+               SkScalar cy,
+               SkScalar radius,
+               const PaintFlags& flags)
+      : cx(cx), cy(cy), radius(radius), flags(flags) {}
+  void RasterWithFlags(SkCanvas* canvas, const PaintFlags& flags) const;
+
+  SkScalar cx;
+  SkScalar cy;
+  SkScalar radius;
+  PaintFlags flags;
+};
+
+struct DrawColorOp final : PaintOp {
+  static constexpr PaintOpType kType = PaintOpType::DrawColor;
+  static constexpr bool kIsDrawOp = true;
+  DrawColorOp(SkColor color, SkBlendMode mode) : color(color), mode(mode) {}
+  void Raster(SkCanvas* canvas) const;
+
+  SkColor color;
+  SkBlendMode mode;
+};
+
+struct DrawDisplayItemListOp final : PaintOp {
+  static constexpr PaintOpType kType = PaintOpType::DrawDisplayItemList;
+  static constexpr bool kIsDrawOp = true;
+  explicit DrawDisplayItemListOp(scoped_refptr<DisplayItemList> list);
+  ~DrawDisplayItemListOp();
+  void Raster(SkCanvas* canvas) const;
+  size_t AdditionalBytesUsed() const;
+  // TODO(enne): DisplayItemList should know number of slow paths.
+
+  scoped_refptr<DisplayItemList> list;
+};
+
+struct DrawDRRectOp final : PaintOp {
+  static constexpr PaintOpType kType = PaintOpType::DrawDRRect;
+  static constexpr bool kIsDrawOp = true;
+  static constexpr bool kHasPaintFlags = true;
+  DrawDRRectOp(const SkRRect& outer,
+               const SkRRect& inner,
+               const PaintFlags& flags)
+      : outer(outer), inner(inner), flags(flags) {}
+  void RasterWithFlags(SkCanvas* canvas, const PaintFlags& flags) const;
+
+  SkRRect outer;
+  SkRRect inner;
+  PaintFlags flags;
+};
+
+struct DrawImageOp final : PaintOp {
+  static constexpr PaintOpType kType = PaintOpType::DrawImage;
+  static constexpr bool kIsDrawOp = true;
+  static constexpr bool kHasPaintFlags = true;
+  DrawImageOp(const PaintImage& image,
+              SkScalar left,
+              SkScalar top,
+              const PaintFlags* flags);
+  ~DrawImageOp();
+  void RasterWithFlags(SkCanvas* canvas, const PaintFlags& flags) const;
+
+  PaintImage image;
+  SkScalar left;
+  SkScalar top;
+  PaintFlags flags;
+};
+
+struct DrawImageRectOp final : PaintOp {
+  static constexpr PaintOpType kType = PaintOpType::DrawImageRect;
+  static constexpr bool kIsDrawOp = true;
+  static constexpr bool kHasPaintFlags = true;
+  DrawImageRectOp(const PaintImage& image,
+                  const SkRect& src,
+                  const SkRect& dst,
+                  const PaintFlags* flags,
+                  PaintCanvas::SrcRectConstraint constraint);
+  ~DrawImageRectOp();
+  void RasterWithFlags(SkCanvas* canvas, const PaintFlags& flags) const;
+
+  PaintImage image;
+  PaintFlags flags;
+  SkRect src;
+  SkRect dst;
+  PaintCanvas::SrcRectConstraint constraint;
+};
+
+struct DrawIRectOp final : PaintOp {
+  static constexpr PaintOpType kType = PaintOpType::DrawIRect;
+  static constexpr bool kIsDrawOp = true;
+  static constexpr bool kHasPaintFlags = true;
+  DrawIRectOp(const SkIRect& rect, const PaintFlags& flags)
+      : rect(rect), flags(flags) {}
+  void RasterWithFlags(SkCanvas* canvas, const PaintFlags& flags) const;
+
+  SkIRect rect;
+  PaintFlags flags;
+};
+
+struct DrawLineOp final : PaintOp {
+  static constexpr PaintOpType kType = PaintOpType::DrawLine;
+  static constexpr bool kIsDrawOp = true;
+  static constexpr bool kHasPaintFlags = true;
+  DrawLineOp(SkScalar x0,
+             SkScalar y0,
+             SkScalar x1,
+             SkScalar y1,
+             const PaintFlags& flags)
+      : x0(x0), y0(y0), x1(x1), y1(y1), flags(flags) {}
+  void RasterWithFlags(SkCanvas* canvas, const PaintFlags& flags) const;
+  int CountSlowPaths() const;
+
+  SkScalar x0;
+  SkScalar y0;
+  SkScalar x1;
+  SkScalar y1;
+  PaintFlags flags;
+};
+
+struct DrawOvalOp final : PaintOp {
+  static constexpr PaintOpType kType = PaintOpType::DrawOval;
+  static constexpr bool kIsDrawOp = true;
+  static constexpr bool kHasPaintFlags = true;
+  DrawOvalOp(const SkRect& oval, const PaintFlags& flags)
+      : oval(oval), flags(flags) {}
+  void RasterWithFlags(SkCanvas* canvas, const PaintFlags& flags) const;
+
+  SkRect oval;
+  PaintFlags flags;
+};
+
+struct DrawPathOp final : PaintOp {
+  static constexpr PaintOpType kType = PaintOpType::DrawPath;
+  static constexpr bool kIsDrawOp = true;
+  static constexpr bool kHasPaintFlags = true;
+  DrawPathOp(const SkPath& path, const PaintFlags& flags)
+      : path(path), flags(flags) {}
+  void RasterWithFlags(SkCanvas* canvas, const PaintFlags& flags) const;
+  int CountSlowPaths() const;
+
+  ThreadsafePath path;
+  PaintFlags flags;
+};
+
+struct DrawPosTextOp final : PaintOpWithDataArray<SkPoint> {
+  static constexpr PaintOpType kType = PaintOpType::DrawPosText;
+  static constexpr bool kIsDrawOp = true;
+  static constexpr bool kHasPaintFlags = true;
+  DrawPosTextOp(size_t bytes, size_t count, const PaintFlags& flags);
+  ~DrawPosTextOp();
+  void RasterWithFlags(SkCanvas* canvas, const PaintFlags& flags) const;
+
+  PaintFlags flags;
+};
+
+struct DrawRecordOp final : PaintOp {
+  static constexpr PaintOpType kType = PaintOpType::DrawRecord;
+  static constexpr bool kIsDrawOp = true;
+  explicit DrawRecordOp(sk_sp<const PaintRecord> record);
+  ~DrawRecordOp();
+  void Raster(SkCanvas* canvas) const;
+  size_t AdditionalBytesUsed() const;
+
+  sk_sp<const PaintRecord> record;
+};
+
+struct DrawRectOp final : PaintOp {
+  static constexpr PaintOpType kType = PaintOpType::DrawRect;
+  static constexpr bool kIsDrawOp = true;
+  static constexpr bool kHasPaintFlags = true;
+  DrawRectOp(const SkRect& rect, const PaintFlags& flags)
+      : rect(rect), flags(flags) {}
+  void RasterWithFlags(SkCanvas* canvas, const PaintFlags& flags) const;
+
+  SkRect rect;
+  PaintFlags flags;
+};
+
+struct DrawRRectOp final : PaintOp {
+  static constexpr PaintOpType kType = PaintOpType::DrawRRect;
+  static constexpr bool kIsDrawOp = true;
+  static constexpr bool kHasPaintFlags = true;
+  DrawRRectOp(const SkRRect& rrect, const PaintFlags& flags)
+      : rrect(rrect), flags(flags) {}
+  void RasterWithFlags(SkCanvas* canvas, const PaintFlags& flags) const;
+
+  SkRRect rrect;
+  PaintFlags flags;
+};
+
+struct DrawTextOp final : PaintOpWithData {
+  static constexpr PaintOpType kType = PaintOpType::DrawText;
+  static constexpr bool kIsDrawOp = true;
+  static constexpr bool kHasPaintFlags = true;
+  DrawTextOp(size_t bytes, SkScalar x, SkScalar y, const PaintFlags& flags)
+      : PaintOpWithData(bytes), x(x), y(y), flags(flags) {}
+  void RasterWithFlags(SkCanvas* canvas, const PaintFlags& flags) const;
+
+  SkScalar x;
+  SkScalar y;
+  PaintFlags flags;
+};
+
+struct DrawTextBlobOp final : PaintOp {
+  static constexpr PaintOpType kType = PaintOpType::DrawTextBlob;
+  static constexpr bool kIsDrawOp = true;
+  static constexpr bool kHasPaintFlags = true;
+  DrawTextBlobOp(sk_sp<SkTextBlob> blob,
+                 SkScalar x,
+                 SkScalar y,
+                 const PaintFlags& flags);
+  ~DrawTextBlobOp();
+  void RasterWithFlags(SkCanvas* canvas, const PaintFlags& flags) const;
+
+  sk_sp<SkTextBlob> blob;
+  SkScalar x;
+  SkScalar y;
+  PaintFlags flags;
+};
+
+struct NoopOp final : PaintOp {
+  static constexpr PaintOpType kType = PaintOpType::Noop;
+  void Raster(SkCanvas* canvas) const {}
+};
+
+struct RestoreOp final : PaintOp {
+  static constexpr PaintOpType kType = PaintOpType::Restore;
+  void Raster(SkCanvas* canvas) const;
+};
+
+struct RotateOp final : PaintOp {
+  static constexpr PaintOpType kType = PaintOpType::Rotate;
+  explicit RotateOp(SkScalar degrees) : degrees(degrees) {}
+  void Raster(SkCanvas* canvas) const;
+
+  SkScalar degrees;
+};
+
+struct SaveOp final : PaintOp {
+  static constexpr PaintOpType kType = PaintOpType::Save;
+  void Raster(SkCanvas* canvas) const;
+};
+
+struct SaveLayerOp final : PaintOp {
+  static constexpr PaintOpType kType = PaintOpType::SaveLayer;
+  static constexpr bool kHasPaintFlags = true;
+  SaveLayerOp(const SkRect* bounds, const PaintFlags* flags)
+      : bounds(bounds ? *bounds : kUnsetRect) {
+    if (flags)
+      this->flags = *flags;
+  }
+  void RasterWithFlags(SkCanvas* canvas, const PaintFlags& flags) const;
+
+  SkRect bounds;
+  PaintFlags flags;
+};
+
+struct SaveLayerAlphaOp final : PaintOp {
+  static constexpr PaintOpType kType = PaintOpType::SaveLayerAlpha;
+  SaveLayerAlphaOp(const SkRect* bounds, uint8_t alpha)
+      : bounds(bounds ? *bounds : kUnsetRect), alpha(alpha) {}
+  void Raster(SkCanvas* canvas) const;
+
+  SkRect bounds;
+  uint8_t alpha;
+};
+
+struct ScaleOp final : PaintOp {
+  static constexpr PaintOpType kType = PaintOpType::Scale;
+  ScaleOp(SkScalar sx, SkScalar sy) : sx(sx), sy(sy) {}
+  void Raster(SkCanvas* canvas) const;
+
+  SkScalar sx;
+  SkScalar sy;
+};
+
+struct SetMatrixOp final : PaintOp {
+  static constexpr PaintOpType kType = PaintOpType::SetMatrix;
+  explicit SetMatrixOp(const SkMatrix& matrix) : matrix(matrix) {}
+  // This is the only op that needs the original ctm of the SkCanvas
+  // used for raster (since SetMatrix is relative to the recording origin and
+  // shouldn't clobber the SkCanvas raster origin).
+  //
+  // TODO(enne): Find some cleaner way to do this, possibly by making
+  // all SetMatrix calls Concat??
+  void Raster(SkCanvas* canvas, const SkMatrix& original_ctm) const;
+
+  ThreadsafeMatrix matrix;
+};
+
+struct TranslateOp final : PaintOp {
+  static constexpr PaintOpType kType = PaintOpType::Translate;
+  TranslateOp(SkScalar dx, SkScalar dy) : dx(dx), dy(dy) {}
+  void Raster(SkCanvas* canvas) const;
+
+  SkScalar dx;
+  SkScalar dy;
+};
+
+using LargestPaintOp = DrawDRRectOp;
+
+class CC_PAINT_EXPORT PaintOpBuffer : public SkRefCnt {
+ public:
+  enum { kPageSize = 4096 };
+
+  PaintOpBuffer();
+  explicit PaintOpBuffer(const SkRect& cull_rect);
+  ~PaintOpBuffer() override;
+
+  void Reset();
+
+  void playback(SkCanvas* canvas) const;
+  void playback(SkCanvas* canvas, SkPicture::AbortCallback* callback) const;
+
+  // TODO(enne): These are no longer approximate.  Rename these.
+  int approximateOpCount() const { return op_count_; }
+  size_t approximateBytesUsed() const {
+    return sizeof(*this) + reserved_ + subrecord_bytes_used_;
+  }
+  int numSlowPaths() const { return num_slow_paths_; }
+
+  // Resize the PaintOpBuffer to exactly fit the current amount of used space.
+  void ShrinkToFit();
+
+  const SkRect& cullRect() const { return cull_rect_; }
+
+  PaintOp* GetFirstOp() const {
+    return reinterpret_cast<PaintOp*>(const_cast<char*>(&first_op_[0]));
+  }
+
+  template <typename T, typename... Args>
+  void push(Args&&... args) {
+    static_assert(std::is_convertible<T, PaintOp>::value, "T not a PaintOp.");
+    static_assert(!std::is_convertible<T, PaintOpWithData>::value,
+                  "Type needs to use push_with_data");
+    push_internal<T>(0, std::forward<Args>(args)...);
+  }
+
+  template <typename T, typename... Args>
+  void push_with_data(const void* data, size_t bytes, Args&&... args) {
+    static_assert(std::is_convertible<T, PaintOpWithData>::value,
+                  "T is not a PaintOpWithData");
+#if !defined(OS_CHROMEOS)
+    // TODO(enne): non-linux chromeos builds think that DrawTextOp
+    // can be converted to a PaintOpWithDataArrayBase.  OOPS.
+    static_assert(!std::is_convertible<T, PaintOpWithDataArrayBase>::value,
+                  "Type needs to use push_with_data_array");
+#endif
+    DCHECK_GE(bytes, 0u);
+    T* op = push_internal<T>(bytes, bytes, std::forward<Args>(args)...);
+    memcpy(paint_op_data(op), data, bytes);
+
+#if DCHECK_IS_ON()
+    // Double check the data fits between op and next op and doesn't clobber.
+    char* op_start = reinterpret_cast<char*>(op);
+    char* op_end = op_start + sizeof(T);
+    char* next_op = op_start + op->skip;
+    char* data_start = reinterpret_cast<char*>(paint_op_data(op));
+    char* data_end = data_start + bytes;
+    DCHECK_GE(data_start, op_end);
+    DCHECK_LT(data_start, next_op);
+    DCHECK_LE(data_end, next_op);
+#endif
+  }
+
+  template <typename T, typename M, typename... Args>
+  void push_with_data_array(const void* data,
+                            size_t bytes,
+                            const M* array,
+                            size_t count,
+                            Args&&... args) {
+    static_assert(std::is_convertible<T, PaintOpWithDataArray<M>>::value,
+                  "T is not a PaintOpWithDataArray");
+    DCHECK_GE(bytes, 0u);
+    DCHECK_GE(count, 0u);
+    size_t array_size = sizeof(M) * count;
+    size_t total_size = bytes + array_size;
+    T* op =
+        push_internal<T>(total_size, bytes, count, std::forward<Args>(args)...);
+    memcpy(paint_op_data(op), data, bytes);
+    memcpy(paint_op_array<M>(op), array, array_size);
+
+#if DCHECK_IS_ON()
+    // Double check data and array don't clobber op, next op, or each other
+    char* op_start = reinterpret_cast<char*>(op);
+    char* op_end = op_start + sizeof(T);
+    char* next_op = op_start + op->skip;
+    char* data_start = reinterpret_cast<char*>(paint_op_data(op));
+    char* data_end = data_start + bytes;
+    char* array_start = reinterpret_cast<char*>(paint_op_array<M>(op));
+    char* array_end = array_start + array_size;
+    DCHECK_GE(data_start, op_end);
+    DCHECK_LE(data_start, array_start);
+    DCHECK_GE(array_start, data_end);
+    DCHECK_LE(array_end, next_op);
+#endif
+  }
+
+  class Iterator {
+   public:
+    explicit Iterator(const PaintOpBuffer* buffer)
+        : buffer_(buffer), ptr_(buffer_->data_.get()) {}
+
+    PaintOp* operator->() const {
+      return op_idx_ ? reinterpret_cast<PaintOp*>(ptr_) : buffer_->GetFirstOp();
+    }
+    PaintOp* operator*() const { return operator->(); }
+    Iterator begin() { return Iterator(buffer_, buffer_->data_.get(), 0); }
+    Iterator end() {
+      return Iterator(buffer_, buffer_->data_.get() + buffer_->used_,
+                      buffer_->approximateOpCount());
+    }
+    bool operator!=(const Iterator& other) {
+      // Not valid to compare iterators on different buffers.
+      DCHECK_EQ(other.buffer_, buffer_);
+      return other.op_idx_ != op_idx_;
+    }
+    Iterator& operator++() {
+      if (!op_idx_++)
+        return *this;
+      PaintOp* op = **this;
+      uint32_t type = op->type;
+      CHECK_LE(type, static_cast<uint32_t>(PaintOpType::LastPaintOpType));
+      ptr_ += op->skip;
+      return *this;
+    }
+    operator bool() const { return op_idx_ < buffer_->approximateOpCount(); }
+
+    int op_idx() const { return op_idx_; }
+
+    // Return the next op without advancing the iterator, or nullptr if none.
+    PaintOp* peek1() const {
+      if (op_idx_ + 1 >= buffer_->approximateOpCount())
+        return nullptr;
+      if (!op_idx_)
+        return reinterpret_cast<PaintOp*>(ptr_);
+      return reinterpret_cast<PaintOp*>(ptr_ + (*this)->skip);
+    }
+
+    // Return the op two ops ahead without advancing the iterator, or nullptr if
+    // none.
+    PaintOp* peek2() const {
+      if (op_idx_ + 2 >= buffer_->approximateOpCount())
+        return nullptr;
+      char* next = ptr_ + reinterpret_cast<PaintOp*>(ptr_)->skip;
+      PaintOp* next_op = reinterpret_cast<PaintOp*>(next);
+      if (!op_idx_)
+        return next_op;
+      return reinterpret_cast<PaintOp*>(next + next_op->skip);
+    }
+
+   private:
+    Iterator(const PaintOpBuffer* buffer, char* ptr, int op_idx)
+        : buffer_(buffer), ptr_(ptr), op_idx_(op_idx) {}
+
+    const PaintOpBuffer* buffer_ = nullptr;
+    char* ptr_ = nullptr;
+    int op_idx_ = 0;
+  };
+
+ private:
+  template <typename T, bool HasFlags>
+  struct CountSlowPathsFromFlags {
+    static int Count(const T* op) { return 0; }
+  };
+
+  template <typename T>
+  struct CountSlowPathsFromFlags<T, true> {
+    static int Count(const T* op) { return op->flags.getPathEffect() ? 1 : 0; }
+  };
+
+  template <typename T, typename... Args>
+  T* push_internal(size_t bytes, Args&&... args) {
+    size_t skip = SkAlignPtr(sizeof(T) + bytes);
+    DCHECK_LT(skip, static_cast<size_t>(1) << 24);
+    if (used_ + skip > reserved_ || !op_count_) {
+      if (!op_count_) {
+        if (bytes) {
+          // Internal first_op buffer doesn't have room for extra data.
+          // If the op wants extra bytes, then we'll just store a Noop
+          // in the first_op and proceed from there.  This seems unlikely
+          // to be a common case.
+          push<NoopOp>();
+        } else {
+          T* op = reinterpret_cast<T*>(&first_op_[0]);
+          new (op) T{std::forward<Args>(args)...};
+          op->type = static_cast<uint32_t>(T::kType);
+          op->skip = 0;
+          op_count_++;
+          return op;
+        }
+      }
+
+      static_assert(SkIsPow2(kPageSize),
+                    "This math needs updating for non-pow2.");
+      // Next greater multiple of kPageSize.
+      reserved_ = (used_ + skip + kPageSize) & ~(kPageSize - 1);
+      data_.realloc(reserved_);
+    }
+    DCHECK_LE(used_ + skip, reserved_);
+
+    T* op = reinterpret_cast<T*>(data_.get() + used_);
+    used_ += skip;
+    new (op) T(std::forward<Args>(args)...);
+    op->type = static_cast<uint32_t>(T::kType);
+    op->skip = skip;
+    op_count_++;
+
+    num_slow_paths_ += CountSlowPathsFromFlags<T, T::kHasPaintFlags>::Count(op);
+    num_slow_paths_ += op->CountSlowPaths();
+
+    subrecord_bytes_used_ += op->AdditionalBytesUsed();
+
+    return op;
+  }
+
+  // As a performance optimization because n=1 is an extremely common case just
+  // store the first op in the PaintOpBuffer itself to avoid an extra alloc.
+  char first_op_[sizeof(LargestPaintOp)];
+  SkAutoTMalloc<char> data_;
+  size_t used_ = 0;
+  size_t reserved_ = 0;
+  int op_count_ = 0;
+
+  // Record paths for veto-to-msaa for gpu raster.
+  int num_slow_paths_ = 0;
+  // Record additional bytes used by referenced sub-records and display lists.
+  size_t subrecord_bytes_used_ = 0;
+  SkRect cull_rect_;
+
+  DISALLOW_COPY_AND_ASSIGN(PaintOpBuffer);
+};
+
+}  // namespace cc
+
+#endif  // CC_PAINT_PAINT_OP_BUFFER_H_
diff --git a/cc/paint/paint_op_buffer_unittest.cc b/cc/paint/paint_op_buffer_unittest.cc
new file mode 100644
index 0000000..5e0bf2aa
--- /dev/null
+++ b/cc/paint/paint_op_buffer_unittest.cc
@@ -0,0 +1,259 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "cc/paint/paint_op_buffer.h"
+#include "cc/test/test_skcanvas.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+template <typename T>
+void CheckRefCnt(const T& obj, int32_t count) {
+// Skia doesn't define getRefCnt in all builds.
+#ifdef SK_DEBUG
+  EXPECT_EQ(obj->getRefCnt(), count);
+#endif
+}
+
+}  // namespace
+
+namespace cc {
+
+TEST(PaintOpBufferTest, Empty) {
+  PaintOpBuffer buffer;
+  EXPECT_EQ(buffer.approximateOpCount(), 0);
+  EXPECT_EQ(buffer.approximateBytesUsed(), sizeof(PaintOpBuffer));
+  EXPECT_EQ(PaintOpBuffer::Iterator(&buffer), false);
+
+  buffer.Reset();
+  EXPECT_EQ(buffer.approximateOpCount(), 0);
+  EXPECT_EQ(buffer.approximateBytesUsed(), sizeof(PaintOpBuffer));
+  EXPECT_EQ(PaintOpBuffer::Iterator(&buffer), false);
+}
+
+TEST(PaintOpBufferTest, SimpleAppend) {
+  SkRect rect = SkRect::MakeXYWH(2, 3, 4, 5);
+  PaintFlags flags;
+  flags.setColor(SK_ColorMAGENTA);
+  flags.setAlpha(100);
+  SkColor draw_color = SK_ColorRED;
+  SkBlendMode blend = SkBlendMode::kSrc;
+
+  PaintOpBuffer buffer;
+  buffer.push<SaveLayerOp>(&rect, &flags);
+  buffer.push<SaveOp>();
+  buffer.push<DrawColorOp>(draw_color, blend);
+  buffer.push<RestoreOp>();
+
+  EXPECT_EQ(buffer.approximateOpCount(), 4);
+
+  PaintOpBuffer::Iterator iter(&buffer);
+  ASSERT_EQ(iter->GetType(), PaintOpType::SaveLayer);
+  SaveLayerOp* save_op = static_cast<SaveLayerOp*>(*iter);
+  EXPECT_EQ(save_op->bounds, rect);
+  EXPECT_TRUE(save_op->flags == flags);
+  ++iter;
+
+  ASSERT_EQ(iter->GetType(), PaintOpType::Save);
+  ++iter;
+
+  ASSERT_EQ(iter->GetType(), PaintOpType::DrawColor);
+  DrawColorOp* op = static_cast<DrawColorOp*>(*iter);
+  EXPECT_EQ(op->color, draw_color);
+  EXPECT_EQ(op->mode, blend);
+  ++iter;
+
+  ASSERT_EQ(iter->GetType(), PaintOpType::Restore);
+  ++iter;
+
+  EXPECT_FALSE(iter);
+}
+
+// PaintOpBuffer has a special case for first ops stored locally, so
+// make sure that appending different kind of ops as a first op works
+// properly, as well as resetting and reusing the first local op.
+TEST(PaintOpBufferTest, FirstOpWithAndWithoutData) {
+  PaintOpBuffer buffer;
+  char text[] = "asdf";
+
+  // Use a color filter and its ref count to verify that the destructor
+  // is called on ops after reset.
+  PaintFlags flags;
+  sk_sp<SkColorFilter> filter =
+      SkColorFilter::MakeModeFilter(SK_ColorMAGENTA, SkBlendMode::kSrcOver);
+  flags.setColorFilter(filter);
+  CheckRefCnt(filter, 2);
+
+  buffer.push_with_data<DrawTextOp>(text, arraysize(text), 0.f, 0.f, flags);
+  CheckRefCnt(filter, 3);
+
+  // Verify that when the first op has data, which may not fit in the
+  // PaintRecord internal buffer, that it adds a noop as the first op
+  // and then appends the "op with data" into the heap buffer.
+  ASSERT_EQ(buffer.approximateOpCount(), 2);
+  EXPECT_EQ(buffer.GetFirstOp()->GetType(), PaintOpType::Noop);
+
+  // Verify iteration behavior and brief smoke test of op state.
+  {
+    PaintOpBuffer::Iterator iter(&buffer);
+    PaintOp* noop = *iter;
+    EXPECT_EQ(buffer.GetFirstOp(), noop);
+    ++iter;
+
+    PaintOp* op = *iter;
+    ASSERT_EQ(op->GetType(), PaintOpType::DrawText);
+    DrawTextOp* draw_text_op = static_cast<DrawTextOp*>(op);
+    EXPECT_EQ(draw_text_op->bytes, arraysize(text));
+
+    void* data = paint_op_data(draw_text_op);
+    EXPECT_EQ(memcmp(data, text, arraysize(text)), 0);
+
+    ++iter;
+    EXPECT_FALSE(iter);
+  }
+
+  // Reset, verify state, and append an op that will fit in the first slot.
+  buffer.Reset();
+  CheckRefCnt(filter, 2);
+
+  ASSERT_EQ(buffer.approximateOpCount(), 0);
+  EXPECT_EQ(PaintOpBuffer::Iterator(&buffer), false);
+
+  SkRect rect = SkRect::MakeXYWH(1, 2, 3, 4);
+  buffer.push<DrawRectOp>(rect, flags);
+  CheckRefCnt(filter, 3);
+
+  ASSERT_EQ(buffer.approximateOpCount(), 1);
+  EXPECT_EQ(buffer.GetFirstOp()->GetType(), PaintOpType::DrawRect);
+
+  PaintOpBuffer::Iterator iter(&buffer);
+  ASSERT_EQ(iter->GetType(), PaintOpType::DrawRect);
+  DrawRectOp* draw_rect_op = static_cast<DrawRectOp*>(*iter);
+  EXPECT_EQ(draw_rect_op->rect, rect);
+
+  ++iter;
+  EXPECT_FALSE(iter);
+
+  buffer.Reset();
+  ASSERT_EQ(buffer.approximateOpCount(), 0);
+  CheckRefCnt(filter, 2);
+}
+
+TEST(PaintOpBufferTest, Peek) {
+  PaintOpBuffer buffer;
+
+  uint8_t alpha = 100;
+  buffer.push<SaveLayerAlphaOp>(nullptr, alpha);
+  PaintFlags draw_flags;
+  buffer.push<DrawRectOp>(SkRect::MakeXYWH(1, 2, 3, 4), draw_flags);
+  buffer.push<RestoreOp>();
+  buffer.push<SaveOp>();
+  buffer.push<NoopOp>();
+  buffer.push<RestoreOp>();
+
+  PaintOpBuffer::Iterator init_iter(&buffer);
+  PaintOp* peek[2] = {*init_iter, init_iter.peek1()};
+
+  // Expect that while iterating that next = current.peek1() and that
+  // next.peek1() == current.peek2().
+  for (PaintOpBuffer::Iterator iter(&buffer); iter; ++iter) {
+    EXPECT_EQ(*iter, peek[0]) << iter.op_idx();
+    EXPECT_EQ(iter.peek1(), peek[1]) << iter.op_idx();
+
+    peek[0] = iter.peek1();
+    peek[1] = iter.peek2();
+  }
+}
+
+TEST(PaintOpBufferTest, PeekEmpty) {
+  PaintOpBuffer empty;
+  PaintOpBuffer::Iterator empty_iter(&empty);
+  EXPECT_EQ(nullptr, empty_iter.peek1());
+  EXPECT_EQ(nullptr, empty_iter.peek2());
+}
+
+// Verify that a SaveLayerAlpha / Draw / Restore can be optimized to just
+// a draw with opacity.
+TEST(PaintOpBufferTest, SaveDrawRestore) {
+  PaintOpBuffer buffer;
+
+  uint8_t alpha = 100;
+  buffer.push<SaveLayerAlphaOp>(nullptr, alpha);
+
+  PaintFlags draw_flags;
+  draw_flags.setColor(SK_ColorMAGENTA);
+  draw_flags.setAlpha(50);
+  EXPECT_TRUE(draw_flags.SupportsFoldingAlpha());
+  SkRect rect = SkRect::MakeXYWH(1, 2, 3, 4);
+  buffer.push<DrawRectOp>(rect, draw_flags);
+  buffer.push<RestoreOp>();
+
+  SaveCountingCanvas canvas;
+  buffer.playback(&canvas);
+
+  EXPECT_EQ(0, canvas.save_count_);
+  EXPECT_EQ(0, canvas.restore_count_);
+  EXPECT_EQ(rect, canvas.draw_rect_);
+
+  // Expect the alpha from the draw and the save layer to be folded together.
+  // Since alpha is stored in a uint8_t and gets rounded, so use tolerance.
+  float expected_alpha = alpha * 50 / 255.f;
+  EXPECT_LE(std::abs(expected_alpha - canvas.paint_.getAlpha()), 1.f);
+}
+
+// The same as SaveDrawRestore, but test that the optimization doesn't apply
+// when the drawing op's flags are not compatible with being folded into the
+// save layer with opacity.
+TEST(PaintOpBufferTest, SaveDrawRestoreFail_BadFlags) {
+  PaintOpBuffer buffer;
+
+  uint8_t alpha = 100;
+  buffer.push<SaveLayerAlphaOp>(nullptr, alpha);
+
+  PaintFlags draw_flags;
+  draw_flags.setColor(SK_ColorMAGENTA);
+  draw_flags.setAlpha(50);
+  draw_flags.setBlendMode(SkBlendMode::kSrc);
+  EXPECT_FALSE(draw_flags.SupportsFoldingAlpha());
+  SkRect rect = SkRect::MakeXYWH(1, 2, 3, 4);
+  buffer.push<DrawRectOp>(rect, draw_flags);
+  buffer.push<RestoreOp>();
+
+  SaveCountingCanvas canvas;
+  buffer.playback(&canvas);
+
+  EXPECT_EQ(1, canvas.save_count_);
+  EXPECT_EQ(1, canvas.restore_count_);
+  EXPECT_EQ(rect, canvas.draw_rect_);
+  EXPECT_EQ(draw_flags.getAlpha(), canvas.paint_.getAlpha());
+}
+
+// The same as SaveDrawRestore, but test that the optimization doesn't apply
+// when there are more than one ops between the save and restore.
+TEST(PaintOpBufferTest, SaveDrawRestoreFail_TooManyOps) {
+  PaintOpBuffer buffer;
+
+  uint8_t alpha = 100;
+  buffer.push<SaveLayerAlphaOp>(nullptr, alpha);
+
+  PaintFlags draw_flags;
+  draw_flags.setColor(SK_ColorMAGENTA);
+  draw_flags.setAlpha(50);
+  draw_flags.setBlendMode(SkBlendMode::kSrcOver);
+  EXPECT_TRUE(draw_flags.SupportsFoldingAlpha());
+  SkRect rect = SkRect::MakeXYWH(1, 2, 3, 4);
+  buffer.push<DrawRectOp>(rect, draw_flags);
+  buffer.push<NoopOp>();
+  buffer.push<RestoreOp>();
+
+  SaveCountingCanvas canvas;
+  buffer.playback(&canvas);
+
+  EXPECT_EQ(1, canvas.save_count_);
+  EXPECT_EQ(1, canvas.restore_count_);
+  EXPECT_EQ(rect, canvas.draw_rect_);
+  EXPECT_EQ(draw_flags.getAlpha(), canvas.paint_.getAlpha());
+}
+
+}  // namespace cc
diff --git a/cc/paint/paint_record.cc b/cc/paint/paint_record.cc
new file mode 100644
index 0000000..52cb2524
--- /dev/null
+++ b/cc/paint/paint_record.cc
@@ -0,0 +1,26 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "cc/paint/paint_record.h"
+
+#include "cc/paint/paint_op_buffer.h"
+#include "third_party/skia/include/core/SkPictureRecorder.h"
+
+namespace cc {
+
+sk_sp<SkPicture> ToSkPicture(sk_sp<PaintRecord> record) {
+  SkPictureRecorder recorder;
+  SkCanvas* canvas = recorder.beginRecording(record->cullRect());
+  record->playback(canvas);
+  return recorder.finishRecordingAsPicture();
+}
+
+sk_sp<const SkPicture> ToSkPicture(sk_sp<const PaintRecord> record) {
+  SkPictureRecorder recorder;
+  SkCanvas* canvas = recorder.beginRecording(record->cullRect());
+  record->playback(canvas);
+  return recorder.finishRecordingAsPicture();
+}
+
+}  // namespace cc
diff --git a/cc/paint/paint_record.h b/cc/paint/paint_record.h
index 8506606b..daeee004 100644
--- a/cc/paint/paint_record.h
+++ b/cc/paint/paint_record.h
@@ -5,19 +5,22 @@
 #ifndef CC_PAINT_PAINT_RECORD_H_
 #define CC_PAINT_PAINT_RECORD_H_
 
+#include "cc/paint/paint_export.h"
+#include "cc/paint/paint_op_buffer.h"
 #include "third_party/skia/include/core/SkPicture.h"
 
 namespace cc {
 
-using PaintRecord = SkPicture;
+// TODO(enne): Don't want to rename the world for this.  Using these as the
+// same types for now prevents an extra allocation.  Probably PaintRecord
+// will become an interface in the future.
+using PaintRecord = PaintOpBuffer;
 
-inline sk_sp<SkPicture> ToSkPicture(sk_sp<PaintRecord> record) {
-  return record;
-}
+// TODO(enne): Remove these if possible, they are really expensive.
+CC_PAINT_EXPORT sk_sp<SkPicture> ToSkPicture(sk_sp<PaintRecord> record);
 
-inline sk_sp<const SkPicture> ToSkPicture(sk_sp<const PaintRecord> record) {
-  return record;
-}
+CC_PAINT_EXPORT sk_sp<const SkPicture> ToSkPicture(
+    sk_sp<const PaintRecord> record);
 
 }  // namespace cc
 
diff --git a/cc/paint/paint_recorder.cc b/cc/paint/paint_recorder.cc
index 672f0712..c43f965 100644
--- a/cc/paint/paint_recorder.cc
+++ b/cc/paint/paint_recorder.cc
@@ -4,9 +4,36 @@
 
 #include "cc/paint/paint_recorder.h"
 
+#include "cc/paint/paint_op_buffer.h"
+
 namespace cc {
 
 PaintRecorder::PaintRecorder() = default;
+
 PaintRecorder::~PaintRecorder() = default;
 
+PaintCanvas* PaintRecorder::beginRecording(const SkRect& bounds) {
+  buffer_.reset(new PaintOpBuffer(bounds));
+  canvas_.emplace(buffer_.get(), bounds);
+  return getRecordingCanvas();
+}
+
+sk_sp<PaintRecord> PaintRecorder::finishRecordingAsPicture() {
+  // SkPictureRecorder users expect that their saves are automatically
+  // closed for them.
+  //
+  // NOTE: Blink paint in general doesn't appear to need this, but the
+  // RecordingImageBufferSurface::fallBackToRasterCanvas finishing off the
+  // current frame depends on this.  Maybe we could remove this assumption and
+  // just have callers do it.
+  canvas_->restoreToCount(1);
+
+  // Some users (e.g. printing) use the existence of the recording canvas
+  // to know if recording is finished, so reset it here.
+  canvas_.reset();
+
+  buffer_->ShrinkToFit();
+  return std::move(buffer_);
+}
+
 }  // namespace cc
diff --git a/cc/paint/paint_recorder.h b/cc/paint/paint_recorder.h
index 2bbea83b..7f582b85 100644
--- a/cc/paint/paint_recorder.h
+++ b/cc/paint/paint_recorder.h
@@ -9,47 +9,36 @@
 #include "base/macros.h"
 #include "base/memory/ptr_util.h"
 #include "base/optional.h"
-#include "cc/paint/paint_canvas.h"
 #include "cc/paint/paint_record.h"
-#include "cc/paint/skia_paint_canvas.h"
-#include "third_party/skia/include/core/SkPictureRecorder.h"
+#include "cc/paint/record_paint_canvas.h"
 
 namespace cc {
 
+class PaintOpBuffer;
+
 class CC_PAINT_EXPORT PaintRecorder {
  public:
   PaintRecorder();
   ~PaintRecorder();
 
-  ALWAYS_INLINE PaintCanvas* beginRecording(const SkRect& bounds) {
-    uint32_t record_flags = 0;
-    canvas_.emplace(recorder_.beginRecording(bounds, nullptr, record_flags));
-    return getRecordingCanvas();
-  }
+  PaintCanvas* beginRecording(const SkRect& bounds);
 
-  ALWAYS_INLINE PaintCanvas* beginRecording(SkScalar width, SkScalar height) {
-    uint32_t record_flags = 0;
-    canvas_.emplace(
-        recorder_.beginRecording(width, height, nullptr, record_flags));
-    return getRecordingCanvas();
+  // TODO(enne): should make everything go through the non-rect version.
+  // See comments in RecordPaintCanvas ctor for why.
+  PaintCanvas* beginRecording(SkScalar width, SkScalar height) {
+    return beginRecording(SkRect::MakeWH(width, height));
   }
 
   // Only valid between between and finish recording.
-  ALWAYS_INLINE PaintCanvas* getRecordingCanvas() {
+  ALWAYS_INLINE RecordPaintCanvas* getRecordingCanvas() {
     return canvas_.has_value() ? &canvas_.value() : nullptr;
   }
 
-  ALWAYS_INLINE sk_sp<PaintRecord> finishRecordingAsPicture() {
-    sk_sp<SkPicture> picture = recorder_.finishRecordingAsPicture();
-    // Some users (e.g. printing) use the existence of the recording canvas
-    // to know if recording is finished, so reset it here.
-    canvas_.reset();
-    return sk_ref_sp(static_cast<PaintRecord*>(picture.get()));
-  }
+  sk_sp<PaintRecord> finishRecordingAsPicture();
 
  private:
-  SkPictureRecorder recorder_;
-  base::Optional<SkiaPaintCanvas> canvas_;
+  sk_sp<PaintOpBuffer> buffer_;
+  base::Optional<RecordPaintCanvas> canvas_;
 
   DISALLOW_COPY_AND_ASSIGN(PaintRecorder);
 };
diff --git a/cc/paint/record_paint_canvas.cc b/cc/paint/record_paint_canvas.cc
new file mode 100644
index 0000000..7f2ba61
--- /dev/null
+++ b/cc/paint/record_paint_canvas.cc
@@ -0,0 +1,384 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "cc/paint/record_paint_canvas.h"
+
+#include "base/memory/ptr_util.h"
+#include "cc/paint/display_item_list.h"
+#include "cc/paint/paint_op_buffer.h"
+#include "cc/paint/paint_record.h"
+#include "cc/paint/paint_recorder.h"
+#include "third_party/skia/include/core/SkAnnotation.h"
+#include "third_party/skia/include/core/SkMetaData.h"
+#include "third_party/skia/include/utils/SkNWayCanvas.h"
+
+namespace cc {
+
+RecordPaintCanvas::RecordPaintCanvas(PaintOpBuffer* buffer,
+                                     const SkRect& cull_rect)
+    : buffer_(buffer),
+      canvas_(static_cast<int>(std::ceil(cull_rect.right())),
+              static_cast<int>(std::ceil(cull_rect.bottom()))) {
+  DCHECK(buffer_);
+
+  // This is part of the "recording canvases have a size, but why" dance.
+  // By creating a canvas of size (right x bottom) and then clipping it,
+  // It makes getDeviceClipBounds return the original cull rect, which code
+  // in GraphicsContextCanvas on Mac expects.  (Just creating an SkNoDrawCanvas
+  // with the cull_rect makes a canvas of size (width x height) instead
+  // which is incorrect.  SkRecorder cheats with private resetForNextCanvas.
+  canvas_.clipRect(SkRect::Make(cull_rect.roundOut()), SkClipOp::kIntersect,
+                   false);
+}
+
+RecordPaintCanvas::~RecordPaintCanvas() = default;
+
+SkMetaData& RecordPaintCanvas::getMetaData() {
+  // This could just be SkMetaData owned by RecordPaintCanvas, but since
+  // SkCanvas already has one, we might as well use it directly.
+  return canvas_.getMetaData();
+}
+
+SkImageInfo RecordPaintCanvas::imageInfo() const {
+  return canvas_.imageInfo();
+}
+
+void RecordPaintCanvas::flush() {
+  // This is a noop when recording.
+}
+
+SkISize RecordPaintCanvas::getBaseLayerSize() const {
+  return canvas_.getBaseLayerSize();
+}
+
+bool RecordPaintCanvas::writePixels(const SkImageInfo& info,
+                                    const void* pixels,
+                                    size_t row_bytes,
+                                    int x,
+                                    int y) {
+  NOTREACHED();
+  return false;
+}
+
+int RecordPaintCanvas::save() {
+  buffer_->push<SaveOp>();
+  return canvas_.save();
+}
+
+int RecordPaintCanvas::saveLayer(const SkRect* bounds,
+                                 const PaintFlags* flags) {
+  if (flags) {
+    if (flags->IsSimpleOpacity()) {
+      // TODO(enne): maybe more callers should know this and call
+      // saveLayerAlpha instead of needing to check here.
+      uint8_t alpha = SkColorGetA(flags->getColor());
+      return saveLayerAlpha(bounds, alpha);
+    }
+
+    // TODO(enne): it appears that image filters affect matrices and color
+    // matrices affect transparent flags on SkCanvas layers, but it's not clear
+    // whether those are actually needed and we could just skip ToSkPaint here.
+    buffer_->push<SaveLayerOp>(bounds, flags);
+    const SkPaint& paint = ToSkPaint(*flags);
+    return canvas_.saveLayer(bounds, &paint);
+  }
+  buffer_->push<SaveLayerOp>(bounds, flags);
+  return canvas_.saveLayer(bounds, nullptr);
+}
+
+int RecordPaintCanvas::saveLayerAlpha(const SkRect* bounds, uint8_t alpha) {
+  buffer_->push<SaveLayerAlphaOp>(bounds, alpha);
+  return canvas_.saveLayerAlpha(bounds, alpha);
+}
+
+void RecordPaintCanvas::restore() {
+  buffer_->push<RestoreOp>();
+  canvas_.restore();
+}
+
+int RecordPaintCanvas::getSaveCount() const {
+  return canvas_.getSaveCount();
+}
+
+void RecordPaintCanvas::restoreToCount(int save_count) {
+  DCHECK_GE(save_count, 1);
+  int diff = canvas_.getSaveCount() - save_count;
+  DCHECK_GE(diff, 0);
+  for (int i = 0; i < diff; ++i)
+    restore();
+}
+
+void RecordPaintCanvas::translate(SkScalar dx, SkScalar dy) {
+  buffer_->push<TranslateOp>(dx, dy);
+  canvas_.translate(dx, dy);
+}
+
+void RecordPaintCanvas::scale(SkScalar sx, SkScalar sy) {
+  buffer_->push<ScaleOp>(sx, sy);
+  canvas_.scale(sx, sy);
+}
+
+void RecordPaintCanvas::rotate(SkScalar degrees) {
+  buffer_->push<RotateOp>(degrees);
+  canvas_.rotate(degrees);
+}
+
+void RecordPaintCanvas::concat(const SkMatrix& matrix) {
+  buffer_->push<ConcatOp>(matrix);
+  canvas_.concat(matrix);
+}
+
+void RecordPaintCanvas::setMatrix(const SkMatrix& matrix) {
+  buffer_->push<SetMatrixOp>(matrix);
+  canvas_.setMatrix(matrix);
+}
+
+void RecordPaintCanvas::clipRect(const SkRect& rect,
+                                 SkClipOp op,
+                                 bool antialias) {
+  buffer_->push<ClipRectOp>(rect, op, antialias);
+  canvas_.clipRect(rect, op, antialias);
+}
+
+void RecordPaintCanvas::clipRRect(const SkRRect& rrect,
+                                  SkClipOp op,
+                                  bool antialias) {
+  // TODO(enne): does this happen? Should the caller know this?
+  if (rrect.isRect()) {
+    clipRect(rrect.getBounds(), op, antialias);
+    return;
+  }
+  buffer_->push<ClipRRectOp>(rrect, op, antialias);
+  canvas_.clipRRect(rrect, op, antialias);
+}
+
+void RecordPaintCanvas::clipPath(const SkPath& path,
+                                 SkClipOp op,
+                                 bool antialias) {
+  if (!path.isInverseFillType() && canvas_.getTotalMatrix().rectStaysRect()) {
+    // TODO(enne): do these cases happen? should the caller know that this isn't
+    // a path?
+    SkRect rect;
+    if (path.isRect(&rect)) {
+      clipRect(rect, op, antialias);
+      return;
+    }
+    SkRRect rrect;
+    if (path.isOval(&rect)) {
+      rrect.setOval(rect);
+      clipRRect(rrect, op, antialias);
+      return;
+    }
+    if (path.isRRect(&rrect)) {
+      clipRRect(rrect, op, antialias);
+      return;
+    }
+  }
+
+  buffer_->push<ClipPathOp>(path, op, antialias);
+  canvas_.clipPath(path, op, antialias);
+  return;
+}
+
+bool RecordPaintCanvas::quickReject(const SkRect& rect) const {
+  return canvas_.quickReject(rect);
+}
+
+bool RecordPaintCanvas::quickReject(const SkPath& path) const {
+  return canvas_.quickReject(path);
+}
+
+SkRect RecordPaintCanvas::getLocalClipBounds() const {
+  return canvas_.getLocalClipBounds();
+}
+
+bool RecordPaintCanvas::getLocalClipBounds(SkRect* bounds) const {
+  return canvas_.getLocalClipBounds(bounds);
+}
+
+SkIRect RecordPaintCanvas::getDeviceClipBounds() const {
+  return canvas_.getDeviceClipBounds();
+}
+
+bool RecordPaintCanvas::getDeviceClipBounds(SkIRect* bounds) const {
+  return canvas_.getDeviceClipBounds(bounds);
+}
+
+void RecordPaintCanvas::drawColor(SkColor color, SkBlendMode mode) {
+  buffer_->push<DrawColorOp>(color, mode);
+}
+
+void RecordPaintCanvas::clear(SkColor color) {
+  buffer_->push<DrawColorOp>(color, SkBlendMode::kSrc);
+}
+
+void RecordPaintCanvas::drawLine(SkScalar x0,
+                                 SkScalar y0,
+                                 SkScalar x1,
+                                 SkScalar y1,
+                                 const PaintFlags& flags) {
+  buffer_->push<DrawLineOp>(x0, y0, x1, y1, flags);
+}
+
+void RecordPaintCanvas::drawRect(const SkRect& rect, const PaintFlags& flags) {
+  buffer_->push<DrawRectOp>(rect, flags);
+}
+
+void RecordPaintCanvas::drawIRect(const SkIRect& rect,
+                                  const PaintFlags& flags) {
+  buffer_->push<DrawIRectOp>(rect, flags);
+}
+
+void RecordPaintCanvas::drawOval(const SkRect& oval, const PaintFlags& flags) {
+  buffer_->push<DrawOvalOp>(oval, flags);
+}
+
+void RecordPaintCanvas::drawRRect(const SkRRect& rrect,
+                                  const PaintFlags& flags) {
+  buffer_->push<DrawRRectOp>(rrect, flags);
+}
+
+void RecordPaintCanvas::drawDRRect(const SkRRect& outer,
+                                   const SkRRect& inner,
+                                   const PaintFlags& flags) {
+  if (outer.isEmpty())
+    return;
+  if (inner.isEmpty()) {
+    drawRRect(outer, flags);
+    return;
+  }
+  buffer_->push<DrawDRRectOp>(outer, inner, flags);
+}
+
+void RecordPaintCanvas::drawCircle(SkScalar cx,
+                                   SkScalar cy,
+                                   SkScalar radius,
+                                   const PaintFlags& flags) {
+  buffer_->push<DrawCircleOp>(cx, cy, radius, flags);
+}
+
+void RecordPaintCanvas::drawArc(const SkRect& oval,
+                                SkScalar start_angle,
+                                SkScalar sweep_angle,
+                                bool use_center,
+                                const PaintFlags& flags) {
+  buffer_->push<DrawArcOp>(oval, start_angle, sweep_angle, use_center, flags);
+}
+
+void RecordPaintCanvas::drawRoundRect(const SkRect& rect,
+                                      SkScalar rx,
+                                      SkScalar ry,
+                                      const PaintFlags& flags) {
+  // TODO(enne): move this into base class?
+  if (rx > 0 && ry > 0) {
+    SkRRect rrect;
+    rrect.setRectXY(rect, rx, ry);
+    drawRRect(rrect, flags);
+  } else {
+    drawRect(rect, flags);
+  }
+}
+
+void RecordPaintCanvas::drawPath(const SkPath& path, const PaintFlags& flags) {
+  buffer_->push<DrawPathOp>(path, flags);
+}
+
+void RecordPaintCanvas::drawImage(const PaintImage& image,
+                                  SkScalar left,
+                                  SkScalar top,
+                                  const PaintFlags* flags) {
+  buffer_->push<DrawImageOp>(image, left, top, flags);
+}
+
+void RecordPaintCanvas::drawImageRect(const PaintImage& image,
+                                      const SkRect& src,
+                                      const SkRect& dst,
+                                      const PaintFlags* flags,
+                                      SrcRectConstraint constraint) {
+  buffer_->push<DrawImageRectOp>(image, src, dst, flags, constraint);
+}
+
+void RecordPaintCanvas::drawBitmap(const SkBitmap& bitmap,
+                                   SkScalar left,
+                                   SkScalar top,
+                                   const PaintFlags* flags) {
+  // TODO(enne): Move into base class?
+  if (bitmap.drawsNothing())
+    return;
+  drawImage(PaintImage(SkImage::MakeFromBitmap(bitmap),
+                       PaintImage::AnimationType::UNKNOWN,
+                       PaintImage::CompletionState::UNKNOWN),
+            left, top, flags);
+}
+
+void RecordPaintCanvas::drawText(const void* text,
+                                 size_t byte_length,
+                                 SkScalar x,
+                                 SkScalar y,
+                                 const PaintFlags& flags) {
+  buffer_->push_with_data<DrawTextOp>(text, byte_length, x, y, flags);
+}
+
+void RecordPaintCanvas::drawPosText(const void* text,
+                                    size_t byte_length,
+                                    const SkPoint pos[],
+                                    const PaintFlags& flags) {
+  size_t count = ToSkPaint(flags).countText(text, byte_length);
+  buffer_->push_with_data_array<DrawPosTextOp>(text, byte_length, pos, count,
+                                               flags);
+}
+
+void RecordPaintCanvas::drawTextBlob(sk_sp<SkTextBlob> blob,
+                                     SkScalar x,
+                                     SkScalar y,
+                                     const PaintFlags& flags) {
+  buffer_->push<DrawTextBlobOp>(blob, x, y, flags);
+}
+
+void RecordPaintCanvas::drawDisplayItemList(
+    scoped_refptr<DisplayItemList> list) {
+  buffer_->push<DrawDisplayItemListOp>(list);
+}
+
+void RecordPaintCanvas::drawPicture(sk_sp<const PaintRecord> record) {
+  // TODO(enne): If this is small, maybe flatten it?
+  buffer_->push<DrawRecordOp>(record);
+}
+
+bool RecordPaintCanvas::isClipEmpty() const {
+  return canvas_.isClipEmpty();
+}
+
+bool RecordPaintCanvas::isClipRect() const {
+  return canvas_.isClipRect();
+}
+
+const SkMatrix& RecordPaintCanvas::getTotalMatrix() const {
+  return canvas_.getTotalMatrix();
+}
+
+void RecordPaintCanvas::temporary_internal_describeTopLayer(
+    SkMatrix* matrix,
+    SkIRect* clip_bounds) {
+  return canvas_.temporary_internal_describeTopLayer(matrix, clip_bounds);
+}
+
+bool RecordPaintCanvas::ToPixmap(SkPixmap* output) {
+  // TODO(enne): It'd be nice to make this NOTREACHED() or remove this from
+  // RecordPaintCanvas, but this is used by GraphicsContextCanvas for knowing
+  // whether or not it can raster directly into pixels with Cg.
+  return false;
+}
+
+void RecordPaintCanvas::Annotate(AnnotationType type,
+                                 const SkRect& rect,
+                                 sk_sp<SkData> data) {
+  buffer_->push<AnnotateOp>(type, rect, data);
+}
+
+void RecordPaintCanvas::PlaybackPaintRecord(sk_sp<const PaintRecord> record) {
+  drawPicture(record);
+}
+
+}  // namespace cc
diff --git a/cc/paint/record_paint_canvas.h b/cc/paint/record_paint_canvas.h
new file mode 100644
index 0000000..15f0102
--- /dev/null
+++ b/cc/paint/record_paint_canvas.h
@@ -0,0 +1,160 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CC_PAINT_RECORD_PAINT_CANVAS_H_
+#define CC_PAINT_RECORD_PAINT_CANVAS_H_
+
+#include <memory>
+
+#include "base/compiler_specific.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "build/build_config.h"
+#include "cc/paint/paint_canvas.h"
+#include "cc/paint/paint_flags.h"
+#include "cc/paint/paint_record.h"
+#include "third_party/skia/include/utils/SkNoDrawCanvas.h"
+
+namespace cc {
+
+class PaintOpBuffer;
+class PaintFlags;
+
+class CC_PAINT_EXPORT RecordPaintCanvas final : public PaintCanvas {
+ public:
+  explicit RecordPaintCanvas(PaintOpBuffer* buffer, const SkRect& cull_rect);
+  ~RecordPaintCanvas() override;
+
+  SkMetaData& getMetaData() override;
+  SkImageInfo imageInfo() const override;
+
+  void flush() override;
+
+  SkISize getBaseLayerSize() const override;
+  bool writePixels(const SkImageInfo& info,
+                   const void* pixels,
+                   size_t row_bytes,
+                   int x,
+                   int y) override;
+  int save() override;
+  int saveLayer(const SkRect* bounds, const PaintFlags* flags) override;
+  int saveLayerAlpha(const SkRect* bounds, uint8_t alpha) override;
+
+  void restore() override;
+  int getSaveCount() const override;
+  void restoreToCount(int save_count) override;
+  void translate(SkScalar dx, SkScalar dy) override;
+  void scale(SkScalar sx, SkScalar sy) override;
+  void rotate(SkScalar degrees) override;
+  void concat(const SkMatrix& matrix) override;
+  void setMatrix(const SkMatrix& matrix) override;
+
+  void clipRect(const SkRect& rect, SkClipOp op, bool antialias) override;
+  void clipRRect(const SkRRect& rrect, SkClipOp op, bool antialias) override;
+  void clipPath(const SkPath& path, SkClipOp op, bool antialias) override;
+  bool quickReject(const SkRect& rect) const override;
+  bool quickReject(const SkPath& path) const override;
+  SkRect getLocalClipBounds() const override;
+  bool getLocalClipBounds(SkRect* bounds) const override;
+  SkIRect getDeviceClipBounds() const override;
+  bool getDeviceClipBounds(SkIRect* bounds) const override;
+  void drawColor(SkColor color, SkBlendMode mode) override;
+  void clear(SkColor color) override;
+
+  void drawLine(SkScalar x0,
+                SkScalar y0,
+                SkScalar x1,
+                SkScalar y1,
+                const PaintFlags& flags) override;
+  void drawRect(const SkRect& rect, const PaintFlags& flags) override;
+  void drawIRect(const SkIRect& rect, const PaintFlags& flags) override;
+  void drawOval(const SkRect& oval, const PaintFlags& flags) override;
+  void drawRRect(const SkRRect& rrect, const PaintFlags& flags) override;
+  void drawDRRect(const SkRRect& outer,
+                  const SkRRect& inner,
+                  const PaintFlags& flags) override;
+  void drawCircle(SkScalar cx,
+                  SkScalar cy,
+                  SkScalar radius,
+                  const PaintFlags& flags) override;
+  void drawArc(const SkRect& oval,
+               SkScalar start_angle,
+               SkScalar sweep_angle,
+               bool use_center,
+               const PaintFlags& flags) override;
+  void drawRoundRect(const SkRect& rect,
+                     SkScalar rx,
+                     SkScalar ry,
+                     const PaintFlags& flags) override;
+  void drawPath(const SkPath& path, const PaintFlags& flags) override;
+  void drawImage(const PaintImage& image,
+                 SkScalar left,
+                 SkScalar top,
+                 const PaintFlags* flags) override;
+  void drawImageRect(const PaintImage& image,
+                     const SkRect& src,
+                     const SkRect& dst,
+                     const PaintFlags* flags,
+                     SrcRectConstraint constraint) override;
+  void drawBitmap(const SkBitmap& bitmap,
+                  SkScalar left,
+                  SkScalar top,
+                  const PaintFlags* flags) override;
+
+  void drawText(const void* text,
+                size_t byte_length,
+                SkScalar x,
+                SkScalar y,
+                const PaintFlags& flags) override;
+  void drawPosText(const void* text,
+                   size_t byte_length,
+                   const SkPoint pos[],
+                   const PaintFlags& flags) override;
+  void drawTextBlob(sk_sp<SkTextBlob> blob,
+                    SkScalar x,
+                    SkScalar y,
+                    const PaintFlags& flags) override;
+
+  void drawDisplayItemList(
+      scoped_refptr<DisplayItemList> display_item_list) override;
+
+  void drawPicture(sk_sp<const PaintRecord> record) override;
+
+  bool isClipEmpty() const override;
+  bool isClipRect() const override;
+  const SkMatrix& getTotalMatrix() const override;
+
+  void temporary_internal_describeTopLayer(SkMatrix* matrix,
+                                           SkIRect* clip_bounds) override;
+
+  bool ToPixmap(SkPixmap* output) override;
+  void Annotate(AnnotationType type,
+                const SkRect& rect,
+                sk_sp<SkData> data) override;
+
+  void PlaybackPaintRecord(sk_sp<const PaintRecord> record) override;
+
+  // Don't shadow non-virtual helper functions.
+  using PaintCanvas::clipRect;
+  using PaintCanvas::clipRRect;
+  using PaintCanvas::clipPath;
+  using PaintCanvas::drawBitmap;
+  using PaintCanvas::drawColor;
+  using PaintCanvas::drawImage;
+  using PaintCanvas::drawPicture;
+
+ private:
+  PaintOpBuffer* buffer_;
+
+  // TODO(enne): Although RecordPaintCanvas is mostly a write-only interface
+  // where paint commands are stored, occasionally users of PaintCanvas want
+  // to ask stateful questions mid-stream of clip and transform state.
+  // To avoid duplicating all this code (for now?), just forward to an SkCanvas
+  // that's not backed by anything but can answer these questions.
+  SkNoDrawCanvas canvas_;
+};
+
+}  // namespace cc
+
+#endif  // CC_PAINT_RECORD_PAINT_CANVAS_H_
diff --git a/cc/paint/skia_paint_canvas.cc b/cc/paint/skia_paint_canvas.cc
index bfc38b6..501ccdf 100644
--- a/cc/paint/skia_paint_canvas.cc
+++ b/cc/paint/skia_paint_canvas.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "cc/paint/paint_canvas.h"
+#include "cc/paint/skia_paint_canvas.h"
 
 #include "base/memory/ptr_util.h"
 #include "cc/paint/display_item_list.h"
@@ -58,7 +58,7 @@
   return canvas_->saveLayer(bounds, ToSkPaint(flags));
 }
 
-int SkiaPaintCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha) {
+int SkiaPaintCanvas::saveLayerAlpha(const SkRect* bounds, uint8_t alpha) {
   return canvas_->saveLayerAlpha(bounds, alpha);
 }
 
diff --git a/cc/paint/skia_paint_canvas.h b/cc/paint/skia_paint_canvas.h
index 47dba86..1bd23f0 100644
--- a/cc/paint/skia_paint_canvas.h
+++ b/cc/paint/skia_paint_canvas.h
@@ -46,7 +46,7 @@
                    int y) override;
   int save() override;
   int saveLayer(const SkRect* bounds, const PaintFlags* flags) override;
-  int saveLayerAlpha(const SkRect* bounds, U8CPU alpha) override;
+  int saveLayerAlpha(const SkRect* bounds, uint8_t alpha) override;
 
   void restore() override;
   int getSaveCount() const override;
diff --git a/cc/test/test_skcanvas.cc b/cc/test/test_skcanvas.cc
new file mode 100644
index 0000000..e45f42de
--- /dev/null
+++ b/cc/test/test_skcanvas.cc
@@ -0,0 +1,26 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "cc/test/test_skcanvas.h"
+
+namespace cc {
+
+SaveCountingCanvas::SaveCountingCanvas() : SkNoDrawCanvas(100, 100) {}
+
+SkCanvas::SaveLayerStrategy SaveCountingCanvas::getSaveLayerStrategy(
+    const SaveLayerRec& rec) {
+  save_count_++;
+  return SkNoDrawCanvas::getSaveLayerStrategy(rec);
+}
+
+void SaveCountingCanvas::willRestore() {
+  restore_count_++;
+}
+
+void SaveCountingCanvas::onDrawRect(const SkRect& rect, const SkPaint& paint) {
+  draw_rect_ = rect;
+  paint_ = paint;
+}
+
+}  // namespace cc
diff --git a/cc/test/test_skcanvas.h b/cc/test/test_skcanvas.h
new file mode 100644
index 0000000..2b130a4
--- /dev/null
+++ b/cc/test/test_skcanvas.h
@@ -0,0 +1,31 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CC_TEST_TEST_SKCANVAS_H_
+#define CC_TEST_TEST_SKCANVAS_H_
+
+#include "third_party/skia/include/core/SkCanvas.h"
+#include "third_party/skia/include/utils/SkNoDrawCanvas.h"
+
+namespace cc {
+
+class SaveCountingCanvas : public SkNoDrawCanvas {
+ public:
+  SaveCountingCanvas();
+
+  // Note: getSaveLayerStrategy is used as "willSave", as willSave
+  // is not always called.
+  SaveLayerStrategy getSaveLayerStrategy(const SaveLayerRec& rec) override;
+  void willRestore() override;
+  void onDrawRect(const SkRect& rect, const SkPaint& paint) override;
+
+  int save_count_ = 0;
+  int restore_count_ = 0;
+  SkRect draw_rect_;
+  SkPaint paint_;
+};
+
+}  // namespace cc
+
+#endif  // CC_TEST_TEST_SKCANVAS_H_
diff --git a/chrome/app/chromeos_strings.grdp b/chrome/app/chromeos_strings.grdp
index 88298674..4c9541cb 100644
--- a/chrome/app/chromeos_strings.grdp
+++ b/chrome/app/chromeos_strings.grdp
@@ -6597,6 +6597,12 @@
   <message name="IDS_ARC_CRITICALLY_LOW_DISK_NOTIFICATION_MESSAGE" desc="Notification text warning users that they are critically low on disk space and Android apps can not be launched.">
     Free up disk space to launch Android apps.
   </message>
+  <message name="IDS_ARC_MANAGED_PROVISION_NOTIFICATION_TITLE" desc="Title of the notification shown to user during ARC provision when the ARC opt-in flow happens silently due to enterprise policies">
+    Google Play Store (beta)
+  </message>
+  <message name="IDS_ARC_MANAGED_PROVISION_NOTIFICATION_MESSAGE" desc="Notification shown to user during ARC provision when the ARC opt-in flow happens silently due to enterprise policies.">
+    Installing the Google Play Store on your <ph name="DEVICE_TYPE">$1<ex>Chromebook</ex></ph>. This could take a few minutes.
+  </message>
   <message name="IDS_ARC_OOBE_TERMS_HEADING" desc="Heading of the Arc Terms OOBE dialog.">
     Google Play Terms of Service
   </message>
diff --git a/chrome/app/theme/default_100_percent/cros/notification_play_store_optin_in_progress.png b/chrome/app/theme/default_100_percent/cros/notification_play_store_optin_in_progress.png
new file mode 100644
index 0000000..cd13e2d
--- /dev/null
+++ b/chrome/app/theme/default_100_percent/cros/notification_play_store_optin_in_progress.png
Binary files differ
diff --git a/chrome/app/theme/default_200_percent/cros/notification_play_store_optin_in_progress.png b/chrome/app/theme/default_200_percent/cros/notification_play_store_optin_in_progress.png
new file mode 100644
index 0000000..4a15b65
--- /dev/null
+++ b/chrome/app/theme/default_200_percent/cros/notification_play_store_optin_in_progress.png
Binary files differ
diff --git a/chrome/app/theme/theme_resources.grd b/chrome/app/theme/theme_resources.grd
index d6a1e78..23007e7 100644
--- a/chrome/app/theme/theme_resources.grd
+++ b/chrome/app/theme/theme_resources.grd
@@ -271,6 +271,7 @@
         <structure type="chrome_scaled_image" name="IDR_NOTIFICATION_PERIPHERAL_BATTERY_LOW" file="cros/notification_peripheral_battery_low.png" />
         <structure type="chrome_scaled_image" name="IDR_PORTAL_DETECTION_ALERT" file="cros/captive_portal_icon.png" />
         <structure type="chrome_scaled_image" name="IDR_ARC_PLAY_STORE_NOTIFICATION" file="cros/notification_play_store.png" />
+        <structure type="chrome_scaled_image" name="IDR_ARC_PLAY_STORE_OPTIN_IN_PROGRESS_NOTIFICATION" file="cros/notification_play_store_optin_in_progress.png" />
         <structure type="chrome_scaled_image" name="IDR_NOTIFICATION_EXTENSION_INSTALLED" file="common/notification_extension_installed.png" />
         <structure type="chrome_scaled_image" name="IDR_NOTIFICATION_SCREENSHOT_ANNOTATE" file="cros/notification_screenshot_annotate.png" />
         <structure type="chrome_scaled_image" name="IDR_NOTIFICATION_SCREENSHOT_COPY_TO_CLIPBOARD" file="cros/notification_screenshot_copy_to_clipboard.png" />
diff --git a/chrome/browser/browser_process_platform_part_chromeos.cc b/chrome/browser/browser_process_platform_part_chromeos.cc
index 568ab75..4b63b33 100644
--- a/chrome/browser/browser_process_platform_part_chromeos.cc
+++ b/chrome/browser/browser_process_platform_part_chromeos.cc
@@ -66,6 +66,7 @@
       device_disabling_manager_delegate_.get(),
       chromeos::CrosSettings::Get(),
       user_manager::UserManager::Get()));
+  device_disabling_manager_->Init();
 }
 
 void BrowserProcessPlatformPart::ShutdownDeviceDisablingManager() {
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn
index 29650e6..65cbb34 100644
--- a/chrome/browser/chromeos/BUILD.gn
+++ b/chrome/browser/chromeos/BUILD.gn
@@ -302,6 +302,8 @@
     "arc/intent_helper/arc_settings_service.h",
     "arc/notification/arc_boot_error_notification.cc",
     "arc/notification/arc_boot_error_notification.h",
+    "arc/notification/arc_provision_notification_service.cc",
+    "arc/notification/arc_provision_notification_service.h",
     "arc/optin/arc_optin_preference_handler.cc",
     "arc/optin/arc_optin_preference_handler.h",
     "arc/optin/arc_optin_preference_handler_observer.h",
@@ -1550,6 +1552,7 @@
     "arc/fileapi/arc_file_system_operation_runner_unittest.cc",
     "arc/intent_helper/arc_external_protocol_dialog_unittest.cc",
     "arc/intent_helper/arc_navigation_throttle_unittest.cc",
+    "arc/notification/arc_provision_notification_service_unittest.cc",
     "arc/optin/arc_terms_of_service_default_negotiator_unittest.cc",
     "arc/policy/arc_policy_bridge_unittest.cc",
     "attestation/attestation_ca_client_unittest.cc",
diff --git a/chrome/browser/chromeos/arc/arc_service_launcher.cc b/chrome/browser/chromeos/arc/arc_service_launcher.cc
index 989eb109..16915295 100644
--- a/chrome/browser/chromeos/arc/arc_service_launcher.cc
+++ b/chrome/browser/chromeos/arc/arc_service_launcher.cc
@@ -23,6 +23,7 @@
 #include "chrome/browser/chromeos/arc/fileapi/arc_file_system_operation_runner.h"
 #include "chrome/browser/chromeos/arc/intent_helper/arc_settings_service.h"
 #include "chrome/browser/chromeos/arc/notification/arc_boot_error_notification.h"
+#include "chrome/browser/chromeos/arc/notification/arc_provision_notification_service.h"
 #include "chrome/browser/chromeos/arc/policy/arc_policy_bridge.h"
 #include "chrome/browser/chromeos/arc/policy/arc_policy_util.h"
 #include "chrome/browser/chromeos/arc/print/arc_print_service.h"
@@ -133,6 +134,8 @@
   arc_service_manager_->AddService(
       base::MakeUnique<ArcProcessService>(arc_bridge_service));
   arc_service_manager_->AddService(
+      base::MakeUnique<ArcProvisionNotificationService>(arc_bridge_service));
+  arc_service_manager_->AddService(
       base::MakeUnique<ArcSettingsService>(arc_bridge_service));
   arc_service_manager_->AddService(
       base::MakeUnique<ArcStorageManager>(arc_bridge_service));
diff --git a/chrome/browser/chromeos/arc/arc_session_manager.cc b/chrome/browser/chromeos/arc/arc_session_manager.cc
index 37782a4..cfb02ac 100644
--- a/chrome/browser/chromeos/arc/arc_session_manager.cc
+++ b/chrome/browser/chromeos/arc/arc_session_manager.cc
@@ -178,7 +178,6 @@
   // AD-managed device.
   registry->RegisterStringPref(prefs::kArcActiveDirectoryPlayUserId,
                                std::string());
-  registry->RegisterBooleanPref(prefs::kArcCompatibleFilesystemChosen, false);
 }
 
 // static
@@ -373,8 +372,7 @@
     if (profile_->GetPrefs()->HasPrefPath(prefs::kArcSignedIn))
       profile_->GetPrefs()->SetBoolean(prefs::kArcSignedIn, false);
     ShutdownSession();
-    if (support_host_)
-      support_host_->ShowError(error, true);
+    ShowArcSupportHostError(error, true);
     return;
   }
 
@@ -392,8 +390,7 @@
 
   // We'll delay shutting down the ARC instance in this case to allow people
   // to send feedback.
-  if (support_host_)
-    support_host_->ShowError(error, true /* = show send feedback button */);
+  ShowArcSupportHostError(error, true /* = show send feedback button */);
 }
 
 void ArcSessionManager::SetState(State state) {
@@ -594,7 +591,7 @@
     // Check Android management in parallel.
     // Note: StartBackgroundAndroidManagementCheck() may call
     // OnBackgroundAndroidManagementChecked() synchronously (or
-    // asynchornously). In the callback, Google Play Store enabled preference
+    // asynchronously). In the callback, Google Play Store enabled preference
     // can be set to false if managed, and it triggers RequestDisable() via
     // ArcPlayStoreEnabledPreferenceHandler.
     // Thus, StartArc() should be called so that disabling should work even
@@ -627,7 +624,7 @@
 void ArcSessionManager::RequestArcDataRemoval() {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   DCHECK(profile_);
-  // TODO(hidehiko): DCHECK the previous state. This is called for three cases;
+  // TODO(hidehiko): DCHECK the previous state. This is called for four cases;
   // 1) Supporting managed user initial disabled case (Please see also
   //    ArcPlayStoreEnabledPreferenceHandler::Start() for details).
   // 2) Supporting enterprise triggered data removal.
@@ -666,10 +663,8 @@
     // If the user attempts to re-enable ARC while the ARC instance is still
     // running the user should not be able to continue until the ARC instance
     // has stopped.
-    if (support_host_) {
-      support_host_->ShowError(
-          ArcSupportHost::Error::SIGN_IN_SERVICE_UNAVAILABLE_ERROR, false);
-    }
+    ShowArcSupportHostError(
+        ArcSupportHost::Error::SIGN_IN_SERVICE_UNAVAILABLE_ERROR, false);
     UpdateOptInCancelUMA(OptInCancelReason::SESSION_BUSY);
     return;
   }
@@ -773,6 +768,9 @@
     support_host_->ShowArcLoading();
   }
 
+  for (auto& observer : observer_list_)
+    observer.OnArcOptInManagementCheckStarted();
+
   android_management_checker_ = base::MakeUnique<ArcAndroidManagementChecker>(
       profile_, context_->token_service(), context_->account_id(),
       false /* retry_on_error */);
@@ -799,17 +797,13 @@
       StartArc();
       break;
     case policy::AndroidManagementClient::Result::MANAGED:
-      if (support_host_) {
-        support_host_->ShowError(
-            ArcSupportHost::Error::ANDROID_MANAGEMENT_REQUIRED_ERROR, false);
-      }
+      ShowArcSupportHostError(
+          ArcSupportHost::Error::ANDROID_MANAGEMENT_REQUIRED_ERROR, false);
       UpdateOptInCancelUMA(OptInCancelReason::ANDROID_MANAGEMENT_REQUIRED);
       break;
     case policy::AndroidManagementClient::Result::ERROR:
-      if (support_host_) {
-        support_host_->ShowError(
-            ArcSupportHost::Error::SERVER_COMMUNICATION_ERROR, true);
-      }
+      ShowArcSupportHostError(ArcSupportHost::Error::SERVER_COMMUNICATION_ERROR,
+                              true);
       UpdateOptInCancelUMA(OptInCancelReason::NETWORK_ERROR);
       break;
   }
@@ -1030,6 +1024,15 @@
   attempt_user_exit_callback_ = callback;
 }
 
+void ArcSessionManager::ShowArcSupportHostError(
+    ArcSupportHost::Error error,
+    bool should_show_send_feedback) {
+  if (support_host_)
+    support_host_->ShowError(error, should_show_send_feedback);
+  for (auto& observer : observer_list_)
+    observer.OnArcErrorShowRequested(error);
+}
+
 std::ostream& operator<<(std::ostream& os,
                          const ArcSessionManager::State& state) {
 #define MAP_STATE(name)                \
diff --git a/chrome/browser/chromeos/arc/arc_session_manager.h b/chrome/browser/chromeos/arc/arc_session_manager.h
index a9bf4f5..f923d6a 100644
--- a/chrome/browser/chromeos/arc/arc_session_manager.h
+++ b/chrome/browser/chromeos/arc/arc_session_manager.h
@@ -94,6 +94,10 @@
     // is represented by "arc.enabled" preference, is updated.
     virtual void OnArcPlayStoreEnabledChanged(bool enabled) {}
 
+    // Called to notify that checking of Android management status started
+    // during the opt-in flow.
+    virtual void OnArcOptInManagementCheckStarted() {}
+
     // Called to notify that ARC has been initialized successfully.
     virtual void OnArcInitialStart() {}
 
@@ -105,6 +109,12 @@
     // browser_tests
     virtual void OnArcDataRemoved() {}
 
+    // Called to notify that the error is requested by the session manager to be
+    // displayed in the support host. This is called even if Support UI is
+    // disabled. Note that this is not called in cases when the support app
+    // switches to an error page by itself.
+    virtual void OnArcErrorShowRequested(ArcSupportHost::Error error) {}
+
    protected:
     virtual ~Observer() = default;
   };
@@ -305,6 +315,11 @@
   // is fixed.
   void MaybeReenableArc();
 
+  // Requests the support host (if it exists) to show the error, and notifies
+  // the observers.
+  void ShowArcSupportHostError(ArcSupportHost::Error error,
+                               bool should_show_send_feedback);
+
   std::unique_ptr<ArcSessionRunner> arc_session_runner_;
 
   // Unowned pointer. Keeps current profile.
diff --git a/chrome/browser/chromeos/arc/arc_util.cc b/chrome/browser/chromeos/arc/arc_util.cc
index 7723792c..fc6ce1c 100644
--- a/chrome/browser/chromeos/arc/arc_util.cc
+++ b/chrome/browser/chromeos/arc/arc_util.cc
@@ -7,9 +7,11 @@
 #include <linux/magic.h>
 #include <sys/statfs.h>
 
+#include "base/callback.h"
 #include "base/files/file_path.h"
 #include "base/logging.h"
 #include "base/sys_info.h"
+#include "base/task_scheduler/post_task.h"
 #include "base/threading/thread_restrictions.h"
 #include "chrome/browser/chromeos/arc/arc_session_manager.h"
 #include "chrome/browser/chromeos/login/user_flow.h"
@@ -19,6 +21,7 @@
 #include "chrome/common/pref_names.h"
 #include "components/arc/arc_util.h"
 #include "components/prefs/pref_service.h"
+#include "components/user_manager/known_user.h"
 #include "components/user_manager/user.h"
 #include "components/user_manager/user_manager.h"
 
@@ -32,6 +35,39 @@
 // Let IsAllowedForProfile() return "false" for any profile.
 bool g_disallow_for_testing = false;
 
+// Returns whether ARC can run on the filesystem mounted at |path|.
+// This function should run only on threads where IO operations are allowed.
+bool IsArcCompatibleFilesystem(const base::FilePath& path) {
+  base::ThreadRestrictions::AssertIOAllowed();
+
+  // If it can be verified it is not on ecryptfs, then it is ok.
+  struct statfs statfs_buf;
+  if (statfs(path.value().c_str(), &statfs_buf) < 0)
+    return false;
+  return statfs_buf.f_type != ECRYPTFS_SUPER_MAGIC;
+}
+
+// Stores the result of IsArcCompatibleFilesystem posted back from the blocking
+// task runner.
+void StoreCompatibilityCheckResult(const AccountId& account_id,
+                                   const base::Closure& callback,
+                                   bool is_compatible) {
+  if (is_compatible) {
+    user_manager::known_user::SetIntegerPref(
+        account_id, prefs::kArcCompatibleFilesystemChosen,
+        arc::kFileSystemCompatible);
+  }
+  callback.Run();
+}
+
+FileSystemCompatibilityState GetFileSystemCompatibilityPref(
+    const AccountId& account_id) {
+  int pref_value = kFileSystemIncompatible;
+  user_manager::known_user::GetIntegerPref(
+      account_id, prefs::kArcCompatibleFilesystemChosen, &pref_value);
+  return static_cast<FileSystemCompatibilityState>(pref_value);
+}
+
 }  // namespace
 
 bool IsArcAllowedForProfile(const Profile* profile) {
@@ -121,10 +157,19 @@
 }
 
 bool IsArcCompatibleFileSystemUsedForProfile(const Profile* profile) {
-  // chromeos::UserSessionManager::PrepareProfile does the actual file system
-  // check and stores the result to prefs, so that it survives crash-restart.
+  const user_manager::User* user =
+      chromeos::ProfileHelper::Get()->GetUserByProfile(profile);
+
+  // Returns false for profiles not associated with users (like sign-in profile)
+  if (!user)
+    return false;
+
+  // chromeos::UserSessionManager does the actual file system check and stores
+  // the result to prefs, so that it survives crash-restart.
+  FileSystemCompatibilityState filesystem_compatibility =
+      GetFileSystemCompatibilityPref(user->GetAccountId());
   const bool is_filesystem_compatible =
-      profile->GetPrefs()->GetBoolean(prefs::kArcCompatibleFilesystemChosen);
+      filesystem_compatibility != kFileSystemIncompatible;
   std::string arc_sdk_version;
   const bool is_M = base::SysInfo::GetLsbReleaseValue(kLsbReleaseArcVersionKey,
                                                       &arc_sdk_version) &&
@@ -189,14 +234,34 @@
              prefs::kArcLocationServiceEnabled);
 }
 
-bool IsArcCompatibleFilesystem(const base::FilePath& path) {
-  base::ThreadRestrictions::AssertIOAllowed();
+void UpdateArcFileSystemCompatibilityPrefIfNeeded(
+    const AccountId& account_id,
+    const base::FilePath& profile_path,
+    const base::Closure& callback) {
+  DCHECK(!callback.is_null());
 
-  // If it can be verified it is not on ecryptfs, then it is ok.
-  struct statfs statfs_buf;
-  if (statfs(path.value().c_str(), &statfs_buf) < 0)
-    return false;
-  return statfs_buf.f_type != ECRYPTFS_SUPER_MAGIC;
+  // If ARC is not available, skip the check.
+  if (!IsArcAvailable()) {
+    callback.Run();
+    return;
+  }
+
+  // If the compatibility has been already confirmed, skip the check.
+  if (GetFileSystemCompatibilityPref(account_id) != kFileSystemIncompatible) {
+    callback.Run();
+    return;
+  }
+
+  // Otherwise, check the underlying filesystem.
+  base::PostTaskWithTraitsAndReplyWithResult(
+      FROM_HERE,
+      base::TaskTraits()
+          .WithShutdownBehavior(
+              base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN)
+          .WithPriority(base::TaskPriority::USER_BLOCKING)
+          .MayBlock(),
+      base::Bind(&IsArcCompatibleFilesystem, profile_path),
+      base::Bind(&StoreCompatibilityCheckResult, account_id, callback));
 }
 
 }  // namespace arc
diff --git a/chrome/browser/chromeos/arc/arc_util.h b/chrome/browser/chromeos/arc/arc_util.h
index 7765832..80b8a9f 100644
--- a/chrome/browser/chromeos/arc/arc_util.h
+++ b/chrome/browser/chromeos/arc/arc_util.h
@@ -5,12 +5,17 @@
 #ifndef CHROME_BROWSER_CHROMEOS_ARC_ARC_UTIL_H_
 #define CHROME_BROWSER_CHROMEOS_ARC_ARC_UTIL_H_
 
+#include <stdint.h>
+
+#include "base/callback_forward.h"
+
 // Most utility should be put in components/arc/arc_util.{h,cc}, rather than
 // here. However, some utility implementation requires other modules defined in
 // chrome/, so this file contains such utilities.
 // Note that it is not allowed to have dependency from components/ to chrome/
 // by DEPS.
 
+class AccountId;
 class Profile;
 
 namespace base {
@@ -19,6 +24,22 @@
 
 namespace arc {
 
+// Values to be stored in the local state preference to keep track of the
+// filesystem encryption migration status.
+enum FileSystemCompatibilityState : int32_t {
+  // No migiration has happend, user keeps using the old file system.
+  kFileSystemIncompatible = 0,
+  // Migration has happend. New filesystem is in use.
+  kFileSystemCompatible = 1,
+  // Migration has happend, and a notification about the fact was already shown.
+  // TODO(kinaba): This value isn't yet used until crbug.com/711095 is done.
+  kFileSystemCompatibleAndNotified = 2,
+
+  // Existing code assumes that kFileSystemIncompatible is the only state
+  // representing incompatibility and other values are all variants of
+  // "compatible" state. Be careful in the case adding a new enum value.
+};
+
 // Returns true if ARC is allowed to run for the given profile.
 // Otherwise, returns false, e.g. if the Profile is not for the primary user,
 // ARC is not available on the device, it is in the flow to set up managed
@@ -78,9 +99,13 @@
 // ArcBackupRestoreEnabled and ArcLocationServiceEnabled) are managed.
 bool AreArcAllOptInPreferencesManagedForProfile(const Profile* profile);
 
-// Returns whether ARC can run on the filesystem mounted at |path|.
-// This function should run only on threads where IO operations are allowed.
-bool IsArcCompatibleFilesystem(const base::FilePath& path);
+// Checks and updates the preference value whether the underlying filesystem
+// for the profile is compatible with ARC, when necessary. After it's done (or
+// skipped), |callback| is run either synchronously or asynchronously.
+void UpdateArcFileSystemCompatibilityPrefIfNeeded(
+    const AccountId& account_id,
+    const base::FilePath& profile_path,
+    const base::Closure& callback);
 
 }  // namespace arc
 
diff --git a/chrome/browser/chromeos/arc/arc_util_unittest.cc b/chrome/browser/chromeos/arc/arc_util_unittest.cc
index 24e6539..57e06a8 100644
--- a/chrome/browser/chromeos/arc/arc_util_unittest.cc
+++ b/chrome/browser/chromeos/arc/arc_util_unittest.cc
@@ -12,6 +12,7 @@
 #include "base/sys_info.h"
 #include "base/test/scoped_command_line.h"
 #include "base/values.h"
+#include "chrome/browser/chromeos/arc/arc_session_manager.h"
 #include "chrome/browser/chromeos/login/supervised/supervised_user_creation_flow.h"
 #include "chrome/browser/chromeos/login/users/fake_chrome_user_manager.h"
 #include "chrome/browser/chromeos/login/users/scoped_user_manager_enabler.h"
@@ -21,8 +22,10 @@
 #include "chrome/common/pref_names.h"
 #include "chrome/test/base/testing_profile.h"
 #include "components/prefs/pref_service.h"
+#include "components/prefs/testing_pref_service.h"
 #include "components/signin/core/account_id/account_id.h"
 #include "components/sync_preferences/testing_pref_service_syncable.h"
+#include "components/user_manager/known_user.h"
 #include "components/user_manager/user_manager.h"
 #include "components/user_manager/user_names.h"
 #include "content/public/test/test_browser_thread_bundle.h"
@@ -85,6 +88,23 @@
   DISALLOW_COPY_AND_ASSIGN(ScopedLogIn);
 };
 
+class FakeUserManagerWithLocalState : public chromeos::FakeChromeUserManager {
+ public:
+  FakeUserManagerWithLocalState()
+      : test_local_state_(base::MakeUnique<TestingPrefServiceSimple>()) {
+    RegisterPrefs(test_local_state_->registry());
+  }
+
+  PrefService* GetLocalState() const override {
+    return test_local_state_.get();
+  }
+
+ private:
+  std::unique_ptr<TestingPrefServiceSimple> test_local_state_;
+
+  DISALLOW_COPY_AND_ASSIGN(FakeUserManagerWithLocalState);
+};
+
 }  // namespace
 
 class ChromeArcUtilTest : public testing::Test {
@@ -98,7 +118,7 @@
 
     user_manager_enabler_ =
         base::MakeUnique<chromeos::ScopedUserManagerEnabler>(
-            new chromeos::FakeChromeUserManager());
+            new FakeUserManagerWithLocalState());
     chromeos::WallpaperManager::Initialize();
     profile_ = base::MakeUnique<TestingProfile>();
     profile_->set_profile_name(kTestProfileName);
@@ -278,12 +298,11 @@
   // TODO(kinaba): Come up with some way to test the conditions below
   // causes differences in the return values of IsArcAllowedForProfile()
   // and IsArcAllowedInAppListForProfile().
-  ScopedLogIn login(GetFakeUserManager(),
-                    AccountId::FromUserEmailGaiaId(
-                        profile()->GetProfileUserName(), kTestGaiaId));
+  const AccountId id(AccountId::FromUserEmailGaiaId(
+      profile()->GetProfileUserName(), kTestGaiaId));
+  ScopedLogIn login(GetFakeUserManager(), id);
 
   // Unconfirmed + Old ARC
-  profile()->GetPrefs()->ClearPref(prefs::kArcCompatibleFilesystemChosen);
   base::SysInfo::SetChromeOSVersionInfoForTest(
       "CHROMEOS_ARC_ANDROID_SDK_VERSION=23", base::Time::Now());
   EXPECT_TRUE(IsArcCompatibleFileSystemUsedForProfile(profile()));
@@ -294,8 +313,8 @@
   EXPECT_FALSE(IsArcCompatibleFileSystemUsedForProfile(profile()));
 
   // Old FS + Old ARC
-  profile()->GetPrefs()->SetBoolean(prefs::kArcCompatibleFilesystemChosen,
-                                    false);
+  user_manager::known_user::SetIntegerPref(
+      id, prefs::kArcCompatibleFilesystemChosen, kFileSystemIncompatible);
   base::SysInfo::SetChromeOSVersionInfoForTest(
       "CHROMEOS_ARC_ANDROID_SDK_VERSION=23", base::Time::Now());
   EXPECT_TRUE(IsArcCompatibleFileSystemUsedForProfile(profile()));
@@ -306,8 +325,8 @@
   EXPECT_FALSE(IsArcCompatibleFileSystemUsedForProfile(profile()));
 
   // New FS + Old ARC
-  profile()->GetPrefs()->SetBoolean(prefs::kArcCompatibleFilesystemChosen,
-                                    true);
+  user_manager::known_user::SetIntegerPref(
+      id, prefs::kArcCompatibleFilesystemChosen, kFileSystemCompatible);
   base::SysInfo::SetChromeOSVersionInfoForTest(
       "CHROMEOS_ARC_ANDROID_SDK_VERSION=23", base::Time::Now());
   EXPECT_TRUE(IsArcCompatibleFileSystemUsedForProfile(profile()));
@@ -316,6 +335,19 @@
   base::SysInfo::SetChromeOSVersionInfoForTest(
       "CHROMEOS_ARC_ANDROID_SDK_VERSION=25", base::Time::Now());
   EXPECT_TRUE(IsArcCompatibleFileSystemUsedForProfile(profile()));
+
+  // New FS (User notified) + Old ARC
+  user_manager::known_user::SetIntegerPref(
+      id, prefs::kArcCompatibleFilesystemChosen,
+      kFileSystemCompatibleAndNotified);
+  base::SysInfo::SetChromeOSVersionInfoForTest(
+      "CHROMEOS_ARC_ANDROID_SDK_VERSION=23", base::Time::Now());
+  EXPECT_TRUE(IsArcCompatibleFileSystemUsedForProfile(profile()));
+
+  // New FS (User notified) + New ARC
+  base::SysInfo::SetChromeOSVersionInfoForTest(
+      "CHROMEOS_ARC_ANDROID_SDK_VERSION=25", base::Time::Now());
+  EXPECT_TRUE(IsArcCompatibleFileSystemUsedForProfile(profile()));
 }
 
 TEST_F(ChromeArcUtilTest, ArcPlayStoreEnabledForProfile) {
diff --git a/chrome/browser/chromeos/arc/notification/arc_provision_notification_service.cc b/chrome/browser/chromeos/arc/notification/arc_provision_notification_service.cc
new file mode 100644
index 0000000..15b22b6
--- /dev/null
+++ b/chrome/browser/chromeos/arc/notification/arc_provision_notification_service.cc
@@ -0,0 +1,148 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/chromeos/arc/notification/arc_provision_notification_service.h"
+
+#include <utility>
+
+#include "ash/system/devicetype_utils.h"
+#include "base/memory/ptr_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "chrome/browser/chromeos/arc/arc_util.h"
+#include "chrome/grit/component_extension_resources.h"
+#include "chrome/grit/generated_resources.h"
+#include "chrome/grit/theme_resources.h"
+#include "components/signin/core/account_id/account_id.h"
+#include "components/user_manager/user.h"
+#include "components/user_manager/user_manager.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/base/resource/resource_bundle.h"
+#include "ui/gfx/image/image.h"
+#include "ui/message_center/message_center.h"
+#include "ui/message_center/notification.h"
+#include "ui/message_center/notification_types.h"
+#include "ui/message_center/notifier_settings.h"
+#include "url/gurl.h"
+
+namespace arc {
+
+namespace {
+
+constexpr char kManagedProvisionNotificationId[] = "arc_managed_provision";
+constexpr char kManagedProvisionNotifierId[] = "arc_managed_provision";
+constexpr char kManagedProvisionDisplaySource[] = "arc_managed_provision";
+
+class DelegateImpl : public ArcProvisionNotificationService::Delegate {
+ public:
+  void ShowManagedProvisionNotification() override;
+  void RemoveManagedProvisionNotification() override;
+};
+
+void DelegateImpl::ShowManagedProvisionNotification() {
+  message_center::NotifierId notifier_id(
+      message_center::NotifierId::SYSTEM_COMPONENT,
+      kManagedProvisionNotifierId);
+  const AccountId& account_id =
+      user_manager::UserManager::Get()->GetPrimaryUser()->GetAccountId();
+  notifier_id.profile_id = account_id.GetUserEmail();
+  message_center::RichNotificationData optional_fields;
+  optional_fields.clickable = false;
+  optional_fields.never_timeout = true;
+
+  message_center::MessageCenter::Get()->AddNotification(
+      base::MakeUnique<message_center::Notification>(
+          message_center::NOTIFICATION_TYPE_SIMPLE,
+          kManagedProvisionNotificationId,
+          l10n_util::GetStringUTF16(
+              IDS_ARC_MANAGED_PROVISION_NOTIFICATION_TITLE),
+          l10n_util::GetStringFUTF16(
+              IDS_ARC_MANAGED_PROVISION_NOTIFICATION_MESSAGE,
+              ash::GetChromeOSDeviceName()),
+          gfx::Image(ui::ResourceBundle::GetSharedInstance().GetImageNamed(
+              IDR_ARC_PLAY_STORE_OPTIN_IN_PROGRESS_NOTIFICATION)),
+          base::UTF8ToUTF16(kManagedProvisionDisplaySource), GURL(),
+          notifier_id, optional_fields, nullptr));
+}
+
+void DelegateImpl::RemoveManagedProvisionNotification() {
+  message_center::MessageCenter::Get()->RemoveNotification(
+      kManagedProvisionNotificationId, false);
+}
+
+}  // namespace
+
+ArcProvisionNotificationService::Delegate::Delegate() = default;
+
+ArcProvisionNotificationService::Delegate::~Delegate() = default;
+
+ArcProvisionNotificationService::ArcProvisionNotificationService(
+    ArcBridgeService* bridge_service)
+    : ArcProvisionNotificationService(bridge_service,
+                                      base::MakeUnique<DelegateImpl>()) {}
+
+ArcProvisionNotificationService::~ArcProvisionNotificationService() {
+  // Make sure no notification is left being shown.
+  delegate_->RemoveManagedProvisionNotification();
+  ArcSessionManager::Get()->RemoveObserver(this);
+}
+
+// static
+std::unique_ptr<ArcProvisionNotificationService>
+ArcProvisionNotificationService::CreateForTesting(
+    ArcBridgeService* bridge_service,
+    std::unique_ptr<Delegate> delegate) {
+  return base::WrapUnique<ArcProvisionNotificationService>(
+      new ArcProvisionNotificationService(bridge_service, std::move(delegate)));
+}
+
+ArcProvisionNotificationService::ArcProvisionNotificationService(
+    ArcBridgeService* bridge_service,
+    std::unique_ptr<Delegate> delegate)
+    : ArcService(bridge_service), delegate_(std::move(delegate)) {
+  ArcSessionManager::Get()->AddObserver(this);
+}
+
+void ArcProvisionNotificationService::OnArcPlayStoreEnabledChanged(
+    bool enabled) {
+  if (!enabled) {
+    // Make sure no notification is shown after ARC gets disabled.
+    delegate_->RemoveManagedProvisionNotification();
+  }
+}
+
+void ArcProvisionNotificationService::OnArcOptInManagementCheckStarted() {
+  // This observer is notified at an early phase of the opt-in flow, so start
+  // showing the notification if the opt-in flow happens silently (due to the
+  // managed prefs), or ensure that no notification is shown otherwise.
+  const Profile* const profile = ArcSessionManager::Get()->profile();
+  if (IsArcPlayStoreEnabledPreferenceManagedForProfile(profile) &&
+      AreArcAllOptInPreferencesManagedForProfile(profile)) {
+    delegate_->ShowManagedProvisionNotification();
+  } else {
+    delegate_->RemoveManagedProvisionNotification();
+  }
+}
+
+void ArcProvisionNotificationService::OnArcInitialStart() {
+  // The opt-in flow finished successfully, so remove the notification.
+  delegate_->RemoveManagedProvisionNotification();
+}
+
+void ArcProvisionNotificationService::OnArcSessionStopped(
+    ArcStopReason stop_reason) {
+  // One of the reasons of ARC being stopped is a failure of the opt-in flow.
+  // Therefore remove the notification if it is shown.
+  delegate_->RemoveManagedProvisionNotification();
+}
+
+void ArcProvisionNotificationService::OnArcErrorShowRequested(
+    ArcSupportHost::Error error) {
+  // If an error happens during the opt-in flow, then the provision fails, and
+  // the notification should be therefore removed if it is shown. Do this here
+  // unconditionally as there should be no notification displayed otherwise
+  // anyway.
+  delegate_->RemoveManagedProvisionNotification();
+}
+
+}  // namespace arc
diff --git a/chrome/browser/chromeos/arc/notification/arc_provision_notification_service.h b/chrome/browser/chromeos/arc/notification/arc_provision_notification_service.h
new file mode 100644
index 0000000..2f2aa6fa
--- /dev/null
+++ b/chrome/browser/chromeos/arc/notification/arc_provision_notification_service.h
@@ -0,0 +1,64 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_CHROMEOS_ARC_NOTIFICATION_ARC_PROVISION_NOTIFICATION_SERVICE_H_
+#define CHROME_BROWSER_CHROMEOS_ARC_NOTIFICATION_ARC_PROVISION_NOTIFICATION_SERVICE_H_
+
+#include <memory>
+
+#include "base/macros.h"
+#include "chrome/browser/chromeos/arc/arc_session_manager.h"
+#include "components/arc/arc_service.h"
+
+namespace arc {
+
+// Watches for ARC provisioning status and displays a notification during
+// provision when ARC opt-in flow happens silently due to configured policies.
+class ArcProvisionNotificationService : public ArcService,
+                                        public ArcSessionManager::Observer {
+ public:
+  // The delegate whose methods are used by the service for showing/hiding the
+  // notifications. The delegate exists for unit testing purposes.
+  class Delegate {
+   public:
+    Delegate();
+    virtual ~Delegate();
+    virtual void ShowManagedProvisionNotification() = 0;
+    virtual void RemoveManagedProvisionNotification() = 0;
+
+   private:
+    DISALLOW_COPY_AND_ASSIGN(Delegate);
+  };
+
+  // Constructs with the default delegate implementation that uses message
+  // center for showing the notifications.
+  explicit ArcProvisionNotificationService(ArcBridgeService* bridge_service);
+
+  ~ArcProvisionNotificationService() override;
+
+  // Constructs an instance with the supplied delegate.
+  static std::unique_ptr<ArcProvisionNotificationService> CreateForTesting(
+      ArcBridgeService* bridge_service,
+      std::unique_ptr<Delegate> delegate);
+
+ private:
+  // Constructs with the supplied delegate.
+  ArcProvisionNotificationService(ArcBridgeService* bridge_service,
+                                  std::unique_ptr<Delegate> delegate);
+
+  // ArcSessionManager::Observer:
+  void OnArcPlayStoreEnabledChanged(bool enabled) override;
+  void OnArcOptInManagementCheckStarted() override;
+  void OnArcInitialStart() override;
+  void OnArcSessionStopped(ArcStopReason stop_reason) override;
+  void OnArcErrorShowRequested(ArcSupportHost::Error error) override;
+
+  std::unique_ptr<Delegate> delegate_;
+
+  DISALLOW_COPY_AND_ASSIGN(ArcProvisionNotificationService);
+};
+
+}  // namespace arc
+
+#endif  // CHROME_BROWSER_CHROMEOS_ARC_NOTIFICATION_ARC_PROVISION_NOTIFICATION_SERVICE_H_
diff --git a/chrome/browser/chromeos/arc/notification/arc_provision_notification_service_unittest.cc b/chrome/browser/chromeos/arc/notification/arc_provision_notification_service_unittest.cc
new file mode 100644
index 0000000..6a855d2e
--- /dev/null
+++ b/chrome/browser/chromeos/arc/notification/arc_provision_notification_service_unittest.cc
@@ -0,0 +1,299 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/chromeos/arc/notification/arc_provision_notification_service.h"
+
+#include <memory>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/command_line.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/memory/ptr_util.h"
+#include "base/values.h"
+#include "chrome/browser/chromeos/arc/arc_auth_notification.h"
+#include "chrome/browser/chromeos/arc/arc_optin_uma.h"
+#include "chrome/browser/chromeos/arc/arc_session_manager.h"
+#include "chrome/browser/chromeos/login/users/fake_chrome_user_manager.h"
+#include "chrome/browser/chromeos/login/users/scoped_user_manager_enabler.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/test/base/testing_profile.h"
+#include "chromeos/dbus/dbus_thread_manager.h"
+#include "chromeos/dbus/fake_session_manager_client.h"
+#include "components/arc/arc_bridge_service.h"
+#include "components/arc/arc_service_manager.h"
+#include "components/arc/arc_util.h"
+#include "components/arc/test/fake_arc_session.h"
+#include "components/prefs/pref_service.h"
+#include "components/sync_preferences/testing_pref_service_syncable.h"
+#include "components/user_manager/user_manager.h"
+#include "content/public/test/test_browser_thread_bundle.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using testing::AnyNumber;
+using testing::AtLeast;
+using testing::Mock;
+using testing::StrictMock;
+
+namespace arc {
+
+namespace {
+
+class MockArcProvisionNotificationServiceDelegate
+    : public ArcProvisionNotificationService::Delegate {
+ public:
+  MOCK_METHOD0(ShowManagedProvisionNotification, void());
+  MOCK_METHOD0(RemoveManagedProvisionNotification, void());
+};
+
+class ArcProvisionNotificationServiceTest : public testing::Test {
+ protected:
+  ArcProvisionNotificationServiceTest()
+      : user_manager_enabler_(new chromeos::FakeChromeUserManager()) {}
+
+  void SetUp() override {
+    chromeos::DBusThreadManager::GetSetterForTesting()->SetSessionManagerClient(
+        base::MakeUnique<chromeos::FakeSessionManagerClient>());
+    chromeos::DBusThreadManager::Initialize();
+
+    SetArcAvailableCommandLineForTesting(
+        base::CommandLine::ForCurrentProcess());
+    ArcSessionManager::DisableUIForTesting();
+    ArcAuthNotification::DisableForTesting();
+
+    ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
+    TestingProfile::Builder profile_builder;
+    profile_builder.SetProfileName("user@gmail.com");
+    profile_builder.SetPath(temp_dir_.GetPath().AppendASCII("TestArcProfile"));
+    profile_ = profile_builder.Build();
+
+    arc_service_manager_ = base::MakeUnique<ArcServiceManager>(nullptr);
+    arc_session_manager_ = base::MakeUnique<ArcSessionManager>(
+        base::MakeUnique<ArcSessionRunner>(base::Bind(FakeArcSession::Create)));
+    std::unique_ptr<MockArcProvisionNotificationServiceDelegate>
+        mock_arc_provision_notification_service_delegate = base::MakeUnique<
+            StrictMock<MockArcProvisionNotificationServiceDelegate>>();
+    arc_provision_notification_service_delegate_ =
+        mock_arc_provision_notification_service_delegate.get();
+    arc_provision_notification_service_ =
+        ArcProvisionNotificationService::CreateForTesting(
+            arc_bridge_service(),
+            std::move(mock_arc_provision_notification_service_delegate));
+
+    const AccountId account_id(AccountId::FromUserEmailGaiaId(
+        profile()->GetProfileUserName(), "1234567890"));
+    GetFakeUserManager()->AddUser(account_id);
+    GetFakeUserManager()->LoginUser(account_id);
+  }
+
+  void TearDown() override {
+    EXPECT_CALL(*arc_provision_notification_service_delegate(),
+                RemoveManagedProvisionNotification())
+        .Times(AtLeast(1));
+    arc_provision_notification_service_.reset();
+    arc_provision_notification_service_delegate_ = nullptr;
+    arc_session_manager_.reset();
+    arc_service_manager_.reset();
+    profile_.reset();
+    chromeos::DBusThreadManager::Shutdown();
+  }
+
+  TestingProfile* profile() { return profile_.get(); }
+  ArcServiceManager* arc_service_manager() {
+    return arc_service_manager_.get();
+  }
+  ArcBridgeService* arc_bridge_service() {
+    return arc_service_manager_->arc_bridge_service();
+  }
+  ArcSessionManager* arc_session_manager() {
+    return arc_session_manager_.get();
+  }
+  MockArcProvisionNotificationServiceDelegate*
+  arc_provision_notification_service_delegate() {
+    return arc_provision_notification_service_delegate_;
+  }
+  ArcProvisionNotificationService* arc_provision_notification_service() {
+    return arc_provision_notification_service_.get();
+  }
+  chromeos::FakeChromeUserManager* GetFakeUserManager() {
+    return static_cast<chromeos::FakeChromeUserManager*>(
+        user_manager::UserManager::Get());
+  }
+
+ private:
+  content::TestBrowserThreadBundle thread_bundle_;
+  chromeos::ScopedUserManagerEnabler user_manager_enabler_;
+  base::ScopedTempDir temp_dir_;
+  std::unique_ptr<TestingProfile> profile_;
+  std::unique_ptr<ArcServiceManager> arc_service_manager_;
+  std::unique_ptr<ArcSessionManager> arc_session_manager_;
+  std::unique_ptr<ArcProvisionNotificationService>
+      arc_provision_notification_service_;
+  MockArcProvisionNotificationServiceDelegate*
+      arc_provision_notification_service_delegate_ = nullptr;
+
+  DISALLOW_COPY_AND_ASSIGN(ArcProvisionNotificationServiceTest);
+};
+
+}  // namespace
+
+// The managed provision notification is displayed from the beginning of the
+// silent opt-in till its successful finish.
+TEST_F(ArcProvisionNotificationServiceTest,
+       ManagedProvisionNotification_Basic) {
+  // Set up managed ARC and assign managed values to all opt-in prefs.
+  profile()->GetTestingPrefService()->SetManagedPref(
+      prefs::kArcEnabled, base::MakeUnique<base::Value>(true));
+  profile()->GetTestingPrefService()->SetManagedPref(
+      prefs::kArcBackupRestoreEnabled, base::MakeUnique<base::Value>(false));
+  profile()->GetTestingPrefService()->SetManagedPref(
+      prefs::kArcLocationServiceEnabled, base::MakeUnique<base::Value>(false));
+
+  arc_session_manager()->SetProfile(profile());
+
+  // Trigger opt-in flow. The notification gets shown.
+  EXPECT_CALL(*arc_provision_notification_service_delegate(),
+              ShowManagedProvisionNotification());
+  arc_session_manager()->RequestEnable();
+  EXPECT_EQ(ArcSessionManager::State::ACTIVE, arc_session_manager()->state());
+  Mock::VerifyAndClearExpectations(
+      arc_provision_notification_service_delegate());
+
+  // Emulate successful provisioning. The notification gets removed.
+  EXPECT_CALL(*arc_provision_notification_service_delegate(),
+              RemoveManagedProvisionNotification())
+      .Times(AtLeast(1));
+  arc_session_manager()->OnProvisioningFinished(ProvisioningResult::SUCCESS);
+  Mock::VerifyAndClearExpectations(
+      arc_provision_notification_service_delegate());
+}
+
+// The managed provision notification is not displayed after the restart if the
+// provisioning was successful.
+TEST_F(ArcProvisionNotificationServiceTest,
+       ManagedProvisionNotification_Restart) {
+  // No notifications are expected to be shown in this test, but the delegate
+  // method that removes the notification is allowed to be called.
+  EXPECT_CALL(*arc_provision_notification_service_delegate(),
+              RemoveManagedProvisionNotification())
+      .Times(AnyNumber());
+
+  // Set up managed ARC and assign managed values to all opt-in prefs.
+  profile()->GetTestingPrefService()->SetManagedPref(
+      prefs::kArcEnabled, base::MakeUnique<base::Value>(true));
+  profile()->GetTestingPrefService()->SetManagedPref(
+      prefs::kArcBackupRestoreEnabled, base::MakeUnique<base::Value>(false));
+  profile()->GetTestingPrefService()->SetManagedPref(
+      prefs::kArcLocationServiceEnabled, base::MakeUnique<base::Value>(false));
+  // Set the pref that indicates that signing into ARC has already been
+  // performed.
+  profile()->GetPrefs()->SetBoolean(prefs::kArcSignedIn, true);
+
+  arc_session_manager()->SetProfile(profile());
+
+  // Enable ARC. The opt-in flow doesn't take place, and no notification is
+  // shown.
+  arc_session_manager()->RequestEnable();
+  EXPECT_EQ(ArcSessionManager::State::ACTIVE, arc_session_manager()->state());
+  arc_session_manager()->OnProvisioningFinished(ProvisioningResult::SUCCESS);
+}
+
+// The managed provision notification is displayed from the beginning of the
+// silent opt-in till the failure of the provision.
+TEST_F(ArcProvisionNotificationServiceTest,
+       ManagedProvisionNotification_Failure) {
+  // Set up managed ARC and assign managed values to all opt-in prefs.
+  profile()->GetTestingPrefService()->SetManagedPref(
+      prefs::kArcEnabled, base::MakeUnique<base::Value>(true));
+  profile()->GetTestingPrefService()->SetManagedPref(
+      prefs::kArcBackupRestoreEnabled, base::MakeUnique<base::Value>(false));
+  profile()->GetTestingPrefService()->SetManagedPref(
+      prefs::kArcLocationServiceEnabled, base::MakeUnique<base::Value>(false));
+
+  arc_session_manager()->SetProfile(profile());
+
+  // Trigger opt-in flow. The notification gets shown.
+  EXPECT_CALL(*arc_provision_notification_service_delegate(),
+              ShowManagedProvisionNotification());
+  arc_session_manager()->RequestEnable();
+  EXPECT_EQ(ArcSessionManager::State::ACTIVE, arc_session_manager()->state());
+  Mock::VerifyAndClearExpectations(
+      arc_provision_notification_service_delegate());
+
+  // Emulate provisioning failure that leads to stopping ARC. The notification
+  // gets removed.
+  EXPECT_CALL(*arc_provision_notification_service_delegate(),
+              RemoveManagedProvisionNotification())
+      .Times(AtLeast(1));
+  arc_session_manager()->OnProvisioningFinished(
+      ProvisioningResult::CHROME_SERVER_COMMUNICATION_ERROR);
+  Mock::VerifyAndClearExpectations(
+      arc_provision_notification_service_delegate());
+}
+
+// The managed provision notification is displayed from the beginning of the
+// silent opt-in till the failure of the provision.
+TEST_F(ArcProvisionNotificationServiceTest,
+       ManagedProvisionNotification_FailureNotStopping) {
+  // Set up managed ARC and assign managed values to all opt-in prefs.
+  profile()->GetTestingPrefService()->SetManagedPref(
+      prefs::kArcEnabled, base::MakeUnique<base::Value>(true));
+  profile()->GetTestingPrefService()->SetManagedPref(
+      prefs::kArcBackupRestoreEnabled, base::MakeUnique<base::Value>(false));
+  profile()->GetTestingPrefService()->SetManagedPref(
+      prefs::kArcLocationServiceEnabled, base::MakeUnique<base::Value>(false));
+
+  arc_session_manager()->SetProfile(profile());
+
+  // Trigger opt-in flow. The notification gets shown.
+  EXPECT_CALL(*arc_provision_notification_service_delegate(),
+              ShowManagedProvisionNotification());
+  arc_session_manager()->RequestEnable();
+  EXPECT_EQ(ArcSessionManager::State::ACTIVE, arc_session_manager()->state());
+  Mock::VerifyAndClearExpectations(
+      arc_provision_notification_service_delegate());
+
+  // Emulate provisioning failure that leads to showing an error screen without
+  // shutting ARC down. The notification gets removed.
+  EXPECT_CALL(*arc_provision_notification_service_delegate(),
+              RemoveManagedProvisionNotification())
+      .Times(AtLeast(1));
+  arc_session_manager()->OnProvisioningFinished(
+      ProvisioningResult::NO_NETWORK_CONNECTION);
+  Mock::VerifyAndClearExpectations(
+      arc_provision_notification_service_delegate());
+}
+
+// The managed provision notification is not displayed when opt-in prefs are not
+// managed.
+TEST_F(ArcProvisionNotificationServiceTest,
+       ManagedProvisionNotification_NotSilent) {
+  // No notifications are expected to be shown in this test, but the delegate
+  // method that removes the notification is allowed to be called.
+  EXPECT_CALL(*arc_provision_notification_service_delegate(),
+              RemoveManagedProvisionNotification())
+      .Times(AnyNumber());
+
+  // Set ARC to be managed.
+  profile()->GetTestingPrefService()->SetManagedPref(
+      prefs::kArcEnabled, base::MakeUnique<base::Value>(true));
+
+  arc_session_manager()->SetProfile(profile());
+
+  // Trigger opt-in flow. The notification is not shown.
+  arc_session_manager()->RequestEnable();
+  EXPECT_EQ(ArcSessionManager::State::NEGOTIATING_TERMS_OF_SERVICE,
+            arc_session_manager()->state());
+
+  // Emulate accepting the terms of service.
+  profile()->GetPrefs()->SetBoolean(prefs::kArcTermsAccepted, true);
+  arc_session_manager()->StartArcForTesting();
+  EXPECT_EQ(ArcSessionManager::State::ACTIVE, arc_session_manager()->state());
+
+  // Emulate successful provisioning.
+  arc_session_manager()->OnProvisioningFinished(ProvisioningResult::SUCCESS);
+}
+
+}  // namespace arc
diff --git a/chrome/browser/chromeos/first_run/drive_first_run_browsertest.cc b/chrome/browser/chromeos/first_run/drive_first_run_browsertest.cc
index 2fa07a8..7f1d08a 100644
--- a/chrome/browser/chromeos/first_run/drive_first_run_browsertest.cc
+++ b/chrome/browser/chromeos/first_run/drive_first_run_browsertest.cc
@@ -10,14 +10,11 @@
 #include "base/path_service.h"
 #include "base/strings/string_number_conversions.h"
 #include "chrome/browser/chromeos/first_run/drive_first_run_controller.h"
-#include "chrome/browser/extensions/chrome_extension_test_notification_observer.h"
-#include "chrome/browser/extensions/crx_installer.h"
-#include "chrome/browser/extensions/extension_service.h"
+#include "chrome/browser/extensions/chrome_test_extension_loader.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/common/chrome_paths.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "content/public/test/test_utils.h"
-#include "extensions/browser/extension_system.h"
 #include "net/dns/mock_host_resolver.h"
 #include "net/http/http_status_code.h"
 #include "net/test/embedded_test_server/embedded_test_server.h"
@@ -123,16 +120,9 @@
 }
 
 void DriveFirstRunTest::InstallApp() {
-  ExtensionService* extension_service = extensions::ExtensionSystem::Get(
-      browser()->profile())->extension_service();
-  scoped_refptr<extensions::CrxInstaller> installer =
-      extensions::CrxInstaller::CreateSilent(extension_service);
-
-  installer->InstallCrx(test_data_dir_.AppendASCII(kTestAppCrxName));
-  extensions::ChromeExtensionTestNotificationObserver observer(browser());
-  observer.WaitForExtensionLoad();
-
-  ASSERT_TRUE(extension_service->GetExtensionById(kTestAppId, false));
+  extensions::ChromeTestExtensionLoader loader(browser()->profile());
+  ASSERT_TRUE(
+      loader.LoadExtension(test_data_dir_.AppendASCII(kTestAppCrxName)));
 }
 
 void DriveFirstRunTest::EnableOfflineMode() {
diff --git a/chrome/browser/chromeos/login/screens/device_disabled_screen.cc b/chrome/browser/chromeos/login/screens/device_disabled_screen.cc
index 66d644c..d8d8c25 100644
--- a/chrome/browser/chromeos/login/screens/device_disabled_screen.cc
+++ b/chrome/browser/chromeos/login/screens/device_disabled_screen.cc
@@ -21,9 +21,7 @@
       device_disabling_manager_(
           g_browser_process->platform_part()->device_disabling_manager()),
       showing_(false) {
-  DCHECK(view_);
-  if (view_)
-    view_->SetDelegate(this);
+  view_->SetDelegate(this);
   device_disabling_manager_->AddObserver(this);
 }
 
diff --git a/chrome/browser/chromeos/login/session/user_session_manager.cc b/chrome/browser/chromeos/login/session/user_session_manager.cc
index 1d6b4e5..1c9cc4e1 100644
--- a/chrome/browser/chromeos/login/session/user_session_manager.cc
+++ b/chrome/browser/chromeos/login/session/user_session_manager.cc
@@ -513,7 +513,10 @@
                                           user_context.GetDeviceId());
   }
 
-  PrepareProfile();
+  arc::UpdateArcFileSystemCompatibilityPrefIfNeeded(
+      user_context_.GetAccountId(),
+      ProfileHelper::GetProfilePathByUserIdHash(user_context_.GetUserIDHash()),
+      base::Bind(&UserSessionManager::PrepareProfile, AsWeakPtr()));
 }
 
 void UserSessionManager::DelegateDeleted(UserSessionManagerDelegate* delegate) {
@@ -984,23 +987,6 @@
 }
 
 void UserSessionManager::PrepareProfile() {
-  const base::FilePath profile_path =
-      ProfileHelper::GetProfilePathByUserIdHash(user_context_.GetUserIDHash());
-
-  base::PostTaskWithTraitsAndReplyWithResult(
-      FROM_HERE,
-      base::TaskTraits()
-          .WithShutdownBehavior(
-              base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN)
-          .WithPriority(base::TaskPriority::USER_BLOCKING)
-          .MayBlock(),
-      base::Bind(&arc::IsArcCompatibleFilesystem, profile_path),
-      base::Bind(&UserSessionManager::PrepareProfileAfterFilesystemCheck,
-                 AsWeakPtr()));
-}
-
-void UserSessionManager::PrepareProfileAfterFilesystemCheck(
-    bool arc_compatible_filesystem) {
   const bool is_demo_session =
       DemoAppLauncher::IsDemoAppSession(user_context_.GetAccountId());
 
@@ -1009,13 +995,12 @@
   g_browser_process->profile_manager()->CreateProfileAsync(
       ProfileHelper::GetProfilePathByUserIdHash(user_context_.GetUserIDHash()),
       base::Bind(&UserSessionManager::OnProfileCreated, AsWeakPtr(),
-                 user_context_, is_demo_session, arc_compatible_filesystem),
+                 user_context_, is_demo_session),
       base::string16(), std::string(), std::string());
 }
 
 void UserSessionManager::OnProfileCreated(const UserContext& user_context,
                                           bool is_incognito_profile,
-                                          bool arc_compatible_filesystem,
                                           Profile* profile,
                                           Profile::CreateStatus status) {
   CHECK(profile);
@@ -1023,7 +1008,7 @@
   switch (status) {
     case Profile::CREATE_STATUS_CREATED:
       // Profile created but before initializing extensions and promo resources.
-      InitProfilePreferences(profile, user_context, arc_compatible_filesystem);
+      InitProfilePreferences(profile, user_context);
       break;
     case Profile::CREATE_STATUS_INITIALIZED:
       // Profile is created, extensions and promo resources are initialized.
@@ -1043,11 +1028,7 @@
 
 void UserSessionManager::InitProfilePreferences(
     Profile* profile,
-    const UserContext& user_context,
-    bool arc_compatible_filesystem) {
-  profile->GetPrefs()->SetBoolean(prefs::kArcCompatibleFilesystemChosen,
-                                  arc_compatible_filesystem);
-
+    const UserContext& user_context) {
   const user_manager::User* user =
       ProfileHelper::Get()->GetUserByProfile(profile);
   if (user->GetType() == user_manager::USER_TYPE_KIOSK_APP &&
diff --git a/chrome/browser/chromeos/login/session/user_session_manager.h b/chrome/browser/chromeos/login/session/user_session_manager.h
index d83f33c3..3a853e3 100644
--- a/chrome/browser/chromeos/login/session/user_session_manager.h
+++ b/chrome/browser/chromeos/login/session/user_session_manager.h
@@ -289,12 +289,10 @@
 
   void StartCrosSession();
   void PrepareProfile();
-  void PrepareProfileAfterFilesystemCheck(bool arc_compatible_filesystem);
 
   // Callback for asynchronous profile creation.
   void OnProfileCreated(const UserContext& user_context,
                         bool is_incognito_profile,
-                        bool arc_compatible_filesystem,
                         Profile* profile,
                         Profile::CreateStatus status);
 
@@ -303,8 +301,7 @@
   // early profile initialization that needs to happen before
   // ProfileManager::DoFinalInit() gets called is done here.
   void InitProfilePreferences(Profile* profile,
-                              const UserContext& user_context,
-                              bool arc_compatible_filesystem);
+                              const UserContext& user_context);
 
   // Callback for Profile::CREATE_STATUS_INITIALIZED profile state.
   // Profile is created, extensions and promo resources are initialized.
diff --git a/chrome/browser/chromeos/policy/device_cloud_policy_browsertest.cc b/chrome/browser/chromeos/policy/device_cloud_policy_browsertest.cc
index e73c3564..118d92ea 100644
--- a/chrome/browser/chromeos/policy/device_cloud_policy_browsertest.cc
+++ b/chrome/browser/chromeos/policy/device_cloud_policy_browsertest.cc
@@ -27,8 +27,8 @@
 #include "chrome/browser/chromeos/policy/proto/chrome_device_policy.pb.h"
 #include "chrome/browser/chromeos/profiles/profile_helper.h"
 #include "chrome/browser/chromeos/settings/device_settings_service.h"
+#include "chrome/browser/extensions/chrome_test_extension_loader.h"
 #include "chrome/browser/extensions/extension_service.h"
-#include "chrome/browser/extensions/unpacked_installer.h"
 #include "chrome/browser/policy/test/local_policy_test_server.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser.h"
@@ -58,7 +58,6 @@
 #include "extensions/browser/extension_system.h"
 #include "extensions/common/constants.h"
 #include "extensions/common/extension.h"
-#include "extensions/test/extension_test_notification_observer.h"
 #include "extensions/test/result_catcher.h"
 #include "net/http/http_status_code.h"
 #include "net/url_request/test_url_fetcher_factory.h"
@@ -385,16 +384,10 @@
   SigninExtensionsDeviceCloudPolicyBrowserTest()
       : fetcher_factory_(&fetcher_impl_factory_) {}
 
-  const extensions::Extension* InstallAndLoadTestExtension() const {
-    Profile* signin_profile = GetSigninProfile();
-    ExtensionService* service =
-        extensions::ExtensionSystem::Get(signin_profile)->extension_service();
-    scoped_refptr<extensions::UnpackedInstaller> installer(
-        extensions::UnpackedInstaller::Create(service));
-    extensions::ExtensionTestNotificationObserver observer(signin_profile);
-    installer->Load(GetTestExtensionSourcePath());
-    observer.WaitForExtensionLoad();
-    return GetTestExtension();
+  scoped_refptr<const extensions::Extension> InstallAndLoadTestExtension()
+      const {
+    extensions::ChromeTestExtensionLoader loader(GetSigninProfile());
+    return loader.LoadExtension(GetTestExtensionSourcePath());
   }
 
  private:
@@ -462,7 +455,8 @@
 
 IN_PROC_BROWSER_TEST_F(SigninExtensionsDeviceCloudPolicyBrowserTest,
                        InstallAndRunInWindow) {
-  const extensions::Extension* extension = InstallAndLoadTestExtension();
+  scoped_refptr<const extensions::Extension> extension =
+      InstallAndLoadTestExtension();
   ASSERT_TRUE(extension);
   Browser* browser = CreateBrowser(GetSigninProfile());
   extensions::ResultCatcher result_catcher;
diff --git a/chrome/browser/chromeos/system/device_disabling_manager.cc b/chrome/browser/chromeos/system/device_disabling_manager.cc
index dce9e67..e3b335c 100644
--- a/chrome/browser/chromeos/system/device_disabling_manager.cc
+++ b/chrome/browser/chromeos/system/device_disabling_manager.cc
@@ -43,7 +43,6 @@
       device_disabled_(false),
       weak_factory_(this) {
   CHECK(delegate_);
-  Init();
 }
 
 DeviceDisablingManager::~DeviceDisablingManager() {
diff --git a/chrome/browser/chromeos/system/device_disabling_manager.h b/chrome/browser/chromeos/system/device_disabling_manager.h
index c8e7eba..3a7f2f6 100644
--- a/chrome/browser/chromeos/system/device_disabling_manager.h
+++ b/chrome/browser/chromeos/system/device_disabling_manager.h
@@ -85,6 +85,9 @@
                          user_manager::UserManager* user_manager);
   ~DeviceDisablingManager();
 
+  // Must be called after construction.
+  void Init();
+
   void AddObserver(Observer* observer);
   void RemoveObserver(Observer* observer);
 
@@ -107,8 +110,6 @@
   static bool HonorDeviceDisablingDuringNormalOperation();
 
  private:
-  void Init();
-
   // Cache the disabled message and inform observers if it changed.
   void CacheDisabledMessageAndNotify(const std::string& disabled_message);
 
diff --git a/chrome/browser/chromeos/system/device_disabling_manager_unittest.cc b/chrome/browser/chromeos/system/device_disabling_manager_unittest.cc
index 78ecf89..f224bb3 100644
--- a/chrome/browser/chromeos/system/device_disabling_manager_unittest.cc
+++ b/chrome/browser/chromeos/system/device_disabling_manager_unittest.cc
@@ -95,6 +95,7 @@
       this,
       CrosSettings::Get(),
       &fake_user_manager_));
+  device_disabling_manager_->Init();
 }
 
 void DeviceDisablingManagerTestBase::DestroyDeviceDisablingManager() {
diff --git a/chrome/browser/extensions/chrome_extension_test_notification_observer.cc b/chrome/browser/extensions/chrome_extension_test_notification_observer.cc
index 3ca518da..7a34f448 100644
--- a/chrome/browser/extensions/chrome_extension_test_notification_observer.cc
+++ b/chrome/browser/extensions/chrome_extension_test_notification_observer.cc
@@ -115,11 +115,6 @@
   return true;
 }
 
-void ChromeExtensionTestNotificationObserver::WaitForExtensionAndViewLoad() {
-  WaitForExtensionLoad();
-  WaitForExtensionViewsToLoad();
-}
-
 void ChromeExtensionTestNotificationObserver::OnPageActionsUpdated(
     content::WebContents* web_contents) {
   MaybeQuit();
diff --git a/chrome/browser/extensions/chrome_extension_test_notification_observer.h b/chrome/browser/extensions/chrome_extension_test_notification_observer.h
index 3b71517..df4995b 100644
--- a/chrome/browser/extensions/chrome_extension_test_notification_observer.h
+++ b/chrome/browser/extensions/chrome_extension_test_notification_observer.h
@@ -32,9 +32,6 @@
   // Waits for the number of visible page actions to change to |count|.
   bool WaitForPageActionVisibilityChangeTo(int count);
 
-  // Waits until an extension is loaded and all view have loaded.
-  void WaitForExtensionAndViewLoad();
-
   // Waits for all extension views to load.
   bool WaitForExtensionViewsToLoad();
 
diff --git a/chrome/browser/extensions/extension_browsertest.h b/chrome/browser/extensions/extension_browsertest.h
index ca87820..cb356d68 100644
--- a/chrome/browser/extensions/extension_browsertest.h
+++ b/chrome/browser/extensions/extension_browsertest.h
@@ -241,16 +241,6 @@
     return observer_->WaitForExtensionInstallError();
   }
 
-  // Waits until an extension is loaded and all view have loaded.
-  void WaitForExtensionAndViewLoad() {
-    return observer_->WaitForExtensionAndViewLoad();
-  }
-
-  // Waits until an extension is loaded.
-  void WaitForExtensionLoad() {
-    return observer_->WaitForExtensionLoad();
-  }
-
   // Waits for an extension load error. Returns true if the error really
   // happened.
   bool WaitForExtensionLoadError() {
diff --git a/chrome/browser/extensions/extension_crash_recovery_browsertest.cc b/chrome/browser/extensions/extension_crash_recovery_browsertest.cc
index 227951c0..e9aedef 100644
--- a/chrome/browser/extensions/extension_crash_recovery_browsertest.cc
+++ b/chrome/browser/extensions/extension_crash_recovery_browsertest.cc
@@ -29,7 +29,9 @@
 #include "extensions/browser/extension_system.h"
 #include "extensions/browser/process_manager.h"
 #include "extensions/browser/process_map.h"
+#include "extensions/browser/test_extension_registry_observer.h"
 #include "extensions/common/constants.h"
+#include "extensions/test/background_page_watcher.cc"
 #include "ui/message_center/message_center.h"
 #include "ui/message_center/notification_list.h"
 
@@ -143,8 +145,11 @@
     for (size_t i = 0; i < index; ++i)
       ++it;
     std::string id = (*it)->id();
+    extensions::TestExtensionRegistryObserver observer(GetExtensionRegistry());
     message_center->ClickOnNotification(id);
-    WaitForExtensionLoad();
+    auto* extension = observer.WaitForExtensionLoaded();
+    extensions::BackgroundPageWatcher(GetProcessManager(), extension)
+        .WaitForOpen();
   }
 
   void CancelNotification(size_t index) override {
diff --git a/chrome/browser/search_engines/template_url_service_unittest.cc b/chrome/browser/search_engines/template_url_service_unittest.cc
index c3b8de6..9ec307f 100644
--- a/chrome/browser/search_engines/template_url_service_unittest.cc
+++ b/chrome/browser/search_engines/template_url_service_unittest.cc
@@ -20,6 +20,7 @@
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/task/cancelable_task_tracker.h"
+#include "base/test/histogram_tester.h"
 #include "base/test/simple_test_clock.h"
 #include "base/threading/thread.h"
 #include "base/time/time.h"
@@ -1944,3 +1945,22 @@
   EXPECT_NE(original_last_modified, reloaded_last_modified);
   EXPECT_EQ(update_last_modified, reloaded_last_modified);
 }
+
+// Tests checks that Search.DefaultSearchChangeOrigin histogram is correctly
+// emitted when TemplateURLService is not yet loaded.
+TEST_F(TemplateURLServiceTest, ChangeDefaultEngineBeforeLoad) {
+  TemplateURL* search_engine1 = model()->Add(
+      base::MakeUnique<TemplateURL>(*GenerateDummyTemplateURLData("keyword1")));
+  DCHECK(search_engine1);
+  TemplateURL* search_engine2 = model()->Add(
+      base::MakeUnique<TemplateURL>(*GenerateDummyTemplateURLData("keyword2")));
+  DCHECK(search_engine2);
+
+  base::HistogramTester histogram_tester;
+  model()->SetUserSelectedDefaultSearchProvider(search_engine1);
+  histogram_tester.ExpectTotalCount("Search.DefaultSearchChangeOrigin", 1);
+  model()->SetUserSelectedDefaultSearchProvider(search_engine1);
+  histogram_tester.ExpectTotalCount("Search.DefaultSearchChangeOrigin", 1);
+  model()->SetUserSelectedDefaultSearchProvider(search_engine2);
+  histogram_tester.ExpectTotalCount("Search.DefaultSearchChangeOrigin", 2);
+}
diff --git a/chrome/browser/ui/cocoa/browser_window_controller.h b/chrome/browser/ui/cocoa/browser_window_controller.h
index cdbe2d1..d98a2b9 100644
--- a/chrome/browser/ui/cocoa/browser_window_controller.h
+++ b/chrome/browser/ui/cocoa/browser_window_controller.h
@@ -65,6 +65,10 @@
 class Command;
 }
 
+namespace {
+class OmniboxPopupModelObserverBridge;
+}
+
 constexpr const gfx::Size kMinCocoaTabbedWindowSize(400, 272);
 constexpr const gfx::Size kMinCocoaPopupWindowSize(100, 122);
 
@@ -197,6 +201,10 @@
   // handle.
   std::unique_ptr<ExtensionKeybindingRegistryCocoa>
       extensionKeybindingRegistry_;
+
+  // Observes whether the omnibox popup is shown or hidden.
+  std::unique_ptr<OmniboxPopupModelObserverBridge>
+      omniboxPopupModelObserverBridge_;
 }
 
 // A convenience class method which gets the |BrowserWindowController| for a
diff --git a/chrome/browser/ui/cocoa/browser_window_controller.mm b/chrome/browser/ui/cocoa/browser_window_controller.mm
index 2fd4f20f..13ce7a8 100644
--- a/chrome/browser/ui/cocoa/browser_window_controller.mm
+++ b/chrome/browser/ui/cocoa/browser_window_controller.mm
@@ -13,6 +13,7 @@
 #import "base/mac/foundation_util.h"
 #include "base/mac/mac_util.h"
 #import "base/mac/sdk_forward_declarations.h"
+#include "base/scoped_observer.h"
 #include "base/strings/sys_string_conversions.h"
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/app/chrome_command_ids.h"  // IDC_*
@@ -64,6 +65,7 @@
 #import "chrome/browser/ui/cocoa/infobars/infobar_container_controller.h"
 #include "chrome/browser/ui/cocoa/l10n_util.h"
 #import "chrome/browser/ui/cocoa/location_bar/autocomplete_text_field_editor.h"
+#import "chrome/browser/ui/cocoa/location_bar/location_bar_view_mac.h"
 #include "chrome/browser/ui/cocoa/permission_bubble/permission_bubble_cocoa.h"
 #import "chrome/browser/ui/cocoa/profiles/avatar_base_controller.h"
 #import "chrome/browser/ui/cocoa/profiles/avatar_button_controller.h"
@@ -87,6 +89,9 @@
 #include "chrome/common/pref_names.h"
 #include "components/bookmarks/browser/bookmark_model.h"
 #include "components/bookmarks/managed/managed_bookmark_service.h"
+#include "components/omnibox/browser/omnibox_edit_model.h"
+#include "components/omnibox/browser/omnibox_popup_model.h"
+#include "components/omnibox/browser/omnibox_popup_model_observer.h"
 #include "components/signin/core/common/profile_management_switches.h"
 #include "components/translate/core/browser/translate_manager.h"
 #include "components/translate/core/browser/translate_ui_delegate.h"
@@ -185,6 +190,43 @@
 
 namespace {
 
+// This class shows or hides the top arrow of the infobar in accordance with the
+// visibility of the omnibox popup. It hides the top arrow when the omnibox
+// popup is shown, and vice versa.
+class OmniboxPopupModelObserverBridge final : public OmniboxPopupModelObserver {
+ public:
+  explicit OmniboxPopupModelObserverBridge(BrowserWindowController* controller)
+      : controller_(controller),
+        omnibox_popup_model_([controller_ locationBarBridge]
+                                 ->GetOmniboxView()
+                                 ->model()
+                                 ->popup_model()),
+        omnibox_popup_model_observer_(this) {
+    DCHECK(omnibox_popup_model_);
+    omnibox_popup_model_observer_.Add(omnibox_popup_model_);
+  }
+
+  void OnOmniboxPopupShownOrHidden() override {
+    int max_top_arrow_height = 0;
+    if (!omnibox_popup_model_->IsOpen()) {
+      base::scoped_nsobject<BrowserWindowLayout> layout(
+          [[BrowserWindowLayout alloc] init]);
+      [controller_ updateLayoutParameters:layout];
+      max_top_arrow_height = [layout computeLayout].infoBarMaxTopArrowHeight;
+    }
+    [[controller_ infoBarContainerController]
+        setMaxTopArrowHeight:max_top_arrow_height];
+  }
+
+ private:
+  BrowserWindowController* controller_;
+  OmniboxPopupModel* omnibox_popup_model_;
+  ScopedObserver<OmniboxPopupModel, OmniboxPopupModelObserver>
+      omnibox_popup_model_observer_;
+
+  DISALLOW_COPY_AND_ASSIGN(OmniboxPopupModelObserverBridge);
+};
+
 void SetUpBrowserWindowCommandHandler(NSWindow* window) {
   // Make the window handle browser window commands.
   [base::mac::ObjCCastStrict<ChromeEventProcessingWindow>(window)
@@ -389,6 +431,9 @@
             extensions::ExtensionKeybindingRegistry::ALL_EXTENSIONS,
             windowShim_.get()));
 
+    omniboxPopupModelObserverBridge_.reset(
+        new OmniboxPopupModelObserverBridge(self));
+
     blockLayoutSubviews_ = NO;
 
     // We are done initializing now.
diff --git a/components/search_engines/template_url_service.cc b/components/search_engines/template_url_service.cc
index a4db5e1..f4e7a85 100644
--- a/components/search_engines/template_url_service.cc
+++ b/components/search_engines/template_url_service.cc
@@ -1921,7 +1921,7 @@
     // mainly so we can hold ownership until we get to the point where the list
     // of keywords from Web Data is the owner of everything including the
     // default.
-    bool changed = TemplateURL::MatchesData(
+    bool changed = !TemplateURL::MatchesData(
         initial_default_search_provider_.get(), data, search_terms_data());
     TemplateURL::Type initial_engine_type =
         (source == DefaultSearchManager::FROM_EXTENSION)
diff --git a/content/browser/media/media_internals.cc b/content/browser/media/media_internals.cc
index a43f919..29d14fdf 100644
--- a/content/browser/media/media_internals.cc
+++ b/content/browser/media/media_internals.cc
@@ -7,6 +7,7 @@
 #include <stddef.h>
 
 #include <memory>
+#include <tuple>
 #include <utility>
 
 #include "base/containers/flat_map.h"
@@ -18,6 +19,7 @@
 #include "base/strings/stringprintf.h"
 #include "build/build_config.h"
 #include "content/browser/renderer_host/media/media_stream_manager.h"
+#include "content/public/browser/browser_context.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/notification_service.h"
 #include "content/public/browser/notification_types.h"
@@ -92,6 +94,23 @@
   return "unknown";
 }
 
+// Whether the player is in incognito mode or ChromeOS guest mode.
+bool IsIncognito(int render_process_id) {
+  content::RenderProcessHost* render_process_host =
+      content::RenderProcessHost::FromID(render_process_id);
+  if (!render_process_host) {
+    // This could happen in tests.
+    LOG(ERROR) << "Cannot get RenderProcessHost";
+    return false;
+  }
+
+  content::BrowserContext* browser_context =
+      render_process_host->GetBrowserContext();
+  DCHECK(browser_context);
+
+  return browser_context->IsOffTheRecord();
+}
+
 const char kAudioLogStatusKey[] = "status";
 const char kAudioLogUpdateFunction[] = "media.updateAudioComponent";
 
@@ -294,6 +313,8 @@
  private:
   using WatchTimeInfo = base::flat_map<base::StringPiece, base::TimeDelta>;
   struct PipelineInfo {
+    explicit PipelineInfo(bool is_incognito) : is_incognito(is_incognito) {}
+
     bool has_pipeline = false;
     bool has_ever_played = false;
     bool has_reached_have_enough = false;
@@ -302,6 +323,8 @@
     bool has_video = false;
     bool video_dds = false;
     bool video_decoder_changed = false;
+    bool has_cdm = false;
+    bool is_incognito = false;
     std::string audio_codec_name;
     std::string video_codec_name;
     std::string video_decoder;
@@ -366,62 +389,76 @@
     int render_process_id,
     const media::MediaLogEvent& event) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  PlayerInfoMap& player_info = renderer_info_[render_process_id];
+
+  PlayerInfoMap& player_info_map = renderer_info_[render_process_id];
+
+  auto it = player_info_map.find(event.id);
+  if (it == player_info_map.end()) {
+    bool success = false;
+    std::tie(it, success) = player_info_map.emplace(
+        std::make_pair(event.id, PipelineInfo(IsIncognito(render_process_id))));
+    if (!success) {
+      LOG(ERROR) << "Failed to insert a new PipelineInfo.";
+      return;
+    }
+  }
+
+  PipelineInfo& player_info = it->second;
+
   switch (event.type) {
     case media::MediaLogEvent::PLAY: {
-      player_info[event.id].has_ever_played = true;
+      player_info.has_ever_played = true;
       break;
     }
     case media::MediaLogEvent::PIPELINE_STATE_CHANGED: {
-      player_info[event.id].has_pipeline = true;
+      player_info.has_pipeline = true;
       break;
     }
     case media::MediaLogEvent::PIPELINE_ERROR: {
       int status;
       event.params.GetInteger("pipeline_error", &status);
-      player_info[event.id].last_pipeline_status =
+      player_info.last_pipeline_status =
           static_cast<media::PipelineStatus>(status);
       break;
     }
     case media::MediaLogEvent::PROPERTY_CHANGE:
       if (event.params.HasKey("found_audio_stream")) {
-        event.params.GetBoolean("found_audio_stream",
-                                &player_info[event.id].has_audio);
+        event.params.GetBoolean("found_audio_stream", &player_info.has_audio);
       }
       if (event.params.HasKey("found_video_stream")) {
-        event.params.GetBoolean("found_video_stream",
-                                &player_info[event.id].has_video);
+        event.params.GetBoolean("found_video_stream", &player_info.has_video);
       }
       if (event.params.HasKey("audio_codec_name")) {
         event.params.GetString("audio_codec_name",
-                               &player_info[event.id].audio_codec_name);
+                               &player_info.audio_codec_name);
       }
       if (event.params.HasKey("video_codec_name")) {
         event.params.GetString("video_codec_name",
-                               &player_info[event.id].video_codec_name);
+                               &player_info.video_codec_name);
       }
       if (event.params.HasKey("video_decoder")) {
-        std::string previous_video_decoder(player_info[event.id].video_decoder);
-        event.params.GetString("video_decoder",
-                               &player_info[event.id].video_decoder);
+        std::string previous_video_decoder(player_info.video_decoder);
+        event.params.GetString("video_decoder", &player_info.video_decoder);
         if (!previous_video_decoder.empty() &&
-            previous_video_decoder != player_info[event.id].video_decoder) {
-          player_info[event.id].video_decoder_changed = true;
+            previous_video_decoder != player_info.video_decoder) {
+          player_info.video_decoder_changed = true;
         }
       }
       if (event.params.HasKey("video_dds")) {
-        event.params.GetBoolean("video_dds", &player_info[event.id].video_dds);
+        event.params.GetBoolean("video_dds", &player_info.video_dds);
+      }
+      if (event.params.HasKey("has_cdm")) {
+        event.params.GetBoolean("has_cdm", &player_info.has_cdm);
       }
       if (event.params.HasKey("pipeline_buffering_state")) {
         std::string buffering_state;
         event.params.GetString("pipeline_buffering_state", &buffering_state);
         if (buffering_state == "BUFFERING_HAVE_ENOUGH")
-          player_info[event.id].has_reached_have_enough = true;
+          player_info.has_reached_have_enough = true;
       }
       break;
     case media::MediaLogEvent::Type::WATCH_TIME_UPDATE: {
       DVLOG(2) << "Processing watch time update.";
-      PipelineInfo& info = player_info[event.id];
 
       for (base::DictionaryValue::Iterator it(event.params); !it.IsAtEnd();
            it.Advance()) {
@@ -432,7 +469,7 @@
         auto key_name = watch_time_keys_.find(it.key());
         if (key_name == watch_time_keys_.end())
           continue;
-        info.watch_time_info[*key_name] =
+        player_info.watch_time_info[*key_name] =
             base::TimeDelta::FromSecondsD(it.value().GetDouble());
       }
 
@@ -441,7 +478,7 @@
         DCHECK(event.params.GetBoolean(media::MediaLog::kWatchTimeFinalize,
                                        &should_finalize) &&
                should_finalize);
-        FinalizeWatchTime(info.has_video, &info.watch_time_info,
+        FinalizeWatchTime(player_info.has_video, &player_info.watch_time_info,
                           FinalizeType::EVERYTHING);
       } else if (event.params.HasKey(
                      media::MediaLog::kWatchTimeFinalizePower)) {
@@ -449,7 +486,7 @@
         DCHECK(event.params.GetBoolean(media::MediaLog::kWatchTimeFinalizePower,
                                        &should_finalize) &&
                should_finalize);
-        FinalizeWatchTime(info.has_video, &info.watch_time_info,
+        FinalizeWatchTime(player_info.has_video, &player_info.watch_time_info,
                           FinalizeType::POWER_ONLY);
       }
       break;
@@ -457,14 +494,14 @@
     case media::MediaLogEvent::Type::WEBMEDIAPLAYER_DESTROYED: {
       // Upon player destruction report UMA data; if the player is not torn down
       // before process exit, it will be logged during OnProcessTerminated().
-      auto it = player_info.find(event.id);
-      if (it == player_info.end())
+      auto it = player_info_map.find(event.id);
+      if (it == player_info_map.end())
         break;
 
       ReportUMAForPipelineStatus(it->second);
       FinalizeWatchTime(it->second.has_video, &(it->second.watch_time_info),
                         FinalizeType::EVERYTHING);
-      player_info.erase(it);
+      player_info_map.erase(it);
     }
     default:
       break;
@@ -506,6 +543,8 @@
   return uma_name;
 }
 
+// TODO(xhwang): This function reports more metrics than just pipeline status
+// and should be renamed. Similarly, PipelineInfo should be PlayerInfo.
 void MediaInternals::MediaInternalsUMAHandler::ReportUMAForPipelineStatus(
     const PipelineInfo& player_info) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
@@ -548,6 +587,11 @@
   // effectiveness of efforts to reduce loaded-but-never-used players.
   if (player_info.has_reached_have_enough)
     UMA_HISTOGRAM_BOOLEAN("Media.HasEverPlayed", player_info.has_ever_played);
+
+  // Report whether an encrypted playback is in incognito window, excluding
+  // never-used players.
+  if (player_info.has_cdm && player_info.has_ever_played)
+    UMA_HISTOGRAM_BOOLEAN("Media.EME.IsIncognito", player_info.is_incognito);
 }
 
 void MediaInternals::MediaInternalsUMAHandler::OnProcessTerminated(
diff --git a/content/public/android/javatests/src/org/chromium/content/browser/input/ImeTest.java b/content/public/android/javatests/src/org/chromium/content/browser/input/ImeTest.java
index 61b64b0..67ad4dc2e 100644
--- a/content/public/android/javatests/src/org/chromium/content/browser/input/ImeTest.java
+++ b/content/public/android/javatests/src/org/chromium/content/browser/input/ImeTest.java
@@ -785,7 +785,7 @@
         DOMUtils.longPressNode(mContentViewCore, "input_text");
         assertWaitForKeyboardStatus(true);
         assertWaitForSelectActionBarStatus(true);
-        DOMUtils.clickNode(mContentViewCore, "input_radio");
+        DOMUtils.clickNode(mContentViewCore, "plain_text");
         assertWaitForKeyboardStatus(false);
         assertWaitForSelectActionBarStatus(false);
     }
@@ -1317,7 +1317,7 @@
     @Feature({"TextInput"})
     public void testContentEditableEvents_ComposingText() throws Throwable {
         focusElementAndWaitForStateUpdate("contenteditable_event");
-        waitForEventLogs("selectionchange,selectionchange");
+        waitForEventLogs("selectionchange");
         clearEventLogs();
 
         setComposingText("a", 1);
@@ -1352,7 +1352,7 @@
     @Feature({"TextInput"})
     public void testContentEditableEvents_CommitText() throws Throwable {
         focusElementAndWaitForStateUpdate("contenteditable_event");
-        waitForEventLogs("selectionchange,selectionchange");
+        waitForEventLogs("selectionchange");
         clearEventLogs();
 
         commitText("a", 1);
@@ -1374,7 +1374,7 @@
     @Feature({"TextInput"})
     public void testContentEditableEvents_DeleteSurroundingText() throws Throwable {
         focusElementAndWaitForStateUpdate("contenteditable_event");
-        waitForEventLogs("selectionchange,selectionchange");
+        waitForEventLogs("selectionchange");
         clearEventLogs();
 
         commitText("hello", 1);
@@ -1416,7 +1416,7 @@
     @Feature({"TextInput"})
     public void testContentEditableEvents_DeleteSurroundingTextInCodePoints() throws Throwable {
         focusElementAndWaitForStateUpdate("contenteditable_event");
-        waitForEventLogs("selectionchange,selectionchange");
+        waitForEventLogs("selectionchange");
         clearEventLogs();
 
         commitText("hello", 1);
diff --git a/content/test/data/android/input/input_forms.html b/content/test/data/android/input/input_forms.html
index c5a3bad..55b0dadf 100644
--- a/content/test/data/android/input/input_forms.html
+++ b/content/test/data/android/input/input_forms.html
@@ -17,6 +17,7 @@
       <br/>
       <input id="input_number1" type="number" /><br/>
       <input id="input_number2" type="number" /><br/>
+      <br/>
       <span id="plain_text">This is Plain Text One</span>
     </form>
 
diff --git a/extensions/test/extension_test_notification_observer.cc b/extensions/test/extension_test_notification_observer.cc
index 7d6db807..3d2f23f 100644
--- a/extensions/test/extension_test_notification_observer.cc
+++ b/extensions/test/extension_test_notification_observer.cc
@@ -74,7 +74,11 @@
     : context_(context),
       extension_installs_observed_(0),
       extension_load_errors_observed_(0),
-      crx_installers_done_observed_(0) {}
+      crx_installers_done_observed_(0),
+      registry_observer_(this) {
+  if (context_)
+    registry_observer_.Add(ExtensionRegistry::Get(context_));
+}
 
 ExtensionTestNotificationObserver::~ExtensionTestNotificationObserver() {}
 
@@ -100,10 +104,6 @@
   return extension_installs_observed_ == before;
 }
 
-void ExtensionTestNotificationObserver::WaitForExtensionLoad() {
-  WaitForNotification(NOTIFICATION_EXTENSION_LOADED_DEPRECATED);
-}
-
 bool ExtensionTestNotificationObserver::WaitForExtensionLoadError() {
   int before = extension_load_errors_observed_;
   WaitForNotification(NOTIFICATION_EXTENSION_LOAD_ERROR);
@@ -151,12 +151,6 @@
     const content::NotificationSource& source,
     const content::NotificationDetails& details) {
   switch (type) {
-    case NOTIFICATION_EXTENSION_LOADED_DEPRECATED:
-      last_loaded_extension_id_ =
-          content::Details<const Extension>(details).ptr()->id();
-      VLOG(1) << "Got EXTENSION_LOADED notification.";
-      break;
-
     case NOTIFICATION_CRX_INSTALLER_DONE:
       VLOG(1) << "Got CRX_INSTALLER_DONE notification.";
       {
@@ -181,6 +175,18 @@
   }
 }
 
+void ExtensionTestNotificationObserver::OnExtensionLoaded(
+    content::BrowserContext* browser_context,
+    const Extension* extension) {
+  last_loaded_extension_id_ = extension->id();
+  VLOG(1) << "Got EXTENSION_LOADED notification.";
+}
+
+void ExtensionTestNotificationObserver::OnShutdown(
+    ExtensionRegistry* registry) {
+  registry_observer_.RemoveAll();
+}
+
 void ExtensionTestNotificationObserver::WaitForCondition(
     const ConditionCallback& condition,
     NotificationSet* notification_set) {
diff --git a/extensions/test/extension_test_notification_observer.h b/extensions/test/extension_test_notification_observer.h
index 418d567a..fe2aa8e 100644
--- a/extensions/test/extension_test_notification_observer.h
+++ b/extensions/test/extension_test_notification_observer.h
@@ -14,6 +14,7 @@
 #include "base/scoped_observer.h"
 #include "content/public/browser/notification_observer.h"
 #include "content/public/browser/notification_registrar.h"
+#include "extensions/browser/extension_registry_observer.h"
 #include "extensions/browser/process_manager_observer.h"
 
 namespace content {
@@ -23,10 +24,12 @@
 }
 
 namespace extensions {
+class ExtensionRegistry;
 class ProcessManager;
 
 // Test helper class for observing extension-related events.
-class ExtensionTestNotificationObserver : public content::NotificationObserver {
+class ExtensionTestNotificationObserver : public content::NotificationObserver,
+                                          ExtensionRegistryObserver {
  public:
   explicit ExtensionTestNotificationObserver(content::BrowserContext* context);
   ~ExtensionTestNotificationObserver() override;
@@ -35,9 +38,6 @@
   // error was raised.
   bool WaitForExtensionInstallError();
 
-  // Waits until an extension is loaded.
-  void WaitForExtensionLoad();
-
   // Waits for an extension load error. Returns true if the error really
   // happened.
   bool WaitForExtensionLoadError();
@@ -67,11 +67,16 @@
     last_loaded_extension_id_ = last_loaded_extension_id;
   }
 
-  // content::NotificationObserver
+  // content::NotificationObserver:
   void Observe(int type,
                const content::NotificationSource& source,
                const content::NotificationDetails& details) override;
 
+  // ExtensionRegistryObserver:
+  void OnExtensionLoaded(content::BrowserContext* browser_context,
+                         const Extension* extension) override;
+  void OnShutdown(ExtensionRegistry* registry) override;
+
  protected:
   class NotificationSet : public content::NotificationObserver,
                           public extensions::ProcessManagerObserver {
@@ -136,6 +141,10 @@
   // The closure to quit the currently-running message loop.
   base::Closure quit_closure_;
 
+  // Listens to extension loaded notifications.
+  ScopedObserver<ExtensionRegistry, ExtensionRegistryObserver>
+      registry_observer_;
+
   DISALLOW_COPY_AND_ASSIGN(ExtensionTestNotificationObserver);
 };
 
diff --git a/ios/chrome/browser/ui/authentication/signin_interaction_controller_egtest.mm b/ios/chrome/browser/ui/authentication/signin_interaction_controller_egtest.mm
index 67df59d..c827e04 100644
--- a/ios/chrome/browser/ui/authentication/signin_interaction_controller_egtest.mm
+++ b/ios/chrome/browser/ui/authentication/signin_interaction_controller_egtest.mm
@@ -312,7 +312,8 @@
   TapButtonWithLabelId(IDS_IOS_DISCONNECT_DIALOG_CONTINUE_BUTTON_MOBILE);
 
   // Check that the settings home screen is shown.
-  WaitForMatcher(grey_accessibilityID(kSettingsSignInCellId));
+  WaitForMatcher(grey_allOf(grey_accessibilityID(kSettingsSignInCellId),
+                            grey_sufficientlyVisible(), nil));
 
   // Close Settings.
   TapButtonWithLabelId(IDS_IOS_NAVIGATION_BAR_DONE_BUTTON);
@@ -357,7 +358,8 @@
   TapButtonWithLabelId(IDS_IOS_MANAGED_DISCONNECT_DIALOG_ACCEPT);
 
   // Check that the settings home screen is shown.
-  WaitForMatcher(grey_accessibilityID(kSettingsSignInCellId));
+  WaitForMatcher(grey_allOf(grey_accessibilityID(kSettingsSignInCellId),
+                            grey_sufficientlyVisible(), nil));
 
   // Close Settings.
   TapButtonWithLabelId(IDS_IOS_NAVIGATION_BAR_DONE_BUTTON);
@@ -500,7 +502,8 @@
   AssertAuthenticatedIdentityInActiveProfile(nil);
 
   // Start sign-in with |identity1|.
-  WaitForMatcher(grey_accessibilityID(kSettingsSignInCellId));
+  WaitForMatcher(grey_allOf(grey_accessibilityID(kSettingsSignInCellId),
+                            grey_sufficientlyVisible(), nil));
   TapViewWithAccessibilityId(kSettingsSignInCellId);
   TapButtonWithAccessibilityLabel(identity1.userEmail);
   TapButtonWithLabelId(IDS_IOS_ACCOUNT_CONSISTENCY_SETUP_SIGNIN_BUTTON);
diff --git a/ios/chrome/browser/ui/history/history_ui_egtest.mm b/ios/chrome/browser/ui/history/history_ui_egtest.mm
index 3f20831..89bc395 100644
--- a/ios/chrome/browser/ui/history/history_ui_egtest.mm
+++ b/ios/chrome/browser/ui/history/history_ui_egtest.mm
@@ -153,7 +153,9 @@
       performAction:grey_tap()];
 
   [[EarlGrey
-      selectElementWithMatcher:grey_accessibilityID(kSettingsSignInCellId)]
+      selectElementWithMatcher:grey_allOf(
+                                   grey_accessibilityID(kSettingsSignInCellId),
+                                   grey_sufficientlyVisible(), nil)]
       performAction:grey_tap()];
   [[EarlGrey
       selectElementWithMatcher:chrome_test_util::ButtonWithAccessibilityLabel(
diff --git a/ios/chrome/browser/ui/settings/accounts_collection_egtest.mm b/ios/chrome/browser/ui/settings/accounts_collection_egtest.mm
index 0d9aec4..31040d3 100644
--- a/ios/chrome/browser/ui/settings/accounts_collection_egtest.mm
+++ b/ios/chrome/browser/ui/settings/accounts_collection_egtest.mm
@@ -126,8 +126,10 @@
       ->ForgetIdentity(identity, nil);
 
   [[EarlGrey
-      selectElementWithMatcher:grey_accessibilityID(kSettingsSignInCellId)]
-      assertWithMatcher:grey_sufficientlyVisible()];
+      selectElementWithMatcher:grey_allOf(
+                                   grey_accessibilityID(kSettingsSignInCellId),
+                                   grey_sufficientlyVisible(), nil)]
+      assertWithMatcher:grey_notNil()];
   AssertAuthenticatedIdentityInActiveProfile(nil);
 
   // Close Settings.
@@ -164,8 +166,10 @@
       ->ForgetIdentity(identity, nil);
 
   [[EarlGrey
-      selectElementWithMatcher:grey_accessibilityID(kSettingsSignInCellId)]
-      assertWithMatcher:grey_sufficientlyVisible()];
+      selectElementWithMatcher:grey_allOf(
+                                   grey_accessibilityID(kSettingsSignInCellId),
+                                   grey_sufficientlyVisible(), nil)]
+      assertWithMatcher:grey_notNil()];
   AssertAuthenticatedIdentityInActiveProfile(nil);
 
   // Close Settings.
@@ -268,8 +272,10 @@
 
   // Check that the user is signed out and the Main Settings screen is shown.
   [[EarlGrey
-      selectElementWithMatcher:grey_accessibilityID(kSettingsSignInCellId)]
-      assertWithMatcher:grey_sufficientlyVisible()];
+      selectElementWithMatcher:grey_allOf(
+                                   grey_accessibilityID(kSettingsSignInCellId),
+                                   grey_sufficientlyVisible(), nil)]
+      assertWithMatcher:grey_notNil()];
   AssertAuthenticatedIdentityInActiveProfile(nil);
 
   // Close Settings.
diff --git a/ios/chrome/browser/ui/settings/settings_collection_view_controller.mm b/ios/chrome/browser/ui/settings/settings_collection_view_controller.mm
index 73c8da6..a582f938 100644
--- a/ios/chrome/browser/ui/settings/settings_collection_view_controller.mm
+++ b/ios/chrome/browser/ui/settings/settings_collection_view_controller.mm
@@ -200,6 +200,9 @@
   // Mediator to configure the sign-in promo cell. Also used to received
   // identity update notifications.
   base::scoped_nsobject<SigninPromoViewMediator> _signinPromoViewMediator;
+  // Item in the Sign-in section. Either CollectionViewAccountItem,
+  // AccountSignInItem or SigninPromoItem.
+  CollectionViewItem* _signinSectionItem;
 
   // Cached resized profile image.
   base::scoped_nsobject<UIImage> _resizedImage;
@@ -311,6 +314,10 @@
 - (void)viewWillAppear:(BOOL)animated {
   [super viewWillAppear:animated];
   [self updateSearchCell];
+  // Sign-in section should not be reloaded while the controller is overlaid. So
+  // this section has to be reloaded before reappearing.
+  // See -[SettingsCollectionViewController onSignInStateChanged].
+  [self reloadSigninSectionWithAnimation:NO];
 }
 
 #pragma mark SettingsRootCollectionViewController
@@ -322,27 +329,8 @@
 
   // Sign in/Account section
   [model addSectionWithIdentifier:SectionIdentifierSignIn];
-  AuthenticationService* authService =
-      AuthenticationServiceFactory::GetForBrowserState(_mainBrowserState);
-  if (!authService->IsAuthenticated()) {
-    if (!_hasRecordedSigninImpression) {
-      // Once the Settings are open, this button impression will at most be
-      // recorded once until they are closed.
-      base::RecordAction(
-          base::UserMetricsAction("Signin_Impression_FromSettings"));
-      _hasRecordedSigninImpression = YES;
-    }
-    if (experimental_flags::IsSigninPromoEnabled()) {
-      _signinPromoViewMediator.reset([[SigninPromoViewMediator alloc] init]);
-      _signinPromoViewMediator.get().consumer = self;
-    }
-    [model addItem:[self signInTextItem]
-        toSectionWithIdentifier:SectionIdentifierSignIn];
-  } else {
-    _signinPromoViewMediator.reset(nil);
-    [model addItem:[self accountCellItem]
-        toSectionWithIdentifier:SectionIdentifierSignIn];
-  }
+  [model addItem:[self signinSectionItem]
+      toSectionWithIdentifier:SectionIdentifierSignIn];
 
   // Basics section
   [model addSectionWithIdentifier:SectionIdentifierBasics];
@@ -413,6 +401,29 @@
 #endif  // CHROMIUM_BUILD && !defined(NDEBUG)
 }
 
+- (CollectionViewItem*)signinSectionItem {
+  AuthenticationService* authService =
+      AuthenticationServiceFactory::GetForBrowserState(_mainBrowserState);
+  if (!authService->IsAuthenticated()) {
+    if (!_hasRecordedSigninImpression) {
+      // Once the Settings are open, this button impression will at most be
+      // recorded once until they are closed.
+      base::RecordAction(
+          base::UserMetricsAction("Signin_Impression_FromSettings"));
+      _hasRecordedSigninImpression = YES;
+    }
+    if (experimental_flags::IsSigninPromoEnabled()) {
+      _signinPromoViewMediator.reset([[SigninPromoViewMediator alloc] init]);
+      _signinPromoViewMediator.get().consumer = self;
+    }
+    _signinSectionItem = [self signInTextItem];
+  } else {
+    _signinPromoViewMediator.reset(nil);
+    _signinSectionItem = [self accountCellItem];
+  }
+  return _signinSectionItem;
+}
+
 #pragma mark - Model Items
 
 - (CollectionViewItem*)signInTextItem {
@@ -593,6 +604,24 @@
          inSectionWithIdentifier:SectionIdentifierBasics];
 }
 
+- (void)reloadSigninSectionWithAnimation:(BOOL)animation {
+  CollectionViewModel* model = self.collectionViewModel;
+  NSIndexPath* indexPath = [model indexPathForItem:_signinSectionItem
+                           inSectionWithIdentifier:SectionIdentifierSignIn];
+  [model removeItemWithType:_signinSectionItem.type
+      fromSectionWithIdentifier:SectionIdentifierSignIn];
+  [model addItem:[self signinSectionItem]
+      toSectionWithIdentifier:SectionIdentifierSignIn];
+  void (^reloadItemsBlock)(void) = ^{
+    [self.collectionView reloadItemsAtIndexPaths:@[ indexPath ]];
+  };
+  if (animation) {
+    reloadItemsBlock();
+  } else {
+    [UIView performWithoutAnimation:reloadItemsBlock];
+  }
+}
+
 #pragma mark Item Constructors
 
 - (CollectionViewDetailItem*)detailItemWithType:(NSInteger)type
@@ -1022,9 +1051,17 @@
 #pragma mark NotificationBridgeDelegate
 
 - (void)onSignInStateChanged {
-  // Sign in state changes are rare. Just reload the entire collection when this
-  // happens.
-  [self reloadData];
+  // Sign-in section should only be reloaded when
+  // SettingsCollectionViewController is not overlaid.
+  // When clicking on "CONTINUE AS xxx" button, SigninInteractionController
+  // presents a signed-in view and sign-in the user at the same time.
+  // So to avoid having two animations, the sign-in section should only be
+  // reloaded when the settings controler is not overlaid.
+  // The sign-in section will be reloaded when
+  // -[SettingsCollectionViewController viewWillAppear:] is called.
+  if (!self.presentedViewController) {
+    [self reloadSigninSectionWithAnimation:YES];
+  }
 }
 
 #pragma mark SettingsControllerProtocol
diff --git a/media/blink/webmediaplayer_impl.cc b/media/blink/webmediaplayer_impl.cc
index 309441e..20796d3 100644
--- a/media/blink/webmediaplayer_impl.cc
+++ b/media/blink/webmediaplayer_impl.cc
@@ -1096,6 +1096,8 @@
   // If the CDM is set from the constructor there is no promise
   // (|set_cdm_result_|) to fulfill.
   if (success) {
+    media_log_->SetBooleanProperty("has_cdm", true);
+
     // This will release the previously attached CDM (if any).
     cdm_ = std::move(pending_cdm_);
     if (set_cdm_result_) {
diff --git a/printing/pdf_metafile_skia.cc b/printing/pdf_metafile_skia.cc
index 54c1cf35..12b24ad 100644
--- a/printing/pdf_metafile_skia.cc
+++ b/printing/pdf_metafile_skia.cc
@@ -12,9 +12,9 @@
 #include "base/files/file.h"
 #include "base/memory/ptr_util.h"
 #include "base/time/time.h"
-#include "cc/paint/paint_canvas.h"
 #include "cc/paint/paint_record.h"
 #include "cc/paint/paint_recorder.h"
+#include "cc/paint/skia_paint_canvas.h"
 #include "printing/print_settings.h"
 #include "third_party/skia/include/core/SkDocument.h"
 #include "third_party/skia/include/core/SkStream.h"
diff --git a/third_party/WebKit/LayoutTests/TestExpectations b/third_party/WebKit/LayoutTests/TestExpectations
index 9c7a0c5..7ee554e 100644
--- a/third_party/WebKit/LayoutTests/TestExpectations
+++ b/third_party/WebKit/LayoutTests/TestExpectations
@@ -598,6 +598,103 @@
 crbug.com/635619 virtual/layout_ng/fast/block/float/float-not-removed-from-next-sibling4.html [ Skip ]
 crbug.com/635619 virtual/layout_ng/fast/block/float/line-break-after-white-space-crash.html [ Skip ]
 
+#### external/wpt/css/CSS2/linebox
+crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/border-padding-bleed-001.xht [ Skip ]
+crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/border-padding-bleed-002.xht [ Skip ]
+crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/border-padding-bleed-003.xht [ Skip ]
+crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/empty-inline-002.xht [ Skip ]
+crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/empty-inline-003.xht [ Skip ]
+crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/inline-formatting-context-001.xht [ Skip ]
+crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/inline-formatting-context-002.xht [ Skip ]
+crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/inline-formatting-context-003.xht [ Skip ]
+crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/inline-formatting-context-004.xht [ Skip ]
+crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/inline-formatting-context-005.xht [ Skip ]
+crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/inline-formatting-context-006.xht [ Skip ]
+crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/inline-formatting-context-007.xht [ Skip ]
+crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/inline-formatting-context-010b.xht [ Skip ]
+crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/inline-formatting-context-011.xht [ Skip ]
+crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/inline-formatting-context-012.xht [ Skip ]
+crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/inline-formatting-context-013.xht [ Skip ]
+crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/inline-formatting-context-015.xht [ Skip ]
+crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/inline-formatting-context-022.xht [ Skip ]
+crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/inline-formatting-context-023.xht [ Skip ]
+crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/line-height-002.xht [ Skip ]
+crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/line-height-004.xht [ Skip ]
+crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/line-height-005.xht [ Skip ]
+crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/line-height-applies-to-012.xht [ Skip ]
+crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/line-height-013.xht [ Skip ]
+crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/line-height-015.xht [ Skip ]
+crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/line-height-016.xht [ Skip ]
+crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/line-height-024.xht [ Skip ]
+crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/line-height-026.xht [ Skip ]
+crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/line-height-027.xht [ Skip ]
+crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/line-height-035.xht [ Skip ]
+crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/line-height-037.xht [ Skip ]
+crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/line-height-038.xht [ Skip ]
+crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/line-height-046.xht [ Skip ]
+crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/line-height-048.xht [ Skip ]
+crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/line-height-049.xht [ Skip ]
+crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/line-height-057.xht [ Skip ]
+crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/line-height-059.xht [ Skip ]
+crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/line-height-060.xht [ Skip ]
+crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/line-height-068.xht [ Skip ]
+crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/line-height-070.xht [ Skip ]
+crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/line-height-071.xht [ Skip ]
+crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/line-height-079.xht [ Skip ]
+crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/line-height-081.xht [ Skip ]
+crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/line-height-082.xht [ Skip ]
+crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/line-height-090.xht [ Skip ]
+crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/line-height-092.xht [ Skip ]
+crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/line-height-093.xht [ Skip ]
+crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/line-height-101.xht [ Skip ]
+crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/line-height-103.xht [ Skip ]
+crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/line-height-104.xht [ Skip ]
+crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/line-height-125.xht [ Skip ]
+crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/line-height-126.xht [ Skip ]
+crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/line-height-127.xht [ Skip ]
+crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/line-height-128.xht [ Skip ]
+crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/line-height-129.xht [ Skip ]
+crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/line-height-applies-to-001.xht [ Skip ]
+crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/line-height-applies-to-002.xht [ Skip ]
+crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/line-height-applies-to-003.xht [ Skip ]
+crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/line-height-applies-to-004.xht [ Skip ]
+crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/line-height-applies-to-005.xht [ Skip ]
+crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/line-height-applies-to-006.xht [ Skip ]
+crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/line-height-applies-to-007.xht [ Skip ]
+crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/line-height-applies-to-013.xht [ Skip ]
+crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/line-height-applies-to-015.xht [ Skip ]
+crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/line-height-bleed-001.xht [ Skip ]
+crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/line-height-bleed-002.xht [ Skip ]
+crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/vertical-align-baseline-003.xht [ Skip ]
+crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/vertical-align-007.xht [ Skip ]
+crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/vertical-align-008.xht [ Skip ]
+crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/vertical-align-019.xht [ Skip ]
+crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/vertical-align-020.xht [ Skip ]
+crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/vertical-align-031.xht [ Skip ]
+crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/vertical-align-032.xht [ Skip ]
+crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/vertical-align-043.xht [ Skip ]
+crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/vertical-align-044.xht [ Skip ]
+crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/vertical-align-055.xht [ Skip ]
+crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/vertical-align-056.xht [ Skip ]
+crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/vertical-align-067.xht [ Skip ]
+crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/vertical-align-068.xht [ Skip ]
+crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/vertical-align-079.xht [ Skip ]
+crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/vertical-align-080.xht [ Skip ]
+crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/vertical-align-091.xht [ Skip ]
+crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/vertical-align-092.xht [ Skip ]
+crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/vertical-align-103.xht [ Skip ]
+crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/vertical-align-104.xht [ Skip ]
+crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/vertical-align-117a.xht [ Skip ]
+crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/vertical-align-118a.xht [ Skip ]
+crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/vertical-align-121.xht [ Skip ]
+crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/vertical-align-applies-to-008.xht [ Skip ]
+crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/vertical-align-baseline-001.xht [ Skip ]
+crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/vertical-align-baseline-002.xht [ Skip ]
+crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/vertical-align-baseline-004a.xht [ Skip ]
+crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/vertical-align-baseline-005a.xht [ Skip ]
+crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/vertical-align-sub-001.xht [ Skip ]
+crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/vertical-align-super-001.xht [ Skip ]
+
 # ====== LayoutNG-only failures until here ======
 
 # Run these tests with under virtual/scalefactor... only.
diff --git a/third_party/WebKit/LayoutTests/VirtualTestSuites b/third_party/WebKit/LayoutTests/VirtualTestSuites
index fa731844..7c024e3 100644
--- a/third_party/WebKit/LayoutTests/VirtualTestSuites
+++ b/third_party/WebKit/LayoutTests/VirtualTestSuites
@@ -454,7 +454,7 @@
   },
   {
     "prefix": "layout_ng",
-    "base": "external/wpt/css/css21/linebox",
+    "base": "external/wpt/css/CSS2/linebox",
     "args": ["--enable-blink-features=LayoutNG"]
   },
   {
diff --git a/third_party/WebKit/LayoutTests/editing/input/keyboard_event_without_focus.html b/third_party/WebKit/LayoutTests/editing/input/keyboard_event_without_focus.html
index e0ccc38d..dbfbbd3 100644
--- a/third_party/WebKit/LayoutTests/editing/input/keyboard_event_without_focus.html
+++ b/third_party/WebKit/LayoutTests/editing/input/keyboard_event_without_focus.html
@@ -20,7 +20,7 @@
       // Insert |textField.value| to HTML for verification
       textField.appendChild(document.createTextNode(textField.value));
     },
-    '<input value="x">x</input><input type="checkbox">',
+    '|<input value="x">x</input><input type="checkbox">',
     'unfocused text field should not insert a charcter by keyboard event');
 
   assert_selection(
diff --git a/third_party/WebKit/LayoutTests/editing/selection/focus-and-display-none-and-redisplay-expected.txt b/third_party/WebKit/LayoutTests/editing/selection/focus-and-display-none-and-redisplay-expected.txt
index 5ea9cc07f3..dd9d28b5 100644
--- a/third_party/WebKit/LayoutTests/editing/selection/focus-and-display-none-and-redisplay-expected.txt
+++ b/third_party/WebKit/LayoutTests/editing/selection/focus-and-display-none-and-redisplay-expected.txt
@@ -1,10 +1,10 @@
-Unfocused TEXTAREA should not accept editing.
+Unfocused TEXTAREA should accept editing.
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
 
 PASS document.activeElement is not textarea
-PASS textarea.value is "original"
+PASS textarea.value is "addoriginal"
 PASS successfullyParsed is true
 
 TEST COMPLETE
diff --git a/third_party/WebKit/LayoutTests/editing/selection/focus-and-display-none-and-redisplay.html b/third_party/WebKit/LayoutTests/editing/selection/focus-and-display-none-and-redisplay.html
index e895a7d..e316a71 100644
--- a/third_party/WebKit/LayoutTests/editing/selection/focus-and-display-none-and-redisplay.html
+++ b/third_party/WebKit/LayoutTests/editing/selection/focus-and-display-none-and-redisplay.html
@@ -3,14 +3,14 @@
 <script src="../../resources/js-test.js"></script>
 <textarea>original</textarea>
 <script>
-description('Unfocused TEXTAREA should not accept editing.');
+description('Unfocused TEXTAREA should accept editing.');
 jsTestIsAsync = true;
 var textarea = document.querySelector('textarea');
 textarea.addEventListener('blur', function() {
     textarea.style.display = 'inline-block';
     shouldNotBe('document.activeElement', 'textarea');
     document.execCommand('inserttext', false, 'add');
-    shouldBeEqualToString('textarea.value', 'original');
+    shouldBeEqualToString('textarea.value', 'addoriginal');
     finishJSTest();
 }, false);
 textarea.focus();
diff --git a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/focus-selectionchange-on-tap-expected.txt b/third_party/WebKit/LayoutTests/fast/events/touch/gesture/focus-selectionchange-on-tap-expected.txt
index 9c70202d..92c75a2 100644
--- a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/focus-selectionchange-on-tap-expected.txt
+++ b/third_party/WebKit/LayoutTests/fast/events/touch/gesture/focus-selectionchange-on-tap-expected.txt
@@ -34,7 +34,6 @@
 Received mouseout on target
 PASS tapHandled is false
 Received selectionchange on #document anchor=#text[0]
-Received selectionchange on #document anchor=#text[0]
 PASS isFocused(target) is false
 
 PASS successfullyParsed is true
diff --git a/third_party/WebKit/LayoutTests/fast/forms/focus-selection-input-expected.txt b/third_party/WebKit/LayoutTests/fast/forms/focus-selection-input-expected.txt
index 5d84ebd..492d12a 100644
--- a/third_party/WebKit/LayoutTests/fast/forms/focus-selection-input-expected.txt
+++ b/third_party/WebKit/LayoutTests/fast/forms/focus-selection-input-expected.txt
@@ -8,14 +8,6 @@
 EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
 EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
 EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
-EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
-EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
-EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
-EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
-EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
-EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
-EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
-EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
 <input type="text"> focus selection
 
 This test checks whether the selection is restored, cleared, or set to the full range when using different ways to focus a text field. These results all match Mozilla, except test 6, which selects the whole field contents to match all other cases of keyboard focus. When running manually, please follow the steps below. In the test harness, the test runs automatically.
diff --git a/third_party/WebKit/LayoutTests/fast/forms/focus-selection-textarea-expected.txt b/third_party/WebKit/LayoutTests/fast/forms/focus-selection-textarea-expected.txt
index 25c91c1..6290055 100644
--- a/third_party/WebKit/LayoutTests/fast/forms/focus-selection-textarea-expected.txt
+++ b/third_party/WebKit/LayoutTests/fast/forms/focus-selection-textarea-expected.txt
@@ -8,14 +8,6 @@
 EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
 EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
 EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
-EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
-EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
-EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
-EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
-EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
-EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
-EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
-EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
 <textarea> focus selection
 
 This test checks whether the selection is restored, cleared, or set to the full range when using different ways to focus a text area. These results all match Mozilla. When running manually, please follow the steps below. In the test harness, the test runs automatically.
diff --git a/third_party/WebKit/LayoutTests/fast/multicol/layers-in-multicol-expected.html b/third_party/WebKit/LayoutTests/fast/multicol/layers-in-multicol-expected.html
deleted file mode 100644
index 97d5761f..0000000
--- a/third_party/WebKit/LayoutTests/fast/multicol/layers-in-multicol-expected.html
+++ /dev/null
@@ -1,87 +0,0 @@
-<!DOCTYPE html>
-<style>
-    .multicol {
-        width: 300px;
-        height: 100px;
-        line-height: 20px;
-        border: 5px solid maroon;
-    }
-    .column {
-        width: 100px;
-        float: left;
-    }
-    .multicol[dir="rtl"] > .column {
-        float: right;
-    }
-    .block {
-        display: inline-block;
-        width: 1em;
-        height: 10px;
-        background-color: green;
-    }
-    .opacity {
-        opacity: 0.5;
-        color: green;
-    }
-    .relative {
-        position: relative;
-        top: -4px;
-        color: green;
-    }
-</style>
-<p>
-    Test layers which are fully contained within a single column.
-</p>
-LTR:
-<div class="multicol">
-    <div class="column">
-        line1<br>
-        line2<br>
-        line3<br>
-        line4<br>
-        line5<br>
-    </div>
-    <div class="column">
-        line6<br>
-        <div class="block"></div> line7<br>
-        line8<br>
-        <span class="relative">relative9</span><br>
-        line10<br>
-    </div>
-    <div class="column">
-        line11<br>
-        line12<br>
-        <!-- The extra inner span below forces the creation of a transparency layer in Skia to work
-             around optimizations that would cause blending differences between the test and the
-             expectation. -->
-        <span class="opacity">opacity<span>13</span></span><br>
-        line14
-    </div>
-</div>
-
-RTL:
-<div class="multicol" dir="rtl">
-    <div class="column">
-        line1<br>
-        line2<br>
-        line3<br>
-        line4<br>
-        line5<br>
-    </div>
-    <div class="column">
-        line6<br>
-        <div class="block"></div> line7<br>
-        line8<br>
-        <span class="relative">relative9</span><br>
-        line10<br>
-    </div>
-    <div class="column">
-        line11<br>
-        line12<br>
-        <!-- The extra inner span below forces the creation of a transparency layer in Skia to work
-             around optimizations that would cause blending differences between the test and the
-             expectation. -->
-        <span class="opacity">opacity<span>13</span></span><br>
-        line14
-    </div>
-</div>
diff --git a/third_party/WebKit/LayoutTests/fast/multicol/layers-split-across-columns-expected.html b/third_party/WebKit/LayoutTests/fast/multicol/layers-split-across-columns-expected.html
deleted file mode 100644
index 8bbd920..0000000
--- a/third_party/WebKit/LayoutTests/fast/multicol/layers-split-across-columns-expected.html
+++ /dev/null
@@ -1,90 +0,0 @@
-<!DOCTYPE html>
-<style>
-    .container {
-        margin-right: 4px;
-        position: absolute;
-    }
-    .multicol {
-        width: 110px;
-        height: 150px;
-        border: 5px solid black;
-    }
-    .multicol > div {
-        float: left;
-        width: 50px;
-        height: 50px;
-    }
-
-    .row1_left { background-color: black; }
-    .row1_right { background-color: #0000b0; }
-    .row2_left { background-color: #0000f0; }
-    .row2_right { background-color: #000090; }
-    .row3_left { background-color: #0000d0; }
-    .row3_right { background-color: black; }
-
-    .row1_right,
-    .row2_right,
-    .row3_right {
-        margin-left: 10px;
-    }
-
-    #opacity .row1_right,
-    #opacity .row2_left,
-    #opacity .row2_right,
-    #opacity .row3_left {
-        opacity: 0.99;
-    }
-
-    .pos1 { left: 10px; top: 10px; }
-    .pos2 { left: 150px; top: 10px; }
-    .pos3 { left: 10px; top: 200px; }
-    .pos4 { left: 150px; top: 200px; }
-
-</style>
-<div class="container pos1">
-    Overflow:
-    <div class="multicol">
-        <div class="row1_left"></div>
-        <div class="row1_right"></div>
-        <div class="row2_left"></div>
-        <div class="row2_right"></div>
-        <div class="row3_left"></div>
-        <div class="row3_right"></div>
-    </div>
-</div>
-<div class="container pos2">
-    Transforms:
-    <div class="multicol">
-        <div class="row1_left"></div>
-        <div class="row1_right"></div>
-        <div class="row2_left"></div>
-        <div class="row2_right"></div>
-        <div class="row3_left"></div>
-        <div class="row3_right"></div>
-    </div>
-</div>
-<div class="container pos3">
-    Relative Pos.:
-    <div class="multicol">
-        <div class="row1_left"></div>
-        <div class="row1_right"></div>
-        <div class="row2_left"></div>
-        <div class="row2_right"></div>
-        <div class="row3_left"></div>
-        <div class="row3_right"></div>
-    </div>
-</div>
-<div class="container pos4" id="opacity">
-    Opacity:
-    <div class="multicol">
-        <div class="row1_left"></div>
-        <!-- The extra &nbsp;s below force the creation of transparency layers in Skia to work
-             around optimizations that would cause blending differences between the test and the
-             expectation. -->
-        <div class="row1_right">&nbsp;</div>
-        <div class="row2_left">&nbsp;</div>
-        <div class="row2_right">&nbsp;</div>
-        <div class="row3_left">&nbsp;</div>
-        <div class="row3_right"></div>
-    </div>
-</div>
diff --git a/third_party/WebKit/LayoutTests/fast/multicol/transform-inside-opacity-expected.html b/third_party/WebKit/LayoutTests/fast/multicol/transform-inside-opacity-expected.html
deleted file mode 100644
index 0b67873..0000000
--- a/third_party/WebKit/LayoutTests/fast/multicol/transform-inside-opacity-expected.html
+++ /dev/null
@@ -1,6 +0,0 @@
-<div style="position:relative; width:420px;border:2px solid black; height:200px">
-<!-- The extra &nbsp; below forces the creation of a transparency layer in Skia to work around
-     optimizations that would cause blending differences between the test and the expectation. -->
-<div style="opacity:0.5; position:absolute;width:200px;height:100px;background-color:green;right:0;top:0">&nbsp;</div>
-</div>
-</div>
diff --git a/third_party/WebKit/LayoutTests/fast/multicol/transform-inside-opacity-expected.png b/third_party/WebKit/LayoutTests/fast/multicol/transform-inside-opacity-expected.png
new file mode 100644
index 0000000..1ebb6e8
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/fast/multicol/transform-inside-opacity-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/column-float-under-stacked-inline-expected.png b/third_party/WebKit/LayoutTests/paint/invalidation/column-float-under-stacked-inline-expected.png
index a92e6a8..c6cabcbb 100644
--- a/third_party/WebKit/LayoutTests/paint/invalidation/column-float-under-stacked-inline-expected.png
+++ b/third_party/WebKit/LayoutTests/paint/invalidation/column-float-under-stacked-inline-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/android/fast/forms/textarea/textarea-scrolled-mask-expected.txt b/third_party/WebKit/LayoutTests/platform/android/fast/forms/textarea/textarea-scrolled-mask-expected.txt
index 339ce87..3e0ac81 100644
--- a/third_party/WebKit/LayoutTests/platform/android/fast/forms/textarea/textarea-scrolled-mask-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/android/fast/forms/textarea/textarea-scrolled-mask-expected.txt
@@ -23,3 +23,4 @@
         text run at (0,48) width 8: "4"
       LayoutBR {BR} at (8,48) size 0x16
       LayoutBR {BR} at (0,64) size 0x16
+caret: position 0 of child 1 {BR} of child 0 {DIV} of {#document-fragment} of child 3 {TEXTAREA} of body
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/forms/textarea/textarea-scrolled-mask-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/fast/forms/textarea/textarea-scrolled-mask-expected.txt
index 91c920f..82c277bd9 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/fast/forms/textarea/textarea-scrolled-mask-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/linux/fast/forms/textarea/textarea-scrolled-mask-expected.txt
@@ -20,3 +20,4 @@
         text run at (0,48) width 8: "4"
         text run at (8,48) width 0: " "
       LayoutBR {BR} at (0,64) size 0x16
+caret: position 0 of child 1 {BR} of child 0 {DIV} of {#document-fragment} of child 3 {TEXTAREA} of body
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/multicol/layers-in-multicol-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/multicol/layers-in-multicol-expected.png
new file mode 100644
index 0000000..44fc4a4
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/linux/fast/multicol/layers-in-multicol-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/multicol/layers-split-across-columns-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/multicol/layers-split-across-columns-expected.png
new file mode 100644
index 0000000..27fbf58
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/linux/fast/multicol/layers-split-across-columns-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/svg/scrolling-embedded-svg-file-image-repaint-problem-expected.png b/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/svg/scrolling-embedded-svg-file-image-repaint-problem-expected.png
index b024e71..5e1fff8 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/svg/scrolling-embedded-svg-file-image-repaint-problem-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/svg/scrolling-embedded-svg-file-image-repaint-problem-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/transforms/2d/hindi-rotated-expected.png b/third_party/WebKit/LayoutTests/platform/linux/transforms/2d/hindi-rotated-expected.png
index 8d5f6fd3..6f98684 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/transforms/2d/hindi-rotated-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/linux/transforms/2d/hindi-rotated-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/virtual/disable-spinvalidation/paint/invalidation/svg/scrolling-embedded-svg-file-image-repaint-problem-expected.png b/third_party/WebKit/LayoutTests/platform/linux/virtual/disable-spinvalidation/paint/invalidation/svg/scrolling-embedded-svg-file-image-repaint-problem-expected.png
index b024e71..5e1fff8 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/virtual/disable-spinvalidation/paint/invalidation/svg/scrolling-embedded-svg-file-image-repaint-problem-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/linux/virtual/disable-spinvalidation/paint/invalidation/svg/scrolling-embedded-svg-file-image-repaint-problem-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.10/fast/forms/textarea/textarea-scrolled-mask-expected.txt b/third_party/WebKit/LayoutTests/platform/mac-mac10.10/fast/forms/textarea/textarea-scrolled-mask-expected.txt
index ae8c481..446465f 100644
--- a/third_party/WebKit/LayoutTests/platform/mac-mac10.10/fast/forms/textarea/textarea-scrolled-mask-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/mac-mac10.10/fast/forms/textarea/textarea-scrolled-mask-expected.txt
@@ -20,3 +20,4 @@
         text run at (0,39) width 7: "4"
         text run at (6,39) width 1: " "
       LayoutBR {BR} at (0,52) size 0x13
+caret: position 0 of child 1 {BR} of child 0 {DIV} of {#document-fragment} of child 3 {TEXTAREA} of body
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.10/plugins/mouse-click-plugin-clears-selection-expected.txt b/third_party/WebKit/LayoutTests/platform/mac-mac10.10/plugins/mouse-click-plugin-clears-selection-expected.txt
index b38d4363..96dc06f 100644
--- a/third_party/WebKit/LayoutTests/platform/mac-mac10.10/plugins/mouse-click-plugin-clears-selection-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/mac-mac10.10/plugins/mouse-click-plugin-clears-selection-expected.txt
@@ -18,3 +18,5 @@
   LayoutBlockFlow {DIV} at (3,3) size 125x13
     LayoutText {#text} at (0,0) size 24x13
       text run at (0,0) width 24: "hello"
+selection start: position 0 of child 0 {#text} of child 0 {DIV} of {#document-fragment} of child 4 {INPUT} of body
+selection end:   position 5 of child 0 {#text} of child 0 {DIV} of {#document-fragment} of child 4 {INPUT} of body
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.10/transforms/2d/hindi-rotated-expected.png b/third_party/WebKit/LayoutTests/platform/mac-mac10.10/transforms/2d/hindi-rotated-expected.png
index 3a881330..0acea10 100644
--- a/third_party/WebKit/LayoutTests/platform/mac-mac10.10/transforms/2d/hindi-rotated-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac-mac10.10/transforms/2d/hindi-rotated-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.9/fast/forms/textarea/textarea-scrolled-mask-expected.txt b/third_party/WebKit/LayoutTests/platform/mac-mac10.9/fast/forms/textarea/textarea-scrolled-mask-expected.txt
index 3f6bb4c..d0ed23eb 100644
--- a/third_party/WebKit/LayoutTests/platform/mac-mac10.9/fast/forms/textarea/textarea-scrolled-mask-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/mac-mac10.9/fast/forms/textarea/textarea-scrolled-mask-expected.txt
@@ -20,3 +20,4 @@
         text run at (0,39) width 7: "4"
         text run at (6,39) width 1: " "
       LayoutBR {BR} at (0,52) size 0x13
+caret: position 0 of child 1 {BR} of child 0 {DIV} of {#document-fragment} of child 3 {TEXTAREA} of body
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.9/paint/invalidation/svg/scrolling-embedded-svg-file-image-repaint-problem-expected.png b/third_party/WebKit/LayoutTests/platform/mac-mac10.9/paint/invalidation/svg/scrolling-embedded-svg-file-image-repaint-problem-expected.png
index 07e019c..991aaf7a 100644
--- a/third_party/WebKit/LayoutTests/platform/mac-mac10.9/paint/invalidation/svg/scrolling-embedded-svg-file-image-repaint-problem-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac-mac10.9/paint/invalidation/svg/scrolling-embedded-svg-file-image-repaint-problem-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.9/plugins/mouse-click-plugin-clears-selection-expected.txt b/third_party/WebKit/LayoutTests/platform/mac-mac10.9/plugins/mouse-click-plugin-clears-selection-expected.txt
index bace2a37..c8c61722 100644
--- a/third_party/WebKit/LayoutTests/platform/mac-mac10.9/plugins/mouse-click-plugin-clears-selection-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/mac-mac10.9/plugins/mouse-click-plugin-clears-selection-expected.txt
@@ -18,3 +18,5 @@
   LayoutBlockFlow {DIV} at (3,3) size 117x13
     LayoutText {#text} at (0,0) size 27x13
       text run at (0,0) width 27: "hello"
+selection start: position 0 of child 0 {#text} of child 0 {DIV} of {#document-fragment} of child 4 {INPUT} of body
+selection end:   position 5 of child 0 {#text} of child 0 {DIV} of {#document-fragment} of child 4 {INPUT} of body
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.9/transforms/2d/hindi-rotated-expected.png b/third_party/WebKit/LayoutTests/platform/mac-mac10.9/transforms/2d/hindi-rotated-expected.png
index 11b5d71..84b89da 100644
--- a/third_party/WebKit/LayoutTests/platform/mac-mac10.9/transforms/2d/hindi-rotated-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac-mac10.9/transforms/2d/hindi-rotated-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.9/transforms/transformed-focused-text-input-expected.png b/third_party/WebKit/LayoutTests/platform/mac-mac10.9/transforms/transformed-focused-text-input-expected.png
index f409f9e..1693941 100644
--- a/third_party/WebKit/LayoutTests/platform/mac-mac10.9/transforms/transformed-focused-text-input-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac-mac10.9/transforms/transformed-focused-text-input-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.9/virtual/disable-spinvalidation/paint/invalidation/svg/scrolling-embedded-svg-file-image-repaint-problem-expected.png b/third_party/WebKit/LayoutTests/platform/mac-mac10.9/virtual/disable-spinvalidation/paint/invalidation/svg/scrolling-embedded-svg-file-image-repaint-problem-expected.png
index 07e019c..991aaf7a 100644
--- a/third_party/WebKit/LayoutTests/platform/mac-mac10.9/virtual/disable-spinvalidation/paint/invalidation/svg/scrolling-embedded-svg-file-image-repaint-problem-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac-mac10.9/virtual/disable-spinvalidation/paint/invalidation/svg/scrolling-embedded-svg-file-image-repaint-problem-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/forms/textarea/textarea-scrolled-mask-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/fast/forms/textarea/textarea-scrolled-mask-expected.txt
index 15d83677..a69b259f 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/fast/forms/textarea/textarea-scrolled-mask-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/mac/fast/forms/textarea/textarea-scrolled-mask-expected.txt
@@ -20,3 +20,4 @@
         text run at (0,39) width 8: "4"
         text run at (7,39) width 1: " "
       LayoutBR {BR} at (0,52) size 0x13
+caret: position 0 of child 1 {BR} of child 0 {DIV} of {#document-fragment} of child 3 {TEXTAREA} of body
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/multicol/layers-in-multicol-expected.png b/third_party/WebKit/LayoutTests/platform/mac/fast/multicol/layers-in-multicol-expected.png
new file mode 100644
index 0000000..9243efa
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/mac/fast/multicol/layers-in-multicol-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/multicol/layers-split-across-columns-expected.png b/third_party/WebKit/LayoutTests/platform/mac/fast/multicol/layers-split-across-columns-expected.png
new file mode 100644
index 0000000..86cf379
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/mac/fast/multicol/layers-split-across-columns-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/svg/scrolling-embedded-svg-file-image-repaint-problem-expected.png b/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/svg/scrolling-embedded-svg-file-image-repaint-problem-expected.png
index 56d7e380c..af00c64c0 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/svg/scrolling-embedded-svg-file-image-repaint-problem-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/svg/scrolling-embedded-svg-file-image-repaint-problem-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/plugins/mouse-click-plugin-clears-selection-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/plugins/mouse-click-plugin-clears-selection-expected.txt
index d4f72274..0fd6c25e 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/plugins/mouse-click-plugin-clears-selection-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/mac/plugins/mouse-click-plugin-clears-selection-expected.txt
@@ -18,3 +18,5 @@
   LayoutBlockFlow {DIV} at (3,3) size 125x13
     LayoutText {#text} at (0,0) size 25x13
       text run at (0,0) width 25: "hello"
+selection start: position 0 of child 0 {#text} of child 0 {DIV} of {#document-fragment} of child 4 {INPUT} of body
+selection end:   position 5 of child 0 {#text} of child 0 {DIV} of {#document-fragment} of child 4 {INPUT} of body
diff --git a/third_party/WebKit/LayoutTests/platform/mac/svg/as-background-image/background-image-preserveaspectRatio-support-expected.png b/third_party/WebKit/LayoutTests/platform/mac/svg/as-background-image/background-image-preserveaspectRatio-support-expected.png
index ab426ac..31dd704 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/svg/as-background-image/background-image-preserveaspectRatio-support-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac/svg/as-background-image/background-image-preserveaspectRatio-support-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/svg/as-image/img-preserveAspectRatio-support-1-expected.png b/third_party/WebKit/LayoutTests/platform/mac/svg/as-image/img-preserveAspectRatio-support-1-expected.png
index 7e1243a1..98ce8c5 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/svg/as-image/img-preserveAspectRatio-support-1-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac/svg/as-image/img-preserveAspectRatio-support-1-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/svg/zoom/page/zoom-img-preserveAspectRatio-support-1-expected.png b/third_party/WebKit/LayoutTests/platform/mac/svg/zoom/page/zoom-img-preserveAspectRatio-support-1-expected.png
index 8938c8b..556507c 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/svg/zoom/page/zoom-img-preserveAspectRatio-support-1-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac/svg/zoom/page/zoom-img-preserveAspectRatio-support-1-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/transforms/2d/hindi-rotated-expected.png b/third_party/WebKit/LayoutTests/platform/mac/transforms/2d/hindi-rotated-expected.png
index 497919fc..70f55312 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/transforms/2d/hindi-rotated-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac/transforms/2d/hindi-rotated-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/transforms/transformed-focused-text-input-expected.png b/third_party/WebKit/LayoutTests/platform/mac/transforms/transformed-focused-text-input-expected.png
index 38e6870..c57f417b 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/transforms/transformed-focused-text-input-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac/transforms/transformed-focused-text-input-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/virtual/disable-spinvalidation/paint/invalidation/svg/scrolling-embedded-svg-file-image-repaint-problem-expected.png b/third_party/WebKit/LayoutTests/platform/mac/virtual/disable-spinvalidation/paint/invalidation/svg/scrolling-embedded-svg-file-image-repaint-problem-expected.png
index 56d7e380c..af00c64c0 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/virtual/disable-spinvalidation/paint/invalidation/svg/scrolling-embedded-svg-file-image-repaint-problem-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac/virtual/disable-spinvalidation/paint/invalidation/svg/scrolling-embedded-svg-file-image-repaint-problem-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/forms/textarea/textarea-scrolled-mask-expected.txt b/third_party/WebKit/LayoutTests/platform/win/fast/forms/textarea/textarea-scrolled-mask-expected.txt
index 086f3fd..46378db 100644
--- a/third_party/WebKit/LayoutTests/platform/win/fast/forms/textarea/textarea-scrolled-mask-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/win/fast/forms/textarea/textarea-scrolled-mask-expected.txt
@@ -20,3 +20,4 @@
         text run at (0,48) width 8: "4"
         text run at (8,48) width 0: " "
       LayoutBR {BR} at (0,64) size 0x16
+caret: position 0 of child 1 {BR} of child 0 {DIV} of {#document-fragment} of child 3 {TEXTAREA} of body
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/multicol/layers-in-multicol-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/multicol/layers-in-multicol-expected.png
new file mode 100644
index 0000000..62b98ea
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/win/fast/multicol/layers-in-multicol-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/multicol/layers-split-across-columns-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/multicol/layers-split-across-columns-expected.png
new file mode 100644
index 0000000..608b610
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/win/fast/multicol/layers-split-across-columns-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/svg/scrolling-embedded-svg-file-image-repaint-problem-expected.png b/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/svg/scrolling-embedded-svg-file-image-repaint-problem-expected.png
index 8829276ef..6eb1bf6 100644
--- a/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/svg/scrolling-embedded-svg-file-image-repaint-problem-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/svg/scrolling-embedded-svg-file-image-repaint-problem-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/plugins/mouse-click-plugin-clears-selection-expected.txt b/third_party/WebKit/LayoutTests/platform/win/plugins/mouse-click-plugin-clears-selection-expected.txt
index 30e34bb..acbcc286 100644
--- a/third_party/WebKit/LayoutTests/platform/win/plugins/mouse-click-plugin-clears-selection-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/win/plugins/mouse-click-plugin-clears-selection-expected.txt
@@ -18,3 +18,5 @@
   LayoutBlockFlow {DIV} at (2,3) size 169x16
     LayoutText {#text} at (0,0) size 27x16
       text run at (0,0) width 27: "hello"
+selection start: position 0 of child 0 {#text} of child 0 {DIV} of {#document-fragment} of child 4 {INPUT} of body
+selection end:   position 5 of child 0 {#text} of child 0 {DIV} of {#document-fragment} of child 4 {INPUT} of body
diff --git a/third_party/WebKit/LayoutTests/platform/win/transforms/2d/hindi-rotated-expected.png b/third_party/WebKit/LayoutTests/platform/win/transforms/2d/hindi-rotated-expected.png
index 6837fed..5b597ed 100644
--- a/third_party/WebKit/LayoutTests/platform/win/transforms/2d/hindi-rotated-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/win/transforms/2d/hindi-rotated-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/virtual/disable-spinvalidation/paint/invalidation/svg/scrolling-embedded-svg-file-image-repaint-problem-expected.png b/third_party/WebKit/LayoutTests/platform/win/virtual/disable-spinvalidation/paint/invalidation/svg/scrolling-embedded-svg-file-image-repaint-problem-expected.png
index 8829276ef..6eb1bf6 100644
--- a/third_party/WebKit/LayoutTests/platform/win/virtual/disable-spinvalidation/paint/invalidation/svg/scrolling-embedded-svg-file-image-repaint-problem-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/win/virtual/disable-spinvalidation/paint/invalidation/svg/scrolling-embedded-svg-file-image-repaint-problem-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win7/paint/invalidation/svg/scrolling-embedded-svg-file-image-repaint-problem-expected.png b/third_party/WebKit/LayoutTests/platform/win7/paint/invalidation/svg/scrolling-embedded-svg-file-image-repaint-problem-expected.png
index bbb8278..27dad6f 100644
--- a/third_party/WebKit/LayoutTests/platform/win7/paint/invalidation/svg/scrolling-embedded-svg-file-image-repaint-problem-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/win7/paint/invalidation/svg/scrolling-embedded-svg-file-image-repaint-problem-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win7/transforms/2d/hindi-rotated-expected.png b/third_party/WebKit/LayoutTests/platform/win7/transforms/2d/hindi-rotated-expected.png
index 7e9778db..855bfdf8 100644
--- a/third_party/WebKit/LayoutTests/platform/win7/transforms/2d/hindi-rotated-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/win7/transforms/2d/hindi-rotated-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win7/virtual/disable-spinvalidation/paint/invalidation/svg/scrolling-embedded-svg-file-image-repaint-problem-expected.png b/third_party/WebKit/LayoutTests/platform/win7/virtual/disable-spinvalidation/paint/invalidation/svg/scrolling-embedded-svg-file-image-repaint-problem-expected.png
index bbb8278..27dad6f 100644
--- a/third_party/WebKit/LayoutTests/platform/win7/virtual/disable-spinvalidation/paint/invalidation/svg/scrolling-embedded-svg-file-image-repaint-problem-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/win7/virtual/disable-spinvalidation/paint/invalidation/svg/scrolling-embedded-svg-file-image-repaint-problem-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/virtual/layout_ng/external/wpt/css/CSS2/linebox/README.txt b/third_party/WebKit/LayoutTests/virtual/layout_ng/external/wpt/css/CSS2/linebox/README.txt
new file mode 100644
index 0000000..b20ff420
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/virtual/layout_ng/external/wpt/css/CSS2/linebox/README.txt
@@ -0,0 +1,3 @@
+# This suite runs the tests in external/wpt/css/CSS2/linebox with
+# --enable-blink-features=LayoutNG.
+# The LayoutNG project is described here: http://goo.gl/1hwhfX
diff --git a/third_party/WebKit/LayoutTests/virtual/layout_ng/external/wpt/css/css21/linebox/README.txt b/third_party/WebKit/LayoutTests/virtual/layout_ng/external/wpt/css/css21/linebox/README.txt
deleted file mode 100644
index 93aac50..0000000
--- a/third_party/WebKit/LayoutTests/virtual/layout_ng/external/wpt/css/css21/linebox/README.txt
+++ /dev/null
@@ -1,3 +0,0 @@
-# This suite runs the tests in external/wpt/css/css21/linebox with
-# --enable-blink-features=LayoutNG,LayoutNGInline.
-# The LayoutNG project is described here: http://goo.gl/1hwhfX
diff --git a/third_party/WebKit/Source/bindings/templates/dictionary_impl.h.tmpl b/third_party/WebKit/Source/bindings/templates/dictionary_impl.h.tmpl
index 7c904e5..5d6c7b53 100644
--- a/third_party/WebKit/Source/bindings/templates/dictionary_impl.h.tmpl
+++ b/third_party/WebKit/Source/bindings/templates/dictionary_impl.h.tmpl
@@ -36,10 +36,11 @@
   DECLARE_VIRTUAL_TRACE();
 
  private:
-  {% for member in members %}
-  {% if member.nullable_indicator_name %}
+  {% for member in members if member.nullable_indicator_name %}
   bool {{member.nullable_indicator_name}} = false;
-  {% endif %}
+  {% endfor %}
+
+  {% for member in members %}
   {{member.member_cpp_type}} m_{{member.cpp_name}};
   {% endfor %}
 
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/TestDictionary.h b/third_party/WebKit/Source/bindings/tests/results/core/TestDictionary.h
index 951f536..3488b36 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/TestDictionary.h
+++ b/third_party/WebKit/Source/bindings/tests/results/core/TestDictionary.h
@@ -207,61 +207,62 @@
 
  private:
   bool m_hasAnyInRecordMember = false;
+  bool m_hasBooleanMember = false;
+  bool m_hasCreateMember = false;
+  bool m_hasDoubleOrNullMember = false;
+  bool m_hasDoubleOrStringSequenceMember = false;
+  bool m_hasEnumSequenceMember = false;
+  bool m_hasGarbageCollectedRecordMember = false;
+  bool m_hasInternalDictionarySequenceMember = false;
+  bool m_hasLongMember = false;
+  bool m_hasRecordMember = false;
+  bool m_hasRestrictedDoubleMember = false;
+  bool m_hasRuntimeMember = false;
+  bool m_hasStringArrayMember = false;
+  bool m_hasStringSequenceMember = false;
+  bool m_hasTestInterfaceGarbageCollectedSequenceMember = false;
+  bool m_hasTestInterfaceSequenceMember = false;
+  bool m_hasTestObjectSequenceMember = false;
+  bool m_hasUnionInRecordMember = false;
+  bool m_hasUnrestrictedDoubleMember = false;
+
   Vector<std::pair<String, ScriptValue>> m_anyInRecordMember;
   ScriptValue m_anyMember;
-  bool m_hasBooleanMember = false;
   bool m_booleanMember;
-  bool m_hasCreateMember = false;
   bool m_createMember;
   Dictionary m_dictionaryMember;
-  bool m_hasDoubleOrNullMember = false;
   double m_doubleOrNullMember;
   DoubleOrString m_doubleOrStringMember;
-  bool m_hasDoubleOrStringSequenceMember = false;
   HeapVector<DoubleOrString> m_doubleOrStringSequenceMember;
   Member<Element> m_elementOrNullMember;
   String m_enumMember;
-  bool m_hasEnumSequenceMember = false;
   Vector<String> m_enumSequenceMember;
   Member<EventTarget> m_eventTargetMember;
-  bool m_hasGarbageCollectedRecordMember = false;
   HeapVector<std::pair<String, Member<TestObject>>> m_garbageCollectedRecordMember;
-  bool m_hasInternalDictionarySequenceMember = false;
   HeapVector<InternalDictionary> m_internalDictionarySequenceMember;
-  bool m_hasLongMember = false;
   int32_t m_longMember;
   ScriptValue m_objectMember;
   ScriptValue m_objectOrNullMember;
   DoubleOrString m_otherDoubleOrStringMember;
   ScriptValue m_prefixGetMember;
-  bool m_hasRecordMember = false;
   Vector<std::pair<String, int8_t>> m_recordMember;
-  bool m_hasRestrictedDoubleMember = false;
   double m_restrictedDoubleMember;
-  bool m_hasRuntimeMember = false;
   bool m_runtimeMember;
-  bool m_hasStringArrayMember = false;
   Vector<String> m_stringArrayMember;
   String m_stringMember;
   String m_stringOrNullMember;
-  bool m_hasStringSequenceMember = false;
   Vector<String> m_stringSequenceMember;
   TestInterface2OrUint8Array m_testInterface2OrUint8ArrayMember;
   Member<TestInterfaceGarbageCollected> m_testInterfaceGarbageCollectedMember;
   Member<TestInterfaceGarbageCollected> m_testInterfaceGarbageCollectedOrNullMember;
-  bool m_hasTestInterfaceGarbageCollectedSequenceMember = false;
   HeapVector<Member<TestInterfaceGarbageCollected>> m_testInterfaceGarbageCollectedSequenceMember;
   Member<TestInterfaceImplementation> m_testInterfaceMember;
   Member<TestInterfaceImplementation> m_testInterfaceOrNullMember;
-  bool m_hasTestInterfaceSequenceMember = false;
   HeapVector<Member<TestInterfaceImplementation>> m_testInterfaceSequenceMember;
-  bool m_hasTestObjectSequenceMember = false;
   HeapVector<Member<TestObject>> m_testObjectSequenceMember;
   Member<DOMUint8Array> m_uint8ArrayMember;
-  bool m_hasUnionInRecordMember = false;
   HeapVector<std::pair<String, LongOrBoolean>> m_unionInRecordMember;
   FloatOrBoolean m_unionWithTypedefs;
-  bool m_hasUnrestrictedDoubleMember = false;
   double m_unrestrictedDoubleMember;
 
   friend class V8TestDictionary;
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/TestDictionaryDerivedImplementedAs.h b/third_party/WebKit/Source/bindings/tests/results/core/TestDictionaryDerivedImplementedAs.h
index cba1420..069f43e2 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/TestDictionaryDerivedImplementedAs.h
+++ b/third_party/WebKit/Source/bindings/tests/results/core/TestDictionaryDerivedImplementedAs.h
@@ -49,11 +49,12 @@
   DECLARE_VIRTUAL_TRACE();
 
  private:
+  bool m_hasRequiredLongMember = false;
+  bool m_hasStringOrDoubleSequenceMember = false;
+
   String m_derivedStringMember;
   String m_derivedStringMemberWithDefault;
-  bool m_hasRequiredLongMember = false;
   int32_t m_requiredLongMember;
-  bool m_hasStringOrDoubleSequenceMember = false;
   HeapVector<StringOrDouble> m_stringOrDoubleSequenceMember;
 
   friend class V8TestDictionaryDerivedImplementedAs;
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/TestInterfaceEventInit.h b/third_party/WebKit/Source/bindings/tests/results/core/TestInterfaceEventInit.h
index fc345302..72ffa07 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/TestInterfaceEventInit.h
+++ b/third_party/WebKit/Source/bindings/tests/results/core/TestInterfaceEventInit.h
@@ -35,6 +35,7 @@
   DECLARE_VIRTUAL_TRACE();
 
  private:
+
   String m_stringMember;
 
   friend class V8TestInterfaceEventInit;
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/TestPermissiveDictionary.h b/third_party/WebKit/Source/bindings/tests/results/core/TestPermissiveDictionary.h
index 1893735..a93b096 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/TestPermissiveDictionary.h
+++ b/third_party/WebKit/Source/bindings/tests/results/core/TestPermissiveDictionary.h
@@ -35,6 +35,7 @@
 
  private:
   bool m_hasBooleanMember = false;
+
   bool m_booleanMember;
 
   friend class V8TestPermissiveDictionary;
diff --git a/third_party/WebKit/Source/core/editing/EditingCommandTest.cpp b/third_party/WebKit/Source/core/editing/EditingCommandTest.cpp
index 19e0bfd..7df938f7 100644
--- a/third_party/WebKit/Source/core/editing/EditingCommandTest.cpp
+++ b/third_party/WebKit/Source/core/editing/EditingCommandTest.cpp
@@ -57,7 +57,7 @@
         dummy_editor.CreateCommand(String(entry.name).DeprecatedLower());
     EXPECT_EQ(static_cast<int>(entry.type), command.IdForHistogram())
         << entry.name;
-    command = dummy_editor.CreateCommand(String(entry.name).DeprecatedUpper());
+    command = dummy_editor.CreateCommand(String(entry.name).UpperASCII());
     EXPECT_EQ(static_cast<int>(entry.type), command.IdForHistogram())
         << entry.name;
   }
diff --git a/third_party/WebKit/Source/core/editing/FrameSelection.cpp b/third_party/WebKit/Source/core/editing/FrameSelection.cpp
index 02bbb33..dd7d834 100644
--- a/third_party/WebKit/Source/core/editing/FrameSelection.cpp
+++ b/third_party/WebKit/Source/core/editing/FrameSelection.cpp
@@ -250,7 +250,7 @@
   }
 
   frame_caret_->StopCaretBlinkTimer();
-  UpdateAppearance();
+  UpdateAppearance(LayoutSelection::PaintHint::kPaint);
 
   // Always clear the x position used for vertical arrow navigation.
   // It will be restored by the vertical arrow navigation code if necessary.
@@ -335,7 +335,26 @@
   // Hits in
   // virtual/gpu/compositedscrolling/scrollbars/scrollbar-miss-mousemove-disabled.html
   DisableCompositingQueryAsserts disabler;
-  UpdateAppearance();
+
+  // No focused element means document root has focus.
+  const Element* const focus = GetDocument().FocusedElement()
+                                   ? GetDocument().FocusedElement()
+                                   : GetDocument().documentElement();
+
+  // Protection against LayoutTests/editing/selection/selection-crash.html
+  if (!focus) {
+    frame_caret_->ScheduleVisualUpdateForPaintInvalidationIfNeeded();
+    text_control_focused_ = false;
+    return;
+  }
+
+  // Hide the selection when focus goes away from a text-field and into
+  // something that is not a text-field. When focus enters another text-field we
+  // do not need to update appearance; the appearance is updated when the new
+  // selection is set.
+  if (text_control_focused_ && !focus->IsTextControl())
+    UpdateAppearance(LayoutSelection::PaintHint::kHide);
+  text_control_focused_ = focus->IsTextControl();
 }
 
 static DispatchEventResult DispatchSelectStart(
@@ -773,13 +792,15 @@
 }
 
 void FrameSelection::DidLayout() {
-  UpdateAppearance();
+  // Upon relayout, a hidden selection must be kept hidden and a visible
+  // selection must be kept visible.
+  UpdateAppearance(LayoutSelection::PaintHint::kKeep);
 }
 
-void FrameSelection::UpdateAppearance() {
+void FrameSelection::UpdateAppearance(LayoutSelection::PaintHint hint) {
   DCHECK(!frame_->ContentLayoutItem().IsNull());
   frame_caret_->ScheduleVisualUpdateForPaintInvalidationIfNeeded();
-  layout_selection_->SetHasPendingSelection();
+  layout_selection_->SetHasPendingSelection(hint);
 }
 
 void FrameSelection::NotifyLayoutObjectOfSelectionChange(
@@ -968,7 +989,7 @@
       document_loader->GetInitialScrollState().was_scrolled_by_user = true;
     if (start.AnchorNode()->GetLayoutObject()->ScrollRectToVisible(
             rect, alignment, alignment))
-      UpdateAppearance();
+      UpdateAppearance(LayoutSelection::PaintHint::kPaint);
   }
 }
 
diff --git a/third_party/WebKit/Source/core/editing/FrameSelection.h b/third_party/WebKit/Source/core/editing/FrameSelection.h
index 31b7c5e..befd406 100644
--- a/third_party/WebKit/Source/core/editing/FrameSelection.h
+++ b/third_party/WebKit/Source/core/editing/FrameSelection.h
@@ -32,6 +32,7 @@
 #include "core/dom/Range.h"
 #include "core/dom/SynchronousMutationObserver.h"
 #include "core/editing/EphemeralRange.h"
+#include "core/editing/LayoutSelection.h"
 #include "core/editing/VisiblePosition.h"
 #include "core/editing/VisibleSelection.h"
 #include "core/editing/iterators/TextIteratorBehavior.h"
@@ -242,7 +243,7 @@
       RevealExtentOption = kDoNotRevealExtent);
   void SetSelectionFromNone();
 
-  void UpdateAppearance();
+  void UpdateAppearance(LayoutSelection::PaintHint);
   bool ShouldShowBlockCursor() const;
   void SetShouldShowBlockCursor(bool);
 
@@ -308,6 +309,7 @@
 
   const Member<FrameCaret> frame_caret_;
   bool use_secure_keyboard_entry_when_active_ = false;
+  bool text_control_focused_ = false;
 };
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/editing/LayoutSelection.cpp b/third_party/WebKit/Source/core/editing/LayoutSelection.cpp
index 1dccdeb..772eaad 100644
--- a/third_party/WebKit/Source/core/editing/LayoutSelection.cpp
+++ b/third_party/WebKit/Source/core/editing/LayoutSelection.cpp
@@ -35,6 +35,7 @@
 LayoutSelection::LayoutSelection(FrameSelection& frame_selection)
     : frame_selection_(&frame_selection),
       has_pending_selection_(false),
+      force_hide_(false),
       selection_start_(nullptr),
       selection_end_(nullptr),
       selection_start_pos_(-1),
@@ -337,6 +338,14 @@
   SetSelection(0, -1, 0, -1, kPaintInvalidationNewMinusOld);
 }
 
+void LayoutSelection::SetHasPendingSelection(PaintHint hint) {
+  has_pending_selection_ = true;
+  if (hint == PaintHint::kHide)
+    force_hide_ = true;
+  else if (hint == PaintHint::kPaint)
+    force_hide_ = false;
+}
+
 void LayoutSelection::Commit(LayoutView& layout_view) {
   if (!HasPendingSelection())
     return;
@@ -357,7 +366,7 @@
   const VisibleSelectionInFlatTree& selection =
       CreateVisibleSelection(CalcVisibleSelection(original_selection));
 
-  if (!selection.IsRange()) {
+  if (!selection.IsRange() || force_hide_) {
     ClearSelection();
     return;
   }
@@ -396,6 +405,7 @@
 
 void LayoutSelection::OnDocumentShutdown() {
   has_pending_selection_ = false;
+  force_hide_ = false;
   selection_start_ = nullptr;
   selection_end_ = nullptr;
   selection_start_pos_ = -1;
diff --git a/third_party/WebKit/Source/core/editing/LayoutSelection.h b/third_party/WebKit/Source/core/editing/LayoutSelection.h
index db981e0..3b640344 100644
--- a/third_party/WebKit/Source/core/editing/LayoutSelection.h
+++ b/third_party/WebKit/Source/core/editing/LayoutSelection.h
@@ -33,12 +33,14 @@
 
 class LayoutSelection final : public GarbageCollected<LayoutSelection> {
  public:
+  enum class PaintHint { kHide, kKeep, kPaint };
+
   static LayoutSelection* Create(FrameSelection& frame_selection) {
     return new LayoutSelection(frame_selection);
   }
 
   bool HasPendingSelection() const { return has_pending_selection_; }
-  void SetHasPendingSelection() { has_pending_selection_ = true; }
+  void SetHasPendingSelection(PaintHint);
   void Commit(LayoutView&);
 
   IntRect SelectionBounds();
@@ -69,6 +71,7 @@
 
   Member<FrameSelection> frame_selection_;
   bool has_pending_selection_ : 1;
+  bool force_hide_ : 1;
 
   // The current selection represented as 2 boundaries.
   // Selection boundaries are represented in LayoutView by a tuple
diff --git a/third_party/WebKit/Source/core/html/media/MediaDocument.cpp b/third_party/WebKit/Source/core/html/media/MediaDocument.cpp
index ab43a693..faff1e5 100644
--- a/third_party/WebKit/Source/core/html/media/MediaDocument.cpp
+++ b/third_party/WebKit/Source/core/html/media/MediaDocument.cpp
@@ -212,7 +212,7 @@
         GetDocument()
             ->GetCachedLocale(GetDocument()->ContentLanguage())
             .QueryString(WebLocalizedString::kDownloadButtonLabel)
-            .DeprecatedUpper());
+            .UpperUnicode(GetDocument()->ContentLanguage()));
     // Using CSS style according to Android material design.
     anchor->setAttribute(
         styleAttr,
diff --git a/third_party/WebKit/Source/core/layout/svg/SVGLayoutTreeAsText.cpp b/third_party/WebKit/Source/core/layout/svg/SVGLayoutTreeAsText.cpp
index 8ce53ffb..b497c12 100644
--- a/third_party/WebKit/Source/core/layout/svg/SVGLayoutTreeAsText.cpp
+++ b/third_party/WebKit/Source/core/layout/svg/SVGLayoutTreeAsText.cpp
@@ -239,7 +239,7 @@
 }
 
 static TextStream& operator<<(TextStream& ts, const SVGSpreadMethodType& type) {
-  ts << SVGEnumerationToString<SVGSpreadMethodType>(type).DeprecatedUpper();
+  ts << SVGEnumerationToString<SVGSpreadMethodType>(type).UpperASCII();
   return ts;
 }
 
diff --git a/third_party/WebKit/Source/core/page/FocusController.cpp b/third_party/WebKit/Source/core/page/FocusController.cpp
index 88bc321b..dcf0fbf 100644
--- a/third_party/WebKit/Source/core/page/FocusController.cpp
+++ b/third_party/WebKit/Source/core/page/FocusController.cpp
@@ -1065,36 +1065,6 @@
   return element.GetDocument().GetFrame() && RootEditableElement(element);
 }
 
-static void ClearSelectionIfNeeded(LocalFrame* old_focused_frame,
-                                   LocalFrame* new_focused_frame,
-                                   Element* new_focused_element) {
-  if (!old_focused_frame || !new_focused_frame)
-    return;
-
-  if (old_focused_frame->GetDocument() != new_focused_frame->GetDocument())
-    return;
-
-  FrameSelection& selection = old_focused_frame->Selection();
-  const SelectionInDOMTree& selection_in_dom_tree =
-      selection.GetSelectionInDOMTree();
-  if (selection_in_dom_tree.IsNone())
-    return;
-
-  Node* selection_start_node = selection_in_dom_tree.Base().AnchorNode();
-  if (selection_start_node == new_focused_element ||
-      selection_start_node->IsDescendantOf(new_focused_element))
-    return;
-
-  if (!EnclosingTextControl(selection_start_node))
-    return;
-
-  if (selection_start_node->IsInShadowTree() &&
-      selection_start_node->OwnerShadowHost() == new_focused_element)
-    return;
-
-  selection.Clear();
-}
-
 bool FocusController::SetFocusedElement(Element* element,
                                         Frame* new_focused_frame) {
   return SetFocusedElement(
@@ -1131,9 +1101,6 @@
       new_document->FocusedElement() == element)
     return true;
 
-  if (new_focused_frame && new_focused_frame->IsLocalFrame())
-    ClearSelectionIfNeeded(old_focused_frame, ToLocalFrame(new_focused_frame),
-                           element);
 
   if (old_document && old_document != new_document)
     old_document->ClearFocusedElement();
@@ -1360,6 +1327,11 @@
   Element* element = ToElement(focus_candidate.focusable_node);
   DCHECK(element);
 
+  if (!element->IsTextControl() && !HasEditableStyle(*element->ToNode())) {
+    // To fulfill the expectation of spatial-navigation/snav-input.html
+    // we clear selection when spatnav moves focus away from a text-field.
+    FocusedFrame()->Selection().Clear();
+  }
   element->focus(FocusParams(SelectionBehaviorOnFocus::kReset, type, nullptr));
   return true;
 }
diff --git a/third_party/WebKit/Source/core/svg/graphics/SVGImage.cpp b/third_party/WebKit/Source/core/svg/graphics/SVGImage.cpp
index 445cfe074..39564a8 100644
--- a/third_party/WebKit/Source/core/svg/graphics/SVGImage.cpp
+++ b/third_party/WebKit/Source/core/svg/graphics/SVGImage.cpp
@@ -365,7 +365,7 @@
   FloatRect float_bounds(FloatPoint(), size);
   const SkRect bounds(float_bounds);
 
-  flags.setShader(SkShader::MakePictureShader(
+  flags.setShader(MakePaintShaderRecord(
       PaintRecordForCurrentFrame(float_bounds, url), SkShader::kRepeat_TileMode,
       SkShader::kRepeat_TileMode, &local_matrix, &bounds));
 
diff --git a/third_party/WebKit/Source/modules/crypto/NormalizeAlgorithm.cpp b/third_party/WebKit/Source/modules/crypto/NormalizeAlgorithm.cpp
index 7cd8662..f5120fab 100644
--- a/third_party/WebKit/Source/modules/crypto/NormalizeAlgorithm.cpp
+++ b/third_party/WebKit/Source/modules/crypto/NormalizeAlgorithm.cpp
@@ -140,7 +140,7 @@
     String str(it->algorithm_name, it->algorithm_name_length);
     if (!str.ContainsOnlyASCII())
       return false;
-    if (str.DeprecatedUpper() != str)
+    if (str.UpperASCII() != str)
       return false;
   }
 
diff --git a/third_party/WebKit/Source/platform/graphics/paint/ClipPathDisplayItem.cpp b/third_party/WebKit/Source/platform/graphics/paint/ClipPathDisplayItem.cpp
index 75f9fdd2..a215a535 100644
--- a/third_party/WebKit/Source/platform/graphics/paint/ClipPathDisplayItem.cpp
+++ b/third_party/WebKit/Source/platform/graphics/paint/ClipPathDisplayItem.cpp
@@ -23,11 +23,12 @@
   list->AppendClipPathItem(clip_path_, true);
 }
 
-void BeginClipPathDisplayItem::AnalyzeForGpuRasterization(
-    SkPictureGpuAnalyzer& analyzer) const {
+int BeginClipPathDisplayItem::NumberOfSlowPaths() const {
   // Temporarily disabled (pref regressions due to GPU veto stickiness:
   // http://crbug.com/603969).
   // analyzer.analyzeClipPath(m_clipPath, SkRegion::kIntersect_Op, true);
+  // TODO(enne): fixup this code to return an int.
+  return 0;
 }
 
 void EndClipPathDisplayItem::Replay(GraphicsContext& context) const {
diff --git a/third_party/WebKit/Source/platform/graphics/paint/ClipPathDisplayItem.h b/third_party/WebKit/Source/platform/graphics/paint/ClipPathDisplayItem.h
index 42434ae..3952c87 100644
--- a/third_party/WebKit/Source/platform/graphics/paint/ClipPathDisplayItem.h
+++ b/third_party/WebKit/Source/platform/graphics/paint/ClipPathDisplayItem.h
@@ -24,7 +24,7 @@
   void AppendToWebDisplayItemList(const IntRect&,
                                   WebDisplayItemList*) const override;
 
-  void AnalyzeForGpuRasterization(SkPictureGpuAnalyzer&) const override;
+  int NumberOfSlowPaths() const override;
 
  private:
 #ifndef NDEBUG
diff --git a/third_party/WebKit/Source/platform/graphics/paint/CompositingRecorder.cpp b/third_party/WebKit/Source/platform/graphics/paint/CompositingRecorder.cpp
index cdcd8fd..7eac3491 100644
--- a/third_party/WebKit/Source/platform/graphics/paint/CompositingRecorder.cpp
+++ b/third_party/WebKit/Source/platform/graphics/paint/CompositingRecorder.cpp
@@ -29,58 +29,8 @@
 CompositingRecorder::~CompositingRecorder() {
   if (RuntimeEnabledFeatures::slimmingPaintV2Enabled())
     return;
-  // If the end of the current display list is of the form
-  // [BeginCompositingDisplayItem] [DrawingDisplayItem], then fold the
-  // BeginCompositingDisplayItem into a new DrawingDisplayItem that replaces
-  // them both. This allows Skia to optimize for the case when the
-  // BeginCompositingDisplayItem represents a simple opacity/color that can be
-  // merged into the opacity/color of the drawing. See crbug.com/628831 for more
-  // details.
-  PaintController& paint_controller = graphics_context_.GetPaintController();
-  const DisplayItem* last_display_item = paint_controller.LastDisplayItem(0);
-  const DisplayItem* second_to_last_display_item =
-      paint_controller.LastDisplayItem(1);
-  // TODO(chrishtr): remove the call to LastDisplayItemIsSubsequenceEnd when
-  // https://codereview.chromium.org/2768143002 lands.
-  if (!RuntimeEnabledFeatures::slimmingPaintV2Enabled() && last_display_item &&
-      second_to_last_display_item && last_display_item->DrawsContent() &&
-      second_to_last_display_item->GetType() ==
-          DisplayItem::kBeginCompositing &&
-      !paint_controller.LastDisplayItemIsSubsequenceEnd()) {
-    FloatRect cull_rect(
-        ((DrawingDisplayItem*)last_display_item)->GetPaintRecord()->cullRect());
-    const DisplayItemClient& display_item_client = last_display_item->Client();
-    DisplayItem::Type display_item_type = last_display_item->GetType();
-
-    // Re-record the last two DisplayItems into a new drawing. The new item
-    // cannot be cached, because it is a mutation of the DisplayItem the client
-    // thought it was painting.
-    paint_controller.BeginSkippingCache();
-    {
-#if DCHECK_IS_ON()
-      // In the recorder's scope we remove the last two display items which
-      // are combined into a new drawing.
-      DisableListModificationCheck disabler;
-#endif
-      DrawingRecorder new_recorder(graphics_context_, display_item_client,
-                                   display_item_type, cull_rect);
-      DCHECK(!DrawingRecorder::UseCachedDrawingIfPossible(
-          graphics_context_, display_item_client, display_item_type));
-
-      second_to_last_display_item->Replay(graphics_context_);
-      last_display_item->Replay(graphics_context_);
-      EndCompositingDisplayItem(client_).Replay(graphics_context_);
-
-      // Remove the DrawingDisplayItem.
-      paint_controller.RemoveLastDisplayItem();
-      // Remove the BeginCompositingDisplayItem.
-      paint_controller.RemoveLastDisplayItem();
-    }
-    paint_controller.EndSkippingCache();
-  } else {
-    graphics_context_.GetPaintController().EndItem<EndCompositingDisplayItem>(
-        client_);
-  }
+  graphics_context_.GetPaintController().EndItem<EndCompositingDisplayItem>(
+      client_);
 }
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/platform/graphics/paint/DisplayItem.h b/third_party/WebKit/Source/platform/graphics/paint/DisplayItem.h
index a9afcd1..d5f344e6 100644
--- a/third_party/WebKit/Source/platform/graphics/paint/DisplayItem.h
+++ b/third_party/WebKit/Source/platform/graphics/paint/DisplayItem.h
@@ -17,8 +17,6 @@
 #include "platform/wtf/text/WTFString.h"
 #endif
 
-class SkPictureGpuAnalyzer;
-
 namespace blink {
 
 class GraphicsContext;
@@ -337,7 +335,7 @@
   virtual bool DrawsContent() const { return false; }
 
   // Override to implement specific analysis strategies.
-  virtual void AnalyzeForGpuRasterization(SkPictureGpuAnalyzer&) const {}
+  virtual int NumberOfSlowPaths() const { return 0; }
 
 #ifndef NDEBUG
   static WTF::String TypeAsDebugString(DisplayItem::Type);
diff --git a/third_party/WebKit/Source/platform/graphics/paint/DisplayItemList.cpp b/third_party/WebKit/Source/platform/graphics/paint/DisplayItemList.cpp
index 474a7d12..4fe7853 100644
--- a/third_party/WebKit/Source/platform/graphics/paint/DisplayItemList.cpp
+++ b/third_party/WebKit/Source/platform/graphics/paint/DisplayItemList.cpp
@@ -7,7 +7,6 @@
 #include "platform/graphics/LoggingCanvas.h"
 #include "platform/graphics/paint/DrawingDisplayItem.h"
 #include "platform/graphics/paint/PaintChunk.h"
-#include "third_party/skia/include/core/SkPictureAnalyzer.h"
 
 #ifndef NDEBUG
 #include "platform/wtf/text/WTFString.h"
diff --git a/third_party/WebKit/Source/platform/graphics/paint/DrawingDisplayItem.cpp b/third_party/WebKit/Source/platform/graphics/paint/DrawingDisplayItem.cpp
index 2a17f3d..7c14717 100644
--- a/third_party/WebKit/Source/platform/graphics/paint/DrawingDisplayItem.cpp
+++ b/third_party/WebKit/Source/platform/graphics/paint/DrawingDisplayItem.cpp
@@ -10,7 +10,6 @@
 #include "third_party/skia/include/core/SkBitmap.h"
 #include "third_party/skia/include/core/SkCanvas.h"
 #include "third_party/skia/include/core/SkData.h"
-#include "third_party/skia/include/core/SkPictureAnalyzer.h"
 
 namespace blink {
 
@@ -30,14 +29,8 @@
   return record_.get();
 }
 
-void DrawingDisplayItem::AnalyzeForGpuRasterization(
-    SkPictureGpuAnalyzer& analyzer) const {
-  // TODO(enne): Need an SkPictureGpuAnalyzer on PictureRecord.
-  // This is a bit overkill to ToSkPicture a record just to get
-  // numSlowPaths.
-  if (!record_)
-    return;
-  analyzer.analyzePicture(ToSkPicture(record_).get());
+int DrawingDisplayItem::NumberOfSlowPaths() const {
+  return record_ ? record_->numSlowPaths() : 0;
 }
 
 #ifndef NDEBUG
diff --git a/third_party/WebKit/Source/platform/graphics/paint/DrawingDisplayItem.h b/third_party/WebKit/Source/platform/graphics/paint/DrawingDisplayItem.h
index e5f989f8..3dedbcbd 100644
--- a/third_party/WebKit/Source/platform/graphics/paint/DrawingDisplayItem.h
+++ b/third_party/WebKit/Source/platform/graphics/paint/DrawingDisplayItem.h
@@ -41,7 +41,7 @@
     return known_to_be_opaque_;
   }
 
-  void AnalyzeForGpuRasterization(SkPictureGpuAnalyzer&) const override;
+  int NumberOfSlowPaths() const override;
 
  private:
 #ifndef NDEBUG
diff --git a/third_party/WebKit/Source/platform/graphics/paint/PaintController.cpp b/third_party/WebKit/Source/platform/graphics/paint/PaintController.cpp
index e26681df..d3b7553 100644
--- a/third_party/WebKit/Source/platform/graphics/paint/PaintController.cpp
+++ b/third_party/WebKit/Source/platform/graphics/paint/PaintController.cpp
@@ -16,6 +16,8 @@
 #include <stdio.h>
 #endif
 
+static constexpr int kMaxNumberOfSlowPathsBeforeVeto = 5;
+
 namespace blink {
 
 void PaintController::SetTracksRasterInvalidations(bool value) {
@@ -535,7 +537,7 @@
       !new_display_item_list_.IsEmpty())
     GenerateChunkRasterInvalidationRects(new_paint_chunks_.LastChunk());
 
-  SkPictureGpuAnalyzer gpu_analyzer;
+  int num_slow_paths = 0;
 
   current_cache_generation_ =
       DisplayItemClient::CacheGenerationOrInvalidationReason::Next();
@@ -554,8 +556,8 @@
   Vector<const DisplayItemClient*> skipped_cache_clients;
   for (const auto& item : new_display_item_list_) {
     // No reason to continue the analysis once we have a veto.
-    if (gpu_analyzer.suitableForGpuRasterization())
-      item.AnalyzeForGpuRasterization(gpu_analyzer);
+    if (num_slow_paths <= kMaxNumberOfSlowPathsBeforeVeto)
+      num_slow_paths += item.NumberOfSlowPaths();
 
     // TODO(wkorman): Only compute and append visual rect for drawings.
     new_display_item_list_.AppendVisualRect(
@@ -593,7 +595,7 @@
   new_display_item_list_.ShrinkToFit();
   current_paint_artifact_ = PaintArtifact(
       std::move(new_display_item_list_), new_paint_chunks_.ReleasePaintChunks(),
-      gpu_analyzer.suitableForGpuRasterization());
+      num_slow_paths <= kMaxNumberOfSlowPathsBeforeVeto);
   ResetCurrentListIndices();
   out_of_order_item_indices_.Clear();
   out_of_order_chunk_indices_.Clear();
diff --git a/third_party/WebKit/Source/platform/graphics/paint/PaintControllerTest.cpp b/third_party/WebKit/Source/platform/graphics/paint/PaintControllerTest.cpp
index f5f45cc8..038f0bd 100644
--- a/third_party/WebKit/Source/platform/graphics/paint/PaintControllerTest.cpp
+++ b/third_party/WebKit/Source/platform/graphics/paint/PaintControllerTest.cpp
@@ -2273,40 +2273,6 @@
     DisplayItemClient::EndShouldKeepAliveAllClients();
 #endif
   }
-
-  void TestFoldCompositingDrawingInSubsequence() {
-    FakeDisplayItemClient container("container");
-    FakeDisplayItemClient content("content");
-    GraphicsContext context(GetPaintController());
-
-    {
-      SubsequenceRecorder subsequence(context, container);
-      CompositingRecorder compositing(context, content, SkBlendMode::kSrc, 0.5);
-      DrawRect(context, content, kBackgroundDrawingType,
-               FloatRect(100, 100, 300, 300));
-    }
-    GetPaintController().CommitNewDisplayItems();
-    EXPECT_EQ(
-        1u,
-        GetPaintController().GetPaintArtifact().GetDisplayItemList().size());
-
-    {
-      EXPECT_FALSE(SubsequenceRecorder::UseCachedSubsequenceIfPossible(
-          context, container));
-      SubsequenceRecorder subsequence(context, container);
-      CompositingRecorder compositing(context, content, SkBlendMode::kSrc, 0.5);
-      DrawRect(context, content, kBackgroundDrawingType,
-               FloatRect(100, 100, 300, 300));
-    }
-    GetPaintController().CommitNewDisplayItems();
-    EXPECT_EQ(
-        1u,
-        GetPaintController().GetPaintArtifact().GetDisplayItemList().size());
-
-#if CHECK_DISPLAY_ITEM_CLIENT_ALIVENESS
-    DisplayItemClient::EndShouldKeepAliveAllClients();
-#endif
-  }
 };
 
 TEST_F(PaintControllerUnderInvalidationTest, ChangeDrawing) {
@@ -2363,11 +2329,6 @@
   TestInvalidationInSubsequence();
 }
 
-TEST_F(PaintControllerUnderInvalidationTest,
-       FoldCompositingDrawingInSubsequence) {
-  TestFoldCompositingDrawingInSubsequence();
-}
-
 #endif  // defined(GTEST_HAS_DEATH_TEST) && !OS(ANDROID)
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/platform/mac/LocalCurrentGraphicsContext.mm b/third_party/WebKit/Source/platform/mac/LocalCurrentGraphicsContext.mm
index 6379d6a..e3eada5 100644
--- a/third_party/WebKit/Source/platform/mac/LocalCurrentGraphicsContext.mm
+++ b/third_party/WebKit/Source/platform/mac/LocalCurrentGraphicsContext.mm
@@ -24,6 +24,7 @@
 #include "platform/graphics/paint/PaintCanvas.h"
 #include "platform/mac/ThemeMac.h"
 #include "platform_canvas.h"
+#include "third_party/skia/include/core/SkRegion.h"
 
 namespace blink {
 
diff --git a/third_party/WebKit/Source/platform/wtf/text/WTFString.cpp b/third_party/WebKit/Source/platform/wtf/text/WTFString.cpp
index 6950ae73..e2b6693 100644
--- a/third_party/WebKit/Source/platform/wtf/text/WTFString.cpp
+++ b/third_party/WebKit/Source/platform/wtf/text/WTFString.cpp
@@ -276,12 +276,6 @@
   return impl_->LowerUnicode();
 }
 
-String String::DeprecatedUpper() const {
-  if (!impl_)
-    return String();
-  return impl_->UpperUnicode();
-}
-
 String String::LowerUnicode(const AtomicString& locale_identifier) const {
   if (!impl_)
     return String();
diff --git a/third_party/WebKit/Source/platform/wtf/text/WTFString.h b/third_party/WebKit/Source/platform/wtf/text/WTFString.h
index 87d75f9..2b6cc48 100644
--- a/third_party/WebKit/Source/platform/wtf/text/WTFString.h
+++ b/third_party/WebKit/Source/platform/wtf/text/WTFString.h
@@ -284,16 +284,18 @@
   String Left(unsigned len) const { return Substring(0, len); }
   String Right(unsigned len) const { return Substring(length() - len, len); }
 
-  // Returns a lowercase/uppercase version of the string. These functions might
-  // convert non-ASCII characters to ASCII characters. For example,
-  // DeprecatedLower() for U+212A is 'k', DeprecatedUpper() for U+017F is 'S'.
-  // These functions are rarely used to implement web platform features. See
+  // Returns a lowercase version of the string. This function might convert
+  // non-ASCII characters to ASCII characters. For example, DeprecatedLower()
+  // for U+212A is 'k'.
+  // This function is rarely used to implement web platform features. See
   // crbug.com/627682.
-  // These functions are deprecated. We should use UpperASCII(), or introduce
-  // UpperUnicode(), and should introduce LowerASCII() or LowerUnicode().
+  // This function is deprecated. We should use LowerASCII() or introduce
+  // LowerUnicode().
   String DeprecatedLower() const;
-  String DeprecatedUpper() const;
 
+  // |locale_identifier| is case-insensitive, and accepts either of "aa_aa" or
+  // "aa-aa". Empty/null |locale_identifier| indicates locale-independent
+  // Unicode case conversion.
   String LowerUnicode(const AtomicString& locale_identifier) const;
   String UpperUnicode(const AtomicString& locale_identifier) const;
 
diff --git a/third_party/WebKit/Source/platform/wtf/text/WTFStringTest.cpp b/third_party/WebKit/Source/platform/wtf/text/WTFStringTest.cpp
index 2748761..a65d1f4 100644
--- a/third_party/WebKit/Source/platform/wtf/text/WTFStringTest.cpp
+++ b/third_party/WebKit/Source/platform/wtf/text/WTFStringTest.cpp
@@ -443,14 +443,6 @@
       String::FromUTF8("LIN\xE2\x84\xAA").DeprecatedLower().Utf8().Data());
 }
 
-TEST(StringTest, DeprecatedUpper) {
-  EXPECT_STREQ("CROSS", String("cross").DeprecatedUpper().Utf8().Data());
-  // U+017F -> S
-  EXPECT_STREQ(
-      "CROSS",
-      String::FromUTF8("cro\xC5\xBFs").DeprecatedUpper().Utf8().Data());
-}
-
 TEST(StringTest, Ensure16Bit) {
   String string8("8bit");
   EXPECT_TRUE(string8.Is8Bit());
diff --git a/third_party/WebKit/Source/web/WebLocalFrameImpl.cpp b/third_party/WebKit/Source/web/WebLocalFrameImpl.cpp
index 634bd57f..8c49fca 100644
--- a/third_party/WebKit/Source/web/WebLocalFrameImpl.cpp
+++ b/third_party/WebKit/Source/web/WebLocalFrameImpl.cpp
@@ -1054,7 +1054,7 @@
   String command = name;
 
   // Make sure the first letter is upper case.
-  command.Replace(0, 1, command.Substring(0, 1).DeprecatedUpper());
+  command.Replace(0, 1, command.Substring(0, 1).UpperASCII());
 
   // Remove the trailing ':' if existing.
   if (command[command.length() - 1] == UChar(':'))
diff --git a/third_party/WebKit/Source/web/tests/WebFrameTest.cpp b/third_party/WebKit/Source/web/tests/WebFrameTest.cpp
index 81fb1ba4..e9ddd44 100644
--- a/third_party/WebKit/Source/web/tests/WebFrameTest.cpp
+++ b/third_party/WebKit/Source/web/tests/WebFrameTest.cpp
@@ -86,6 +86,7 @@
 #include "modules/mediastream/MediaStreamRegistry.h"
 #include "platform/Cursor.h"
 #include "platform/DragImage.h"
+#include "platform/KeyboardCodes.h"
 #include "platform/PlatformResourceLoader.h"
 #include "platform/RuntimeEnabledFeatures.h"
 #include "platform/UserGestureIndicator.h"
@@ -113,6 +114,7 @@
 #include "public/platform/WebCachePolicy.h"
 #include "public/platform/WebClipboard.h"
 #include "public/platform/WebFloatRect.h"
+#include "public/platform/WebKeyboardEvent.h"
 #include "public/platform/WebMockClipboard.h"
 #include "public/platform/WebSecurityOrigin.h"
 #include "public/platform/WebThread.h"
@@ -4367,6 +4369,61 @@
   EXPECT_EQ(0, web_view_helper.WebView()->FocusedElement());
 }
 
+class ChangedSelectionCounter : public FrameTestHelpers::TestWebFrameClient {
+ public:
+  ChangedSelectionCounter() : call_count_(0) {}
+  void DidChangeSelection(bool isSelectionEmpty) { ++call_count_; }
+  int Count() const { return call_count_; }
+  void Reset() { call_count_ = 0; }
+
+ private:
+  int call_count_;
+};
+
+TEST_P(ParameterizedWebFrameTest, TabKeyCursorMoveTriggersOneSelectionChange) {
+  ChangedSelectionCounter counter;
+  FrameTestHelpers::WebViewHelper web_view_helper;
+  RegisterMockedHttpURLLoad("editable_elements.html");
+  WebViewImpl* web_view = web_view_helper.InitializeAndLoad(
+      base_url_ + "editable_elements.html", true, &counter);
+
+  WebKeyboardEvent tab_down(WebInputEvent::kKeyDown,
+                            WebInputEvent::kNoModifiers,
+                            WebInputEvent::kTimeStampForTesting);
+  WebKeyboardEvent tab_up(WebInputEvent::kKeyUp, WebInputEvent::kNoModifiers,
+                          WebInputEvent::kTimeStampForTesting);
+  tab_down.dom_key = Platform::Current()->DomKeyEnumFromString("\t");
+  tab_up.dom_key = Platform::Current()->DomKeyEnumFromString("\t");
+  tab_down.windows_key_code = VKEY_TAB;
+  tab_up.windows_key_code = VKEY_TAB;
+
+  // Move to the next text-field: 1 cursor change.
+  counter.Reset();
+  web_view->HandleInputEvent(WebCoalescedInputEvent(tab_down));
+  web_view->HandleInputEvent(WebCoalescedInputEvent(tab_up));
+  EXPECT_EQ(1, counter.Count());
+
+  // Move to another text-field: 1 cursor change.
+  web_view->HandleInputEvent(WebCoalescedInputEvent(tab_down));
+  web_view->HandleInputEvent(WebCoalescedInputEvent(tab_up));
+  EXPECT_EQ(2, counter.Count());
+
+  // Move to a number-field: 1 cursor change.
+  web_view->HandleInputEvent(WebCoalescedInputEvent(tab_down));
+  web_view->HandleInputEvent(WebCoalescedInputEvent(tab_up));
+  EXPECT_EQ(3, counter.Count());
+
+  // Move to an editable element: 1 cursor change.
+  web_view->HandleInputEvent(WebCoalescedInputEvent(tab_down));
+  web_view->HandleInputEvent(WebCoalescedInputEvent(tab_up));
+  EXPECT_EQ(4, counter.Count());
+
+  // Move to a non-editable element: 0 cursor changes.
+  web_view->HandleInputEvent(WebCoalescedInputEvent(tab_down));
+  web_view->HandleInputEvent(WebCoalescedInputEvent(tab_up));
+  EXPECT_EQ(4, counter.Count());
+}
+
 // Implementation of WebFrameClient that tracks the v8 contexts that are created
 // and destroyed for verification.
 class ContextLifetimeTestWebFrameClient
diff --git a/third_party/WebKit/Source/web/tests/data/editable_elements.html b/third_party/WebKit/Source/web/tests/data/editable_elements.html
new file mode 100644
index 0000000..9fdd82d
--- /dev/null
+++ b/third_party/WebKit/Source/web/tests/data/editable_elements.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<body>
+    <input autofocus>
+    <input>
+    <input>
+    <input type="number">
+    <p contenteditable="true">blabla</p>
+    <button>
+</body>
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/base.py b/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/base.py
index 1a2c3057..dbcd2ea84 100644
--- a/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/base.py
+++ b/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/base.py
@@ -664,6 +664,9 @@
             return reftest_list
 
         # Try to extract information from MANIFEST.json.
+        match = re.match(r'virtual/[^/]+/', test_name)
+        if match:
+            test_name = test_name[match.end(0):]
         match = re.match(r'external/wpt/(.*)', test_name)
         if not match:
             return []
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/base_unittest.py b/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/base_unittest.py
index 3532200..dabb72b 100644
--- a/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/base_unittest.py
+++ b/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/base_unittest.py
@@ -372,6 +372,10 @@
         self.assertEqual(port.reference_files('external/wpt/html/dom/elements/global-attributes/dir_auto-EN-L.html'),
                          [('==', port.layout_tests_dir() +
                            '/external/wpt/html/dom/elements/global-attributes/dir_auto-EN-L-ref.html')])
+        self.assertEqual(port.reference_files('virtual/layout_ng/' +
+                                              'external/wpt/html/dom/elements/global-attributes/dir_auto-EN-L.html'),
+                         [('==', port.layout_tests_dir() +
+                           '/external/wpt/html/dom/elements/global-attributes/dir_auto-EN-L-ref.html')])
 
     def test_operating_system(self):
         self.assertEqual('mac', self.make_port().operating_system())
diff --git a/third_party/qcms/BUILD.gn b/third_party/qcms/BUILD.gn
index 183ae43..3e7075f 100644
--- a/third_party/qcms/BUILD.gn
+++ b/third_party/qcms/BUILD.gn
@@ -31,10 +31,16 @@
       "src/transform_util.h",
     ]
 
+    defines = []
+
     if (current_cpu == "x86" || current_cpu == "x64") {
-      defines = [ "SSE2_ENABLE" ]
+      defines += [ "SSE2_ENABLE" ]
       sources += [ "src/transform-sse2.c" ]
     }
+
+    if (use_libfuzzer) {
+      defines += [ "USE_LIBFUZZER" ]
+    }
   }
 
   configs -= [ "//build/config/compiler:chromium_code" ]
diff --git a/third_party/qcms/README.chromium b/third_party/qcms/README.chromium
index e2ef2809..f3b2277 100644
--- a/third_party/qcms/README.chromium
+++ b/third_party/qcms/README.chromium
@@ -165,6 +165,8 @@
    - https://bugs.chromium.org/p/chromium/issues/detail?id=708145
  - Fix overflow when reading parametric gamma curves
    - https://bugs.chromium.org/p/chromium/issues/detail?id=708143
+ - Disable some asserts when running in libfuzzer
+   - https://bugs.chromium.org/p/chromium/issues/detail?id=711418
 
 For the Chromium changes, since the import, in a patch format run:
   git diff b8456f38 src
diff --git a/third_party/qcms/src/chain.c b/third_party/qcms/src/chain.c
index 5255070..46143cc 100644
--- a/third_party/qcms/src/chain.c
+++ b/third_party/qcms/src/chain.c
@@ -29,6 +29,12 @@
 #include "transform_util.h"
 #include "matrix.h"
 
+#ifdef USE_LIBFUZZER
+#define ASSERT(x)
+#else
+#define ASSERT(x) assert(x)
+#endif
+
 static struct matrix build_lut_matrix(struct lutType *lut)
 {
 	struct matrix result;
@@ -831,7 +837,7 @@
 			goto fail;
 		}
 	} else {
-		assert(0 && "Unsupported output profile workflow.");
+		ASSERT(0 && "Unsupported output profile workflow.");
 		return NULL;
 	}
 
@@ -906,7 +912,7 @@
 			goto fail;
 		append_transform(rgb_to_pcs, &next_transform);
 	} else {
-		assert(0 && "input color space not supported");
+		ASSERT(0 && "input color space not supported");
 		goto fail;
 	}
 
@@ -949,7 +955,7 @@
 			goto fail;
 		append_transform(pcs_to_rgb, &next_transform);
 	} else {
-		assert(0 && "output color space not supported");
+		ASSERT(0 && "output color space not supported");
 		goto fail;
 	}
 	// Not Completed
diff --git a/third_party/qcms/src/iccread.c b/third_party/qcms/src/iccread.c
index 7dfe995c..11c1d76 100644
--- a/third_party/qcms/src/iccread.c
+++ b/third_party/qcms/src/iccread.c
@@ -1271,7 +1271,6 @@
 			white_point.Y = -1.0;
 
 			assert(0 && "invalid temp");
-
 			return white_point;
 		}
 	}
diff --git a/third_party/qcms/src/transform.c b/third_party/qcms/src/transform.c
index dd4eee4..b20a39c 100644
--- a/third_party/qcms/src/transform.c
+++ b/third_party/qcms/src/transform.c
@@ -31,6 +31,12 @@
 #include "matrix.h"
 #include "transform_util.h"
 
+#ifdef USE_LIBFUZZER
+#define ASSERT(x)
+#else
+#define ASSERT(x) assert(x)
+#endif
+
 /* for MSVC, GCC, Intel, and Sun compilers */
 #if defined(_M_IX86) || defined(__i386__) || defined(__i386) || defined(_M_AMD64) || defined(__x86_64__) || defined(__x86_64)
 #define X86
@@ -1296,7 +1302,7 @@
 	}
 
 	if (out_type != QCMS_DATA_RGB_8 && out_type != QCMS_DATA_RGBA_8) {
-		assert(0 && "output type");
+		ASSERT(0 && "output type");
 		qcms_transform_release(transform);
 		return NULL;
 	}
@@ -1315,7 +1321,7 @@
 		// precaching should be avoided.
 		qcms_transform *result = qcms_transform_precacheLUT_float(transform, in, out, 33, in_type);
 		if (!result) {
-			assert(0 && "precacheLUT failed");
+			ASSERT(0 && "precacheLUT failed");
 			qcms_transform_release(transform);
 			return NULL;
 		}
@@ -1353,7 +1359,7 @@
 		struct matrix in_matrix, out_matrix, result;
 
 		if (in_type != QCMS_DATA_RGB_8 && in_type != QCMS_DATA_RGBA_8) {
-			assert(0 && "input type");
+			ASSERT(0 && "input type");
 			qcms_transform_release(transform);
 			return NULL;
 		}
@@ -1428,7 +1434,7 @@
 
 	} else if (in->color_space == GRAY_SIGNATURE) {
 		if (in_type != QCMS_DATA_GRAY_8 && in_type != QCMS_DATA_GRAYA_8) {
-			assert(0 && "input type");
+			ASSERT(0 && "input type");
 			qcms_transform_release(transform);
 			return NULL;
 		}
@@ -1454,7 +1460,7 @@
 			}
 		}
 	} else {
-		assert(0 && "unexpected colorspace");
+		ASSERT(0 && "unexpected colorspace");
 		qcms_transform_release(transform);
 		return NULL;
 	}
@@ -1567,7 +1573,7 @@
 			break;
 		default:
 			/* should not be reached */
-			assert(0);
+			ASSERT(0);
 	}
 
 	return size;
@@ -1634,7 +1640,7 @@
 			break;
 		default:
 			/* should not be reached */
-			assert(0);
+			ASSERT(0);
 	}
 
 	return size;
diff --git a/third_party/qcms/src/transform_util.c b/third_party/qcms/src/transform_util.c
index 7db8c75..1d7b153 100644
--- a/third_party/qcms/src/transform_util.c
+++ b/third_party/qcms/src/transform_util.c
@@ -32,6 +32,12 @@
 #define INFINITY HUGE_VAL
 #endif
 
+#ifdef USE_LIBFUZZER
+#define ASSERT(x)
+#else
+#define ASSERT(x) assert(x)
+#endif
+
 #define PARAMETRIC_CURVE_TYPE 0x70617261 //'para'
 
 /* value must be a value between 0 and 1 */
@@ -182,7 +188,7 @@
                 f = parameter[6];
                 interval = parameter[4];
         } else {
-                assert(0 && "invalid parametric function type.");
+                ASSERT(0 && "invalid parametric function type.");
                 a = 1;
                 b = 0;
                 c = 0;
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index 6a5d23d..4b5f27c4 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -26933,6 +26933,15 @@
   </summary>
 </histogram>
 
+<histogram name="Media.EME.IsIncognito" enum="BooleanIncognito">
+  <owner>xhwang@chromium.org</owner>
+  <summary>
+    Whether a given WebMediaPlayer instance, with a CDM attached, is played in
+    an incognito window or in ChromeOS guest mode. Players that never started
+    playback are excluded. Recorded once at time of player destruction.
+  </summary>
+</histogram>
+
 <histogram name="Media.EME.KeySystemSupport.Widevine"
     enum="MediaKeySystemSupportStatus">
   <obsolete>
@@ -84719,6 +84728,11 @@
   <int value="1" label="inappropriate_fallback alert"/>
 </enum>
 
+<enum name="BooleanIncognito" type="int">
+  <int value="0" label="Not Incognito"/>
+  <int value="1" label="Incognito"/>
+</enum>
+
 <enum name="BooleanInForeground" type="int">
   <int value="0" label="Background"/>
   <int value="1" label="Foreground"/>
diff --git a/ui/views/controls/scrollbar/cocoa_scroll_bar.mm b/ui/views/controls/scrollbar/cocoa_scroll_bar.mm
index c77bdf4..ce75522 100644
--- a/ui/views/controls/scrollbar/cocoa_scroll_bar.mm
+++ b/ui/views/controls/scrollbar/cocoa_scroll_bar.mm
@@ -5,6 +5,7 @@
 #import "ui/views/controls/scrollbar/cocoa_scroll_bar.h"
 
 #import "base/mac/sdk_forward_declarations.h"
+#include "cc/paint/paint_shader.h"
 #include "third_party/skia/include/core/SkColor.h"
 #include "third_party/skia/include/effects/SkGradientShader.h"
 #include "ui/compositor/layer.h"